Merge pull request 'Migrate rest of modules to pinia, remove vuex, replace lodash with lodash-es' (#3571) from vuex-removal into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma-fe/pulls/3571
This commit is contained in:
commit
7bc4fd7be7
128 changed files with 1980 additions and 1662 deletions
2
.babelrc
2
.babelrc
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-jsx"],
|
||||
"plugins": ["@babel/plugin-transform-runtime", "@vue/babel-plugin-jsx"],
|
||||
"comments": true
|
||||
}
|
||||
|
|
|
|||
1
changelog.d/drafts.fix
Normal file
1
changelog.d/drafts.fix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fixed drafts creating duplicates on edit instead of updating
|
||||
|
|
@ -81,7 +81,7 @@ In 99% cases PleromaFE uses [MastoAPI](https://docs.joinmastodon.org/api/) with
|
|||
|
||||
PleromaFE supports both formats by transforming them into internal format which is basically QvitterAPI one with some additions and renaming. All data is passed trough [Entity Normalizer](https://git.pleroma.social/pleroma/pleroma-fe/src/src/services/entity_normalizer/entity_normalizer.service.js) which can serve as a reference of API and what's actually used, it's also a host for all the hacks and data transformation.
|
||||
|
||||
For most part, PleromaFE tries to store all the info it can get in global vuex store - every user and post are passed trough updating mechanism where data is either added or merged with existing data, reactively updating the information throughout UI, so if in newest request user's post counter increased, it will be instantly updated in open user profile cards. This is also used to find users, posts and sometimes to build timelines and/or request parameters.
|
||||
For most part, PleromaFE tries to store all the info it can get in global pinia store - every user and post are passed trough updating mechanism where data is either added or merged with existing data, reactively updating the information throughout UI, so if in newest request user's post counter increased, it will be instantly updated in open user profile cards. This is also used to find users, posts and sometimes to build timelines and/or request parameters.
|
||||
|
||||
PleromaFE also tries to persist this store, however only stable data is stored, such as user authentication and preferences, user highlights. Persistence is performed by saving and loading chunk of vuex store in browser's LocalStorage/IndexedDB.
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
"hash-sum": "^2.0.0",
|
||||
"js-cookie": "3.0.5",
|
||||
"localforage": "1.10.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"parse-link-header": "2.0.0",
|
||||
"phoenix": "1.8.1",
|
||||
"pinia": "^3.0.4",
|
||||
|
|
@ -54,8 +55,7 @@
|
|||
"vue": "3.5.22",
|
||||
"vue-i18n": "11",
|
||||
"vue-router": "4.6.4",
|
||||
"vue-virtual-scroller": "^2.0.0-beta.7",
|
||||
"vuex": "4.1.0"
|
||||
"vue-virtual-scroller": "^2.0.0-beta.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.28.5",
|
||||
|
|
@ -78,7 +78,6 @@
|
|||
"@vue/compiler-sfc": "3.5.22",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"autoprefixer": "10.4.21",
|
||||
"babel-plugin-lodash": "3.3.4",
|
||||
"chai": "5.3.3",
|
||||
"chalk": "5.6.2",
|
||||
"chromedriver": "135.0.4",
|
||||
|
|
@ -97,7 +96,6 @@
|
|||
"function-bind": "1.1.2",
|
||||
"http-proxy-middleware": "3.0.5",
|
||||
"iso-639-1": "3.1.5",
|
||||
"lodash": "4.17.21",
|
||||
"msw": "2.14.6",
|
||||
"nightwatch": "3.12.2",
|
||||
"oxc": "^1.0.1",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { throttle } from 'lodash'
|
||||
import { throttle } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
|
|
@ -49,6 +49,12 @@ export default {
|
|||
MobilePostStatusButton,
|
||||
MobileNav,
|
||||
DesktopNav,
|
||||
FollowRequestConfirm: defineAsyncComponent(
|
||||
() =>
|
||||
import(
|
||||
'src/components/follow_request_confirm/follow_request_confirm.vue'
|
||||
),
|
||||
),
|
||||
SettingsModal: defineAsyncComponent(
|
||||
() => import('src/components/settings_modal/settings_modal.vue'),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
<UpdateNotification />
|
||||
<GlobalError />
|
||||
<GlobalNoticeList />
|
||||
<FollowRequestConfirm v-if="currentUser" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { snakeCase } from 'lodash'
|
||||
import { snakeCase } from 'lodash-es'
|
||||
|
||||
import { StatusCodeError } from 'src/services/errors/errors'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { last } from 'lodash'
|
||||
import { last } from 'lodash-es'
|
||||
|
||||
import { paramsString, promisedRequest } from './helpers.js'
|
||||
import { fetchFriends, MASTODON_STATUS_URL } from './public.js'
|
||||
|
|
@ -17,7 +17,11 @@ const CHANGE_EMAIL_URL = '/api/pleroma/change_email'
|
|||
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||
const MOVE_ACCOUNT_URL = '/api/pleroma/move_account'
|
||||
const ALIASES_URL = '/api/pleroma/aliases'
|
||||
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
||||
const NOTIFICATION_SETTINGS_URL = ({
|
||||
blockFromStrangers,
|
||||
hideNotificationContents,
|
||||
}) =>
|
||||
`/api/pleroma/notification_settings${paramsString({ blockFromStrangers, hideNotificationContents })}`
|
||||
export const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
|
||||
|
||||
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
||||
|
|
@ -39,9 +43,10 @@ export const MASTODON_FOLLOW_URL = (id) => `/api/v1/accounts/${id}/follow`
|
|||
export const MASTODON_UNFOLLOW_URL = (id) => `/api/v1/accounts/${id}/unfollow`
|
||||
|
||||
const MASTODON_FOLLOW_REQUESTS_URL = '/api/v1/follow_requests'
|
||||
const MASTODON_APPROVE_USER_URL = (id) =>
|
||||
export const MASTODON_APPROVE_USER_URL = (id) =>
|
||||
`/api/v1/follow_requests/${id}/authorize`
|
||||
const MASTODON_DENY_USER_URL = (id) => `/api/v1/follow_requests/${id}/reject`
|
||||
export const MASTODON_DENY_USER_URL = (id) =>
|
||||
`/api/v1/follow_requests/${id}/reject`
|
||||
const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) =>
|
||||
`/api/v1/accounts/relationships/${paramsString({ id, withSuspended })}`
|
||||
export const MASTODON_USER_IN_LISTS = (id) => `/api/v1/accounts/${id}/lists`
|
||||
|
|
@ -432,10 +437,9 @@ export const exportFriends = ({ id, credentials }) => {
|
|||
// #Profile settings
|
||||
export const updateNotificationSettings = ({ credentials, settings }) => {
|
||||
return promisedRequest({
|
||||
url: NOTIFICATION_SETTINGS_URL,
|
||||
url: NOTIFICATION_SETTINGS_URL(settings),
|
||||
credentials,
|
||||
method: 'PUT',
|
||||
payload: settings,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { orderBy, uniqueId } from 'lodash'
|
||||
import { orderBy, uniqueId } from 'lodash-es'
|
||||
|
||||
import ChatMessage from 'src/components/chat_message/chat_message.vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ const chatNew = {
|
|||
|
||||
this.loading = true
|
||||
this.userIds = []
|
||||
this.$store
|
||||
useSearchStore()
|
||||
.search({ q: query, resolve: true, type: 'accounts' })
|
||||
.then((data) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get, maxBy, minBy, sortBy, throttle } from 'lodash'
|
||||
import { get, maxBy, minBy, sortBy, throttle } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
import { nextTick } from 'vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { throttle } from 'lodash'
|
||||
import { throttle } from 'lodash-es'
|
||||
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get, reduce } from 'lodash'
|
||||
import { get, reduce } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { cloneDeep } from 'lodash'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import Gallery from 'src/components/gallery/gallery.vue'
|
||||
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
|
||||
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useDraftsStore } from 'src/stores/drafts.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -33,7 +32,6 @@ const Draft = {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
referenceDraft: cloneDeep(this.draft),
|
||||
editing: false,
|
||||
showingConfirmDialog: false,
|
||||
}
|
||||
|
|
@ -50,14 +48,6 @@ const Draft = {
|
|||
return {}
|
||||
}
|
||||
},
|
||||
safeToSave() {
|
||||
return (
|
||||
this.draft.status ||
|
||||
this.draft.files?.length ||
|
||||
this.draft.hasPoll ||
|
||||
this.draft.hasQuote
|
||||
)
|
||||
},
|
||||
postStatusFormProps() {
|
||||
return {
|
||||
draftId: this.draft.id,
|
||||
|
|
@ -69,18 +59,12 @@ const Draft = {
|
|||
? useStatusesStore().allStatuses.get(this.draft.refId)
|
||||
: undefined
|
||||
},
|
||||
localCollapseSubjectDefault() {
|
||||
return useMergedConfigStore().mergedConfig.collapseMessageWithSubject
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editing(newVal) {
|
||||
if (newVal) return
|
||||
if (this.safeToSave) {
|
||||
this.$store.dispatch('addOrSaveDraft', { draft: this.draft })
|
||||
} else {
|
||||
this.$store.dispatch('addOrSaveDraft', { draft: this.referenceDraft })
|
||||
}
|
||||
// (Post|Edit)StatusForm handles draft saving
|
||||
this.$refs.form.saveDraft()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -91,9 +75,11 @@ const Draft = {
|
|||
this.showingConfirmDialog = true
|
||||
},
|
||||
doAbandon() {
|
||||
this.$store.dispatch('abandonDraft', { id: this.draft.id }).then(() => {
|
||||
this.hideConfirmDialog()
|
||||
})
|
||||
useDraftsStore()
|
||||
.abandonDraft(this.draft.id)
|
||||
.then(() => {
|
||||
this.hideConfirmDialog()
|
||||
})
|
||||
},
|
||||
hideConfirmDialog() {
|
||||
this.showingConfirmDialog = false
|
||||
|
|
|
|||
|
|
@ -67,11 +67,13 @@
|
|||
<div v-if="editing">
|
||||
<PostStatusForm
|
||||
v-if="draft.type !== 'edit'"
|
||||
ref="form"
|
||||
:hide-draft="true"
|
||||
v-bind="postStatusFormProps"
|
||||
/>
|
||||
<EditStatusForm
|
||||
v-else
|
||||
ref="form"
|
||||
:hide-draft="true"
|
||||
:params="postStatusFormProps"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import { defineAsyncComponent } from 'vue'
|
|||
import Draft from 'src/components/draft/draft.vue'
|
||||
import List from 'src/components/list/list.vue'
|
||||
|
||||
import { useDraftsStore } from 'src/stores/drafts.js'
|
||||
|
||||
const Drafts = {
|
||||
components: {
|
||||
Draft,
|
||||
|
|
@ -18,7 +20,7 @@ const Drafts = {
|
|||
},
|
||||
computed: {
|
||||
drafts() {
|
||||
return this.$store.getters.draftsArray
|
||||
return useDraftsStore().draftsArray
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -26,8 +28,8 @@ const Drafts = {
|
|||
this.showingConfirmDialog = true
|
||||
},
|
||||
doAbandonAll() {
|
||||
this.$store
|
||||
.dispatch('abandonAllDrafts')
|
||||
useDraftsStore()
|
||||
.abandonAllDrafts()
|
||||
.then(() => this.hideConfirmDialog())
|
||||
},
|
||||
hideConfirmDialog() {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ const EditStatusForm = {
|
|||
requestClose() {
|
||||
this.$refs.postStatusForm.requestClose()
|
||||
},
|
||||
saveDraft() {
|
||||
this.$refs.postStatusForm.saveDraft()
|
||||
},
|
||||
doEditStatus({ status, spoilerText, sensitive, media, contentType, poll }) {
|
||||
const params = {
|
||||
store: this.$store,
|
||||
statusId: this.params.statusId,
|
||||
status,
|
||||
spoilerText,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { take } from 'lodash'
|
||||
import { take } from 'lodash-es'
|
||||
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue'
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { useUsersStore } from 'src/stores/users.js'
|
|||
|
||||
export default (data) => {
|
||||
const emojiCurry = suggestEmoji(data.emoji)
|
||||
const usersCurry = data.store && suggestUsers(data.store)
|
||||
const usersCurry = suggestUsers()
|
||||
return (input, nameKeywordLocalizer) => {
|
||||
const firstChar = input[0]
|
||||
if (firstChar === ':' && data.emoji) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { chunk, debounce, trim } from 'lodash'
|
||||
import { chunk, debounce, trim } from 'lodash-es'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { mapState } from 'pinia'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
|
@ -30,14 +30,14 @@ const ExtraNotifications = {
|
|||
return (
|
||||
this.mergedConfig.showExtraNotifications &&
|
||||
this.mergedConfig.showAnnouncementsInExtraNotifications &&
|
||||
this.unreadAnnouncementCount
|
||||
this.unreadAnnouncementsCount
|
||||
)
|
||||
},
|
||||
shouldShowFollowRequests() {
|
||||
return (
|
||||
this.mergedConfig.showExtraNotifications &&
|
||||
this.mergedConfig.showFollowRequestsInExtraNotifications &&
|
||||
this.followRequestCount
|
||||
this.followRequestsCount
|
||||
)
|
||||
},
|
||||
hasAnythingToShow() {
|
||||
|
|
@ -55,12 +55,12 @@ const ExtraNotifications = {
|
|||
currentUser() {
|
||||
return useUsersStore().currentUser
|
||||
},
|
||||
...mapGetters(['followRequestCount']),
|
||||
...mapState(useAnnouncementsStore, {
|
||||
unreadAnnouncementCount: 'unreadAnnouncementCount',
|
||||
unreadAnnouncementsCount: 'unreadAnnouncementsCount',
|
||||
}),
|
||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||
...mapState(useFollowRequestsStore, ['followRequestsCount']),
|
||||
},
|
||||
methods: {
|
||||
openNotificationSettings() {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
class="fa-scale-110 icon"
|
||||
icon="bullhorn"
|
||||
/>
|
||||
{{ $t('notifications.unread_announcements', { num: unreadAnnouncementCount }, unreadAnnouncementCount) }}
|
||||
{{ $t('notifications.unread_announcements', { num: unreadAnnouncementsCount }, unreadAnnouncementsCount) }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
class="fa-scale-110 icon"
|
||||
icon="user-plus"
|
||||
/>
|
||||
{{ $t('notifications.unread_follow_requests', { num: followRequestCount }, followRequestCount) }}
|
||||
{{ $t('notifications.unread_follow_requests', { num: followRequestsCount }, followRequestsCount) }}
|
||||
</router-link>
|
||||
</div>
|
||||
<i18n-t
|
||||
|
|
|
|||
|
|
@ -1,98 +1,16 @@
|
|||
import { defineAsyncComponent } from 'vue'
|
||||
import { mapActions } from 'pinia'
|
||||
|
||||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
|
||||
import { approveUser, denyUser } from 'src/api/user.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
|
||||
const FollowRequestCard = {
|
||||
props: ['user'],
|
||||
components: {
|
||||
BasicUserCard,
|
||||
ConfirmModal: defineAsyncComponent(
|
||||
() => import('src/components/confirm_modal/confirm_modal.vue'),
|
||||
),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showingApproveConfirmDialog: false,
|
||||
showingDenyConfirmDialog: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
findFollowRequestNotificationId() {
|
||||
const notif = useNotificationsStore().data.find(
|
||||
(notif) =>
|
||||
notif.from_profile.id === this.user.id &&
|
||||
notif.type === 'follow_request',
|
||||
)
|
||||
return notif?.id
|
||||
},
|
||||
showApproveConfirmDialog() {
|
||||
this.showingApproveConfirmDialog = true
|
||||
},
|
||||
hideApproveConfirmDialog() {
|
||||
this.showingApproveConfirmDialog = false
|
||||
},
|
||||
showDenyConfirmDialog() {
|
||||
this.showingDenyConfirmDialog = true
|
||||
},
|
||||
hideDenyConfirmDialog() {
|
||||
this.showingDenyConfirmDialog = false
|
||||
},
|
||||
approveUser() {
|
||||
if (this.shouldConfirmApprove) {
|
||||
this.showApproveConfirmDialog()
|
||||
} else {
|
||||
this.doApprove()
|
||||
}
|
||||
},
|
||||
doApprove() {
|
||||
approveUser({
|
||||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
// TODO fix
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
|
||||
const notifId = this.findFollowRequestNotificationId()
|
||||
useNotificationsStore().markSingleNotificationAsSeen(notifId)
|
||||
this.hideApproveConfirmDialog()
|
||||
},
|
||||
denyUser() {
|
||||
if (this.shouldConfirmDeny) {
|
||||
this.showDenyConfirmDialog()
|
||||
} else {
|
||||
this.doDeny()
|
||||
}
|
||||
},
|
||||
doDeny() {
|
||||
const notifId = this.findFollowRequestNotificationId()
|
||||
|
||||
denyUser({
|
||||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(() => {
|
||||
useNotificationsStore().dismissNotificationLocal(notifId)
|
||||
// TODO fix
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
})
|
||||
this.hideDenyConfirmDialog()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
mergedConfig() {
|
||||
return useMergedConfigStore().mergedConfig
|
||||
},
|
||||
shouldConfirmApprove() {
|
||||
return this.mergedConfig.modalOnApproveFollow
|
||||
},
|
||||
shouldConfirmDeny() {
|
||||
return this.mergedConfig.modalOnDenyFollow
|
||||
},
|
||||
...mapActions(useFollowRequestsStore, ['approve', 'deny']),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,39 +3,17 @@
|
|||
<div class="follow-request-card-content-container">
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="approveUser"
|
||||
@click="() => approve(user.id)"
|
||||
>
|
||||
{{ $t('user_card.approve') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="denyUser"
|
||||
@click="() => deny(user.id)"
|
||||
>
|
||||
{{ $t('user_card.deny') }}
|
||||
</button>
|
||||
</div>
|
||||
<teleport to="#modal">
|
||||
<ConfirmModal
|
||||
v-if="showingApproveConfirmDialog"
|
||||
:title="$t('user_card.approve_confirm_title')"
|
||||
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
||||
@accepted="doApprove"
|
||||
@cancelled="hideApproveConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
||||
</ConfirmModal>
|
||||
<ConfirmModal
|
||||
v-if="showingDenyConfirmDialog"
|
||||
:title="$t('user_card.deny_confirm_title')"
|
||||
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
||||
@accepted="doDeny"
|
||||
@cancelled="hideDenyConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
||||
</ConfirmModal>
|
||||
</teleport>
|
||||
</basic-user-card>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<teleport to="#modal">
|
||||
<div>
|
||||
<ConfirmModal
|
||||
v-if="store.showingApproveConfirmDialog"
|
||||
:title="$t('user_card.approve_confirm_title')"
|
||||
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
||||
@accepted="store.doApprove"
|
||||
@cancelled="store.hideApproveConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
||||
</ConfirmModal>
|
||||
<ConfirmModal
|
||||
v-if="store.showingDenyConfirmDialog"
|
||||
:title="$t('user_card.deny_confirm_title')"
|
||||
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
||||
@accepted="store.doDeny"
|
||||
@cancelled="store.hideDenyConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const store = useFollowRequestsStore()
|
||||
const user = computed(() => useUsersStore().findUser(store.tempId))
|
||||
</script>
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
import FollowRequestCard from 'src/components/follow_request_card/follow_request_card.vue'
|
||||
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
|
||||
const FollowRequests = {
|
||||
components: {
|
||||
FollowRequestCard,
|
||||
},
|
||||
computed: {
|
||||
requests() {
|
||||
return this.$store.state.api.followRequests
|
||||
return useFollowRequestsStore().requests.values()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { set, sumBy } from 'lodash'
|
||||
import { set, sumBy } from 'lodash-es'
|
||||
|
||||
import Attachment from 'src/components/attachment/attachment.vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { isEmpty } from 'lodash'
|
||||
import { isEmpty } from 'lodash-es'
|
||||
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { debounce } from 'lodash'
|
||||
import { debounce } from 'lodash-es'
|
||||
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ const mediaUpload = {
|
|||
},
|
||||
async uploadFile(file) {
|
||||
const self = this
|
||||
const store = this.$store
|
||||
if (file.size > useInstanceStore().uploadlimit) {
|
||||
const filesize = fileSizeFormatService.fileSizeFormat(file.size)
|
||||
const allowedsize = fileSizeFormatService.fileSizeFormat(
|
||||
|
|
@ -145,7 +144,7 @@ const mediaUpload = {
|
|||
self.$emit('uploading')
|
||||
self.uploadCount++
|
||||
|
||||
statusPosterService.uploadMedia({ store, formData }).then(
|
||||
statusPosterService.uploadMedia({ formData }).then(
|
||||
(fileData) => {
|
||||
self.$emit('uploaded', fileData)
|
||||
self.decreaseUploadCount()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
|
|
@ -67,10 +68,10 @@ const MobileNav = {
|
|||
return (
|
||||
this.unseenNotifications.length +
|
||||
countExtraNotifications(
|
||||
this.$store,
|
||||
useMergedConfigStore().mergedConfig,
|
||||
useChatsStore().unreadChatsCount,
|
||||
useAnnouncementsStore().unreadAnnouncementCount,
|
||||
useAnnouncementsStore().unreadAnnouncementsCount,
|
||||
useFollowRequestsStore().followRequestsCount,
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
@ -95,7 +96,7 @@ const MobileNav = {
|
|||
closingDrawerMarksAsSeen() {
|
||||
return useMergedConfigStore().mergedConfig.closingDrawerMarksAsSeen
|
||||
},
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementsCount']),
|
||||
...mapState(useMergedConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
icon="bars"
|
||||
/>
|
||||
<div
|
||||
v-if="(unreadChatsCount && !chatsPinned) || unreadAnnouncementCount"
|
||||
v-if="(unreadChatsCount && !chatsPinned) || unreadAnnouncementsCount"
|
||||
class="badge -dot -notification"
|
||||
/>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { debounce } from 'lodash'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { last } from 'lodash'
|
||||
import { last } from 'lodash-es'
|
||||
|
||||
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { mapState } from 'pinia'
|
||||
import { mapState as mapVuexState } from 'vuex'
|
||||
|
||||
import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
|
@ -11,6 +10,7 @@ import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
|||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
|
@ -112,7 +112,7 @@ const NavPanel = {
|
|||
},
|
||||
computed: {
|
||||
...mapState(useAnnouncementsStore, {
|
||||
unreadAnnouncementCount: 'unreadAnnouncementCount',
|
||||
unreadAnnouncementsCount: 'unreadAnnouncementsCount',
|
||||
supportsAnnouncements: (store) => store.supportsAnnouncements,
|
||||
}),
|
||||
...mapState(useInstanceCapabilitiesStore, [
|
||||
|
|
@ -130,9 +130,7 @@ const NavPanel = {
|
|||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
}),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapVuexState({
|
||||
followRequestCount: (state) => state.api.followRequests.length,
|
||||
}),
|
||||
...mapState(useFollowRequestsStore, ['followRequestsCount']),
|
||||
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||
timelinesItems() {
|
||||
return filterNavigation(
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export const ROOT_ITEMS = {
|
|||
icon: 'comments',
|
||||
label: 'nav.chats',
|
||||
badgeStyle: 'notification',
|
||||
badgeGetter: 'unreadChatsCount',
|
||||
badgeGetter: 'unreadChats',
|
||||
criteria: ['chats'],
|
||||
},
|
||||
friendRequests: {
|
||||
|
|
@ -85,7 +85,7 @@ export const ROOT_ITEMS = {
|
|||
label: 'nav.friend_requests',
|
||||
badgeStyle: 'notification',
|
||||
criteria: ['lockedUser'],
|
||||
badgeGetter: 'followRequestCount',
|
||||
badgeGetter: 'followRequests',
|
||||
},
|
||||
about: {
|
||||
route: 'about',
|
||||
|
|
@ -99,7 +99,7 @@ export const ROOT_ITEMS = {
|
|||
label: 'nav.announcements',
|
||||
store: 'announcements',
|
||||
badgeStyle: 'notification',
|
||||
badgeGetter: 'unreadAnnouncementCount',
|
||||
badgeGetter: 'unreadAnnouncements',
|
||||
criteria: ['announcements'],
|
||||
},
|
||||
drafts: {
|
||||
|
|
@ -107,7 +107,7 @@ export const ROOT_ITEMS = {
|
|||
icon: 'file-pen',
|
||||
label: 'nav.drafts',
|
||||
badgeStyle: 'neutral',
|
||||
badgeGetter: 'draftCount',
|
||||
badgeGetter: 'drafts',
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import { mapState, mapStores } from 'pinia'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import { routeTo } from 'src/components/navigation/navigation.js'
|
||||
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useDraftsStore } from 'src/stores/drafts.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
|
|
@ -40,11 +43,19 @@ const NavigationEntry = {
|
|||
routeTo() {
|
||||
return routeTo(this.item, this.currentUser)
|
||||
},
|
||||
getters() {
|
||||
return this.$store.getters
|
||||
badges() {
|
||||
return {
|
||||
drafts: this.draftsCount,
|
||||
unreadAnnouncements: this.unreadAnnouncementsCount,
|
||||
followRequests: this.followRequestsCount,
|
||||
unreadChats: this.unreadChatsCount,
|
||||
}
|
||||
},
|
||||
...mapStores(useAnnouncementsStore),
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementsCount']),
|
||||
...mapState(useDraftsStore, ['draftsCount']),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||
...mapState(useFollowRequestsStore, ['followRequestsCount']),
|
||||
...mapState(useSyncConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
|
|
|
|||
|
|
@ -47,17 +47,11 @@
|
|||
</component>
|
||||
<slot />
|
||||
<div
|
||||
v-if="item.badgeGetter && getters[item.badgeGetter]"
|
||||
v-if="item.badgeGetter && badges[item.badgeGetter]"
|
||||
class="badge"
|
||||
:class="[`-${item.badgeStyle}`]"
|
||||
>
|
||||
{{ getters[item.badgeGetter] }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="item.badgeGetter && item.store && this[`${item.store}Store`][item.badgeGetter]"
|
||||
class="badge badge-notification"
|
||||
>
|
||||
{{ this[`${item.store}Store`][item.badgeGetter] }}
|
||||
{{ badges[item.badgeGetter] }}
|
||||
</div>
|
||||
<button
|
||||
v-if="showPin && currentUser"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { mapState } from 'pinia'
|
||||
import { mapState as mapVuexState } from 'vuex'
|
||||
|
||||
import {
|
||||
filterNavigation,
|
||||
|
|
@ -14,6 +13,9 @@ import {
|
|||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useDraftsStore } from 'src/stores/drafts.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useListsStore } from 'src/stores/lists'
|
||||
|
|
@ -56,18 +58,27 @@ const NavPanel = {
|
|||
},
|
||||
components: {},
|
||||
computed: {
|
||||
getters() {
|
||||
return this.$store.getters
|
||||
badges() {
|
||||
return {
|
||||
drafts: this.draftsCount,
|
||||
unreadAnnouncements: this.unreadAnnouncementsCount,
|
||||
followRequests: this.followRequestsCount,
|
||||
unreadChats: this.unreadChatsCount,
|
||||
}
|
||||
},
|
||||
...mapState(useListsStore, {
|
||||
lists: getListEntries,
|
||||
}),
|
||||
...mapState(useAnnouncementsStore, {
|
||||
supportsAnnouncements: (store) => store.supportsAnnouncements,
|
||||
unreadAnnouncementsCount: 'unreadAnnouncementsCount',
|
||||
}),
|
||||
...mapState(useDraftsStore, ['draftsCount']),
|
||||
...mapState(useFollowRequestsStore, ['followRequestsCount']),
|
||||
...mapState(useBookmarkFoldersStore, {
|
||||
bookmarks: getBookmarkFolderEntries,
|
||||
}),
|
||||
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||
...mapState(useSyncConfigStore, {
|
||||
pinnedItems: (store) =>
|
||||
new Set(store.prefsStorage.collections.pinnedNavItems),
|
||||
|
|
@ -78,9 +89,6 @@ const NavPanel = {
|
|||
'localBubble',
|
||||
]),
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapVuexState({
|
||||
followRequestCount: (state) => state.api.followRequests.length,
|
||||
}),
|
||||
pinnedList() {
|
||||
if (!this.currentUser) {
|
||||
return filterNavigation(
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
:src="item.iconEmojiUrl"
|
||||
/>
|
||||
<div
|
||||
v-if="item.badgeGetter && getters[item.badgeGetter]"
|
||||
v-if="item.badgeGetter && badges[item.badgeGetter]"
|
||||
class="badge -dot"
|
||||
:class="[`-${item.badgeStyle}`]"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
|
||||
import Report from 'src/components/report/report.vue'
|
||||
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||
|
|
@ -13,15 +12,13 @@ import {
|
|||
highlightStyle,
|
||||
} from '../../services/user_highlighter/user_highlighter.js'
|
||||
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { approveUser, denyUser } from 'src/api/user.js'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -57,8 +54,6 @@ const Notification = {
|
|||
selecting: false,
|
||||
statusExpanded: false,
|
||||
unmuted: false,
|
||||
showingApproveConfirmDialog: false,
|
||||
showingDenyConfirmDialog: false,
|
||||
}
|
||||
},
|
||||
props: ['notification'],
|
||||
|
|
@ -72,9 +67,6 @@ const Notification = {
|
|||
|
||||
UserPopover,
|
||||
UserLink,
|
||||
ConfirmModal: defineAsyncComponent(
|
||||
() => import('src/components/confirm_modal/confirm_modal.vue'),
|
||||
),
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('selectionchange', this.onContentSelect)
|
||||
|
|
@ -124,53 +116,7 @@ const Notification = {
|
|||
toggleMute() {
|
||||
this.unmuted = !this.unmuted
|
||||
},
|
||||
showApproveConfirmDialog() {
|
||||
this.showingApproveConfirmDialog = true
|
||||
},
|
||||
hideApproveConfirmDialog() {
|
||||
this.showingApproveConfirmDialog = false
|
||||
},
|
||||
showDenyConfirmDialog() {
|
||||
this.showingDenyConfirmDialog = true
|
||||
},
|
||||
hideDenyConfirmDialog() {
|
||||
this.showingDenyConfirmDialog = false
|
||||
},
|
||||
approveUser() {
|
||||
if (this.shouldConfirmApprove) {
|
||||
this.showApproveConfirmDialog()
|
||||
} else {
|
||||
this.doApprove()
|
||||
}
|
||||
},
|
||||
doApprove() {
|
||||
approveUser({
|
||||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
// TODO Fix this
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
useNotificationsStore().markSingleNotificationAsSeen(this.notification.id)
|
||||
this.hideApproveConfirmDialog()
|
||||
},
|
||||
denyUser() {
|
||||
if (this.shouldConfirmDeny) {
|
||||
this.showDenyConfirmDialog()
|
||||
} else {
|
||||
this.doDeny()
|
||||
}
|
||||
},
|
||||
doDeny() {
|
||||
denyUser({
|
||||
id: this.user.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(() => {
|
||||
useNotificationsStore().dismissNotificationLocal(this.notification.id)
|
||||
// TODO Fix this
|
||||
this.$store.dispatch('removeFollowRequest', this.user)
|
||||
})
|
||||
this.hideDenyConfirmDialog()
|
||||
},
|
||||
...mapActions(useFollowRequestsStore, ['approve', 'deny']),
|
||||
},
|
||||
computed: {
|
||||
status() {
|
||||
|
|
@ -221,12 +167,6 @@ const Notification = {
|
|||
scaleMfm() {
|
||||
return this.mergedConfig.scaleMfm
|
||||
},
|
||||
shouldConfirmApprove() {
|
||||
return this.mergedConfig.modalOnApproveFollow
|
||||
},
|
||||
shouldConfirmDeny() {
|
||||
return this.mergedConfig.modalOnDenyFollow
|
||||
},
|
||||
...mapState(useUsersStore, ['currentUser']),
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@
|
|||
<button
|
||||
class="button-unstyled"
|
||||
:title="$t('tool_tip.accept_follow_request')"
|
||||
@click="approveUser()"
|
||||
@click="() => approve(user.id)"
|
||||
>
|
||||
<FAIcon
|
||||
icon="check"
|
||||
|
|
@ -236,7 +236,7 @@
|
|||
<button
|
||||
class="button-unstyled"
|
||||
:title="$t('tool_tip.reject_follow_request')"
|
||||
@click="denyUser()"
|
||||
@click="() => deny(user.id)"
|
||||
>
|
||||
<FAIcon
|
||||
icon="times"
|
||||
|
|
@ -268,28 +268,6 @@
|
|||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<teleport to="#modal">
|
||||
<ConfirmModal
|
||||
v-if="showingApproveConfirmDialog"
|
||||
:title="$t('user_card.approve_confirm_title')"
|
||||
:confirm-text="$t('user_card.approve_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.approve_confirm_cancel_button')"
|
||||
@accepted="doApprove"
|
||||
@cancelled="hideApproveConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }}
|
||||
</ConfirmModal>
|
||||
<ConfirmModal
|
||||
v-if="showingDenyConfirmDialog"
|
||||
:title="$t('user_card.deny_confirm_title')"
|
||||
:confirm-text="$t('user_card.deny_confirm_accept_button')"
|
||||
:cancel-text="$t('user_card.deny_confirm_cancel_button')"
|
||||
@accepted="doDeny"
|
||||
@cancelled="hideDenyConfirmDialog"
|
||||
>
|
||||
{{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }}
|
||||
</ConfirmModal>
|
||||
</teleport>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import NotificationFilters from './notification_filters.vue'
|
|||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements.js'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
|
|
@ -107,17 +108,17 @@ const Notifications = {
|
|||
},
|
||||
extraNotificationsCount() {
|
||||
return countExtraNotifications(
|
||||
this.$store,
|
||||
useMergedConfigStore().mergedConfig,
|
||||
useChatsStore().unreadChatsCount,
|
||||
useAnnouncementsStore().unreadAnnouncementCount,
|
||||
useAnnouncementsStore().unreadAnnouncementsCount,
|
||||
useFollowRequestsStore().followRequestsCount,
|
||||
)
|
||||
},
|
||||
unseenCountTitle() {
|
||||
return (
|
||||
this.unseenNotifications.length +
|
||||
this.unreadChatsCount +
|
||||
this.unreadAnnouncementCount
|
||||
this.unreadAnnouncementsCount
|
||||
)
|
||||
},
|
||||
loading() {
|
||||
|
|
@ -156,7 +157,7 @@ const Notifications = {
|
|||
showExtraNotifications() {
|
||||
return !this.noExtra
|
||||
},
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementsCount']),
|
||||
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||
...mapState(useInterfaceStore, ['layoutType']),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
unescape as ldUnescape,
|
||||
reject,
|
||||
uniqBy,
|
||||
} from 'lodash'
|
||||
} from 'lodash-es'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
|
|
@ -24,6 +24,7 @@ import { findOffset } from '../../services/offset_finder/offset_finder.service.j
|
|||
import genRandomSeed from '../../services/random_seed/random_seed.service.js'
|
||||
import statusPoster from '../../services/status_poster/status_poster.service.js'
|
||||
|
||||
import { useDraftsStore } from 'src/stores/drafts.js'
|
||||
import { useEmojiStore } from 'src/stores/emoji.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
|
|
@ -400,8 +401,6 @@ const PostStatusForm = {
|
|||
contentType: this.newStatus.contentType,
|
||||
poll,
|
||||
idempotencyKey: this.idempotencyKey,
|
||||
|
||||
store: this.$store,
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -412,7 +411,6 @@ const PostStatusForm = {
|
|||
...useEmojiStore().standardEmojiList,
|
||||
...useEmojiStore().customEmoji,
|
||||
],
|
||||
store: this.$store,
|
||||
})
|
||||
},
|
||||
emojiSuggestor() {
|
||||
|
|
@ -574,7 +572,7 @@ const PostStatusForm = {
|
|||
...mapState(useUsersStore, ['currentUser']),
|
||||
...mapState(useMergedConfigStore, ['mergedConfig']),
|
||||
...mapState(useInterfaceStore, {
|
||||
mobileLayout: (store) => store.mobileLayout,
|
||||
mobileLayout: (state) => state.mobileLayout,
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -751,7 +749,6 @@ const PostStatusForm = {
|
|||
const description = this.newStatus.mediaDescriptions[id]
|
||||
if (!description || description.trim() === '') return
|
||||
return statusPoster.setMediaDescription({
|
||||
store: this.$store,
|
||||
id,
|
||||
description,
|
||||
})
|
||||
|
|
@ -985,13 +982,13 @@ const PostStatusForm = {
|
|||
saveDraft() {
|
||||
if (!this.disableDraft && !this.saveInhibited) {
|
||||
if (this.safeToSaveDraft) {
|
||||
return this.$store
|
||||
.dispatch('addOrSaveDraft', {
|
||||
draft: {
|
||||
type: this.statusType,
|
||||
refId: this.refId,
|
||||
...this.newStatus,
|
||||
},
|
||||
return useDraftsStore()
|
||||
.addOrSaveDraft({
|
||||
type: this.statusType,
|
||||
refId: this.refId,
|
||||
...this.newStatus,
|
||||
// Draft ID overwrites status ID (which is undefined for fresh statuses)
|
||||
id: this.draftId,
|
||||
})
|
||||
.then((id) => {
|
||||
if (this.newStatus.id !== id) {
|
||||
|
|
@ -1024,14 +1021,14 @@ const PostStatusForm = {
|
|||
}
|
||||
},
|
||||
abandonDraft() {
|
||||
return this.$store.dispatch('abandonDraft', { id: this.draftId })
|
||||
return useDraftsStore().abandonDraft(this.draftId)
|
||||
},
|
||||
getDraft() {
|
||||
const maybeDraft = this.$store.state.drafts.drafts[this.draftId]
|
||||
const maybeDraft = useDraftsStore().drafts.get(this.draftId)
|
||||
if (this.draftId && maybeDraft) {
|
||||
return maybeDraft
|
||||
} else {
|
||||
const existingDrafts = this.$store.getters.draftsByTypeAndRefId(
|
||||
const existingDrafts = useDraftsStore().draftsByTypeAndRefId(
|
||||
this.statusType,
|
||||
this.refId,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import Modal from 'src/components/modal/modal.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { debounce } from 'lodash'
|
||||
import { debounce } from 'lodash-es'
|
||||
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import Quote from './quote.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { flattenDeep, unescape as ldUnescape } from 'lodash'
|
||||
import { flattenDeep, unescape as ldUnescape } from 'lodash-es'
|
||||
|
||||
import HashtagLink from 'src/components/hashtag_link/hashtag_link.vue'
|
||||
import { MENTIONS_LIMIT } from 'src/components/mentions_line/mentions_line.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { map, uniqBy } from 'lodash'
|
||||
import { map, uniqBy } from 'lodash-es'
|
||||
|
||||
import Conversation from 'src/components/conversation/conversation.vue'
|
||||
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Checkbox from 'components/checkbox/checkbox.vue'
|
|||
import Popover from 'components/popover/popover.vue'
|
||||
import Select from 'components/select/select.vue'
|
||||
import StillImage from 'components/still-image/still-image.vue'
|
||||
import { clone } from 'lodash'
|
||||
import { clone } from 'lodash-es'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
|
||||
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
|
||||
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { clone } from 'lodash'
|
||||
import { clone } from 'lodash-es'
|
||||
|
||||
import Attachment from 'src/components/attachment/attachment.vue'
|
||||
import MediaUpload from 'src/components/media_upload/media_upload.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cloneDeep, get, isEqual, set } from 'lodash'
|
||||
import { cloneDeep, get, isEqual, set } from 'lodash-es'
|
||||
|
||||
import DraftButtons from './draft_buttons.vue'
|
||||
import LocalSettingIndicator from './local_setting_indicator.vue'
|
||||
|
|
@ -8,6 +8,7 @@ import { useAdminSettingsStore } from 'src/stores/admin_settings.js'
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useProfileConfigStore } from 'src/stores/profile_config.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
export default {
|
||||
|
|
@ -236,7 +237,7 @@ export default {
|
|||
configSource() {
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return this.$store.state.profileConfig
|
||||
return useProfileConfigStore().config
|
||||
case 'admin':
|
||||
return useAdminSettingsStore().config
|
||||
default:
|
||||
|
|
@ -253,7 +254,7 @@ export default {
|
|||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return (k, v) =>
|
||||
this.$store.dispatch('setProfileOption', { name: k, value: v })
|
||||
useProfileConfigStore().setProfileOption({ name: k, value: v })
|
||||
case 'admin':
|
||||
return (k, v) =>
|
||||
useAdminSettingsStore().pushAdminSetting({ path: k, value: v })
|
||||
|
|
@ -412,8 +413,8 @@ export default {
|
|||
hardReset() {
|
||||
switch (this.realSource) {
|
||||
case 'admin':
|
||||
return this.$store
|
||||
.dispatch('resetAdminSetting', { path: this.path })
|
||||
return useAdminSettingsStore()
|
||||
.resetAdminSetting({ path: this.path })
|
||||
.then(() => {
|
||||
this.draft = this.state
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cloneDeep, isEqual } from 'lodash'
|
||||
import { cloneDeep, isEqual } from 'lodash-es'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useProfileConfigStore } from 'src/stores/profile_config.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
|
|
@ -109,6 +110,9 @@ const ComposingTab = {
|
|||
FontControl,
|
||||
},
|
||||
computed: {
|
||||
defaultScope() {
|
||||
return useProfileConfigStore().config.defaultScope
|
||||
},
|
||||
postFormats() {
|
||||
return useInstanceCapabilitiesStore().postFormats
|
||||
},
|
||||
|
|
@ -135,7 +139,7 @@ const ComposingTab = {
|
|||
},
|
||||
methods: {
|
||||
changeDefaultScope(value) {
|
||||
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
|
||||
useProfileConfigStore().setProfileOption({ name: 'defaultScope', value })
|
||||
},
|
||||
clearCache(key) {
|
||||
clearCache(key)
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
<ScopeSelector
|
||||
class="scope-selector setting-control"
|
||||
:show-all="true"
|
||||
:user-default="$store.state.profileConfig.defaultScope"
|
||||
:initial-scope="$store.state.profileConfig.defaultScope"
|
||||
:on-scope-change="changeDefaultScope"
|
||||
:user-default="defaultScope"
|
||||
:initial-scope="defaultScope"
|
||||
:unstyled="false"
|
||||
@change="changeDefaultScope"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cloneDeep } from 'lodash'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get, map, reject } from 'lodash'
|
||||
import { get, map, reject } from 'lodash-es'
|
||||
|
||||
import Autosuggest from 'src/components/autosuggest/autosuggest.vue'
|
||||
import BlockCard from 'src/components/block_card/block_card.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get, set, throttle, unset } from 'lodash'
|
||||
import { get, set, throttle, unset } from 'lodash-es'
|
||||
import {
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { flattenDeep, throttle } from 'lodash'
|
||||
import { flattenDeep, throttle } from 'lodash-es'
|
||||
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { mapActions, mapState } from 'pinia'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js'
|
||||
import UserCard from 'src/components/user_card/user_card.vue'
|
||||
|
|
@ -8,6 +7,8 @@ import { unseenNotifications } from '../../services/notification_utils/notificat
|
|||
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useChatsStore } from 'src/stores/chats.js'
|
||||
import { useDraftsStore } from 'src/stores/drafts.js'
|
||||
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
|
@ -61,10 +62,6 @@ const SideDrawer = {
|
|||
GestureService.DIRECTION_LEFT,
|
||||
this.toggleDrawer,
|
||||
)
|
||||
|
||||
if (this.currentUser?.locked) {
|
||||
this.$store.dispatch('startFetchingFollowRequests')
|
||||
}
|
||||
},
|
||||
components: {
|
||||
UserCard,
|
||||
|
|
@ -85,9 +82,6 @@ const SideDrawer = {
|
|||
unseenNotificationsCount() {
|
||||
return this.unseenNotifications.length
|
||||
},
|
||||
followRequestCount() {
|
||||
return this.$store.state.api.followRequests.length
|
||||
},
|
||||
timelinesRoute() {
|
||||
let name
|
||||
if (useInterfaceStore().lastTimeline) {
|
||||
|
|
@ -100,9 +94,10 @@ const SideDrawer = {
|
|||
return { name }
|
||||
}
|
||||
},
|
||||
...mapState(useFollowRequestsStore, ['followRequestsCount']),
|
||||
...mapState(useAnnouncementsStore, [
|
||||
'supportsAnnouncements',
|
||||
'unreadAnnouncementCount',
|
||||
'unreadAnnouncementsCount',
|
||||
]),
|
||||
...mapState(useInstanceCapabilitiesStore, [
|
||||
'pleromaChatMessagesAvailable',
|
||||
|
|
@ -115,7 +110,7 @@ const SideDrawer = {
|
|||
hideSitename: (store) => store.instanceIdentity.hideSitename,
|
||||
}),
|
||||
...mapState(useChatsStore, ['unreadChatsCount']),
|
||||
...mapGetters(['draftCount']),
|
||||
...mapState(useDraftsStore, ['draftsCount']),
|
||||
},
|
||||
methods: {
|
||||
toggleDrawer() {
|
||||
|
|
|
|||
|
|
@ -141,10 +141,10 @@
|
|||
icon="user-plus"
|
||||
/> {{ $t("nav.friend_requests") }}
|
||||
<span
|
||||
v-if="followRequestCount > 0"
|
||||
v-if="followRequestsCount > 0"
|
||||
class="badge -notification"
|
||||
>
|
||||
{{ followRequestCount }}
|
||||
{{ followRequestsCount }}
|
||||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
|
|
@ -248,10 +248,10 @@
|
|||
icon="bullhorn"
|
||||
/> {{ $t("nav.announcements") }}
|
||||
<span
|
||||
v-if="unreadAnnouncementCount"
|
||||
v-if="unreadAnnouncementsCount"
|
||||
class="badge -notification"
|
||||
>
|
||||
{{ unreadAnnouncementCount }}
|
||||
{{ unreadAnnouncementsCount }}
|
||||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
|
|
@ -269,10 +269,10 @@
|
|||
icon="file-pen"
|
||||
/> {{ $t('nav.drafts') }}
|
||||
<span
|
||||
v-if="draftCount"
|
||||
v-if="draftsCount"
|
||||
class="badge -neutral"
|
||||
>
|
||||
{{ draftCount }}
|
||||
{{ draftsCount }}
|
||||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { groupBy, map } from 'lodash'
|
||||
import { groupBy, map } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import BasicUserCard from 'src/components/basic_user_card/basic_user_card.vue'
|
||||
|
|
|
|||
|
|
@ -97,9 +97,6 @@ const StatusActionButtons = {
|
|||
replying: this.replying,
|
||||
emojiPickerShown: this.emojiPickerShown,
|
||||
emit: this.$emit,
|
||||
dispatch: this.$store.dispatch,
|
||||
state: this.$store.state,
|
||||
getters: this.$store.getters,
|
||||
router: this.$router,
|
||||
currentUser: this.currentUser,
|
||||
loggedIn: !!this.currentUser,
|
||||
|
|
|
|||
|
|
@ -29,14 +29,13 @@ const StickerPicker = {
|
|||
}
|
||||
},
|
||||
pick(sticker, name) {
|
||||
const store = this.$store
|
||||
// TODO remove this workaround by finding a way to bypass reuploads
|
||||
fetch(sticker).then((res) => {
|
||||
res.blob().then((blob) => {
|
||||
const file = new File([blob], name, { mimetype: 'image/png' })
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
statusPosterService.uploadMedia({ store, formData }).then(
|
||||
statusPosterService.uploadMedia({ formData }).then(
|
||||
(fileData) => {
|
||||
this.$emit('uploaded', fileData)
|
||||
this.clear()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { debounce, throttle } from 'lodash'
|
||||
import { debounce, throttle } from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import Conversation from 'src/components/conversation/conversation.vue'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
escape as ldEscape,
|
||||
unescape as ldUnescape,
|
||||
merge,
|
||||
} from 'lodash'
|
||||
} from 'lodash-es'
|
||||
import { mapState } from 'pinia'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
|
|
@ -418,7 +418,6 @@ export default {
|
|||
...useEmojiStore().standardEmojiList,
|
||||
...useEmojiStore().customEmoji,
|
||||
],
|
||||
store: this.$store,
|
||||
})
|
||||
},
|
||||
emojiSuggestor() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
|
||||
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
||||
import List from 'src/components/list/list.vue'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { shuffle } from 'lodash'
|
||||
import { shuffle } from 'lodash-es'
|
||||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
|
|
|
|||
|
|
@ -1742,6 +1742,7 @@
|
|||
"approve_confirm_accept_button": "Approve",
|
||||
"approve_confirm_cancel_button": "Do not approve",
|
||||
"approve_confirm": "Do you want to approve {user}'s follow request?",
|
||||
"approve_error": "Failure approving follow request: {error}",
|
||||
"block": "Block",
|
||||
"blocked": "Blocked!",
|
||||
"block_confirm_title": "Block confirmation",
|
||||
|
|
@ -1754,6 +1755,7 @@
|
|||
"deny_confirm_accept_button": "Deny",
|
||||
"deny_confirm_cancel_button": "Do not deny",
|
||||
"deny_confirm": "Do you want to deny {user}'s follow request?",
|
||||
"deny_error": "Failure denying follow request: {error}",
|
||||
"edit_profile": "Edit profile",
|
||||
"favorites": "Favorites",
|
||||
"follow": "Follow",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
// sed -i -e "s/'//gm" -e 's/"/\\"/gm' -re 's/^( +)(.+?): ((.+?))?(,?)(\{?)$/\1"\2": "\4"/gm' -e 's/\"\{\"/{/g' -e 's/,"$/",/g' file.json
|
||||
// There's only problem that apostrophe character ' gets replaced by \\ so you have to fix it manually, sorry.
|
||||
|
||||
import { isEqual } from 'lodash'
|
||||
import { isEqual } from 'lodash-es'
|
||||
|
||||
import enMessages from './en.json'
|
||||
import { langCodeToJsonName, languages } from './languages.js'
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
import { cloneDeep, each, get, merge, set } from 'lodash'
|
||||
import { cloneDeep, get, set } from 'lodash-es'
|
||||
|
||||
import { storage } from './storage.js'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
let loaded = false
|
||||
|
||||
const defaultReducer = (state, paths) =>
|
||||
paths.length === 0
|
||||
? state
|
||||
|
|
@ -14,86 +10,10 @@ const defaultReducer = (state, paths) =>
|
|||
return substate
|
||||
}, {})
|
||||
|
||||
const saveImmedeatelyActions = [
|
||||
'markNotificationsAsSeen',
|
||||
'setHighlight',
|
||||
'setOption',
|
||||
'setClientData',
|
||||
'setToken',
|
||||
'clearToken',
|
||||
]
|
||||
|
||||
const defaultStorage = (() => {
|
||||
return storage
|
||||
})()
|
||||
|
||||
export default function createPersistedState({
|
||||
key = 'vuex-lz',
|
||||
paths = [],
|
||||
getState = (key, storage) => {
|
||||
const value = storage.getItem(key)
|
||||
return value
|
||||
},
|
||||
setState = (key, state, storage) => {
|
||||
if (!loaded) {
|
||||
console.info('waiting for old state to be loaded...')
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
return storage.setItem(key, state)
|
||||
}
|
||||
},
|
||||
reducer = defaultReducer,
|
||||
storage = defaultStorage,
|
||||
subscriber = (store) => (handler) => store.subscribe(handler),
|
||||
} = {}) {
|
||||
return getState(key, storage).then((savedState) => {
|
||||
return (store) => {
|
||||
try {
|
||||
if (savedState !== null && typeof savedState === 'object') {
|
||||
// build user cache
|
||||
const usersState = savedState.users || {}
|
||||
usersState.usersObject = {}
|
||||
const users = usersState.users || []
|
||||
each(users, (user) => {
|
||||
usersState.usersObject[user.id] = user
|
||||
})
|
||||
savedState.users = usersState
|
||||
|
||||
store.replaceState(merge({}, store.state, savedState))
|
||||
}
|
||||
loaded = true
|
||||
} catch (e) {
|
||||
console.error("Couldn't load state")
|
||||
console.error(e)
|
||||
loaded = true
|
||||
}
|
||||
subscriber(store)((mutation, state) => {
|
||||
try {
|
||||
if (saveImmedeatelyActions.includes(mutation.type)) {
|
||||
setState(key, reducer(cloneDeep(state), paths), storage).then(
|
||||
(success) => {
|
||||
if (success !== undefined) {
|
||||
if (mutation.type === 'setOption') {
|
||||
useInterfaceStore().settingsSaved({ success })
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (mutation.type === 'setOption') {
|
||||
useInterfaceStore().settingsSaved({ error })
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Couldn't persist state:")
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* This persists state for pinia, which falls back to read from the vuex state
|
||||
* if pinia persisted state does not exist.
|
||||
|
|
|
|||
32
src/main.js
32
src/main.js
|
|
@ -1,7 +1,6 @@
|
|||
/* global process */
|
||||
|
||||
import { createPinia } from 'pinia'
|
||||
import { createStore } from 'vuex'
|
||||
|
||||
import 'custom-event-polyfill'
|
||||
import './lib/event_target_polyfill.js'
|
||||
|
|
@ -17,11 +16,8 @@ import { createI18n } from 'vue-i18n'
|
|||
|
||||
import afterStoreSetup from './boot/after_store.js'
|
||||
import messages from './i18n/messages.js'
|
||||
import createPersistedState, {
|
||||
piniaPersistPlugin,
|
||||
} from './lib/persisted_state.js'
|
||||
import { piniaPersistPlugin } from './lib/persisted_state.js'
|
||||
import { piniaPushNotificationsPlugin } from './lib/push_notifications_plugin.js'
|
||||
import vuexModules from './modules/index.js'
|
||||
|
||||
import { piniaLanguagePlugin } from 'src/lib/language.js'
|
||||
import { piniaStylePlugin } from 'src/lib/style.js'
|
||||
|
|
@ -37,10 +33,6 @@ const i18n = createI18n({
|
|||
|
||||
messages.setLanguage(i18n.global, currentLocale)
|
||||
|
||||
const persistedStateOptions = {
|
||||
paths: ['oauth', 'config'],
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const isFox = Math.floor(Math.random() * 2) > 0 ? '_fox' : ''
|
||||
|
||||
|
|
@ -69,20 +61,12 @@ const persistedStateOptions = {
|
|||
|
||||
try {
|
||||
let storageError
|
||||
const plugins = []
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPersistPlugin())
|
||||
pinia.use(piniaLanguagePlugin)
|
||||
pinia.use(piniaStylePlugin)
|
||||
pinia.use(piniaPushNotificationsPlugin)
|
||||
|
||||
try {
|
||||
const persistedState = await createPersistedState(persistedStateOptions)
|
||||
plugins.push(persistedState)
|
||||
} catch (e) {
|
||||
console.error('Storage error', e)
|
||||
storageError = e
|
||||
}
|
||||
document.querySelector('#splash').classList.remove('initial-hidden')
|
||||
document.querySelector('#mascot').src =
|
||||
`/static/pleromatan_apology${isFox}_small.webp`
|
||||
|
|
@ -93,18 +77,8 @@ const persistedStateOptions = {
|
|||
'update.art_by',
|
||||
{ linkToArtist: 'pipivovott' },
|
||||
)
|
||||
const store = createStore({
|
||||
modules: vuexModules,
|
||||
plugins,
|
||||
options: {
|
||||
devtools: process.env.NODE_ENV !== 'production',
|
||||
},
|
||||
strict: false, // Socket modifies itself, let's ignore this for now.
|
||||
// strict: process.env.NODE_ENV !== 'production'
|
||||
})
|
||||
window.vuex = store
|
||||
// Temporarily passing pinia and vuex stores along with storageError result until migration is fully complete.
|
||||
return await afterStoreSetup({ pinia, store, storageError, i18n })
|
||||
// Temporarily passing pinia stores along with storageError result until migration is fully complete.
|
||||
return await afterStoreSetup({ pinia, storageError, i18n })
|
||||
} catch (e) {
|
||||
splashError(i18n, e)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
import { Socket } from 'phoenix'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useShoutStore } from 'src/stores/shout.js'
|
||||
|
||||
import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service'
|
||||
|
||||
const api = {
|
||||
state: {
|
||||
fetchers: {},
|
||||
socket: null,
|
||||
followRequests: [],
|
||||
},
|
||||
getters: {
|
||||
followRequestCount: (state) => state.followRequests.length,
|
||||
},
|
||||
mutations: {
|
||||
addFetcher(state, { fetcherName, fetcher }) {
|
||||
state.fetchers[fetcherName] = fetcher
|
||||
},
|
||||
removeFetcher(state, { fetcherName }) {
|
||||
state.fetchers[fetcherName].stop()
|
||||
delete state.fetchers[fetcherName]
|
||||
},
|
||||
setWsToken(state, token) {
|
||||
state.wsToken = token
|
||||
},
|
||||
setSocket(state, socket) {
|
||||
state.socket = socket
|
||||
},
|
||||
setFollowRequests(state, value) {
|
||||
state.followRequests = value
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// Follow requests
|
||||
startFetchingFollowRequests(store) {
|
||||
if (store.state.fetchers.followRequests) return
|
||||
const fetcher = followRequestFetcher.startFetching({
|
||||
store,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
|
||||
store.commit('addFetcher', { fetcherName: 'followRequests', fetcher })
|
||||
},
|
||||
stopFetchingFollowRequests(store) {
|
||||
const fetcher = store.state.fetchers.followRequests
|
||||
if (!fetcher) return
|
||||
store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher })
|
||||
},
|
||||
|
||||
// Pleroma websocket
|
||||
setWsToken(store, token) {
|
||||
store.commit('setWsToken', token)
|
||||
},
|
||||
initializeSocket({ commit, state, rootState }) {
|
||||
// Set up websocket connection
|
||||
const token = state.wsToken
|
||||
if (
|
||||
useInstanceCapabilitiesStore().shoutAvailable &&
|
||||
token !== undefined &&
|
||||
state.socket === null
|
||||
) {
|
||||
const socket = new Socket('/socket', { params: { token } })
|
||||
socket.connect()
|
||||
|
||||
commit('setSocket', socket)
|
||||
useShoutStore().initializeShout(socket)
|
||||
}
|
||||
},
|
||||
disconnectFromSocket({ commit, state }) {
|
||||
state.socket?.disconnect()
|
||||
commit('setSocket', null)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default api
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
|
||||
const browserLocale = (navigator.language || 'en').split('-')[0]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
import { storage } from 'src/lib/storage.js'
|
||||
|
||||
export const defaultState = {
|
||||
drafts: {},
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
addOrSaveDraft(state, { draft }) {
|
||||
state.drafts[draft.id] = draft
|
||||
},
|
||||
abandonDraft(state, { id }) {
|
||||
delete state.drafts[id]
|
||||
},
|
||||
loadDrafts(state, data) {
|
||||
state.drafts = data
|
||||
},
|
||||
}
|
||||
|
||||
const storageKey = 'pleroma-fe-drafts'
|
||||
|
||||
/*
|
||||
* Note: we do not use the persist state plugin because
|
||||
* it is not impossible for a user to have two windows at
|
||||
* the same time. The persist state plugin is just overriding
|
||||
* everything with the current state. This isn't good because
|
||||
* if a draft is created in one window and another draft is
|
||||
* created in another, the draft in the first window will just
|
||||
* be overriden.
|
||||
* Here, we can't guarantee 100% atomicity unless one uses
|
||||
* different keys, which will just pollute the whole storage.
|
||||
* It is indeed best to have backend support for this.
|
||||
*/
|
||||
const getStorageData = async () =>
|
||||
(await storage.getItem(storageKey)) ||
|
||||
{
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
const saveDraftToStorage = async (draft) => {
|
||||
const currentData = await getStorageData()
|
||||
currentData[draft.id] = JSON.parse(JSON.stringify(draft))
|
||||
await storage.setItem(storageKey, currentData)
|
||||
}
|
||||
|
||||
const deleteDraftFromStorage = async (ids) => {
|
||||
const currentData = await getStorageData()
|
||||
ids.forEach((id) => {
|
||||
delete currentData[id]
|
||||
})
|
||||
await storage.setItem(storageKey, currentData)
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
async addOrSaveDraft(store, { draft }) {
|
||||
const id = draft.id || new Date().getTime().toString()
|
||||
const draftWithId = { ...draft, id }
|
||||
store.commit('addOrSaveDraft', { draft: draftWithId })
|
||||
await saveDraftToStorage(draftWithId)
|
||||
return id
|
||||
},
|
||||
async abandonDraft(store, { id }) {
|
||||
store.commit('abandonDraft', { id })
|
||||
await deleteDraftFromStorage([id])
|
||||
},
|
||||
async abandonAllDrafts(store) {
|
||||
const ids = Object.keys(store.state.drafts)
|
||||
ids.forEach((id) => store.commit('abandonDraft', { id }))
|
||||
await deleteDraftFromStorage(ids)
|
||||
},
|
||||
async loadDrafts(store) {
|
||||
const currentData = await getStorageData()
|
||||
store.commit('loadDrafts', currentData)
|
||||
},
|
||||
}
|
||||
|
||||
export const getters = {
|
||||
draftsByTypeAndRefId(state) {
|
||||
return (type, refId) => {
|
||||
return Object.values(state.drafts).filter(
|
||||
(draft) => draft.type === type && draft.refId === refId,
|
||||
)
|
||||
}
|
||||
},
|
||||
draftsArray(state) {
|
||||
return Object.values(state.drafts)
|
||||
},
|
||||
draftCount(state) {
|
||||
return Object.values(state.drafts).length
|
||||
},
|
||||
}
|
||||
|
||||
const drafts = {
|
||||
state: defaultState,
|
||||
mutations,
|
||||
getters,
|
||||
actions,
|
||||
}
|
||||
|
||||
export default drafts
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import api from './api.js'
|
||||
import drafts from './drafts.js'
|
||||
import profileConfig from './profileConfig.js'
|
||||
|
||||
export default {
|
||||
api,
|
||||
profileConfig,
|
||||
drafts,
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { kebabCase } from 'lodash'
|
||||
import { kebabCase } from 'lodash-es'
|
||||
|
||||
const propsToNative = (props) =>
|
||||
Object.keys(props).reduce((acc, cur) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { find, reduce } from 'lodash'
|
||||
import { find, reduce } from 'lodash-es'
|
||||
|
||||
export const replaceWord = (str, toReplace, replacement) => {
|
||||
return str.slice(0, toReplace.start) + replacement + str.slice(toReplace.end)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { isFunction } from 'lodash'
|
||||
import { isFunction } from 'lodash-es'
|
||||
|
||||
const getComponentOptions = (Component) =>
|
||||
isFunction(Component) ? Component.options : Component
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { parseLinkHeader } from '@web3-storage/parse-link-header'
|
||||
import escapeHtml from 'escape-html'
|
||||
import { unescape as lodashUnescape } from 'lodash'
|
||||
import { unescape as lodashUnescape } from 'lodash-es'
|
||||
import punycode from 'punycode.js'
|
||||
|
||||
import { fileType } from '../file_type/file_type.service.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { capitalize } from 'lodash'
|
||||
import { capitalize } from 'lodash-es'
|
||||
|
||||
function humanizeErrors(errors) {
|
||||
return Object.entries(errors).reduce((errs, [k, val]) => {
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { fetchFollowRequests } from 'src/api/user.js'
|
||||
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
|
||||
|
||||
const fetchAndUpdate = ({ store, credentials }) => {
|
||||
return fetchFollowRequests({ credentials })
|
||||
.then(
|
||||
(result) => {
|
||||
const { data: requests } = result
|
||||
store.commit('setFollowRequests', requests)
|
||||
useUsersStore().addNewUsers(result)
|
||||
},
|
||||
(rej) => {
|
||||
console.error(rej)
|
||||
},
|
||||
)
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
|
||||
const startFetching = ({ credentials, store }) => {
|
||||
const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store })
|
||||
boundFetchAndUpdate()
|
||||
return promiseInterval(boundFetchAndUpdate, 10000)
|
||||
}
|
||||
|
||||
const followRequestFetcher = {
|
||||
startFetching,
|
||||
}
|
||||
|
||||
export default followRequestFetcher
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { unescape as ldUnescape } from 'lodash'
|
||||
import { unescape as ldUnescape } from 'lodash-es'
|
||||
|
||||
import { getTagName } from './utility.service.js'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import ISO6391 from 'iso-639-1'
|
||||
import { map } from 'lodash'
|
||||
import { map } from 'lodash-es'
|
||||
|
||||
import languagesObject from '../../i18n/messages'
|
||||
|
||||
|
|
|
|||
|
|
@ -98,13 +98,11 @@ export const unseenNotifications = (
|
|||
}
|
||||
|
||||
export const countExtraNotifications = (
|
||||
store,
|
||||
mergedConfig,
|
||||
unreadChatsCount,
|
||||
unreadAnnouncementCount,
|
||||
unreadAnnouncementsCount,
|
||||
followRequestsCount,
|
||||
) => {
|
||||
const rootGetters = store.rootGetters || store.getters
|
||||
|
||||
if (!mergedConfig.showExtraNotifications) {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -112,10 +110,10 @@ export const countExtraNotifications = (
|
|||
return [
|
||||
mergedConfig.showChatsInExtraNotifications ? unreadChatsCount : 0,
|
||||
mergedConfig.showAnnouncementsInExtraNotifications
|
||||
? unreadAnnouncementCount
|
||||
? unreadAnnouncementsCount
|
||||
: 0,
|
||||
mergedConfig.showFollowRequestsInExtraNotifications
|
||||
? rootGetters.followRequestCount
|
||||
? followRequestsCount
|
||||
: 0,
|
||||
].reduce((a, c) => a + c, 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { map } from 'lodash'
|
||||
import { map } from 'lodash-es'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import sum from 'hash-sum'
|
||||
import localforage from 'localforage'
|
||||
import { chunk, throttle } from 'lodash'
|
||||
import { chunk, throttle } from 'lodash-es'
|
||||
|
||||
import { getCssRules } from '../theme_data/css_utils.js'
|
||||
import { getEngineChecksum, init } from '../theme_data/theme_data_3.service.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { flattenDeep } from 'lodash'
|
||||
import { flattenDeep } from 'lodash-es'
|
||||
|
||||
export const deserializeShadow = (string) => {
|
||||
const modes = [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { sortBy } from 'lodash'
|
||||
import { sortBy } from 'lodash-es'
|
||||
|
||||
// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
|
||||
// into an array [item2, item3] for iterating
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { brightness, convert } from 'chromatism'
|
||||
import sum from 'hash-sum'
|
||||
import { flattenDeep, sortBy } from 'lodash'
|
||||
import { flattenDeep, sortBy } from 'lodash-es'
|
||||
|
||||
import {
|
||||
alphaBlend,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { includes } from 'lodash'
|
||||
import { includes } from 'lodash-es'
|
||||
|
||||
const generateProfileLink = (id, screenName, restrictedNicknames) => {
|
||||
const complicated =
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cloneDeep, differenceWith, get, isEqual, set } from 'lodash'
|
||||
import { cloneDeep, differenceWith, get, isEqual, set } from 'lodash-es'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export const useAnnouncementsStore = defineStore('announcements', {
|
|||
userActions: {},
|
||||
}),
|
||||
getters: {
|
||||
unreadAnnouncementCount() {
|
||||
unreadAnnouncementsCount() {
|
||||
if (!useUsersStore().currentUser) {
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { find, remove } from 'lodash'
|
||||
import { find, remove } from 'lodash-es'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { orderBy, sumBy } from 'lodash'
|
||||
import { orderBy, sumBy } from 'lodash-es'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
||||
|
|
|
|||
76
src/stores/drafts.js
Normal file
76
src/stores/drafts.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
import { storage } from 'src/lib/storage.js'
|
||||
|
||||
const storageKey = 'pleroma-fe-drafts'
|
||||
|
||||
/*
|
||||
* Note: we do not use the persist state plugin because
|
||||
* it is not impossible for a user to have two windows at
|
||||
* the same time. The persist state plugin is just overriding
|
||||
* everything with the current state. This isn't good because
|
||||
* if a draft is created in one window and another draft is
|
||||
* created in another, the draft in the first window will just
|
||||
* be overriden.
|
||||
* Here, we can't guarantee 100% atomicity unless one uses
|
||||
* different keys, which will just pollute the whole storage.
|
||||
* It is indeed best to have backend support for this.
|
||||
*/
|
||||
const getStorageData = async () => await storage.getItem(storageKey)
|
||||
|
||||
const saveDraftToStorage = async (draft) => {
|
||||
const currentData = (await getStorageData()) ?? {}
|
||||
currentData[draft.id] = JSON.parse(JSON.stringify(draft))
|
||||
await storage.setItem(storageKey, currentData)
|
||||
}
|
||||
|
||||
const deleteDraftFromStorage = async (ids) => {
|
||||
const currentData = (await getStorageData()) ?? {}
|
||||
ids.forEach((id) => {
|
||||
delete currentData[id]
|
||||
})
|
||||
await storage.setItem(storageKey, currentData)
|
||||
}
|
||||
|
||||
export const useDraftsStore = defineStore('drafts', {
|
||||
state: () => ({
|
||||
drafts: new Map(),
|
||||
}),
|
||||
getters: {
|
||||
draftsByTypeAndRefId(state) {
|
||||
return (type, refId) => {
|
||||
return [...state.drafts.values()].filter(
|
||||
(draft) => draft.type === type && draft.refId === refId,
|
||||
)
|
||||
}
|
||||
},
|
||||
draftsArray(state) {
|
||||
return [...state.drafts.values()]
|
||||
},
|
||||
draftsCount(state) {
|
||||
return state.drafts.size
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async loadDrafts() {
|
||||
const currentData = (await getStorageData()) ?? {}
|
||||
this.drafts = new Map(Object.entries(currentData))
|
||||
},
|
||||
async addOrSaveDraft(draft) {
|
||||
const id = draft.id ?? new Date().getTime().toString()
|
||||
const draftWithId = { ...draft, id }
|
||||
this.drafts.set(id, draftWithId)
|
||||
await saveDraftToStorage(draftWithId)
|
||||
return id
|
||||
},
|
||||
async abandonDraft(id) {
|
||||
this.drafts.delete(id)
|
||||
await deleteDraftFromStorage([id])
|
||||
},
|
||||
async abandonAllDrafts() {
|
||||
const ids = [...this.drafts.keys()]
|
||||
ids.forEach((id) => this.drafts.delete(id))
|
||||
await deleteDraftFromStorage(ids)
|
||||
},
|
||||
},
|
||||
})
|
||||
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