Merge remote-tracking branch 'origin/websocket-fixes' into shigusegubu
* origin/websocket-fixes: fixed few-posts TLs when streaming is enabled fix not being able to re-enable sockets until page refresh added notices for ws events add success global notice style/level fix local dev websockets
This commit is contained in:
commit
2df4683dc5
13 changed files with 128 additions and 13 deletions
|
@ -11,6 +11,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
### Fixed
|
||||
- Follows/Followers tabs on user profiles now display the content properly.
|
||||
- Handle punycode in screen names
|
||||
- Fixed local dev mode having non-functional websockets in some cases
|
||||
- Show notices for websocket events (errors, abnormal closures, reconnections)
|
||||
- Fix not being able to re-enable websocket until page refresh
|
||||
- Fix annoying issue where timeline might have few posts when streaming is enabled
|
||||
|
||||
## [2.2.2] - 2020-12-22
|
||||
### Added
|
||||
|
|
|
@ -3,6 +3,11 @@ const path = require('path')
|
|||
let settings = {}
|
||||
try {
|
||||
settings = require('./local.json')
|
||||
if (settings.target && settings.target.endsWith('/')) {
|
||||
// replacing trailing slash since it can conflict with some apis
|
||||
// and that's how actual BE reports its url
|
||||
settings.target = settings.target.replace(/\/$/, '')
|
||||
}
|
||||
console.log('Using local dev server settings (/config/local.json):')
|
||||
console.log(JSON.stringify(settings, null, 2))
|
||||
} catch (e) {
|
||||
|
|
|
@ -698,6 +698,15 @@ nav {
|
|||
color: var(--alertWarningPanelText, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
&.success {
|
||||
background-color: var(--alertSuccess, $fallback--alertWarning);
|
||||
color: var(--alertSuccessText, $fallback--text);
|
||||
|
||||
.panel-heading & {
|
||||
color: var(--alertSuccessPanelText, $fallback--text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.faint {
|
||||
|
|
|
@ -71,6 +71,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.global-success {
|
||||
background-color: var(--alertPopupSuccess, $fallback--cGreen);
|
||||
color: var(--alertPopupSuccessText, $fallback--text);
|
||||
.svg-inline--fa {
|
||||
color: var(--alertPopupSuccessText, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
.global-info {
|
||||
background-color: var(--alertPopupNeutral, $fallback--fg);
|
||||
color: var(--alertPopupNeutralText, $fallback--text);
|
||||
|
|
|
@ -35,11 +35,6 @@ const Notifications = {
|
|||
seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const store = this.$store
|
||||
const credentials = store.state.users.currentUser.credentials
|
||||
notificationsFetcher.fetchAndUpdate({ store, credentials })
|
||||
},
|
||||
computed: {
|
||||
mainClass () {
|
||||
return this.minimalMode ? '' : 'panel panel-default'
|
||||
|
|
|
@ -646,7 +646,10 @@
|
|||
"reload": "Reload",
|
||||
"up_to_date": "Up-to-date",
|
||||
"no_more_statuses": "No more statuses",
|
||||
"no_statuses": "No statuses"
|
||||
"no_statuses": "No statuses",
|
||||
"socket_reconnected": "Realtime connection established",
|
||||
"socket_broke": "Realtime connection lost: CloseEvent code {0}",
|
||||
"socket_closed": "No realtime connection, updates can arrive with a delaye until connection is re-established"
|
||||
},
|
||||
"status": {
|
||||
"favorites": "Favorites",
|
||||
|
|
|
@ -40,7 +40,16 @@ const api = {
|
|||
// Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
|
||||
enableMastoSockets (store) {
|
||||
const { state, dispatch } = store
|
||||
if (state.mastoUserSocket) return
|
||||
// Do not initialize unless nonexistent or closed
|
||||
if (
|
||||
state.mastoUserSocket &&
|
||||
![
|
||||
WebSocket.CLOSED,
|
||||
WebSocket.CLOSING
|
||||
].includes(state.mastoUserSocket.getState())
|
||||
) {
|
||||
return
|
||||
}
|
||||
return dispatch('startMastoUserSocket')
|
||||
},
|
||||
disableMastoSockets (store) {
|
||||
|
@ -91,12 +100,29 @@ const api = {
|
|||
}
|
||||
)
|
||||
state.mastoUserSocket.addEventListener('open', () => {
|
||||
// Do not show notification when we just opened up the page
|
||||
if (state.mastoUserSocketStatus !== null) {
|
||||
dispatch('pushGlobalNotice', {
|
||||
level: 'success',
|
||||
messageKey: 'timeline.socket_reconnected',
|
||||
timeout: 5000
|
||||
})
|
||||
}
|
||||
commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED)
|
||||
})
|
||||
state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
|
||||
console.error('Error in MastoAPI websocket:', error)
|
||||
commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
|
||||
dispatch('clearOpenedChats')
|
||||
/* Since data in WS event for error is useless it's better to show
|
||||
* generic warning instead of in "close" which actually has some
|
||||
* useful data
|
||||
*/
|
||||
dispatch('pushGlobalNotice', {
|
||||
level: 'error',
|
||||
messageKey: 'timeline.socket_closed',
|
||||
timeout: 5000
|
||||
})
|
||||
})
|
||||
state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
|
||||
const ignoreCodes = new Set([
|
||||
|
@ -112,6 +138,12 @@ const api = {
|
|||
dispatch('startFetchingNotifications')
|
||||
dispatch('startFetchingChats')
|
||||
dispatch('restartMastoUserSocket')
|
||||
dispatch('pushGlobalNotice', {
|
||||
level: 'error',
|
||||
messageKey: 'timeline.socket_broke',
|
||||
messageArgs: [code],
|
||||
timeout: 5000
|
||||
})
|
||||
}
|
||||
commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED)
|
||||
dispatch('clearOpenedChats')
|
||||
|
@ -156,6 +188,13 @@ const api = {
|
|||
if (!fetcher) return
|
||||
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
|
||||
},
|
||||
fetchTimeline (store, timeline, { ...rest }) {
|
||||
store.state.backendInteractor.fetchTimeline({
|
||||
store,
|
||||
timeline,
|
||||
...rest
|
||||
})
|
||||
},
|
||||
|
||||
// Notifications
|
||||
startFetchingNotifications (store) {
|
||||
|
@ -168,6 +207,12 @@ const api = {
|
|||
if (!fetcher) return
|
||||
store.commit('removeFetcher', { fetcherName: 'notifications', fetcher })
|
||||
},
|
||||
fetchNotifications (store, { ...rest }) {
|
||||
store.state.backendInteractor.fetchNotifications({
|
||||
store,
|
||||
...rest
|
||||
})
|
||||
},
|
||||
|
||||
// Follow requests
|
||||
startFetchingFollowRequests (store) {
|
||||
|
|
|
@ -547,6 +547,8 @@ const users = {
|
|||
}
|
||||
|
||||
if (store.getters.mergedConfig.useStreamingApi) {
|
||||
store.dispatch('fetchTimeline', 'friends', { since: null })
|
||||
store.dispatch('fetchNotifications', { since: null })
|
||||
store.dispatch('enableMastoSockets').catch((error) => {
|
||||
console.error('Failed initializing MastoAPI Streaming socket', error)
|
||||
startPolling()
|
||||
|
|
|
@ -1152,6 +1152,7 @@ export const ProcessedWS = ({
|
|||
|
||||
// 1000 = Normal Closure
|
||||
eventTarget.close = () => { socket.close(1000, 'Shutting down socket') }
|
||||
eventTarget.getState = () => socket.readyState
|
||||
|
||||
return eventTarget
|
||||
}
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.service.js'
|
||||
import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js'
|
||||
import timelineFetcher from '../timeline_fetcher/timeline_fetcher.service.js'
|
||||
import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js'
|
||||
import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service'
|
||||
|
||||
const backendInteractorService = credentials => ({
|
||||
startFetchingTimeline ({ timeline, store, userId = false, tag }) {
|
||||
return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag })
|
||||
return timelineFetcher.startFetching({ timeline, store, credentials, userId, tag })
|
||||
},
|
||||
|
||||
fetchTimeline (args) {
|
||||
return timelineFetcher.fetchAndUpdate({ ...args, credentials })
|
||||
},
|
||||
|
||||
startFetchingNotifications ({ store }) {
|
||||
return notificationsFetcher.startFetching({ store, credentials })
|
||||
},
|
||||
|
||||
fetchNotifications (args) {
|
||||
return notificationsFetcher.fetchAndUpdate({ ...args, credentials })
|
||||
},
|
||||
|
||||
startFetchingFollowRequests ({ store }) {
|
||||
return followRequestFetcher.startFetching({ store, credentials })
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@ const update = ({ store, notifications, older }) => {
|
|||
store.dispatch('addNewNotifications', { notifications, older })
|
||||
}
|
||||
|
||||
const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
||||
const fetchAndUpdate = ({ store, credentials, older = false, since }) => {
|
||||
const args = { credentials }
|
||||
const { getters } = store
|
||||
const rootState = store.rootState || store.state
|
||||
|
@ -22,8 +22,10 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
|||
return fetchNotifications({ store, args, older })
|
||||
} else {
|
||||
// fetch new notifications
|
||||
if (timelineData.maxId !== Number.POSITIVE_INFINITY) {
|
||||
if (since === undefined && timelineData.maxId !== Number.POSITIVE_INFINITY) {
|
||||
args['since'] = timelineData.maxId
|
||||
} else if (since !== null) {
|
||||
args['since'] = since
|
||||
}
|
||||
const result = fetchNotifications({ store, args, older })
|
||||
|
||||
|
|
|
@ -616,6 +616,23 @@ export const SLOT_INHERITANCE = {
|
|||
textColor: true
|
||||
},
|
||||
|
||||
alertSuccess: {
|
||||
depends: ['cGreen'],
|
||||
opacity: 'alert'
|
||||
},
|
||||
alertSuccessText: {
|
||||
depends: ['text'],
|
||||
layer: 'alert',
|
||||
variant: 'alertSuccess',
|
||||
textColor: true
|
||||
},
|
||||
alertSuccessPanelText: {
|
||||
depends: ['panelText'],
|
||||
layer: 'alertPanel',
|
||||
variant: 'alertSuccess',
|
||||
textColor: true
|
||||
},
|
||||
|
||||
alertNeutral: {
|
||||
depends: ['text'],
|
||||
opacity: 'alert'
|
||||
|
@ -656,6 +673,17 @@ export const SLOT_INHERITANCE = {
|
|||
textColor: true
|
||||
},
|
||||
|
||||
alertPopupSuccess: {
|
||||
depends: ['alertSuccess'],
|
||||
opacity: 'alertPopup'
|
||||
},
|
||||
alertPopupSuccessText: {
|
||||
depends: ['alertSuccessText'],
|
||||
layer: 'popover',
|
||||
variant: 'alertPopupSuccess',
|
||||
textColor: true
|
||||
},
|
||||
|
||||
alertPopupNeutral: {
|
||||
depends: ['alertNeutral'],
|
||||
opacity: 'alertPopup'
|
||||
|
|
|
@ -23,7 +23,8 @@ const fetchAndUpdate = ({
|
|||
showImmediately = false,
|
||||
userId = false,
|
||||
tag = false,
|
||||
until
|
||||
until,
|
||||
since
|
||||
}) => {
|
||||
const args = { timeline, credentials }
|
||||
const rootState = store.rootState || store.state
|
||||
|
@ -35,7 +36,11 @@ const fetchAndUpdate = ({
|
|||
if (older) {
|
||||
args['until'] = until || timelineData.minId
|
||||
} else {
|
||||
args['since'] = timelineData.maxId
|
||||
if (since === undefined) {
|
||||
args['since'] = timelineData.maxId
|
||||
} else if (since !== null) {
|
||||
args['since'] = since
|
||||
}
|
||||
}
|
||||
|
||||
args['userId'] = userId
|
||||
|
|
Loading…
Add table
Reference in a new issue