Merge branch 'weight-removal' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2026-09-01 18:52:54 +03:00
commit 44f845c71b
28 changed files with 255 additions and 301 deletions

View file

@ -1,5 +1,5 @@
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-jsx"],
"plugins": ["@babel/plugin-transform-runtime", "@vue/babel-plugin-jsx"],
"comments": true
}

View file

@ -78,7 +78,6 @@
"@vue/compiler-sfc": "3.5.22",
"@vue/test-utils": "2.4.6",
"autoprefixer": "10.4.21",
"babel-plugin-lodash": "3.3.4",
"chai": "5.3.3",
"chalk": "5.6.2",
"chromedriver": "135.0.4",
@ -97,7 +96,7 @@
"function-bind": "1.1.2",
"http-proxy-middleware": "3.0.5",
"iso-639-1": "3.1.5",
"lodash": "4.17.21",
"lodash-es": "4.17.21",
"msw": "2.14.6",
"nightwatch": "3.12.2",
"oxc": "^1.0.1",

View file

@ -17,7 +17,8 @@ const CHANGE_EMAIL_URL = '/api/pleroma/change_email'
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
const ALIASES_URL = '/api/pleroma/aliases'
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
const NOTIFICATION_SETTINGS_URL = ({ blockFromStrangers, hideNotificationContents }) =>
`/api/pleroma/notification_settings${paramsString({ blockFromStrangers, hideNotificationContents })}`
export const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
@ -432,10 +433,9 @@ export const exportFriends = ({ id, credentials }) => {
// #Profile settings
export const updateNotificationSettings = ({ credentials, settings }) => {
return promisedRequest({
url: NOTIFICATION_SETTINGS_URL,
url: NOTIFICATION_SETTINGS_URL(settings),
credentials,
method: 'PUT',
payload: settings,
})
}

View file

@ -1,8 +1,8 @@
import { mapState } from 'pinia'
import { mapGetters } from 'vuex'
import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
@ -37,7 +37,7 @@ const ExtraNotifications = {
return (
this.mergedConfig.showExtraNotifications &&
this.mergedConfig.showFollowRequestsInExtraNotifications &&
this.followRequestCount
this.followRequestsCount
)
},
hasAnythingToShow() {
@ -55,12 +55,12 @@ const ExtraNotifications = {
currentUser() {
return useUsersStore().currentUser
},
...mapGetters(['followRequestCount']),
...mapState(useAnnouncementsStore, {
unreadAnnouncementCount: 'unreadAnnouncementCount',
}),
...mapState(useMergedConfigStore, ['mergedConfig']),
...mapState(useChatsStore, ['unreadChatsCount']),
...mapState(useFollowRequestsStore, ['followRequestsCount']),
},
methods: {
openNotificationSettings() {

View file

@ -48,7 +48,7 @@
class="fa-scale-110 icon"
icon="user-plus"
/>
{{ $t('notifications.unread_follow_requests', { num: followRequestCount }, followRequestCount) }}
{{ $t('notifications.unread_follow_requests', { num: followRequestsCount }, followRequestsCount) }}
</router-link>
</div>
<i18n-t

View file

@ -2,6 +2,7 @@ import { defineAsyncComponent } from 'vue'
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useNotificationsStore } from 'src/stores/notifications.js'
import { useOAuthStore } from 'src/stores/oauth.js'
@ -54,12 +55,11 @@ const FollowRequestCard = {
approveUser({
id: this.user.id,
credentials: useOAuthStore().token,
}).then(() => {
const notifId = this.findFollowRequestNotificationId()
useFollowRequestsStore().remove(this.user.id)
notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId)
})
// TODO fix
this.$store.dispatch('removeFollowRequest', this.user)
const notifId = this.findFollowRequestNotificationId()
useNotificationsStore().markSingleNotificationAsSeen(notifId)
this.hideApproveConfirmDialog()
},
denyUser() {
@ -70,15 +70,13 @@ const FollowRequestCard = {
}
},
doDeny() {
const notifId = this.findFollowRequestNotificationId()
denyUser({
id: this.user.id,
credentials: useOAuthStore().token,
}).then(() => {
useNotificationsStore().dismissNotificationLocal(notifId)
// TODO fix
this.$store.dispatch('removeFollowRequest', this.user)
const notifId = this.findFollowRequestNotificationId()
useFollowRequestsStore().remove(this.user.id)
notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId)
})
this.hideDenyConfirmDialog()
},

View file

@ -1,12 +1,14 @@
import FollowRequestCard from 'src/components/follow_request_card/follow_request_card.vue'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
const FollowRequests = {
components: {
FollowRequestCard,
},
computed: {
requests() {
return this.$store.state.api.followRequests
return useFollowRequestsStore().requests.values()
},
},
}

View file

@ -10,6 +10,7 @@ import {
import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useNotificationsStore } from 'src/stores/notifications.js'
@ -67,10 +68,10 @@ const MobileNav = {
return (
this.unseenNotifications.length +
countExtraNotifications(
this.$store,
useMergedConfigStore().mergedConfig,
useChatsStore().unreadChatsCount,
useAnnouncementsStore().unreadAnnouncementCount,
useFollowRequestsStore().followRequestsCount,
)
)
},

View file

@ -1,5 +1,4 @@
import { mapState } from 'pinia'
import { mapState as mapVuexState } from 'vuex'
import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
@ -11,6 +10,7 @@ import NavigationPins from 'src/components/navigation/navigation_pins.vue'
import { useAnnouncementsStore } from 'src/stores/announcements'
import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
@ -130,9 +130,7 @@ const NavPanel = {
new Set(store.prefsStorage.collections.pinnedNavItems),
}),
...mapState(useUsersStore, ['currentUser']),
...mapVuexState({
followRequestCount: (state) => state.api.followRequests.length,
}),
...mapState(useFollowRequestsStore, ['followRequestsCount']),
...mapState(useChatsStore, ['unreadChatsCount']),
timelinesItems() {
return filterNavigation(

View file

@ -85,7 +85,7 @@ export const ROOT_ITEMS = {
label: 'nav.friend_requests',
badgeStyle: 'notification',
criteria: ['lockedUser'],
badgeGetter: 'followRequestCount',
badgeGetter: 'followRequestsCount',
},
about: {
route: 'about',

View file

@ -1,5 +1,4 @@
import { mapState } from 'pinia'
import { mapState as mapVuexState } from 'vuex'
import {
filterNavigation,
@ -14,6 +13,7 @@ import {
import { useAnnouncementsStore } from 'src/stores/announcements'
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useListsStore } from 'src/stores/lists'
@ -78,9 +78,7 @@ const NavPanel = {
'localBubble',
]),
...mapState(useUsersStore, ['currentUser']),
...mapVuexState({
followRequestCount: (state) => state.api.followRequests.length,
}),
...mapState(useFollowRequestsStore, ['followRequestsCount']),
pinnedList() {
if (!this.currentUser) {
return filterNavigation(

View file

@ -14,6 +14,7 @@ import NotificationFilters from './notification_filters.vue'
import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useNotificationsStore } from 'src/stores/notifications.js'
@ -107,10 +108,10 @@ const Notifications = {
},
extraNotificationsCount() {
return countExtraNotifications(
this.$store,
useMergedConfigStore().mergedConfig,
useChatsStore().unreadChatsCount,
useAnnouncementsStore().unreadAnnouncementCount,
useFollowRequestsStore().followRequestsCount,
)
},
unseenCountTitle() {

View file

@ -9,6 +9,7 @@ import { useInterfaceStore } from 'src/stores/interface.js'
import { useLocalConfigStore } from 'src/stores/local_config.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { useProfileConfigStore } from 'src/stores/profile_config.js'
export default {
components: {
@ -236,7 +237,7 @@ export default {
configSource() {
switch (this.realSource) {
case 'profile':
return this.$store.state.profileConfig
return useProfileConfigStore().config
case 'admin':
return useAdminSettingsStore().config
default:
@ -253,7 +254,7 @@ export default {
switch (this.realSource) {
case 'profile':
return (k, v) =>
this.$store.dispatch('setProfileOption', { name: k, value: v })
useProfileConfigStore().setProfileOption({ name: k, value: v })
case 'admin':
return (k, v) =>
useAdminSettingsStore().pushAdminSetting({ path: k, value: v })

View file

@ -17,6 +17,7 @@ import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { useProfileConfigStore } from 'src/stores/profile_config.js'
import { useUsersStore } from 'src/stores/users.js'
import { updateProfile } from 'src/api/user.js'
@ -109,6 +110,9 @@ const ComposingTab = {
FontControl,
},
computed: {
defaultScope() {
return useProfileConfigStore().config.defaultScope
},
postFormats() {
return useInstanceCapabilitiesStore().postFormats
},
@ -135,7 +139,7 @@ const ComposingTab = {
},
methods: {
changeDefaultScope(value) {
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
useProfileConfigStore().setProfileOption({ name: 'defaultScope', value })
},
clearCache(key) {
clearCache(key)

View file

@ -11,10 +11,10 @@
<ScopeSelector
class="scope-selector setting-control"
:show-all="true"
:user-default="$store.state.profileConfig.defaultScope"
:initial-scope="$store.state.profileConfig.defaultScope"
:on-scope-change="changeDefaultScope"
:user-default="defaultScope"
:initial-scope="defaultScope"
:unstyled="false"
@change="changeDefaultScope"
/>
</label>
</li>

View file

@ -8,6 +8,7 @@ import { unseenNotifications } from '../../services/notification_utils/notificat
import { useAnnouncementsStore } from 'src/stores/announcements'
import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface'
@ -85,9 +86,6 @@ const SideDrawer = {
unseenNotificationsCount() {
return this.unseenNotifications.length
},
followRequestCount() {
return this.$store.state.api.followRequests.length
},
timelinesRoute() {
let name
if (useInterfaceStore().lastTimeline) {
@ -100,6 +98,7 @@ const SideDrawer = {
return { name }
}
},
...mapState(useFollowRequestsStore, ['followRequestsCount']),
...mapState(useAnnouncementsStore, [
'supportsAnnouncements',
'unreadAnnouncementCount',

View file

@ -141,10 +141,10 @@
icon="user-plus"
/> {{ $t("nav.friend_requests") }}
<span
v-if="followRequestCount > 0"
v-if="followRequestsCount > 0"
class="badge -notification"
>
{{ followRequestCount }}
{{ followRequestsCount }}
</span>
</router-link>
</li>

View file

@ -1,79 +0,0 @@
import { Socket } from 'phoenix'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useShoutStore } from 'src/stores/shout.js'
import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service'
const api = {
state: {
fetchers: {},
socket: null,
followRequests: [],
},
getters: {
followRequestCount: (state) => state.followRequests.length,
},
mutations: {
addFetcher(state, { fetcherName, fetcher }) {
state.fetchers[fetcherName] = fetcher
},
removeFetcher(state, { fetcherName }) {
state.fetchers[fetcherName].stop()
delete state.fetchers[fetcherName]
},
setWsToken(state, token) {
state.wsToken = token
},
setSocket(state, socket) {
state.socket = socket
},
setFollowRequests(state, value) {
state.followRequests = value
},
},
actions: {
// Follow requests
startFetchingFollowRequests(store) {
if (store.state.fetchers.followRequests) return
const fetcher = followRequestFetcher.startFetching({
store,
credentials: useOAuthStore().token,
})
store.commit('addFetcher', { fetcherName: 'followRequests', fetcher })
},
stopFetchingFollowRequests(store) {
const fetcher = store.state.fetchers.followRequests
if (!fetcher) return
store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher })
},
// Pleroma websocket
setWsToken(store, token) {
store.commit('setWsToken', token)
},
initializeSocket({ commit, state, rootState }) {
// Set up websocket connection
const token = state.wsToken
if (
useInstanceCapabilitiesStore().shoutAvailable &&
token !== undefined &&
state.socket === null
) {
const socket = new Socket('/socket', { params: { token } })
socket.connect()
commit('setSocket', socket)
useShoutStore().initializeShout(socket)
}
},
disconnectFromSocket({ commit, state }) {
state.socket?.disconnect()
commit('setSocket', null)
},
},
}
export default api

View file

@ -1,9 +1,5 @@
import api from './api.js'
import drafts from './drafts.js'
import profileConfig from './profileConfig.js'
export default {
api,
profileConfig,
drafts,
}

View file

@ -1,33 +0,0 @@
import { useUsersStore } from 'src/stores/users.js'
import { fetchFollowRequests } from 'src/api/user.js'
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
const fetchAndUpdate = ({ store, credentials }) => {
return fetchFollowRequests({ credentials })
.then(
(result) => {
const { data: requests } = result
store.commit('setFollowRequests', requests)
useUsersStore().addNewUsers(result)
},
(rej) => {
console.error(rej)
},
)
.catch((e) => {
console.error(e)
})
}
const startFetching = ({ credentials, store }) => {
const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })
boundFetchAndUpdate()
return promiseInterval(boundFetchAndUpdate, 10000)
}
const followRequestFetcher = {
startFetching,
}
export default followRequestFetcher

View file

@ -98,13 +98,11 @@ export const unseenNotifications = (
}
export const countExtraNotifications = (
store,
mergedConfig,
unreadChatsCount,
unreadAnnouncementCount,
unreadAnnouncementsCount,
followRequestsCount,
) => {
const rootGetters = store.rootGetters || store.getters
if (!mergedConfig.showExtraNotifications) {
return 0
}
@ -112,10 +110,10 @@ export const countExtraNotifications = (
return [
mergedConfig.showChatsInExtraNotifications ? unreadChatsCount : 0,
mergedConfig.showAnnouncementsInExtraNotifications
? unreadAnnouncementCount
? unreadAnnouncementsCount
: 0,
mergedConfig.showFollowRequestsInExtraNotifications
? rootGetters.followRequestCount
? followRequestsCount
: 0,
].reduce((a, c) => a + c, 0)
}

View file

@ -0,0 +1,44 @@
import { ref } from 'vue'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useUsersStore } from 'src/stores/users.js'
import { fetchFollowRequests } from 'src/api/user.js'
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
const followRequestFetcher = ({ credentials }) => {
const interval = ref(null)
const fetchAndUpdate = () => {
return fetchFollowRequests({ credentials })
.then((result) => {
const { data: requests } = result
useFollowRequestsStore().setFollowRequests(requests)
useUsersStore().addNewUsers(result)
})
.catch((e) => {
console.error(e)
})
}
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 {
fetchAndUpdate,
startFetching,
stopFetching,
}
}
export default followRequestFetcher

View file

@ -0,0 +1,37 @@
import { defineStore } from 'pinia'
import followRequestFetcher from 'src/stores/fetchers/follow_requests.js'
import { useOAuthStore } from 'src/stores/oauth.js'
export const useFollowRequestsStore = defineStore('followRequests', {
state: () => ({
fetcher: null,
requests: new Map(),
}),
getters: {
followRequestsCount(state) {
return state.requests.size
},
},
actions: {
startFetching() {
if (this.fetcher) throw 'Fetcher already exists!'
this.fetcher = followRequestFetcher({
credentials: useOAuthStore().token,
})
this.fetcher.startFetching()
},
stopFetching() {
if (!this.fetcher) throw "Fetcher doesn't exists!"
this.fetcher.stopFetching(), (this.fetcher = null)
},
setFollowRequests(requests) {
this.requests = new Map(requests.map((user) => [user.id, user]))
},
remove(id) {
this.requests.delete(id)
},
},
})

View file

@ -1,34 +1,36 @@
import { get, set } from 'lodash'
import { defineStore } from 'pinia'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useUsersStore } from 'src/stores/users.js'
import { updateNotificationSettings, updateProfile } from 'src/api/user.js'
import { updateNotificationSettings, updateProfileJSON } from 'src/api/user.js'
const defaultApi = ({ rootState, commit }, { path, value }) => {
const defaultApi = async ({ path, value }) => {
const params = {}
set(params, path, value)
return updateProfile({
return await updateProfileJSON({
params,
credentials: useOAuthStore().token,
}).then((result) => {
useUsersStore().addNewUsers(result)
})
}
const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => {
const notificationsApi = async ({ path, value, oldValue }) => {
const settings = {}
set(settings, path, value)
return updateNotificationSettings({
const result = await updateNotificationSettings({
settings,
credentials: useOAuthStore().token,
}).then(({ data: result }) => {
if (result.status === 'success') {
commit('confirmProfileOption', { name, value })
} else {
commit('confirmProfileOption', { name, value: oldValue })
}
})
if (result.data.status === 'success') {
// a bit of a hack
return { ...result, success: true }
} else {
throw new Error('Failed updating notification settings', result)
}
}
/**
@ -84,60 +86,65 @@ export const settingsMap = {
// NotificationSettingsAPIs
webPushHideContents: {
get: 'pleroma.notification_settings.hide_notification_contents',
set: 'hide_notification_contents',
set: 'hideNotificationContents',
api: notificationsApi,
},
blockNotificationsFromStrangers: {
get: 'pleroma.notification_settings.block_from_strangers',
set: 'block_from_strangers',
set: 'blockFromStrangers',
api: notificationsApi,
},
}
export const defaultState = Object.fromEntries(
Object.keys(settingsMap).map((key) => [key, null]),
)
export const defaultState = () => ({
config: Object.fromEntries(Object.keys(settingsMap).map((key) => [key, null]))
})
const profileConfig = {
state: { ...defaultState },
mutations: {
confirmProfileOption(state, { name, value }) {
set(state, name, value)
},
wipeProfileOption(state, { name }) {
set(state, name, null)
},
wipeAllProfileOptions(state) {
Object.keys(settingsMap).forEach((key) => {
set(state, key, null)
})
export const useProfileConfigStore = defineStore('profileConfig', {
state: defaultState,
actions: {
confirmProfileOption({ name, value }) {
set(this.config, name, value)
},
// Set the settings based on their path location
setCurrentUser(state, user) {
async setProfileOption({ name, value }) {
const oldValue = get(this, name)
const map = settingsMap[name]
if (!map) throw new Error('Invalid server-side setting')
const { set: path = map, api = defaultApi } = map
set(this.config, name, null)
try {
const result = await api({ path, value, oldValue })
const { success } = result
if (success) {
set(this.config, name, value)
return
}
useUsersStore().addNewUsers(result)
this.update(user)
} catch (e) {
console.warn('Error setting server-side option:', e)
set(this.config, name, oldValue)
}
},
update(user) {
Object.entries(settingsMap).forEach((map) => {
const [name, value] = map
const { get: path = value } = value
set(state, name, get(user._original, path))
set(this.config, name, get(user._original, path))
})
},
onLogin(user) {
this.update(user)
},
onLogout() {
Object.keys(settingsMap).forEach((key) => {
set(this.config, key, null)
})
},
},
actions: {
setProfileOption({ rootState, state, commit }, { name, value }) {
const oldValue = get(state, name)
const map = settingsMap[name]
if (!map) throw new Error('Invalid server-side setting')
const { set: path = map, api = defaultApi } = map
commit('wipeProfileOption', { name })
api({ rootState, commit }, { path, value, oldValue }).catch((e) => {
console.warn('Error setting server-side option:', e)
commit('confirmProfileOption', { name, value: oldValue })
})
},
logout({ commit }) {
commit('wipeAllProfileOptions')
},
},
}
export default profileConfig
})

View file

@ -1,14 +1,33 @@
import { Socket } from 'phoenix'
import { defineStore } from 'pinia'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useUsersStore } from 'src/stores/users.js'
// Maybe rename it to PhoenixSocket if we ever utilize this socket more
export const useShoutStore = defineStore('shout', {
state: () => ({
messages: [],
channel: { state: '' },
joined: false,
token: null,
socket: null,
}),
getters: {
token: () => useUsersStore().currentUser?.token,
},
actions: {
initializeShout(socket) {
const channel = socket.channel('chat:public')
initializeSocket() {
if (this.token === null) return
if (!useInstanceCapabilitiesStore().shoutAvailable) return
if (this.socket !== null) throw new Error('Shout socket already exist!')
this.socket = new Socket('/socket', { params: { token: this.token } })
this.socket.connect()
},
initializeShout() {
const channel = this.socket.channel('chat:public')
channel.joinPush.receive('ok', () => {
this.joined = true
})
@ -28,5 +47,9 @@ export const useShoutStore = defineStore('shout', {
channel.join()
this.channel = channel
},
disconnectSocket() {
this.socket?.disconnect()
this.socket = null
},
},
})

View file

@ -108,7 +108,19 @@ export const useStreamingStore = defineStore('streaming', {
}
},
initSocket(initial) {
if (this.socket) throw new Error('Socket already exists!')
if (this.socket) {
console.error(
"Socket already exists! This shouldn't happen! Force-closing the socket just in case",
)
try {
this.socket.close()
} catch (e) {
console.error(
"Error closing existing socket that shouldn't be there. Welp. Moving on.",
e,
)
}
}
this.state = initial
? WSConnectionStatus.STARTING_INITIAL
@ -130,7 +142,9 @@ export const useStreamingStore = defineStore('streaming', {
this.socket.addEventListener('error', this.onError)
},
stopSocket() {
this.socket.close()
if (this.socket == null)
console.error("Socket is already stopped! This shouldn't happen!")
this.socket?.close()
this.socket = null
this.state = WSConnectionStatus.CLOSED
this.retrying = false
@ -235,7 +249,7 @@ export const useStreamingStore = defineStore('streaming', {
)
setTimeout(() => {
if (this.retrying) return // retry aborted (i.e. due to logout)
if (!this.retrying) return // retry aborted (i.e. due to logout)
this.initSocket()
}, retryTimeout(this.retryMultiplier))

View file

@ -6,6 +6,7 @@ import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useEmojiStore } from 'src/stores/emoji.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface.js'
@ -13,9 +14,11 @@ import { useListsStore } from 'src/stores/lists.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useNotificationsStore } from 'src/stores/notifications.js'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useShoutStore } from 'src/stores/shout.js'
import { useStatusesStore } from 'src/stores/statuses.js'
import { useStreamingStore } from 'src/stores/streaming.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { useProfileConfigStore } from 'src/stores/profile_config.js'
import { useTimelinesStore } from 'src/stores/timelines.js'
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
@ -140,6 +143,7 @@ export const useUsersStore = defineStore('users', {
if (user.id === this.currentUser?.id) {
this.currentUser = reactive
useProfileConfigStore().update(reactive)
}
// Initialize some stuff
@ -274,9 +278,8 @@ export const useUsersStore = defineStore('users', {
map.set(identifier, promise)
const result = await promise
try {
const result = await promise
if (result) {
const { id, screen_name } = result
@ -646,6 +649,7 @@ export const useUsersStore = defineStore('users', {
console.error('Error setting theme', e)
})
})
useProfileConfigStore().onLogin(user)
useUserHighlightStore().initUserHighlight(user)
@ -668,8 +672,8 @@ export const useUsersStore = defineStore('users', {
if (user.token) {
// Shoutbox
dispatch('setWsToken', user.token)
dispatch('initializeSocket')
useShoutStore().initializeSocket()
useShoutStore().initializeShout()
}
// DMs and Home
@ -686,6 +690,7 @@ export const useUsersStore = defineStore('users', {
if (user.locked) {
dispatch('startFetchingFollowRequests')
useFollowRequestsStore().startFetching()
}
if (useMergedConfigStore().mergedConfig.useStreamingApi) {
@ -730,6 +735,9 @@ export const useUsersStore = defineStore('users', {
useListsStore().stopFetching()
useBookmarkFoldersStore().stopFetching()
useChatsStore().stopFetching()
if (this.currentUser.locked) {
useFollowRequestsStore().stopFetching()
}
store?.dispatch('stopFetchingFollowRequests')
@ -748,6 +756,7 @@ export const useUsersStore = defineStore('users', {
})
.then(() => {
oauth.clearToken()
useShoutStore().disconnectSocket()
this.currentUser = null
@ -773,6 +782,7 @@ export const useUsersStore = defineStore('users', {
Cookies.remove('__Host-pleroma_key', { path: '/' })
useInterfaceStore().onLogout()
useProfileConfigStore().onLogout()
})
.catch((e) => {
useInterfaceStore().pushGlobalNotice({

View file

@ -20,7 +20,7 @@
dependencies:
"@babel/highlight" "^7.0.0"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.26.2":
"@babel/code-frame@^7.0.0":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
@ -113,17 +113,6 @@
eslint-visitor-keys "^2.1.0"
semver "^6.3.1"
"@babel/generator@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c"
integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==
dependencies:
"@babel/parser" "^7.27.0"
"@babel/types" "^7.27.0"
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.25"
jsesc "^3.0.2"
"@babel/generator@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e"
@ -281,14 +270,6 @@
"@babel/traverse" "^7.29.7"
"@babel/types" "^7.29.7"
"@babel/helper-module-imports@^7.0.0-beta.49":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
dependencies:
"@babel/traverse" "^7.25.9"
"@babel/types" "^7.25.9"
"@babel/helper-module-imports@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204"
@ -479,13 +460,6 @@
js-tokens "^4.0.0"
picocolors "^1.0.0"
"@babel/parser@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec"
integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==
dependencies:
"@babel/types" "^7.27.0"
"@babel/parser@^7.27.2", "@babel/parser@^7.28.0", "@babel/parser@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71"
@ -1131,15 +1105,6 @@
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326"
integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
"@babel/template@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4"
integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==
dependencies:
"@babel/code-frame" "^7.26.2"
"@babel/parser" "^7.27.0"
"@babel/types" "^7.27.0"
"@babel/template@^7.27.1", "@babel/template@^7.27.2":
version "7.27.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
@ -1158,19 +1123,6 @@
"@babel/parser" "^7.29.7"
"@babel/types" "^7.29.7"
"@babel/traverse@^7.25.9":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70"
integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==
dependencies:
"@babel/code-frame" "^7.26.2"
"@babel/generator" "^7.27.0"
"@babel/parser" "^7.27.0"
"@babel/template" "^7.27.0"
"@babel/types" "^7.27.0"
debug "^4.3.1"
globals "^11.1.0"
"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3":
version "7.28.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434"
@ -1223,7 +1175,7 @@
"@babel/types" "^7.29.7"
debug "^4.3.1"
"@babel/types@^7.0.0-beta.49", "@babel/types@^7.25.9", "@babel/types@^7.27.0", "@babel/types@^7.4.4":
"@babel/types@^7.25.9", "@babel/types@^7.4.4":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559"
integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==
@ -1804,7 +1756,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28":
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
version "0.3.30"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99"
integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==
@ -3393,17 +3345,6 @@ axios@^1.7.4:
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-plugin-lodash@3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz#4f6844358a1340baed182adbeffa8df9967bc196"
integrity sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg==
dependencies:
"@babel/helper-module-imports" "^7.0.0-beta.49"
"@babel/types" "^7.0.0-beta.49"
glob "^7.1.1"
lodash "^4.17.10"
require-package-name "^2.0.1"
babel-plugin-polyfill-corejs2@^0.4.14:
version "0.4.14"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz#8101b82b769c568835611542488d463395c2ef8f"
@ -5404,7 +5345,7 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
glob@7.2.3, glob@^7.1.1, glob@^7.1.4, glob@^7.2.3:
glob@7.2.3, glob@^7.1.4, glob@^7.2.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@ -5455,11 +5396,6 @@ global-prefix@^3.0.0:
kind-of "^6.0.2"
which "^1.3.1"
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
@ -6443,6 +6379,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash-es@4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@ -6488,7 +6429,7 @@ lodash.union@^4.6.0:
resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==
lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.21:
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -7763,11 +7704,6 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
require-package-name@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9"
integrity sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"