Merge pull request 'Users/Statuses/Notifications Pinia migration and refactor' (#3555) from users-statuses-pinia into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma-fe/pulls/3555
This commit is contained in:
commit
d603aac31b
217 changed files with 7961 additions and 5697 deletions
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
|
||||||
3
changelog.d/streaming_indicator.add
Normal file
3
changelog.d/streaming_indicator.add
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Added an indicator next to instance's name showing WebSocket connection status (if enabled).
|
||||||
|
Timeline no longer show "loading" indicator at the bottom when fetching newer posts.
|
||||||
|
Added small indicator on top of timeline when new posts are being fetched
|
||||||
|
|
@ -21,6 +21,7 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useShoutStore } from 'src/stores/shout.js'
|
import { useShoutStore } from 'src/stores/shout.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
// Helper to unwrap reactive proxies
|
// Helper to unwrap reactive proxies
|
||||||
window.toValue = (x) => JSON.parse(JSON.stringify(x))
|
window.toValue = (x) => JSON.parse(JSON.stringify(x))
|
||||||
|
|
@ -153,11 +154,8 @@ export default {
|
||||||
...(navbarColumnStretch ? ['-column-stretch'] : []),
|
...(navbarColumnStretch ? ['-column-stretch'] : []),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
currentUser() {
|
|
||||||
return this.$store.state.users.currentUser
|
|
||||||
},
|
|
||||||
userBackground() {
|
userBackground() {
|
||||||
return this.currentUser.background_image
|
return this.currentUser?.background_image
|
||||||
},
|
},
|
||||||
foreignProfileBackground() {
|
foreignProfileBackground() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -246,6 +244,7 @@ export default {
|
||||||
'styleDataUsed',
|
'styleDataUsed',
|
||||||
'layoutType',
|
'layoutType',
|
||||||
]),
|
]),
|
||||||
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
...mapState(useInstanceStore, ['styleDataUsed']),
|
...mapState(useInstanceStore, ['styleDataUsed']),
|
||||||
...mapState(useInstanceCapabilitiesStore, [
|
...mapState(useInstanceCapabilitiesStore, [
|
||||||
'suggestionsEnabled',
|
'suggestionsEnabled',
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
class="column -scrollable"
|
class="column -scrollable"
|
||||||
:class="{ '-show-scrollbar': showScrollbars }"
|
:class="{ '-show-scrollbar': showScrollbars }"
|
||||||
>
|
>
|
||||||
<user-panel />
|
<UserPanel />
|
||||||
<template v-if="layoutType !== 'mobile'">
|
<template v-if="layoutType !== 'mobile'">
|
||||||
<NavPanel />
|
<NavPanel />
|
||||||
<InstanceSpecificPanel v-if="showInstanceSpecificPanel" />
|
<InstanceSpecificPanel v-if="showInstanceSpecificPanel" />
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ export const paramsString = (params = {}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const promisedRequest = async ({
|
export const promisedRequest = async ({
|
||||||
method,
|
method = 'GET',
|
||||||
url,
|
url,
|
||||||
payload,
|
payload,
|
||||||
formData,
|
formData,
|
||||||
|
|
@ -124,7 +124,7 @@ export const promisedRequest = async ({
|
||||||
const { ok, status } = response
|
const { ok, status } = response
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
return { response, status, data }
|
return { response, status, data, timestamp: Date.now() }
|
||||||
} else {
|
} else {
|
||||||
throw new StatusCodeError(response.status, data, { url, options }, response)
|
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 }) =>
|
const MASTODON_PASSWORD_RESET_URL = ({ email }) =>
|
||||||
`/auth/password${paramsString({ email })}`
|
`/auth/password${paramsString({ email })}`
|
||||||
|
|
||||||
const MASTODON_FOLLOWING_URL = (
|
export const MASTODON_FOLLOWING_URL = (
|
||||||
id,
|
id,
|
||||||
{ minId, maxId, sinceId, limit, withRelationships },
|
{ minId, maxId, sinceId, limit, withRelationships },
|
||||||
) =>
|
) =>
|
||||||
`/api/v1/accounts/${id}/following${paramsString({ 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,
|
id,
|
||||||
{ minId, maxId, sinceId, limit, withRelationships },
|
{ minId, maxId, sinceId, limit, withRelationships },
|
||||||
) =>
|
) =>
|
||||||
|
|
@ -26,15 +26,17 @@ const MASTODON_FOLLOWERS_URL = (
|
||||||
|
|
||||||
export const MASTODON_STATUS_URL = (id) => `/api/v1/statuses/${id}`
|
export const MASTODON_STATUS_URL = (id) => `/api/v1/statuses/${id}`
|
||||||
const MASTODON_STATUS_CONTEXT_URL = (id) => `/api/v1/statuses/${id}/context`
|
const MASTODON_STATUS_CONTEXT_URL = (id) => `/api/v1/statuses/${id}/context`
|
||||||
const MASTODON_STATUS_SOURCE_URL = (id) => `/api/v1/statuses/${id}/source`
|
export const MASTODON_STATUS_SOURCE_URL = (id) =>
|
||||||
const MASTODON_STATUS_HISTORY_URL = (id) => `/api/v1/statuses/${id}/history`
|
`/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_URL = '/api/v1/accounts'
|
||||||
const MASTODON_USER_LOOKUP_URL = ({ acct }) =>
|
const MASTODON_USER_LOOKUP_URL = ({ acct }) =>
|
||||||
`/api/v1/accounts/lookup${paramsString({ acct })}`
|
`/api/v1/accounts/lookup${paramsString({ acct })}`
|
||||||
const MASTODON_POLL_URL = (id = '') => `/api/v1/polls/${id}`
|
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`
|
`/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`
|
`/api/v1/statuses/${id}/reblogged_by`
|
||||||
const MASTODON_SEARCH_2 = ({
|
const MASTODON_SEARCH_2 = ({
|
||||||
q,
|
q,
|
||||||
|
|
@ -51,7 +53,7 @@ const MASTODON_SEARCH_2 = ({
|
||||||
const MASTODON_USER_SEARCH_URL = ({ q, resolve }) =>
|
const MASTODON_USER_SEARCH_URL = ({ q, resolve }) =>
|
||||||
`/api/v1/accounts/search${paramsString({ q, resolve })}`
|
`/api/v1/accounts/search${paramsString({ q, resolve })}`
|
||||||
const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers'
|
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`
|
`/api/v1/pleroma/statuses/${id}/reactions`
|
||||||
const PLEROMA_SCROBBLES_URL = (id, { maxId, sinceId, minId, limit, offset }) =>
|
const PLEROMA_SCROBBLES_URL = (id, { maxId, sinceId, minId, limit, offset }) =>
|
||||||
`/api/v1/pleroma/accounts/${id}/scrobbles${paramsString({ maxId, sinceId, minId, limit, offset })}`
|
`/api/v1/pleroma/accounts/${id}/scrobbles${paramsString({ maxId, sinceId, minId, limit, offset })}`
|
||||||
|
|
@ -174,15 +176,21 @@ export const fetchStatusSource = ({ id, credentials }) =>
|
||||||
credentials,
|
credentials,
|
||||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseSource(data) }))
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseSource(data) }))
|
||||||
|
|
||||||
export const fetchStatusHistory = ({ status, credentials }) =>
|
export const fetchStatusHistory = ({ id, credentials }) =>
|
||||||
promisedRequest({
|
promisedRequest({
|
||||||
url: MASTODON_STATUS_HISTORY_URL(status.id),
|
url: MASTODON_STATUS_HISTORY_URL(id),
|
||||||
credentials,
|
credentials,
|
||||||
}).then(({ data, ...rest }) => {
|
}).then(({ data, ...rest }) => {
|
||||||
return [...data].reverse().map((item) => {
|
return {
|
||||||
item.originalStatus = status
|
...rest,
|
||||||
return { ...rest, data: parseStatus(item) }
|
data: [...data].reverse().map((item) => {
|
||||||
})
|
// History data is missing a lot of stuff present in original
|
||||||
|
// but we're really only missing the id for the timeago, the
|
||||||
|
// rest seem to render just fine.
|
||||||
|
item.id = id
|
||||||
|
return parseStatus(item)
|
||||||
|
}),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const listEmojiPacks = ({ page, pageSize, credentials }) =>
|
export const listEmojiPacks = ({ page, pageSize, credentials }) =>
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ export const fetchTimeline = ({
|
||||||
publicAndExternal: MASTODON_PUBLIC_TIMELINE,
|
publicAndExternal: MASTODON_PUBLIC_TIMELINE,
|
||||||
dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
|
dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
|
||||||
user: MASTODON_USER_TIMELINE_URL,
|
user: MASTODON_USER_TIMELINE_URL,
|
||||||
|
userPinned: MASTODON_USER_TIMELINE_URL,
|
||||||
media: MASTODON_USER_TIMELINE_URL,
|
media: MASTODON_USER_TIMELINE_URL,
|
||||||
list: MASTODON_LIST_TIMELINE_URL,
|
list: MASTODON_LIST_TIMELINE_URL,
|
||||||
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
|
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
|
||||||
|
|
@ -130,6 +131,7 @@ export const fetchTimeline = ({
|
||||||
|
|
||||||
const twoArgs = new Set([
|
const twoArgs = new Set([
|
||||||
'user',
|
'user',
|
||||||
|
'userPinned',
|
||||||
'media',
|
'media',
|
||||||
'list',
|
'list',
|
||||||
'publicFavorites',
|
'publicFavorites',
|
||||||
|
|
@ -147,6 +149,7 @@ export const fetchTimeline = ({
|
||||||
const id = (() => {
|
const id = (() => {
|
||||||
switch (timeline) {
|
switch (timeline) {
|
||||||
case 'user':
|
case 'user':
|
||||||
|
case 'userPinned':
|
||||||
case 'media':
|
case 'media':
|
||||||
return userId
|
return userId
|
||||||
case 'list':
|
case 'list':
|
||||||
|
|
@ -163,6 +166,9 @@ export const fetchTimeline = ({
|
||||||
if (timeline === 'media') {
|
if (timeline === 'media') {
|
||||||
params.onlyMedia = true
|
params.onlyMedia = true
|
||||||
}
|
}
|
||||||
|
if (timeline === 'userPinned') {
|
||||||
|
params.pinned = true
|
||||||
|
}
|
||||||
if (timeline === 'public') {
|
if (timeline === 'public') {
|
||||||
params.local = true
|
params.local = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||||
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
|
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
|
||||||
const ALIASES_URL = '/api/pleroma/aliases'
|
const ALIASES_URL = '/api/pleroma/aliases'
|
||||||
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
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_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
||||||
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
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_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
||||||
const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/mfa/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`
|
`/api/v1/notifications/${id}/dismiss`
|
||||||
const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite`
|
export const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite`
|
||||||
const MASTODON_UNFAVORITE_URL = (id) => `/api/v1/statuses/${id}/unfavourite`
|
export const MASTODON_UNFAVORITE_URL = (id) =>
|
||||||
const MASTODON_RETWEET_URL = (id) => `/api/v1/statuses/${id}/reblog`
|
`/api/v1/statuses/${id}/unfavourite`
|
||||||
const MASTODON_UNRETWEET_URL = (id) => `/api/v1/statuses/${id}/unreblog`
|
export const MASTODON_RETWEET_URL = (id) => `/api/v1/statuses/${id}/reblog`
|
||||||
const MASTODON_DELETE_URL = (id) => `/api/v1/statuses/${id}`
|
export const MASTODON_UNRETWEET_URL = (id) => `/api/v1/statuses/${id}/unreblog`
|
||||||
const MASTODON_FOLLOW_URL = (id) => `/api/v1/accounts/${id}/follow`
|
export const MASTODON_DELETE_URL = (id) => `/api/v1/statuses/${id}`
|
||||||
const MASTODON_UNFOLLOW_URL = (id) => `/api/v1/accounts/${id}/unfollow`
|
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_FOLLOW_REQUESTS_URL = '/api/v1/follow_requests'
|
||||||
const MASTODON_APPROVE_USER_URL = (id) =>
|
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_DENY_USER_URL = (id) => `/api/v1/follow_requests/${id}/reject`
|
||||||
const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) =>
|
const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) =>
|
||||||
`/api/v1/accounts/relationships/${paramsString({ 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_URL = (id = '') => `/api/v1/lists/${id}`
|
||||||
export const MASTODON_LIST_ACCOUNTS_URL = (id) => `/api/v1/lists/${id}/accounts`
|
export const MASTODON_LIST_ACCOUNTS_URL = (id) => `/api/v1/lists/${id}/accounts`
|
||||||
const MASTODON_USER_BLOCKS_URL = ({
|
export const MASTODON_USER_BLOCKS_URL = ({
|
||||||
maxId,
|
maxId,
|
||||||
sinceId,
|
sinceId,
|
||||||
limit,
|
limit,
|
||||||
withRelationships,
|
withRelationships,
|
||||||
}) =>
|
}) =>
|
||||||
`/api/v1/blocks/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
`/api/v1/blocks/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
||||||
const MASTODON_USER_MUTES_URL = ({
|
export const MASTODON_USER_MUTES_URL = ({
|
||||||
maxId,
|
maxId,
|
||||||
sinceId,
|
sinceId,
|
||||||
limit,
|
limit,
|
||||||
withRelationships,
|
withRelationships,
|
||||||
}) =>
|
}) =>
|
||||||
`/api/v1/mutes/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
`/api/v1/mutes/${paramsString({ maxId, sinceId, limit, withRelationships })}`
|
||||||
const MASTODON_BLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/block`
|
export const MASTODON_BLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/block`
|
||||||
const MASTODON_UNBLOCK_USER_URL = (id) => `/api/v1/accounts/${id}/unblock`
|
export const MASTODON_UNBLOCK_USER_URL = (id) =>
|
||||||
const MASTODON_MUTE_USER_URL = (id) => `/api/v1/accounts/${id}/mute`
|
`/api/v1/accounts/${id}/unblock`
|
||||||
const MASTODON_UNMUTE_USER_URL = (id) => `/api/v1/accounts/${id}/unmute`
|
export const MASTODON_MUTE_USER_URL = (id) => `/api/v1/accounts/${id}/mute`
|
||||||
const MASTODON_REMOVE_USER_FROM_FOLLOWERS = (id) =>
|
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`
|
`/api/v1/accounts/${id}/remove_from_followers`
|
||||||
const MASTODON_USER_NOTE_URL = (id) => `/api/v1/accounts/${id}/note`
|
export const MASTODON_USER_NOTE_URL = (id) => `/api/v1/accounts/${id}/note`
|
||||||
const MASTODON_BOOKMARK_STATUS_URL = (id) => `/api/v1/statuses/${id}/bookmark`
|
export const MASTODON_BOOKMARK_STATUS_URL = (id) =>
|
||||||
const MASTODON_UNBOOKMARK_STATUS_URL = (id) =>
|
`/api/v1/statuses/${id}/bookmark`
|
||||||
|
export const MASTODON_UNBOOKMARK_STATUS_URL = (id) =>
|
||||||
`/api/v1/statuses/${id}/unbookmark`
|
`/api/v1/statuses/${id}/unbookmark`
|
||||||
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
||||||
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
||||||
const MASTODON_VOTE_URL = (id) => `/api/v1/polls/${id}/votes`
|
const MASTODON_VOTE_URL = (id) => `/api/v1/polls/${id}/votes`
|
||||||
const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
|
const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
|
||||||
const MASTODON_REPORT_USER_URL = '/api/v1/reports'
|
const MASTODON_REPORT_USER_URL = '/api/v1/reports'
|
||||||
const MASTODON_PIN_OWN_STATUS = (id) => `/api/v1/statuses/${id}/pin`
|
export const MASTODON_PIN_OWN_STATUS_URL = (id) => `/api/v1/statuses/${id}/pin`
|
||||||
const MASTODON_UNPIN_OWN_STATUS = (id) => `/api/v1/statuses/${id}/unpin`
|
export const MASTODON_UNPIN_OWN_STATUS_URL = (id) =>
|
||||||
const MASTODON_MUTE_CONVERSATION = (id) => `/api/v1/statuses/${id}/mute`
|
`/api/v1/statuses/${id}/unpin`
|
||||||
const MASTODON_UNMUTE_CONVERSATION = (id) => `/api/v1/statuses/${id}/unmute`
|
export const MASTODON_MUTE_CONVERSATION_URL = (id) =>
|
||||||
const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks'
|
`/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_URL = '/api/v1/announcements'
|
||||||
const MASTODON_ANNOUNCEMENTS_DISMISS_URL = (id) =>
|
const MASTODON_ANNOUNCEMENTS_DISMISS_URL = (id) =>
|
||||||
`/api/v1/announcements/${id}/dismiss`
|
`/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}`
|
`/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}`
|
`/api/v1/pleroma/statuses/${id}/reactions/${emoji}`
|
||||||
const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
|
const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups'
|
||||||
const PLEROMA_BOOKMARK_FOLDERS_URL = '/api/v1/pleroma/bookmark_folders'
|
const PLEROMA_BOOKMARK_FOLDERS_URL = '/api/v1/pleroma/bookmark_folders'
|
||||||
|
|
@ -143,39 +149,39 @@ export const bookmarkStatus = ({ id, credentials, ...options }) =>
|
||||||
payload: {
|
payload: {
|
||||||
folder_id: options.folder_id,
|
folder_id: options.folder_id,
|
||||||
},
|
},
|
||||||
})
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
export const unbookmarkStatus = ({ id, credentials }) =>
|
export const unbookmarkStatus = ({ id, credentials }) =>
|
||||||
promisedRequest({
|
promisedRequest({
|
||||||
url: MASTODON_UNBOOKMARK_STATUS_URL(id),
|
url: MASTODON_UNBOOKMARK_STATUS_URL(id),
|
||||||
credentials,
|
credentials,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
export const pinOwnStatus = ({ id, credentials }) =>
|
export const pinOwnStatus = ({ id, credentials }) =>
|
||||||
promisedRequest({
|
promisedRequest({
|
||||||
url: MASTODON_PIN_OWN_STATUS(id),
|
url: MASTODON_PIN_OWN_STATUS_URL(id),
|
||||||
credentials,
|
credentials,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
export const unpinOwnStatus = ({ id, credentials }) =>
|
export const unpinOwnStatus = ({ id, credentials }) =>
|
||||||
promisedRequest({
|
promisedRequest({
|
||||||
url: MASTODON_UNPIN_OWN_STATUS(id),
|
url: MASTODON_UNPIN_OWN_STATUS_URL(id),
|
||||||
credentials,
|
credentials,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
export const muteConversation = ({ id, credentials }) =>
|
export const muteConversation = ({ id, credentials }) =>
|
||||||
promisedRequest({
|
promisedRequest({
|
||||||
url: MASTODON_MUTE_CONVERSATION(id),
|
url: MASTODON_MUTE_CONVERSATION_URL(id),
|
||||||
credentials,
|
credentials,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
||||||
export const unmuteConversation = ({ id, credentials }) =>
|
export const unmuteConversation = ({ id, credentials }) =>
|
||||||
promisedRequest({
|
promisedRequest({
|
||||||
url: MASTODON_UNMUTE_CONVERSATION(id),
|
url: MASTODON_UNMUTE_CONVERSATION_URL(id),
|
||||||
credentials,
|
credentials,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
}).then(({ data, ...rest }) => ({ ...rest, data: parseStatus(data) }))
|
||||||
|
|
@ -656,7 +662,7 @@ export const fetchUserInLists = ({ id, credentials }) =>
|
||||||
|
|
||||||
export const removeUserFromFollowers = ({ id, credentials }) =>
|
export const removeUserFromFollowers = ({ id, credentials }) =>
|
||||||
promisedRequest({
|
promisedRequest({
|
||||||
url: MASTODON_REMOVE_USER_FROM_FOLLOWERS(id),
|
url: MASTODON_REMOVE_USER_FROM_FOLLOWERS_URL(id),
|
||||||
credentials,
|
credentials,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,26 @@ const PLEROMA_STREAMING_EVENTS = new Set([
|
||||||
'pleroma:respond',
|
'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
|
// 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 = ({
|
export const ProcessedWS = ({
|
||||||
url,
|
url,
|
||||||
preprocessor = handleMastoWS,
|
preprocessor = handleMastoWS,
|
||||||
|
|
@ -39,9 +57,7 @@ export const ProcessedWS = ({
|
||||||
if (!socket) throw new Error(`Failed to create socket ${id}`)
|
if (!socket) throw new Error(`Failed to create socket ${id}`)
|
||||||
const proxy = (original, eventName, processor = (a) => a) => {
|
const proxy = (original, eventName, processor = (a) => a) => {
|
||||||
original.addEventListener(eventName, (eventData) => {
|
original.addEventListener(eventName, (eventData) => {
|
||||||
eventTarget.dispatchEvent(
|
eventTarget.dispatchEvent(new WSEvent(eventName, processor(eventData)))
|
||||||
new CustomEvent(eventName, { detail: processor(eventData) }),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
socket.addEventListener('open', (wsEvent) => {
|
socket.addEventListener('open', (wsEvent) => {
|
||||||
|
|
@ -75,7 +91,7 @@ export const ProcessedWS = ({
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
const onAuthenticated = () => {
|
const onAuthenticated = () => {
|
||||||
eventTarget.dispatchEvent(new CustomEvent('pleroma:authenticated'))
|
eventTarget.dispatchEvent(new WSEvent('pleroma:authenticated'))
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy(socket, 'open')
|
proxy(socket, 'open')
|
||||||
|
|
@ -126,14 +142,14 @@ export const handleMastoWS = (
|
||||||
const { data } = wsEvent
|
const { data } = wsEvent
|
||||||
if (!data) return
|
if (!data) return
|
||||||
const parsedEvent = JSON.parse(data)
|
const parsedEvent = JSON.parse(data)
|
||||||
const { event, payload } = parsedEvent
|
const { event, stream, payload } = parsedEvent
|
||||||
if (
|
if (
|
||||||
MASTODON_STREAMING_EVENTS.has(event) ||
|
MASTODON_STREAMING_EVENTS.has(event) ||
|
||||||
PLEROMA_STREAMING_EVENTS.has(event)
|
PLEROMA_STREAMING_EVENTS.has(event)
|
||||||
) {
|
) {
|
||||||
// MastoBE and PleromaBE both send payload for delete as a PLAIN string
|
// MastoBE and PleromaBE both send payload for delete as a PLAIN string
|
||||||
if (event === 'delete') {
|
if (event === 'delete') {
|
||||||
return { event, id: payload }
|
return { event, stream, id: payload }
|
||||||
}
|
}
|
||||||
const data = payload ? JSON.parse(payload) : null
|
const data = payload ? JSON.parse(payload) : null
|
||||||
if (event === 'pleroma:respond') {
|
if (event === 'pleroma:respond') {
|
||||||
|
|
@ -150,25 +166,16 @@ export const handleMastoWS = (
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
} else if (event === 'update') {
|
} else if (event === 'update') {
|
||||||
return { event, status: parseStatus(data) }
|
return { event, stream, status: parseStatus(data) }
|
||||||
} else if (event === 'status.update') {
|
} else if (event === 'status.update') {
|
||||||
return { event, status: parseStatus(data) }
|
return { event, stream, status: parseStatus(data) }
|
||||||
} else if (event === 'notification') {
|
} else if (event === 'notification') {
|
||||||
return { event, notification: parseNotification(data) }
|
return { event, stream, notification: parseNotification(data) }
|
||||||
} else if (event === 'pleroma:chat_update') {
|
} else if (event === 'pleroma:chat_update') {
|
||||||
return { event, chatUpdate: parseChat(data) }
|
return { event, stream, chatUpdate: parseChat(data) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('Unknown event', wsEvent)
|
console.warn('Unknown event', wsEvent)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WSConnectionStatus = Object.freeze({
|
|
||||||
JOINED: 1,
|
|
||||||
CLOSED: 2,
|
|
||||||
ERROR: 3,
|
|
||||||
DISABLED: 4,
|
|
||||||
STARTING: 5,
|
|
||||||
STARTING_INITIAL: 6,
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import {
|
||||||
import routes from './routes'
|
import routes from './routes'
|
||||||
|
|
||||||
import { useAuthFlowStore } from 'src/stores/auth_flow'
|
import { useAuthFlowStore } from 'src/stores/auth_flow'
|
||||||
|
import { useChatsStore } from 'src/stores/chats.js'
|
||||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||||
import { useI18nStore } from 'src/stores/i18n'
|
import { useI18nStore } from 'src/stores/i18n'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
|
@ -36,9 +37,12 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_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 { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.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 VBodyScrollLock from 'src/directives/body_scroll_lock'
|
||||||
import {
|
import {
|
||||||
|
|
@ -454,7 +458,7 @@ const setConfig = async ({ store }) => {
|
||||||
const checkOAuthToken = async ({ store }) => {
|
const checkOAuthToken = async ({ store }) => {
|
||||||
const oauth = useOAuthStore()
|
const oauth = useOAuthStore()
|
||||||
if (oauth.userToken) {
|
if (oauth.userToken) {
|
||||||
return store.dispatch('loginUser', oauth.userToken)
|
return useUsersStore().loginUser(oauth.userToken)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -541,7 +545,7 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
||||||
window.highlightConfig = useUserHighlightStore()
|
window.highlightConfig = useUserHighlightStore()
|
||||||
|
|
||||||
FaviconService.initFaviconService()
|
FaviconService.initFaviconService()
|
||||||
initServiceWorker(store)
|
initServiceWorker(useNotificationsStore())
|
||||||
|
|
||||||
window.addEventListener('focus', () => updateFocus())
|
window.addEventListener('focus', () => updateFocus())
|
||||||
|
|
||||||
|
|
@ -589,6 +593,11 @@ const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
||||||
|
|
||||||
useI18nStore().setI18n(i18n)
|
useI18nStore().setI18n(i18n)
|
||||||
|
|
||||||
|
// Global WS handlers
|
||||||
|
useChatsStore().attachSocket()
|
||||||
|
useInterfaceStore().attachSocket()
|
||||||
|
useStatusesStore().attachSocket()
|
||||||
|
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(store)
|
app.use(store)
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,16 @@
|
||||||
import AuthForm from 'src/components/auth_form/auth_form.js'
|
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 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 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 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 { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.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) => {
|
const validateAuthenticatedRoute = (to, from, next) => {
|
||||||
if (store.state.users.currentUser) {
|
if (useUsersStore().currentUser) {
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
next(
|
next(
|
||||||
|
|
@ -31,7 +25,7 @@ export default (store) => {
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: () => {
|
redirect: () => {
|
||||||
return (
|
return (
|
||||||
(store.state.users.currentUser
|
(useUsersStore().currentUser
|
||||||
? useInstanceStore().instanceIdentity.redirectRootLogin
|
? useInstanceStore().instanceIdentity.redirectRootLogin
|
||||||
: useInstanceStore().instanceIdentity.redirectRootNoLogin) ||
|
: useInstanceStore().instanceIdentity.redirectRootNoLogin) ||
|
||||||
'/main/all'
|
'/main/all'
|
||||||
|
|
@ -41,22 +35,52 @@ export default (store) => {
|
||||||
{
|
{
|
||||||
name: 'public-external-timeline',
|
name: 'public-external-timeline',
|
||||||
path: '/main/all',
|
path: '/main/all',
|
||||||
component: PublicAndExternalTimeline,
|
component: Timeline,
|
||||||
|
props: () => ({
|
||||||
|
timelineRef: { name: 'publicAndExternal' },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'public-timeline',
|
name: 'public-timeline',
|
||||||
path: '/main/public',
|
path: '/main/public',
|
||||||
component: PublicTimeline,
|
component: Timeline,
|
||||||
|
props: () => ({
|
||||||
|
timelineRef: { name: 'public' },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'friends',
|
name: 'friends',
|
||||||
path: '/main/friends',
|
path: '/main/friends',
|
||||||
component: FriendsTimeline,
|
component: Timeline,
|
||||||
beforeEnter: validateAuthenticatedRoute,
|
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',
|
name: 'conversation',
|
||||||
path: '/notice/:id',
|
path: '/notice/:id',
|
||||||
|
|
@ -71,7 +95,14 @@ export default (store) => {
|
||||||
meta: { dontScroll: true },
|
meta: { dontScroll: true },
|
||||||
beforeEnter: validateAuthenticatedRoute,
|
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',
|
name: 'remote-user-profile-acct',
|
||||||
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
||||||
|
|
@ -104,8 +135,11 @@ export default (store) => {
|
||||||
{
|
{
|
||||||
name: 'dms',
|
name: 'dms',
|
||||||
path: '/users/:username/dms',
|
path: '/users/:username/dms',
|
||||||
component: DMs,
|
component: Timeline,
|
||||||
beforeEnter: validateAuthenticatedRoute,
|
beforeEnter: validateAuthenticatedRoute,
|
||||||
|
props: () => ({
|
||||||
|
timelineRef: { name: 'dms' },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'registration',
|
name: 'registration',
|
||||||
|
|
@ -202,8 +236,10 @@ export default (store) => {
|
||||||
{
|
{
|
||||||
name: 'lists-timeline',
|
name: 'lists-timeline',
|
||||||
path: '/lists/:id',
|
path: '/lists/:id',
|
||||||
component: () =>
|
component: Timeline,
|
||||||
import('src/components/lists_timeline/lists_timeline.vue'),
|
props: (route) => ({
|
||||||
|
timelineRef: { name: 'list', argument: route.params.id },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'lists-edit',
|
name: 'lists-edit',
|
||||||
|
|
@ -237,7 +273,10 @@ export default (store) => {
|
||||||
{
|
{
|
||||||
name: 'bookmark-folder',
|
name: 'bookmark-folder',
|
||||||
path: '/bookmarks/:id',
|
path: '/bookmarks/:id',
|
||||||
component: BookmarkTimeline,
|
component: Timeline,
|
||||||
|
props: (route) => ({
|
||||||
|
timelineRef: { name: 'bookmarks', argument: route.params.id },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'bookmark-folder-edit',
|
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 { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useReportsStore } from 'src/stores/reports'
|
import { useReportsStore } from 'src/stores/reports'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'
|
import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -47,10 +48,10 @@ const AccountActions = {
|
||||||
this.showingConfirmBlock = false
|
this.showingConfirmBlock = false
|
||||||
},
|
},
|
||||||
showRepeats() {
|
showRepeats() {
|
||||||
this.$store.dispatch('showReblogs', this.user.id)
|
useUsersStore().showReblogs(this.user.id)
|
||||||
},
|
},
|
||||||
hideRepeats() {
|
hideRepeats() {
|
||||||
this.$store.dispatch('hideReblogs', this.user.id)
|
useUsersStore().hideReblogs(this.user.id)
|
||||||
},
|
},
|
||||||
blockUser() {
|
blockUser() {
|
||||||
if (this.$refs.timedBlockDialog) {
|
if (this.$refs.timedBlockDialog) {
|
||||||
|
|
@ -64,11 +65,11 @@ const AccountActions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doBlockUser() {
|
doBlockUser() {
|
||||||
this.$store.dispatch('blockUser', { id: this.user.id })
|
useUsersStore().blockUser(this.user.id)
|
||||||
this.hideConfirmBlock()
|
this.hideConfirmBlock()
|
||||||
},
|
},
|
||||||
unblockUser() {
|
unblockUser() {
|
||||||
this.$store.dispatch('unblockUser', this.user.id)
|
useUsersStore().unblockUser(this.user.id)
|
||||||
},
|
},
|
||||||
removeUserFromFollowers() {
|
removeUserFromFollowers() {
|
||||||
if (!this.shouldConfirmRemoveUserFromFollowers) {
|
if (!this.shouldConfirmRemoveUserFromFollowers) {
|
||||||
|
|
@ -78,7 +79,7 @@ const AccountActions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doRemoveUserFromFollowers() {
|
doRemoveUserFromFollowers() {
|
||||||
this.$store.dispatch('removeUserFromFollowers', this.user.id)
|
useUsersStore().removeUserFromFollowers(this.user.id)
|
||||||
this.hideConfirmRemoveUserFromFollowers()
|
this.hideConfirmRemoveUserFromFollowers()
|
||||||
},
|
},
|
||||||
reportUser() {
|
reportUser() {
|
||||||
|
|
@ -88,8 +89,8 @@ const AccountActions = {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'chat',
|
name: 'chat',
|
||||||
params: {
|
params: {
|
||||||
username: this.$store.state.users.currentUser.screen_name,
|
username: useUsersStore().currentUser.screen_name,
|
||||||
recipient_id: this.user.id,
|
chatUserId: 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 AnnouncementEditor from 'src/components/announcement_editor/announcement_editor.vue'
|
||||||
import localeService from '../../services/locale/locale.service.js'
|
import localeService from '../../services/locale/locale.service.js'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const Announcement = {
|
const Announcement = {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -25,9 +26,7 @@ const Announcement = {
|
||||||
announcement: Object,
|
announcement: Object,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
|
||||||
}),
|
|
||||||
canEditAnnouncement() {
|
canEditAnnouncement() {
|
||||||
return this.currentUser?.privileges.has(
|
return this.currentUser?.privileges.has(
|
||||||
'announcements_manage_announcements',
|
'announcements_manage_announcements',
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import Announcement from 'src/components/announcement/announcement.vue'
|
import Announcement from 'src/components/announcement/announcement.vue'
|
||||||
import AnnouncementEditor from 'src/components/announcement_editor/announcement_editor.vue'
|
import AnnouncementEditor from 'src/components/announcement_editor/announcement_editor.vue'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const AnnouncementsPage = {
|
const AnnouncementsPage = {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -26,9 +27,7 @@ const AnnouncementsPage = {
|
||||||
useAnnouncementsStore().fetchAnnouncements()
|
useAnnouncementsStore().fetchAnnouncements()
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
|
||||||
}),
|
|
||||||
announcements() {
|
announcements() {
|
||||||
return useAnnouncementsStore().announcements
|
return useAnnouncementsStore().announcements
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
||||||
const AvatarList = {
|
const AvatarList = {
|
||||||
props: ['users'],
|
props: {
|
||||||
|
userIds: Set,
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
slicedUsers() {
|
slicedUsers() {
|
||||||
return this.users ? this.users.slice(0, 15) : []
|
return [...(this.userIds ?? [])]
|
||||||
|
.slice(0, 15)
|
||||||
|
.map((id) => useUsersStore().findUser(id))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
class="avatars-item"
|
class="avatars-item"
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
:user="user"
|
:user-id="user.id"
|
||||||
class="avatar-small"
|
class="avatar-small"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
class="user-avatar avatar"
|
class="user-avatar avatar"
|
||||||
:user="user"
|
:user-id="user.id"
|
||||||
@click.prevent
|
@click.prevent
|
||||||
/>
|
/>
|
||||||
</UserPopover>
|
</UserPopover>
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
{{ $t('admin_dash.users.labels.handle_colon') }}
|
{{ $t('admin_dash.users.labels.handle_colon') }}
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
</strong>
|
</strong>
|
||||||
<user-link
|
<UserLink
|
||||||
class="basic-user-card-screen-name"
|
class="basic-user-card-screen-name"
|
||||||
:user="user"
|
:user="user"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -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 UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
||||||
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const BlockCard = {
|
const BlockCard = {
|
||||||
props: ['userId'],
|
props: ['userId'],
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return this.$store.getters.findUser(this.userId)
|
return useUsersStore().findUser(this.userId)
|
||||||
},
|
},
|
||||||
relationship() {
|
relationship() {
|
||||||
return this.$store.getters.relationship(this.userId)
|
return useUsersStore().relationship(this.userId)
|
||||||
},
|
},
|
||||||
blocked() {
|
blocked() {
|
||||||
return this.relationship.blocking
|
return this.relationship.blocking
|
||||||
|
|
@ -35,13 +36,13 @@ const BlockCard = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
unblockUser() {
|
unblockUser() {
|
||||||
this.$store.dispatch('unblockUser', this.user.id)
|
useUsersStore().unblockUser(this.user.id)
|
||||||
},
|
},
|
||||||
blockUser() {
|
blockUser() {
|
||||||
if (this.blockExpiration) {
|
if (this.blockExpiration) {
|
||||||
this.$refs.timedBlockDialog.optionallyPrompt()
|
this.$refs.timedBlockDialog.optionallyPrompt()
|
||||||
} else {
|
} 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 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import ChatListItem from 'src/components/chat_list_item/chat_list_item.vue'
|
import ChatListItem from 'src/components/chat_list_item/chat_list_item.vue'
|
||||||
import ChatNew from 'src/components/chat_new/chat_new.vue'
|
import ChatNew from 'src/components/chat_new/chat_new.vue'
|
||||||
import List from 'src/components/list/list.vue'
|
import List from 'src/components/list/list.vue'
|
||||||
|
|
||||||
import { useChatsStore } from 'src/stores/chats.js'
|
import { useChatsStore } from 'src/stores/chats.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const ChatList = {
|
const ChatList = {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -14,10 +14,8 @@ const ChatList = {
|
||||||
ChatNew,
|
ChatNew,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
...mapState(useChatsStore, ['sortedChatList']),
|
||||||
}),
|
|
||||||
...mapPiniaState(useChatsStore, ['sortedChatList']),
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import AvatarList from 'src/components/avatar_list/avatar_list.vue'
|
import AvatarList from 'src/components/avatar_list/avatar_list.vue'
|
||||||
import ChatTitle from 'src/components/chat_title/chat_title.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 Timeago from 'src/components/timeago/timeago.vue'
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
|
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const ChatListItem = {
|
const ChatListItem = {
|
||||||
name: 'ChatListItem',
|
name: 'ChatListItem',
|
||||||
props: ['chat'],
|
props: ['chat'],
|
||||||
|
|
@ -17,9 +19,7 @@ const ChatListItem = {
|
||||||
StatusBody,
|
StatusBody,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
|
||||||
}),
|
|
||||||
attachmentInfo() {
|
attachmentInfo() {
|
||||||
if (this.chat.lastMessage.attachments.length === 0) {
|
if (this.chat.lastMessage.attachments.length === 0) {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
>
|
>
|
||||||
<div class="chat-list-item-left">
|
<div class="chat-list-item-left">
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
:user="chat.account"
|
:user-id="chat.account.id"
|
||||||
height="48px"
|
height="48px"
|
||||||
width="48px"
|
width="48px"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Attachment from 'src/components/attachment/attachment.vue'
|
import Attachment from 'src/components/attachment/attachment.vue'
|
||||||
import ChatMessageDate from 'src/components/chat_message_date/chat_message_date.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 { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface'
|
import { useInterfaceStore } from 'src/stores/interface'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -79,7 +80,7 @@ const ChatMessage = {
|
||||||
return this.isStatus ? this.message.user.id : this.message.account_id
|
return this.isStatus ? this.message.user.id : this.message.account_id
|
||||||
},
|
},
|
||||||
author() {
|
author() {
|
||||||
return this.$store.getters.findUser(this.authorId)
|
return useUsersStore().findUser(this.authorId)
|
||||||
},
|
},
|
||||||
isCurrentUser() {
|
isCurrentUser() {
|
||||||
// mini-hack/optimizaiton:
|
// mini-hack/optimizaiton:
|
||||||
|
|
@ -100,25 +101,21 @@ const ChatMessage = {
|
||||||
return !this.message.in_reply_to_status_id
|
return !this.message.in_reply_to_status_id
|
||||||
},
|
},
|
||||||
customReplyTo() {
|
customReplyTo() {
|
||||||
return this.$store.state.statuses.allStatusesObject[
|
return useStatusesStore().allStatuses.get(
|
||||||
this.message.in_reply_to_status_id
|
this.message.in_reply_to_status_id,
|
||||||
]
|
)
|
||||||
},
|
},
|
||||||
replyToName() {
|
replyToName() {
|
||||||
if (this.message.in_reply_to_screen_name) {
|
if (this.message.in_reply_to_screen_name) {
|
||||||
return this.message.in_reply_to_screen_name
|
return this.message.in_reply_to_screen_name
|
||||||
} else {
|
} else {
|
||||||
const user = this.$store.getters.findUser(
|
const user = useUsersStore().findUser(this.message.in_reply_to_user_id)
|
||||||
this.message.in_reply_to_user_id,
|
|
||||||
)
|
|
||||||
return user?.screen_name_ui
|
return user?.screen_name_ui
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replyProfileLink() {
|
replyProfileLink() {
|
||||||
if (this.isCustomReply) {
|
if (this.isCustomReply) {
|
||||||
const user = this.$store.getters.findUser(
|
const user = useUsersStore().findUser(this.message.in_reply_to_user_id)
|
||||||
this.message.in_reply_to_user_id,
|
|
||||||
)
|
|
||||||
// FIXME Why user not found sometimes???
|
// FIXME Why user not found sometimes???
|
||||||
return user ? user.statusnet_profile_url : 'NOT_FOUND'
|
return user ? user.statusnet_profile_url : 'NOT_FOUND'
|
||||||
}
|
}
|
||||||
|
|
@ -167,14 +164,12 @@ const ChatMessage = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Global stuff
|
// Global stuff
|
||||||
...mapPiniaState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
betterShadow: (store) => store.browserSupport.cssFilter,
|
betterShadow: (store) => store.browserSupport.cssFilter,
|
||||||
}),
|
}),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
...mapState(useInstanceStore, ['restrictedNicknames']),
|
||||||
restrictedNicknames: (state) => useInstanceStore().restrictedNicknames,
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
}),
|
|
||||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
v-if="author"
|
v-if="author"
|
||||||
:compact="true"
|
:compact="true"
|
||||||
:user="author"
|
:user-id="author.id"
|
||||||
/>
|
/>
|
||||||
</UserPopover>
|
</UserPopover>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { mapGetters, mapState } from 'vuex'
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
|
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useSearchStore } from 'src/stores/search.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { chats } from 'src/api/chats.js'
|
import { chats } from 'src/api/chats.js'
|
||||||
|
|
||||||
|
|
@ -42,17 +44,14 @@ const chatNew = {
|
||||||
return this.suggestions
|
return this.suggestions
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser', 'findUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
|
||||||
}),
|
|
||||||
...mapGetters(['findUser']),
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goBack() {
|
goBack() {
|
||||||
this.$emit('cancel')
|
this.$emit('cancel')
|
||||||
},
|
},
|
||||||
goToChat(user) {
|
goToChat(user) {
|
||||||
this.$router.push({ name: 'chat', params: { recipient_id: user.id } })
|
this.$router.push({ name: 'chat', params: { chatUserId: user.id } })
|
||||||
},
|
},
|
||||||
onInput() {
|
onInput() {
|
||||||
this.search(this.query)
|
this.search(this.query)
|
||||||
|
|
@ -73,7 +72,8 @@ const chatNew = {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.userIds = []
|
this.userIds = []
|
||||||
this.$store
|
this.$store
|
||||||
.dispatch('search', { q: query, resolve: true, type: 'accounts' })
|
useSearchStore()
|
||||||
|
.search({ q: query, resolve: true, type: 'accounts' })
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.userIds = data.accounts.map((a) => a.id)
|
this.userIds = data.accounts.map((a) => a.id)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
>
|
>
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
class="titlebar-avatar"
|
class="titlebar-avatar"
|
||||||
:user="user"
|
:user-id="user.id"
|
||||||
/>
|
/>
|
||||||
</UserPopover>
|
</UserPopover>
|
||||||
<RichContent
|
<RichContent
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { get, maxBy, minBy, sortBy, throttle } from 'lodash'
|
import { get, maxBy, minBy, sortBy, throttle } from 'lodash'
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
||||||
import ChatTitle from 'src/components/chat_title/chat_title.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 { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.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 {
|
import {
|
||||||
chatMessages,
|
chatMessages,
|
||||||
|
|
@ -85,14 +87,18 @@ const Chat = {
|
||||||
|
|
||||||
// Internal network stuff
|
// Internal network stuff
|
||||||
fetcher: null,
|
fetcher: null,
|
||||||
|
socket: null,
|
||||||
|
streaming: false,
|
||||||
|
fetching: true,
|
||||||
errorLoadingChat: false,
|
errorLoadingChat: false,
|
||||||
messageRetriers: {},
|
messageRetriers: {},
|
||||||
idempotencyKeyIndex: {},
|
idempotencyKeyIndex: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
async created() {
|
||||||
if (this.testMode) return
|
if (this.testMode) return
|
||||||
this.startFetching()
|
await this.activate()
|
||||||
|
this.attachSocket()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
window.addEventListener('resize', this.handleResize)
|
window.addEventListener('resize', this.handleResize)
|
||||||
|
|
@ -118,10 +124,14 @@ const Chat = {
|
||||||
this.handleVisibilityChange,
|
this.handleVisibilityChange,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (this.testMode) return
|
||||||
|
this.deactivate()
|
||||||
|
this.detachSocket()
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
conversationId() {
|
conversationId() {
|
||||||
const status = this.$store.state.statuses.allStatusesObject[this.statusId]
|
const status = useStatusesStore().allStatuses.get(this.statusId)
|
||||||
return get(
|
return get(
|
||||||
status,
|
status,
|
||||||
'retweeted_status.statusnet_conversation_id',
|
'retweeted_status.statusnet_conversation_id',
|
||||||
|
|
@ -157,17 +167,14 @@ const Chat = {
|
||||||
if (this.isConversation) return false // Unsupported
|
if (this.isConversation) return false // Unsupported
|
||||||
return (
|
return (
|
||||||
this.mergedConfig.useStreamingApi &&
|
this.mergedConfig.useStreamingApi &&
|
||||||
this.mastoUserSocketStatus === WSConnectionStatus.JOINED
|
useStreamingStore().state === WSConnectionStatus.JOINED
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
...mapPiniaState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus,
|
|
||||||
currentUser: (state) => state.users.currentUser,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
messages(old, neu) {
|
messages(old, neu) {
|
||||||
|
|
@ -226,16 +233,93 @@ const Chat = {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clear()
|
this.deactivate()
|
||||||
this.startFetching()
|
this.activate()
|
||||||
},
|
|
||||||
mastoUserSocketStatus(newValue) {
|
|
||||||
if (newValue === WSConnectionStatus.JOINED) {
|
|
||||||
this.fetchChat({ isFirstFetch: true })
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async activate() {
|
||||||
|
if (!this.isConversation) {
|
||||||
|
try {
|
||||||
|
const result = await getOrCreateChat({
|
||||||
|
accountId: this.chatUserId,
|
||||||
|
credentials: useOAuthStore().token,
|
||||||
|
})
|
||||||
|
const { data } = result
|
||||||
|
useUsersStore().addNewUsers({ ...result, data: data.account })
|
||||||
|
data.account = useUsersStore().findUser(data.account.id)
|
||||||
|
this.chat = data
|
||||||
|
this.maxId = this.chat.lastMessage?.id
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error creating or getting a chat', e)
|
||||||
|
this.errorLoadingChat = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isConversation || this.chat) {
|
||||||
|
this.startFetching('Chat activated', true)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollDown({ forceRead: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deactivate() {
|
||||||
|
this.clear()
|
||||||
|
if (this.fetching) {
|
||||||
|
this.stopFetching('Chat deactivated')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attachSocket() {
|
||||||
|
const et = new EventTarget()
|
||||||
|
const socket = {
|
||||||
|
name: 'chatview',
|
||||||
|
et,
|
||||||
|
}
|
||||||
|
|
||||||
|
et.addEventListener('update', this.onStreamMessage)
|
||||||
|
et.addEventListener('pleroma:chat_update', this.onChatUpdate)
|
||||||
|
et.addEventListener('open', this.onStreamConnect)
|
||||||
|
et.addEventListener('close', this.onStreamDisconnect)
|
||||||
|
|
||||||
|
this.socket = socket
|
||||||
|
useStreamingStore().addSubscriber(this.socket)
|
||||||
|
},
|
||||||
|
detachSocket() {
|
||||||
|
const { et } = this.socket
|
||||||
|
|
||||||
|
et.removeEventListener('update', this.onStreamMessage)
|
||||||
|
et.removeEventListener('pleroma:chat_update', this.onChatUpdate)
|
||||||
|
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 })
|
||||||
|
this.fetching = true
|
||||||
|
},
|
||||||
|
stopFetching(reason) {
|
||||||
|
console.debug('[Chat View] Stopped fetching', 'Reason:', reason)
|
||||||
|
this.fetcher.stop()
|
||||||
|
this.fetcher = null
|
||||||
|
this.fetching = false
|
||||||
|
},
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
async readChat() {
|
async readChat() {
|
||||||
if (this.conversationId) return // Unsupported
|
if (this.conversationId) return // Unsupported
|
||||||
|
|
@ -259,18 +343,8 @@ const Chat = {
|
||||||
this.lastReadMessageId = this.maxId
|
this.lastReadMessageId = this.maxId
|
||||||
this.newMessageCount = 0
|
this.newMessageCount = 0
|
||||||
},
|
},
|
||||||
scrollDown(options = {}) {
|
|
||||||
const { behavior = 'auto', forceRead = false } = options
|
// Clears
|
||||||
this.$nextTick(() => {
|
|
||||||
window.scrollTo({
|
|
||||||
top: document.documentElement.scrollHeight,
|
|
||||||
behavior,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (forceRead) {
|
|
||||||
this.readChat()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cullOlder() {
|
cullOlder() {
|
||||||
const maxIndex = this.messages.length
|
const maxIndex = this.messages.length
|
||||||
const minIndex = maxIndex - 50
|
const minIndex = maxIndex - 50
|
||||||
|
|
@ -366,35 +440,16 @@ const Chat = {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async startFetching() {
|
onStreamMessage({ data }) {
|
||||||
if (!this.isConversation) {
|
const messages = data.filter(
|
||||||
try {
|
({ statusnet_conversation_id }) =>
|
||||||
const { data } = await getOrCreateChat({
|
statusnet_conversation_id === this.conversationId,
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
this.fetchChat({ isFirstFetch: true })
|
this.addMessages({ messages })
|
||||||
|
},
|
||||||
|
onChatUpdate({ data: { chatUpdate } }) {
|
||||||
|
const messages = [chatUpdate.lastMessage]
|
||||||
|
this.addMessages({ messages })
|
||||||
},
|
},
|
||||||
addMessages({ messages: newMessages }) {
|
addMessages({ messages: newMessages }) {
|
||||||
for (let i = 0; i < newMessages.length; i++) {
|
for (let i = 0; i < newMessages.length; i++) {
|
||||||
|
|
@ -402,17 +457,23 @@ const Chat = {
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (!this.isConversation && message.chat_id !== this.chat.id) {
|
if (!this.isConversation && message.chat_id !== this.chat.id) {
|
||||||
|
// This is spammy, we get chat updates from a global chat update
|
||||||
|
// handler, which naturally receives updates for ALL chats.
|
||||||
|
// There is no way to subscribe to specific chat updates and listen
|
||||||
|
// to that in the API.
|
||||||
|
/*
|
||||||
console.warn(
|
console.warn(
|
||||||
`Chat message doesn't belong to current chat (id: ${this.chat.id})!!`,
|
`Chat message doesn't belong to current chat (id: ${this.chat.id})!!`,
|
||||||
message,
|
message,
|
||||||
)
|
)
|
||||||
|
*/
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear any known pending messages
|
// Clear any known pending messages
|
||||||
if (message.idempotency_key) {
|
if (message.idempotency_key) {
|
||||||
if (this.pendingMessagesIndex[message.idempotencyKeyIndex]) {
|
if (this.pendingMessagesIndex[message.idempotency_key]) {
|
||||||
delete this.pendingMessagesIndex[message.idempotencyKeyIndex]
|
delete this.pendingMessagesIndex[message.idempotency_key]
|
||||||
this.pendingMessages = this.pendingMessages.filter(
|
this.pendingMessages = this.pendingMessages.filter(
|
||||||
({ idempotency_key }) =>
|
({ idempotency_key }) =>
|
||||||
idempotency_key !== message.idempotency_key,
|
idempotency_key !== message.idempotency_key,
|
||||||
|
|
@ -438,9 +499,6 @@ const Chat = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
goBack() {
|
|
||||||
this.$router.back()
|
|
||||||
},
|
|
||||||
|
|
||||||
// Optimistic posting (chats only)
|
// Optimistic posting (chats only)
|
||||||
async sendMessage({ status, media, idempotencyKey }) {
|
async sendMessage({ status, media, idempotencyKey }) {
|
||||||
|
|
@ -621,6 +679,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
|
// Ugly
|
||||||
// TODO move to ChatMessage
|
// TODO move to ChatMessage
|
||||||
async deleteChatMessage({ chatId, messageId }) {
|
async deleteChatMessage({ chatId, messageId }) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import { defineAsyncComponent } from 'vue'
|
||||||
import Select from 'src/components/select/select.vue'
|
import Select from 'src/components/select/select.vue'
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['type', 'user', 'status'],
|
props: ['type', 'user', 'status'],
|
||||||
|
|
@ -33,9 +35,7 @@ export default {
|
||||||
return this.status.conversation_muted
|
return this.status.conversation_muted
|
||||||
},
|
},
|
||||||
domainIsMuted() {
|
domainIsMuted() {
|
||||||
return new Set(this.$store.state.users.currentUser.domainMutes).has(
|
return new Set(useUsersStore().currentUser.domainMutes).has(this.domain)
|
||||||
this.domain,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
shouldConfirm() {
|
shouldConfirm() {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
|
|
@ -70,17 +70,17 @@ export default {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'domain': {
|
case 'domain': {
|
||||||
if (!this.domainIsMuted) {
|
if (!this.domainIsMuted) {
|
||||||
this.$store.dispatch('muteDomain', this.domain)
|
useUsersStore().muteDomain(this.domain)
|
||||||
} else {
|
} else {
|
||||||
this.$store.dispatch('unmuteDomain', this.domain)
|
useUsersStore().unmuteDomain(this.domain)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'conversation': {
|
case 'conversation': {
|
||||||
if (!this.conversationIsMuted) {
|
if (!this.conversationIsMuted) {
|
||||||
this.$store.dispatch('muteConversation', { id: this.status.id })
|
useStatusesStore().muteConversation(this.status.id)
|
||||||
} else {
|
} else {
|
||||||
this.$store.dispatch('unmuteConversation', { id: this.status.id })
|
useStatusesStore().unmuteConversation(this.status.id)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { clone, filter, findIndex, get, reduce } from 'lodash'
|
import { get, reduce } from 'lodash'
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
||||||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||||
|
|
@ -9,9 +8,11 @@ import QuickViewSettings from 'src/components/quick_view_settings/quick_view_set
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
import ThreadTree from 'src/components/thread_tree/thread_tree.vue'
|
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 { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
import { useStreamingStore } from 'src/stores/streaming.js'
|
||||||
|
|
||||||
import { fetchConversation, fetchStatus } from 'src/api/public.js'
|
import { fetchConversation, fetchStatus } from 'src/api/public.js'
|
||||||
import { WSConnectionStatus } from 'src/api/websocket.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 = {
|
const conversation = {
|
||||||
props: {
|
props: {
|
||||||
statusId: {
|
statusId: {
|
||||||
|
|
@ -106,6 +93,7 @@ const conversation = {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits: ['update:virtualHeight'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
focused: null,
|
focused: null,
|
||||||
|
|
@ -114,6 +102,7 @@ const conversation = {
|
||||||
inlineDivePosition: null,
|
inlineDivePosition: null,
|
||||||
loadStatusError: null,
|
loadStatusError: null,
|
||||||
unsuspendibleIds: new Set(),
|
unsuspendibleIds: new Set(),
|
||||||
|
virtualHeight: 120,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
@ -121,7 +110,13 @@ const conversation = {
|
||||||
this.fetchConversation()
|
this.fetchConversation()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updateVirtualHeight()
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
status() {
|
||||||
|
return useStatusesStore().allStatuses.get(this.statusId)
|
||||||
|
},
|
||||||
maxDepthToShowByDefault() {
|
maxDepthToShowByDefault() {
|
||||||
// maxDepthInThread = max number of depths that is *visible*
|
// maxDepthInThread = max number of depths that is *visible*
|
||||||
// since our depth starts with 0 and "showing" means "showing children"
|
// since our depth starts with 0 and "showing" means "showing children"
|
||||||
|
|
@ -160,14 +155,11 @@ const conversation = {
|
||||||
return this.otherRepliesButtonPosition === 'inside'
|
return this.otherRepliesButtonPosition === 'inside'
|
||||||
},
|
},
|
||||||
suspendable() {
|
suspendable() {
|
||||||
return this.unsuspendibleIds.size > 0
|
return this.unsuspendibleIds.size === 0
|
||||||
},
|
},
|
||||||
hideStatus() {
|
hide() {
|
||||||
return this.virtualHidden && this.suspendable
|
return this.virtualHidden && this.suspendable
|
||||||
},
|
},
|
||||||
status() {
|
|
||||||
return this.$store.state.statuses.allStatusesObject[this.statusId]
|
|
||||||
},
|
|
||||||
originalStatusId() {
|
originalStatusId() {
|
||||||
if (this.status.retweeted_status) {
|
if (this.status.retweeted_status) {
|
||||||
return this.status.retweeted_status.id
|
return this.status.retweeted_status.id
|
||||||
|
|
@ -187,15 +179,14 @@ const conversation = {
|
||||||
return [this.status]
|
return [this.status]
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversation = clone(
|
const conversation = useStatusesStore().conversations.get(
|
||||||
this.$store.state.statuses.conversationsObject[this.conversationId],
|
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))
|
||||||
|
.filter((status) => status.type != 'repeat') // Old backend behavior?
|
||||||
|
.toSorted(sortById)
|
||||||
},
|
},
|
||||||
statusMap() {
|
statusMap() {
|
||||||
return this.conversation.reduce((res, s) => {
|
return this.conversation.reduce((res, s) => {
|
||||||
|
|
@ -375,8 +366,7 @@ const conversation = {
|
||||||
return !!(this.expanded || this.isPage)
|
return !!(this.expanded || this.isPage)
|
||||||
},
|
},
|
||||||
hiddenStyle() {
|
hiddenStyle() {
|
||||||
const height = this.status?.virtualHeight || '120px'
|
return { height: this.virtualHeight + 'px' }
|
||||||
return this.virtualHidden ? { height } : {}
|
|
||||||
},
|
},
|
||||||
threadDisplayStatus() {
|
threadDisplayStatus() {
|
||||||
return this.conversation.reduce((a, k) => {
|
return this.conversation.reduce((a, k) => {
|
||||||
|
|
@ -403,11 +393,11 @@ const conversation = {
|
||||||
maybeFocused() {
|
maybeFocused() {
|
||||||
return this.isExpanded ? this.focused : null
|
return this.isExpanded ? this.focused : null
|
||||||
},
|
},
|
||||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
...mapState({
|
...mapState(useStreamingStore, {
|
||||||
mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus,
|
mastoUserSocketStatus: (state) => state.state,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
@ -441,10 +431,7 @@ const conversation = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
virtualHidden() {
|
virtualHidden() {
|
||||||
this.$store.dispatch('setVirtualHeight', {
|
this.updateVirtualHeight()
|
||||||
statusId: this.statusId,
|
|
||||||
height: `${this.$el.clientHeight}px`,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -453,9 +440,12 @@ const conversation = {
|
||||||
fetchConversation({
|
fetchConversation({
|
||||||
id: this.statusId,
|
id: this.statusId,
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
}).then(({ data: { ancestors, descendants } }) => {
|
}).then(({ data: { ancestors, descendants }, timestamp }) => {
|
||||||
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
|
useStatusesStore().addNewStatuses({ statuses: ancestors, timestamp })
|
||||||
this.$store.dispatch('addNewStatuses', { statuses: descendants })
|
useStatusesStore().addNewStatuses({
|
||||||
|
statuses: descendants,
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
this.setFocused(this.originalStatusId)
|
this.setFocused(this.originalStatusId)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -465,7 +455,7 @@ const conversation = {
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
.then(({ data: status }) => {
|
.then(({ data: status }) => {
|
||||||
this.$store.dispatch('addNewStatuses', { statuses: [status] })
|
useStatusesStore().addNewStatuses({ statuses: [status] })
|
||||||
this.fetchConversation()
|
this.fetchConversation()
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|
@ -482,17 +472,17 @@ const conversation = {
|
||||||
this.focused = id
|
this.focused = id
|
||||||
|
|
||||||
if (!this.streamingEnabled) {
|
if (!this.streamingEnabled) {
|
||||||
this.$store.dispatch('fetchStatus', id)
|
useStatusesStore().fetchStatus(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.dispatch('fetchFavsAndRepeats', id)
|
useStatusesStore().fetchFavsAndRepeats(id)
|
||||||
this.$store.dispatch('fetchEmojiReactionsBy', id)
|
useStatusesStore().fetchEmojiReactions(id)
|
||||||
},
|
},
|
||||||
toggleExpanded() {
|
toggleExpanded() {
|
||||||
this.expanded = !this.expanded
|
this.expanded = !this.expanded
|
||||||
},
|
},
|
||||||
getConversationId(statusId) {
|
getConversationId(statusId) {
|
||||||
const status = this.$store.state.statuses.allStatusesObject[statusId]
|
const status = useStatusesStore().allStatuses.get(statusId)
|
||||||
return get(
|
return get(
|
||||||
status,
|
status,
|
||||||
'retweeted_status.statusnet_conversation_id',
|
'retweeted_status.statusnet_conversation_id',
|
||||||
|
|
@ -630,6 +620,18 @@ const conversation = {
|
||||||
this.$router.push({ name: 'conversation', params: { id: data.id } })
|
this.$router.push({ name: 'conversation', params: { id: data.id } })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateVirtualHeight() {
|
||||||
|
if (this.hide) return // no updates when not rendering
|
||||||
|
if (!this.status) return // not loaded yet
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.virtualHeight = this.$refs.body.getBoundingClientRect().height
|
||||||
|
this.$emit('update:virtualHeight', {
|
||||||
|
id: this.status.id,
|
||||||
|
height: this.virtualHeight,
|
||||||
|
top: this.$el.clientTop,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="!hideStatus"
|
v-if="!hide"
|
||||||
:style="hiddenStyle"
|
|
||||||
class="Conversation"
|
class="Conversation"
|
||||||
:class="{ '-expanded' : isExpanded, 'panel' : isExpanded }"
|
:class="{ '-expanded' : isExpanded, 'panel' : isExpanded }"
|
||||||
>
|
>
|
||||||
|
|
@ -40,6 +39,7 @@
|
||||||
<div
|
<div
|
||||||
v-if="isPage && !status"
|
v-if="isPage && !status"
|
||||||
class="conversation-body"
|
class="conversation-body"
|
||||||
|
ref="body"
|
||||||
:class="{ 'panel-body': isExpanded }"
|
:class="{ 'panel-body': isExpanded }"
|
||||||
>
|
>
|
||||||
<p v-if="!loadStatusError">
|
<p v-if="!loadStatusError">
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="conversation-body"
|
class="conversation-body"
|
||||||
|
ref="body"
|
||||||
:class="{ 'panel-body': isExpanded }"
|
:class="{ 'panel-body': isExpanded }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -99,7 +100,7 @@
|
||||||
ref="statusComponent"
|
ref="statusComponent"
|
||||||
class="conversation-status status-fadein panel-body"
|
class="conversation-status status-fadein panel-body"
|
||||||
|
|
||||||
:statusoid="status"
|
:status-id="status.id"
|
||||||
:replies="getReplies(status.id)"
|
:replies="getReplies(status.id)"
|
||||||
|
|
||||||
:expandable="!isExpanded"
|
:expandable="!isExpanded"
|
||||||
|
|
@ -116,6 +117,7 @@
|
||||||
@goto="setFocused"
|
@goto="setFocused"
|
||||||
@dive="() => diveIntoStatus(status.id)"
|
@dive="() => diveIntoStatus(status.id)"
|
||||||
@suspendable-state-change="onStatusSuspendStateChange"
|
@suspendable-state-change="onStatusSuspendStateChange"
|
||||||
|
@height-change="updateVirtualHeight"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).length > 1"
|
v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).length > 1"
|
||||||
|
|
@ -152,7 +154,7 @@
|
||||||
ref="statusComponent"
|
ref="statusComponent"
|
||||||
:depth="0"
|
:depth="0"
|
||||||
|
|
||||||
:status="status"
|
:status-id="status.id"
|
||||||
:in-profile="inProfile"
|
:in-profile="inProfile"
|
||||||
:conversation="conversation"
|
:conversation="conversation"
|
||||||
:collapsable="collapsable"
|
:collapsable="collapsable"
|
||||||
|
|
@ -174,6 +176,7 @@
|
||||||
@goto="setFocused"
|
@goto="setFocused"
|
||||||
@dive="diveIntoStatus"
|
@dive="diveIntoStatus"
|
||||||
@suspendable-state-change="onStatusSuspendStateChange"
|
@suspendable-state-change="onStatusSuspendStateChange"
|
||||||
|
@height-change="updateVirtualHeight"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -186,7 +189,7 @@
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
ref="statusComponent"
|
ref="statusComponent"
|
||||||
class="conversation-status status-fadein panel-body"
|
class="conversation-status status-fadein panel-body"
|
||||||
:statusoid="status"
|
:status-id="status.id"
|
||||||
:replies="getReplies(status.id)"
|
:replies="getReplies(status.id)"
|
||||||
|
|
||||||
:expandable="!isExpanded"
|
:expandable="!isExpanded"
|
||||||
|
|
@ -200,6 +203,7 @@
|
||||||
@goto="setFocused"
|
@goto="setFocused"
|
||||||
@toggle-expanded="toggleExpanded"
|
@toggle-expanded="toggleExpanded"
|
||||||
@suspendable-state-change="onStatusSuspendStateChange"
|
@suspendable-state-change="onStatusSuspendStateChange"
|
||||||
|
@height-change="updateVirtualHeight"
|
||||||
/>
|
/>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ import { defineAsyncComponent } from 'vue'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface'
|
import { useInterfaceStore } from 'src/stores/interface'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -14,6 +18,8 @@ import {
|
||||||
faComments,
|
faComments,
|
||||||
faHome,
|
faHome,
|
||||||
faInfoCircle,
|
faInfoCircle,
|
||||||
|
faPlug,
|
||||||
|
faPlugCircleXmark,
|
||||||
faSearch,
|
faSearch,
|
||||||
faSignInAlt,
|
faSignInAlt,
|
||||||
faSignOutAlt,
|
faSignOutAlt,
|
||||||
|
|
@ -33,6 +39,8 @@ library.add(
|
||||||
faTachometerAlt,
|
faTachometerAlt,
|
||||||
faCog,
|
faCog,
|
||||||
faInfoCircle,
|
faInfoCircle,
|
||||||
|
faPlug,
|
||||||
|
faPlugCircleXmark,
|
||||||
)
|
)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -91,11 +99,23 @@ export default {
|
||||||
sitename: (store) => store.instanceIdentity.name,
|
sitename: (store) => store.instanceIdentity.name,
|
||||||
hideSitename: (store) => store.instanceIdentity.hideSitename,
|
hideSitename: (store) => store.instanceIdentity.hideSitename,
|
||||||
}),
|
}),
|
||||||
currentUser() {
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
return this.$store.state.users.currentUser
|
...mapState(useStreamingStore, {
|
||||||
},
|
streamingConnected: (store) => store.state === WSConnectionStatus.JOINED,
|
||||||
|
}),
|
||||||
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
shouldConfirmLogout() {
|
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: {
|
methods: {
|
||||||
|
|
@ -115,9 +135,9 @@ export default {
|
||||||
this.showConfirmLogout()
|
this.showConfirmLogout()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doLogout() {
|
async doLogout() {
|
||||||
|
await useUsersStore().logout()
|
||||||
this.$router.replace('/main/public')
|
this.$router.replace('/main/public')
|
||||||
this.$store.dispatch('logout')
|
|
||||||
this.hideConfirmLogout()
|
this.hideConfirmLogout()
|
||||||
},
|
},
|
||||||
onSearchBarToggled(hidden) {
|
onSearchBarToggled(hidden) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,24 @@
|
||||||
>
|
>
|
||||||
{{ sitename }}
|
{{ sitename }}
|
||||||
</router-link>
|
</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>
|
</div>
|
||||||
<router-link
|
<router-link
|
||||||
class="logo"
|
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 ProgressButton from 'src/components/progress_button/progress_button.vue'
|
||||||
|
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const DomainMuteCard = {
|
const DomainMuteCard = {
|
||||||
props: ['domain'],
|
props: ['domain'],
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -7,18 +9,18 @@ const DomainMuteCard = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return this.$store.state.users.currentUser
|
return useUsersStore().currentUser
|
||||||
},
|
},
|
||||||
muted() {
|
muted() {
|
||||||
return this.user.domainMutes.includes(this.domain)
|
return this.user.domainMutes.has(this.domain)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
unmuteDomain() {
|
unmuteDomain() {
|
||||||
return this.$store.dispatch('unmuteDomain', this.domain)
|
return useUsersStore().unmuteDomain(this.domain)
|
||||||
},
|
},
|
||||||
muteDomain() {
|
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 StatusContent from 'src/components/status_content/status_content.vue'
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faPollH } from '@fortawesome/free-solid-svg-icons'
|
import { faPollH } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -65,7 +66,7 @@ const Draft = {
|
||||||
},
|
},
|
||||||
refStatus() {
|
refStatus() {
|
||||||
return this.draft.refId
|
return this.draft.refId
|
||||||
? this.$store.state.statuses.allStatusesObject[this.draft.refId]
|
? useStatusesStore().allStatuses.get(this.draft.refId)
|
||||||
: undefined
|
: undefined
|
||||||
},
|
},
|
||||||
localCollapseSubjectDefault() {
|
localCollapseSubjectDefault() {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash'
|
||||||
|
import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import Modal from 'src/components/modal/modal.vue'
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
|
|
||||||
import { useEditStatusStore } from 'src/stores/editStatus.js'
|
import { useEditStatusStore } from 'src/stores/editStatus.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const EditStatusModal = {
|
const EditStatusModal = {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -18,18 +20,16 @@ const EditStatusModal = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isLoggedIn() {
|
|
||||||
return !!this.$store.state.users.currentUser
|
|
||||||
},
|
|
||||||
modalActivated() {
|
modalActivated() {
|
||||||
return useEditStatusStore().modalActivated
|
return useEditStatusStore().modalActivated
|
||||||
},
|
},
|
||||||
isFormVisible() {
|
isFormVisible() {
|
||||||
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
return this.loggedIn && !this.resettingForm && this.modalActivated
|
||||||
},
|
},
|
||||||
params() {
|
params() {
|
||||||
return useEditStatusStore().params || {}
|
return useEditStatusStore().params || {}
|
||||||
},
|
},
|
||||||
|
...mapState(useUsersStore, ['loggedIn']),
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
params(newVal, oldVal) {
|
params(newVal, oldVal) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { useSearchStore } from 'src/stores/search.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* suggest - generates a suggestor function to be used by emoji-input
|
* suggest - generates a suggestor function to be used by emoji-input
|
||||||
* data: object providing source information for specific types of suggestions:
|
* data: object providing source information for specific types of suggestions:
|
||||||
|
|
@ -69,7 +72,7 @@ export const suggestEmoji = (emojis) => (input, nameKeywordLocalizer) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const suggestUsers = ({ dispatch, state }) => {
|
export const suggestUsers = () => {
|
||||||
// Keep some persistent values in closure, most importantly for the
|
// Keep some persistent values in closure, most importantly for the
|
||||||
// custom debounce to work. Lodash debounce does not return a promise.
|
// custom debounce to work. Lodash debounce does not return a promise.
|
||||||
let suggestions = []
|
let suggestions = []
|
||||||
|
|
@ -77,7 +80,7 @@ export const suggestUsers = ({ dispatch, state }) => {
|
||||||
let timeout = null
|
let timeout = null
|
||||||
let cancelUserSearch = null
|
let cancelUserSearch = null
|
||||||
|
|
||||||
const userSearch = (query) => dispatch('searchUsers', { query })
|
const userSearch = (query) => useSearchStore().searchUsers({ query })
|
||||||
const debounceUserSearch = (query) => {
|
const debounceUserSearch = (query) => {
|
||||||
cancelUserSearch?.()
|
cancelUserSearch?.()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
@ -105,7 +108,7 @@ export const suggestUsers = ({ dispatch, state }) => {
|
||||||
await debounceUserSearch(noPrefix)
|
await debounceUserSearch(noPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
const newSuggestions = state.users.users
|
const newSuggestions = [...useUsersStore().users.values()]
|
||||||
.filter(
|
.filter(
|
||||||
(user) =>
|
(user) =>
|
||||||
user.screen_name &&
|
user.screen_name &&
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import UserListPopover from 'src/components/user_list_popover/user_list_popover.
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCheck, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
|
import { faCheck, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -35,12 +37,12 @@ const EmojiReactions = {
|
||||||
},
|
},
|
||||||
accountsForEmoji() {
|
accountsForEmoji() {
|
||||||
return this.status.emoji_reactions.reduce((acc, reaction) => {
|
return this.status.emoji_reactions.reduce((acc, reaction) => {
|
||||||
acc[reaction.name] = reaction.accounts || []
|
acc.set(reaction.name, new Set(reaction.account_ids))
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, new Map())
|
||||||
},
|
},
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
return !!this.$store.state.users.currentUser
|
return !!useUsersStore().currentUser
|
||||||
},
|
},
|
||||||
remoteInteractionLink() {
|
remoteInteractionLink() {
|
||||||
return useInstanceStore().getRemoteInteractionLink({
|
return useInstanceStore().getRemoteInteractionLink({
|
||||||
|
|
@ -58,25 +60,22 @@ const EmojiReactions = {
|
||||||
reactedWith(emoji) {
|
reactedWith(emoji) {
|
||||||
return this.status.emoji_reactions.find((r) => r.name === emoji).me
|
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)
|
const hasNoAccounts = this.status.emoji_reactions.find((r) => !r.accounts)
|
||||||
if (hasNoAccounts) {
|
if (hasNoAccounts) {
|
||||||
return await this.$store.dispatch(
|
return await useStatusesStore().fetchEmojiReactions(this.status.id)
|
||||||
'fetchEmojiReactionsBy',
|
|
||||||
this.status.id,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reactWith(emoji) {
|
reactWith(emoji) {
|
||||||
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
useStatusesStore().reactWithEmoji(this.status.id, emoji)
|
||||||
},
|
},
|
||||||
unreact(emoji) {
|
unreact(emoji) {
|
||||||
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
|
useStatusesStore().unreactWithEmoji(this.status.id, emoji)
|
||||||
},
|
},
|
||||||
async emojiOnClick(emoji) {
|
async emojiOnClick(emoji) {
|
||||||
if (!this.loggedIn) return
|
if (!this.loggedIn) return
|
||||||
|
|
||||||
await this.fetchEmojiReactionsByIfMissing()
|
await this.fetchEmojiReactionsIfMissing()
|
||||||
if (this.reactedWith(emoji)) {
|
if (this.reactedWith(emoji)) {
|
||||||
this.unreact(emoji)
|
this.unreact(emoji)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -52,11 +52,11 @@
|
||||||
</FALayers>
|
</FALayers>
|
||||||
</component>
|
</component>
|
||||||
<UserListPopover
|
<UserListPopover
|
||||||
:users="accountsForEmoji[reaction.name]"
|
:user-ids="accountsForEmoji.get(reaction.name)"
|
||||||
class="emoji-reaction-popover"
|
class="emoji-reaction-popover"
|
||||||
:normal-button="true"
|
:normal-button="true"
|
||||||
:trigger-attrs="counterTriggerAttrs(reaction)"
|
:trigger-attrs="counterTriggerAttrs(reaction)"
|
||||||
@show="fetchEmojiReactionsByIfMissing()"
|
@show="fetchEmojiReactionsIfMissing()"
|
||||||
>
|
>
|
||||||
<span class="emoji-reaction-counts">{{ reaction.count }}</span>
|
<span class="emoji-reaction-counts">{{ reaction.count }}</span>
|
||||||
</UserListPopover>
|
</UserListPopover>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { useChatsStore } from 'src/stores/chats.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -52,7 +53,7 @@ const ExtraNotifications = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
currentUser() {
|
currentUser() {
|
||||||
return this.$store.state.users.currentUser
|
return useUsersStore().currentUser
|
||||||
},
|
},
|
||||||
...mapGetters(['followRequestCount']),
|
...mapGetters(['followRequestCount']),
|
||||||
...mapState(useAnnouncementsStore, {
|
...mapState(useAnnouncementsStore, {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import {
|
|
||||||
requestFollow,
|
|
||||||
requestUnfollow,
|
|
||||||
} from '../../services/follow_manipulate/follow_manipulate'
|
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['relationship', 'user', 'labelFollowing', 'buttonClass'],
|
props: ['relationship', 'user', 'labelFollowing', 'buttonClass'],
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -64,9 +61,11 @@ export default {
|
||||||
},
|
},
|
||||||
follow() {
|
follow() {
|
||||||
this.inProgress = true
|
this.inProgress = true
|
||||||
requestFollow(this.relationship.id, this.$store).then(() => {
|
useUsersStore()
|
||||||
this.inProgress = false
|
.followUser(this.relationship.id)
|
||||||
})
|
.finally(() => {
|
||||||
|
this.inProgress = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
unfollow() {
|
unfollow() {
|
||||||
if (this.shouldConfirmUnfollow) {
|
if (this.shouldConfirmUnfollow) {
|
||||||
|
|
@ -76,15 +75,12 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doUnfollow() {
|
doUnfollow() {
|
||||||
const store = this.$store
|
|
||||||
this.inProgress = true
|
this.inProgress = true
|
||||||
requestUnfollow(this.relationship.id, store).then(() => {
|
useUsersStore()
|
||||||
this.inProgress = false
|
.unfollowUser(this.relationship.id)
|
||||||
store.commit('removeStatus', {
|
.finally(() => {
|
||||||
timeline: 'friends',
|
this.inProgress = false
|
||||||
userId: this.relationship.id,
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
this.hideConfirmUnfollow()
|
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 RemoteFollow from 'src/components/remote_follow/remote_follow.vue'
|
||||||
import RemoveFollowerButton from 'src/components/remove_follower_button/remove_follower_button.vue'
|
import RemoveFollowerButton from 'src/components/remove_follower_button/remove_follower_button.vue'
|
||||||
|
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const FollowCard = {
|
const FollowCard = {
|
||||||
props: ['user', 'noFollowsYou'],
|
props: ['user', 'noFollowsYou'],
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -13,13 +15,13 @@ const FollowCard = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isMe() {
|
isMe() {
|
||||||
return this.$store.state.users.currentUser.id === this.user.id
|
return useUsersStore().currentUser?.id === this.user.id
|
||||||
},
|
},
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
return this.$store.state.users.currentUser
|
return useUsersStore().currentUser
|
||||||
},
|
},
|
||||||
relationship() {
|
relationship() {
|
||||||
return this.$store.getters.relationship(this.user.id)
|
return useUsersStore().relationships.get(this.user.id)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<basic-user-card :user="user">
|
<BasicUserCard :user="user">
|
||||||
<div class="follow-card-content-container">
|
<div class="follow-card-content-container">
|
||||||
<span
|
<span
|
||||||
v-if="isMe || (!noFollowsYou && relationship.followed_by)"
|
v-if="isMe || (!noFollowsYou && relationship.followed_by)"
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</basic-user-card>
|
</BasicUserCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./follow_card.js"></script>
|
<script src="./follow_card.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js'
|
|
||||||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_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 { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
|
||||||
import { approveUser, denyUser } from 'src/api/user.js'
|
import { approveUser, denyUser } from 'src/api/user.js'
|
||||||
|
|
@ -24,7 +24,7 @@ const FollowRequestCard = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
findFollowRequestNotificationId() {
|
findFollowRequestNotificationId() {
|
||||||
const notif = notificationsFromStore(this.$store).find(
|
const notif = useNotificationsStore().data.find(
|
||||||
(notif) =>
|
(notif) =>
|
||||||
notif.from_profile.id === this.user.id &&
|
notif.from_profile.id === this.user.id &&
|
||||||
notif.type === 'follow_request',
|
notif.type === 'follow_request',
|
||||||
|
|
@ -55,16 +55,11 @@ const FollowRequestCard = {
|
||||||
id: this.user.id,
|
id: this.user.id,
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
|
// TODO fix
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
|
|
||||||
const notifId = this.findFollowRequestNotificationId()
|
const notifId = this.findFollowRequestNotificationId()
|
||||||
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
|
useNotificationsStore().markSingleNotificationAsSeen(notifId)
|
||||||
this.$store.dispatch('updateNotification', {
|
|
||||||
id: notifId,
|
|
||||||
updater: (notification) => {
|
|
||||||
notification.type = 'follow'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this.hideApproveConfirmDialog()
|
this.hideApproveConfirmDialog()
|
||||||
},
|
},
|
||||||
denyUser() {
|
denyUser() {
|
||||||
|
|
@ -81,7 +76,8 @@ const FollowRequestCard = {
|
||||||
id: this.user.id,
|
id: this.user.id,
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
|
useNotificationsStore().dismissNotificationLocal(notifId)
|
||||||
|
// TODO fix
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
})
|
})
|
||||||
this.hideDenyConfirmDialog()
|
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 Notifications from 'src/components/notifications/notifications.vue'
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
|
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const tabModeDict = {
|
const tabModeDict = {
|
||||||
mentions: ['mention'],
|
mentions: ['mention'],
|
||||||
statuses: ['status'],
|
statuses: ['status'],
|
||||||
|
|
@ -14,10 +16,9 @@ const tabModeDict = {
|
||||||
const Interactions = {
|
const Interactions = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
allowFollowingMove:
|
allowFollowingMove: useUsersStore().currentUser.allow_following_move,
|
||||||
this.$store.state.users.currentUser.allow_following_move,
|
|
||||||
filterMode: tabModeDict.mentions,
|
filterMode: tabModeDict.mentions,
|
||||||
canSeeReports: this.$store.state.users.currentUser.privileges.has(
|
canSeeReports: useUsersStore().currentUser.privileges.has(
|
||||||
'reports_manage_reports',
|
'reports_manage_reports',
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ const List = {
|
||||||
default: () => '',
|
default: () => '',
|
||||||
},
|
},
|
||||||
preSelect: {
|
preSelect: {
|
||||||
type: Array,
|
type: Set,
|
||||||
default: [],
|
default: new Set(),
|
||||||
},
|
},
|
||||||
nonInteractive: {
|
nonInteractive: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
@ -48,7 +48,7 @@ const List = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
items: [],
|
items: [],
|
||||||
selected: new Set(this.preSelect),
|
selected: new Set(this.preSelect), // clone
|
||||||
loading: false,
|
loading: false,
|
||||||
bottomedOut: true,
|
bottomedOut: true,
|
||||||
error: null,
|
error: null,
|
||||||
|
|
@ -99,11 +99,11 @@ const List = {
|
||||||
this.fetchFunction(this.page)
|
this.fetchFunction(this.page)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
this.bottomedOut = isEmpty(result.items)
|
this.bottomedOut = isEmpty(result)
|
||||||
if (this.externalItems) return
|
if (this.externalItems) return
|
||||||
this.page += 1
|
this.page += 1
|
||||||
this.total = result.count
|
this.total = result.length
|
||||||
this.items.push(...result.items)
|
this.items.push(...result)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
|
||||||
|
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||||
import ListsUserSearch from 'src/components/lists_user_search/lists_user_search.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 { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useListsStore } from 'src/stores/lists.js'
|
import { useListsStore } from 'src/stores/lists.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -47,8 +47,8 @@ const ListsNew = {
|
||||||
.fetchListAccounts({ listId: this.id })
|
.fetchListAccounts({ listId: this.id })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.membersUserIds = this.findListAccounts(this.id)
|
this.membersUserIds = this.findListAccounts(this.id)
|
||||||
this.membersUserIds.forEach((userId) => {
|
this.membersUserIds.forEach((id) => {
|
||||||
this.$store.dispatch('fetchUserIfMissing', userId)
|
useUsersStore().fetchUserIfMissing({ id })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -66,11 +66,8 @@ const ListsNew = {
|
||||||
.map((userId) => this.findUser(userId))
|
.map((userId) => this.findUser(userId))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser', 'findUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
...mapState(useListsStore, ['findListTitle', 'findListAccounts']),
|
||||||
}),
|
|
||||||
...mapPiniaState(useListsStore, ['findListTitle', 'findListAccounts']),
|
|
||||||
...mapGetters(['findUser']),
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onInput() {
|
onInput() {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import { getListEntries } from 'src/components/navigation/filter.js'
|
import { getListEntries } from 'src/components/navigation/filter.js'
|
||||||
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
||||||
|
|
||||||
import { useListsStore } from 'src/stores/lists.js'
|
import { useListsStore } from 'src/stores/lists.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
export const ListsMenuContent = {
|
export const ListsMenuContent = {
|
||||||
props: ['showPin'],
|
props: ['showPin'],
|
||||||
|
|
@ -12,12 +12,10 @@ export const ListsMenuContent = {
|
||||||
NavigationEntry,
|
NavigationEntry,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapPiniaState(useListsStore, {
|
...mapState(useListsStore, {
|
||||||
lists: getListEntries,
|
lists: getListEntries,
|
||||||
}),
|
}),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.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>
|
|
||||||
|
|
@ -2,6 +2,8 @@ import { debounce } from 'lodash'
|
||||||
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
|
||||||
|
import { useSearchStore } from 'src/stores/search.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -32,8 +34,8 @@ const ListsUserSearch = {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.$emit('loading')
|
this.$emit('loading')
|
||||||
this.userIds = []
|
this.userIds = []
|
||||||
this.$store
|
useSearchStore()
|
||||||
.dispatch('search', {
|
.search({
|
||||||
q: query,
|
q: query,
|
||||||
resolve: true,
|
resolve: true,
|
||||||
type: 'accounts',
|
type: 'accounts',
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { mapActions, mapState as mapPiniaState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { getLoginUrl, getTokenWithCredentials } from 'src/api/oauth.js'
|
import { getLoginUrl, getTokenWithCredentials } from 'src/api/oauth.js'
|
||||||
|
|
||||||
|
|
@ -18,12 +18,10 @@ const LoginForm = {
|
||||||
error: false,
|
error: false,
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useUsersStore, ['loggingIn']),
|
||||||
loggingIn: (state) => state.users.loggingIn,
|
...mapState(useOAuthStore, ['clientId', 'clientSecret']),
|
||||||
}),
|
...mapState(useInstanceStore, ['server', 'registrationOpen']),
|
||||||
...mapPiniaState(useOAuthStore, ['clientId', 'clientSecret']),
|
...mapState(useAuthFlowStore, {
|
||||||
...mapPiniaState(useInstanceStore, ['server', 'registrationOpen']),
|
|
||||||
...mapPiniaState(useAuthFlowStore, {
|
|
||||||
isTokenAuth: (store) => store.requiredToken,
|
isTokenAuth: (store) => store.requiredToken,
|
||||||
isPasswordAuth: (store) => !store.requiredToken,
|
isPasswordAuth: (store) => !store.requiredToken,
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import UnicodeDomainIndicator from 'src/components/unicode_domain_indicator/unicode_domain_indicator.vue'
|
import UnicodeDomainIndicator from 'src/components/unicode_domain_indicator/unicode_domain_indicator.vue'
|
||||||
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
|
|
@ -12,6 +11,7 @@ import {
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.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'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ const MentionLink = {
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
url: {
|
url: {
|
||||||
required: true,
|
required: false,
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
|
|
@ -75,11 +75,11 @@ const MentionLink = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return this.url && this.$store?.getters.findUserByUrl(this.url)
|
return this.url ? useUsersStore().findUserByUrl(this.url) : null
|
||||||
},
|
},
|
||||||
isYou() {
|
isYou() {
|
||||||
// FIXME why user !== currentUser???
|
if (!this.currentUser) return false
|
||||||
return this.user?.id === this.currentUser.id
|
return this.user === this.currentUser
|
||||||
},
|
},
|
||||||
userName() {
|
userName() {
|
||||||
return this.user && this.userNameFullUi.split('@')[0]
|
return this.user && this.userNameFullUi.split('@')[0]
|
||||||
|
|
@ -156,11 +156,9 @@ const MentionLink = {
|
||||||
shouldFadeDomain() {
|
shouldFadeDomain() {
|
||||||
return this.mergedConfig.mentionLinkFadeDomain
|
return this.mergedConfig.mentionLinkFadeDomain
|
||||||
},
|
},
|
||||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
...mapPiniaState(useUserHighlightStore, ['highlight']),
|
...mapState(useUserHighlightStore, ['highlight']),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
v-if="shouldShowAvatar"
|
v-if="shouldShowAvatar"
|
||||||
class="mention-avatar"
|
class="mention-avatar"
|
||||||
:user="user"
|
:user-id="user.id"
|
||||||
/><span
|
/><span
|
||||||
class="shortName"
|
class="shortName"
|
||||||
>@<span
|
>@<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 GestureService from '../../services/gesture_service/gesture_service'
|
||||||
import {
|
import {
|
||||||
countExtraNotifications,
|
countExtraNotifications,
|
||||||
unseenNotificationsFromStore,
|
unseenNotifications,
|
||||||
} from '../../services/notification_utils/notification_utils'
|
} from '../../services/notification_utils/notification_utils'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
import { useChatsStore } from 'src/stores/chats.js'
|
import { useChatsStore } from 'src/stores/chats.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -53,11 +55,10 @@ const MobileNav = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentUser() {
|
currentUser() {
|
||||||
return this.$store.state.users.currentUser
|
return useUsersStore().currentUser
|
||||||
},
|
},
|
||||||
unseenNotifications() {
|
unseenNotifications() {
|
||||||
return unseenNotificationsFromStore(
|
return unseenNotifications(
|
||||||
this.$store,
|
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
||||||
)
|
)
|
||||||
|
|
@ -144,12 +145,12 @@ const MobileNav = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doLogout() {
|
doLogout() {
|
||||||
this.$router.replace('/main/public')
|
useUsersStore().logout()
|
||||||
this.$store.dispatch('logout')
|
|
||||||
this.hideConfirmLogout()
|
this.hideConfirmLogout()
|
||||||
|
this.$router.replace('/main/public')
|
||||||
},
|
},
|
||||||
markNotificationsAsSeen() {
|
markNotificationsAsSeen() {
|
||||||
this.$store.dispatch('markNotificationsAsSeen')
|
useNotificationsStore().markNotificationsAsSeen()
|
||||||
},
|
},
|
||||||
onScroll({ target: { scrollTop, clientHeight, scrollHeight } }) {
|
onScroll({ target: { scrollTop, clientHeight, scrollHeight } }) {
|
||||||
this.notificationsAtTop = scrollTop > 0
|
this.notificationsAtTop = scrollTop > 0
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { usePostStatusStore } from 'src/stores/post_status.js'
|
import { usePostStatusStore } from 'src/stores/post_status.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faPen } from '@fortawesome/free-solid-svg-icons'
|
import { faPen } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -33,9 +35,6 @@ const MobilePostStatusButton = {
|
||||||
window.removeEventListener('resize', this.handleOSK)
|
window.removeEventListener('resize', this.handleOSK)
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isLoggedIn() {
|
|
||||||
return !!this.$store.state.users.currentUser
|
|
||||||
},
|
|
||||||
isHidden() {
|
isHidden() {
|
||||||
if (HIDDEN_FOR_PAGES.has(this.$route.name)) {
|
if (HIDDEN_FOR_PAGES.has(this.$route.name)) {
|
||||||
return true
|
return true
|
||||||
|
|
@ -51,6 +50,7 @@ const MobilePostStatusButton = {
|
||||||
autohideFloatingPostButton() {
|
autohideFloatingPostButton() {
|
||||||
return !!useMergedConfigStore().mergedConfig.autohideFloatingPostButton
|
return !!useMergedConfigStore().mergedConfig.autohideFloatingPostButton
|
||||||
},
|
},
|
||||||
|
...mapState(useUsersStore, ['loggedIn']),
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
autohideFloatingPostButton: function (isEnabled) {
|
autohideFloatingPostButton: function (isEnabled) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
v-if="isLoggedIn"
|
v-if="loggedIn"
|
||||||
class="MobilePostButton button-default new-status-button"
|
class="MobilePostButton button-default new-status-button"
|
||||||
:class="{ 'hidden': isHidden, 'always-show': isPersistent }"
|
:class="{ 'hidden': isHidden, 'always-show': isPersistent }"
|
||||||
:title="$t('post_status.new_status')"
|
:title="$t('post_status.new_status')"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import Popover from 'src/components/popover/popover.vue'
|
||||||
|
|
||||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faChevronDown } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronDown } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -405,7 +406,7 @@ const ModerationTools = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
return this.$store.state.users.currentUser.role === 'admin'
|
return useUsersStore().currentUser.role === 'admin'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -452,7 +453,7 @@ const ModerationTools = {
|
||||||
},
|
},
|
||||||
privileged(privilege) {
|
privileged(privilege) {
|
||||||
if (this.isAdmin) return true
|
if (this.isAdmin) return true
|
||||||
return this.$store.state.users.currentUser.privileges.has(privilege)
|
return useUsersStore().currentUser.privileges.has(privilege)
|
||||||
},
|
},
|
||||||
setTag(tag, value) {
|
setTag(tag, value) {
|
||||||
useAdminSettingsStore().setUsersTags({
|
useAdminSettingsStore().setUsersTags({
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
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 UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
||||||
|
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const MuteCard = {
|
const MuteCard = {
|
||||||
props: ['userId'],
|
props: ['userId'],
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return this.$store.getters.findUser(this.userId)
|
return useUsersStore().findUser(this.userId)
|
||||||
},
|
},
|
||||||
relationship() {
|
relationship() {
|
||||||
return this.$store.getters.relationship(this.userId)
|
return useUsersStore().relationship(this.userId)
|
||||||
},
|
},
|
||||||
muted() {
|
muted() {
|
||||||
return this.relationship.muting
|
return this.relationship.muting
|
||||||
|
|
@ -30,7 +32,7 @@ const MuteCard = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
unmuteUser() {
|
unmuteUser() {
|
||||||
this.$store.dispatch('unmuteUser', this.userId)
|
useUsersStore().unmuteUser(this.user.id)
|
||||||
},
|
},
|
||||||
muteUser() {
|
muteUser() {
|
||||||
this.$refs.timedMuteDialog.optionallyPrompt()
|
this.$refs.timedMuteDialog.optionallyPrompt()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
import { mapState as mapVuexState } from 'vuex'
|
||||||
|
|
||||||
import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue'
|
import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue'
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
|
@ -14,6 +14,7 @@ import { useChatsStore } from 'src/stores/chats.js'
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -110,29 +111,29 @@ const NavPanel = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapPiniaState(useAnnouncementsStore, {
|
...mapState(useAnnouncementsStore, {
|
||||||
unreadAnnouncementCount: 'unreadAnnouncementCount',
|
unreadAnnouncementCount: 'unreadAnnouncementCount',
|
||||||
supportsAnnouncements: (store) => store.supportsAnnouncements,
|
supportsAnnouncements: (store) => store.supportsAnnouncements,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useInstanceCapabilitiesStore, [
|
...mapState(useInstanceCapabilitiesStore, [
|
||||||
'pleromaChatMessagesAvailable',
|
'pleromaChatMessagesAvailable',
|
||||||
'pleromaBookmarkFoldersAvailable',
|
'pleromaBookmarkFoldersAvailable',
|
||||||
'localBubble',
|
'localBubble',
|
||||||
]),
|
]),
|
||||||
...mapPiniaState(useInstanceStore, ['federating']),
|
...mapState(useInstanceStore, ['federating']),
|
||||||
...mapPiniaState(useInstanceStore, {
|
...mapState(useInstanceStore, {
|
||||||
privateMode: (store) => store.private,
|
privateMode: (store) => store.private,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useSyncConfigStore, {
|
...mapState(useSyncConfigStore, {
|
||||||
collapsed: (store) => store.prefsStorage.simple.collapseNav,
|
collapsed: (store) => store.prefsStorage.simple.collapseNav,
|
||||||
pinnedItems: (store) =>
|
pinnedItems: (store) =>
|
||||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||||
}),
|
}),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
...mapVuexState({
|
||||||
followRequestCount: (state) => state.api.followRequests.length,
|
followRequestCount: (state) => state.api.followRequests.length,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useChatsStore, ['unreadChatsCount']),
|
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||||
timelinesItems() {
|
timelinesItems() {
|
||||||
return filterNavigation(
|
return filterNavigation(
|
||||||
Object.entries({ ...TIMELINES })
|
Object.entries({ ...TIMELINES })
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { mapState as mapPiniaState, mapStores } from 'pinia'
|
import { mapState, mapStores } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import { routeTo } from 'src/components/navigation/navigation.js'
|
import { routeTo } from 'src/components/navigation/navigation.js'
|
||||||
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -44,10 +44,8 @@ const NavigationEntry = {
|
||||||
return this.$store.getters
|
return this.$store.getters
|
||||||
},
|
},
|
||||||
...mapStores(useAnnouncementsStore),
|
...mapStores(useAnnouncementsStore),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
...mapState(useSyncConfigStore, {
|
||||||
}),
|
|
||||||
...mapPiniaState(useSyncConfigStore, {
|
|
||||||
pinnedItems: (store) =>
|
pinnedItems: (store) =>
|
||||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
import { mapState as mapVuexState } from 'vuex'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
filterNavigation,
|
filterNavigation,
|
||||||
|
|
@ -18,6 +18,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useListsStore } from 'src/stores/lists'
|
import { useListsStore } from 'src/stores/lists'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -58,26 +59,26 @@ const NavPanel = {
|
||||||
getters() {
|
getters() {
|
||||||
return this.$store.getters
|
return this.$store.getters
|
||||||
},
|
},
|
||||||
...mapPiniaState(useListsStore, {
|
...mapState(useListsStore, {
|
||||||
lists: getListEntries,
|
lists: getListEntries,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useAnnouncementsStore, {
|
...mapState(useAnnouncementsStore, {
|
||||||
supportsAnnouncements: (store) => store.supportsAnnouncements,
|
supportsAnnouncements: (store) => store.supportsAnnouncements,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useBookmarkFoldersStore, {
|
...mapState(useBookmarkFoldersStore, {
|
||||||
bookmarks: getBookmarkFolderEntries,
|
bookmarks: getBookmarkFolderEntries,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useSyncConfigStore, {
|
...mapState(useSyncConfigStore, {
|
||||||
pinnedItems: (store) =>
|
pinnedItems: (store) =>
|
||||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useInstanceStore, ['privateMode', 'federating']),
|
...mapState(useInstanceStore, ['privateMode', 'federating']),
|
||||||
...mapPiniaState(useInstanceCapabilitiesStore, [
|
...mapState(useInstanceCapabilitiesStore, [
|
||||||
'pleromaChatMessagesAvailable',
|
'pleromaChatMessagesAvailable',
|
||||||
'localBubble',
|
'localBubble',
|
||||||
]),
|
]),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
...mapVuexState({
|
||||||
followRequestCount: (state) => state.api.followRequests.length,
|
followRequestCount: (state) => state.api.followRequests.length,
|
||||||
}),
|
}),
|
||||||
pinnedList() {
|
pinnedList() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
import { mapState } from 'pinia'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Report from 'src/components/report/report.vue'
|
import Report from 'src/components/report/report.vue'
|
||||||
import StatusContent from 'src/components/status_content/status_content.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 UserAvatar from 'src/components/user_avatar/user_avatar.vue'
|
||||||
import UserLink from 'src/components/user_link/user_link.vue'
|
import UserLink from 'src/components/user_link/user_link.vue'
|
||||||
import UserPopover from 'src/components/user_popover/user_popover.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 {
|
import {
|
||||||
highlightClass,
|
highlightClass,
|
||||||
highlightStyle,
|
highlightStyle,
|
||||||
|
|
@ -15,8 +15,11 @@ import {
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_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 { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.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 { approveUser, denyUser } from 'src/api/user.js'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
|
|
@ -115,9 +118,6 @@ const Notification = {
|
||||||
useInstanceStore().restrictedNicknames,
|
useInstanceStore().restrictedNicknames,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
getUser(notification) {
|
|
||||||
return this.$store.state.users.usersObject[notification.from_profile.id]
|
|
||||||
},
|
|
||||||
interacted() {
|
interacted() {
|
||||||
this.$emit('interacted')
|
this.$emit('interacted')
|
||||||
},
|
},
|
||||||
|
|
@ -148,16 +148,9 @@ const Notification = {
|
||||||
id: this.user.id,
|
id: this.user.id,
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
|
// TODO Fix this
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
this.$store.dispatch('markSingleNotificationAsSeen', {
|
useNotificationsStore().markSingleNotificationAsSeen(this.notification.id)
|
||||||
id: this.notification.id,
|
|
||||||
})
|
|
||||||
this.$store.dispatch('updateNotification', {
|
|
||||||
id: this.notification.id,
|
|
||||||
updater: (notification) => {
|
|
||||||
notification.type = 'follow'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this.hideApproveConfirmDialog()
|
this.hideApproveConfirmDialog()
|
||||||
},
|
},
|
||||||
denyUser() {
|
denyUser() {
|
||||||
|
|
@ -172,15 +165,20 @@ const Notification = {
|
||||||
id: this.user.id,
|
id: this.user.id,
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$store.dispatch('dismissNotificationLocal', {
|
useNotificationsStore().dismissNotificationLocal(this.notification.id)
|
||||||
id: this.notification.id,
|
// TODO Fix this
|
||||||
})
|
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
})
|
})
|
||||||
this.hideDenyConfirmDialog()
|
this.hideDenyConfirmDialog()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
status() {
|
||||||
|
// Used for StatusContent
|
||||||
|
if (this.notification.status) {
|
||||||
|
return useStatusesStore().allStatuses.get(this.notification.status.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
userClass() {
|
userClass() {
|
||||||
return highlightClass(this.notification.from_profile)
|
return highlightClass(this.notification.from_profile)
|
||||||
},
|
},
|
||||||
|
|
@ -194,19 +192,19 @@ const Notification = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
user() {
|
user() {
|
||||||
return this.$store.getters.findUser(this.notification.from_profile.id)
|
return useUsersStore().findUser(this.notification.from_profile.id)
|
||||||
},
|
},
|
||||||
userProfileLink() {
|
userProfileLink() {
|
||||||
return this.generateUserProfileLink(this.user)
|
return this.generateUserProfileLink(this.user)
|
||||||
},
|
},
|
||||||
targetUser() {
|
targetUser() {
|
||||||
return this.$store.getters.findUser(this.notification.target.id)
|
return useUsersStore().findUser(this.notification.target.id)
|
||||||
},
|
},
|
||||||
targetUserProfileLink() {
|
targetUserProfileLink() {
|
||||||
return this.generateUserProfileLink(this.targetUser)
|
return this.generateUserProfileLink(this.targetUser)
|
||||||
},
|
},
|
||||||
needMute() {
|
needMute() {
|
||||||
return this.$store.getters.relationship(this.user.id).muting
|
return useUsersStore().relationship(this.user.id).muting
|
||||||
},
|
},
|
||||||
isStatusNotification() {
|
isStatusNotification() {
|
||||||
return isStatusNotification(this.notification.type)
|
return isStatusNotification(this.notification.type)
|
||||||
|
|
@ -229,9 +227,7 @@ const Notification = {
|
||||||
shouldConfirmDeny() {
|
shouldConfirmDeny() {
|
||||||
return this.mergedConfig.modalOnDenyFollow
|
return this.mergedConfig.modalOnDenyFollow
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
currentUser: (state) => state.users.currentUser,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<Status
|
<Status
|
||||||
class="Notification panel-body"
|
class="Notification panel-body"
|
||||||
:compact="true"
|
:compact="true"
|
||||||
:statusoid="notification.status"
|
:status-id="notification.status?.id"
|
||||||
@click="interacted"
|
@click="interacted"
|
||||||
/>
|
/>
|
||||||
</article>
|
</article>
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
class="Notification container -muted"
|
class="Notification container -muted"
|
||||||
>
|
>
|
||||||
<small>
|
<small>
|
||||||
<user-link
|
<UserLink
|
||||||
:user="notification.from_profile"
|
:user="notification.from_profile"
|
||||||
:at="false"
|
:at="false"
|
||||||
/>
|
/>
|
||||||
|
|
@ -57,7 +57,7 @@
|
||||||
<UserAvatar
|
<UserAvatar
|
||||||
class="post-avatar"
|
class="post-avatar"
|
||||||
:compact="true"
|
:compact="true"
|
||||||
:user="notification.from_profile"
|
:user-id="notification.from_profile.id"
|
||||||
/>
|
/>
|
||||||
</UserPopover>
|
</UserPopover>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -215,7 +215,7 @@
|
||||||
v-if="notification.type === 'follow' || notification.type === 'follow_request'"
|
v-if="notification.type === 'follow' || notification.type === 'follow_request'"
|
||||||
class="follow-text"
|
class="follow-text"
|
||||||
>
|
>
|
||||||
<user-link
|
<UserLink
|
||||||
class="follow-name"
|
class="follow-name"
|
||||||
:user="notification.from_profile"
|
:user="notification.from_profile"
|
||||||
/>
|
/>
|
||||||
|
|
@ -249,7 +249,7 @@
|
||||||
v-else-if="notification.type === 'move'"
|
v-else-if="notification.type === 'move'"
|
||||||
class="move-text"
|
class="move-text"
|
||||||
>
|
>
|
||||||
<user-link
|
<UserLink
|
||||||
:user="notification.target"
|
:user="notification.target"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -261,7 +261,7 @@
|
||||||
<StatusContent
|
<StatusContent
|
||||||
class="status-content"
|
class="status-content"
|
||||||
:compact="!statusExpanded"
|
:compact="!statusExpanded"
|
||||||
:status="notification.status"
|
:status="status"
|
||||||
:collapse="!statusExpanded"
|
:collapse="!statusExpanded"
|
||||||
@click="onContentClick"
|
@click="onContentClick"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,16 @@ import FaviconService from '../../services/favicon_service/favicon_service.js'
|
||||||
import {
|
import {
|
||||||
ACTIONABLE_NOTIFICATION_TYPES,
|
ACTIONABLE_NOTIFICATION_TYPES,
|
||||||
countExtraNotifications,
|
countExtraNotifications,
|
||||||
filteredNotificationsFromStore,
|
filteredNotifications,
|
||||||
notificationsFromStore,
|
unseenNotifications,
|
||||||
unseenNotificationsFromStore,
|
|
||||||
} from '../../services/notification_utils/notification_utils.js'
|
} from '../../services/notification_utils/notification_utils.js'
|
||||||
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
|
|
||||||
import NotificationFilters from './notification_filters.vue'
|
import NotificationFilters from './notification_filters.vue'
|
||||||
|
|
||||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||||
import { useChatsStore } from 'src/stores/chats.js'
|
import { useChatsStore } from 'src/stores/chats.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -53,7 +52,6 @@ const Notifications = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showScrollTop: false,
|
showScrollTop: false,
|
||||||
bottomedOut: false,
|
|
||||||
// How many seen notifications to display in the list. The more there are,
|
// How many seen notifications to display in the list. The more there are,
|
||||||
// the heavier the page becomes. This count is increased when loading
|
// the heavier the page becomes. This count is increased when loading
|
||||||
// older notifications, and cut back to default whenever hitting "Read!".
|
// older notifications, and cut back to default whenever hitting "Read!".
|
||||||
|
|
@ -70,14 +68,13 @@ const Notifications = {
|
||||||
return this.minimalMode ? '' : 'panel panel-default'
|
return this.minimalMode ? '' : 'panel panel-default'
|
||||||
},
|
},
|
||||||
notifications() {
|
notifications() {
|
||||||
return notificationsFromStore(this.$store)
|
return useNotificationsStore().data
|
||||||
},
|
},
|
||||||
error() {
|
error() {
|
||||||
return this.$store.state.notifications.error
|
return useNotificationsStore().error
|
||||||
},
|
},
|
||||||
unseenNotifications() {
|
unseenNotifications() {
|
||||||
return unseenNotificationsFromStore(
|
return unseenNotifications(
|
||||||
this.$store,
|
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
useMergedConfigStore().mergedConfig.ignoreInactionableSeen,
|
||||||
)
|
)
|
||||||
|
|
@ -85,18 +82,15 @@ const Notifications = {
|
||||||
filteredNotifications() {
|
filteredNotifications() {
|
||||||
if (this.unseenAtTop) {
|
if (this.unseenAtTop) {
|
||||||
return [
|
return [
|
||||||
...filteredNotificationsFromStore(
|
...filteredNotifications(
|
||||||
this.$store,
|
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
).filter((n) => this.shouldShowUnseen(n)),
|
).filter((n) => this.shouldShowUnseen(n)),
|
||||||
...filteredNotificationsFromStore(
|
...filteredNotifications(
|
||||||
this.$store,
|
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
).filter((n) => !this.shouldShowUnseen(n)),
|
).filter((n) => !this.shouldShowUnseen(n)),
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
return filteredNotificationsFromStore(
|
return filteredNotifications(
|
||||||
this.$store,
|
|
||||||
useMergedConfigStore().mergedConfig.notificationVisibility,
|
useMergedConfigStore().mergedConfig.notificationVisibility,
|
||||||
this.filterMode,
|
this.filterMode,
|
||||||
)
|
)
|
||||||
|
|
@ -127,7 +121,10 @@ const Notifications = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
loading() {
|
loading() {
|
||||||
return this.$store.state.notifications.loading
|
return useNotificationsStore().fetcher.loading
|
||||||
|
},
|
||||||
|
bottomedOut() {
|
||||||
|
return useNotificationsStore().fetcher.bottomedOut
|
||||||
},
|
},
|
||||||
noHeading() {
|
noHeading() {
|
||||||
const { layoutType } = useInterfaceStore()
|
const { layoutType } = useInterfaceStore()
|
||||||
|
|
@ -224,14 +221,14 @@ const Notifications = {
|
||||||
*/
|
*/
|
||||||
notificationClicked(notification) {
|
notificationClicked(notification) {
|
||||||
const { id } = notification
|
const { id } = notification
|
||||||
this.$store.dispatch('notificationClicked', { id })
|
useNotificationsStore().notificationClicked(id)
|
||||||
},
|
},
|
||||||
notificationInteracted(notification) {
|
notificationInteracted(notification) {
|
||||||
const { id } = notification
|
const { id } = notification
|
||||||
this.$store.dispatch('markSingleNotificationAsSeen', { id })
|
useNotificationsStore().markSingleNotificationAsSeen(id)
|
||||||
},
|
},
|
||||||
markAsSeen() {
|
markAsSeen() {
|
||||||
this.$store.dispatch('markNotificationsAsSeen')
|
useNotificationsStore().markNotificationsAsSeen()
|
||||||
this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT
|
this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT
|
||||||
},
|
},
|
||||||
fetchOlderNotifications() {
|
fetchOlderNotifications() {
|
||||||
|
|
@ -250,22 +247,7 @@ const Notifications = {
|
||||||
this.seenToDisplayCount = seenCount
|
this.seenToDisplayCount = seenCount
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = this.$store
|
useNotificationsStore().fetcher.fetchOlder()
|
||||||
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
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { getToken } from 'src/api/oauth.js'
|
import { getToken } from 'src/api/oauth.js'
|
||||||
|
|
||||||
|
|
@ -15,9 +16,10 @@ const oac = {
|
||||||
clientSecret,
|
clientSecret,
|
||||||
instance: useInstanceStore().server,
|
instance: useInstanceStore().server,
|
||||||
code: this.code,
|
code: this.code,
|
||||||
}).then(({ data: result }) => {
|
}).then(async ({ data: result }) => {
|
||||||
oauthStore.setToken(result.access_token)
|
oauthStore.setToken(result.access_token)
|
||||||
this.$store.dispatch('loginUser', result.access_token)
|
|
||||||
|
await useUsersStore().loginUser(result.access_token)
|
||||||
this.$router.push({ name: 'friends' })
|
this.$router.push({ name: 'friends' })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { resetPassword } from 'src/api/public.js'
|
import { resetPassword } from 'src/api/public.js'
|
||||||
|
|
||||||
|
|
@ -21,13 +21,11 @@ const passwordReset = {
|
||||||
error: null,
|
error: null,
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useUsersStore, ['loggedIn']),
|
||||||
signedIn: (state) => !!state.users.currentUser,
|
...mapState(useInstanceStore, ['mailerEnabled']),
|
||||||
}),
|
|
||||||
...mapPiniaState(useInstanceStore, ['mailerEnabled']),
|
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.signedIn) {
|
if (this.loggedIn) {
|
||||||
this.$router.push({ name: 'root' })
|
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 { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { usePollsStore } from 'src/stores/polls.js'
|
import { usePollsStore } from 'src/stores/polls.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Poll',
|
name: 'Poll',
|
||||||
|
|
@ -64,7 +65,7 @@ export default {
|
||||||
return useMergedConfigStore().mergedConfig.scaleMfm
|
return useMergedConfigStore().mergedConfig.scaleMfm
|
||||||
},
|
},
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
return this.$store.state.users.currentUser
|
return useUsersStore().currentUser
|
||||||
},
|
},
|
||||||
showResults() {
|
showResults() {
|
||||||
return this.poll.voted || this.expired || !this.loggedIn
|
return this.poll.voted || this.expired || !this.loggedIn
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
import { useMediaViewerStore } from 'src/stores/media_viewer.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_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'
|
import { pollFormToMasto } from 'src/services/poll/poll.service.js'
|
||||||
|
|
||||||
|
|
@ -570,9 +571,7 @@ const PostStatusForm = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Global stuff
|
// Global stuff
|
||||||
currentUser() {
|
...mapState(useUsersStore, ['currentUser']),
|
||||||
return this.$store.state.users.currentUser
|
|
||||||
},
|
|
||||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
...mapState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
mobileLayout: (store) => store.mobileLayout,
|
mobileLayout: (store) => store.mobileLayout,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash'
|
||||||
|
import { mapState } from 'pinia'
|
||||||
|
|
||||||
import Modal from 'src/components/modal/modal.vue'
|
import Modal from 'src/components/modal/modal.vue'
|
||||||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||||
|
|
||||||
import { usePostStatusStore } from 'src/stores/post_status.js'
|
import { usePostStatusStore } from 'src/stores/post_status.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const PostStatusModal = {
|
const PostStatusModal = {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -16,18 +18,16 @@ const PostStatusModal = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isLoggedIn() {
|
|
||||||
return !!this.$store.state.users.currentUser
|
|
||||||
},
|
|
||||||
modalActivated() {
|
modalActivated() {
|
||||||
return usePostStatusStore().modalActivated
|
return usePostStatusStore().modalActivated
|
||||||
},
|
},
|
||||||
isFormVisible() {
|
isFormVisible() {
|
||||||
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
return this.loggedIn && !this.resettingForm && this.modalActivated
|
||||||
},
|
},
|
||||||
params() {
|
params() {
|
||||||
return usePostStatusStore().params || {}
|
return usePostStatusStore().params || {}
|
||||||
},
|
},
|
||||||
|
...mapState(useUsersStore, ['loggedIn']),
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
params(newVal, oldVal) {
|
params(newVal, oldVal) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal
|
||||||
v-if="isLoggedIn && !resettingForm"
|
v-if="loggedIn && !resettingForm"
|
||||||
:is-open="modalActivated"
|
:is-open="modalActivated"
|
||||||
class="post-form-modal-view"
|
class="post-form-modal-view"
|
||||||
@backdrop-clicked="closeModal"
|
@backdrop-clicked="closeModal"
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -6,6 +6,8 @@ import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
|
import { useTimelinesStore } from 'src/stores/timelines.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
@ -26,13 +28,14 @@ const QuickFilterSettings = {
|
||||||
path: 'replyVisibility',
|
path: 'replyVisibility',
|
||||||
value: visibility,
|
value: visibility,
|
||||||
})
|
})
|
||||||
this.$store.dispatch('queueFlushAll')
|
useTimelinesStore().requireReloadAll()
|
||||||
},
|
},
|
||||||
openTab(tab) {
|
openTab(tab) {
|
||||||
useInterfaceStore().openSettingsModalTab(tab)
|
useInterfaceStore().openSettingsModalTab(tab)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapState(useUsersStore, ['loggedIn']),
|
||||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
...mapState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
mobileLayout: (state) => state.layoutType === 'mobile',
|
mobileLayout: (state) => state.layoutType === 'mobile',
|
||||||
|
|
@ -53,9 +56,6 @@ const QuickFilterSettings = {
|
||||||
return 'dropdown-item'
|
return 'dropdown-item'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loggedIn() {
|
|
||||||
return !!this.$store.state.users.currentUser
|
|
||||||
},
|
|
||||||
replyVisibilitySelf: {
|
replyVisibilitySelf: {
|
||||||
get() {
|
get() {
|
||||||
return this.mergedConfig.replyVisibility === 'self'
|
return this.mergedConfig.replyVisibility === 'self'
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filt
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_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 { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -35,9 +36,7 @@ const QuickViewSettings = {
|
||||||
...mapState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
mobileLayout: (state) => state.layoutType === 'mobile',
|
mobileLayout: (state) => state.layoutType === 'mobile',
|
||||||
}),
|
}),
|
||||||
loggedIn() {
|
...mapState(useUsersStore, ['loggedIn']),
|
||||||
return !!this.$store.state.users.currentUser
|
|
||||||
},
|
|
||||||
conversationDisplay: {
|
conversationDisplay: {
|
||||||
get() {
|
get() {
|
||||||
return this.mergedConfig.conversationDisplay
|
return this.mergedConfig.conversationDisplay
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -45,7 +47,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
quotedStatus() {
|
quotedStatus() {
|
||||||
return this.statusId
|
return this.statusId
|
||||||
? this.$store.state.statuses.allStatusesObject[this.statusId]
|
? useStatusesStore().allStatuses.get(this.statusId)
|
||||||
: undefined
|
: undefined
|
||||||
},
|
},
|
||||||
shouldDisplayQuote() {
|
shouldDisplayQuote() {
|
||||||
|
|
@ -79,8 +81,8 @@ export default {
|
||||||
this.fetchAttempted = true
|
this.fetchAttempted = true
|
||||||
this.fetching = true
|
this.fetching = true
|
||||||
this.$emit('loading', true)
|
this.$emit('loading', true)
|
||||||
this.$store
|
useStatusesStore()
|
||||||
.dispatch('fetchStatus', this.statusId)
|
.fetchStatus(this.statusId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.displayQuote = true
|
this.displayQuote = true
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import Quote from './quote.vue'
|
import Quote from './quote.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
import { useSearchStore } from 'src/stores/search.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -93,8 +94,8 @@ export default {
|
||||||
this.$emit('update:id', notice[3])
|
this.$emit('update:id', notice[3])
|
||||||
} else if (value) {
|
} else if (value) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.$store
|
useSearchStore()
|
||||||
.dispatch('search', {
|
.search({
|
||||||
q: value,
|
q: value,
|
||||||
resolve: true,
|
resolve: true,
|
||||||
offset: 0,
|
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 useVuelidate from '@vuelidate/core'
|
||||||
import { required, requiredIf, sameAs } from '@vuelidate/validators'
|
import { required, requiredIf, sameAs } from '@vuelidate/validators'
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapActions, mapState } from 'pinia'
|
||||||
import { mapActions, mapState } from 'vuex'
|
|
||||||
|
|
||||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
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 TermsOfServicePanel from 'src/components/terms_of_service_panel/terms_of_service_panel.vue'
|
||||||
import localeService from '../../services/locale/locale.service.js'
|
import localeService from '../../services/locale/locale.service.js'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.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'
|
import { DAY } from 'src/services/date_utils/date_utils.js'
|
||||||
|
|
||||||
const registration = {
|
const registration = {
|
||||||
|
|
@ -26,6 +28,9 @@ const registration = {
|
||||||
reason: '',
|
reason: '',
|
||||||
language: [''],
|
language: [''],
|
||||||
},
|
},
|
||||||
|
signUpPending: false,
|
||||||
|
signUpErrors: [],
|
||||||
|
signUpNotice: {},
|
||||||
captcha: {},
|
captcha: {},
|
||||||
}),
|
}),
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -58,7 +63,7 @@ const registration = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if ((!this.registrationOpen && !this.token) || this.signedIn) {
|
if ((!this.registrationOpen && !this.token) || this.loggedIn) {
|
||||||
this.$router.push({ name: 'root' })
|
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,
|
registrationOpen: (store) => store.registrationOpen,
|
||||||
embeddedToS: (store) => store.embeddedToS,
|
embeddedToS: (store) => store.embeddedToS,
|
||||||
termsOfService: (store) => store.tos,
|
termsOfService: (store) => store.tos,
|
||||||
|
|
@ -109,16 +117,49 @@ const registration = {
|
||||||
birthdayRequired: (store) => store.birthdayRequired,
|
birthdayRequired: (store) => store.birthdayRequired,
|
||||||
birthdayMinAge: (store) => store.birthdayMinAge,
|
birthdayMinAge: (store) => store.birthdayMinAge,
|
||||||
}),
|
}),
|
||||||
...mapState({
|
...mapState(useUsersStore, ['loggedIn']),
|
||||||
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,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
async submit() {
|
||||||
this.user.nickname = this.user.username
|
this.user.nickname = this.user.username
|
||||||
this.user.token = this.token
|
this.user.token = this.token
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<input
|
<input
|
||||||
id="sign-up-username"
|
id="sign-up-username"
|
||||||
v-model.trim="v$.user.username.$model"
|
v-model.trim="v$.user.username.$model"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
:aria-required="true"
|
:aria-required="true"
|
||||||
:placeholder="$t('registration.username_placeholder')"
|
:placeholder="$t('registration.username_placeholder')"
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
<input
|
<input
|
||||||
id="sign-up-fullname"
|
id="sign-up-fullname"
|
||||||
v-model.trim="v$.user.fullname.$model"
|
v-model.trim="v$.user.fullname.$model"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
:aria-required="true"
|
:aria-required="true"
|
||||||
:placeholder="$t('registration.fullname_placeholder')"
|
:placeholder="$t('registration.fullname_placeholder')"
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
v-model="v$.user.email.$model"
|
v-model="v$.user.email.$model"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
type="email"
|
type="email"
|
||||||
:aria-required="accountActivationRequired"
|
:aria-required="accountActivationRequired"
|
||||||
|
|
@ -109,7 +109,7 @@
|
||||||
<textarea
|
<textarea
|
||||||
id="bio"
|
id="bio"
|
||||||
v-model="user.bio"
|
v-model="user.bio"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
:placeholder="bioPlaceholder"
|
:placeholder="bioPlaceholder"
|
||||||
/>
|
/>
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
<input
|
<input
|
||||||
id="sign-up-password"
|
id="sign-up-password"
|
||||||
v-model="user.password"
|
v-model="user.password"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
type="password"
|
type="password"
|
||||||
:aria-required="true"
|
:aria-required="true"
|
||||||
|
|
@ -154,7 +154,7 @@
|
||||||
<input
|
<input
|
||||||
id="sign-up-password-confirmation"
|
id="sign-up-password-confirmation"
|
||||||
v-model="user.confirm"
|
v-model="user.confirm"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
type="password"
|
type="password"
|
||||||
:aria-required="true"
|
:aria-required="true"
|
||||||
|
|
@ -187,7 +187,7 @@
|
||||||
<input
|
<input
|
||||||
id="sign-up-birthday"
|
id="sign-up-birthday"
|
||||||
v-model="user.birthday"
|
v-model="user.birthday"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
type="date"
|
type="date"
|
||||||
:max="birthdayRequired ? birthdayMinAttr : undefined"
|
:max="birthdayRequired ? birthdayMinAttr : undefined"
|
||||||
|
|
@ -232,7 +232,7 @@
|
||||||
<textarea
|
<textarea
|
||||||
id="reason"
|
id="reason"
|
||||||
v-model="user.reason"
|
v-model="user.reason"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
:placeholder="reasonPlaceholder"
|
:placeholder="reasonPlaceholder"
|
||||||
/>
|
/>
|
||||||
|
|
@ -259,7 +259,7 @@
|
||||||
<input
|
<input
|
||||||
id="captcha-answer"
|
id="captcha-answer"
|
||||||
v-model="captcha.solution"
|
v-model="captcha.solution"
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
class="input form-control"
|
class="input form-control"
|
||||||
type="text"
|
type="text"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
|
@ -285,7 +285,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button
|
<button
|
||||||
:disabled="isPending"
|
:disabled="signUpPending"
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
>
|
>
|
||||||
|
|
@ -303,12 +303,12 @@
|
||||||
<!-- eslint-enable vue/no-v-html -->
|
<!-- eslint-enable vue/no-v-html -->
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="serverValidationErrors.length"
|
v-if="signUpErrors.length"
|
||||||
class="form-group"
|
class="form-group"
|
||||||
>
|
>
|
||||||
<div class="alert error">
|
<div class="alert error">
|
||||||
<span
|
<span
|
||||||
v-for="error in serverValidationErrors"
|
v-for="error in signUpErrors"
|
||||||
:key="error"
|
:key="error"
|
||||||
>{{ error }}</span>
|
>{{ error }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { fetchUser } from 'src/api/public.js'
|
import { fetchUser } from 'src/api/public.js'
|
||||||
|
|
||||||
|
|
@ -16,17 +17,14 @@ const RemoteUserResolver = {
|
||||||
id,
|
id,
|
||||||
credentials: useOAuthStore().token,
|
credentials: useOAuthStore().token,
|
||||||
})
|
})
|
||||||
.then(({ data: externalUser }) => {
|
.then((result) => {
|
||||||
if (externalUser.error) {
|
const { data: externalUser } = result
|
||||||
this.error = true
|
useUsersStore().addNewUsers(result)
|
||||||
} else {
|
const id = externalUser.id
|
||||||
this.$store.commit('addNewUsers', [externalUser])
|
this.$router.replace({
|
||||||
const id = externalUser.id
|
name: 'external-user-profile',
|
||||||
this.$router.replace({
|
params: { id },
|
||||||
name: 'external-user-profile',
|
})
|
||||||
params: { id },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.error = true
|
this.error = true
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['user', 'relationship'],
|
props: ['user', 'relationship'],
|
||||||
|
|
@ -43,9 +44,9 @@ export default {
|
||||||
},
|
},
|
||||||
doRemoveUserFromFollowers() {
|
doRemoveUserFromFollowers() {
|
||||||
this.inProgress = true
|
this.inProgress = true
|
||||||
this.$store
|
useUsersStore()
|
||||||
.dispatch('removeUserFromFollowers', this.relationship.id)
|
.removeUserFromFollowers(this.relationship.id)
|
||||||
.then(() => {
|
.finally(() => {
|
||||||
this.inProgress = false
|
this.inProgress = false
|
||||||
})
|
})
|
||||||
this.hideConfirmRemoveUserFromFollowers()
|
this.hideConfirmRemoveUserFromFollowers()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@ import Conversation from 'src/components/conversation/conversation.vue'
|
||||||
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
|
|
||||||
|
import { useSearchStore } from 'src/stores/search.js'
|
||||||
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCircleNotch, faSearch } from '@fortawesome/free-solid-svg-icons'
|
import { faCircleNotch, faSearch } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
|
@ -34,14 +38,14 @@ const Search = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
users() {
|
users() {
|
||||||
return this.userIds.map((userId) => this.$store.getters.findUser(userId))
|
return this.userIds.map((userId) => useUsersStore().findUser(userId))
|
||||||
},
|
},
|
||||||
visibleStatuses() {
|
visibleStatuses() {
|
||||||
const allStatusesObject = this.$store.state.statuses.allStatusesObject
|
const allStatuses = useStatusesStore().allStatuses
|
||||||
|
|
||||||
return this.statuses.filter(
|
return this.statuses.filter(
|
||||||
(status) =>
|
(status) =>
|
||||||
allStatusesObject[status.id] && !allStatusesObject[status.id].deleted,
|
allStatuses.has(status.id) && !allStatuses.get(status.id).deleted,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -76,8 +80,8 @@ const Search = {
|
||||||
this.lastStatusFetchCount = 0
|
this.lastStatusFetchCount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store
|
useSearchStore()
|
||||||
.dispatch('search', {
|
.search({
|
||||||
q: query,
|
q: query,
|
||||||
resolve: true,
|
resolve: true,
|
||||||
offset: this.statusesOffset,
|
offset: this.statusesOffset,
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
class="list-item hashtag"
|
class="list-item hashtag"
|
||||||
:to="{ name: 'tag-timeline', params: { tag: hashtag.name } }"
|
:to="{ name: 'tag-timeline', params: { id: hashtag.name } }"
|
||||||
>
|
>
|
||||||
<span class="name">
|
<span class="name">
|
||||||
#{{ hashtag.name }}
|
#{{ hashtag.name }}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||||
import ModerationTools from 'src/components/moderation_tools/moderation_tools.vue'
|
import ModerationTools from 'src/components/moderation_tools/moderation_tools.vue'
|
||||||
|
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const AdminUserCard = {
|
const AdminUserCard = {
|
||||||
props: {
|
props: {
|
||||||
userId: {
|
userId: {
|
||||||
|
|
@ -13,7 +15,7 @@ const AdminUserCard = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return this.$store.getters.findUser(this.userId)
|
return useUsersStore().findUser(this.userId)
|
||||||
},
|
},
|
||||||
isAdmin() {
|
isAdmin() {
|
||||||
return this.user.rights.admin
|
return this.user.rights.admin
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
const SharedComputedObject = () => ({
|
const SharedComputedObject = () => ({
|
||||||
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
|
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||||
...mapPiniaState(useMergedConfigStore, {
|
...mapState(useMergedConfigStore, {
|
||||||
expertLevel: (store) => store.mergedConfig.expertLevel,
|
expertLevel: (store) => store.mergedConfig.expertLevel,
|
||||||
}),
|
}),
|
||||||
...mapPiniaState(useAdminSettingsStore, {
|
...mapState(useAdminSettingsStore, {
|
||||||
adminConfig: (store) => store.config,
|
adminConfig: (store) => store.config,
|
||||||
adminDraft: (store) => store.draft,
|
adminDraft: (store) => store.draft,
|
||||||
}),
|
}),
|
||||||
...mapState({
|
...mapState(useUsersStore, {
|
||||||
user: (state) => state.users.currentUser,
|
user: (store) => store.currentUser,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// eslint-disable-next-line no-unused
|
// eslint-disable-next-line no-unused
|
||||||
|
|
||||||
import { mapState as mapPiniaState } from 'pinia'
|
import { mapState } from 'pinia'
|
||||||
import { Fragment } from 'vue'
|
import { Fragment } from 'vue'
|
||||||
|
|
||||||
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
||||||
|
|
@ -60,7 +60,7 @@ export default {
|
||||||
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...mapPiniaState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import VerticalTabSwitcher from './helpers/vertical_tab_switcher.jsx'
|
||||||
|
|
||||||
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -97,10 +98,7 @@ const SettingsModalAdminContent = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user() {
|
user() {
|
||||||
return this.$store.state.users.currentUser
|
return useUsersStore().currentUser
|
||||||
},
|
|
||||||
isLoggedIn() {
|
|
||||||
return !!this.$store.state.users.currentUser
|
|
||||||
},
|
},
|
||||||
open() {
|
open() {
|
||||||
return useInterfaceStore().settingsModalState !== 'hidden'
|
return useInterfaceStore().settingsModalState !== 'hidden'
|
||||||
|
|
|
||||||
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