forked from mirrors/akkoma-fe
Add bookmarks
Co-authored-by: jared <jaredrmain@gmail.com>
This commit is contained in:
parent
7bd89b579f
commit
de291e2e33
20 changed files with 213 additions and 24 deletions
|
@ -99,6 +99,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Ability to change user's email
|
||||
- About page
|
||||
- Added remote user redirect
|
||||
- Bookmarks
|
||||
### Changed
|
||||
- changed the way fading effects for user profile/long statuses works, now uses css-mask instead of gradient background hacks which weren't exactly compatible with semi-transparent themes
|
||||
### Fixed
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"cropperjs": "^1.4.3",
|
||||
"diff": "^3.0.1",
|
||||
"escape-html": "^1.0.3",
|
||||
"parse-link-header": "^1.0.1",
|
||||
"localforage": "^1.5.0",
|
||||
"phoenix": "^1.3.0",
|
||||
"portal-vue": "^2.1.4",
|
||||
|
|
|
@ -2,6 +2,7 @@ import PublicTimeline from 'components/public_timeline/public_timeline.vue'
|
|||
import PublicAndExternalTimeline from 'components/public_and_external_timeline/public_and_external_timeline.vue'
|
||||
import FriendsTimeline from 'components/friends_timeline/friends_timeline.vue'
|
||||
import TagTimeline from 'components/tag_timeline/tag_timeline.vue'
|
||||
import BookmarkTimeline from 'components/bookmark_timeline/bookmark_timeline.vue'
|
||||
import ConversationPage from 'components/conversation-page/conversation-page.vue'
|
||||
import Interactions from 'components/interactions/interactions.vue'
|
||||
import DMs from 'components/dm_timeline/dm_timeline.vue'
|
||||
|
@ -40,6 +41,7 @@ export default (store) => {
|
|||
{ name: 'public-timeline', path: '/main/public', component: PublicTimeline },
|
||||
{ name: 'friends', path: '/main/friends', component: FriendsTimeline, beforeEnter: validateAuthenticatedRoute },
|
||||
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
|
||||
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
|
||||
{ name: 'conversation', path: '/notice/:id', component: ConversationPage, meta: { dontScroll: true } },
|
||||
{ name: 'remote-user-profile-acct',
|
||||
path: '/remote-users/(@?):username([^/@]+)@:hostname([^/@]+)',
|
||||
|
|
17
src/components/bookmark_timeline/bookmark_timeline.js
Normal file
17
src/components/bookmark_timeline/bookmark_timeline.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import Timeline from '../timeline/timeline.vue'
|
||||
|
||||
const Bookmarks = {
|
||||
computed: {
|
||||
timeline () {
|
||||
return this.$store.state.statuses.timelines.bookmarks
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Timeline
|
||||
},
|
||||
destroyed () {
|
||||
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
|
||||
}
|
||||
}
|
||||
|
||||
export default Bookmarks
|
9
src/components/bookmark_timeline/bookmark_timeline.vue
Normal file
9
src/components/bookmark_timeline/bookmark_timeline.vue
Normal file
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.bookmarks')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'bookmarks'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./bookmark_timeline.js"></script>
|
|
@ -34,6 +34,16 @@ const ExtraButtons = {
|
|||
navigator.clipboard.writeText(this.statusLink)
|
||||
.then(() => this.$emit('onSuccess'))
|
||||
.catch(err => this.$emit('onError', err.error.error))
|
||||
},
|
||||
bookmarkStatus () {
|
||||
this.$store.dispatch('bookmark', { id: this.status.id })
|
||||
.then(() => this.$emit('onSuccess'))
|
||||
.catch(err => this.$emit('onError', err.error.error))
|
||||
},
|
||||
unbookmarkStatus () {
|
||||
this.$store.dispatch('unbookmark', { id: this.status.id })
|
||||
.then(() => this.$emit('onSuccess'))
|
||||
.catch(err => this.$emit('onError', err.error.error))
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -40,6 +40,22 @@
|
|||
>
|
||||
<i class="icon-pin" /><span>{{ $t("status.unpin") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="!status.bookmarked"
|
||||
class="dropdown-item dropdown-item-icon"
|
||||
@click.prevent="bookmarkStatus"
|
||||
@click="close"
|
||||
>
|
||||
<i class="icon-bookmark-empty" /><span>{{ $t("status.bookmark") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="status.bookmarked"
|
||||
class="dropdown-item dropdown-item-icon"
|
||||
@click.prevent="unbookmarkStatus"
|
||||
@click="close"
|
||||
>
|
||||
<i class="icon-bookmark" /><span>{{ $t("status.unbookmark") }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="canDelete"
|
||||
class="dropdown-item dropdown-item-icon"
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
<i class="button-icon icon-mail-alt" /> {{ $t("nav.dms") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser">
|
||||
<router-link :to="{ name: 'bookmarks'}">
|
||||
<i class="button-icon icon-bookmark" /> {{ $t("nav.bookmarks") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-if="currentUser && currentUser.locked">
|
||||
<router-link :to="{ name: 'friend-requests' }">
|
||||
<i class="button-icon icon-user-plus" /> {{ $t("nav.friend_requests") }}
|
||||
|
|
|
@ -65,6 +65,14 @@
|
|||
<i class="button-icon icon-home-2" /> {{ $t("nav.timeline") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link :to="{ name: 'bookmarks'}">
|
||||
<i class="button-icon icon-bookmark" /> {{ $t("nav.bookmarks") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser && currentUser.locked"
|
||||
@click="toggleDrawer"
|
||||
|
|
|
@ -137,7 +137,7 @@ const Timeline = {
|
|||
showImmediately: true,
|
||||
userId: this.userId,
|
||||
tag: this.tag
|
||||
}).then(statuses => {
|
||||
}).then(({ statuses }) => {
|
||||
store.commit('setLoading', { timeline: this.timelineName, value: false })
|
||||
if (statuses && statuses.length === 0) {
|
||||
this.bottomedOut = true
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
"public_tl": "Public Timeline",
|
||||
"timeline": "Timeline",
|
||||
"twkn": "The Whole Known Network",
|
||||
"bookmarks": "Bookmarks",
|
||||
"user_search": "User Search",
|
||||
"search": "Search",
|
||||
"who_to_follow": "Who to follow",
|
||||
|
@ -629,6 +630,8 @@
|
|||
"pin": "Pin on profile",
|
||||
"unpin": "Unpin from profile",
|
||||
"pinned": "Pinned",
|
||||
"bookmark": "Bookmark",
|
||||
"unbookmark": "Unbookmark",
|
||||
"delete_confirm": "Do you really want to delete this status?",
|
||||
"reply_to": "Reply to",
|
||||
"replies_list": "Replies:",
|
||||
|
@ -724,7 +727,8 @@
|
|||
"add_reaction": "Add Reaction",
|
||||
"user_settings": "User Settings",
|
||||
"accept_follow_request": "Accept follow request",
|
||||
"reject_follow_request": "Reject follow request"
|
||||
"reject_follow_request": "Reject follow request",
|
||||
"bookmark": "Bookmark"
|
||||
},
|
||||
"upload": {
|
||||
"error": {
|
||||
|
|
|
@ -45,7 +45,8 @@
|
|||
"timeline": "Лента",
|
||||
"twkn": "Федеративная лента",
|
||||
"search": "Поиск",
|
||||
"friend_requests": "Запросы на чтение"
|
||||
"friend_requests": "Запросы на чтение",
|
||||
"bookmarks": "Закладки"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "Неизвестный статус, ищем...",
|
||||
|
@ -366,6 +367,10 @@
|
|||
"show_new": "Показать новые",
|
||||
"up_to_date": "Обновлено"
|
||||
},
|
||||
"status": {
|
||||
"bookmark": "В закладки",
|
||||
"unbookmark": "Удалить из закладок"
|
||||
},
|
||||
"user_card": {
|
||||
"block": "Заблокировать",
|
||||
"blocked": "Заблокирован",
|
||||
|
|
|
@ -62,7 +62,8 @@ export const defaultState = () => ({
|
|||
publicAndExternal: emptyTl(),
|
||||
friends: emptyTl(),
|
||||
tag: emptyTl(),
|
||||
dms: emptyTl()
|
||||
dms: emptyTl(),
|
||||
bookmarks: emptyTl()
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -163,8 +164,7 @@ const removeStatusFromGlobalStorage = (state, status) => {
|
|||
}
|
||||
}
|
||||
|
||||
const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {},
|
||||
noIdUpdate = false, userId }) => {
|
||||
const addNewStatuses = (state, { statuses, showImmediately = false, timeline, user = {}, noIdUpdate = false, userId, pagination = {} }) => {
|
||||
// Sanity check
|
||||
if (!isArray(statuses)) {
|
||||
return false
|
||||
|
@ -173,8 +173,13 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
|||
const allStatuses = state.allStatuses
|
||||
const timelineObject = state.timelines[timeline]
|
||||
|
||||
const maxNew = statuses.length > 0 ? maxBy(statuses, 'id').id : 0
|
||||
const minNew = statuses.length > 0 ? minBy(statuses, 'id').id : 0
|
||||
// Mismatch between API pagination and our internal minId/maxId tracking systems:
|
||||
// pagination.maxId is the oldest of the returned statuses when fetching older,
|
||||
// and pagination.minId is the newest when fetching newer. The names come directly
|
||||
// from the arguments they're supposed to be passed as for the next fetch.
|
||||
const minNew = pagination.maxId || (statuses.length > 0 ? minBy(statuses, 'id').id : 0)
|
||||
const maxNew = pagination.minId || (statuses.length > 0 ? maxBy(statuses, 'id').id : 0)
|
||||
|
||||
const newer = timeline && (maxNew > timelineObject.maxId || timelineObject.maxId === 0) && statuses.length > 0
|
||||
const older = timeline && (minNew < timelineObject.minId || timelineObject.minId === 0) && statuses.length > 0
|
||||
|
||||
|
@ -315,7 +320,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
|||
})
|
||||
|
||||
// Keep the visible statuses sorted
|
||||
if (timeline) {
|
||||
if (timeline && !(timeline === 'bookmarks')) {
|
||||
sortTimeline(timelineObject)
|
||||
}
|
||||
}
|
||||
|
@ -463,6 +468,14 @@ export const mutations = {
|
|||
newStatus.rebloggedBy.push(user)
|
||||
}
|
||||
},
|
||||
setBookmarked (state, { status, value }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.bookmarked = value
|
||||
},
|
||||
setBookmarkedConfirm (state, { status }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.bookmarked = status.bookmarked
|
||||
},
|
||||
setDeleted (state, { status }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.deleted = true
|
||||
|
@ -590,8 +603,8 @@ export const mutations = {
|
|||
const statuses = {
|
||||
state: defaultState(),
|
||||
actions: {
|
||||
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) {
|
||||
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId })
|
||||
addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId, pagination }) {
|
||||
commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId, pagination })
|
||||
},
|
||||
addNewNotifications ({ rootState, commit, dispatch, rootGetters }, { notifications, older }) {
|
||||
commit('addNewNotifications', { visibleNotificationTypes: visibleNotificationTypes(rootState), dispatch, notifications, older, rootGetters })
|
||||
|
@ -666,6 +679,20 @@ const statuses = {
|
|||
rootState.api.backendInteractor.unretweet({ id: status.id })
|
||||
.then(status => commit('setRetweetedConfirm', { status, user: rootState.users.currentUser }))
|
||||
},
|
||||
bookmark ({ rootState, commit }, status) {
|
||||
commit('setBookmarked', { status, value: true })
|
||||
rootState.api.backendInteractor.bookmarkStatus({ id: status.id })
|
||||
.then(status => {
|
||||
commit('setBookmarkedConfirm', { status })
|
||||
})
|
||||
},
|
||||
unbookmark ({ rootState, commit }, status) {
|
||||
commit('setBookmarked', { status, value: false })
|
||||
rootState.api.backendInteractor.unbookmarkStatus({ id: status.id })
|
||||
.then(status => {
|
||||
commit('setBookmarkedConfirm', { status })
|
||||
})
|
||||
},
|
||||
queueFlush ({ rootState, commit }, { timeline, id }) {
|
||||
commit('queueFlush', { timeline, id })
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { each, map, concat, last, get } from 'lodash'
|
||||
import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js'
|
||||
import { parseStatus, parseUser, parseNotification, parseAttachment, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
|
||||
import { RegistrationError, StatusCodeError } from '../errors/errors'
|
||||
|
||||
/* eslint-env browser */
|
||||
|
@ -50,6 +50,7 @@ const MASTODON_USER_URL = '/api/v1/accounts'
|
|||
const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships'
|
||||
const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses`
|
||||
const MASTODON_TAG_TIMELINE_URL = tag => `/api/v1/timelines/tag/${tag}`
|
||||
const MASTODON_BOOKMARK_TIMELINE_URL = '/api/v1/bookmarks'
|
||||
const MASTODON_USER_BLOCKS_URL = '/api/v1/blocks/'
|
||||
const MASTODON_USER_MUTES_URL = '/api/v1/mutes/'
|
||||
const MASTODON_BLOCK_USER_URL = id => `/api/v1/accounts/${id}/block`
|
||||
|
@ -58,6 +59,8 @@ const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute`
|
|||
const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
|
||||
const MASTODON_SUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/subscribe`
|
||||
const MASTODON_UNSUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/unsubscribe`
|
||||
const MASTODON_BOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/bookmark`
|
||||
const MASTODON_UNBOOKMARK_STATUS_URL = id => `/api/v1/statuses/${id}/unbookmark`
|
||||
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
||||
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
||||
const MASTODON_VOTE_URL = id => `/api/v1/polls/${id}/votes`
|
||||
|
@ -510,7 +513,8 @@ const fetchTimeline = ({
|
|||
user: MASTODON_USER_TIMELINE_URL,
|
||||
media: MASTODON_USER_TIMELINE_URL,
|
||||
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
|
||||
tag: MASTODON_TAG_TIMELINE_URL
|
||||
tag: MASTODON_TAG_TIMELINE_URL,
|
||||
bookmarks: MASTODON_BOOKMARK_TIMELINE_URL
|
||||
}
|
||||
const isNotifications = timeline === 'notifications'
|
||||
const params = []
|
||||
|
@ -539,7 +543,7 @@ const fetchTimeline = ({
|
|||
if (timeline === 'public' || timeline === 'publicAndExternal') {
|
||||
params.push(['only_media', false])
|
||||
}
|
||||
if (timeline !== 'favorites') {
|
||||
if (timeline !== 'favorites' && timeline !== 'bookmarks') {
|
||||
params.push(['with_muted', withMuted])
|
||||
}
|
||||
if (replyVisibility !== 'all') {
|
||||
|
@ -552,16 +556,20 @@ const fetchTimeline = ({
|
|||
url += `?${queryString}`
|
||||
let status = ''
|
||||
let statusText = ''
|
||||
let pagination = {}
|
||||
return fetch(url, { headers: authHeaders(credentials) })
|
||||
.then((data) => {
|
||||
status = data.status
|
||||
statusText = data.statusText
|
||||
pagination = parseLinkHeaderPagination(data.headers.get('Link'), {
|
||||
flakeId: timeline !== 'bookmarks' && timeline !== 'notifications'
|
||||
})
|
||||
return data
|
||||
})
|
||||
.then((data) => data.json())
|
||||
.then((data) => {
|
||||
if (!data.error) {
|
||||
return data.map(isNotifications ? parseNotification : parseStatus)
|
||||
return { data: data.map(isNotifications ? parseNotification : parseStatus), pagination }
|
||||
} else {
|
||||
data.status = status
|
||||
data.statusText = statusText
|
||||
|
@ -612,6 +620,22 @@ const unretweet = ({ id, credentials }) => {
|
|||
.then((data) => parseStatus(data))
|
||||
}
|
||||
|
||||
const bookmarkStatus = ({ id, credentials }) => {
|
||||
return promisedRequest({
|
||||
url: MASTODON_BOOKMARK_STATUS_URL(id),
|
||||
headers: authHeaders(credentials),
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
const unbookmarkStatus = ({ id, credentials }) => {
|
||||
return promisedRequest({
|
||||
url: MASTODON_UNBOOKMARK_STATUS_URL(id),
|
||||
headers: authHeaders(credentials),
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
const postStatus = ({
|
||||
credentials,
|
||||
status,
|
||||
|
@ -1150,6 +1174,8 @@ const apiService = {
|
|||
unfavorite,
|
||||
retweet,
|
||||
unretweet,
|
||||
bookmarkStatus,
|
||||
unbookmarkStatus,
|
||||
postStatus,
|
||||
deleteStatus,
|
||||
uploadMedia,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import escape from 'escape-html'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import { isStatusNotification } from '../notification_utils/notification_utils.js'
|
||||
|
||||
const qvitterStatusType = (status) => {
|
||||
|
@ -232,6 +233,8 @@ export const parseStatus = (data) => {
|
|||
output.repeated = data.reblogged
|
||||
output.repeat_num = data.reblogs_count
|
||||
|
||||
output.bookmarked = data.bookmarked
|
||||
|
||||
output.type = data.reblog ? 'retweet' : 'status'
|
||||
output.nsfw = data.sensitive
|
||||
|
||||
|
@ -381,3 +384,16 @@ const isNsfw = (status) => {
|
|||
const nsfwRegex = /#nsfw/i
|
||||
return (status.tags || []).includes('nsfw') || !!(status.text || '').match(nsfwRegex)
|
||||
}
|
||||
|
||||
export const parseLinkHeaderPagination = (linkHeader, opts = {}) => {
|
||||
const flakeId = opts.flakeId
|
||||
const parsedLinkHeader = parseLinkHeader(linkHeader)
|
||||
if (!parsedLinkHeader) return
|
||||
const maxId = parsedLinkHeader.next.max_id
|
||||
const minId = parsedLinkHeader.prev.min_id
|
||||
|
||||
return {
|
||||
maxId: flakeId ? maxId : parseInt(maxId, 10),
|
||||
minId: flakeId ? minId : parseInt(minId, 10)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
|||
|
||||
const fetchNotifications = ({ store, args, older }) => {
|
||||
return apiService.fetchTimeline(args)
|
||||
.then((notifications) => {
|
||||
.then(({ data: notifications }) => {
|
||||
update({ store, notifications, older })
|
||||
return notifications
|
||||
}, () => store.dispatch('setNotificationsError', { value: true }))
|
||||
|
|
|
@ -2,7 +2,7 @@ import { camelCase } from 'lodash'
|
|||
|
||||
import apiService from '../api/api.service.js'
|
||||
|
||||
const update = ({ store, statuses, timeline, showImmediately, userId }) => {
|
||||
const update = ({ store, statuses, timeline, showImmediately, userId, pagination }) => {
|
||||
const ccTimeline = camelCase(timeline)
|
||||
|
||||
store.dispatch('setError', { value: false })
|
||||
|
@ -12,7 +12,8 @@ const update = ({ store, statuses, timeline, showImmediately, userId }) => {
|
|||
timeline: ccTimeline,
|
||||
userId,
|
||||
statuses,
|
||||
showImmediately
|
||||
showImmediately,
|
||||
pagination
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -47,16 +48,18 @@ const fetchAndUpdate = ({
|
|||
const numStatusesBeforeFetch = timelineData.statuses.length
|
||||
|
||||
return apiService.fetchTimeline(args)
|
||||
.then((statuses) => {
|
||||
if (statuses.error) {
|
||||
store.dispatch('setErrorData', { value: statuses })
|
||||
.then(response => {
|
||||
if (response.error) {
|
||||
store.dispatch('setErrorData', { value: response })
|
||||
return
|
||||
}
|
||||
|
||||
const { data: statuses, pagination } = response
|
||||
if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) {
|
||||
store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId })
|
||||
}
|
||||
update({ store, statuses, timeline, showImmediately, userId })
|
||||
return statuses
|
||||
update({ store, statuses, timeline, showImmediately, userId, pagination })
|
||||
return { statuses, pagination }
|
||||
}, () => store.dispatch('setError', { value: true }))
|
||||
}
|
||||
|
||||
|
|
12
static/fontello.json
Executable file → Normal file
12
static/fontello.json
Executable file → Normal file
|
@ -375,6 +375,18 @@
|
|||
"css": "download",
|
||||
"code": 59429,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "f04a5d24e9e659145b966739c4fde82a",
|
||||
"css": "bookmark",
|
||||
"code": 59430,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "2f5ef6f6b7aaebc56458ab4e865beff5",
|
||||
"css": "bookmark-empty",
|
||||
"code": 61591,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { parseStatus, parseUser, parseNotification, addEmojis } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
|
||||
import { parseStatus, parseUser, parseNotification, addEmojis, parseLinkHeaderPagination } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
|
||||
import mastoapidata from '../../../../fixtures/mastoapi.json'
|
||||
import qvitterapidata from '../../../../fixtures/statuses.json'
|
||||
|
||||
|
@ -383,4 +383,24 @@ describe('API Entities normalizer', () => {
|
|||
expect(result).to.include('title=\':[a-z] {|}*:\'')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Link header pagination', () => {
|
||||
it('Parses min and max ids as integers', () => {
|
||||
const linkHeader = '<https://example.com/api/v1/notifications?max_id=861676>; rel="next", <https://example.com/api/v1/notifications?min_id=861741>; rel="prev"'
|
||||
const result = parseLinkHeaderPagination(linkHeader)
|
||||
expect(result).to.eql({
|
||||
'maxId': 861676,
|
||||
'minId': 861741
|
||||
})
|
||||
})
|
||||
|
||||
it('Parses min and max ids as flakes', () => {
|
||||
const linkHeader = '<http://example.com/api/v1/timelines/home?max_id=9waQx5IIS48qVue2Ai>; rel="next", <http://example.com/api/v1/timelines/home?min_id=9wi61nIPnfn674xgie>; rel="prev"'
|
||||
const result = parseLinkHeaderPagination(linkHeader, { flakeId: true })
|
||||
expect(result).to.eql({
|
||||
'maxId': '9waQx5IIS48qVue2Ai',
|
||||
'minId': '9wi61nIPnfn674xgie'
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -5751,6 +5751,13 @@ parse-json@^4.0.0:
|
|||
error-ex "^1.3.1"
|
||||
json-parse-better-errors "^1.0.1"
|
||||
|
||||
parse-link-header@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-1.0.1.tgz#bedfe0d2118aeb84be75e7b025419ec8a61140a7"
|
||||
integrity sha1-vt/g0hGK64S+deewJUGeyKYRQKc=
|
||||
dependencies:
|
||||
xtend "~4.0.1"
|
||||
|
||||
parseqs@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
|
||||
|
|
Loading…
Reference in a new issue