notifications unit test
This commit is contained in:
parent
1f1c0c1300
commit
080a2b00ba
16 changed files with 676 additions and 366 deletions
|
|
@ -18,7 +18,7 @@ const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||||
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
|
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
|
||||||
const ALIASES_URL = '/api/pleroma/aliases'
|
const ALIASES_URL = '/api/pleroma/aliases'
|
||||||
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
||||||
const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
|
export const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
|
||||||
|
|
||||||
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
||||||
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
||||||
|
|
@ -27,7 +27,7 @@ const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp'
|
||||||
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
||||||
const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/totp'
|
const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/totp'
|
||||||
|
|
||||||
const MASTODON_DISMISS_NOTIFICATION_URL = (id) =>
|
export const MASTODON_DISMISS_NOTIFICATION_URL = (id) =>
|
||||||
`/api/v1/notifications/${id}/dismiss`
|
`/api/v1/notifications/${id}/dismiss`
|
||||||
export const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite`
|
export const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite`
|
||||||
export const MASTODON_UNFAVORITE_URL = (id) =>
|
export const MASTODON_UNFAVORITE_URL = (id) =>
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,6 @@ const FollowRequestCard = {
|
||||||
|
|
||||||
const notifId = this.findFollowRequestNotificationId()
|
const notifId = this.findFollowRequestNotificationId()
|
||||||
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
|
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
|
||||||
this.$store.dispatch('updateNotification', {
|
|
||||||
id: notifId,
|
|
||||||
updater: (notification) => {
|
|
||||||
notification.type = 'follow'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this.hideApproveConfirmDialog()
|
this.hideApproveConfirmDialog()
|
||||||
},
|
},
|
||||||
denyUser() {
|
denyUser() {
|
||||||
|
|
|
||||||
|
|
@ -154,12 +154,6 @@ const Notification = {
|
||||||
this.$store.dispatch('markSingleNotificationAsSeen', {
|
this.$store.dispatch('markSingleNotificationAsSeen', {
|
||||||
id: this.notification.id,
|
id: this.notification.id,
|
||||||
})
|
})
|
||||||
this.$store.dispatch('updateNotification', {
|
|
||||||
id: this.notification.id,
|
|
||||||
updater: (notification) => {
|
|
||||||
notification.type = 'follow'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this.hideApproveConfirmDialog()
|
this.hideApproveConfirmDialog()
|
||||||
},
|
},
|
||||||
denyUser() {
|
denyUser() {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import {
|
||||||
filteredNotifications,
|
filteredNotifications,
|
||||||
unseenNotifications,
|
unseenNotifications,
|
||||||
} from '../../services/notification_utils/notification_utils.js'
|
} from '../../services/notification_utils/notification_utils.js'
|
||||||
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
|
|
||||||
import NotificationFilters from './notification_filters.vue'
|
import NotificationFilters from './notification_filters.vue'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
|
|
@ -54,7 +53,6 @@ const Notifications = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showScrollTop: false,
|
showScrollTop: false,
|
||||||
bottomedOut: false,
|
|
||||||
// How many seen notifications to display in the list. The more there are,
|
// How many seen notifications to display in the list. The more there are,
|
||||||
// the heavier the page becomes. This count is increased when loading
|
// the heavier the page becomes. This count is increased when loading
|
||||||
// older notifications, and cut back to default whenever hitting "Read!".
|
// older notifications, and cut back to default whenever hitting "Read!".
|
||||||
|
|
@ -124,7 +122,10 @@ const Notifications = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
loading() {
|
loading() {
|
||||||
return useNotificationsStore().loading
|
return useNotificationsStore().fetcher.loading.value
|
||||||
|
},
|
||||||
|
bottomedOut() {
|
||||||
|
return useNotificationsStore().fetcher.bottomedOut.value
|
||||||
},
|
},
|
||||||
noHeading() {
|
noHeading() {
|
||||||
const { layoutType } = useInterfaceStore()
|
const { layoutType } = useInterfaceStore()
|
||||||
|
|
@ -247,22 +248,7 @@ const Notifications = {
|
||||||
this.seenToDisplayCount = seenCount
|
this.seenToDisplayCount = seenCount
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = this.$store
|
useNotificationsStore().fetcher.fetchOlder()
|
||||||
const credentials = useUsersStore().currentUser.credentials
|
|
||||||
useNotificationsStore().setNotificationsLoading(true)
|
|
||||||
notificationsFetcher
|
|
||||||
.fetchAndUpdate({
|
|
||||||
store,
|
|
||||||
credentials,
|
|
||||||
older: true,
|
|
||||||
})
|
|
||||||
.then((notifs) => {
|
|
||||||
useNotificationsStore().setNotificationsLoading(false)
|
|
||||||
if (notifs.length === 0) {
|
|
||||||
this.bottomedOut = true
|
|
||||||
}
|
|
||||||
this.seenToDisplayCount += notifs.length
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,22 +168,22 @@ const Status = {
|
||||||
return this.status.deleted
|
return this.status.deleted
|
||||||
},
|
},
|
||||||
repeaterStyle() {
|
repeaterStyle() {
|
||||||
const user = this.status.user
|
return highlightStyle(
|
||||||
return highlightStyle(useUserHighlightStore().get(this.repeater.screen_name))
|
useUserHighlightStore().get(this.repeater.screen_name),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
userStyle() {
|
userStyle() {
|
||||||
if (this.noHeading) return
|
if (this.noHeading) return
|
||||||
return highlightStyle(useUserHighlightStore().get(this.user.screen_name))
|
return highlightStyle(useUserHighlightStore().get(this.user.screen_name))
|
||||||
},
|
},
|
||||||
userProfileLink() {
|
userProfileLink() {
|
||||||
return this.generateUserProfileLink(
|
return this.generateUserProfileLink(this.user.id, this.user.screen_name)
|
||||||
this.user.id,
|
|
||||||
this.user.screen_name,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
replyProfileLink() {
|
replyProfileLink() {
|
||||||
if (this.isReply) {
|
if (this.isReply) {
|
||||||
const user = useUsersStore().findUser(this.mainStatus.in_reply_to_user_id)
|
const user = useUsersStore().findUser(
|
||||||
|
this.mainStatus.in_reply_to_user_id,
|
||||||
|
)
|
||||||
// FIXME Why user not found sometimes???
|
// FIXME Why user not found sometimes???
|
||||||
return user ? user.statusnet_profile_url : 'NOT_FOUND'
|
return user ? user.statusnet_profile_url : 'NOT_FOUND'
|
||||||
}
|
}
|
||||||
|
|
@ -359,14 +359,17 @@ const Status = {
|
||||||
},
|
},
|
||||||
isReply() {
|
isReply() {
|
||||||
return !!(
|
return !!(
|
||||||
this.mainStatus.in_reply_to_status_id && this.mainStatus.in_reply_to_user_id
|
this.mainStatus.in_reply_to_status_id &&
|
||||||
|
this.mainStatus.in_reply_to_user_id
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
replyToName() {
|
replyToName() {
|
||||||
if (this.mainStatus.in_reply_to_screen_name) {
|
if (this.mainStatus.in_reply_to_screen_name) {
|
||||||
return this.status.in_reply_to_screen_name
|
return this.status.in_reply_to_screen_name
|
||||||
} else {
|
} else {
|
||||||
const user = useUsersStore().findUser(this.mainStatus.in_reply_to_user_id)
|
const user = useUsersStore().findUser(
|
||||||
|
this.mainStatus.in_reply_to_user_id,
|
||||||
|
)
|
||||||
return user?.screen_name_ui
|
return user?.screen_name_ui
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import ScrollTopButton from 'src/components/scroll_top_button/scroll_top_button.
|
||||||
import TimelineMenu from 'src/components/timeline_menu/timeline_menu.vue'
|
import TimelineMenu from 'src/components/timeline_menu/timeline_menu.vue'
|
||||||
|
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
import { useTimelinesStore } from 'src/stores/timelines.js'
|
import { useTimelinesStore } from 'src/stores/timelines.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
|
@ -103,9 +103,7 @@ const Timeline = {
|
||||||
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
||||||
const min = Math.max(0, this.virtualScrollIndex - statusesPerSide)
|
const min = Math.max(0, this.virtualScrollIndex - statusesPerSide)
|
||||||
const max = Math.min(amount, this.virtualScrollIndex + statusesPerSide)
|
const max = Math.min(amount, this.virtualScrollIndex + statusesPerSide)
|
||||||
return new Set(
|
return new Set(this.timeline.order.slice(min, max))
|
||||||
this.timeline.order.slice(min, max),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
virtualScrollingEnabled() {
|
virtualScrollingEnabled() {
|
||||||
return useMergedConfigStore().mergedConfig.virtualScrolling
|
return useMergedConfigStore().mergedConfig.virtualScrolling
|
||||||
|
|
@ -213,7 +211,6 @@ const Timeline = {
|
||||||
|
|
||||||
// if we have a previous scroll index that can be used, test if it's
|
// if we have a previous scroll index that can be used, test if it's
|
||||||
|
|
||||||
|
|
||||||
const virtualScrollIndexY =
|
const virtualScrollIndexY =
|
||||||
statuses[cappedScrollIndex].getBoundingClientRect().y
|
statuses[cappedScrollIndex].getBoundingClientRect().y
|
||||||
if (Math.abs(err) > virtualScrollIndexY) {
|
if (Math.abs(err) > virtualScrollIndexY) {
|
||||||
|
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
||||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
|
||||||
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
|
||||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
|
||||||
|
|
||||||
import { fetchTimeline } from 'src/api/timelines.js'
|
|
||||||
|
|
||||||
// For using include_types when fetching notifications.
|
|
||||||
// Note: chat_mention excluded as pleroma-fe polls them separately
|
|
||||||
const mastoApiNotificationTypes = new Set([
|
|
||||||
'mention',
|
|
||||||
'status',
|
|
||||||
'favourite',
|
|
||||||
'reblog',
|
|
||||||
'follow',
|
|
||||||
'follow_request',
|
|
||||||
'move',
|
|
||||||
'poll',
|
|
||||||
'pleroma:emoji_reaction',
|
|
||||||
'pleroma:report',
|
|
||||||
])
|
|
||||||
|
|
||||||
const fetchAndUpdate = ({ credentials }, { older = false, sinceId }) => {
|
|
||||||
useNotificationsStore().setLoading(true)
|
|
||||||
const args = { credentials }
|
|
||||||
const timelineData = useNotificationsStore()
|
|
||||||
const hideMutedPosts = useMergedConfigStore().mergedConfig.hideMutedPosts
|
|
||||||
|
|
||||||
if (useInstanceCapabilitiesStore().pleromaChatMessagesAvailable) {
|
|
||||||
mastoApiNotificationTypes.add('pleroma:chat_mention')
|
|
||||||
}
|
|
||||||
|
|
||||||
args.includeTypes = [...mastoApiNotificationTypes]
|
|
||||||
args.withMuted = !hideMutedPosts
|
|
||||||
|
|
||||||
args.timeline = 'notifications'
|
|
||||||
if (older) {
|
|
||||||
if (timelineData.minId !== Number.POSITIVE_INFINITY) {
|
|
||||||
args.maxId = timelineData.minId
|
|
||||||
}
|
|
||||||
return fetchNotifications({ args, older })
|
|
||||||
} else {
|
|
||||||
// fetch new notifications
|
|
||||||
if (
|
|
||||||
sinceId === undefined &&
|
|
||||||
timelineData.maxId !== Number.POSITIVE_INFINITY
|
|
||||||
) {
|
|
||||||
args.sinceId = timelineData.maxId
|
|
||||||
} else if (sinceId !== null) {
|
|
||||||
args.sinceId = sinceId
|
|
||||||
}
|
|
||||||
const result = fetchNotifications({ args, older })
|
|
||||||
|
|
||||||
// If there's any unread notifications, try fetch notifications since
|
|
||||||
// the newest read notification to check if any of the unread notifs
|
|
||||||
// have changed their 'seen' state (marked as read in another session), so
|
|
||||||
// we can update the state in this session to mark them as read as well.
|
|
||||||
// The normal maxId-check does not tell if older notifications have changed
|
|
||||||
const notifications = timelineData.data
|
|
||||||
const readNotifsIds = notifications.filter((n) => n.seen).map((n) => n.id)
|
|
||||||
const unreadNotifsIds = notifications
|
|
||||||
.filter((n) => !n.seen)
|
|
||||||
.map((n) => n.id)
|
|
||||||
|
|
||||||
if (readNotifsIds.length > 0 && unreadNotifsIds.length > 0) {
|
|
||||||
const minId = Math.min(...unreadNotifsIds) // Oldest known unread notification
|
|
||||||
if (minId !== Infinity) {
|
|
||||||
args.sinceId = null // Don't use since_id since it sorta conflicts with min_id
|
|
||||||
args.minId = minId - 1 // go beyond
|
|
||||||
fetchNotifications({ args, older })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchNotifications = ({ args, older }) => {
|
|
||||||
return fetchTimeline(args)
|
|
||||||
.then((response) => {
|
|
||||||
const notifications = response.data
|
|
||||||
|
|
||||||
useNotificationsStore().addNewNotifications(response)
|
|
||||||
|
|
||||||
return notifications
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
if (
|
|
||||||
error.statusCode === 400 &&
|
|
||||||
error.statusText.includes('Invalid value for enum')
|
|
||||||
) {
|
|
||||||
error.statusText
|
|
||||||
.matchAll(/(\w+) - Invalid value for enum./g)
|
|
||||||
.toArray()
|
|
||||||
.map((x) => x[1])
|
|
||||||
.forEach((x) => mastoApiNotificationTypes.delete(x))
|
|
||||||
return fetchNotifications({ args, older })
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error('Notifications Error', error)
|
|
||||||
useInterfaceStore().pushGlobalNotice({
|
|
||||||
level: 'error',
|
|
||||||
messageKey: 'notifications.error',
|
|
||||||
messageArgs: [error.message],
|
|
||||||
timeout: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
useNotificationsStore().setLoading(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const notificationsFetcher = (credentials) => {
|
|
||||||
const state = {
|
|
||||||
interval: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
const boundFetchAndUpdate = ({ older = false, sinceId } = {}) =>
|
|
||||||
fetchAndUpdate({ credentials }, { older, sinceId })
|
|
||||||
|
|
||||||
const startFetching = () => {
|
|
||||||
if (state.interval) throw new Error('Interval already exists!')
|
|
||||||
|
|
||||||
boundFetchAndUpdate()
|
|
||||||
|
|
||||||
state.interval = promiseInterval(boundFetchAndUpdate, 10000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopFetching = () => {
|
|
||||||
state.interval.stop()
|
|
||||||
state.interval = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
startFetching,
|
|
||||||
stopFetching,
|
|
||||||
fetchAndUpdate: boundFetchAndUpdate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default notificationsFetcher
|
|
||||||
143
src/stores/fetchers/notifications_fetcher.js
Normal file
143
src/stores/fetchers/notifications_fetcher.js
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||||
|
|
||||||
|
import { fetchTimeline } from 'src/api/timelines.js'
|
||||||
|
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
|
||||||
|
|
||||||
|
// For using include_types when fetching notifications.
|
||||||
|
// Note: chat_mention excluded as pleroma-fe polls them separately
|
||||||
|
const mastoApiNotificationTypes = new Set([
|
||||||
|
'mention',
|
||||||
|
'status',
|
||||||
|
'favourite',
|
||||||
|
'reblog',
|
||||||
|
'follow',
|
||||||
|
'follow_request',
|
||||||
|
'move',
|
||||||
|
'poll',
|
||||||
|
'pleroma:emoji_reaction',
|
||||||
|
'pleroma:report',
|
||||||
|
])
|
||||||
|
|
||||||
|
const notificationsFetcher = (credentials) => {
|
||||||
|
const interval = ref(null)
|
||||||
|
const loading = ref(false)
|
||||||
|
const bottomedOut = ref(false)
|
||||||
|
|
||||||
|
const fetchNotifications = async ({ args, older }) => {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetchTimeline(args)
|
||||||
|
const notifications = response.data
|
||||||
|
if (older && notifications.length === 0) bottomedOut.value = true
|
||||||
|
|
||||||
|
useNotificationsStore().addNewNotifications(response)
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
error.statusCode === 400 &&
|
||||||
|
error.statusText.includes('Invalid value for enum')
|
||||||
|
) {
|
||||||
|
error.statusText
|
||||||
|
.matchAll(/(\w+) - Invalid value for enum./g)
|
||||||
|
.toArray()
|
||||||
|
.map((x) => x[1])
|
||||||
|
.forEach((x) => mastoApiNotificationTypes.delete(x))
|
||||||
|
|
||||||
|
// Retry
|
||||||
|
return await fetchNotifications({ args, older })
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('Notifications Error', error)
|
||||||
|
useInterfaceStore().pushGlobalNotice({
|
||||||
|
level: 'error',
|
||||||
|
messageKey: 'notifications.error',
|
||||||
|
messageArgs: [error.message],
|
||||||
|
timeout: 5000,
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchAndUpdate = async ({ older = false, sinceId } = {}) => {
|
||||||
|
const args = { credentials }
|
||||||
|
const timelineData = useNotificationsStore()
|
||||||
|
const hideMutedPosts = useMergedConfigStore().mergedConfig.hideMutedPosts
|
||||||
|
|
||||||
|
if (useInstanceCapabilitiesStore().pleromaChatMessagesAvailable) {
|
||||||
|
mastoApiNotificationTypes.add('pleroma:chat_mention')
|
||||||
|
}
|
||||||
|
|
||||||
|
args.includeTypes = [...mastoApiNotificationTypes]
|
||||||
|
args.withMuted = !hideMutedPosts
|
||||||
|
|
||||||
|
args.timeline = 'notifications'
|
||||||
|
if (older) {
|
||||||
|
if (timelineData.minId !== Number.POSITIVE_INFINITY) {
|
||||||
|
args.maxId = timelineData.minId
|
||||||
|
}
|
||||||
|
return await fetchNotifications({ args, older })
|
||||||
|
} else {
|
||||||
|
// fetch new notifications
|
||||||
|
if (
|
||||||
|
sinceId === undefined &&
|
||||||
|
timelineData.maxId !== Number.POSITIVE_INFINITY
|
||||||
|
) {
|
||||||
|
args.sinceId = timelineData.maxId
|
||||||
|
} else if (sinceId !== null) {
|
||||||
|
args.sinceId = sinceId
|
||||||
|
}
|
||||||
|
const result = await fetchNotifications({ args, older })
|
||||||
|
|
||||||
|
// If there's any unread notifications, try fetch notifications since
|
||||||
|
// the newest read notification to check if any of the unread notifs
|
||||||
|
// have changed their 'seen' state (marked as read in another session), so
|
||||||
|
// we can update the state in this session to mark them as read as well.
|
||||||
|
// The normal maxId-check does not tell if older notifications have changed
|
||||||
|
const notifications = timelineData.data
|
||||||
|
const readNotifsIds = notifications.filter((n) => n.seen).map((n) => n.id)
|
||||||
|
const unreadNotifsIds = notifications
|
||||||
|
.filter((n) => !n.seen)
|
||||||
|
.map((n) => n.id)
|
||||||
|
|
||||||
|
if (readNotifsIds.length > 0 && unreadNotifsIds.length > 0) {
|
||||||
|
const minId = Math.min(...unreadNotifsIds) // Oldest known unread notification
|
||||||
|
if (minId !== Infinity) {
|
||||||
|
args.sinceId = null // Don't use since_id since it sorta conflicts with min_id
|
||||||
|
args.minId = minId - 1 // go beyond
|
||||||
|
fetchNotifications({ args, older })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startFetching = () => {
|
||||||
|
if (interval.value) throw new Error('Interval already exists!')
|
||||||
|
|
||||||
|
fetchAndUpdate()
|
||||||
|
|
||||||
|
interval.value = promiseInterval(fetchAndUpdate, 10000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopFetching = () => {
|
||||||
|
interval.value.stop()
|
||||||
|
interval.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
bottomedOut,
|
||||||
|
startFetching,
|
||||||
|
stopFetching,
|
||||||
|
fetchOlder: () => fetchAndUpdate({ older: true }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default notificationsFetcher
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
|
|
||||||
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
|
@ -9,6 +8,7 @@ import { ARGUMENT_MAP, useTimelinesStore } from 'src/stores/timelines.js'
|
||||||
import { useUsersStore } from 'src/stores/users.js'
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { fetchTimeline } from 'src/api/timelines.js'
|
import { fetchTimeline } from 'src/api/timelines.js'
|
||||||
|
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
|
||||||
|
|
||||||
const REPLY_VISIBILITY_TIMELINES = new Set([
|
const REPLY_VISIBILITY_TIMELINES = new Set([
|
||||||
'friends',
|
'friends',
|
||||||
|
|
@ -49,11 +49,7 @@ const timelineFetcher = (timeline, argument, credentials) => {
|
||||||
|
|
||||||
return fetchTimeline(args)
|
return fetchTimeline(args)
|
||||||
.then(({ data: statuses, pagination, timestamp }) => {
|
.then(({ data: statuses, pagination, timestamp }) => {
|
||||||
if (
|
if (!older && statuses.length >= 20 && numStatusesBeforeFetch > 0) {
|
||||||
!older &&
|
|
||||||
statuses.length >= 20 &&
|
|
||||||
numStatusesBeforeFetch > 0
|
|
||||||
) {
|
|
||||||
useTimelinesStore().queueFlush(timeline.name, timeline.maxId)
|
useTimelinesStore().queueFlush(timeline.name, timeline.maxId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +107,6 @@ const timelineFetcher = (timeline, argument, credentials) => {
|
||||||
startFetching,
|
startFetching,
|
||||||
stopFetching,
|
stopFetching,
|
||||||
fetchOlder: () => fetchAndUpdate({ showImmediately: true, older: true }),
|
fetchOlder: () => fetchAndUpdate({ showImmediately: true, older: true }),
|
||||||
fetchNewer: () => fetchAndUpdate({ showImmediately: true, older: false }),
|
|
||||||
loading,
|
loading,
|
||||||
bottomedOut,
|
bottomedOut,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
import notificationsFetcher from 'src/stores/fetchers/notifications_fetcher.js'
|
||||||
import { useI18nStore } from 'src/stores/i18n.js'
|
import { useI18nStore } from 'src/stores/i18n.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
@ -19,7 +20,6 @@ import {
|
||||||
maybeShowNotification,
|
maybeShowNotification,
|
||||||
} from 'src/services/notification_utils/notification_utils.js'
|
} from 'src/services/notification_utils/notification_utils.js'
|
||||||
import { isStatusNotification } from 'src/services/notification_utils/notification_utils_sw.js'
|
import { isStatusNotification } from 'src/services/notification_utils/notification_utils_sw.js'
|
||||||
import notificationsFetcher from 'src/services/notifications_fetcher/notifications_fetcher.service.js'
|
|
||||||
|
|
||||||
export const defaultState = () => ({
|
export const defaultState = () => ({
|
||||||
desktopNotificationSilence: true,
|
desktopNotificationSilence: true,
|
||||||
|
|
@ -28,7 +28,7 @@ export const defaultState = () => ({
|
||||||
data: [],
|
data: [],
|
||||||
statusNotificationRelations: new WeakMap(),
|
statusNotificationRelations: new WeakMap(),
|
||||||
idStore: new Map(),
|
idStore: new Map(),
|
||||||
loading: false,
|
statusIdStore: new Set(),
|
||||||
socket: null,
|
socket: null,
|
||||||
streaming: false,
|
streaming: false,
|
||||||
fetcher: null,
|
fetcher: null,
|
||||||
|
|
@ -108,30 +108,35 @@ export const useNotificationsStore = defineStore('notifications', {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Updates
|
// Updates
|
||||||
updateNotificationsMinMaxId(id) {
|
updateExtremes(id) {
|
||||||
this.maxId = id > this.maxId ? id : this.maxId
|
if (this.maxId === '' || id > this.maxId) {
|
||||||
this.minId = id < this.minId ? id : this.minId
|
this.maxId = id
|
||||||
|
}
|
||||||
|
if (this.minId === '' || id < this.minId) {
|
||||||
|
this.minId = id
|
||||||
|
}
|
||||||
},
|
},
|
||||||
updateNotification({ id, updater }) {
|
addNewNotifications(result, older) {
|
||||||
const notification = this.idStore.get(id)
|
const { timestamp, data } = result
|
||||||
notification && updater(notification)
|
|
||||||
},
|
const notifications = older
|
||||||
addNewNotifications(result) {
|
? data
|
||||||
const { timestamp, data: notifications } = result
|
: [...data].reverse()
|
||||||
|
|
||||||
useUsersStore().addNewUsers({
|
useUsersStore().addNewUsers({
|
||||||
timestamp,
|
timestamp,
|
||||||
data: notifications.map((n) => n.from_profile),
|
data: notifications.map((n) => n.from_profile),
|
||||||
})
|
})
|
||||||
notifications.forEach(
|
|
||||||
(n) => (n.from_profile = useUsersStore().findUser(n.from_profile.id)),
|
notifications.forEach((n) => {
|
||||||
)
|
n.from_profile = useUsersStore().findUser(n.from_profile.id)
|
||||||
|
})
|
||||||
|
|
||||||
const validNotifications = notifications.filter((notification) => {
|
const validNotifications = notifications.filter((notification) => {
|
||||||
// If invalid notification, update ids but don't add it to store
|
// If invalid notification, update ids but don't add it to store
|
||||||
if (!isValidNotification(notification)) {
|
if (!isValidNotification(notification)) {
|
||||||
console.error('Invalid notification:', notification)
|
console.error('Invalid notification:', notification)
|
||||||
this.updateNotificationsMinMaxId(notification.id)
|
this.updateExtremes(notification.id)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
@ -178,12 +183,14 @@ export const useNotificationsStore = defineStore('notifications', {
|
||||||
|
|
||||||
// Only add a new notification if we don't have one for the same action
|
// Only add a new notification if we don't have one for the same action
|
||||||
if (!this.idStore.has(notification.id)) {
|
if (!this.idStore.has(notification.id)) {
|
||||||
this.updateNotificationsMinMaxId(notification.id)
|
this.updateExtremes(notification.id)
|
||||||
|
|
||||||
notifications.forEach((notification) => {
|
if (older) {
|
||||||
this.data.push(notification)
|
this.data.push(notification)
|
||||||
this.idStore.set(notification.id, notification)
|
} else {
|
||||||
})
|
this.data.unshift(notification)
|
||||||
|
}
|
||||||
|
this.idStore.set(notification.id, notification)
|
||||||
|
|
||||||
this.statusNotificationRelations.set(
|
this.statusNotificationRelations.set(
|
||||||
notification.status,
|
notification.status,
|
||||||
|
|
@ -192,7 +199,7 @@ export const useNotificationsStore = defineStore('notifications', {
|
||||||
|
|
||||||
maybeShowNotification(
|
maybeShowNotification(
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
Object.values(useSyncConfigStore().prefsStorage.simple.muteFilters),
|
Object.values(useSyncConfigStore().prefsStorage.simple.muteFilters ?? {}),
|
||||||
notification,
|
notification,
|
||||||
useI18nStore().i18n,
|
useI18nStore().i18n,
|
||||||
)
|
)
|
||||||
|
|
@ -225,26 +232,26 @@ export const useNotificationsStore = defineStore('notifications', {
|
||||||
|
|
||||||
markNotificationsAsSeen({
|
markNotificationsAsSeen({
|
||||||
id: this.maxId,
|
id: this.maxId,
|
||||||
credentials: useUsersStore().currentUser.credentials,
|
credentials: useOAuthStore().token,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
closeAllDesktopNotifications()
|
closeAllDesktopNotifications()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
markSingleNotificationAsSeen({ id }) {
|
markSingleNotificationAsSeen(id) {
|
||||||
const notification = this.idStore.get(id)
|
const notification = this.idStore.get(id)
|
||||||
if (notification) notification.seen = true
|
if (notification) notification.seen = true
|
||||||
|
|
||||||
markNotificationsAsSeen({
|
markNotificationsAsSeen({
|
||||||
single: true,
|
single: true,
|
||||||
id,
|
id,
|
||||||
credentials: useUsersStore().currentUser.credentials,
|
credentials: useOAuthStore().token,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
closeDesktopNotification(id)
|
closeDesktopNotification(id)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
dismissNotificationLocal(id) {
|
dismissNotificationLocal(id) {
|
||||||
this.data = this.data.filter((n) => n.id !== id)
|
this.idStore.delete(id)
|
||||||
delete this.idStore.delete(id)
|
this.syncOrder()
|
||||||
},
|
},
|
||||||
dismissNotification(id) {
|
dismissNotification(id) {
|
||||||
this.dismissNotificationLocal(id)
|
this.dismissNotificationLocal(id)
|
||||||
|
|
@ -254,10 +261,27 @@ export const useNotificationsStore = defineStore('notifications', {
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
syncOrder() {
|
||||||
|
this.minId = ''
|
||||||
|
this.maxId = ''
|
||||||
|
this.data = this.data.filter(({ id }) => {
|
||||||
|
const present = this.idStore.has(id)
|
||||||
|
if (present) {
|
||||||
|
this.updateExtremes(id) // Side-effect
|
||||||
|
}
|
||||||
|
return present
|
||||||
|
})
|
||||||
|
},
|
||||||
|
wipeStatuses(ids) {
|
||||||
|
const set = new Set(ids)
|
||||||
|
this.data.forEach((notification) => {
|
||||||
|
const status = isStatusNotification(notification.type) && notification.status
|
||||||
|
if (status && set.has(status.id)) {
|
||||||
|
this.idStore.delete(notification.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Misc
|
this.syncOrder()
|
||||||
setLoading(value) {
|
|
||||||
this.loading = value
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
import { useUsersStore } from 'src/stores/users.js'
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { search2 } from 'src/api/public.js'
|
import { search2, searchUsers } from 'src/api/public.js'
|
||||||
|
|
||||||
export const useSearchStore = defineStore('search', {
|
export const useSearchStore = defineStore('search', {
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import {
|
||||||
fetchRebloggedByUsers,
|
fetchRebloggedByUsers,
|
||||||
fetchStatus,
|
fetchStatus,
|
||||||
fetchStatusSource,
|
fetchStatusSource,
|
||||||
search2,
|
|
||||||
} from 'src/api/public.js'
|
} from 'src/api/public.js'
|
||||||
import {
|
import {
|
||||||
bookmarkStatus,
|
bookmarkStatus,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import { first, last, max, min } from 'lodash'
|
import { first, last } from 'lodash'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
import timelineFetcher from 'src/stores/fetchers/timeline_fetcher.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
|
||||||
import { TIMELINE_STREAM_MAP, useStreamingStore } from 'src/stores/streaming.js'
|
import { TIMELINE_STREAM_MAP, useStreamingStore } from 'src/stores/streaming.js'
|
||||||
import { useUsersStore } from 'src/stores/users.js'
|
|
||||||
|
|
||||||
import timelineFetcher from 'src/stores/fetchers/timeline_fetcher.js'
|
|
||||||
|
|
||||||
const emptyTl = (name, argument = null) => {
|
const emptyTl = (name, argument = null) => {
|
||||||
const result = {
|
const result = {
|
||||||
|
|
@ -164,7 +161,7 @@ export const useTimelinesStore = defineStore('timelines', {
|
||||||
try {
|
try {
|
||||||
this.deactivate(name, true)
|
this.deactivate(name, true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to deactivate timeline ${name}`)
|
console.error(`Failed to deactivate timeline ${name}:`, e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -178,7 +175,7 @@ export const useTimelinesStore = defineStore('timelines', {
|
||||||
showImmediately = false,
|
showImmediately = false,
|
||||||
noIdUpdate = false,
|
noIdUpdate = false,
|
||||||
pagination = {},
|
pagination = {},
|
||||||
older = false
|
older = false,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if (statuses.length === 0) return
|
if (statuses.length === 0) return
|
||||||
|
|
@ -195,10 +192,7 @@ export const useTimelinesStore = defineStore('timelines', {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!noIdUpdate) {
|
if (!noIdUpdate) {
|
||||||
this.updateTimelineExtremes(
|
this.updateTimelineExtremes(timeline, pagination)
|
||||||
timeline,
|
|
||||||
pagination,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const filtered = statuses.filter((id) => !timeline.statusIds.has(id))
|
const filtered = statuses.filter((id) => !timeline.statusIds.has(id))
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import {
|
||||||
fetchFriends,
|
fetchFriends,
|
||||||
fetchUser,
|
fetchUser,
|
||||||
fetchUserByName,
|
fetchUserByName,
|
||||||
searchUsers,
|
|
||||||
verifyCredentials,
|
verifyCredentials,
|
||||||
} from 'src/api/public.js'
|
} from 'src/api/public.js'
|
||||||
import {
|
import {
|
||||||
|
|
|
||||||
379
test/unit/specs/stores/notifications.spec.js
Normal file
379
test/unit/specs/stores/notifications.spec.js
Normal file
|
|
@ -0,0 +1,379 @@
|
||||||
|
import { createTestingPinia } from '@pinia/testing'
|
||||||
|
import { setActivePinia } from 'pinia'
|
||||||
|
|
||||||
|
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
import { useReportsStore } from 'src/stores/reports.js'
|
||||||
|
import { useStreamingStore } from 'src/stores/streaming.js'
|
||||||
|
import { useI18nStore } from 'src/stores/i18n.js'
|
||||||
|
|
||||||
|
import * as USER_API from 'src/api/user.js'
|
||||||
|
|
||||||
|
const userId = '1'
|
||||||
|
const userScreenName = 'user'
|
||||||
|
const userName = 'Guy'
|
||||||
|
const userUrl = 'http://localhost/user'
|
||||||
|
|
||||||
|
const mockMastoAPIUser = ({
|
||||||
|
screen_name = userScreenName,
|
||||||
|
name = userName,
|
||||||
|
url = userUrl,
|
||||||
|
id = userId,
|
||||||
|
} = {}) => ({
|
||||||
|
id,
|
||||||
|
acct: screen_name,
|
||||||
|
display_name: name,
|
||||||
|
fields: [],
|
||||||
|
avatar: '',
|
||||||
|
url,
|
||||||
|
pleroma: {
|
||||||
|
emoji_reactions: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const mockUser = ({
|
||||||
|
screen_name = userScreenName,
|
||||||
|
id = userId,
|
||||||
|
name = userName,
|
||||||
|
url = userUrl,
|
||||||
|
} = {}) => ({
|
||||||
|
_original: mockMastoAPIUser({
|
||||||
|
screen_name,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
}),
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
screen_name,
|
||||||
|
url,
|
||||||
|
relationship: undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
const mockStatus = ({
|
||||||
|
id = '1',
|
||||||
|
text,
|
||||||
|
summary,
|
||||||
|
type = 'status',
|
||||||
|
statusUser = mockUser(),
|
||||||
|
} = {}) => ({
|
||||||
|
id,
|
||||||
|
user: statusUser,
|
||||||
|
summary: summary ?? `Summary number ${id}`,
|
||||||
|
name: 'status',
|
||||||
|
text: text ?? `Text number ${id}`,
|
||||||
|
uri: '',
|
||||||
|
type,
|
||||||
|
attentions: [],
|
||||||
|
statusnet_conversation_id: 'c1',
|
||||||
|
emoji_reactions: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const mockStatusNotification = ({
|
||||||
|
id = '1',
|
||||||
|
type = 'like',
|
||||||
|
status = mockStatus({ id }),
|
||||||
|
seen = false,
|
||||||
|
user = mockUser(),
|
||||||
|
} = {}) => ({
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
seen,
|
||||||
|
user,
|
||||||
|
from_profile: user,
|
||||||
|
})
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = (method = 'POST') => ({
|
||||||
|
method,
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Notifications store', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||||
|
useI18nStore().i18n = { t: () => { /* no-op */} }
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('activate', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
const sub = vi.fn()
|
||||||
|
useStreamingStore().addSubscriber = sub
|
||||||
|
|
||||||
|
store.activate()
|
||||||
|
|
||||||
|
expect(sub).to.have.been.called
|
||||||
|
expect(store.fetcher).to.not.be.null
|
||||||
|
expect(store.socket).to.not.be.null
|
||||||
|
|
||||||
|
store.deactivate()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deactivate', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
const unsub = vi.fn()
|
||||||
|
useStreamingStore().removeSubscriber = unsub
|
||||||
|
|
||||||
|
store.activate()
|
||||||
|
// Checking so that they were set properly before
|
||||||
|
// since reset changes them to ''
|
||||||
|
store.maxId = '2'
|
||||||
|
store.minId = '1'
|
||||||
|
store.idStore = new Map()
|
||||||
|
store.idStore.set('1', {})
|
||||||
|
store.idStore.set('2', {})
|
||||||
|
store.deactivate()
|
||||||
|
|
||||||
|
expect(unsub).to.have.been.called
|
||||||
|
expect(store.fetcher).to.be.null
|
||||||
|
expect(store.socket).to.be.null
|
||||||
|
expect(store.idStore).to.have.length(0)
|
||||||
|
expect(store).to.have.property('maxId', '')
|
||||||
|
expect(store).to.have.property('minId', '')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updateExtremes should update min and max ids', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
store.maxId = '10'
|
||||||
|
store.minId = '05'
|
||||||
|
|
||||||
|
store.updateExtremes('04')
|
||||||
|
store.updateExtremes('11')
|
||||||
|
|
||||||
|
expect(store).to.have.property('maxId', '11')
|
||||||
|
expect(store).to.have.property('minId', '04')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addNewNotifications', () => {
|
||||||
|
it('adds notifications to the list', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: 'a' }),
|
||||||
|
mockStatusNotification({ id: 'b' }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
// must be ordered
|
||||||
|
expect(store.data.map(({ id }) => id)).to.eql(['a', 'b'])
|
||||||
|
expect(store.idStore).to.have.keys(['a', 'b'])
|
||||||
|
expect(store).to.have.property('maxId', 'b')
|
||||||
|
expect(store).to.have.property('minId', 'a')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ignores duplicates', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: '2' }),
|
||||||
|
mockStatusNotification({ id: '1' }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: '3' }),
|
||||||
|
mockStatusNotification({ id: '2' }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
// must be ordered
|
||||||
|
expect(store.data.map(({ id }) => id)).to.eql(['3', '2', '1'])
|
||||||
|
expect(store.idStore).to.have.keys(['1', '2', '3'])
|
||||||
|
expect(store).to.have.property('maxId', '3')
|
||||||
|
expect(store).to.have.property('minId', '1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('appends notifications if fetching older', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: '4' }),
|
||||||
|
mockStatusNotification({ id: '3' }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: '2' }),
|
||||||
|
mockStatusNotification({ id: '1' }),
|
||||||
|
],
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
// must be ordered
|
||||||
|
expect(store.data.map(({ id }) => id)).to.eql(['4', '3', '2', '1'])
|
||||||
|
expect(store.idStore).to.have.keys(['1', '2', '3', '4'])
|
||||||
|
expect(store).to.have.property('maxId', '4')
|
||||||
|
expect(store).to.have.property('minId', '1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update usersStore', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
const mock = vi.spyOn(useUsersStore(), 'addNewUsers')
|
||||||
|
const mockedNotification = mockStatusNotification({ type: 'follow' })
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1337,
|
||||||
|
data: [mockedNotification],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(mock).to.have.been.called
|
||||||
|
expect(mock.mock.calls[0][0]).to.have.property('timestamp', 1337)
|
||||||
|
expect(mock.mock.calls[0][0].data[0]).to.eql(mockedNotification.from_profile)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should update reportsStore', (notificationType) => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
const mock = vi.spyOn(useReportsStore(), 'addReport')
|
||||||
|
const mockedNotification = mockStatusNotification({ type: 'pleroma:report' })
|
||||||
|
mockedNotification.report = { data: '123' }
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1337,
|
||||||
|
data: [mockedNotification],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(mock).to.have.been.calledWith({ data: '123' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
'like',
|
||||||
|
'mention',
|
||||||
|
'status',
|
||||||
|
'repeat',
|
||||||
|
'pleroma:emoji_reaction',
|
||||||
|
'poll',
|
||||||
|
])('should update statusesStore on %s notification', (notificationType) => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
const mock = vi.fn()
|
||||||
|
useStatusesStore().addNewStatuses = mock
|
||||||
|
const mockedNotification = mockStatusNotification({ type: notificationType })
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1337,
|
||||||
|
data: [mockedNotification],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(mock).to.have.been.called
|
||||||
|
expect(mock.mock.calls[0][0]).to.have.property('timestamp', 1337)
|
||||||
|
expect(mock.mock.calls[0][0].statuses[0]).to.eql(mockedNotification.status)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('wipeStatuses', () => {
|
||||||
|
it('clears all statuses', () => {
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: 'n2', status: mockStatus({ id: 's2' }) }),
|
||||||
|
mockStatusNotification({ id: 'n1', status: mockStatus({ id: 's1' }) }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
store.wipeStatuses(['s2'])
|
||||||
|
expect(store.idStore).to.not.have.members('n2')
|
||||||
|
expect(store.data.map(({ id }) => id)).to.eql(['n1'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('read/dismiss', () => {
|
||||||
|
it('read single', () => {
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify({ ok: 'ok' }), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
const mockedNotification = mockStatusNotification()
|
||||||
|
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1337,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: 'n3' }),
|
||||||
|
mockStatusNotification({ id: 'n2' }),
|
||||||
|
mockStatusNotification({ id: 'n1' }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(store.data[1]).to.have.property('seen', false)
|
||||||
|
|
||||||
|
store.markSingleNotificationAsSeen('n2')
|
||||||
|
|
||||||
|
expect(store.data[0]).to.have.property('seen', false)
|
||||||
|
expect(store.data[1]).to.have.property('seen', true)
|
||||||
|
expect(store.data[0]).to.have.property('seen', false)
|
||||||
|
|
||||||
|
const calls = mockFetch.mock.calls
|
||||||
|
expect(calls).to.have.length(1)
|
||||||
|
const callOne = calls[0]
|
||||||
|
expect(callOne[0]).to.eql(USER_API.NOTIFICATION_READ_URL)
|
||||||
|
const formData = Object.fromEntries(callOne[1].body.entries())
|
||||||
|
expect(formData).to.eql({ id: 'n2' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('read all', () => {
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
mockFetch.mockResolvedValueOnce(
|
||||||
|
new Response(JSON.stringify({ ok: 'ok' }), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
vi.stubGlobal('fetch', mockFetch)
|
||||||
|
|
||||||
|
const store = useNotificationsStore()
|
||||||
|
const mockedNotification = mockStatusNotification()
|
||||||
|
|
||||||
|
// FormData is weird to test
|
||||||
|
store.addNewNotifications({
|
||||||
|
timestamp: 1337,
|
||||||
|
data: [
|
||||||
|
mockStatusNotification({ id: 'n3' }),
|
||||||
|
mockStatusNotification({ id: 'n2' }),
|
||||||
|
mockStatusNotification({ id: 'n1' }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(store.data[0]).to.have.property('seen', false)
|
||||||
|
expect(store.data[1]).to.have.property('seen', false)
|
||||||
|
expect(store.data[2]).to.have.property('seen', false)
|
||||||
|
|
||||||
|
store.markNotificationsAsSeen()
|
||||||
|
|
||||||
|
expect(store.data[0]).to.have.property('seen', true)
|
||||||
|
expect(store.data[1]).to.have.property('seen', true)
|
||||||
|
expect(store.data[2]).to.have.property('seen', true)
|
||||||
|
|
||||||
|
// FormData is weird to test
|
||||||
|
const calls = mockFetch.mock.calls
|
||||||
|
expect(calls).to.have.length(1)
|
||||||
|
const callOne = calls[0]
|
||||||
|
expect(callOne[0]).to.eql(USER_API.NOTIFICATION_READ_URL)
|
||||||
|
const formData = Object.fromEntries(callOne[1].body.entries())
|
||||||
|
expect(formData).to.eql({ max_id: 'n3' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { createTestingPinia } from '@pinia/testing'
|
import { createTestingPinia } from '@pinia/testing'
|
||||||
import { setActivePinia } from 'pinia'
|
import { setActivePinia } from 'pinia'
|
||||||
|
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
|
||||||
import { useTimelinesStore } from 'src/stores/timelines.js'
|
|
||||||
import { useStreamingStore } from 'src/stores/streaming.js'
|
import { useStreamingStore } from 'src/stores/streaming.js'
|
||||||
|
import { useTimelinesStore } from 'src/stores/timelines.js'
|
||||||
|
|
||||||
describe('Timelines store', () => {
|
describe('Timelines store', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -49,14 +48,15 @@ describe('Timelines store', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const unsub = vi.fn()
|
const unsub = vi.fn()
|
||||||
useStreamingStore().removeSubscriber = unsub
|
useStreamingStore().removeSubscriber = unsub
|
||||||
|
useStreamingStore().addSubscriber = vi.fn()
|
||||||
|
|
||||||
store.activate('friends', undefined, true)
|
store.activate('friends', undefined, true)
|
||||||
// Checking so that they were set properly before
|
// Checking so that they were set properly before
|
||||||
// since reset changes them to ''
|
// since reset changes them to ''
|
||||||
store.friends.maxId = '3'
|
store.friends.maxId = '3'
|
||||||
store.friends.minId = '4'
|
store.friends.minId = '4'
|
||||||
store.friends.statusIds = new Set(['3','4'])
|
store.friends.statusIds = new Set(['3', '4'])
|
||||||
store.friends.visibleStausIds = new Set(['3','4'])
|
store.friends.visibleStausIds = new Set(['3', '4'])
|
||||||
store.deactivate('friends', true)
|
store.deactivate('friends', true)
|
||||||
|
|
||||||
expect(unsub).to.have.been.called
|
expect(unsub).to.have.been.called
|
||||||
|
|
@ -72,14 +72,15 @@ describe('Timelines store', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const unsub = vi.fn()
|
const unsub = vi.fn()
|
||||||
useStreamingStore().removeSubscriber = unsub
|
useStreamingStore().removeSubscriber = unsub
|
||||||
|
useStreamingStore().addSubscriber = vi.fn()
|
||||||
|
|
||||||
store.activate('user', '1')
|
store.activate('user', '1')
|
||||||
// Checking so that they were set properly before
|
// Checking so that they were set properly before
|
||||||
// since reset changes them to ''
|
// since reset changes them to ''
|
||||||
store.user.maxId = '3'
|
store.user.maxId = '3'
|
||||||
store.user.minId = '4'
|
store.user.minId = '4'
|
||||||
store.user.statusIds = new Set(['3','4'])
|
store.user.statusIds = new Set(['3', '4'])
|
||||||
store.user.visibleStausIds = new Set(['3','4'])
|
store.user.visibleStausIds = new Set(['3', '4'])
|
||||||
store.deactivate('user')
|
store.deactivate('user')
|
||||||
|
|
||||||
expect(unsub).to.not.have.been.called
|
expect(unsub).to.not.have.been.called
|
||||||
|
|
@ -96,7 +97,7 @@ describe('Timelines store', () => {
|
||||||
it('should derive extremes from data', () => {
|
it('should derive extremes from data', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const timeline = useTimelinesStore().friends
|
const timeline = useTimelinesStore().friends
|
||||||
timeline.order = ['4','1','3','2']
|
timeline.order = ['4', '1', '3', '2']
|
||||||
timeline.statusesIds = new Set(timeline.order)
|
timeline.statusesIds = new Set(timeline.order)
|
||||||
store.updateTimelineExtremes(timeline)
|
store.updateTimelineExtremes(timeline)
|
||||||
|
|
||||||
|
|
@ -107,10 +108,7 @@ describe('Timelines store', () => {
|
||||||
it('should use extremes from pagination', () => {
|
it('should use extremes from pagination', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const timeline = useTimelinesStore().friends
|
const timeline = useTimelinesStore().friends
|
||||||
store.updateTimelineExtremes(
|
store.updateTimelineExtremes(timeline, { maxId: '1', minId: '2' })
|
||||||
timeline,
|
|
||||||
{ maxId: '1', minId: '2' }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Min and max are swapped!
|
// Min and max are swapped!
|
||||||
expect(store.friends).to.have.property('maxId', '2')
|
expect(store.friends).to.have.property('maxId', '2')
|
||||||
|
|
@ -121,17 +119,13 @@ describe('Timelines store', () => {
|
||||||
describe('addStatusesToTimeline', () => {
|
describe('addStatusesToTimeline', () => {
|
||||||
it('adds the status to the given timeline', () => {
|
it('adds the status to the given timeline', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses = ['1','2','3']
|
const statuses = ['1', '2', '3']
|
||||||
|
|
||||||
store.activate('list', '1')
|
store.activate('list', '1')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('list', '1', {
|
||||||
'list',
|
statuses,
|
||||||
'1',
|
pagination: { minId: '1', maxId: '3' },
|
||||||
{
|
})
|
||||||
statuses,
|
|
||||||
pagination: { minId: '1', maxId: '3' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(store.list.order).to.eql(statuses)
|
expect(store.list.order).to.eql(statuses)
|
||||||
expect(store.list.statusIds).to.eql(new Set(statuses))
|
expect(store.list.statusIds).to.eql(new Set(statuses))
|
||||||
|
|
@ -143,25 +137,17 @@ describe('Timelines store', () => {
|
||||||
|
|
||||||
it('ignores duplicates', () => {
|
it('ignores duplicates', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses = ['1','2','3']
|
const statuses = ['1', '2', '3']
|
||||||
|
|
||||||
store.activate('list', '1')
|
store.activate('list', '1')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('list', '1', {
|
||||||
'list',
|
statuses,
|
||||||
'1',
|
pagination: { minId: '1', maxId: '3' },
|
||||||
{
|
})
|
||||||
statuses,
|
store.addStatusesToTimeline('list', '1', {
|
||||||
pagination: { minId: '1', maxId: '3' }
|
statuses,
|
||||||
}
|
pagination: { minId: '1', maxId: '3' },
|
||||||
)
|
})
|
||||||
store.addStatusesToTimeline(
|
|
||||||
'list',
|
|
||||||
'1',
|
|
||||||
{
|
|
||||||
statuses,
|
|
||||||
pagination: { minId: '1', maxId: '3' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(store.list.order).to.eql(statuses)
|
expect(store.list.order).to.eql(statuses)
|
||||||
expect(store.list.statusIds).to.eql(new Set(statuses))
|
expect(store.list.statusIds).to.eql(new Set(statuses))
|
||||||
|
|
@ -173,18 +159,14 @@ describe('Timelines store', () => {
|
||||||
|
|
||||||
it('adds the status the given timeline, directly visible', () => {
|
it('adds the status the given timeline, directly visible', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses = ['1','2','3']
|
const statuses = ['1', '2', '3']
|
||||||
|
|
||||||
store.activate('list', '1')
|
store.activate('list', '1')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('list', '1', {
|
||||||
'list',
|
statuses,
|
||||||
'1',
|
showImmediately: true,
|
||||||
{
|
pagination: { minId: '1', maxId: '3' },
|
||||||
statuses,
|
})
|
||||||
showImmediately: true,
|
|
||||||
pagination: { minId: '1', maxId: '3' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(store.list.order).to.eql(statuses)
|
expect(store.list.order).to.eql(statuses)
|
||||||
expect(store.list.statusIds).to.eql(new Set(statuses))
|
expect(store.list.statusIds).to.eql(new Set(statuses))
|
||||||
|
|
@ -196,18 +178,14 @@ describe('Timelines store', () => {
|
||||||
|
|
||||||
it('does not update the maxId when the noIdUpdate flag is set', () => {
|
it('does not update the maxId when the noIdUpdate flag is set', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses = ['1','2','3']
|
const statuses = ['1', '2', '3']
|
||||||
|
|
||||||
store.activate('list', '1')
|
store.activate('list', '1')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('list', '1', {
|
||||||
'list',
|
statuses,
|
||||||
'1',
|
noIdUpdate: true,
|
||||||
{
|
pagination: { minId: '1', maxId: '3' },
|
||||||
statuses,
|
})
|
||||||
noIdUpdate: true,
|
|
||||||
pagination: { minId: '1', maxId: '3' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(store.list.order).to.eql(statuses)
|
expect(store.list.order).to.eql(statuses)
|
||||||
expect(store.list.statusIds).to.eql(new Set(statuses))
|
expect(store.list.statusIds).to.eql(new Set(statuses))
|
||||||
|
|
@ -219,18 +197,14 @@ describe('Timelines store', () => {
|
||||||
|
|
||||||
it('does not update timeline if it belongs to a different arugment', () => {
|
it('does not update timeline if it belongs to a different arugment', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses = ['1','2','3']
|
const statuses = ['1', '2', '3']
|
||||||
|
|
||||||
store.activate('list', '1')
|
store.activate('list', '1')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('list', '2', {
|
||||||
'list',
|
statuses,
|
||||||
'2',
|
noIdUpdate: true,
|
||||||
{
|
pagination: { minId: '1', maxId: '3' },
|
||||||
statuses,
|
})
|
||||||
noIdUpdate: true,
|
|
||||||
pagination: { minId: '1', maxId: '3' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(store.list.order).to.eql([])
|
expect(store.list.order).to.eql([])
|
||||||
expect(store.list.statusIds).to.eql(new Set())
|
expect(store.list.statusIds).to.eql(new Set())
|
||||||
|
|
@ -242,26 +216,18 @@ describe('Timelines store', () => {
|
||||||
|
|
||||||
it('prepends timeline with new statuses', () => {
|
it('prepends timeline with new statuses', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses1 = ['3','2','1']
|
const statuses1 = ['3', '2', '1']
|
||||||
const statuses2 = ['6','5','4']
|
const statuses2 = ['6', '5', '4']
|
||||||
|
|
||||||
store.activate('list', '1')
|
store.activate('list', '1')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('list', '1', {
|
||||||
'list',
|
statuses: statuses1,
|
||||||
'1',
|
pagination: { minId: '3', maxId: '1' },
|
||||||
{
|
})
|
||||||
statuses: statuses1,
|
store.addStatusesToTimeline('list', '1', {
|
||||||
pagination: { minId: '3', maxId: '1' }
|
statuses: statuses2,
|
||||||
}
|
pagination: { minId: '6', maxId: '4' },
|
||||||
)
|
})
|
||||||
store.addStatusesToTimeline(
|
|
||||||
'list',
|
|
||||||
'1',
|
|
||||||
{
|
|
||||||
statuses: statuses2,
|
|
||||||
pagination: { minId: '6', maxId: '4' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const newOrder = [...statuses2, ...statuses1]
|
const newOrder = [...statuses2, ...statuses1]
|
||||||
expect(store.list.order).to.eql(newOrder)
|
expect(store.list.order).to.eql(newOrder)
|
||||||
|
|
@ -274,27 +240,19 @@ describe('Timelines store', () => {
|
||||||
|
|
||||||
it('appends timeline with new statuses if fetching older', () => {
|
it('appends timeline with new statuses if fetching older', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses1 = ['6','5','4']
|
const statuses1 = ['6', '5', '4']
|
||||||
const statuses2 = ['3','2','1']
|
const statuses2 = ['3', '2', '1']
|
||||||
|
|
||||||
store.activate('list', '1')
|
store.activate('list', '1')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('list', '1', {
|
||||||
'list',
|
statuses: statuses1,
|
||||||
'1',
|
pagination: { minId: '6', maxId: '4' },
|
||||||
{
|
})
|
||||||
statuses: statuses1,
|
store.addStatusesToTimeline('list', '1', {
|
||||||
pagination: { minId: '6', maxId: '4' },
|
statuses: statuses2,
|
||||||
}
|
pagination: { minId: '3', maxId: '1' },
|
||||||
)
|
older: true,
|
||||||
store.addStatusesToTimeline(
|
})
|
||||||
'list',
|
|
||||||
'1',
|
|
||||||
{
|
|
||||||
statuses: statuses2,
|
|
||||||
pagination: { minId: '3', maxId: '1' },
|
|
||||||
older: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const newOrder = [...statuses1, ...statuses2]
|
const newOrder = [...statuses1, ...statuses2]
|
||||||
expect(store.list.order).to.eql(newOrder)
|
expect(store.list.order).to.eql(newOrder)
|
||||||
|
|
@ -309,17 +267,13 @@ describe('Timelines store', () => {
|
||||||
describe('showNewStatuses', () => {
|
describe('showNewStatuses', () => {
|
||||||
it('resets counter and makes all ids visible', () => {
|
it('resets counter and makes all ids visible', () => {
|
||||||
const store = useTimelinesStore()
|
const store = useTimelinesStore()
|
||||||
const statuses = ['1','2','3']
|
const statuses = ['1', '2', '3']
|
||||||
|
|
||||||
store.activate('public')
|
store.activate('public')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('public', undefined, {
|
||||||
'public',
|
statuses,
|
||||||
undefined,
|
pagination: { minId: '1', maxId: '3' },
|
||||||
{
|
})
|
||||||
statuses,
|
|
||||||
pagination: { minId: '1', maxId: '3' }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(store.public.statusIds).to.eql(new Set(statuses))
|
expect(store.public.statusIds).to.eql(new Set(statuses))
|
||||||
expect(store.public.visibleStatusIds).to.eql(new Set())
|
expect(store.public.visibleStatusIds).to.eql(new Set())
|
||||||
|
|
@ -336,20 +290,12 @@ describe('Timelines store', () => {
|
||||||
|
|
||||||
store.activate('friends')
|
store.activate('friends')
|
||||||
store.activate('public')
|
store.activate('public')
|
||||||
store.addStatusesToTimeline(
|
store.addStatusesToTimeline('public', undefined, {
|
||||||
'public',
|
statuses: ['1', '2', '3', '0'],
|
||||||
undefined,
|
})
|
||||||
{
|
store.addStatusesToTimeline('friends', undefined, {
|
||||||
statuses: ['1','2','3','0']
|
statuses: ['5', '0', '9', '1'],
|
||||||
}
|
})
|
||||||
)
|
|
||||||
store.addStatusesToTimeline(
|
|
||||||
'friends',
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
statuses: ['5','0','9','1']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
store.wipeStatuses(['0'])
|
store.wipeStatuses(['0'])
|
||||||
expect(store.friends.statusIds).to.not.have.members('0')
|
expect(store.friends.statusIds).to.not.have.members('0')
|
||||||
expect(store.public.statusIds).to.not.have.members('0')
|
expect(store.public.statusIds).to.not.have.members('0')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue