aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--config/index.js5
-rw-r--r--src/App.scss9
-rw-r--r--src/components/global_notice_list/global_notice_list.vue8
-rw-r--r--src/components/notifications/notifications.js5
-rw-r--r--src/components/password_reset/password_reset.vue2
-rw-r--r--src/components/post_status_form/post_status_form.vue4
-rw-r--r--src/components/registration/registration.vue2
-rw-r--r--src/components/settings_modal/tabs/notifications_tab.vue2
-rw-r--r--src/components/settings_modal/tabs/profile_tab.vue6
-rw-r--r--src/components/settings_modal/tabs/security_tab/security_tab.vue6
-rw-r--r--src/i18n/en.json77
-rw-r--r--src/modules/api.js102
-rw-r--r--src/modules/users.js5
-rw-r--r--src/services/api/api.service.js6
-rw-r--r--src/services/backend_interactor_service/backend_interactor_service.js12
-rw-r--r--src/services/notifications_fetcher/notifications_fetcher.service.js6
-rw-r--r--src/services/theme_data/pleromafe.js28
-rw-r--r--src/services/timeline_fetcher/timeline_fetcher.service.js9
19 files changed, 217 insertions, 81 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 877095a7..cef6d401 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,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
### Changed
- Don't filter own posts when they hit your wordfilter
diff --git a/config/index.js b/config/index.js
index ccec4196..7cb87c3b 100644
--- a/config/index.js
+++ b/config/index.js
@@ -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) {
diff --git a/src/App.scss b/src/App.scss
index 90d083bb..f860c16d 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -706,6 +706,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 {
diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue
index 049e23db..a45f4586 100644
--- a/src/components/global_notice_list/global_notice_list.vue
+++ b/src/components/global_notice_list/global_notice_list.vue
@@ -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);
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index a9bf2931..c8f1ebcb 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -40,11 +40,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'
diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue
index a931cb5a..3ffa5425 100644
--- a/src/components/password_reset/password_reset.vue
+++ b/src/components/password_reset/password_reset.vue
@@ -53,7 +53,7 @@
type="submit"
class="btn button-default btn-block"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 73f6a4f1..6b490aee 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -272,7 +272,7 @@
disabled
class="btn button-default"
>
- {{ $t('general.submit') }}
+ {{ $t('post_status.post') }}
</button>
<!-- touchstart is used to keep the OSK at the same position after a message send -->
<button
@@ -282,7 +282,7 @@
@touchstart.stop.prevent="postStatus($event, newStatus)"
@click.stop.prevent="postStatus($event, newStatus)"
>
- {{ $t('general.submit') }}
+ {{ $t('post_status.post') }}
</button>
</div>
<div
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index 062d4121..65b4bb33 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -230,7 +230,7 @@
type="submit"
class="btn button-default"
>
- {{ $t('general.submit') }}
+ {{ $t('registration.register') }}
</button>
</div>
</div>
diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue
index 8f8fe48e..7e0568ea 100644
--- a/src/components/settings_modal/tabs/notifications_tab.vue
+++ b/src/components/settings_modal/tabs/notifications_tab.vue
@@ -24,7 +24,7 @@
class="btn button-default"
@click="updateNotificationSettings"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index 175a0219..bb3c301d 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -153,7 +153,7 @@
class="btn button-default"
@click="updateProfile"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
<div class="setting-item">
@@ -227,7 +227,7 @@
class="btn button-default"
@click="submitBanner(banner)"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
<div class="setting-item">
@@ -266,7 +266,7 @@
class="btn button-default"
@click="submitBackground(background)"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue
index 56bea1f4..275d4616 100644
--- a/src/components/settings_modal/tabs/security_tab/security_tab.vue
+++ b/src/components/settings_modal/tabs/security_tab/security_tab.vue
@@ -22,7 +22,7 @@
class="btn button-default"
@click="changeEmail"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
<p v-if="changedEmail">
{{ $t('settings.changed_email') }}
@@ -60,7 +60,7 @@
class="btn button-default"
@click="changePassword"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
<p v-if="changedPassword">
{{ $t('settings.changed_password') }}
@@ -133,7 +133,7 @@
class="btn button-default"
@click="confirmDelete"
>
- {{ $t('general.submit') }}
+ {{ $t('settings.save') }}
</button>
</div>
</div>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index d10e7986..471098eb 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -3,27 +3,27 @@
"mrf": {
"federation": "Federation",
"keyword": {
- "keyword_policies": "Keyword Policies",
+ "keyword_policies": "Keyword policies",
"ftl_removal": "Removal from \"The Whole Known Network\" Timeline",
"reject": "Reject",
"replace": "Replace",
"is_replaced_by": "→"
},
- "mrf_policies": "Enabled MRF Policies",
+ "mrf_policies": "Enabled MRF policies",
"mrf_policies_desc": "MRF policies manipulate the federation behaviour of the instance. The following policies are enabled:",
"simple": {
- "simple_policies": "Instance-specific Policies",
+ "simple_policies": "Instance-specific policies",
"accept": "Accept",
"accept_desc": "This instance only accepts messages from the following instances:",
"reject": "Reject",
"reject_desc": "This instance will not accept messages from the following instances:",
"quarantine": "Quarantine",
"quarantine_desc": "This instance will send only public posts to the following instances:",
- "ftl_removal": "Removal from \"The Whole Known Network\" Timeline",
- "ftl_removal_desc": "This instance removes these instances from \"The Whole Known Network\" timeline:",
+ "ftl_removal": "Removal from \"Known Network\" Timeline",
+ "ftl_removal_desc": "This instance removes these instances from \"Known Network\" timeline:",
"media_removal": "Media Removal",
"media_removal_desc": "This instance removes media from posts on the following instances:",
- "media_nsfw": "Media Force-set As Sensitive",
+ "media_nsfw": "Media force-set as sensitive",
"media_nsfw_desc": "This instance forces media to be set sensitive in posts on the following instances:"
}
},
@@ -118,13 +118,13 @@
"about": "About",
"administration": "Administration",
"back": "Back",
- "friend_requests": "Follow Requests",
+ "friend_requests": "Follow requests",
"mentions": "Mentions",
"interactions": "Interactions",
- "dms": "Direct Messages",
- "public_tl": "Public Timeline",
+ "dms": "Direct messages",
+ "public_tl": "Public timeline",
"timeline": "Timeline",
- "home_timeline": "Home Timeline",
+ "home_timeline": "Home timeline",
"twkn": "Known Network",
"bookmarks": "Bookmarks",
"user_search": "User Search",
@@ -149,8 +149,8 @@
"reacted_with": "reacted with {0}"
},
"polls": {
- "add_poll": "Add Poll",
- "add_option": "Add Option",
+ "add_poll": "Add poll",
+ "add_option": "Add option",
"option": "Option",
"votes": "votes",
"people_voted_count": "{count} person voted | {count} people voted",
@@ -179,7 +179,7 @@
"storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies."
},
"interactions": {
- "favs_repeats": "Repeats and Favorites",
+ "favs_repeats": "Repeats and favorites",
"follows": "New follows",
"moves": "User migrates",
"load_older": "Load older interactions"
@@ -201,6 +201,7 @@
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
"direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.",
"posting": "Posting",
+ "post": "Post",
"preview": "Preview",
"preview_empty": "Empty",
"empty_status_error": "Can't post an empty status with no files",
@@ -211,10 +212,10 @@
"unlisted": "This post will not be visible in Public Timeline and The Whole Known Network"
},
"scope": {
- "direct": "Direct - Post to mentioned users only",
- "private": "Followers-only - Post to followers only",
- "public": "Public - Post to public timelines",
- "unlisted": "Unlisted - Do not post to public timelines"
+ "direct": "Direct - post to mentioned users only",
+ "private": "Followers-only - post to followers only",
+ "public": "Public - post to public timelines",
+ "unlisted": "Unlisted - do not post to public timelines"
}
},
"registration": {
@@ -231,6 +232,7 @@
"bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.",
"reason": "Reason to register",
"reason_placeholder": "This instance approves registrations manually.\nLet the administration know why you want to register.",
+ "register": "Register",
"validations": {
"username_required": "cannot be left blank",
"fullname_required": "cannot be left blank",
@@ -250,6 +252,7 @@
},
"settings": {
"app_name": "App name",
+ "save": "Save changes",
"security": "Security",
"setting_changed": "Setting is different from default",
"enter_current_password_to_confirm": "Enter your current password to confirm your identity",
@@ -278,7 +281,7 @@
"attachmentRadius": "Attachments",
"attachments": "Attachments",
"avatar": "Avatar",
- "avatarAltRadius": "Avatars (Notifications)",
+ "avatarAltRadius": "Avatars (notifications)",
"avatarRadius": "Avatars",
"background": "Background",
"bio": "Bio",
@@ -300,10 +303,10 @@
"cGreen": "Green (Retweet)",
"cOrange": "Orange (Favorite)",
"cRed": "Red (Cancel)",
- "change_email": "Change Email",
+ "change_email": "Change email",
"change_email_error": "There was an issue changing your email.",
"changed_email": "Email changed successfully!",
- "change_password": "Change Password",
+ "change_password": "Change password",
"change_password_error": "There was an issue changing your password.",
"changed_password": "Password changed successfully!",
"chatMessageRadius": "Chat message",
@@ -312,9 +315,9 @@
"confirm_new_password": "Confirm new password",
"current_password": "Current password",
"mutes_and_blocks": "Mutes and Blocks",
- "data_import_export_tab": "Data Import / Export",
+ "data_import_export_tab": "Data import / export",
"default_vis": "Default visibility scope",
- "delete_account": "Delete Account",
+ "delete_account": "Delete account",
"delete_account_description": "Permanently delete your data and deactivate your account.",
"delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.",
"delete_account_instructions": "Type your password in the input below to confirm account deletion.",
@@ -367,14 +370,14 @@
"play_videos_in_modal": "Play videos in a popup frame",
"profile_fields": {
"label": "Profile metadata",
- "add_field": "Add Field",
+ "add_field": "Add field",
"name": "Label",
"value": "Content"
},
"use_contain_fit": "Don't crop the attachment in thumbnails",
"name": "Name",
- "name_bio": "Name & Bio",
- "new_email": "New Email",
+ "name_bio": "Name & bio",
+ "new_email": "New email",
"new_password": "New password",
"notification_visibility": "Types of notifications to show",
"notification_visibility_follows": "Follows",
@@ -390,19 +393,19 @@
"hide_followers_description": "Don't show who's following me",
"hide_follows_count_description": "Don't show follow count",
"hide_followers_count_description": "Don't show follower count",
- "show_admin_badge": "Show Admin badge in my profile",
- "show_moderator_badge": "Show Moderator badge in my profile",
+ "show_admin_badge": "Show \"Admin\" badge in my profile",
+ "show_moderator_badge": "Show \"Moderator\" badge in my profile",
"nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses",
"oauth_tokens": "OAuth tokens",
"token": "Token",
- "refresh_token": "Refresh Token",
- "valid_until": "Valid Until",
+ "refresh_token": "Refresh token",
+ "valid_until": "Valid until",
"revoke_token": "Revoke",
"panelRadius": "Panels",
"pause_on_unfocused": "Pause streaming when tab is not focused",
"presets": "Presets",
- "profile_background": "Profile Background",
- "profile_banner": "Profile Banner",
+ "profile_background": "Profile background",
+ "profile_banner": "Profile banner",
"profile_tab": "Profile",
"radii_help": "Set up interface edge rounding (in pixels)",
"replies_in_timeline": "Replies in timeline",
@@ -614,8 +617,8 @@
},
"version": {
"title": "Version",
- "backend_version": "Backend Version",
- "frontend_version": "Frontend Version"
+ "backend_version": "Backend version",
+ "frontend_version": "Frontend version"
}
},
"time": {
@@ -663,7 +666,9 @@
"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}"
},
"status": {
"favorites": "Favorites",
@@ -756,7 +761,7 @@
}
},
"user_profile": {
- "timeline_title": "User Timeline",
+ "timeline_title": "User timeline",
"profile_does_not_exist": "Sorry, this profile does not exist.",
"profile_loading_error": "Sorry, there was an error loading this profile."
},
@@ -774,7 +779,7 @@
"who_to_follow": "Who to follow"
},
"tool_tip": {
- "media_upload": "Upload Media",
+ "media_upload": "Upload media",
"repeat": "Repeat",
"reply": "Reply",
"favorite": "Favorite",
diff --git a/src/modules/api.js b/src/modules/api.js
index 08485a30..8654b90a 100644
--- a/src/modules/api.js
+++ b/src/modules/api.js
@@ -3,8 +3,11 @@ import { WSConnectionStatus } from '../services/api/api.service.js'
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
import { Socket } from 'phoenix'
+const retryTimeout = (multiplier) => 1000 * multiplier
+
const api = {
state: {
+ retryMultiplier: 1,
backendInteractor: backendInteractorService(),
fetchers: {},
socket: null,
@@ -34,18 +37,43 @@ const api = {
},
setMastoUserSocketStatus (state, value) {
state.mastoUserSocketStatus = value
+ },
+ incrementRetryMultiplier (state) {
+ state.retryMultiplier = Math.max(++state.retryMultiplier, 3)
+ },
+ resetRetryMultiplier (state) {
+ state.retryMultiplier = 1
}
},
actions: {
- // Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
- enableMastoSockets (store) {
- const { state, dispatch } = store
- if (state.mastoUserSocket) return
+ /**
+ * Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets
+ *
+ * @param {Boolean} [initial] - whether this enabling happened at boot time or not
+ */
+ enableMastoSockets (store, initial) {
+ const { state, dispatch, commit } = store
+ // Do not initialize unless nonexistent or closed
+ if (
+ state.mastoUserSocket &&
+ ![
+ WebSocket.CLOSED,
+ WebSocket.CLOSING
+ ].includes(state.mastoUserSocket.getState())
+ ) {
+ return
+ }
+ if (initial) {
+ commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING_INITIAL)
+ } else {
+ commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING)
+ }
return dispatch('startMastoUserSocket')
},
disableMastoSockets (store) {
- const { state, dispatch } = store
+ const { state, dispatch, commit } = store
if (!state.mastoUserSocket) return
+ commit('setMastoUserSocketStatus', WSConnectionStatus.DISABLED)
return dispatch('stopMastoUserSocket')
},
@@ -91,11 +119,29 @@ const api = {
}
)
state.mastoUserSocket.addEventListener('open', () => {
+ // Do not show notification when we just opened up the page
+ if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) {
+ dispatch('pushGlobalNotice', {
+ level: 'success',
+ messageKey: 'timeline.socket_reconnected',
+ timeout: 5000
+ })
+ }
+ // Stop polling if we were errored or disabled
+ if (new Set([
+ WSConnectionStatus.ERROR,
+ WSConnectionStatus.DISABLED
+ ]).has(state.mastoUserSocketStatus)) {
+ dispatch('stopFetchingTimeline', { timeline: 'friends' })
+ dispatch('stopFetchingNotifications')
+ dispatch('stopFetchingChats')
+ }
+ commit('resetRetryMultiplier')
commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED)
})
state.mastoUserSocket.addEventListener('error', ({ detail: error }) => {
console.error('Error in MastoAPI websocket:', error)
- commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
+ // TODO is this needed?
dispatch('clearOpenedChats')
})
state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => {
@@ -106,14 +152,26 @@ const api = {
const { code } = closeEvent
if (ignoreCodes.has(code)) {
console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`)
+ commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED)
} else {
console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`)
- dispatch('startFetchingTimeline', { timeline: 'friends' })
- dispatch('startFetchingNotifications')
- dispatch('startFetchingChats')
- dispatch('restartMastoUserSocket')
+ setTimeout(() => {
+ dispatch('startMastoUserSocket')
+ }, retryTimeout(state.retryMultiplier))
+ commit('incrementRetryMultiplier')
+ if (state.mastoUserSocketStatus !== WSConnectionStatus.ERROR) {
+ dispatch('startFetchingTimeline', { timeline: 'friends' })
+ dispatch('startFetchingNotifications')
+ dispatch('startFetchingChats')
+ dispatch('pushGlobalNotice', {
+ level: 'error',
+ messageKey: 'timeline.socket_broke',
+ messageArgs: [code],
+ timeout: 5000
+ })
+ }
+ commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR)
}
- commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED)
dispatch('clearOpenedChats')
})
resolve()
@@ -122,15 +180,6 @@ const api = {
}
})
},
- restartMastoUserSocket ({ dispatch }) {
- // This basically starts MastoAPI user socket and stops conventional
- // fetchers when connection reestablished
- return dispatch('startMastoUserSocket').then(() => {
- dispatch('stopFetchingTimeline', { timeline: 'friends' })
- dispatch('stopFetchingNotifications')
- dispatch('stopFetchingChats')
- })
- },
stopMastoUserSocket ({ state, dispatch }) {
dispatch('startFetchingTimeline', { timeline: 'friends' })
dispatch('startFetchingNotifications')
@@ -156,6 +205,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 +224,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) {
diff --git a/src/modules/users.js b/src/modules/users.js
index 655db4c7..8a764a16 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -547,9 +547,10 @@ const users = {
}
if (store.getters.mergedConfig.useStreamingApi) {
- store.dispatch('enableMastoSockets').catch((error) => {
+ store.dispatch('fetchTimeline', 'friends', { since: null })
+ store.dispatch('fetchNotifications', { since: null })
+ store.dispatch('enableMastoSockets', true).catch((error) => {
console.error('Failed initializing MastoAPI Streaming socket', error)
- startPolling()
}).then(() => {
store.dispatch('fetchChats', { latest: true })
setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000)
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index f4483149..436b8b0a 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -1152,6 +1152,7 @@ export const ProcessedWS = ({
// 1000 = Normal Closure
eventTarget.close = () => { socket.close(1000, 'Shutting down socket') }
+ eventTarget.getState = () => socket.readyState
return eventTarget
}
@@ -1183,7 +1184,10 @@ export const handleMastoWS = (wsEvent) => {
export const WSConnectionStatus = Object.freeze({
'JOINED': 1,
'CLOSED': 2,
- 'ERROR': 3
+ 'ERROR': 3,
+ 'DISABLED': 4,
+ 'STARTING': 5,
+ 'STARTING_INITIAL': 6
})
const chats = ({ credentials }) => {
diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js
index 45e6bd0e..4a40f5b5 100644
--- a/src/services/backend_interactor_service/backend_interactor_service.js
+++ b/src/services/backend_interactor_service/backend_interactor_service.js
@@ -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 })
},
diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js
index beeb167c..f83f871e 100644
--- a/src/services/notifications_fetcher/notifications_fetcher.service.js
+++ b/src/services/notifications_fetcher/notifications_fetcher.service.js
@@ -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 })
diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js
index bec1eebd..14aac975 100644
--- a/src/services/theme_data/pleromafe.js
+++ b/src/services/theme_data/pleromafe.js
@@ -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'
diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js
index 921df3ed..46bba41a 100644
--- a/src/services/timeline_fetcher/timeline_fetcher.service.js
+++ b/src/services/timeline_fetcher/timeline_fetcher.service.js
@@ -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