Merge branch 'users-statuses-pinia' into shigusegubu-themes3
This commit is contained in:
commit
f62e323400
216 changed files with 7262 additions and 5413 deletions
1
changelog.d/actions-extra-tap.fix
Normal file
1
changelog.d/actions-extra-tap.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
fixed tapping "Mute..." and "Change visiblity" (admin action) in extra status actions closing the dropdown
|
||||
1
changelog.d/config-migration.fix
Normal file
1
changelog.d/config-migration.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix legacy settings migration being discarded after it ran.
|
||||
1
changelog.d/fix-chat-post.fix
Normal file
1
changelog.d/fix-chat-post.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix error dialog when posting in chat
|
||||
1
changelog.d/fix-domain-mute.fix
Normal file
1
changelog.d/fix-domain-mute.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
domain mute fixedw
|
||||
1
changelog.d/followers-list.fix
Normal file
1
changelog.d/followers-list.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix followers list showing followed users as non-followed
|
||||
1
changelog.d/media-proxy-sw-cache.fix
Normal file
1
changelog.d/media-proxy-sw-cache.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Prevent service worker from caching media-proxy responses
|
||||
1
changelog.d/moderation-tools.fix
Normal file
1
changelog.d/moderation-tools.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
fix moderation tools button possibly not appearing for admins
|
||||
1
changelog.d/moderation.fix
Normal file
1
changelog.d/moderation.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fixed moderation actions requiring confirmation closing user popover
|
||||
1
changelog.d/streaming.add
Normal file
1
changelog.d/streaming.add
Normal file
|
|
@ -0,0 +1 @@
|
|||
More timelines now can utilize WebSocket streaming
|
||||
1
changelog.d/streaming.fix
Normal file
1
changelog.d/streaming.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
switches between streaming and polling should be more reliable now
|
||||
1
changelog.d/streaming_indicator.add
Normal file
1
changelog.d/streaming_indicator.add
Normal file
|
|
@ -0,0 +1 @@
|
|||
Added an indicator next to instance's name showing WebSocket connection status (if enabled)
|
||||
1
changelog.d/theme-index-html-fallback.fix
Normal file
1
changelog.d/theme-index-html-fallback.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix theme lists failing to load when custom resource indexes are unavailable
|
||||
1
changelog.d/themes2-font.fix
Normal file
1
changelog.d/themes2-font.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix Themes 2.0 applying incorrect fonts
|
||||
1
changelog.d/themes3.fix
Normal file
1
changelog.d/themes3.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fixed themes 3 not loading in appearance tab
|
||||
|
|
@ -21,6 +21,7 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useShoutStore } from 'src/stores/shout.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
// Helper to unwrap reactive proxies
|
||||
window.toValue = (x) => JSON.parse(JSON.stringify(x))
|
||||
|
|
@ -153,11 +154,8 @@ export default {
|
|||
...(navbarColumnStretch ? ['-column-stretch'] : []),
|
||||
]
|
||||
},
|
||||
currentUser() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
userBackground() {
|
||||
return this.currentUser.background_image
|
||||
return this.currentUser?.background_image
|
||||
},
|
||||
foreignProfileBackground() {
|
||||
return (
|
||||
|
|
@ -246,6 +244,7 @@ export default {
|
|||
'styleDataUsed',
|
||||
'layoutType',
|
||||
]),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useInstanceStore, ['styleDataUsed']),
|
||||
...mapState(useInstanceCapabilitiesStore, [
|
||||
'suggestionsEnabled',
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
class="column -scrollable"
|
||||
:class="{ '-show-scrollbar': showScrollbars }"
|
||||
>
|
||||
<user-panel />
|
||||
<UserPanel />
|
||||
<template v-if="layoutType !== 'mobile'">
|
||||
<NavPanel />
|
||||
<InstanceSpecificPanel v-if="showInstanceSpecificPanel" />
|
||||
|
|
|
|||
|
|
@ -63,10 +63,11 @@ export const paramsString = (params = {}) => {
|
|||
}
|
||||
|
||||
export const promisedRequest = async ({
|
||||
method,
|
||||
method = 'GET',
|
||||
url,
|
||||
payload,
|
||||
formData,
|
||||
forceContentType,
|
||||
cache,
|
||||
credentials,
|
||||
headers = {},
|
||||
|
|
@ -75,7 +76,7 @@ export const promisedRequest = async ({
|
|||
method,
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Accept: forceContentType ?? 'application/json',
|
||||
...headers,
|
||||
},
|
||||
}
|
||||
|
|
@ -110,7 +111,7 @@ export const promisedRequest = async ({
|
|||
)
|
||||
if (contentLength === 0) return null
|
||||
|
||||
switch (contentType) {
|
||||
switch (forceContentType ?? contentType) {
|
||||
case 'text/plain':
|
||||
return await response.text()
|
||||
case 'application/json':
|
||||
|
|
@ -123,7 +124,7 @@ export const promisedRequest = async ({
|
|||
const { ok, status } = response
|
||||
|
||||
if (ok) {
|
||||
return { response, status, data }
|
||||
return { response, status, data, timestamp: Date.now() }
|
||||
} else {
|
||||
throw new StatusCodeError(response.status, data, { url, options }, response)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
|
|||
const MASTODON_PASSWORD_RESET_URL = ({ email }) =>
|
||||
`/auth/password${paramsString({ email })}`
|
||||
|
||||
const MASTODON_FOLLOWING_URL = (
|
||||
export const MASTODON_FOLLOWING_URL = (
|
||||
id,
|
||||
{ minId, maxId, sinceId, limit, withRelationships },
|
||||
) =>
|
||||
`/api/v1/accounts/${id}/following${paramsString({ minId, maxId, sinceId, limit, withRelationships })}`
|
||||
const MASTODON_FOLLOWERS_URL = (
|
||||
export const MASTODON_FOLLOWERS_URL = (
|
||||
id,
|
||||
{ minId, maxId, sinceId, limit, withRelationships },
|
||||
) =>
|
||||
|
|
@ -26,15 +26,17 @@ const MASTODON_FOLLOWERS_URL = (
|
|||
|
||||
export const MASTODON_STATUS_URL = (id) => `/api/v1/statuses/${id}`
|
||||
const MASTODON_STATUS_CONTEXT_URL = (id) => `/api/v1/statuses/${id}/context`
|
||||
const MASTODON_STATUS_SOURCE_URL = (id) => `/api/v1/statuses/${id}/source`
|
||||
const MASTODON_STATUS_HISTORY_URL = (id) => `/api/v1/statuses/${id}/history`
|
||||
export const MASTODON_STATUS_SOURCE_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/source`
|
||||
export const MASTODON_STATUS_HISTORY_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/history`
|
||||
const MASTODON_USER_URL = '/api/v1/accounts'
|
||||
const MASTODON_USER_LOOKUP_URL = ({ acct }) =>
|
||||
`/api/v1/accounts/lookup${paramsString({ acct })}`
|
||||
const MASTODON_POLL_URL = (id = '') => `/api/v1/polls/${id}`
|
||||
const MASTODON_STATUS_FAVORITEDBY_URL = (id) =>
|
||||
export const MASTODON_STATUS_FAVORITEDBY_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/favourited_by`
|
||||
const MASTODON_STATUS_REBLOGGEDBY_URL = (id) =>
|
||||
export const MASTODON_STATUS_REBLOGGEDBY_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/reblogged_by`
|
||||
const MASTODON_SEARCH_2 = ({
|
||||
q,
|
||||
|
|
@ -51,7 +53,7 @@ const MASTODON_SEARCH_2 = ({
|
|||
const MASTODON_USER_SEARCH_URL = ({ q, resolve }) =>
|
||||
`/api/v1/accounts/search${paramsString({ q, resolve })}`
|
||||
const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers'
|
||||
const PLEROMA_EMOJI_REACTIONS_URL = (id) =>
|
||||
export const PLEROMA_EMOJI_REACTIONS_URL = (id) =>
|
||||
`/api/v1/pleroma/statuses/${id}/reactions`
|
||||
const PLEROMA_SCROBBLES_URL = (id, { maxId, sinceId, minId, limit, offset }) =>
|
||||
`/api/v1/pleroma/accounts/${id}/scrobbles${paramsString({ maxId, sinceId, minId, limit, offset })}`
|
||||
|
|
@ -117,9 +119,17 @@ export const fetchUserByName = ({ name, credentials }) =>
|
|||
|
||||
export const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_FOLLOWING_URL(id, { maxId, sinceId, limit }),
|
||||
url: MASTODON_FOLLOWING_URL(id, {
|
||||
maxId,
|
||||
sinceId,
|
||||
limit,
|
||||
withRelationships: true,
|
||||
}),
|
||||
credentials,
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||
}).then(({ data, ...rest }) => ({
|
||||
...rest,
|
||||
data: data.map(parseUser),
|
||||
}))
|
||||
|
||||
export const fetchFollowers = ({
|
||||
id,
|
||||
|
|
@ -136,7 +146,10 @@ export const fetchFollowers = ({
|
|||
withRelationships: true,
|
||||
}),
|
||||
credentials,
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: data.map(parseUser) }))
|
||||
}).then(({ data, ...rest }) => ({
|
||||
...rest,
|
||||
data: data.map(parseUser),
|
||||
}))
|
||||
|
||||
export const fetchConversation = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
|
|
@ -163,15 +176,18 @@ export const fetchStatusSource = ({ id, credentials }) =>
|
|||
credentials,
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseSource(data) }))
|
||||
|
||||
export const fetchStatusHistory = ({ status, credentials }) =>
|
||||
export const fetchStatusHistory = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_STATUS_HISTORY_URL(status.id),
|
||||
url: MASTODON_STATUS_HISTORY_URL(id),
|
||||
credentials,
|
||||
}).then(({ data, ...rest }) => {
|
||||
return [...data].reverse().map((item) => {
|
||||
item.originalStatus = status
|
||||
return { ...rest, data: parseStatus(item) }
|
||||
})
|
||||
return {
|
||||
...rest,
|
||||
data: [...data].reverse().map((item) => {
|
||||
item.originalStatus = status
|
||||
return parseStatus(item)
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
export const listEmojiPacks = ({ page, pageSize, credentials }) =>
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ export const fetchTimeline = ({
|
|||
publicAndExternal: MASTODON_PUBLIC_TIMELINE,
|
||||
dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
|
||||
user: MASTODON_USER_TIMELINE_URL,
|
||||
userPinned: MASTODON_USER_TIMELINE_URL,
|
||||
media: MASTODON_USER_TIMELINE_URL,
|
||||
list: MASTODON_LIST_TIMELINE_URL,
|
||||
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
|
||||
|
|
@ -130,6 +131,7 @@ export const fetchTimeline = ({
|
|||
|
||||
const twoArgs = new Set([
|
||||
'user',
|
||||
'userPinned',
|
||||
'media',
|
||||
'list',
|
||||
'publicFavorites',
|
||||
|
|
@ -147,6 +149,7 @@ export const fetchTimeline = ({
|
|||
const id = (() => {
|
||||
switch (timeline) {
|
||||
case 'user':
|
||||
case 'userPinned':
|
||||
case 'media':
|
||||
return userId
|
||||
case 'list':
|
||||
|
|
@ -163,6 +166,9 @@ export const fetchTimeline = ({
|
|||
if (timeline === 'media') {
|
||||
params.onlyMedia = true
|
||||
}
|
||||
if (timeline === 'userPinned') {
|
||||
params.pinned = true
|
||||
}
|
||||
if (timeline === 'public') {
|
||||
params.local = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ 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_READ_URL = '/api/v1/pleroma/notifications/read'
|
||||
export const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
|
||||
|
||||
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
||||
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
||||
|
|
@ -27,15 +27,16 @@ const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp'
|
|||
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
||||
const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/totp'
|
||||
|
||||
const MASTODON_DISMISS_NOTIFICATION_URL = (id) =>
|
||||
export const MASTODON_DISMISS_NOTIFICATION_URL = (id) =>
|
||||
`/api/v1/notifications/${id}/dismiss`
|
||||
const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite`
|
||||
const MASTODON_UNFAVORITE_URL = (id) => `/api/v1/statuses/${id}/unfavourite`
|
||||
const MASTODON_RETWEET_URL = (id) => `/api/v1/statuses/${id}/reblog`
|
||||
const MASTODON_UNRETWEET_URL = (id) => `/api/v1/statuses/${id}/unreblog`
|
||||
const MASTODON_DELETE_URL = (id) => `/api/v1/statuses/${id}`
|
||||
const MASTODON_FOLLOW_URL = (id) => `/api/v1/accounts/${id}/follow`
|
||||
const MASTODON_UNFOLLOW_URL = (id) => `/api/v1/accounts/${id}/unfollow`
|
||||
export const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite`
|
||||
export const MASTODON_UNFAVORITE_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/unfavourite`
|
||||
export const MASTODON_RETWEET_URL = (id) => `/api/v1/statuses/${id}/reblog`
|
||||
export const MASTODON_UNRETWEET_URL = (id) => `/api/v1/statuses/${id}/unreblog`
|
||||
export const MASTODON_DELETE_URL = (id) => `/api/v1/statuses/${id}`
|
||||
export const MASTODON_FOLLOW_URL = (id) => `/api/v1/accounts/${id}/follow`
|
||||
export const MASTODON_UNFOLLOW_URL = (id) => `/api/v1/accounts/${id}/unfollow`
|
||||
|
||||
const MASTODON_FOLLOW_REQUESTS_URL = '/api/v1/follow_requests'
|
||||
const MASTODON_APPROVE_USER_URL = (id) =>
|
||||
|
|
@ -43,49 +44,54 @@ const MASTODON_APPROVE_USER_URL = (id) =>
|
|||
const MASTODON_DENY_USER_URL = (id) => `/api/v1/follow_requests/${id}/reject`
|
||||
const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) =>
|
||||
`/api/v1/accounts/relationships/${paramsString({ id, withSuspended })}`
|
||||
const MASTODON_USER_IN_LISTS = (id) => `/api/v1/accounts/${id}/lists`
|
||||
export const MASTODON_USER_IN_LISTS = (id) => `/api/v1/accounts/${id}/lists`
|
||||
export const MASTODON_LIST_URL = (id = '') => `/api/v1/lists/${id}`
|
||||
export const MASTODON_LIST_ACCOUNTS_URL = (id) => `/api/v1/lists/${id}/accounts`
|
||||
const MASTODON_USER_BLOCKS_URL = ({
|
||||
export const MASTODON_USER_BLOCKS_URL = ({
|
||||
maxId,
|
||||
sinceId,
|
||||
limit,
|
||||
withRelationships,
|
||||
}) =>
|
||||
`/api/v1/blocks/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
||||
const MASTODON_USER_MUTES_URL = ({
|
||||
export const MASTODON_USER_MUTES_URL = ({
|
||||
maxId,
|
||||
sinceId,
|
||||
limit,
|
||||
withRelationships,
|
||||
}) =>
|
||||
`/api/v1/mutes/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
||||
const MASTODON_BLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/block`
|
||||
const MASTODON_UNBLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/unblock`
|
||||
const MASTODON_MUTE_USER_URL = (id) => `/api/v1/accounts/${id}/mute`
|
||||
const MASTODON_UNMUTE_USER_URL = (id) => `/api/v1/accounts/${id}/unmute`
|
||||
const MASTODON_REMOVE_USER_FROM_FOLLOWERS = (id) =>
|
||||
export const MASTODON_BLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/block`
|
||||
export const MASTODON_UNBLOCK_USER_URL = (id) =>
|
||||
`/api/v1/accounts/${id}/unblock`
|
||||
export const MASTODON_MUTE_USER_URL = (id) => `/api/v1/accounts/${id}/mute`
|
||||
export const MASTODON_UNMUTE_USER_URL = (id) => `/api/v1/accounts/${id}/unmute`
|
||||
export const MASTODON_REMOVE_USER_FROM_FOLLOWERS_URL = (id) =>
|
||||
`/api/v1/accounts/${id}/remove_from_followers`
|
||||
const MASTODON_USER_NOTE_URL = (id) => `/api/v1/accounts/${id}/note`
|
||||
const MASTODON_BOOKMARK_STATUS_URL = (id) => `/api/v1/statuses/${id}/bookmark`
|
||||
const MASTODON_UNBOOKMARK_STATUS_URL = (id) =>
|
||||
export const MASTODON_USER_NOTE_URL = (id) => `/api/v1/accounts/${id}/note`
|
||||
export const MASTODON_BOOKMARK_STATUS_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/bookmark`
|
||||
export const MASTODON_UNBOOKMARK_STATUS_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/unbookmark`
|
||||
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
||||
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
||||
const MASTODON_VOTE_URL = (id) => `/api/v1/polls/${id}/votes`
|
||||
const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
|
||||
const MASTODON_REPORT_USER_URL = '/api/v1/reports'
|
||||
const MASTODON_PIN_OWN_STATUS = (id) => `/api/v1/statuses/${id}/pin`
|
||||
const MASTODON_UNPIN_OWN_STATUS = (id) => `/api/v1/statuses/${id}/unpin`
|
||||
const MASTODON_MUTE_CONVERSATION = (id) => `/api/v1/statuses/${id}/mute`
|
||||
const MASTODON_UNMUTE_CONVERSATION = (id) => `/api/v1/statuses/${id}/unmute`
|
||||
const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks'
|
||||
export const MASTODON_PIN_OWN_STATUS_URL = (id) => `/api/v1/statuses/${id}/pin`
|
||||
export const MASTODON_UNPIN_OWN_STATUS_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/unpin`
|
||||
export const MASTODON_MUTE_CONVERSATION_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/mute`
|
||||
export const MASTODON_UNMUTE_CONVERSATION_URL = (id) =>
|
||||
`/api/v1/statuses/${id}/unmute`
|
||||
export const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks'
|
||||
const MASTODON_ANNOUNCEMENTS_URL = '/api/v1/announcements'
|
||||
const MASTODON_ANNOUNCEMENTS_DISMISS_URL = (id) =>
|
||||
`/api/v1/announcements/${id}/dismiss`
|
||||
const PLEROMA_EMOJI_REACT_URL = (id, emoji) =>
|
||||
export const PLEROMA_EMOJI_REACT_URL = (id, emoji) =>
|
||||
`/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
|
||||
const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) =>
|
||||
export const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) =>
|
||||
`/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
|
||||
const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
|
||||
const PLEROMA_BOOKMARK_FOLDERS_URL = '/api/v1/pleroma/bookmark_folders'
|
||||
|
|
@ -143,39 +149,39 @@ export const bookmarkStatus = ({ id, credentials, ...options }) =>
|
|||
payload: {
|
||||
folder_id: options.folder_id,
|
||||
},
|
||||
})
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||
|
||||
export const unbookmarkStatus = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_UNBOOKMARK_STATUS_URL(id),
|
||||
credentials,
|
||||
method: 'POST',
|
||||
})
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||
|
||||
export const pinOwnStatus = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_PIN_OWN_STATUS(id),
|
||||
url: MASTODON_PIN_OWN_STATUS_URL(id),
|
||||
credentials,
|
||||
method: 'POST',
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||
|
||||
export const unpinOwnStatus = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_UNPIN_OWN_STATUS(id),
|
||||
url: MASTODON_UNPIN_OWN_STATUS_URL(id),
|
||||
credentials,
|
||||
method: 'POST',
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||
|
||||
export const muteConversation = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_MUTE_CONVERSATION(id),
|
||||
url: MASTODON_MUTE_CONVERSATION_URL(id),
|
||||
credentials,
|
||||
method: 'POST',
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||
|
||||
export const unmuteConversation = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_UNMUTE_CONVERSATION(id),
|
||||
url: MASTODON_UNMUTE_CONVERSATION_URL(id),
|
||||
credentials,
|
||||
method: 'POST',
|
||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||
|
|
@ -410,7 +416,6 @@ export const exportFriends = ({ id, credentials }) => {
|
|||
id,
|
||||
maxId,
|
||||
credentials,
|
||||
withRelationships: true,
|
||||
})
|
||||
friends = [...friends, ...users]
|
||||
if (users.length === 0) {
|
||||
|
|
@ -657,7 +662,7 @@ export const fetchUserInLists = ({ id, credentials }) =>
|
|||
|
||||
export const removeUserFromFollowers = ({ id, credentials }) =>
|
||||
promisedRequest({
|
||||
url: MASTODON_REMOVE_USER_FROM_FOLLOWERS(id),
|
||||
url: MASTODON_REMOVE_USER_FROM_FOLLOWERS_URL(id),
|
||||
credentials,
|
||||
method: 'POST',
|
||||
})
|
||||
|
|
|
|||
|
|
@ -26,8 +26,26 @@ const PLEROMA_STREAMING_EVENTS = new Set([
|
|||
'pleroma:respond',
|
||||
])
|
||||
|
||||
export const WSConnectionStatus = Object.freeze({
|
||||
JOINED: 1,
|
||||
CLOSED: 2,
|
||||
ERROR: 3,
|
||||
DISABLED: 4,
|
||||
STARTING: 5,
|
||||
STARTING_INITIAL: 6,
|
||||
})
|
||||
|
||||
export class WSEvent extends Event {
|
||||
data
|
||||
|
||||
constructor(name, data, original) {
|
||||
super(name)
|
||||
this.data = data
|
||||
}
|
||||
}
|
||||
|
||||
// A thin wrapper around WebSocket API that allows adding a pre-processor to it
|
||||
// Uses EventTarget and a CustomEvent to proxy events
|
||||
// Uses EventTarget and a WSEvent to proxy events
|
||||
export const ProcessedWS = ({
|
||||
url,
|
||||
preprocessor = handleMastoWS,
|
||||
|
|
@ -39,9 +57,7 @@ export const ProcessedWS = ({
|
|||
if (!socket) throw new Error(`Failed to create socket ${id}`)
|
||||
const proxy = (original, eventName, processor = (a) => a) => {
|
||||
original.addEventListener(eventName, (eventData) => {
|
||||
eventTarget.dispatchEvent(
|
||||
new CustomEvent(eventName, { detail: processor(eventData) }),
|
||||
)
|
||||
eventTarget.dispatchEvent(new WSEvent(eventName, processor(eventData)))
|
||||
})
|
||||
}
|
||||
socket.addEventListener('open', (wsEvent) => {
|
||||
|
|
@ -75,7 +91,7 @@ export const ProcessedWS = ({
|
|||
/**/
|
||||
|
||||
const onAuthenticated = () => {
|
||||
eventTarget.dispatchEvent(new CustomEvent('pleroma:authenticated'))
|
||||
eventTarget.dispatchEvent(new WSEvent('pleroma:authenticated'))
|
||||
}
|
||||
|
||||
proxy(socket, 'open')
|
||||
|
|
@ -126,14 +142,14 @@ export const handleMastoWS = (
|
|||
const { data } = wsEvent
|
||||
if (!data) return
|
||||
const parsedEvent = JSON.parse(data)
|
||||
const { event, payload } = parsedEvent
|
||||
const { event, stream, payload } = parsedEvent
|
||||
if (
|
||||
MASTODON_STREAMING_EVENTS.has(event) ||
|
||||
PLEROMA_STREAMING_EVENTS.has(event)
|
||||
) {
|
||||
// MastoBE and PleromaBE both send payload for delete as a PLAIN string
|
||||
if (event === 'delete') {
|
||||
return { event, id: payload }
|
||||
return { event, stream, id: payload }
|
||||
}
|
||||
const data = payload ? JSON.parse(payload) : null
|
||||
if (event === 'pleroma:respond') {
|
||||
|
|
@ -150,25 +166,16 @@ export const handleMastoWS = (
|
|||
}
|
||||
return null
|
||||
} else if (event === 'update') {
|
||||
return { event, status: parseStatus(data) }
|
||||
return { event, stream, status: parseStatus(data) }
|
||||
} else if (event === 'status.update') {
|
||||
return { event, status: parseStatus(data) }
|
||||
return { event, stream, status: parseStatus(data) }
|
||||
} else if (event === 'notification') {
|
||||
return { event, notification: parseNotification(data) }
|
||||
return { event, stream, notification: parseNotification(data) }
|
||||
} else if (event === 'pleroma:chat_update') {
|
||||
return { event, chatUpdate: parseChat(data) }
|
||||
return { event, stream, chatUpdate: parseChat(data) }
|
||||
}
|
||||
} else {
|
||||
console.warn('Unknown event', wsEvent)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const WSConnectionStatus = Object.freeze({
|
||||
JOINED: 1,
|
||||
CLOSED: 2,
|
||||
ERROR: 3,
|
||||
DISABLED: 4,
|
||||
STARTING: 5,
|
||||
STARTING_INITIAL: 6,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -36,9 +36,12 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import VBodyScrollLock from 'src/directives/body_scroll_lock'
|
||||
import {
|
||||
|
|
@ -454,7 +457,7 @@ const setConfig = async ({ store }) => {
|
|||
const checkOAuthToken = async ({ store }) => {
|
||||
const oauth = useOAuthStore()
|
||||
if (oauth.userToken) {
|
||||
return store.dispatch('loginUser', oauth.userToken)
|
||||
return useUsersStore().loginUser(oauth.userToken)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -541,7 +544,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
|||
window.highlightConfig = useUserHighlightStore()
|
||||
|
||||
FaviconService.initFaviconService()
|
||||
initServiceWorker(store)
|
||||
initServiceWorker(useNotificationsStore())
|
||||
|
||||
window.addEventListener('focus', () => updateFocus())
|
||||
|
||||
|
|
@ -589,6 +592,10 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
|||
|
||||
useI18nStore().setI18n(i18n)
|
||||
|
||||
// Global WS handlers
|
||||
useInterfaceStore().attachSocket()
|
||||
useStatusesStore().attachSocket()
|
||||
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
app.use(i18n)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,16 @@
|
|||
import AuthForm from 'src/components/auth_form/auth_form.js'
|
||||
import BookmarkTimeline from 'src/components/bookmark_timeline/bookmark_timeline.vue'
|
||||
import BubbleTimeline from 'src/components/bubble_timeline/bubble_timeline.vue'
|
||||
import ConversationPage from 'src/components/conversation-page/conversation-page.vue'
|
||||
import DMs from 'src/components/dm_timeline/dm_timeline.vue'
|
||||
import FriendsTimeline from 'src/components/friends_timeline/friends_timeline.vue'
|
||||
import NavPanel from 'src/components/nav_panel/nav_panel.vue'
|
||||
import PublicAndExternalTimeline from 'src/components/public_and_external_timeline/public_and_external_timeline.vue'
|
||||
import PublicTimeline from 'src/components/public_timeline/public_timeline.vue'
|
||||
import QuotesTimeline from 'src/components/quotes_timeline/quotes_timeline.vue'
|
||||
import RemoteUserResolver from 'src/components/remote_user_resolver/remote_user_resolver.vue'
|
||||
import TagTimeline from 'src/components/tag_timeline/tag_timeline.vue'
|
||||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
export default (store) => {
|
||||
export default () => {
|
||||
const validateAuthenticatedRoute = (to, from, next) => {
|
||||
if (store.state.users.currentUser) {
|
||||
if (useUsersStore().currentUser) {
|
||||
next()
|
||||
} else {
|
||||
next(
|
||||
|
|
@ -31,7 +25,7 @@ export default (store) => {
|
|||
path: '/',
|
||||
redirect: () => {
|
||||
return (
|
||||
(store.state.users.currentUser
|
||||
(useUsersStore().currentUser
|
||||
? useInstanceStore().instanceIdentity.redirectRootLogin
|
||||
: useInstanceStore().instanceIdentity.redirectRootNoLogin) ||
|
||||
'/main/all'
|
||||
|
|
@ -41,22 +35,52 @@ export default (store) => {
|
|||
{
|
||||
name: 'public-external-timeline',
|
||||
path: '/main/all',
|
||||
component: PublicAndExternalTimeline,
|
||||
component: Timeline,
|
||||
props: () => ({
|
||||
timelineRef: { name: 'publicAndExternal' },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'public-timeline',
|
||||
path: '/main/public',
|
||||
component: PublicTimeline,
|
||||
component: Timeline,
|
||||
props: () => ({
|
||||
timelineRef: { name: 'public' },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'friends',
|
||||
path: '/main/friends',
|
||||
component: FriendsTimeline,
|
||||
component: Timeline,
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
props: () => ({
|
||||
timelineRef: { name: 'friends' },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'tag-timeline',
|
||||
path: '/tag/:id',
|
||||
component: Timeline,
|
||||
props: (route) => ({
|
||||
timelineRef: { name: 'tag', argument: route.params.id },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'bookmarks',
|
||||
path: '/bookmarks',
|
||||
component: Timeline,
|
||||
props: (route) => ({
|
||||
timelineRef: { name: 'bookmarks', argument: null },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'bubble',
|
||||
path: '/bubble',
|
||||
component: Timeline,
|
||||
props: () => ({
|
||||
timelineRef: { name: 'bubble' },
|
||||
}),
|
||||
},
|
||||
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
|
||||
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
|
||||
{ name: 'bubble', path: '/bubble', component: BubbleTimeline },
|
||||
{
|
||||
name: 'conversation',
|
||||
path: '/notice/:id',
|
||||
|
|
@ -71,7 +95,14 @@ export default (store) => {
|
|||
meta: { dontScroll: true },
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
},
|
||||
{ name: 'quotes', path: '/notice/:id/quotes', component: QuotesTimeline },
|
||||
{
|
||||
name: 'quotes',
|
||||
path: '/notice/:id/quotes',
|
||||
component: Timeline,
|
||||
props: (route) => ({
|
||||
timelineRef: { name: 'quotes', argument: route.params.id },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'remote-user-profile-acct',
|
||||
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
||||
|
|
@ -104,8 +135,11 @@ export default (store) => {
|
|||
{
|
||||
name: 'dms',
|
||||
path: '/users/:username/dms',
|
||||
component: DMs,
|
||||
component: Timeline,
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
props: () => ({
|
||||
timelineRef: { name: 'dms' },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'registration',
|
||||
|
|
@ -202,8 +236,10 @@ export default (store) => {
|
|||
{
|
||||
name: 'lists-timeline',
|
||||
path: '/lists/:id',
|
||||
component: () =>
|
||||
import('src/components/lists_timeline/lists_timeline.vue'),
|
||||
component: Timeline,
|
||||
props: (route) => ({
|
||||
timelineRef: { name: 'list', argument: route.params.id },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'lists-edit',
|
||||
|
|
@ -237,7 +273,10 @@ export default (store) => {
|
|||
{
|
||||
name: 'bookmark-folder',
|
||||
path: '/bookmarks/:id',
|
||||
component: BookmarkTimeline,
|
||||
component: Timeline,
|
||||
props: (route) => ({
|
||||
timelineRef: { name: 'bookmarks', argument: route.params.id },
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'bookmark-folder-edit',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import UserListMenu from 'src/components/user_list_menu/user_list_menu.vue'
|
|||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useReportsStore } from 'src/stores/reports'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -47,10 +48,10 @@ const AccountActions = {
|
|||
this.showingConfirmBlock = false
|
||||
},
|
||||
showRepeats() {
|
||||
this.$store.dispatch('showReblogs', this.user.id)
|
||||
useUsersStore().showReblogs(this.user.id)
|
||||
},
|
||||
hideRepeats() {
|
||||
this.$store.dispatch('hideReblogs', this.user.id)
|
||||
useUsersStore().hideReblogs(this.user.id)
|
||||
},
|
||||
blockUser() {
|
||||
if (this.$refs.timedBlockDialog) {
|
||||
|
|
@ -64,11 +65,11 @@ const AccountActions = {
|
|||
}
|
||||
},
|
||||
doBlockUser() {
|
||||
this.$store.dispatch('blockUser', { id: this.user.id })
|
||||
useUsersStore().blockUser(this.user.id)
|
||||
this.hideConfirmBlock()
|
||||
},
|
||||
unblockUser() {
|
||||
this.$store.dispatch('unblockUser', this.user.id)
|
||||
useUsersStore().unblockUser(this.user.id)
|
||||
},
|
||||
removeUserFromFollowers() {
|
||||
if (!this.shouldConfirmRemoveUserFromFollowers) {
|
||||
|
|
@ -78,7 +79,7 @@ const AccountActions = {
|
|||
}
|
||||
},
|
||||
doRemoveUserFromFollowers() {
|
||||
this.$store.dispatch('removeUserFromFollowers', this.user.id)
|
||||
useUsersStore().removeUserFromFollowers(this.user.id)
|
||||
this.hideConfirmRemoveUserFromFollowers()
|
||||
},
|
||||
reportUser() {
|
||||
|
|
@ -88,7 +89,7 @@ const AccountActions = {
|
|||
this.$router.push({
|
||||
name: 'chat',
|
||||
params: {
|
||||
username: this.$store.state.users.currentUser.screen_name,
|
||||
username: useUsersStore().currentUser.screen_name,
|
||||
recipient_id: this.user.id,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import AnnouncementEditor from 'src/components/announcement_editor/announcement_editor.vue'
|
||||
import localeService from '../../services/locale/locale.service.js'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const Announcement = {
|
||||
components: {
|
||||
|
|
@ -25,9 +26,7 @@ const Announcement = {
|
|||
announcement: Object,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
canEditAnnouncement() {
|
||||
return this.currentUser?.privileges.has(
|
||||
'announcements_manage_announcements',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import Announcement from 'src/components/announcement/announcement.vue'
|
||||
import AnnouncementEditor from 'src/components/announcement_editor/announcement_editor.vue'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const AnnouncementsPage = {
|
||||
components: {
|
||||
|
|
@ -26,9 +27,7 @@ const AnnouncementsPage = {
|
|||
useAnnouncementsStore().fetchAnnouncements()
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
announcements() {
|
||||
return useAnnouncementsStore().announcements
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
class="avatars-item"
|
||||
>
|
||||
<UserAvatar
|
||||
:user="user"
|
||||
:user-id="user.id"
|
||||
class="avatar-small"
|
||||
/>
|
||||
</router-link>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
>
|
||||
<UserAvatar
|
||||
class="user-avatar avatar"
|
||||
:user="user"
|
||||
:user-id="user.id"
|
||||
@click.prevent
|
||||
/>
|
||||
</UserPopover>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
|||
import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const BlockCard = {
|
||||
props: ['userId'],
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.findUser(this.userId)
|
||||
return useUsersStore().findUser(this.userId)
|
||||
},
|
||||
relationship() {
|
||||
return this.$store.getters.relationship(this.userId)
|
||||
return useUsersStore().relationship(this.userId)
|
||||
},
|
||||
blocked() {
|
||||
return this.relationship.blocking
|
||||
|
|
@ -35,13 +36,13 @@ const BlockCard = {
|
|||
},
|
||||
methods: {
|
||||
unblockUser() {
|
||||
this.$store.dispatch('unblockUser', this.user.id)
|
||||
useUsersStore().unblockUser(this.user.id)
|
||||
},
|
||||
blockUser() {
|
||||
if (this.blockExpiration) {
|
||||
this.$refs.timedBlockDialog.optionallyPrompt()
|
||||
} else {
|
||||
this.$store.dispatch('blockUser', { id: this.user.id })
|
||||
useUsersStore().blockUser(this.user.id)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const Bookmarks = {
|
||||
created() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'bookmarks',
|
||||
bookmarkFolderId: this.folderId || null,
|
||||
})
|
||||
},
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
folderId() {
|
||||
return this.$route.params.id
|
||||
},
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.bookmarks
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
folderId() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
|
||||
this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'bookmarks',
|
||||
bookmarkFolderId: this.folderId || null,
|
||||
})
|
||||
},
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
|
||||
this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
|
||||
},
|
||||
}
|
||||
|
||||
export default Bookmarks
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.bookmarks')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'bookmarks'"
|
||||
:bookmark-folder-id="folderId"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./bookmark_timeline.js"></script>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const BubbleTimeline = {
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.bubble
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'bubble' })
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'bubble')
|
||||
},
|
||||
}
|
||||
|
||||
export default BubbleTimeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.bubble')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'bubble'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./bubble_timeline.js"></script>
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import ChatListItem from 'src/components/chat_list_item/chat_list_item.vue'
|
||||
import ChatNew from 'src/components/chat_new/chat_new.vue'
|
||||
import List from 'src/components/list/list.vue'
|
||||
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const ChatList = {
|
||||
components: {
|
||||
|
|
@ -14,10 +14,8 @@ const ChatList = {
|
|||
ChatNew,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapPiniaState(useChatsStore, ['sortedChatList']),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useChatsStore, ['sortedChatList']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import AvatarList from 'src/components/avatar_list/avatar_list.vue'
|
||||
import ChatTitle from 'src/components/chat_title/chat_title.vue'
|
||||
|
|
@ -6,6 +6,8 @@ import StatusBody from 'src/components/status_content/status_content.vue'
|
|||
import Timeago from 'src/components/timeago/timeago.vue'
|
||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const ChatListItem = {
|
||||
name: 'ChatListItem',
|
||||
props: ['chat'],
|
||||
|
|
@ -17,9 +19,7 @@ const ChatListItem = {
|
|||
StatusBody,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
attachmentInfo() {
|
||||
if (this.chat.lastMessage.attachments.length === 0) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
>
|
||||
<div class="chat-list-item-left">
|
||||
<UserAvatar
|
||||
:user="chat.account"
|
||||
:user-id="chat.account.id"
|
||||
height="48px"
|
||||
width="48px"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import Attachment from 'src/components/attachment/attachment.vue'
|
||||
import ChatMessageDate from 'src/components/chat_message_date/chat_message_date.vue'
|
||||
|
|
@ -20,6 +19,8 @@ import UserPopover from 'src/components/user_popover/user_popover.vue'
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -79,7 +80,7 @@ const ChatMessage = {
|
|||
return this.isStatus ? this.message.user.id : this.message.account_id
|
||||
},
|
||||
author() {
|
||||
return this.$store.getters.findUser(this.authorId)
|
||||
return useUsersStore().findUser(this.authorId)
|
||||
},
|
||||
isCurrentUser() {
|
||||
// mini-hack/optimizaiton:
|
||||
|
|
@ -100,25 +101,21 @@ const ChatMessage = {
|
|||
return !this.message.in_reply_to_status_id
|
||||
},
|
||||
customReplyTo() {
|
||||
return this.$store.state.statuses.allStatusesObject[
|
||||
this.message.in_reply_to_status_id
|
||||
]
|
||||
return useStatusesStore().allStatuses.get(
|
||||
this.message.in_reply_to_status_id,
|
||||
)
|
||||
},
|
||||
replyToName() {
|
||||
if (this.message.in_reply_to_screen_name) {
|
||||
return this.message.in_reply_to_screen_name
|
||||
} else {
|
||||
const user = this.$store.getters.findUser(
|
||||
this.message.in_reply_to_user_id,
|
||||
)
|
||||
const user = useUsersStore().findUser(this.message.in_reply_to_user_id)
|
||||
return user?.screen_name_ui
|
||||
}
|
||||
},
|
||||
replyProfileLink() {
|
||||
if (this.isCustomReply) {
|
||||
const user = this.$store.getters.findUser(
|
||||
this.message.in_reply_to_user_id,
|
||||
)
|
||||
const user = useUsersStore().findUser(this.message.in_reply_to_user_id)
|
||||
// FIXME Why user not found sometimes???
|
||||
return user ? user.statusnet_profile_url : 'NOT_FOUND'
|
||||
}
|
||||
|
|
@ -167,14 +164,12 @@ const ChatMessage = {
|
|||
},
|
||||
|
||||
// Global stuff
|
||||
...mapPiniaState(useInterfaceStore, {
|
||||
...mapState(useInterfaceStore, {
|
||||
betterShadow: (store) => store.browserSupport.cssFilter,
|
||||
}),
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
restrictedNicknames: (state) => useInstanceStore().restrictedNicknames,
|
||||
}),
|
||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useInstanceStore, ['restrictedNicknames']),
|
||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
<UserAvatar
|
||||
v-if="author"
|
||||
:compact="true"
|
||||
:user="author"
|
||||
:user-id="author.id"
|
||||
/>
|
||||
</UserPopover>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { mapGetters, mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { chats } from 'src/api/chats.js'
|
||||
|
||||
|
|
@ -42,10 +43,7 @@ const chatNew = {
|
|||
return this.suggestions
|
||||
}
|
||||
},
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapGetters(['findUser']),
|
||||
...mapState(useUsersStore, ['currentUser', 'findUser']),
|
||||
},
|
||||
methods: {
|
||||
goBack() {
|
||||
|
|
@ -73,7 +71,8 @@ const chatNew = {
|
|||
this.loading = true
|
||||
this.userIds = []
|
||||
this.$store
|
||||
.dispatch('search', { q: query, resolve: true, type: 'accounts' })
|
||||
this.useSearchStore()
|
||||
.search({ q: query, resolve: true, type: 'accounts' })
|
||||
.then((data) => {
|
||||
this.loading = false
|
||||
this.userIds = data.accounts.map((a) => a.id)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
>
|
||||
<UserAvatar
|
||||
class="titlebar-avatar"
|
||||
:user="user"
|
||||
:user-id="user.id"
|
||||
/>
|
||||
</UserPopover>
|
||||
<RichContent
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { get, maxBy, minBy, sortBy, throttle } from 'lodash'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'pinia'
|
||||
import { nextTick } from 'vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
||||
import ChatTitle from 'src/components/chat_title/chat_title.vue'
|
||||
|
|
@ -19,6 +18,9 @@ import { useChatsStore } from 'src/stores/chats.js'
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useStreamingStore } from 'src/stores/streaming.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import {
|
||||
chatMessages,
|
||||
|
|
@ -85,6 +87,8 @@ const Chat = {
|
|||
|
||||
// Internal network stuff
|
||||
fetcher: null,
|
||||
socket: null,
|
||||
streaming: false,
|
||||
errorLoadingChat: false,
|
||||
messageRetriers: {},
|
||||
idempotencyKeyIndex: {},
|
||||
|
|
@ -92,7 +96,8 @@ const Chat = {
|
|||
},
|
||||
created() {
|
||||
if (this.testMode) return
|
||||
this.startFetching()
|
||||
this.activate()
|
||||
this.attachSocket()
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
|
|
@ -118,10 +123,13 @@ const Chat = {
|
|||
this.handleVisibilityChange,
|
||||
false,
|
||||
)
|
||||
|
||||
if (this.testMode) return
|
||||
this.deactivate()
|
||||
},
|
||||
computed: {
|
||||
conversationId() {
|
||||
const status = this.$store.state.statuses.allStatusesObject[this.statusId]
|
||||
const status = useStatusesStore().allStatuses.get(this.statusId)
|
||||
return get(
|
||||
status,
|
||||
'retweeted_status.statusnet_conversation_id',
|
||||
|
|
@ -157,17 +165,14 @@ const Chat = {
|
|||
if (this.isConversation) return false // Unsupported
|
||||
return (
|
||||
this.mergedConfig.useStreamingApi &&
|
||||
this.mastoUserSocketStatus === WSConnectionStatus.JOINED
|
||||
useStreamingStore().state === WSConnectionStatus.JOINED
|
||||
)
|
||||
},
|
||||
...mapPiniaState(useInterfaceStore, {
|
||||
...mapState(useInterfaceStore, {
|
||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||
}),
|
||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapState({
|
||||
mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus,
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
},
|
||||
watch: {
|
||||
messages(old, neu) {
|
||||
|
|
@ -226,16 +231,85 @@ const Chat = {
|
|||
return
|
||||
}
|
||||
|
||||
this.clear()
|
||||
this.startFetching()
|
||||
},
|
||||
mastoUserSocketStatus(newValue) {
|
||||
if (newValue === WSConnectionStatus.JOINED) {
|
||||
this.fetchChat({ isFirstFetch: true })
|
||||
}
|
||||
this.deactivate()
|
||||
this.activate()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async activate() {
|
||||
if (!this.isConversation) {
|
||||
try {
|
||||
const result = await getOrCreateChat({
|
||||
accountId: this.chatUserId,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
useUsersStore().addNewUsers(result)
|
||||
const { data } = result
|
||||
data.account = useUsersStore().findUser(data.account.id)
|
||||
this.chat = data
|
||||
} catch (e) {
|
||||
console.error('Error creating or getting a chat', e)
|
||||
this.errorLoadingChat = true
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isConversation || this.chat) {
|
||||
this.$nextTick(() => {
|
||||
this.scrollDown({ forceRead: true })
|
||||
})
|
||||
this.startFetching('Chat activated', true)
|
||||
}
|
||||
},
|
||||
deactivate() {
|
||||
this.clear()
|
||||
if (!this.streaming) {
|
||||
this.stopFetching()
|
||||
}
|
||||
},
|
||||
attachSocket() {
|
||||
const et = new EventTarget()
|
||||
const socket = { et }
|
||||
|
||||
et.addEventListener('update', this.onStreamMessage)
|
||||
et.addEventListener('open', this.onStreamConnect)
|
||||
et.addEventListener('close', this.onStreamDisconnect)
|
||||
|
||||
useStreamingStore().addSubscriber(socket)
|
||||
this.socket = socket
|
||||
},
|
||||
detachSocket() {
|
||||
const { et } = this.socket
|
||||
|
||||
et.removeEventListener('update', this.onStreamMessage)
|
||||
et.removeEventListener('open', this.onStreamConnect)
|
||||
et.removeEventListener('close', this.onStreamDisconnect)
|
||||
|
||||
useStreamingStore().removeSubscriber(this.socket)
|
||||
},
|
||||
|
||||
// Poll & Push
|
||||
onStreamConnect() {
|
||||
this.streaming = true
|
||||
this.stopFetching('Socket connected')
|
||||
},
|
||||
onStreamDisconnect(closeEvent) {
|
||||
this.streaming = false
|
||||
this.startFetching('Socket disconnected')
|
||||
},
|
||||
startFetching(reason, isFirstFetch) {
|
||||
console.debug('[Chat View] Started fetching', 'Reason:', reason)
|
||||
this.fetcher = promiseInterval(
|
||||
() => this.fetchChat({ fetchLatest: true }),
|
||||
5000,
|
||||
)
|
||||
this.fetchChat({ isFirstFetch })
|
||||
},
|
||||
stopFetching(reason) {
|
||||
console.debug('[Chat View] Stopped fetching', 'Reason:', reason)
|
||||
this.fetcher.stop()
|
||||
this.fetcher = null
|
||||
},
|
||||
|
||||
// Actions
|
||||
async readChat() {
|
||||
if (this.conversationId) return // Unsupported
|
||||
|
|
@ -259,18 +333,8 @@ const Chat = {
|
|||
this.lastReadMessageId = this.maxId
|
||||
this.newMessageCount = 0
|
||||
},
|
||||
scrollDown(options = {}) {
|
||||
const { behavior = 'auto', forceRead = false } = options
|
||||
this.$nextTick(() => {
|
||||
window.scrollTo({
|
||||
top: document.documentElement.scrollHeight,
|
||||
behavior,
|
||||
})
|
||||
})
|
||||
if (forceRead) {
|
||||
this.readChat()
|
||||
}
|
||||
},
|
||||
|
||||
// Clears
|
||||
cullOlder() {
|
||||
const maxIndex = this.messages.length
|
||||
const minIndex = maxIndex - 50
|
||||
|
|
@ -366,35 +430,12 @@ const Chat = {
|
|||
})
|
||||
}
|
||||
},
|
||||
async startFetching() {
|
||||
if (!this.isConversation) {
|
||||
try {
|
||||
const { data } = await getOrCreateChat({
|
||||
accountId: this.chatUserId,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
this.$store.commit('addNewUsers', [data.account])
|
||||
data.account = this.$store.getters.findUser(data.account.id)
|
||||
this.chat = data
|
||||
} catch (e) {
|
||||
console.error('Error creating or getting a chat', e)
|
||||
this.errorLoadingChat = true
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isConversation || this.chat) {
|
||||
this.$nextTick(() => {
|
||||
this.scrollDown({ forceRead: true })
|
||||
})
|
||||
this.doStartFetching()
|
||||
}
|
||||
},
|
||||
doStartFetching() {
|
||||
this.fetcher = promiseInterval(
|
||||
() => this.fetchChat({ fetchLatest: true }),
|
||||
5000,
|
||||
onStreamMessage({ data }) {
|
||||
const messages = data.filter(
|
||||
({ statusnet_conversation_id }) =>
|
||||
statusnet_conversation_id === this.conversationId,
|
||||
)
|
||||
this.fetchChat({ isFirstFetch: true })
|
||||
this.addMessages({ messages })
|
||||
},
|
||||
addMessages({ messages: newMessages }) {
|
||||
for (let i = 0; i < newMessages.length; i++) {
|
||||
|
|
@ -438,9 +479,6 @@ const Chat = {
|
|||
}
|
||||
}
|
||||
},
|
||||
goBack() {
|
||||
this.$router.back()
|
||||
},
|
||||
|
||||
// Optimistic posting (chats only)
|
||||
async sendMessage({ status, media, idempotencyKey }) {
|
||||
|
|
@ -538,11 +576,14 @@ const Chat = {
|
|||
|
||||
// Event handlers
|
||||
onPosted(data) {
|
||||
this.explicitReplyStatus = null
|
||||
this.$router.push({
|
||||
name: 'conversation2',
|
||||
params: { statusId: data.id },
|
||||
})
|
||||
// only conversation poster has the returned data
|
||||
if (this.isConversation) {
|
||||
this.explicitReplyStatus = null
|
||||
this.$router.push({
|
||||
name: 'conversation2',
|
||||
params: { statusId: data.id },
|
||||
})
|
||||
}
|
||||
},
|
||||
handleVisibilityChange() {
|
||||
this.$nextTick(() => {
|
||||
|
|
@ -618,6 +659,23 @@ const Chat = {
|
|||
})
|
||||
},
|
||||
|
||||
// Misc
|
||||
scrollDown(options = {}) {
|
||||
const { behavior = 'auto', forceRead = false } = options
|
||||
this.$nextTick(() => {
|
||||
window.scrollTo({
|
||||
top: document.documentElement.scrollHeight,
|
||||
behavior,
|
||||
})
|
||||
})
|
||||
if (forceRead) {
|
||||
this.readChat()
|
||||
}
|
||||
},
|
||||
goBack() {
|
||||
this.$router.back()
|
||||
},
|
||||
|
||||
// Ugly
|
||||
// TODO move to ChatMessage
|
||||
async deleteChatMessage({ chatId, messageId }) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import { defineAsyncComponent } from 'vue'
|
|||
import Select from 'src/components/select/select.vue'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
export default {
|
||||
props: ['type', 'user', 'status'],
|
||||
|
|
@ -33,9 +35,7 @@ export default {
|
|||
return this.status.conversation_muted
|
||||
},
|
||||
domainIsMuted() {
|
||||
return new Set(this.$store.state.users.currentUser.domainMutes).has(
|
||||
this.domain,
|
||||
)
|
||||
return new Set(useUsersStore().currentUser.domainMutes).has(this.domain)
|
||||
},
|
||||
shouldConfirm() {
|
||||
switch (this.type) {
|
||||
|
|
@ -70,17 +70,17 @@ export default {
|
|||
switch (this.type) {
|
||||
case 'domain': {
|
||||
if (!this.domainIsMuted) {
|
||||
this.$store.dispatch('muteDomain', this.domain)
|
||||
useUsersStore().muteDomain(this.domain)
|
||||
} else {
|
||||
this.$store.dispatch('unmuteDomain', this.domain)
|
||||
useUsersStore().unmuteDomain(this.domain)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'conversation': {
|
||||
if (!this.conversationIsMuted) {
|
||||
this.$store.dispatch('muteConversation', { id: this.status.id })
|
||||
useStatusesStore().muteConversation(this.status.id)
|
||||
} else {
|
||||
this.$store.dispatch('unmuteConversation', { id: this.status.id })
|
||||
useStatusesStore().unmuteConversation(this.status.id)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { clone, filter, findIndex, get, reduce } from 'lodash'
|
||||
import { get, reduce } from 'lodash'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
|
|
@ -9,9 +9,10 @@ import QuickViewSettings from 'src/components/quick_view_settings/quick_view_set
|
|||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||
import ThreadTree from 'src/components/thread_tree/thread_tree.vue'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { fetchConversation, fetchStatus } from 'src/api/public.js'
|
||||
import { WSConnectionStatus } from 'src/api/websocket.js'
|
||||
|
|
@ -51,20 +52,6 @@ const sortById = (a, b) => {
|
|||
}
|
||||
}
|
||||
|
||||
const sortAndFilterConversation = (conversation, statusoid) => {
|
||||
if (statusoid.type === 'retweet') {
|
||||
conversation = filter(
|
||||
conversation,
|
||||
(status) =>
|
||||
status.type === 'retweet' ||
|
||||
status.id !== statusoid.retweeted_status.id,
|
||||
)
|
||||
} else {
|
||||
conversation = filter(conversation, (status) => status.type !== 'retweet')
|
||||
}
|
||||
return conversation.filter(Boolean).sort(sortById)
|
||||
}
|
||||
|
||||
const conversation = {
|
||||
props: {
|
||||
statusId: {
|
||||
|
|
@ -122,6 +109,9 @@ const conversation = {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
status() {
|
||||
return useStatusesStore().allStatuses.get(this.statusId)
|
||||
},
|
||||
maxDepthToShowByDefault() {
|
||||
// maxDepthInThread = max number of depths that is *visible*
|
||||
// since our depth starts with 0 and "showing" means "showing children"
|
||||
|
|
@ -165,9 +155,6 @@ const conversation = {
|
|||
hideStatus() {
|
||||
return this.virtualHidden && this.suspendable
|
||||
},
|
||||
status() {
|
||||
return this.$store.state.statuses.allStatusesObject[this.statusId]
|
||||
},
|
||||
originalStatusId() {
|
||||
if (this.status.retweeted_status) {
|
||||
return this.status.retweeted_status.id
|
||||
|
|
@ -187,15 +174,13 @@ const conversation = {
|
|||
return [this.status]
|
||||
}
|
||||
|
||||
const conversation = clone(
|
||||
this.$store.state.statuses.conversationsObject[this.conversationId],
|
||||
const conversation = useStatusesStore().conversations.get(
|
||||
this.conversationId,
|
||||
)
|
||||
const statusIndex = findIndex(conversation, { id: this.originalStatusId })
|
||||
if (statusIndex !== -1) {
|
||||
conversation[statusIndex] = this.status
|
||||
}
|
||||
|
||||
return sortAndFilterConversation(conversation, this.status)
|
||||
return [...conversation.keys()]
|
||||
.map((k) => useStatusesStore().allStatuses.get(k))
|
||||
.toSorted(sortById)
|
||||
},
|
||||
statusMap() {
|
||||
return this.conversation.reduce((res, s) => {
|
||||
|
|
@ -441,7 +426,7 @@ const conversation = {
|
|||
}
|
||||
},
|
||||
virtualHidden() {
|
||||
this.$store.dispatch('setVirtualHeight', {
|
||||
useStatusesStore().setVirtualHeight({
|
||||
statusId: this.statusId,
|
||||
height: `${this.$el.clientHeight}px`,
|
||||
})
|
||||
|
|
@ -453,9 +438,12 @@ const conversation = {
|
|||
fetchConversation({
|
||||
id: this.statusId,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: { ancestors, descendants } }) => {
|
||||
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
|
||||
this.$store.dispatch('addNewStatuses', { statuses: descendants })
|
||||
}).then(({ data: { ancestors, descendants }, timestamp }) => {
|
||||
useStatusesStore().addNewStatuses({ statuses: ancestors, timestamp })
|
||||
useStatusesStore().addNewStatuses({
|
||||
statuses: descendants,
|
||||
timestamp,
|
||||
})
|
||||
this.setFocused(this.originalStatusId)
|
||||
})
|
||||
} else {
|
||||
|
|
@ -465,7 +453,7 @@ const conversation = {
|
|||
credentials: useOAuthStore().token,
|
||||
})
|
||||
.then(({ data: status }) => {
|
||||
this.$store.dispatch('addNewStatuses', { statuses: [status] })
|
||||
useStatusesStore().addNewStatuses({ statuses: [status] })
|
||||
this.fetchConversation()
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -482,17 +470,17 @@ const conversation = {
|
|||
this.focused = id
|
||||
|
||||
if (!this.streamingEnabled) {
|
||||
this.$store.dispatch('fetchStatus', id)
|
||||
useStatusesStore().fetchStatus(id)
|
||||
}
|
||||
|
||||
this.$store.dispatch('fetchFavsAndRepeats', id)
|
||||
this.$store.dispatch('fetchEmojiReactionsBy', id)
|
||||
useStatusesStore().fetchFavsAndRepeats(id)
|
||||
useStatusesStore().fetchEmojiReactions(id)
|
||||
},
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded
|
||||
},
|
||||
getConversationId(statusId) {
|
||||
const status = this.$store.state.statuses.allStatusesObject[statusId]
|
||||
const status = useStatusesStore().allStatuses.get(statusId)
|
||||
return get(
|
||||
status,
|
||||
'retweeted_status.statusnet_conversation_id',
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@
|
|||
ref="statusComponent"
|
||||
class="conversation-status status-fadein panel-body"
|
||||
|
||||
:statusoid="status"
|
||||
:status-id="status.id"
|
||||
:replies="getReplies(status.id)"
|
||||
|
||||
:expandable="!isExpanded"
|
||||
|
|
@ -152,7 +152,7 @@
|
|||
ref="statusComponent"
|
||||
:depth="0"
|
||||
|
||||
:status="status"
|
||||
:status-id="status.id"
|
||||
:in-profile="inProfile"
|
||||
:conversation="conversation"
|
||||
:collapsable="collapsable"
|
||||
|
|
@ -186,7 +186,7 @@
|
|||
:key="status.id"
|
||||
ref="statusComponent"
|
||||
class="conversation-status status-fadein panel-body"
|
||||
:statusoid="status"
|
||||
:status-id="status.id"
|
||||
:replies="getReplies(status.id)"
|
||||
|
||||
:expandable="!isExpanded"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import { defineAsyncComponent } from 'vue'
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStreamingStore } from 'src/stores/streaming.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { WSConnectionStatus } from 'src/api/websocket.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -14,6 +18,8 @@ import {
|
|||
faComments,
|
||||
faHome,
|
||||
faInfoCircle,
|
||||
faPlug,
|
||||
faPlugCircleXmark,
|
||||
faSearch,
|
||||
faSignInAlt,
|
||||
faSignOutAlt,
|
||||
|
|
@ -33,6 +39,8 @@ library.add(
|
|||
faTachometerAlt,
|
||||
faCog,
|
||||
faInfoCircle,
|
||||
faPlug,
|
||||
faPlugCircleXmark,
|
||||
)
|
||||
|
||||
export default {
|
||||
|
|
@ -91,11 +99,23 @@ export default {
|
|||
sitename: (store) => store.instanceIdentity.name,
|
||||
hideSitename: (store) => store.instanceIdentity.hideSitename,
|
||||
}),
|
||||
currentUser() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useStreamingStore, {
|
||||
streamingConnected: (store) => store.state === WSConnectionStatus.JOINED,
|
||||
}),
|
||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||
shouldConfirmLogout() {
|
||||
return useMergedConfigStore().mergedConfig.modalOnLogout
|
||||
return this.mergedConfig.modalOnLogout
|
||||
},
|
||||
streamingEnabled() {
|
||||
return this.mergedConfig.useStreamingApi
|
||||
},
|
||||
streamingTooltip() {
|
||||
if (this.streamingConnected) {
|
||||
return this.$t('timeline.socket_reconnected')
|
||||
} else {
|
||||
return this.$t('timeline.socket_disconnected')
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -117,7 +137,7 @@ export default {
|
|||
},
|
||||
doLogout() {
|
||||
this.$router.replace('/main/public')
|
||||
this.$store.dispatch('logout')
|
||||
useUsersStore().logout()
|
||||
this.hideConfirmLogout()
|
||||
},
|
||||
onSearchBarToggled(hidden) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,24 @@
|
|||
>
|
||||
{{ sitename }}
|
||||
</router-link>
|
||||
<div
|
||||
class="nav-icon"
|
||||
v-if="streamingEnabled"
|
||||
:title="streamingTooltip"
|
||||
>
|
||||
<FAIcon
|
||||
v-if="streamingConnected"
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="plug"
|
||||
/>
|
||||
<FAIcon
|
||||
v-else
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="plug-circle-xmark"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<router-link
|
||||
class="logo"
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const DMs = {
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.dms
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
}
|
||||
|
||||
export default DMs
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.dms')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'dms'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./dm_timeline.js"></script>
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
||||
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const DomainMuteCard = {
|
||||
props: ['domain'],
|
||||
components: {
|
||||
|
|
@ -7,7 +9,7 @@ const DomainMuteCard = {
|
|||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
return useUsersStore().currentUser
|
||||
},
|
||||
muted() {
|
||||
return this.user.domainMutes.includes(this.domain)
|
||||
|
|
@ -15,10 +17,10 @@ const DomainMuteCard = {
|
|||
},
|
||||
methods: {
|
||||
unmuteDomain() {
|
||||
return this.$store.dispatch('unmuteDomain', this.domain)
|
||||
return useUsersStore().unmuteDomain(this.domain)
|
||||
},
|
||||
muteDomain() {
|
||||
return this.$store.dispatch('muteDomain', this.domain)
|
||||
return useUsersStore().muteDomain(this.domain)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import PostStatusForm from 'src/components/post_status_form/post_status_form.vue
|
|||
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faPollH } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -65,7 +66,7 @@ const Draft = {
|
|||
},
|
||||
refStatus() {
|
||||
return this.draft.refId
|
||||
? this.$store.state.statuses.allStatusesObject[this.draft.refId]
|
||||
? useStatusesStore().allStatuses.get(this.draft.refId)
|
||||
: undefined
|
||||
},
|
||||
localCollapseSubjectDefault() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { defineAsyncComponent } from 'vue'
|
|||
import Modal from 'src/components/modal/modal.vue'
|
||||
|
||||
import { useEditStatusStore } from 'src/stores/editStatus.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const EditStatusModal = {
|
||||
components: {
|
||||
|
|
@ -19,7 +20,7 @@ const EditStatusModal = {
|
|||
},
|
||||
computed: {
|
||||
isLoggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
return !!useUsersStore().currentUser
|
||||
},
|
||||
modalActivated() {
|
||||
return useEditStatusStore().modalActivated
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { useSearchStore } from 'src/stores/search.js'
|
||||
|
||||
/**
|
||||
* suggest - generates a suggestor function to be used by emoji-input
|
||||
* data: object providing source information for specific types of suggestions:
|
||||
|
|
@ -77,7 +79,7 @@ export const suggestUsers = ({ dispatch, state }) => {
|
|||
let timeout = null
|
||||
let cancelUserSearch = null
|
||||
|
||||
const userSearch = (query) => dispatch('searchUsers', { query })
|
||||
const userSearch = (query) => useSearchStore().searchUsers({ query })
|
||||
const debounceUserSearch = (query) => {
|
||||
cancelUserSearch?.()
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import UserListPopover from 'src/components/user_list_popover/user_list_popover.
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCheck, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -40,7 +42,7 @@ const EmojiReactions = {
|
|||
}, {})
|
||||
},
|
||||
loggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
return !!useUsersStore().currentUser
|
||||
},
|
||||
remoteInteractionLink() {
|
||||
return useInstanceStore().getRemoteInteractionLink({
|
||||
|
|
@ -58,25 +60,22 @@ const EmojiReactions = {
|
|||
reactedWith(emoji) {
|
||||
return this.status.emoji_reactions.find((r) => r.name === emoji).me
|
||||
},
|
||||
async fetchEmojiReactionsByIfMissing() {
|
||||
async fetchEmojiReactionsIfMissing() {
|
||||
const hasNoAccounts = this.status.emoji_reactions.find((r) => !r.accounts)
|
||||
if (hasNoAccounts) {
|
||||
return await this.$store.dispatch(
|
||||
'fetchEmojiReactionsBy',
|
||||
this.status.id,
|
||||
)
|
||||
if (!hasNoAccounts) {
|
||||
return await useStatusesStore().fetchEmojiReactions(this.status.id)
|
||||
}
|
||||
},
|
||||
reactWith(emoji) {
|
||||
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
||||
useStatusesStore().reactWithEmoji(this.status.id, emoji)
|
||||
},
|
||||
unreact(emoji) {
|
||||
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
|
||||
useStatusesStore().unreactWithEmoji(this.status.id, emoji)
|
||||
},
|
||||
async emojiOnClick(emoji) {
|
||||
if (!this.loggedIn) return
|
||||
|
||||
await this.fetchEmojiReactionsByIfMissing()
|
||||
await this.fetchEmojiReactionsIfMissing()
|
||||
if (this.reactedWith(emoji)) {
|
||||
this.unreact(emoji)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
class="emoji-reaction-popover"
|
||||
:normal-button="true"
|
||||
:trigger-attrs="counterTriggerAttrs(reaction)"
|
||||
@show="fetchEmojiReactionsByIfMissing()"
|
||||
@show="fetchEmojiReactionsIfMissing()"
|
||||
>
|
||||
<span class="emoji-reaction-counts">{{ reaction.count }}</span>
|
||||
</UserListPopover>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useChatsStore } from 'src/stores/chats.js'
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -52,7 +53,7 @@ const ExtraNotifications = {
|
|||
)
|
||||
},
|
||||
currentUser() {
|
||||
return this.$store.state.users.currentUser
|
||||
return useUsersStore().currentUser
|
||||
},
|
||||
...mapGetters(['followRequestCount']),
|
||||
...mapState(useAnnouncementsStore, {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import {
|
||||
requestFollow,
|
||||
requestUnfollow,
|
||||
} from '../../services/follow_manipulate/follow_manipulate'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
export default {
|
||||
props: ['relationship', 'user', 'labelFollowing', 'buttonClass'],
|
||||
components: {
|
||||
|
|
@ -64,9 +61,11 @@ export default {
|
|||
},
|
||||
follow() {
|
||||
this.inProgress = true
|
||||
requestFollow(this.relationship.id, this.$store).then(() => {
|
||||
this.inProgress = false
|
||||
})
|
||||
useUsersStore()
|
||||
.followUser(this.relationship.id)
|
||||
.finally(() => {
|
||||
this.inProgress = false
|
||||
})
|
||||
},
|
||||
unfollow() {
|
||||
if (this.shouldConfirmUnfollow) {
|
||||
|
|
@ -76,15 +75,12 @@ export default {
|
|||
}
|
||||
},
|
||||
doUnfollow() {
|
||||
const store = this.$store
|
||||
this.inProgress = true
|
||||
requestUnfollow(this.relationship.id, store).then(() => {
|
||||
this.inProgress = false
|
||||
store.commit('removeStatus', {
|
||||
timeline: 'friends',
|
||||
userId: this.relationship.id,
|
||||
useUsersStore()
|
||||
.unfollowUser(this.relationship.id)
|
||||
.finally(() => {
|
||||
this.inProgress = false
|
||||
})
|
||||
})
|
||||
|
||||
this.hideConfirmUnfollow()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import FollowButton from 'src/components/follow_button/follow_button.vue'
|
|||
import RemoteFollow from 'src/components/remote_follow/remote_follow.vue'
|
||||
import RemoveFollowerButton from 'src/components/remove_follower_button/remove_follower_button.vue'
|
||||
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const FollowCard = {
|
||||
props: ['user', 'noFollowsYou'],
|
||||
components: {
|
||||
|
|
@ -13,13 +15,13 @@ const FollowCard = {
|
|||
},
|
||||
computed: {
|
||||
isMe() {
|
||||
return this.$store.state.users.currentUser.id === this.user.id
|
||||
return useUsersStore().currentUser.id === this.user.id
|
||||
},
|
||||
loggedIn() {
|
||||
return this.$store.state.users.currentUser
|
||||
return useUsersStore().currentUser
|
||||
},
|
||||
relationship() {
|
||||
return this.$store.getters.relationship(this.user.id)
|
||||
return useUsersStore().relationships.get(this.user.id)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<basic-user-card :user="user">
|
||||
<BasicUserCard :user="user">
|
||||
<div class="follow-card-content-container">
|
||||
<span
|
||||
v-if="isMe || (!noFollowsYou && relationship.followed_by)"
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
/>
|
||||
</template>
|
||||
</div>
|
||||
</basic-user-card>
|
||||
</BasicUserCard>
|
||||
</template>
|
||||
|
||||
<script src="./follow_card.js"></script>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js'
|
||||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
|
||||
import { approveUser, denyUser } from 'src/api/user.js'
|
||||
|
|
@ -24,7 +24,7 @@ const FollowRequestCard = {
|
|||
},
|
||||
methods: {
|
||||
findFollowRequestNotificationId() {
|
||||
const notif = notificationsFromStore(this.$store).find(
|
||||
const notif = useNotificationsStore().data.find(
|
||||
(notif) =>
|
||||
notif.from_profile.id === this.user.id &&
|
||||
notif.type === 'follow_request',
|
||||
|
|
@ -55,16 +55,11 @@ const FollowRequestCard = {
|
|||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
// TODO fix
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
|
||||
const notifId = this.findFollowRequestNotificationId()
|
||||
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
|
||||
this.$store.dispatch('updateNotification', {
|
||||
id: notifId,
|
||||
updater: (notification) => {
|
||||
notification.type = 'follow'
|
||||
},
|
||||
})
|
||||
useNotificationsStore().markSingleNotificationAsSeen(notifId)
|
||||
this.hideApproveConfirmDialog()
|
||||
},
|
||||
denyUser() {
|
||||
|
|
@ -81,7 +76,8 @@ const FollowRequestCard = {
|
|||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(() => {
|
||||
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
|
||||
useNotificationsStore().dismissNotificationLocal(notifId)
|
||||
// TODO fix
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
})
|
||||
this.hideDenyConfirmDialog()
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const FriendsTimeline = {
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.friends
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default FriendsTimeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.timeline')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'friends'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./friends_timeline.js"></script>
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import Notifications from 'src/components/notifications/notifications.vue'
|
||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const tabModeDict = {
|
||||
mentions: ['mention'],
|
||||
statuses: ['status'],
|
||||
|
|
@ -14,10 +16,9 @@ const tabModeDict = {
|
|||
const Interactions = {
|
||||
data() {
|
||||
return {
|
||||
allowFollowingMove:
|
||||
this.$store.state.users.currentUser.allow_following_move,
|
||||
allowFollowingMove: useUsersStore().currentUser.allow_following_move,
|
||||
filterMode: tabModeDict.mentions,
|
||||
canSeeReports: this.$store.state.users.currentUser.privileges.has(
|
||||
canSeeReports: useUsersStore().currentUser.privileges.has(
|
||||
'reports_manage_reports',
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,12 +98,13 @@ const List = {
|
|||
|
||||
this.fetchFunction(this.page)
|
||||
.then((result) => {
|
||||
console.log(result)
|
||||
this.loading = false
|
||||
this.bottomedOut = isEmpty(result.items)
|
||||
this.bottomedOut = isEmpty(result)
|
||||
if (this.externalItems) return
|
||||
this.page += 1
|
||||
this.total = result.count
|
||||
this.items.push(...result.items)
|
||||
this.total = result.length
|
||||
this.items.push(...result)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||
import ListsUserSearch from 'src/components/lists_user_search/lists_user_search.vue'
|
||||
|
|
@ -9,6 +8,7 @@ import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
|||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useListsStore } from 'src/stores/lists.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -47,8 +47,8 @@ const ListsNew = {
|
|||
.fetchListAccounts({ listId: this.id })
|
||||
.then(() => {
|
||||
this.membersUserIds = this.findListAccounts(this.id)
|
||||
this.membersUserIds.forEach((userId) => {
|
||||
this.$store.dispatch('fetchUserIfMissing', userId)
|
||||
this.membersUserIds.forEach((id) => {
|
||||
useUsersStore().fetchUserIfMissing({ id })
|
||||
})
|
||||
})
|
||||
},
|
||||
|
|
@ -66,11 +66,8 @@ const ListsNew = {
|
|||
.map((userId) => this.findUser(userId))
|
||||
.filter(Boolean)
|
||||
},
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapPiniaState(useListsStore, ['findListTitle', 'findListAccounts']),
|
||||
...mapGetters(['findUser']),
|
||||
...mapState(useUsersStore, ['currentUser', 'findUser']),
|
||||
...mapState(useListsStore, ['findListTitle', 'findListAccounts']),
|
||||
},
|
||||
methods: {
|
||||
onInput() {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import { getListEntries } from 'src/components/navigation/filter.js'
|
||||
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
||||
|
||||
import { useListsStore } from 'src/stores/lists.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
export const ListsMenuContent = {
|
||||
props: ['showPin'],
|
||||
|
|
@ -12,12 +12,10 @@ export const ListsMenuContent = {
|
|||
NavigationEntry,
|
||||
},
|
||||
computed: {
|
||||
...mapPiniaState(useListsStore, {
|
||||
...mapState(useListsStore, {
|
||||
lists: getListEntries,
|
||||
}),
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
import { useListsStore } from 'src/stores/lists.js'
|
||||
|
||||
const ListsTimeline = {
|
||||
data() {
|
||||
return {
|
||||
listId: null,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.list
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route: function (route) {
|
||||
if (route.name === 'lists-timeline' && route.params.id !== this.listId) {
|
||||
this.listId = route.params.id
|
||||
this.$store.dispatch('stopFetchingTimeline', 'list')
|
||||
this.$store.commit('clearTimeline', { timeline: 'list' })
|
||||
useListsStore().fetchList({ listId: this.listId })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'list',
|
||||
listId: this.listId,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.listId = this.$route.params.id
|
||||
useListsStore().fetchList({ listId: this.listId })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'list',
|
||||
listId: this.listId,
|
||||
})
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'list')
|
||||
this.$store.commit('clearTimeline', { timeline: 'list' })
|
||||
},
|
||||
}
|
||||
|
||||
export default ListsTimeline
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
title="list.name"
|
||||
:timeline="timeline"
|
||||
:list-id="listId"
|
||||
timeline-name="list"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./lists_timeline.js"></script>
|
||||
|
|
@ -32,8 +32,8 @@ const ListsUserSearch = {
|
|||
this.loading = true
|
||||
this.$emit('loading')
|
||||
this.userIds = []
|
||||
this.$store
|
||||
.dispatch('search', {
|
||||
this.useSearchStore()
|
||||
.search({
|
||||
q: query,
|
||||
resolve: true,
|
||||
type: 'accounts',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import UnicodeDomainIndicator from 'src/components/unicode_domain_indicator/unicode_domain_indicator.vue'
|
||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||
|
|
@ -12,6 +11,7 @@ import {
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ const MentionLink = {
|
|||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.url && this.$store?.getters.findUserByUrl(this.url)
|
||||
return this.url && useUsersStore().findUserByUrl(this.url)
|
||||
},
|
||||
isYou() {
|
||||
// FIXME why user !== currentUser???
|
||||
|
|
@ -156,11 +156,9 @@ const MentionLink = {
|
|||
shouldFadeDomain() {
|
||||
return this.mergedConfig.mentionLinkFadeDomain
|
||||
},
|
||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapPiniaState(useUserHighlightStore, ['highlight']),
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapState(useUserHighlightStore, ['highlight']),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
<UserAvatar
|
||||
v-if="shouldShowAvatar"
|
||||
class="mention-avatar"
|
||||
:user="user"
|
||||
:user-id="user.id"
|
||||
/><span
|
||||
class="shortName"
|
||||
>@<span
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const Mentions = {
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.mentions
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
}
|
||||
|
||||
export default Mentions
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.interactions')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'mentions'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./mentions.js"></script>
|
||||
|
|
@ -5,13 +5,15 @@ import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
|||
import GestureService from '../../services/gesture_service/gesture_service'
|
||||
import {
|
||||
countExtraNotifications,
|
||||
unseenNotificationsFromStore,
|
||||
unseenNotifications,
|
||||
} from '../../services/notification_utils/notification_utils'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -53,11 +55,10 @@ const MobileNav = {
|
|||
},
|
||||
computed: {
|
||||
currentUser() {
|
||||
return this.$store.state.users.currentUser
|
||||
return useUsersStore().currentUser
|
||||
},
|
||||
unseenNotifications() {
|
||||
return unseenNotificationsFromStore(
|
||||
this.$store,
|
||||
return unseenNotifications(
|
||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
||||
)
|
||||
|
|
@ -145,11 +146,11 @@ const MobileNav = {
|
|||
},
|
||||
doLogout() {
|
||||
this.$router.replace('/main/public')
|
||||
this.$store.dispatch('logout')
|
||||
useUsersStore().logout()
|
||||
this.hideConfirmLogout()
|
||||
},
|
||||
markNotificationsAsSeen() {
|
||||
this.$store.dispatch('markNotificationsAsSeen')
|
||||
useNotificationsStore().markNotificationsAsSeen()
|
||||
},
|
||||
onScroll({ target: { scrollTop, clientHeight, scrollHeight } }) {
|
||||
this.notificationsAtTop = scrollTop > 0
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { debounce } from 'lodash'
|
|||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { usePostStatusStore } from 'src/stores/post_status.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faPen } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -34,7 +35,7 @@ const MobilePostStatusButton = {
|
|||
},
|
||||
computed: {
|
||||
isLoggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
return useUsersStore().loggedIn
|
||||
},
|
||||
isHidden() {
|
||||
if (HIDDEN_FOR_PAGES.has(this.$route.name)) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
|
||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faChevronDown } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -405,7 +406,7 @@ const ModerationTools = {
|
|||
)
|
||||
},
|
||||
isAdmin() {
|
||||
return this.$store.state.users.currentUser.role === 'admin'
|
||||
return useUsersStore().currentUser.role === 'admin'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -452,7 +453,7 @@ const ModerationTools = {
|
|||
},
|
||||
privileged(privilege) {
|
||||
if (this.isAdmin) return true
|
||||
return this.$store.state.users.currentUser.privileges.has(privilege)
|
||||
return useUsersStore().currentUser.privileges.has(privilege)
|
||||
},
|
||||
setTag(tag, value) {
|
||||
useAdminSettingsStore().setUsersTags({
|
||||
|
|
@ -516,8 +517,7 @@ const ModerationTools = {
|
|||
setOpen(value) {
|
||||
this.open = value
|
||||
},
|
||||
maybeShowConfirm(close, { group, name, action, value }) {
|
||||
close()
|
||||
maybeShowConfirm({ group, name, action, value }) {
|
||||
this.confirmDialogName = name
|
||||
this.confirmDialogGroup = group
|
||||
this.confirmDialogAction = () => action()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
@show="setOpen(true)"
|
||||
@close="setOpen(false)"
|
||||
>
|
||||
<template #content="{close}">
|
||||
<template #content>
|
||||
<div class="dropdown-menu">
|
||||
<template v-for="(entry, index) in entries">
|
||||
<div
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
>
|
||||
<button
|
||||
class="main-button"
|
||||
@click="() => maybeShowConfirm(close, entry)"
|
||||
@click="() => maybeShowConfirm(entry)"
|
||||
>
|
||||
<span
|
||||
v-if="entry.checkbox"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||
import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
||||
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const MuteCard = {
|
||||
props: ['userId'],
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.findUser(this.userId)
|
||||
return useUsersStore().findUser(this.userId)
|
||||
},
|
||||
relationship() {
|
||||
return this.$store.getters.relationship(this.userId)
|
||||
return useUsersStore().relationship(this.userId)
|
||||
},
|
||||
muted() {
|
||||
return this.relationship.muting
|
||||
|
|
@ -30,7 +32,7 @@ const MuteCard = {
|
|||
},
|
||||
methods: {
|
||||
unmuteUser() {
|
||||
this.$store.dispatch('unmuteUser', this.userId)
|
||||
useUsersStore().unmuteUser(this.user.id)
|
||||
},
|
||||
muteUser() {
|
||||
this.$refs.timedMuteDialog.optionallyPrompt()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { useChatsStore } from 'src/stores/chats.js'
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -128,8 +129,8 @@ const NavPanel = {
|
|||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
}),
|
||||
...mapPiniaState(useUsersStore, ['currentUser']),
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
followRequestCount: (state) => state.api.followRequests.length,
|
||||
}),
|
||||
...mapPiniaState(useChatsStore, ['unreadChatsCount']),
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { mapState as mapPiniaState, mapStores } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState, mapStores } from 'pinia'
|
||||
|
||||
import { routeTo } from 'src/components/navigation/navigation.js'
|
||||
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -44,10 +44,8 @@ const NavigationEntry = {
|
|||
return this.$store.getters
|
||||
},
|
||||
...mapStores(useAnnouncementsStore),
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapPiniaState(useSyncConfigStore, {
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useSyncConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
|
|||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useListsStore } from 'src/stores/lists'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -76,8 +77,8 @@ const NavPanel = {
|
|||
'pleromaChatMessagesAvailable',
|
||||
'localBubble',
|
||||
]),
|
||||
...mapPiniaState(useUsersStore, ['currentUser']),
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
followRequestCount: (state) => state.api.followRequests.length,
|
||||
}),
|
||||
pinnedList() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import Report from 'src/components/report/report.vue'
|
||||
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||
|
|
@ -7,7 +7,7 @@ import Timeago from 'src/components/timeago/timeago.vue'
|
|||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||
import UserLink from 'src/components/user_link/user_link.vue'
|
||||
import UserPopover from 'src/components/user_popover/user_popover.vue'
|
||||
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
|
||||
import { isStatusNotification } from '../../services/notification_utils/notification_utils_sw.js'
|
||||
import {
|
||||
highlightClass,
|
||||
highlightStyle,
|
||||
|
|
@ -15,8 +15,11 @@ import {
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { approveUser, denyUser } from 'src/api/user.js'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
|
|
@ -148,16 +151,9 @@ const Notification = {
|
|||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
// TODO Fix this
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
this.$store.dispatch('markSingleNotificationAsSeen', {
|
||||
id: this.notification.id,
|
||||
})
|
||||
this.$store.dispatch('updateNotification', {
|
||||
id: this.notification.id,
|
||||
updater: (notification) => {
|
||||
notification.type = 'follow'
|
||||
},
|
||||
})
|
||||
useNotificationsStore().markSingleNotificationAsSeen(this.notification.id)
|
||||
this.hideApproveConfirmDialog()
|
||||
},
|
||||
denyUser() {
|
||||
|
|
@ -172,15 +168,20 @@ const Notification = {
|
|||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(() => {
|
||||
this.$store.dispatch('dismissNotificationLocal', {
|
||||
id: this.notification.id,
|
||||
})
|
||||
useNotificationsStore().dismissNotificationLocal(this.notification.id)
|
||||
// TODO Fix this
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
})
|
||||
this.hideDenyConfirmDialog()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
status() {
|
||||
// Used for StatusContent
|
||||
if (this.notification.status) {
|
||||
return useStatusesStore().allStatuses.get(this.notification.status.id)
|
||||
}
|
||||
},
|
||||
userClass() {
|
||||
return highlightClass(this.notification.from_profile)
|
||||
},
|
||||
|
|
@ -194,19 +195,19 @@ const Notification = {
|
|||
)
|
||||
},
|
||||
user() {
|
||||
return this.$store.getters.findUser(this.notification.from_profile.id)
|
||||
return useUsersStore().findUser(this.notification.from_profile.id)
|
||||
},
|
||||
userProfileLink() {
|
||||
return this.generateUserProfileLink(this.user)
|
||||
},
|
||||
targetUser() {
|
||||
return this.$store.getters.findUser(this.notification.target.id)
|
||||
return useUsersStore().findUser(this.notification.target.id)
|
||||
},
|
||||
targetUserProfileLink() {
|
||||
return this.generateUserProfileLink(this.targetUser)
|
||||
},
|
||||
needMute() {
|
||||
return this.$store.getters.relationship(this.user.id).muting
|
||||
return useUsersStore().relationship(this.user.id).muting
|
||||
},
|
||||
isStatusNotification() {
|
||||
return isStatusNotification(this.notification.type)
|
||||
|
|
@ -229,9 +230,7 @@ const Notification = {
|
|||
shouldConfirmDeny() {
|
||||
return this.mergedConfig.modalOnDenyFollow
|
||||
},
|
||||
...mapState({
|
||||
currentUser: (state) => state.users.currentUser,
|
||||
}),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<Status
|
||||
class="Notification panel-body"
|
||||
:compact="true"
|
||||
:statusoid="notification.status"
|
||||
:status-id="notification.status?.id"
|
||||
@click="interacted"
|
||||
/>
|
||||
</article>
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
<UserAvatar
|
||||
class="post-avatar"
|
||||
:compact="true"
|
||||
:user="notification.from_profile"
|
||||
:user-id="notification.from_profile.id"
|
||||
/>
|
||||
</UserPopover>
|
||||
</a>
|
||||
|
|
@ -261,7 +261,7 @@
|
|||
<StatusContent
|
||||
class="status-content"
|
||||
:compact="!statusExpanded"
|
||||
:status="notification.status"
|
||||
:status="status"
|
||||
:collapse="!statusExpanded"
|
||||
@click="onContentClick"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -7,17 +7,16 @@ import FaviconService from '../../services/favicon_service/favicon_service.js'
|
|||
import {
|
||||
ACTIONABLE_NOTIFICATION_TYPES,
|
||||
countExtraNotifications,
|
||||
filteredNotificationsFromStore,
|
||||
notificationsFromStore,
|
||||
unseenNotificationsFromStore,
|
||||
filteredNotifications,
|
||||
unseenNotifications,
|
||||
} from '../../services/notification_utils/notification_utils.js'
|
||||
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
|
||||
import NotificationFilters from './notification_filters.vue'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -53,7 +52,6 @@ const Notifications = {
|
|||
data() {
|
||||
return {
|
||||
showScrollTop: false,
|
||||
bottomedOut: false,
|
||||
// How many seen notifications to display in the list. The more there are,
|
||||
// the heavier the page becomes. This count is increased when loading
|
||||
// older notifications, and cut back to default whenever hitting "Read!".
|
||||
|
|
@ -70,14 +68,13 @@ const Notifications = {
|
|||
return this.minimalMode ? '' : 'panel panel-default'
|
||||
},
|
||||
notifications() {
|
||||
return notificationsFromStore(this.$store)
|
||||
return useNotificationsStore().data
|
||||
},
|
||||
error() {
|
||||
return this.$store.state.notifications.error
|
||||
return useNotificationsStore().error
|
||||
},
|
||||
unseenNotifications() {
|
||||
return unseenNotificationsFromStore(
|
||||
this.$store,
|
||||
return unseenNotifications(
|
||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
||||
)
|
||||
|
|
@ -85,18 +82,15 @@ const Notifications = {
|
|||
filteredNotifications() {
|
||||
if (this.unseenAtTop) {
|
||||
return [
|
||||
...filteredNotificationsFromStore(
|
||||
this.$store,
|
||||
...filteredNotifications(
|
||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||
).filter((n) => this.shouldShowUnseen(n)),
|
||||
...filteredNotificationsFromStore(
|
||||
this.$store,
|
||||
...filteredNotifications(
|
||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||
).filter((n) => !this.shouldShowUnseen(n)),
|
||||
]
|
||||
} else {
|
||||
return filteredNotificationsFromStore(
|
||||
this.$store,
|
||||
return filteredNotifications(
|
||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||
this.filterMode,
|
||||
)
|
||||
|
|
@ -127,7 +121,10 @@ const Notifications = {
|
|||
)
|
||||
},
|
||||
loading() {
|
||||
return this.$store.state.notifications.loading
|
||||
return useNotificationsStore().fetcher.loading.value
|
||||
},
|
||||
bottomedOut() {
|
||||
return useNotificationsStore().fetcher.bottomedOut.value
|
||||
},
|
||||
noHeading() {
|
||||
const { layoutType } = useInterfaceStore()
|
||||
|
|
@ -224,14 +221,14 @@ const Notifications = {
|
|||
*/
|
||||
notificationClicked(notification) {
|
||||
const { id } = notification
|
||||
this.$store.dispatch('notificationClicked', { id })
|
||||
useNotificationsStore().notificationClicked(id)
|
||||
},
|
||||
notificationInteracted(notification) {
|
||||
const { id } = notification
|
||||
this.$store.dispatch('markSingleNotificationAsSeen', { id })
|
||||
useNotificationsStore().markSingleNotificationAsSeen(id)
|
||||
},
|
||||
markAsSeen() {
|
||||
this.$store.dispatch('markNotificationsAsSeen')
|
||||
useNotificationsStore().markNotificationsAsSeen()
|
||||
this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT
|
||||
},
|
||||
fetchOlderNotifications() {
|
||||
|
|
@ -250,22 +247,7 @@ const Notifications = {
|
|||
this.seenToDisplayCount = seenCount
|
||||
}
|
||||
|
||||
const store = this.$store
|
||||
const credentials = store.state.users.currentUser.credentials
|
||||
store.commit('setNotificationsLoading', { value: true })
|
||||
notificationsFetcher
|
||||
.fetchAndUpdate({
|
||||
store,
|
||||
credentials,
|
||||
older: true,
|
||||
})
|
||||
.then((notifs) => {
|
||||
store.commit('setNotificationsLoading', { value: false })
|
||||
if (notifs.length === 0) {
|
||||
this.bottomedOut = true
|
||||
}
|
||||
this.seenToDisplayCount += notifs.length
|
||||
})
|
||||
useNotificationsStore().fetcher.fetchOlder()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { getToken } from 'src/api/oauth.js'
|
||||
|
||||
|
|
@ -17,7 +18,8 @@ const oac = {
|
|||
code: this.code,
|
||||
}).then(({ data: result }) => {
|
||||
oauthStore.setToken(result.access_token)
|
||||
this.$store.dispatch('loginUser', result.access_token)
|
||||
|
||||
useUsersStore().loginUser(result.access_token)
|
||||
this.$router.push({ name: 'friends' })
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { resetPassword } from 'src/api/public.js'
|
||||
|
||||
|
|
@ -21,13 +21,11 @@ const passwordReset = {
|
|||
error: null,
|
||||
}),
|
||||
computed: {
|
||||
...mapState({
|
||||
signedIn: (state) => !!state.users.currentUser,
|
||||
}),
|
||||
...mapPiniaState(useInstanceStore, ['mailerEnabled']),
|
||||
...mapState(useUsersStore, ['loggedIn']),
|
||||
...mapState(useInstanceStore, ['mailerEnabled']),
|
||||
},
|
||||
created() {
|
||||
if (this.signedIn) {
|
||||
if (this.loggedIn) {
|
||||
this.$router.push({ name: 'root' })
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import genRandomSeed from '../../services/random_seed/random_seed.service.js'
|
|||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { usePollsStore } from 'src/stores/polls.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
export default {
|
||||
name: 'Poll',
|
||||
|
|
@ -64,7 +65,7 @@ export default {
|
|||
return useMergedConfigStore().mergedConfig.scaleMfm
|
||||
},
|
||||
loggedIn() {
|
||||
return this.$store.state.users.currentUser
|
||||
return useUsersStore().currentUser
|
||||
},
|
||||
showResults() {
|
||||
return this.poll.voted || this.expired || !this.loggedIn
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@
|
|||
v-model="expiryUnit"
|
||||
unstyled="true"
|
||||
class="expiry-unit"
|
||||
@change="expiryAmountChange"
|
||||
:aria-label="$t('polls.expiry_unit')"
|
||||
@change="expiryAmountChange"
|
||||
>
|
||||
<option
|
||||
v-for="unit in expiryUnits"
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { useInterfaceStore } from 'src/stores/interface.js'
|
|||
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { pollFormToMasto } from 'src/services/poll/poll.service.js'
|
||||
|
||||
|
|
@ -570,17 +571,18 @@ const PostStatusForm = {
|
|||
},
|
||||
|
||||
// Global stuff
|
||||
currentUser() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapState(useInterfaceStore, {
|
||||
mobileLayout: (store) => store.mobileLayout,
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
isDirty(newVal, oldVal) {
|
||||
this.statusChanged()
|
||||
newStatus: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.statusChanged()
|
||||
},
|
||||
},
|
||||
saveable(val) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#usage_notes
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Modal from 'src/components/modal/modal.vue'
|
|||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||
|
||||
import { usePostStatusStore } from 'src/stores/post_status.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const PostStatusModal = {
|
||||
components: {
|
||||
|
|
@ -17,7 +18,7 @@ const PostStatusModal = {
|
|||
},
|
||||
computed: {
|
||||
isLoggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
return !!useUsersStore().currentUser
|
||||
},
|
||||
modalActivated() {
|
||||
return usePostStatusStore().modalActivated
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const PublicAndExternalTimeline = {
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.publicAndExternal
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'publicAndExternal',
|
||||
})
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal')
|
||||
},
|
||||
}
|
||||
|
||||
export default PublicAndExternalTimeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.twkn')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'publicAndExternal'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./public_and_external_timeline.js"></script>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const PublicTimeline = {
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.public
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'public' })
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'public')
|
||||
},
|
||||
}
|
||||
|
||||
export default PublicTimeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.public_tl')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'public'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./public_timeline.js"></script>
|
||||
|
|
@ -5,7 +5,9 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -26,7 +28,7 @@ const QuickFilterSettings = {
|
|||
path: 'replyVisibility',
|
||||
value: visibility,
|
||||
})
|
||||
this.$store.dispatch('queueFlushAll')
|
||||
useStatusesStore().queueFlushAll()
|
||||
},
|
||||
openTab(tab) {
|
||||
useInterfaceStore().openSettingsModalTab(tab)
|
||||
|
|
@ -54,7 +56,7 @@ const QuickFilterSettings = {
|
|||
}
|
||||
},
|
||||
loggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
return !!useUsersStore().currentUser
|
||||
},
|
||||
replyVisibilitySelf: {
|
||||
get() {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filt
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -36,7 +37,7 @@ const QuickViewSettings = {
|
|||
mobileLayout: (state) => state.layoutType === 'mobile',
|
||||
}),
|
||||
loggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
return !!useUsersStore().currentUser
|
||||
},
|
||||
conversationDisplay: {
|
||||
get() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ export default {
|
|||
computed: {
|
||||
quotedStatus() {
|
||||
return this.statusId
|
||||
? this.$store.state.statuses.allStatusesObject[this.statusId]
|
||||
? useStatusesStore().allStatuses.get(this.statusId)
|
||||
: undefined
|
||||
},
|
||||
shouldDisplayQuote() {
|
||||
|
|
@ -79,8 +81,8 @@ export default {
|
|||
this.fetchAttempted = true
|
||||
this.fetching = true
|
||||
this.$emit('loading', true)
|
||||
this.$store
|
||||
.dispatch('fetchStatus', this.statusId)
|
||||
useStatusesStore()
|
||||
.fetchStatus(this.statusId)
|
||||
.then(() => {
|
||||
this.displayQuote = true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@ export default {
|
|||
this.$emit('update:id', notice[3])
|
||||
} else if (value) {
|
||||
this.loading = true
|
||||
this.$store
|
||||
.dispatch('search', {
|
||||
this.useSearchStore()
|
||||
.search({
|
||||
q: value,
|
||||
resolve: true,
|
||||
offset: 0,
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const QuotesTimeline = {
|
||||
created() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'quotes' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'quotes',
|
||||
statusId: this.statusId,
|
||||
})
|
||||
},
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
statusId() {
|
||||
return this.$route.params.id
|
||||
},
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.quotes
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
statusId() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'quotes' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'quotes',
|
||||
statusId: this.statusId,
|
||||
})
|
||||
},
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'quotes')
|
||||
},
|
||||
}
|
||||
|
||||
export default QuotesTimeline
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.quotes')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'quotes'"
|
||||
:status-id="statusId"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src='./quotes_timeline.js'></script>
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
import useVuelidate from '@vuelidate/core'
|
||||
import { required, requiredIf, sameAs } from '@vuelidate/validators'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapActions, mapState } from 'vuex'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
|
||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||
import TermsOfServicePanel from 'src/components/terms_of_service_panel/terms_of_service_panel.vue'
|
||||
import localeService from '../../services/locale/locale.service.js'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { getCaptcha, register } from 'src/api/public.js'
|
||||
import { DAY } from 'src/services/date_utils/date_utils.js'
|
||||
|
||||
const registration = {
|
||||
|
|
@ -26,6 +28,9 @@ const registration = {
|
|||
reason: '',
|
||||
language: [''],
|
||||
},
|
||||
signUpPending: false,
|
||||
signUpErrors: [],
|
||||
signUpNotice: {},
|
||||
captcha: {},
|
||||
}),
|
||||
components: {
|
||||
|
|
@ -58,7 +63,7 @@ const registration = {
|
|||
}
|
||||
},
|
||||
created() {
|
||||
if ((!this.registrationOpen && !this.token) || this.signedIn) {
|
||||
if ((!this.registrationOpen && !this.token) || this.loggedIn) {
|
||||
this.$router.push({ name: 'root' })
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +105,10 @@ const registration = {
|
|||
)
|
||||
)
|
||||
},
|
||||
...mapPiniaState(useInstanceStore, {
|
||||
hasSignUpNotice(state) {
|
||||
return this.signUpNotice.message
|
||||
},
|
||||
...mapState(useInstanceStore, {
|
||||
registrationOpen: (store) => store.registrationOpen,
|
||||
embeddedToS: (store) => store.embeddedToS,
|
||||
termsOfService: (store) => store.tos,
|
||||
|
|
@ -109,16 +117,49 @@ const registration = {
|
|||
birthdayRequired: (store) => store.birthdayRequired,
|
||||
birthdayMinAge: (store) => store.birthdayMinAge,
|
||||
}),
|
||||
...mapState({
|
||||
signedIn: (state) => !!state.users.currentUser,
|
||||
isPending: (state) => state.users.signUpPending,
|
||||
serverValidationErrors: (state) => state.users.signUpErrors,
|
||||
signUpNotice: (state) => state.users.signUpNotice,
|
||||
hasSignUpNotice: (state) => !!state.users.signUpNotice.message,
|
||||
}),
|
||||
...mapState(useUsersStore, ['loggedIn']),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['signUp', 'getCaptcha']),
|
||||
...mapActions(useUsersStore, ['loginUser']),
|
||||
getCaptcha(store) {
|
||||
return getCaptcha({
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data }) => data)
|
||||
},
|
||||
async signUp(userInfo) {
|
||||
const oauthStore = useOAuthStore()
|
||||
|
||||
this.signUpPending = true
|
||||
this.signUpErrors = []
|
||||
this.signUpNotice = {}
|
||||
|
||||
try {
|
||||
const token = await oauthStore.ensureAppToken()
|
||||
const { data } = await register({
|
||||
credentials: token,
|
||||
params: { ...userInfo },
|
||||
})
|
||||
|
||||
if (data.access_token) {
|
||||
this.signUpPending = false
|
||||
oauthStore.setToken(data.access_token)
|
||||
await this.loginUser(data.access_token)
|
||||
return 'ok'
|
||||
} else {
|
||||
// Request succeeded, but user cannot login yet.
|
||||
this.signUpErrors = []
|
||||
this.signUpNotice = data
|
||||
return 'request_sent'
|
||||
}
|
||||
} catch (e) {
|
||||
const errors = e.message
|
||||
this.signUpErrors = errors
|
||||
this.signUpNotice = {}
|
||||
throw e
|
||||
} finally {
|
||||
this.signUpPending = false
|
||||
}
|
||||
},
|
||||
async submit() {
|
||||
this.user.nickname = this.user.username
|
||||
this.user.token = this.token
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue