Page MenuHomePhorge

No OneTemporary

Size
26 KB
Referenced Files
None
Subscribers
None
diff --git a/src/App.js b/src/App.js
index b018929d9d..095c5df80c 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,107 +1,106 @@
import UserPanel from './components/user_panel/user_panel.vue'
import NavPanel from './components/nav_panel/nav_panel.vue'
import Notifications from './components/notifications/notifications.vue'
import UserFinder from './components/user_finder/user_finder.vue'
import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'
import FeaturesPanel from './components/features_panel/features_panel.vue'
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
import ChatPanel from './components/chat_panel/chat_panel.vue'
import MediaModal from './components/media_modal/media_modal.vue'
import SideDrawer from './components/side_drawer/side_drawer.vue'
-import { unseenNotificationsFromStore } from './services/notification_utils/notification_utils'
export default {
name: 'app',
components: {
UserPanel,
NavPanel,
Notifications,
UserFinder,
InstanceSpecificPanel,
FeaturesPanel,
WhoToFollowPanel,
ChatPanel,
MediaModal,
SideDrawer
},
data: () => ({
mobileActivePanel: 'timeline',
finderHidden: true,
supportsMask: window.CSS && window.CSS.supports && (
window.CSS.supports('mask-size', 'contain') ||
window.CSS.supports('-webkit-mask-size', 'contain') ||
window.CSS.supports('-moz-mask-size', 'contain') ||
window.CSS.supports('-ms-mask-size', 'contain') ||
window.CSS.supports('-o-mask-size', 'contain')
)
}),
created () {
// Load the locale from the storage
this.$i18n.locale = this.$store.state.config.interfaceLanguage
},
computed: {
currentUser () { return this.$store.state.users.currentUser },
background () {
return this.currentUser.background_image || this.$store.state.instance.background
},
enableMask () { return this.supportsMask && this.$store.state.instance.logoMask },
logoStyle () {
return {
'visibility': this.enableMask ? 'hidden' : 'visible'
}
},
logoMaskStyle () {
return this.enableMask ? {
'mask-image': `url(${this.$store.state.instance.logo})`
} : {
'background-color': this.enableMask ? '' : 'transparent'
}
},
logoBgStyle () {
return Object.assign({
'margin': `${this.$store.state.instance.logoMargin} 0`,
opacity: this.finderHidden ? 1 : 0
}, this.enableMask ? {} : {
'background-color': this.enableMask ? '' : 'transparent'
})
},
logo () { return this.$store.state.instance.logo },
bgStyle () {
return {
'background-image': `url(${this.background})`
}
},
bgAppStyle () {
return {
'--body-background-image': `url(${this.background})`
}
},
sitename () { return this.$store.state.instance.name },
chat () { return this.$store.state.chat.channel.state === 'joined' },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel },
unseenNotifications () {
// TODO: Fix
return []
},
unseenNotificationsCount () {
return this.unseenNotifications.length
},
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }
},
methods: {
scrollToTop () {
window.scrollTo(0, 0)
},
logout () {
this.$router.replace('/main/public')
this.$store.dispatch('logoutUser')
},
onFinderToggled (hidden) {
this.finderHidden = hidden
},
toggleMobileSidebar () {
this.$refs.sideDrawer.toggleDrawer()
}
}
}
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index c25bf74016..38ef6cb853 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -1,59 +1,62 @@
import Notification from '../notification/notification.vue'
+import { getNotificationVisibleTypes } from '../../services/notification_helper/notification_helper'
-import { map } from 'lodash'
+import values from 'lodash/values'
+import orderBy from 'lodash/orderBy'
const Notifications = {
created () {
this.$store.dispatch('startFetchingNotifications')
},
- data () {
- return {
- bottomedOut: false
- }
- },
computed: {
- error () {
- // TODO: fix
- return false
- },
- unseenNotifications () {
- // TODO: fix
- return []
+ visibleTypes () {
+ return getNotificationVisibleTypes(this.$store.state.config.notificationVisibility)
},
visibleNotifications () {
- const notifications = map(this.$store.state.notifications.visibleNotificationIds, (id) => {
- return this.$store.state.notifications.notifications[id]
- })
- return notifications
+ const notifications = values(this.$store.state.notifications.notifications)
+ const filteredNotifications = notifications.filter(notification => this.visibleTypes.includes(notification.type))
+ const sortedNotifications = orderBy(filteredNotifications, ['seen', 'id'], ['asc', 'desc'])
+ return sortedNotifications
+ },
+ unseenNotifications () {
+ return this.visibleNotifications.filter(notification => !notification.seen)
},
unseenCount () {
return this.unseenNotifications.length
},
loading () {
- // TODO: Fix
- return false
+ return this.$store.state.notifications.loading
+ },
+ error () {
+ return this.$store.state.notifications.error
+ },
+ bottomedOut () {
+ return this.$store.state.notifications.bottomedOut
}
},
components: {
Notification
},
watch: {
unseenCount (count) {
if (count > 0) {
this.$store.dispatch('setPageTitle', `(${count})`)
} else {
this.$store.dispatch('setPageTitle', '')
}
}
},
methods: {
markAsSeen () {
- this.$store.dispatch('markNotificationsAsSeen', this.visibleNotifications)
+ this.$store.dispatch('markNotificationsAsSeen')
},
fetchOlderNotifications () {
- // TODO: Repair
+ this.$store.dispatch('fetchOlderNotifications')
}
+ },
+ destroyed () {
+ this.$store.dispatch('stopFetchingNotifications')
}
}
export default Notifications
diff --git a/src/i18n/i18n.js b/src/i18n/i18n.js
new file mode 100644
index 0000000000..f106259692
--- /dev/null
+++ b/src/i18n/i18n.js
@@ -0,0 +1,14 @@
+import Vue from 'vue'
+import VueI18n from 'vue-i18n'
+import messages from './messages.js'
+
+Vue.use(VueI18n)
+
+const currentLocale = (window.navigator.language || 'en').split('-')[0]
+
+export default new VueI18n({
+ // By default, use the browser locale, we will update it if neccessary
+ locale: currentLocale,
+ fallbackLocale: 'en',
+ messages
+})
diff --git a/src/main.js b/src/main.js
index beab301188..28dd710589 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,86 +1,76 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import interfaceModule from './modules/interface.js'
import instanceModule from './modules/instance.js'
import statusesModule from './modules/statuses.js'
import notificationsModule from './modules/notifications.js'
import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
import configModule from './modules/config.js'
import chatModule from './modules/chat.js'
import oauthModule from './modules/oauth.js'
import mediaViewerModule from './modules/media_viewer.js'
import oauthTokensModule from './modules/oauth_tokens.js'
import VueTimeago from 'vue-timeago'
-import VueI18n from 'vue-i18n'
import createPersistedState from './lib/persisted_state.js'
import pushNotifications from './lib/push_notifications_plugin.js'
-import messages from './i18n/messages.js'
-
import VueChatScroll from 'vue-chat-scroll'
import afterStoreSetup from './boot/after_store.js'
+import i18n from './i18n/i18n.js'
const currentLocale = (window.navigator.language || 'en').split('-')[0]
Vue.use(Vuex)
Vue.use(VueRouter)
Vue.use(VueTimeago, {
locale: currentLocale === 'ja' ? 'ja' : 'en',
locales: {
'en': require('../static/timeago-en.json'),
'ja': require('../static/timeago-ja.json')
}
})
-Vue.use(VueI18n)
Vue.use(VueChatScroll)
-const i18n = new VueI18n({
- // By default, use the browser locale, we will update it if neccessary
- locale: currentLocale,
- fallbackLocale: 'en',
- messages
-})
-
const persistedStateOptions = {
paths: [
'config',
'users.lastLoginName',
'oauth'
]
};
(async function () {
const persistedState = await createPersistedState(persistedStateOptions)
const store = new Vuex.Store({
modules: {
interface: interfaceModule,
instance: instanceModule,
statuses: statusesModule,
notifications: notificationsModule,
users: usersModule,
api: apiModule,
config: configModule,
chat: chatModule,
oauth: oauthModule,
mediaViewer: mediaViewerModule,
oauthTokens: oauthTokensModule
},
plugins: [persistedState, pushNotifications],
strict: false // Socket modifies itself, let's ignore this for now.
// strict: process.env.NODE_ENV !== 'production'
})
afterStoreSetup({ store, i18n })
})()
// These are inlined by webpack's DefinePlugin
/* eslint-disable */
window.___pleromafe_mode = process.env
window.___pleromafe_commit_hash = COMMIT_HASH
window.___pleromafe_dev_overrides = DEV_OVERRIDES
diff --git a/src/modules/api.js b/src/modules/api.js
index 8a0081fc2b..5de5f5aa83 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -1,79 +1,71 @@
import TimelineFetcher from '../services/timeline_fetcher/timeline_fetcher.service.js'
-import NotificationsFetcher from '../services/notifications_fetcher/notifications_fetcher.service.js'
import ApiUtils from '../services/new_api/utils.js'
import parseLinkHeader from 'parse-link-header'
const Api = {
state: {
fetchers: {}
},
mutations: {
setFetcher (state, { fetcher, timelineName }) {
state.fetchers[timelineName] = fetcher
}
},
actions: {
- async startFetchingNotifications ({ state, rootState, commit, dispatch }) {
- if (state.fetchers['user_notifications']) {
- return
- }
- const fetcher = await NotificationsFetcher.create({ state: rootState, commit, dispatch })
- commit('setFetcher', { fetcher, timelineName: 'user_notifications' })
- },
async startFetchingTimeline ({ state, rootState, commit, dispatch }, options) {
if (state.fetchers[options.timelineName]) {
return
}
const fetcher = await TimelineFetcher.create({ state: rootState, commit, dispatch }, options)
commit('setFetcher', { fetcher, timelineName: options.timelineName })
},
// TODO: DRY up.
async fetchPrevForTimeline ({ commit, rootState, dispatch }, { timelineName }) {
const store = { commit, state: rootState, dispatch }
const data = await ApiUtils.request({
store,
fullUrl: store.state.statuses.timelines[timelineName].links.prev.url
})
const statuses = await data.json()
const links = parseLinkHeader(data.headers.get('link'))
store.dispatch('addNewStatusesToTimeline', { statuses, timelineName })
// We don't always get new link headers.
if (links && links.prev) {
store.commit('setPrevLinkForTimeline', { link: links.prev, timelineName })
}
},
async fetchNextForTimeline ({ commit, rootState, dispatch }, { timelineName }) {
const store = { commit, state: rootState, dispatch }
// We don't want to fetch next if the previous fetching is still in progress
if (store.state.statuses.timelines[timelineName].loading) {
return
}
store.dispatch('startFetchingNextForTimeline', { timelineName })
const data = await ApiUtils.request({
store,
fullUrl: store.state.statuses.timelines[timelineName].links.next.url
})
const statuses = await data.json()
const links = parseLinkHeader(data.headers.get('link'))
store.dispatch('addNewStatusesToTimeline', { statuses, timelineName })
store.dispatch('showOldStatusesForTimeline', { timelineName, statuses })
// We don't always get new link headers.
if (links && links.next) {
store.commit('setNextLinkForTimeline', { link: links.next, timelineName })
}
store.dispatch('completeFetchingNextForTimeline', { timelineName })
},
stopFetchingTimeline ({ state, commit }, { timelineName }) {
const fetcher = state.fetchers[timelineName]
clearInterval(fetcher)
commit('setFetcher', { fetcher: false, timelineName })
}
}
}
export default Api
diff --git a/src/modules/notifications.js b/src/modules/notifications.js
index 409fb423d6..f4e6e411a3 100644
--- a/src/modules/notifications.js
+++ b/src/modules/notifications.js
@@ -1,31 +1,139 @@
-import { map, each, merge, uniq } from 'lodash'
import Vue from 'vue'
+import map from 'lodash/map'
+import mapValues from 'lodash/mapValues'
+import each from 'lodash/each'
+import merge from 'lodash/merge'
+import filter from 'lodash/filter'
+import keys from 'lodash/keys'
+import Notifications from '../services/new_api/notifications'
+import { getNotificationVisibleTypes, showDesktopPushNotification } from '../services/notification_helper/notification_helper'
+
+async function fetchAndUpdate (store, older = false) {
+ const rootState = store.rootState || store.state
+ const state = rootState.notifications
+ const notificationIds = keys(state.notifications)
+ const params = {}
+
+ if (older) {
+ if (notificationIds.length > 0) {
+ params['max_id'] = Math.min(...notificationIds)
+ }
+ } else {
+ // load unread notifications repeadedly to provide consistency between browser tabs
+ const unreadIds = filter(state.notifications, n => !n.seen).map(n => n.id)
+ if (unreadIds.length === 0) {
+ if (notificationIds.length > 0) {
+ params['since_id'] = Math.max(...notificationIds)
+ }
+ } else {
+ const minId = Math.min(...unreadIds)
+ const maxId = Math.max(...unreadIds)
+ params['min_id'] = minId
+ if (maxId - minId >= 20) {
+ params['max_id'] = maxId + 1
+ }
+ }
+ }
+
+ try {
+ const { notifications } = await Notifications.all({ store, params })
+ store.dispatch('addNewNotifications', { notifications })
+ store.commit('setNotificationsError', false)
+ return notifications
+ } catch (e) {
+ store.commit('setNotificationsError', true)
+ return null
+ }
+}
const notifications = {
state: {
notifications: {},
- visibleNotificationIds: []
+ error: false,
+ loading: false,
+ bottomedOut: false,
+ fetcher: undefined,
+ desktopNotificationSilence: true
},
mutations: {
- addNewNotificationsTwo (state, newNotifications) {
+ addNewNotifications (state, newNotifications) {
each(newNotifications, (notification) => {
Vue.set(state.notifications, notification.id, merge(state.notifications[notification.id] || {}, notification))
})
},
- setVisibleNotifications (state, { notificationIds }) {
- state.visibleNotificationIds = notificationIds
+ markNotificationsAsSeen (state) {
+ state.notifications = mapValues(state.notifications, (notification) => ({ ...notification, seen: true }))
+ },
+ setNotificationsError (state, error) {
+ state.error = error
+ },
+ setNotificationsLoading (state, loading) {
+ state.loading = loading
+ },
+ setNotificationsBottomedOut (state, bottomedOut) {
+ state.bottomedOut = bottomedOut
+ },
+ setNotificationsFetcher (state, fetcher) {
+ state.fetcher = fetcher
+ },
+ setNotificationsSilence (state, silence) {
+ state.desktopNotificationSilence = silence
}
},
actions: {
- async addNewNotificationsTwo ({ commit, state }, { notifications }) {
+ async startFetchingNotifications ({ state, rootState, commit, dispatch }) {
+ if (state.fetcher) {
+ return
+ }
+
+ // Fetch the initial data
+ const store = { state: rootState, commit, dispatch }
+ const { notifications } = await Notifications.all({ store })
+ dispatch('addNewNotifications', { notifications })
+
+ // Continue fetching at specified intervals
+ const fetcher = setInterval(() => fetchAndUpdate(store), 10000)
+ commit('setNotificationsFetcher', fetcher)
+
+ // Initially there's set flag to silence all desktop notifications so
+ // that there won't spam of them when user just opened up the FE.
+ // We reset that flag after a while to show new notifications once again.
+ setTimeout(() => commit('setNotificationsSilence', false), 10000)
+ },
+ async fetchOlderNotifications ({ rootState, commit, dispatch }) {
+ const store = { state: rootState, commit, dispatch }
+ commit('setNotificationsLoading', true)
+ const notifications = await fetchAndUpdate(store, true)
+ if (notifications && notifications.length === 0) {
+ commit('setNotificationsBottomedOut', true)
+ }
+ commit('setNotificationsLoading', false)
+ },
+ addNewNotifications ({ state, rootState, commit }, { notifications }) {
+ if ('Notification' in window && window.Notification.permission === 'granted' && !state.desktopNotificationSilence) {
+ const visibleNotificationTypes = getNotificationVisibleTypes(rootState.config.notificationVisibility)
+ each(notifications, (notification) => {
+ // Only show a new notification
+ if (!state.notifications.hasOwnProperty(notification.id) && !notification.seen && visibleNotificationTypes.includes(notification.type)) {
+ showDesktopPushNotification(notification)
+ }
+ })
+ }
+
const newUsers = map(notifications, 'account')
commit('addNewUsers', newUsers)
- commit('addNewNotificationsTwo', notifications)
- const newNotificationIds = map(notifications, 'id')
- const notificationIds = uniq([...newNotificationIds, ...state.visibleNotificationIds])
- commit('setVisibleNotifications', { notificationIds })
+ commit('addNewNotifications', notifications)
+ },
+ async markNotificationsAsSeen ({ rootState, commit, dispatch }) {
+ const store = { state: rootState, commit, dispatch }
+ await Notifications.markAsSeen({ store })
+ commit('markNotificationsAsSeen')
+ },
+ stopFetchingNotifications ({ state, commit }) {
+ clearInterval(state.fetcher)
+ commit('setNotificationsFetcher', undefined)
}
}
}
export default notifications
diff --git a/src/services/new_api/notifications.js b/src/services/new_api/notifications.js
index 57c4fcb4c0..080c9c7725 100644
--- a/src/services/new_api/notifications.js
+++ b/src/services/new_api/notifications.js
@@ -1,18 +1,21 @@
import utils from './utils.js'
import parseLinkHeader from 'parse-link-header'
const Notifications = {
async all ({ store, params = {} }) {
const data = await utils.request({
store,
url: '/api/v1/notifications',
params
})
const notifications = await data.json()
const links = parseLinkHeader(data.headers.get('link'))
return { notifications, links }
+ },
+ async markAsSeen ({ store }) {
+ // TODO: Implement This
}
}
export default Notifications
diff --git a/src/services/notification_helper/notification_helper.js b/src/services/notification_helper/notification_helper.js
new file mode 100644
index 0000000000..b85c30413a
--- /dev/null
+++ b/src/services/notification_helper/notification_helper.js
@@ -0,0 +1,42 @@
+import identity from 'lodash/identity'
+import i18n from '../../i18n/i18n'
+
+export const getNotificationVisibleTypes = (notificationVisibilitySetting) => {
+ return [
+ notificationVisibilitySetting.likes && 'favourite',
+ notificationVisibilitySetting.mentions && 'mention',
+ notificationVisibilitySetting.repeats && 'reblog',
+ notificationVisibilitySetting.follows && 'follow'
+ ].filter(identity)
+}
+
+export const showDesktopPushNotification = (notificationData) => {
+ const status = notificationData.status
+ const title = notificationData.account['display_name']
+ const notifObj = {
+ icon: notificationData.account.avatar,
+ body: generateNotificationDescription(notificationData)
+ }
+
+ // Shows the first attached non-nsfw image, if any. Should add configuration for this somehow...
+ if (status['media_attachments'] && status['media_attachments'].length > 0 && !status.sensitive && status['media_attachments'][0].type.startsWith('image/')) {
+ notifObj.image = status['media_attachments'][0].url
+ }
+
+ const notification = new window.Notification(title, notifObj)
+ // Chrome is known for not closing notifications automatically according to MDN, anyway.
+ setTimeout(notification.close.bind(notification), 5000)
+}
+
+function generateNotificationDescription (notification) {
+ switch (notification.type) {
+ case 'favourite':
+ return i18n.t('notifications.favorited_you')
+ case 'reblog':
+ return i18n.t('notifications.repeated_you')
+ case 'follow':
+ return i18n.t('notifications.followed_you')
+ default:
+ return notification.status.content && notification.status.content.replace(/(<([^>]+)>)/ig, '')
+ }
+}
diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js
deleted file mode 100644
index cd8f3f9efd..0000000000
--- a/src/services/notification_utils/notification_utils.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { filter, sortBy } from 'lodash'
-
-export const notificationsFromStore = store => store.state.statuses.notifications.data
-
-export const visibleTypes = store => ([
- store.state.config.notificationVisibility.likes && 'like',
- store.state.config.notificationVisibility.mentions && 'mention',
- store.state.config.notificationVisibility.repeats && 'repeat',
- store.state.config.notificationVisibility.follows && 'follow'
-].filter(_ => _))
-
-const sortById = (a, b) => {
- const seqA = Number(a.action.id)
- const seqB = Number(b.action.id)
- const isSeqA = !Number.isNaN(seqA)
- const isSeqB = !Number.isNaN(seqB)
- if (isSeqA && isSeqB) {
- return seqA > seqB ? -1 : 1
- } else if (isSeqA && !isSeqB) {
- return 1
- } else if (!isSeqA && isSeqB) {
- return -1
- } else {
- return a.action.id > b.action.id ? -1 : 1
- }
-}
-
-export const visibleNotificationsFromStore = store => {
- // map is just to clone the array since sort mutates it and it causes some issues
- let sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById)
- sortedNotifications = sortBy(sortedNotifications, 'seen')
- return sortedNotifications.filter((notification) => visibleTypes(store).includes(notification.type))
-}
-
-export const unseenNotificationsFromStore = store =>
- filter(visibleNotificationsFromStore(store), ({seen}) => !seen)
diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js
deleted file mode 100644
index 42db803577..0000000000
--- a/src/services/notifications_fetcher/notifications_fetcher.service.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Notifications from '../new_api/notifications.js'
-
-const NotificationsFetcher = {
- async create (store) {
- const { notifications, links } = await Notifications.all({ store })
- console.log(links)
- store.dispatch('addNewNotificationsTwo', { notifications })
- }
-}
-
-export default NotificationsFetcher
diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js
index 4a664d1e19..9e602e9480 100644
--- a/src/services/timeline_fetcher/timeline_fetcher.service.js
+++ b/src/services/timeline_fetcher/timeline_fetcher.service.js
@@ -1,89 +1,89 @@
import Timelines from '../new_api/timelines.js'
import Users from '../new_api/users.js'
const streamTimeline = ({ timelineName, store }) => {
const rootState = store.rootState || store.state
const timelines = {
publicAndExternal: 'public',
public: 'public:local',
home: 'user'
}
let url = `${rootState.instance.server}/api/v1/streaming?stream=${timelines[timelineName]}`.replace('http', 'ws')
let status = ''
if (rootState.oauth.token) {
url = `${url}&access_token=${rootState.oauth.token}`
}
let socket = null
const stop = () => socket.close()
const connect = () => {
status = ''
socket = new window.WebSocket(url)
socket.addEventListener('message', (event) => {
if (event.data === '') { return }
const data = JSON.parse(event.data)
console.log(data.event)
if (data.event === 'update') {
const status = JSON.parse(data.payload)
store.dispatch('addNewStatusesToTimeline', { statuses: [status], timelineName })
}
if (data.event === 'notification') {
const notification = JSON.parse(data.payload)
- store.dispatch('addNewNotificationsTwo', { notifications: [notification] })
+ store.dispatch('addNewNotifications', { notifications: [notification] })
}
})
socket.onopen = () => {
status = 'connected'
}
socket.onclose = () => {
status = 'closed'
}
}
const getStatus = () => (status)
return {
connect,
stop,
getStatus
}
}
const TimelineFetcher = {
async create (store, { type, params = {}, timelineName }) {
// Initial fetch, directly
let tf
switch (type) {
case 'public':
tf = Timelines.public
break
case 'user':
tf = Users.statuses
break
case 'home':
tf = Timelines.home
break
}
const { statuses, links } = await tf({ store, params })
store.dispatch('addNewStatusesToTimeline', { statuses, timelineName, showImmediately: true })
store.commit('setPrevLinkForTimeline', { link: links.prev, timelineName })
store.commit('setNextLinkForTimeline', { link: links.next, timelineName })
// Streaming, reactivate later
const socket = streamTimeline({ store, timelineName })
console.log(socket)
socket.connect()
// Next fetch uses the link headers
const fetcher = async () => {
store.dispatch('fetchPrevForTimeline', { timelineName })
}
return setInterval(fetcher, 10000)
}
}
export default TimelineFetcher

File Metadata

Mime Type
text/x-diff
Expires
Sat, Aug 8, 10:40 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1722118
Default Alt Text
(26 KB)

Event Timeline