Compare commits
15 Commits
develop
...
migrate/vu
Author | SHA1 | Date |
---|---|---|
Sean King | 5515f53794 | |
Sean King | 963e163858 | |
Sean King | 8eff081468 | |
Sean King | ad7d47f440 | |
Sean King | f9254e5fb7 | |
Sean King | e3ca5b0a32 | |
Sean King | 22ab848f6b | |
Sean King | b1dcea0199 | |
Sean King | 872569ae8e | |
Sean King | c25cfe540b | |
Sean King | 3430604dda | |
Sean King | 27e36dbc2e | |
Sean King | aa6c13f9e6 | |
Sean King | aa98e83ff0 | |
Sean King | edfaf5e80c |
|
@ -36,6 +36,7 @@
|
|||
"localforage": "1.10.0",
|
||||
"parse-link-header": "2.0.0",
|
||||
"phoenix": "1.6.2",
|
||||
"pinia": "^2.0.33",
|
||||
"punycode.js": "2.3.0",
|
||||
"qrcode": "1.5.0",
|
||||
"querystring-es3": "0.2.1",
|
||||
|
|
10
src/App.js
10
src/App.js
|
@ -17,6 +17,8 @@ import GlobalNoticeList from './components/global_notice_list/global_notice_list
|
|||
import { windowWidth, windowHeight } from './services/window_utils/window_utils'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { useShoutStore } from './stores/shout'
|
||||
import { useInterfaceStore } from './stores/interface'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
|
@ -86,7 +88,7 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
shout () { return this.$store.state.shout.joined },
|
||||
shout () { return useShoutStore().joined },
|
||||
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
|
||||
showInstanceSpecificPanel () {
|
||||
return this.$store.state.instance.showInstanceSpecificPanel &&
|
||||
|
@ -112,7 +114,7 @@ export default {
|
|||
hideShoutbox () {
|
||||
return this.$store.getters.mergedConfig.hideShoutbox
|
||||
},
|
||||
layoutType () { return this.$store.state.interface.layoutType },
|
||||
layoutType () { return useInterfaceStore().layoutType },
|
||||
privateMode () { return this.$store.state.instance.private },
|
||||
reverseLayout () {
|
||||
const { thirdColumnMode, sidebarRight: reverseSetting } = this.$store.getters.mergedConfig
|
||||
|
@ -128,8 +130,8 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
updateMobileState () {
|
||||
this.$store.dispatch('setLayoutWidth', windowWidth())
|
||||
this.$store.dispatch('setLayoutHeight', windowHeight())
|
||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
|||
import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
||||
import FaviconService from '../services/favicon_service/favicon_service.js'
|
||||
|
||||
import { useI18nStore } from '../stores/i18n'
|
||||
import { useInterfaceStore } from '../stores/interface'
|
||||
import { useAnnouncementsStore } from '../stores/announcements'
|
||||
|
||||
let staticInitialResults = null
|
||||
|
||||
const parsedInitialResults = () => {
|
||||
|
@ -338,9 +342,16 @@ const checkOAuthToken = async ({ store }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const afterStoreSetup = async ({ store, i18n }) => {
|
||||
store.dispatch('setLayoutWidth', windowWidth())
|
||||
store.dispatch('setLayoutHeight', windowHeight())
|
||||
const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
|
||||
const app = createApp(App)
|
||||
app.use(pinia)
|
||||
|
||||
if (storageError) {
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error' })
|
||||
}
|
||||
|
||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||
|
||||
FaviconService.initFaviconService()
|
||||
|
||||
|
@ -379,7 +390,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
|||
|
||||
// Start fetching things that don't need to block the UI
|
||||
store.dispatch('fetchMutes')
|
||||
store.dispatch('startFetchingAnnouncements')
|
||||
useAnnouncementsStore().startFetchingAnnouncements()
|
||||
getTOS({ store })
|
||||
getStickers({ store })
|
||||
|
||||
|
@ -394,7 +405,7 @@ const afterStoreSetup = async ({ store, i18n }) => {
|
|||
}
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
useI18nStore().setI18n(i18n)
|
||||
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
|
|
|
@ -7,6 +7,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
|||
import {
|
||||
faEllipsisV
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useReportsStore } from '../../stores/reports'
|
||||
|
||||
library.add(
|
||||
faEllipsisV
|
||||
|
@ -73,7 +74,7 @@ const AccountActions = {
|
|||
this.hideConfirmRemoveUserFromFollowers()
|
||||
},
|
||||
reportUser () {
|
||||
this.$store.dispatch('openUserReportingModal', { userId: this.user.id })
|
||||
useReportsStore().openUserReportingModal({ userId: this.user.id })
|
||||
},
|
||||
openChat () {
|
||||
this.$router.push({
|
||||
|
|
|
@ -2,6 +2,7 @@ import { mapState } from 'vuex'
|
|||
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
||||
import RichContent from '../rich_content/rich_content.jsx'
|
||||
import localeService from '../../services/locale/locale.service.js'
|
||||
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||
|
||||
const Announcement = {
|
||||
components: {
|
||||
|
@ -67,11 +68,11 @@ const Announcement = {
|
|||
methods: {
|
||||
markAsRead () {
|
||||
if (!this.isRead) {
|
||||
return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id)
|
||||
return useAnnouncementsStore().markAnnouncementAsRead(this.announcement.id)
|
||||
}
|
||||
},
|
||||
deleteAnnouncement () {
|
||||
return this.$store.dispatch('deleteAnnouncement', this.announcement.id)
|
||||
return useAnnouncementsStore().deleteAnnouncement(this.announcement.id)
|
||||
},
|
||||
formatTimeOrDate (time, locale) {
|
||||
const d = new Date(time)
|
||||
|
@ -85,7 +86,7 @@ const Announcement = {
|
|||
this.editing = true
|
||||
},
|
||||
submitEdit () {
|
||||
this.$store.dispatch('editAnnouncement', {
|
||||
useAnnouncementsStore().editAnnouncement({
|
||||
id: this.announcement.id,
|
||||
...this.editedAnnouncement
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { mapState } from 'vuex'
|
||||
import Announcement from '../announcement/announcement.vue'
|
||||
import AnnouncementEditor from '../announcement_editor/announcement_editor.vue'
|
||||
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||
|
||||
const AnnouncementsPage = {
|
||||
components: {
|
||||
|
@ -20,14 +21,14 @@ const AnnouncementsPage = {
|
|||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$store.dispatch('fetchAnnouncements')
|
||||
useAnnouncementsStore().fetchAnnouncements()
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser
|
||||
}),
|
||||
announcements () {
|
||||
return this.$store.state.announcements.announcements
|
||||
return useAnnouncementsStore().announcements
|
||||
},
|
||||
canPostAnnouncement () {
|
||||
return this.currentUser && this.currentUser.privileges.includes('announcements_manage_announcements')
|
||||
|
@ -36,7 +37,7 @@ const AnnouncementsPage = {
|
|||
methods: {
|
||||
postAnnouncement () {
|
||||
this.posting = true
|
||||
this.$store.dispatch('postAnnouncement', this.newAnnouncement)
|
||||
useAnnouncementsStore().postAnnouncement(this.newAnnouncement)
|
||||
.then(() => {
|
||||
this.newAnnouncement.content = ''
|
||||
this.startsAt = undefined
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
faPencilAlt,
|
||||
faAlignRight
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useMediaViewerStore } from '../../stores/media_viewer'
|
||||
|
||||
library.add(
|
||||
faFile,
|
||||
|
@ -147,14 +148,14 @@ const Attachment = {
|
|||
openModal (event) {
|
||||
if (this.useModal) {
|
||||
this.$emit('setMedia')
|
||||
this.$store.dispatch('setCurrentMedia', this.attachment)
|
||||
useMediaViewerStore().setCurrentMedia(this.attachment)
|
||||
} else if (this.type === 'unknown') {
|
||||
window.open(this.attachment.url)
|
||||
}
|
||||
},
|
||||
openModalForce (event) {
|
||||
this.$emit('setMedia')
|
||||
this.$store.dispatch('setCurrentMedia', this.attachment)
|
||||
useMediaViewerStore().setCurrentMedia(this.attachment)
|
||||
},
|
||||
onEdit (event) {
|
||||
this.edit && this.edit(this.attachment, event)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import _ from 'lodash'
|
||||
import { WSConnectionStatus } from '../../services/api/api.service.js'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import ChatMessage from '../chat_message/chat_message.vue'
|
||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||
import ChatTitle from '../chat_title/chat_title.vue'
|
||||
|
@ -13,6 +14,7 @@ import {
|
|||
faChevronLeft
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js'
|
||||
import { useInterfaceStore } from '../../stores/interface.js'
|
||||
|
||||
library.add(
|
||||
faChevronDown,
|
||||
|
@ -90,10 +92,12 @@ const Chat = {
|
|||
'findOpenedChatByRecipientId',
|
||||
'mergedConfig'
|
||||
]),
|
||||
...mapPiniaState(useInterfaceStore, {
|
||||
mobileLayout: store => store.layoutType === 'mobile'
|
||||
}),
|
||||
...mapState({
|
||||
backendInteractor: state => state.api.backendInteractor,
|
||||
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus,
|
||||
mobileLayout: state => state.interface.layoutType === 'mobile',
|
||||
currentUser: state => state.users.currentUser
|
||||
})
|
||||
},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { mapState, mapGetters } from 'vuex'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import Popover from '../popover/popover.vue'
|
||||
import Attachment from '../attachment/attachment.vue'
|
||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||
|
@ -12,6 +13,7 @@ import {
|
|||
faTimes,
|
||||
faEllipsisH
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faTimes,
|
||||
|
@ -65,8 +67,10 @@ const ChatMessage = {
|
|||
hasAttachment () {
|
||||
return this.message.attachments.length > 0
|
||||
},
|
||||
...mapPiniaState(useInterfaceStore, {
|
||||
betterShadow: store => store.browserSupport.cssFilter
|
||||
}),
|
||||
...mapState({
|
||||
betterShadow: state => state.interface.browserSupport.cssFilter,
|
||||
currentUser: state => state.users.currentUser,
|
||||
restrictedNicknames: state => state.instance.restrictedNicknames
|
||||
}),
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
faCog,
|
||||
faInfoCircle
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faSignInAlt,
|
||||
|
@ -107,7 +108,7 @@ export default {
|
|||
this.searchBarHidden = hidden
|
||||
},
|
||||
openSettingsModal () {
|
||||
this.$store.dispatch('openSettingsModal')
|
||||
useInterfaceStore().openSettingsModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import PostStatusForm from '../post_status_form/post_status_form.vue'
|
|||
import Modal from '../modal/modal.vue'
|
||||
import statusPosterService from '../../services/status_poster/status_poster.service.js'
|
||||
import get from 'lodash/get'
|
||||
import { useEditStatusStore } from '../../stores/editStatus'
|
||||
|
||||
const EditStatusModal = {
|
||||
components: {
|
||||
|
@ -18,13 +19,13 @@ const EditStatusModal = {
|
|||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
modalActivated () {
|
||||
return this.$store.state.editStatus.modalActivated
|
||||
return useEditStatusStore().modalActivated
|
||||
},
|
||||
isFormVisible () {
|
||||
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
||||
},
|
||||
params () {
|
||||
return this.$store.state.editStatus.params || {}
|
||||
return useEditStatusStore().params || {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -46,7 +47,7 @@ const EditStatusModal = {
|
|||
doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) {
|
||||
const params = {
|
||||
store: this.$store,
|
||||
statusId: this.$store.state.editStatus.params.statusId,
|
||||
statusId: useEditStatusStore().params.statusId,
|
||||
status,
|
||||
spoilerText,
|
||||
sensitive,
|
||||
|
@ -67,7 +68,7 @@ const EditStatusModal = {
|
|||
})
|
||||
},
|
||||
closeModal () {
|
||||
this.$store.dispatch('closeEditStatusModal')
|
||||
useEditStatusStore().closeEditStatusModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ import {
|
|||
faBookmark as faBookmarkReg,
|
||||
faFlag
|
||||
} from '@fortawesome/free-regular-svg-icons'
|
||||
import { useEditStatusStore } from '../../stores/editStatus'
|
||||
import { useStatusHistoryStore } from '../../stores/statusHistory'
|
||||
import { useReportsStore } from '../../stores/reports'
|
||||
|
||||
library.add(
|
||||
faEllipsisH,
|
||||
|
@ -103,11 +106,11 @@ const ExtraButtons = {
|
|||
.catch(err => this.$emit('onError', err.error.error))
|
||||
},
|
||||
reportStatus () {
|
||||
this.$store.dispatch('openUserReportingModal', { userId: this.status.user.id, statusIds: [this.status.id] })
|
||||
useReportsStore().openUserReportingModal({ userId: this.status.user.id, statusIds: [this.status.id] })
|
||||
},
|
||||
editStatus () {
|
||||
this.$store.dispatch('fetchStatusSource', { id: this.status.id })
|
||||
.then(data => this.$store.dispatch('openEditStatusModal', {
|
||||
.then(data => useEditStatusStore().openEditStatusModal({
|
||||
statusId: this.status.id,
|
||||
subject: data.spoiler_text,
|
||||
statusText: data.text,
|
||||
|
@ -122,7 +125,7 @@ const ExtraButtons = {
|
|||
const originalStatus = { ...this.status }
|
||||
const stripFieldsList = ['attachments', 'created_at', 'emojis', 'text', 'raw_html', 'nsfw', 'poll', 'summary', 'summary_raw_html']
|
||||
stripFieldsList.forEach(p => delete originalStatus[p])
|
||||
this.$store.dispatch('openStatusHistoryModal', originalStatus)
|
||||
useStatusHistoryStore().openStatusHistoryModal(originalStatus)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useMediaViewerStore } from '../../stores/media_viewer'
|
||||
import Attachment from '../attachment/attachment.vue'
|
||||
import { sumBy, set } from 'lodash'
|
||||
|
||||
|
@ -107,11 +108,11 @@ const Gallery = {
|
|||
this.hidingLong = event
|
||||
},
|
||||
openGallery () {
|
||||
this.$store.dispatch('setMedia', this.attachments)
|
||||
this.$store.dispatch('setCurrentMedia', this.attachments[0])
|
||||
useMediaViewerStore().setMedia(this.attachments)
|
||||
useMediaViewerStore().setCurrentMedia(this.attachments[0])
|
||||
},
|
||||
onMedia () {
|
||||
this.$store.dispatch('setMedia', this.attachments)
|
||||
useMediaViewerStore().setMedia(this.attachments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
|||
import {
|
||||
faTimes
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faTimes
|
||||
|
@ -10,12 +11,12 @@ library.add(
|
|||
const GlobalNoticeList = {
|
||||
computed: {
|
||||
notices () {
|
||||
return this.$store.state.interface.globalNotices
|
||||
return useInterfaceStore().globalNotices
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeNotice (notice) {
|
||||
this.$store.dispatch('removeGlobalNotice', notice)
|
||||
useInterfaceStore().removeGlobalNotice(notice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useListsStore } from '../../stores/lists'
|
||||
import ListsCard from '../lists_card/lists_card.vue'
|
||||
|
||||
const Lists = {
|
||||
|
@ -11,7 +12,7 @@ const Lists = {
|
|||
},
|
||||
computed: {
|
||||
lists () {
|
||||
return this.$store.state.lists.allLists
|
||||
return useListsStore().allLists
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { mapState, mapGetters } from 'vuex'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||
import ListsUserSearch from '../lists_user_search/lists_user_search.vue'
|
||||
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
|
||||
|
@ -9,6 +10,8 @@ import {
|
|||
faSearch,
|
||||
faChevronLeft
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
import { useListsStore } from '../../stores/lists'
|
||||
|
||||
library.add(
|
||||
faSearch,
|
||||
|
@ -37,12 +40,12 @@ const ListsNew = {
|
|||
},
|
||||
created () {
|
||||
if (!this.id) return
|
||||
this.$store.dispatch('fetchList', { listId: this.id })
|
||||
useListsStore().fetchList({ listId: this.id })
|
||||
.then(() => {
|
||||
this.title = this.findListTitle(this.id)
|
||||
this.titleDraft = this.title
|
||||
})
|
||||
this.$store.dispatch('fetchListAccounts', { listId: this.id })
|
||||
useListsStore().fetchListAccounts({ listId: this.id })
|
||||
.then(() => {
|
||||
this.membersUserIds = this.findListAccounts(this.id)
|
||||
this.membersUserIds.forEach(userId => {
|
||||
|
@ -64,7 +67,8 @@ const ListsNew = {
|
|||
...mapState({
|
||||
currentUser: state => state.users.currentUser
|
||||
}),
|
||||
...mapGetters(['findUser', 'findListTitle', 'findListAccounts'])
|
||||
...mapPiniaState(useListsStore, ['findListTitle', 'findListAccounts']),
|
||||
...mapGetters(['findUser'])
|
||||
},
|
||||
methods: {
|
||||
onInput () {
|
||||
|
@ -95,10 +99,10 @@ const ListsNew = {
|
|||
return this.addedUserIds.has(user.id)
|
||||
},
|
||||
addUser (user) {
|
||||
this.$store.dispatch('addListAccount', { accountId: user.id, listId: this.id })
|
||||
useListsStore().addListAccount({ accountId: user.id, listId: this.id })
|
||||
},
|
||||
removeUser (userId) {
|
||||
this.$store.dispatch('removeListAccount', { accountId: userId, listId: this.id })
|
||||
useListsStore().removeListAccount({ accountId: userId, listId: this.id })
|
||||
},
|
||||
onSearchLoading (results) {
|
||||
this.searchLoading = true
|
||||
|
@ -111,24 +115,23 @@ const ListsNew = {
|
|||
this.searchUserIds = results
|
||||
},
|
||||
updateListTitle () {
|
||||
this.$store.dispatch('setList', { listId: this.id, title: this.titleDraft })
|
||||
useListsStore().setList({ listId: this.id, title: this.titleDraft })
|
||||
.then(() => {
|
||||
this.title = this.findListTitle(this.id)
|
||||
})
|
||||
},
|
||||
createList () {
|
||||
this.$store.dispatch('createList', { title: this.titleDraft })
|
||||
useListsStore().createList({ title: this.titleDraft })
|
||||
.then((list) => {
|
||||
return this
|
||||
.$store
|
||||
.dispatch('setListAccounts', { listId: list.id, accountIds: [...this.addedUserIds] })
|
||||
return useListsStore()
|
||||
.setListAccounts({ listId: list.id, accountIds: [...this.addedUserIds] })
|
||||
.then(() => list.id)
|
||||
})
|
||||
.then((listId) => {
|
||||
this.$router.push({ name: 'lists-timeline', params: { id: listId } })
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$store.dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'lists.error',
|
||||
messageArgs: [e.message],
|
||||
level: 'error'
|
||||
|
@ -136,7 +139,7 @@ const ListsNew = {
|
|||
})
|
||||
},
|
||||
deleteList () {
|
||||
this.$store.dispatch('deleteList', { listId: this.id })
|
||||
useListsStore().deleteList({ listId: this.id })
|
||||
this.$router.push({ name: 'lists' })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
||||
import { getListEntries } from 'src/components/navigation/filter.js'
|
||||
import { useListsStore } from '../../stores/lists'
|
||||
|
||||
export const ListsMenuContent = {
|
||||
props: [
|
||||
|
@ -10,8 +12,10 @@ export const ListsMenuContent = {
|
|||
NavigationEntry
|
||||
},
|
||||
computed: {
|
||||
...mapPiniaState(useListsStore, {
|
||||
lists: getListEntries
|
||||
}),
|
||||
...mapState({
|
||||
lists: getListEntries,
|
||||
currentUser: state => state.users.currentUser,
|
||||
privateMode: state => state.instance.private,
|
||||
federating: state => state.instance.federating
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useListsStore } from '../../stores/lists'
|
||||
import Timeline from '../timeline/timeline.vue'
|
||||
const ListsTimeline = {
|
||||
data () {
|
||||
|
@ -17,14 +18,14 @@ const ListsTimeline = {
|
|||
this.listId = route.params.id
|
||||
this.$store.dispatch('stopFetchingTimeline', 'list')
|
||||
this.$store.commit('clearTimeline', { timeline: 'list' })
|
||||
this.$store.dispatch('fetchList', { listId: this.listId })
|
||||
useListsStore().fetchList({ listId: this.listId })
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId })
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.listId = this.$route.params.id
|
||||
this.$store.dispatch('fetchList', { listId: this.listId })
|
||||
useListsStore().fetchList({ listId: this.listId })
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId })
|
||||
},
|
||||
unmounted () {
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
faCircleNotch,
|
||||
faTimes
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useMediaViewerStore } from '../../stores/media_viewer'
|
||||
|
||||
library.add(
|
||||
faChevronLeft,
|
||||
|
@ -44,16 +45,16 @@ const MediaModal = {
|
|||
},
|
||||
computed: {
|
||||
showing () {
|
||||
return this.$store.state.mediaViewer.activated
|
||||
return useMediaViewerStore().activated
|
||||
},
|
||||
media () {
|
||||
return this.$store.state.mediaViewer.media
|
||||
return useMediaViewerStore().media
|
||||
},
|
||||
description () {
|
||||
return this.currentMedia.description
|
||||
},
|
||||
currentIndex () {
|
||||
return this.$store.state.mediaViewer.currentIndex
|
||||
return useMediaViewerStore().currentIndex
|
||||
},
|
||||
currentMedia () {
|
||||
return this.media[this.currentIndex]
|
||||
|
@ -79,7 +80,7 @@ const MediaModal = {
|
|||
// to be processed on the content below the overlay
|
||||
const transitionTime = 100 // ms
|
||||
setTimeout(() => {
|
||||
this.$store.dispatch('closeMediaViewer')
|
||||
useMediaViewerStore().closeMediaViewer()
|
||||
}, transitionTime)
|
||||
},
|
||||
hideIfNotSwiped (event) {
|
||||
|
@ -98,7 +99,7 @@ const MediaModal = {
|
|||
if (this.getType(newMedia) === 'image') {
|
||||
this.loading = true
|
||||
}
|
||||
this.$store.dispatch('setCurrentMedia', newMedia)
|
||||
useMediaViewerStore().setCurrentMedia(newMedia)
|
||||
}
|
||||
},
|
||||
goNext () {
|
||||
|
@ -108,7 +109,7 @@ const MediaModal = {
|
|||
if (this.getType(newMedia) === 'image') {
|
||||
this.loading = true
|
||||
}
|
||||
this.$store.dispatch('setCurrentMedia', newMedia)
|
||||
useMediaViewerStore().setCurrentMedia(newMedia)
|
||||
}
|
||||
},
|
||||
onImageLoaded () {
|
||||
|
|
|
@ -5,6 +5,7 @@ import { unseenNotificationsFromStore } from '../../services/notification_utils/
|
|||
import GestureService from '../../services/gesture_service/gesture_service'
|
||||
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faTimes,
|
||||
|
@ -13,6 +14,7 @@ import {
|
|||
faArrowUp,
|
||||
faMinus
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||
|
||||
library.add(
|
||||
faTimes,
|
||||
|
@ -57,7 +59,8 @@ const MobileNav = {
|
|||
isChat () {
|
||||
return this.$route.name === 'chat'
|
||||
},
|
||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']),
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||
...mapGetters(['unreadChatCount']),
|
||||
chatsPinned () {
|
||||
return new Set(this.$store.state.serverSideStorage.prefsStorage.collections.pinnedNavItems).has('chats')
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
|||
import {
|
||||
faPen
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { usePostStatusStore } from '../../stores/postStatus'
|
||||
|
||||
library.add(
|
||||
faPen
|
||||
|
@ -71,7 +72,7 @@ const MobilePostStatusButton = {
|
|||
window.removeEventListener('scroll', this.handleScrollEnd)
|
||||
},
|
||||
openPostForm () {
|
||||
this.$store.dispatch('openPostStatusModal')
|
||||
usePostStatusStore().openPostStatusModal()
|
||||
},
|
||||
handleOSK () {
|
||||
// This is a big hack: we're guessing from changed window sizes if the
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ListsMenuContent from 'src/components/lists_menu/lists_menu_content.vue'
|
||||
import { mapState, mapGetters } from 'vuex'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { TIMELINES, ROOT_ITEMS } from 'src/components/navigation/navigation.js'
|
||||
import { filterNavigation } from 'src/components/navigation/filter.js'
|
||||
import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
|
||||
|
@ -21,6 +22,7 @@ import {
|
|||
faList,
|
||||
faBullhorn
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||
|
||||
library.add(
|
||||
faUsers,
|
||||
|
@ -82,13 +84,16 @@ const NavPanel = {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
...mapPiniaState(useAnnouncementsStore, {
|
||||
unreadAnnouncementCount: 'unreadAnnouncementCount',
|
||||
supportsAnnouncements: store => store.supportsAnnouncements
|
||||
}),
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser,
|
||||
followRequestCount: state => state.api.followRequests.length,
|
||||
privateMode: state => state.instance.private,
|
||||
federating: state => state.instance.federating,
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
||||
supportsAnnouncements: state => state.announcements.supportsAnnouncements,
|
||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems),
|
||||
collapsed: state => state.serverSideStorage.prefsStorage.simple.collapseNav
|
||||
}),
|
||||
|
@ -120,7 +125,7 @@ const NavPanel = {
|
|||
}
|
||||
)
|
||||
},
|
||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
|
||||
...mapGetters(['unreadChatCount'])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export const filterNavigation = (list = [], { hasChats, hasAnnouncements, isFede
|
|||
})
|
||||
}
|
||||
|
||||
export const getListEntries = state => state.lists.allLists.map(list => ({
|
||||
export const getListEntries = store => store.allLists.map(list => ({
|
||||
name: 'list-' + list.id,
|
||||
routeObject: { name: 'lists-timeline', params: { id: list.id } },
|
||||
labelRaw: list.title,
|
||||
|
|
|
@ -76,6 +76,7 @@ export const ROOT_ITEMS = {
|
|||
route: 'announcements',
|
||||
icon: 'bullhorn',
|
||||
label: 'nav.announcements',
|
||||
store: 'announcements',
|
||||
badgeGetter: 'unreadAnnouncementCount',
|
||||
criteria: ['announcements']
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import { routeTo } from 'src/components/navigation/navigation.js'
|
|||
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
||||
import { mapStores } from 'pinia'
|
||||
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||
|
||||
library.add(faThumbtack)
|
||||
|
||||
|
@ -31,6 +33,7 @@ const NavigationEntry = {
|
|||
getters () {
|
||||
return this.$store.getters
|
||||
},
|
||||
...mapStores(useAnnouncementsStore),
|
||||
...mapState({
|
||||
currentUser: state => state.users.currentUser,
|
||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
|
||||
|
|
|
@ -39,6 +39,12 @@
|
|||
>
|
||||
{{ 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] }}
|
||||
</div>
|
||||
<button
|
||||
v-if="showPin && currentUser"
|
||||
type="button"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { TIMELINES, ROOT_ITEMS, routeTo } from 'src/components/navigation/navigation.js'
|
||||
import { getListEntries, filterNavigation } from 'src/components/navigation/filter.js'
|
||||
|
||||
|
@ -14,6 +15,7 @@ import {
|
|||
faStream,
|
||||
faList
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useListsStore } from '../../stores/lists'
|
||||
|
||||
library.add(
|
||||
faUsers,
|
||||
|
@ -38,8 +40,10 @@ const NavPanel = {
|
|||
getters () {
|
||||
return this.$store.getters
|
||||
},
|
||||
...mapPiniaState(useListsStore, {
|
||||
lists: getListEntries
|
||||
}),
|
||||
...mapState({
|
||||
lists: getListEntries,
|
||||
currentUser: state => state.users.currentUser,
|
||||
followRequestCount: state => state.api.followRequests.length,
|
||||
privateMode: state => state.instance.private,
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
faExpandAlt,
|
||||
faCompressAlt
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faCheck,
|
||||
|
@ -43,7 +44,7 @@ const Notification = {
|
|||
data () {
|
||||
return {
|
||||
statusExpanded: false,
|
||||
betterShadow: this.$store.state.interface.browserSupport.cssFilter,
|
||||
betterShadow: useInterfaceStore().browserSupport.cssFilter,
|
||||
unmuted: false,
|
||||
showingApproveConfirmDialog: false,
|
||||
showingDenyConfirmDialog: false
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { computed } from 'vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
import Notification from '../notification/notification.vue'
|
||||
import NotificationFilters from './notification_filters.vue'
|
||||
import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js'
|
||||
|
@ -11,6 +12,8 @@ import {
|
|||
import FaviconService from '../../services/favicon_service/favicon_service.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||
|
||||
library.add(
|
||||
faCircleNotch,
|
||||
|
@ -75,11 +78,11 @@ const Notifications = {
|
|||
return this.$store.state.statuses.notifications.loading
|
||||
},
|
||||
noHeading () {
|
||||
const { layoutType } = this.$store.state.interface
|
||||
const { layoutType } = useInterfaceStore()
|
||||
return this.minimalMode || layoutType === 'mobile'
|
||||
},
|
||||
teleportTarget () {
|
||||
const { layoutType } = this.$store.state.interface
|
||||
const { layoutType } = useInterfaceStore()
|
||||
const map = {
|
||||
wide: '#notifs-column',
|
||||
mobile: '#mobile-notifications'
|
||||
|
@ -87,14 +90,15 @@ const Notifications = {
|
|||
return map[layoutType] || '#notifs-sidebar'
|
||||
},
|
||||
popoversZLayer () {
|
||||
const { layoutType } = this.$store.state.interface
|
||||
const { layoutType } = useInterfaceStore()
|
||||
return layoutType === 'mobile' ? 'navbar' : null
|
||||
},
|
||||
notificationsToDisplay () {
|
||||
return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount)
|
||||
},
|
||||
noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders },
|
||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
|
||||
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
|
||||
...mapGetters(['unreadChatCount'])
|
||||
},
|
||||
mounted () {
|
||||
this.scrollerRef = this.$refs.root.closest('.column.-scrollable')
|
||||
|
@ -114,10 +118,10 @@ const Notifications = {
|
|||
unseenCountTitle (count) {
|
||||
if (count > 0) {
|
||||
FaviconService.drawFaviconBadge()
|
||||
this.$store.dispatch('setPageTitle', `(${count})`)
|
||||
useInterfaceStore().setPageTitle(`(${count})`)
|
||||
} else {
|
||||
FaviconService.clearFaviconBadge()
|
||||
this.$store.dispatch('setPageTitle', '')
|
||||
useInterfaceStore().setPageTitle('')
|
||||
}
|
||||
},
|
||||
teleportTarget () {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Timeago from 'components/timeago/timeago.vue'
|
||||
import RichContent from 'components/rich_content/rich_content.jsx'
|
||||
import { forEach, map } from 'lodash'
|
||||
import { usePollsStore } from '../../stores/polls'
|
||||
|
||||
export default {
|
||||
name: 'Poll',
|
||||
|
@ -17,20 +18,20 @@ export default {
|
|||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.$store.state.polls.pollsObject[this.pollId]) {
|
||||
this.$store.dispatch('mergeOrAddPoll', this.basePoll)
|
||||
if (!usePollsStore().pollsObject[this.pollId]) {
|
||||
usePollsStore().mergeOrAddPoll(this.basePoll)
|
||||
}
|
||||
this.$store.dispatch('trackPoll', this.pollId)
|
||||
usePollsStore().trackPoll(this.pollId)
|
||||
},
|
||||
unmounted () {
|
||||
this.$store.dispatch('untrackPoll', this.pollId)
|
||||
usePollsStore().untrackPoll(this.pollId)
|
||||
},
|
||||
computed: {
|
||||
pollId () {
|
||||
return this.basePoll.id
|
||||
},
|
||||
poll () {
|
||||
const storePoll = this.$store.state.polls.pollsObject[this.pollId]
|
||||
const storePoll = usePollsStore().pollsObject[this.pollId]
|
||||
return storePoll || {}
|
||||
},
|
||||
options () {
|
||||
|
@ -76,9 +77,6 @@ export default {
|
|||
resultTitle (option) {
|
||||
return `${option.votes_count}/${this.totalVotesCount} ${this.$t('polls.votes')}`
|
||||
},
|
||||
fetchPoll () {
|
||||
this.$store.dispatch('refreshPoll', { id: this.statusId, pollId: this.poll.id })
|
||||
},
|
||||
activateOption (index) {
|
||||
// forgive me father: doing checking the radio/checkboxes
|
||||
// in code because of customized input elements need either
|
||||
|
@ -106,8 +104,7 @@ export default {
|
|||
vote () {
|
||||
if (this.choiceIndices.length === 0) return
|
||||
this.loading = true
|
||||
this.$store.dispatch(
|
||||
'votePoll',
|
||||
usePollsStore().votePoll(
|
||||
{ id: this.statusId, pollId: this.poll.id, choices: this.choiceIndices }
|
||||
).then(poll => {
|
||||
this.loading = false
|
||||
|
|
|
@ -11,7 +11,8 @@ import { findOffset } from '../../services/offset_finder/offset_finder.service.j
|
|||
import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
|
||||
import { reject, map, uniqBy, debounce } from 'lodash'
|
||||
import suggestor from '../emoji_input/suggestor.js'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
import Checkbox from '../checkbox/checkbox.vue'
|
||||
import Select from '../select/select.vue'
|
||||
|
||||
|
@ -24,6 +25,7 @@ import {
|
|||
faTimes,
|
||||
faCircleNotch
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface.js'
|
||||
|
||||
library.add(
|
||||
faSmileBeam,
|
||||
|
@ -266,8 +268,8 @@ const PostStatusForm = {
|
|||
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
|
||||
},
|
||||
...mapGetters(['mergedConfig']),
|
||||
...mapState({
|
||||
mobileLayout: state => state.interface.mobileLayout
|
||||
...mapState(useInterfaceStore, {
|
||||
mobileLayout: store => store.mobileLayout
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
|
@ -629,7 +631,7 @@ const PostStatusForm = {
|
|||
this.idempotencyKey = Date.now().toString()
|
||||
},
|
||||
openProfileTab () {
|
||||
this.$store.dispatch('openSettingsModalTab', 'profile')
|
||||
useInterfaceStore().openSettingsModalTab('profile')
|
||||
},
|
||||
propsToNative (props) {
|
||||
return propsToNative(props)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||
import Modal from '../modal/modal.vue'
|
||||
import get from 'lodash/get'
|
||||
import { usePostStatusStore } from '../../stores/postStatus'
|
||||
|
||||
const PostStatusModal = {
|
||||
components: {
|
||||
|
@ -17,13 +18,13 @@ const PostStatusModal = {
|
|||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
modalActivated () {
|
||||
return this.$store.state.postStatus.modalActivated
|
||||
return usePostStatusStore().modalActivated
|
||||
},
|
||||
isFormVisible () {
|
||||
return this.isLoggedIn && !this.resettingForm && this.modalActivated
|
||||
},
|
||||
params () {
|
||||
return this.$store.state.postStatus.params || {}
|
||||
return usePostStatusStore().params || {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -43,7 +44,7 @@ const PostStatusModal = {
|
|||
},
|
||||
methods: {
|
||||
closeModal () {
|
||||
this.$store.dispatch('closePostStatusModal')
|
||||
usePostStatusStore().closePostStatusModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import Popover from '../popover/popover.vue'
|
|||
import { mapGetters } from 'vuex'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faFilter,
|
||||
|
@ -22,7 +23,7 @@ const QuickFilterSettings = {
|
|||
this.$store.dispatch('queueFlushAll')
|
||||
},
|
||||
openTab (tab) {
|
||||
this.$store.dispatch('openSettingsModalTab', tab)
|
||||
useInterfaceStore().openSettingsModalTab(tab)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -2,6 +2,7 @@ import Popover from '../popover/popover.vue'
|
|||
import { mapGetters } from 'vuex'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faList, faFolderTree, faBars, faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faList,
|
||||
|
@ -22,7 +23,7 @@ const QuickViewSettings = {
|
|||
this.$store.dispatch('setOption', { name: 'conversationDisplay', value: visibility })
|
||||
},
|
||||
openTab (tab) {
|
||||
this.$store.dispatch('openSettingsModalTab', tab)
|
||||
useInterfaceStore().openSettingsModalTab(tab)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useReportsStore } from '../../stores/reports'
|
||||
import Select from '../select/select.vue'
|
||||
import StatusContent from '../status_content/status_content.vue'
|
||||
import Timeago from '../timeago/timeago.vue'
|
||||
|
@ -14,7 +15,7 @@ const Report = {
|
|||
},
|
||||
computed: {
|
||||
report () {
|
||||
return this.$store.state.reports.reports[this.reportId] || {}
|
||||
return useReportsStore().reports[this.reportId] || {}
|
||||
},
|
||||
state: {
|
||||
get: function () { return this.report.state },
|
||||
|
@ -26,7 +27,7 @@ const Report = {
|
|||
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
||||
},
|
||||
setReportState (state) {
|
||||
return this.$store.dispatch('setReportState', { id: this.report.id, state })
|
||||
return useReportsStore().setReportState({ id: this.report.id, state })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
import {
|
||||
faWindowMinimize
|
||||
} from '@fortawesome/free-regular-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1
|
||||
const PLEROMAFE_SETTINGS_MINOR_VERSION = 0
|
||||
|
@ -64,10 +65,10 @@ const SettingsModal = {
|
|||
},
|
||||
methods: {
|
||||
closeModal () {
|
||||
this.$store.dispatch('closeSettingsModal')
|
||||
useInterfaceStore().closeSettingsModal()
|
||||
},
|
||||
peekModal () {
|
||||
this.$store.dispatch('togglePeekSettingsModal')
|
||||
useInterfaceStore().togglePeekSettingsModal()
|
||||
},
|
||||
importValidator (data) {
|
||||
if (!Array.isArray(data._pleroma_settings_version)) {
|
||||
|
@ -99,7 +100,7 @@ const SettingsModal = {
|
|||
}
|
||||
|
||||
if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) {
|
||||
this.$store.dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'warning',
|
||||
messageKey: 'settings.file_export_import.errors.file_slightly_new'
|
||||
})
|
||||
|
@ -109,9 +110,9 @@ const SettingsModal = {
|
|||
},
|
||||
onImportFailure (result) {
|
||||
if (result.error) {
|
||||
this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_settings_imported', level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_settings_imported', level: 'error' })
|
||||
} else {
|
||||
this.$store.dispatch('pushGlobalNotice', { ...result.validationResult, level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({ ...result.validationResult, level: 'error' })
|
||||
}
|
||||
},
|
||||
onImport (data) {
|
||||
|
@ -151,16 +152,16 @@ const SettingsModal = {
|
|||
},
|
||||
computed: {
|
||||
currentSaveStateNotice () {
|
||||
return this.$store.state.interface.settings.currentSaveStateNotice
|
||||
return useInterfaceStore().settings.currentSaveStateNotice
|
||||
},
|
||||
modalActivated () {
|
||||
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||
return useInterfaceStore().settingsModalState !== 'hidden'
|
||||
},
|
||||
modalOpenedOnce () {
|
||||
return this.$store.state.interface.settingsModalLoaded
|
||||
return useInterfaceStore().settingsModalLoaded
|
||||
},
|
||||
modalPeeked () {
|
||||
return this.$store.state.interface.settingsModalState === 'minimized'
|
||||
return useInterfaceStore().settingsModalState === 'minimized'
|
||||
},
|
||||
expertLevel: {
|
||||
get () {
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
faEyeSlash,
|
||||
faInfo
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faWrench,
|
||||
|
@ -52,15 +53,15 @@ const SettingsModalContent = {
|
|||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
open () {
|
||||
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||
return useInterfaceStore().settingsModalState !== 'hidden'
|
||||
},
|
||||
bodyLock () {
|
||||
return this.$store.state.interface.settingsModalState === 'visible'
|
||||
return useInterfaceStore().settingsModalState === 'visible'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen () {
|
||||
const targetTab = this.$store.state.interface.settingsModalTargetTab
|
||||
const targetTab = useInterfaceStore().settingsModalTargetTab
|
||||
// We're being told to open in specific tab
|
||||
if (targetTab) {
|
||||
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {
|
||||
|
@ -72,7 +73,7 @@ const SettingsModalContent = {
|
|||
}
|
||||
// Clear the state of target tab, so that next time settings is opened
|
||||
// it doesn't force it.
|
||||
this.$store.dispatch('clearSettingsModalTargetTab')
|
||||
useInterfaceStore().clearSettingsModalTargetTab()
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
faPlus,
|
||||
faCircleNotch
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faTimes,
|
||||
|
@ -166,7 +167,7 @@ const ProfileTab = {
|
|||
if (file.size > this.$store.state.instance[slot + 'limit']) {
|
||||
const filesize = fileSizeFormatService.fileSizeFormat(file.size)
|
||||
const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
|
||||
this.$store.dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'upload.error.message',
|
||||
messageArgs: [
|
||||
this.$t('upload.error.file_too_big', {
|
||||
|
@ -257,7 +258,7 @@ const ProfileTab = {
|
|||
.finally(() => { this.backgroundUploading = false })
|
||||
},
|
||||
displayUploadError (error) {
|
||||
this.$store.dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'upload.error.message',
|
||||
messageArgs: [error.message],
|
||||
level: 'error'
|
||||
|
|
|
@ -38,6 +38,7 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
|
|||
import Select from 'src/components/select/select.vue'
|
||||
|
||||
import Preview from './preview.vue'
|
||||
import { useInterfaceStore } from '../../../../stores/interface'
|
||||
|
||||
// List of color values used in v1
|
||||
const v1OnlyNames = [
|
||||
|
@ -548,7 +549,7 @@ export default {
|
|||
this.loadTheme(parsed, 'file', forceSource)
|
||||
},
|
||||
onImportFailure (result) {
|
||||
this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
},
|
||||
importValidator (parsed) {
|
||||
const version = parsed._pleroma_theme_version
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
faBullhorn,
|
||||
faTimes
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useShoutStore } from '../../stores/shout'
|
||||
|
||||
library.add(
|
||||
faBullhorn,
|
||||
|
@ -21,12 +22,12 @@ const shoutPanel = {
|
|||
},
|
||||
computed: {
|
||||
messages () {
|
||||
return this.$store.state.shout.messages
|
||||
return useShoutStore().messages
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit (message) {
|
||||
this.$store.state.shout.channel.push('new_msg', { text: message }, 10000)
|
||||
useShoutStore().channel.push('new_msg', { text: message }, 10000)
|
||||
this.currentMessage = ''
|
||||
},
|
||||
togglePanel () {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { mapState, mapGetters } from 'vuex'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import UserCard from '../user_card/user_card.vue'
|
||||
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
|
||||
import GestureService from '../../services/gesture_service/gesture_service'
|
||||
|
@ -19,6 +20,9 @@ import {
|
|||
faCompass,
|
||||
faList
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useShoutStore } from '../../stores/shout'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
import { useAnnouncementsStore } from '../../stores/announcements'
|
||||
|
||||
library.add(
|
||||
faSignInAlt,
|
||||
|
@ -54,7 +58,7 @@ const SideDrawer = {
|
|||
currentUser () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
shout () { return this.$store.state.shout.joined },
|
||||
shout () { return useShoutStore().joined },
|
||||
unseenNotifications () {
|
||||
return unseenNotificationsFromStore(this.$store)
|
||||
},
|
||||
|
@ -84,8 +88,8 @@ const SideDrawer = {
|
|||
},
|
||||
timelinesRoute () {
|
||||
let name
|
||||
if (this.$store.state.interface.lastTimeline) {
|
||||
name = this.$store.state.interface.lastTimeline
|
||||
if (useInterfaceStore().lastTimeline) {
|
||||
name = useInterfaceStore().lastTimeline
|
||||
}
|
||||
name = this.currentUser ? 'friends' : 'public-timeline'
|
||||
if (USERNAME_ROUTES.has(name)) {
|
||||
|
@ -94,11 +98,14 @@ const SideDrawer = {
|
|||
return { name }
|
||||
}
|
||||
},
|
||||
...mapState({
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
||||
supportsAnnouncements: state => state.announcements.supportsAnnouncements
|
||||
...mapPiniaState(useAnnouncementsStore, {
|
||||
supportsAnnouncements: store => store.supportsAnnouncements,
|
||||
unreadAnnouncementCount: 'unreadAnnouncementCount'
|
||||
}),
|
||||
...mapGetters(['unreadChatCount', 'unreadAnnouncementCount'])
|
||||
...mapState({
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable
|
||||
}),
|
||||
...mapGetters(['unreadChatCount'])
|
||||
},
|
||||
methods: {
|
||||
toggleDrawer () {
|
||||
|
@ -115,7 +122,7 @@ const SideDrawer = {
|
|||
GestureService.updateSwipe(e, this.closeGesture)
|
||||
},
|
||||
openSettingsModal () {
|
||||
this.$store.dispatch('openSettingsModal')
|
||||
useInterfaceStore().openSettingsModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p
|
|||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||
import { muteWordHits } from '../../services/status_parser/status_parser.js'
|
||||
import { unescape, uniqBy } from 'lodash'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
@ -379,7 +380,7 @@ const Status = {
|
|||
return this.$store.state.users.currentUser
|
||||
},
|
||||
betterShadow () {
|
||||
return this.$store.state.interface.browserSupport.cssFilter
|
||||
return useInterfaceStore().browserSupport.cssFilter
|
||||
},
|
||||
mergedConfig () {
|
||||
return this.$store.getters.mergedConfig
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
faLink,
|
||||
faPollH
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useMediaViewerStore } from '../../stores/media_viewer'
|
||||
|
||||
library.add(
|
||||
faCircleNotch,
|
||||
|
@ -123,7 +124,7 @@ const StatusContent = {
|
|||
},
|
||||
setMedia () {
|
||||
const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments
|
||||
return () => this.$store.dispatch('setMedia', attachments)
|
||||
return () => useMediaViewerStore().setMedia(attachments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { get } from 'lodash'
|
||||
import Modal from '../modal/modal.vue'
|
||||
import Status from '../status/status.vue'
|
||||
import { useStatusHistoryStore } from '../../stores/statusHistory'
|
||||
|
||||
const StatusHistoryModal = {
|
||||
components: {
|
||||
|
@ -14,10 +15,10 @@ const StatusHistoryModal = {
|
|||
},
|
||||
computed: {
|
||||
modalActivated () {
|
||||
return this.$store.state.statusHistory.modalActivated
|
||||
return useStatusHistoryStore().modalActivated
|
||||
},
|
||||
params () {
|
||||
return this.$store.state.statusHistory.params
|
||||
return useStatusHistoryStore().params
|
||||
},
|
||||
statusId () {
|
||||
return this.params.id
|
||||
|
@ -52,7 +53,7 @@ const StatusHistoryModal = {
|
|||
})
|
||||
},
|
||||
closeModal () {
|
||||
this.$store.dispatch('closeStatusHistoryModal')
|
||||
useStatusHistoryStore().closeStatusHistoryModal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// eslint-disable-next-line no-unused
|
||||
import { h, Fragment } from 'vue'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
||||
|
||||
import './tab_switcher.scss'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
const findFirstUsable = (slots) => slots.findIndex(_ => _.props)
|
||||
|
||||
|
@ -64,8 +65,8 @@ export default {
|
|||
settingsModalVisible () {
|
||||
return this.settingsModalState === 'visible'
|
||||
},
|
||||
...mapState({
|
||||
settingsModalState: state => state.interface.settingsModalState
|
||||
...mapState(useInterfaceStore, {
|
||||
settingsModalState: store => store.settingsModalState
|
||||
})
|
||||
},
|
||||
beforeUpdate () {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Status from '../status/status.vue'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
|
||||
import Conversation from '../conversation/conversation.vue'
|
||||
import TimelineMenu from '../timeline_menu/timeline_menu.vue'
|
||||
|
@ -8,6 +8,7 @@ import QuickViewSettings from '../quick_view_settings/quick_view_settings.vue'
|
|||
import { debounce, throttle, keyBy } from 'lodash'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faCircleNotch,
|
||||
|
@ -101,8 +102,8 @@ const Timeline = {
|
|||
virtualScrollingEnabled () {
|
||||
return this.$store.getters.mergedConfig.virtualScrolling
|
||||
},
|
||||
...mapState({
|
||||
mobileLayout: state => state.interface.layoutType === 'mobile'
|
||||
...mapState(useInterfaceStore, {
|
||||
mobileLayout: store => store.layoutType === 'mobile'
|
||||
})
|
||||
},
|
||||
created () {
|
||||
|
|
|
@ -8,6 +8,8 @@ import { filterNavigation } from 'src/components/navigation/filter.js'
|
|||
import {
|
||||
faChevronDown
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
import { useListsStore } from '../../stores/lists'
|
||||
|
||||
library.add(faChevronDown)
|
||||
|
||||
|
@ -36,7 +38,7 @@ const TimelineMenu = {
|
|||
},
|
||||
created () {
|
||||
if (timelineNames()[this.$route.name]) {
|
||||
this.$store.dispatch('setLastTimeline', this.$route.name)
|
||||
useInterfaceStore().setLastTimeline(this.$route.name)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -86,7 +88,7 @@ const TimelineMenu = {
|
|||
return '#' + this.$route.params.tag
|
||||
}
|
||||
if (route === 'lists-timeline') {
|
||||
return this.$store.getters.findListTitle(this.$route.params.id)
|
||||
return useListsStore().findListTitle(this.$route.params.id)
|
||||
}
|
||||
const i18nkey = timelineNames()[this.$route.name]
|
||||
return i18nkey ? this.$t(i18nkey) : route
|
||||
|
|
|
@ -12,6 +12,7 @@ import RichContent from 'src/components/rich_content/rich_content.jsx'
|
|||
import ConfirmModal from '../confirm_modal/confirm_modal.vue'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { usePostStatusStore } from '../../stores/postStatus'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faBell,
|
||||
|
@ -22,6 +23,8 @@ import {
|
|||
faTimes,
|
||||
faExpandAlt
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useMediaViewerStore } from '../../stores/media_viewer'
|
||||
import { useInterfaceStore } from '../../stores/interface'
|
||||
|
||||
library.add(
|
||||
faRss,
|
||||
|
@ -48,7 +51,7 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
followRequestInProgress: false,
|
||||
betterShadow: this.$store.state.interface.browserSupport.cssFilter,
|
||||
betterShadow: useInterfaceStore().browserSupport.cssFilter,
|
||||
showingConfirmMute: false,
|
||||
muteExpiryAmount: 0,
|
||||
muteExpiryUnit: 'minutes'
|
||||
|
@ -214,18 +217,18 @@ export default {
|
|||
)
|
||||
},
|
||||
openProfileTab () {
|
||||
this.$store.dispatch('openSettingsModalTab', 'profile')
|
||||
useInterfaceStore().openSettingsModalTab('profile')
|
||||
},
|
||||
zoomAvatar () {
|
||||
const attachment = {
|
||||
url: this.user.profile_image_url_original,
|
||||
mimetype: 'image'
|
||||
}
|
||||
this.$store.dispatch('setMedia', [attachment])
|
||||
this.$store.dispatch('setCurrentMedia', attachment)
|
||||
useMediaViewerStore().setMedia([attachment])
|
||||
useMediaViewerStore().setCurrentMedia(attachment)
|
||||
},
|
||||
mentionUser () {
|
||||
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
|
||||
usePostStatusStore().openPostStatusModal({ replyTo: true, repliedUser: this.user })
|
||||
},
|
||||
onAvatarClickHandler (e) {
|
||||
if (this.onAvatarClick) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faChevronRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import { mapState } from 'vuex'
|
||||
import { mapState } from 'pinia'
|
||||
|
||||
import DialogModal from '../dialog_modal/dialog_modal.vue'
|
||||
import Popover from '../popover/popover.vue'
|
||||
import { useListsStore } from '../../stores/lists'
|
||||
|
||||
library.add(faChevronRight)
|
||||
|
||||
|
@ -22,8 +23,8 @@ const UserListMenu = {
|
|||
this.$store.dispatch('fetchUserInLists', this.user.id)
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
allLists: state => state.lists.allLists
|
||||
...mapState(useListsStore, {
|
||||
allLists: store => store.allLists
|
||||
}),
|
||||
inListsSet () {
|
||||
return new Set(this.user.inLists.map(x => x.id))
|
||||
|
@ -39,12 +40,12 @@ const UserListMenu = {
|
|||
methods: {
|
||||
toggleList (listId) {
|
||||
if (this.inListsSet.has(listId)) {
|
||||
this.$store.dispatch('removeListAccount', { accountId: this.user.id, listId }).then((response) => {
|
||||
useListsStore().removeListAccount({ accountId: this.user.id, listId }).then((response) => {
|
||||
if (!response.ok) { return }
|
||||
this.$store.dispatch('fetchUserInLists', this.user.id)
|
||||
})
|
||||
} else {
|
||||
this.$store.dispatch('addListAccount', { accountId: this.user.id, listId }).then((response) => {
|
||||
useListsStore().addListAccount({ accountId: this.user.id, listId }).then((response) => {
|
||||
if (!response.ok) { return }
|
||||
this.$store.dispatch('fetchUserInLists', this.user.id)
|
||||
})
|
||||
|
|
|
@ -3,6 +3,7 @@ import List from '../list/list.vue'
|
|||
import Checkbox from '../checkbox/checkbox.vue'
|
||||
import Modal from '../modal/modal.vue'
|
||||
import UserLink from '../user_link/user_link.vue'
|
||||
import { useReportsStore } from '../../stores/reports'
|
||||
|
||||
const UserReportingModal = {
|
||||
components: {
|
||||
|
@ -23,7 +24,7 @@ const UserReportingModal = {
|
|||
},
|
||||
computed: {
|
||||
reportModal () {
|
||||
return this.$store.state.reports.reportModal
|
||||
return useReportsStore().reportModal
|
||||
},
|
||||
isLoggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
|
@ -63,7 +64,7 @@ const UserReportingModal = {
|
|||
this.error = false
|
||||
},
|
||||
closeModal () {
|
||||
this.$store.dispatch('closeUserReportingModal')
|
||||
useReportsStore().closeUserReportingModal()
|
||||
},
|
||||
reportUser () {
|
||||
this.processing = true
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import merge from 'lodash.merge'
|
||||
import localforage from 'localforage'
|
||||
import { each, get, set, cloneDeep } from 'lodash'
|
||||
import { useInterfaceStore } from '../stores/interface'
|
||||
|
||||
let loaded = false
|
||||
|
||||
|
@ -76,12 +77,12 @@ export default function createPersistedState ({
|
|||
.then(success => {
|
||||
if (typeof success !== 'undefined') {
|
||||
if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {
|
||||
store.dispatch('settingsSaved', { success })
|
||||
useInterfaceStore().settingsSaved({ success })
|
||||
}
|
||||
}
|
||||
}, error => {
|
||||
if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {
|
||||
store.dispatch('settingsSaved', { error })
|
||||
useInterfaceStore().settingsSaved({ error })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { useInterfaceStore } from '../stores/interface'
|
||||
|
||||
export default (store) => {
|
||||
store.subscribe((mutation, state) => {
|
||||
const vapidPublicKey = state.instance.vapidPublicKey
|
||||
const webPushNotification = state.config.webPushNotifications
|
||||
const permission = state.interface.notificationPermission === 'granted'
|
||||
const permission = useInterfaceStore().notificationPermission === 'granted'
|
||||
const user = state.users.currentUser
|
||||
|
||||
const isUserMutation = mutation.type === 'setCurrentUser'
|
||||
|
|
42
src/main.js
42
src/main.js
|
@ -1,30 +1,21 @@
|
|||
import { createStore } from 'vuex'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import 'custom-event-polyfill'
|
||||
import './lib/event_target_polyfill.js'
|
||||
|
||||
import interfaceModule from './modules/interface.js'
|
||||
import instanceModule from './modules/instance.js'
|
||||
import statusesModule from './modules/statuses.js'
|
||||
import listsModule from './modules/lists.js'
|
||||
import usersModule from './modules/users.js'
|
||||
import apiModule from './modules/api.js'
|
||||
import configModule from './modules/config.js'
|
||||
import serverSideConfigModule from './modules/serverSideConfig.js'
|
||||
import serverSideStorageModule from './modules/serverSideStorage.js'
|
||||
import shoutModule from './modules/shout.js'
|
||||
import oauthModule from './modules/oauth.js'
|
||||
import authFlowModule from './modules/auth_flow.js'
|
||||
import mediaViewerModule from './modules/media_viewer.js'
|
||||
import oauthTokensModule from './modules/oauth_tokens.js'
|
||||
import reportsModule from './modules/reports.js'
|
||||
import pollsModule from './modules/polls.js'
|
||||
import postStatusModule from './modules/postStatus.js'
|
||||
import editStatusModule from './modules/editStatus.js'
|
||||
import statusHistoryModule from './modules/statusHistory.js'
|
||||
|
||||
import chatsModule from './modules/chats.js'
|
||||
import announcementsModule from './modules/announcements.js'
|
||||
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
|
@ -58,6 +49,7 @@ const persistedStateOptions = {
|
|||
(async () => {
|
||||
let storageError = false
|
||||
const plugins = [pushNotifications]
|
||||
const pinia = createPinia()
|
||||
try {
|
||||
const persistedState = await createPersistedState(persistedStateOptions)
|
||||
plugins.push(persistedState)
|
||||
|
@ -65,44 +57,32 @@ const persistedStateOptions = {
|
|||
console.error(e)
|
||||
storageError = true
|
||||
}
|
||||
const store = createStore({
|
||||
|
||||
// Temporarily storing as a global variable while we migrate to Pinia
|
||||
window.vuex = createStore({
|
||||
modules: {
|
||||
i18n: {
|
||||
getters: {
|
||||
i18n: () => i18n.global
|
||||
}
|
||||
},
|
||||
interface: interfaceModule,
|
||||
instance: instanceModule,
|
||||
// TODO refactor users/statuses modules, they depend on each other
|
||||
users: usersModule,
|
||||
statuses: statusesModule,
|
||||
lists: listsModule,
|
||||
api: apiModule,
|
||||
config: configModule,
|
||||
serverSideConfig: serverSideConfigModule,
|
||||
serverSideStorage: serverSideStorageModule,
|
||||
shout: shoutModule,
|
||||
oauth: oauthModule,
|
||||
authFlow: authFlowModule,
|
||||
mediaViewer: mediaViewerModule,
|
||||
oauthTokens: oauthTokensModule,
|
||||
reports: reportsModule,
|
||||
polls: pollsModule,
|
||||
postStatus: postStatusModule,
|
||||
editStatus: editStatusModule,
|
||||
statusHistory: statusHistoryModule,
|
||||
chats: chatsModule,
|
||||
announcements: announcementsModule
|
||||
chats: chatsModule
|
||||
},
|
||||
plugins,
|
||||
strict: false // Socket modifies itself, let's ignore this for now.
|
||||
// strict: process.env.NODE_ENV !== 'production'
|
||||
})
|
||||
if (storageError) {
|
||||
store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
|
||||
}
|
||||
afterStoreSetup({ store, i18n })
|
||||
|
||||
const store = window.vuex
|
||||
|
||||
// Temporarily passing pinia and vuex stores along with storageError result until migration is fully complete.
|
||||
afterStoreSetup({ pinia, store, storageError, i18n })
|
||||
})()
|
||||
|
||||
// These are inlined by webpack's DefinePlugin
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5
|
||||
|
||||
export const defaultState = {
|
||||
announcements: [],
|
||||
supportsAnnouncements: true,
|
||||
fetchAnnouncementsTimer: undefined
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
setAnnouncements (state, announcements) {
|
||||
state.announcements = announcements
|
||||
},
|
||||
setAnnouncementRead (state, { id, read }) {
|
||||
const index = state.announcements.findIndex(a => a.id === id)
|
||||
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
state.announcements[index].read = read
|
||||
},
|
||||
setFetchAnnouncementsTimer (state, timer) {
|
||||
state.fetchAnnouncementsTimer = timer
|
||||
},
|
||||
setSupportsAnnouncements (state, supportsAnnouncements) {
|
||||
state.supportsAnnouncements = supportsAnnouncements
|
||||
}
|
||||
}
|
||||
|
||||
export const getters = {
|
||||
unreadAnnouncementCount (state, _getters, rootState) {
|
||||
if (!rootState.users.currentUser) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const unread = state.announcements.filter(announcement => !(announcement.inactive || announcement.read))
|
||||
return unread.length
|
||||
}
|
||||
}
|
||||
|
||||
const announcements = {
|
||||
state: defaultState,
|
||||
mutations,
|
||||
getters,
|
||||
actions: {
|
||||
fetchAnnouncements (store) {
|
||||
if (!store.state.supportsAnnouncements) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const currentUser = store.rootState.users.currentUser
|
||||
const isAdmin = currentUser && currentUser.privileges.includes('announcements_manage_announcements')
|
||||
|
||||
const getAnnouncements = async () => {
|
||||
if (!isAdmin) {
|
||||
return store.rootState.api.backendInteractor.fetchAnnouncements()
|
||||
}
|
||||
|
||||
const all = await store.rootState.api.backendInteractor.adminFetchAnnouncements()
|
||||
const visible = await store.rootState.api.backendInteractor.fetchAnnouncements()
|
||||
const visibleObject = visible.reduce((a, c) => {
|
||||
a[c.id] = c
|
||||
return a
|
||||
}, {})
|
||||
const getWithinVisible = announcement => visibleObject[announcement.id]
|
||||
|
||||
all.forEach(announcement => {
|
||||
const visibleAnnouncement = getWithinVisible(announcement)
|
||||
if (!visibleAnnouncement) {
|
||||
announcement.inactive = true
|
||||
} else {
|
||||
announcement.read = visibleAnnouncement.read
|
||||
}
|
||||
})
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
return getAnnouncements()
|
||||
.then(announcements => {
|
||||
store.commit('setAnnouncements', announcements)
|
||||
})
|
||||
.catch(error => {
|
||||
// If and only if backend does not support announcements, it would return 404.
|
||||
// In this case, silently ignores it.
|
||||
if (error && error.statusCode === 404) {
|
||||
store.commit('setSupportsAnnouncements', false)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
},
|
||||
markAnnouncementAsRead (store, id) {
|
||||
return store.rootState.api.backendInteractor.dismissAnnouncement({ id })
|
||||
.then(() => {
|
||||
store.commit('setAnnouncementRead', { id, read: true })
|
||||
})
|
||||
},
|
||||
startFetchingAnnouncements (store) {
|
||||
if (store.state.fetchAnnouncementsTimer) {
|
||||
return
|
||||
}
|
||||
|
||||
const interval = setInterval(() => store.dispatch('fetchAnnouncements'), FETCH_ANNOUNCEMENT_INTERVAL_MS)
|
||||
store.commit('setFetchAnnouncementsTimer', interval)
|
||||
|
||||
return store.dispatch('fetchAnnouncements')
|
||||
},
|
||||
stopFetchingAnnouncements (store) {
|
||||
const interval = store.state.fetchAnnouncementsTimer
|
||||
store.commit('setFetchAnnouncementsTimer', undefined)
|
||||
clearInterval(interval)
|
||||
},
|
||||
postAnnouncement (store, { content, startsAt, endsAt, allDay }) {
|
||||
return store.rootState.api.backendInteractor.postAnnouncement({ content, startsAt, endsAt, allDay })
|
||||
.then(() => {
|
||||
return store.dispatch('fetchAnnouncements')
|
||||
})
|
||||
},
|
||||
editAnnouncement (store, { id, content, startsAt, endsAt, allDay }) {
|
||||
return store.rootState.api.backendInteractor.editAnnouncement({ id, content, startsAt, endsAt, allDay })
|
||||
.then(() => {
|
||||
return store.dispatch('fetchAnnouncements')
|
||||
})
|
||||
},
|
||||
deleteAnnouncement (store, id) {
|
||||
return store.rootState.api.backendInteractor.deleteAnnouncement({ id })
|
||||
.then(() => {
|
||||
return store.dispatch('fetchAnnouncements')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default announcements
|
|
@ -2,6 +2,8 @@ import backendInteractorService from '../services/backend_interactor_service/bac
|
|||
import { WSConnectionStatus } from '../services/api/api.service.js'
|
||||
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
|
||||
import { Socket } from 'phoenix'
|
||||
import { useShoutStore } from '../stores/shout.js'
|
||||
import { useInterfaceStore } from '../stores/interface.js'
|
||||
|
||||
const retryTimeout = (multiplier) => 1000 * multiplier
|
||||
|
||||
|
@ -131,7 +133,7 @@ const api = {
|
|||
state.mastoUserSocket.addEventListener('open', () => {
|
||||
// Do not show notification when we just opened up the page
|
||||
if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) {
|
||||
dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'success',
|
||||
messageKey: 'timeline.socket_reconnected',
|
||||
timeout: 5000
|
||||
|
@ -173,7 +175,7 @@ const api = {
|
|||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
||||
dispatch('startFetchingNotifications')
|
||||
dispatch('startFetchingChats')
|
||||
dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'timeline.socket_broke',
|
||||
messageArgs: [code],
|
||||
|
@ -283,7 +285,7 @@ const api = {
|
|||
socket.connect()
|
||||
|
||||
commit('setSocket', socket)
|
||||
dispatch('initializeShout', socket)
|
||||
useShoutStore().initializeShout(socket)
|
||||
}
|
||||
},
|
||||
disconnectFromSocket ({ commit, state }) {
|
||||
|
|
|
@ -2,6 +2,8 @@ import Cookies from 'js-cookie'
|
|||
import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
|
||||
import messages from '../i18n/messages'
|
||||
import localeService from '../services/locale/locale.service.js'
|
||||
import { useI18nStore } from '../stores/i18n.js'
|
||||
import { useInterfaceStore } from '../stores/interface.js'
|
||||
|
||||
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
|
||||
|
||||
|
@ -194,7 +196,7 @@ const config = {
|
|||
applyTheme(value)
|
||||
break
|
||||
case 'interfaceLanguage':
|
||||
messages.setLanguage(this.getters.i18n, value)
|
||||
messages.setLanguage(useI18nStore().i18n, value)
|
||||
dispatch('loadUnicodeEmojiData', value)
|
||||
Cookies.set(
|
||||
BACKEND_LANGUAGE_COOKIE_NAME,
|
||||
|
@ -202,7 +204,7 @@ const config = {
|
|||
)
|
||||
break
|
||||
case 'thirdColumnMode':
|
||||
dispatch('setLayoutWidth', undefined)
|
||||
useInterfaceStore().setLayoutWidth(undefined)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
const editStatus = {
|
||||
state: {
|
||||
params: null,
|
||||
modalActivated: false
|
||||
},
|
||||
mutations: {
|
||||
openEditStatusModal (state, params) {
|
||||
state.params = params
|
||||
state.modalActivated = true
|
||||
},
|
||||
closeEditStatusModal (state) {
|
||||
state.modalActivated = false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
openEditStatusModal ({ commit }, params) {
|
||||
commit('openEditStatusModal', params)
|
||||
},
|
||||
closeEditStatusModal ({ commit }) {
|
||||
commit('closeEditStatusModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default editStatus
|
|
@ -3,6 +3,7 @@ import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
|
|||
import apiService from '../services/api/api.service.js'
|
||||
import { instanceDefaultProperties } from './config.js'
|
||||
import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js'
|
||||
import { useInterfaceStore } from '../stores/interface.js'
|
||||
|
||||
const SORTED_EMOJI_GROUP_IDS = [
|
||||
'smileys-and-emotion',
|
||||
|
@ -261,7 +262,7 @@ const instance = {
|
|||
commit('setInstanceOption', { name, value })
|
||||
switch (name) {
|
||||
case 'name':
|
||||
dispatch('setPageTitle')
|
||||
useInterfaceStore().setPageTitle()
|
||||
break
|
||||
case 'shoutAvailable':
|
||||
if (value) {
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
const defaultState = {
|
||||
settingsModalState: 'hidden',
|
||||
settingsModalLoaded: false,
|
||||
settingsModalTargetTab: null,
|
||||
settings: {
|
||||
currentSaveStateNotice: null,
|
||||
noticeClearTimeout: null,
|
||||
notificationPermission: null
|
||||
},
|
||||
browserSupport: {
|
||||
cssFilter: window.CSS && window.CSS.supports && (
|
||||
window.CSS.supports('filter', 'drop-shadow(0 0)') ||
|
||||
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
|
||||
)
|
||||
},
|
||||
layoutType: 'normal',
|
||||
globalNotices: [],
|
||||
layoutHeight: 0,
|
||||
lastTimeline: null
|
||||
}
|
||||
|
||||
const interfaceMod = {
|
||||
state: defaultState,
|
||||
mutations: {
|
||||
settingsSaved (state, { success, error }) {
|
||||
if (success) {
|
||||
if (state.noticeClearTimeout) {
|
||||
clearTimeout(state.noticeClearTimeout)
|
||||
}
|
||||
state.settings.currentSaveStateNotice = { error: false, data: success }
|
||||
state.settings.noticeClearTimeout = setTimeout(() => delete state.settings.currentSaveStateNotice, 2000)
|
||||
} else {
|
||||
state.settings.currentSaveStateNotice = { error: true, errorData: error }
|
||||
}
|
||||
},
|
||||
setNotificationPermission (state, permission) {
|
||||
state.notificationPermission = permission
|
||||
},
|
||||
setLayoutType (state, value) {
|
||||
state.layoutType = value
|
||||
},
|
||||
closeSettingsModal (state) {
|
||||
state.settingsModalState = 'hidden'
|
||||
},
|
||||
togglePeekSettingsModal (state) {
|
||||
switch (state.settingsModalState) {
|
||||
case 'minimized':
|
||||
state.settingsModalState = 'visible'
|
||||
return
|
||||
case 'visible':
|
||||
state.settingsModalState = 'minimized'
|
||||
return
|
||||
default:
|
||||
throw new Error('Illegal minimization state of settings modal')
|
||||
}
|
||||
},
|
||||
openSettingsModal (state) {
|
||||
state.settingsModalState = 'visible'
|
||||
if (!state.settingsModalLoaded) {
|
||||
state.settingsModalLoaded = true
|
||||
}
|
||||
},
|
||||
setSettingsModalTargetTab (state, value) {
|
||||
state.settingsModalTargetTab = value
|
||||
},
|
||||
pushGlobalNotice (state, notice) {
|
||||
state.globalNotices.push(notice)
|
||||
},
|
||||
removeGlobalNotice (state, notice) {
|
||||
state.globalNotices = state.globalNotices.filter(n => n !== notice)
|
||||
},
|
||||
setLayoutHeight (state, value) {
|
||||
state.layoutHeight = value
|
||||
},
|
||||
setLayoutWidth (state, value) {
|
||||
state.layoutWidth = value
|
||||
},
|
||||
setLastTimeline (state, value) {
|
||||
state.lastTimeline = value
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setPageTitle ({ rootState }, option = '') {
|
||||
document.title = `${option} ${rootState.instance.name}`
|
||||
},
|
||||
settingsSaved ({ commit, dispatch }, { success, error }) {
|
||||
commit('settingsSaved', { success, error })
|
||||
},
|
||||
setNotificationPermission ({ commit }, permission) {
|
||||
commit('setNotificationPermission', permission)
|
||||
},
|
||||
closeSettingsModal ({ commit }) {
|
||||
commit('closeSettingsModal')
|
||||
},
|
||||
openSettingsModal ({ commit }) {
|
||||
commit('openSettingsModal')
|
||||
},
|
||||
togglePeekSettingsModal ({ commit }) {
|
||||
commit('togglePeekSettingsModal')
|
||||
},
|
||||
clearSettingsModalTargetTab ({ commit }) {
|
||||
commit('setSettingsModalTargetTab', null)
|
||||
},
|
||||
openSettingsModalTab ({ commit }, value) {
|
||||
commit('setSettingsModalTargetTab', value)
|
||||
commit('openSettingsModal')
|
||||
},
|
||||
pushGlobalNotice (
|
||||
{ commit, dispatch, state },
|
||||
{
|
||||
messageKey,
|
||||
messageArgs = {},
|
||||
level = 'error',
|
||||
timeout = 0
|
||||
}) {
|
||||
const notice = {
|
||||
messageKey,
|
||||
messageArgs,
|
||||
level
|
||||
}
|
||||
commit('pushGlobalNotice', notice)
|
||||
// Adding a new element to array wraps it in a Proxy, which breaks the comparison
|
||||
// TODO: Generate UUID or something instead or relying on !== operator?
|
||||
const newNotice = state.globalNotices[state.globalNotices.length - 1]
|
||||
if (timeout) {
|
||||
setTimeout(() => dispatch('removeGlobalNotice', newNotice), timeout)
|
||||
}
|
||||
return newNotice
|
||||
},
|
||||
removeGlobalNotice ({ commit }, notice) {
|
||||
commit('removeGlobalNotice', notice)
|
||||
},
|
||||
setLayoutHeight ({ commit }, value) {
|
||||
commit('setLayoutHeight', value)
|
||||
},
|
||||
// value is optional, assuming it was cached prior
|
||||
setLayoutWidth ({ commit, state, rootGetters, rootState }, value) {
|
||||
let width = value
|
||||
if (value !== undefined) {
|
||||
commit('setLayoutWidth', value)
|
||||
} else {
|
||||
width = state.layoutWidth
|
||||
}
|
||||
const mobileLayout = width <= 800
|
||||
const normalOrMobile = mobileLayout ? 'mobile' : 'normal'
|
||||
const { thirdColumnMode } = rootGetters.mergedConfig
|
||||
if (thirdColumnMode === 'none' || !rootState.users.currentUser) {
|
||||
commit('setLayoutType', normalOrMobile)
|
||||
} else {
|
||||
const wideLayout = width >= 1300
|
||||
commit('setLayoutType', wideLayout ? 'wide' : normalOrMobile)
|
||||
}
|
||||
},
|
||||
setLastTimeline ({ commit }, value) {
|
||||
commit('setLastTimeline', value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default interfaceMod
|
|
@ -1,40 +0,0 @@
|
|||
import fileTypeService from '../services/file_type/file_type.service.js'
|
||||
const supportedTypes = new Set(['image', 'video', 'audio', 'flash'])
|
||||
|
||||
const mediaViewer = {
|
||||
state: {
|
||||
media: [],
|
||||
currentIndex: 0,
|
||||
activated: false
|
||||
},
|
||||
mutations: {
|
||||
setMedia (state, media) {
|
||||
state.media = media
|
||||
},
|
||||
setCurrentMedia (state, index) {
|
||||
state.activated = true
|
||||
state.currentIndex = index
|
||||
},
|
||||
close (state) {
|
||||
state.activated = false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setMedia ({ commit }, attachments) {
|
||||
const media = attachments.filter(attachment => {
|
||||
const type = fileTypeService.fileType(attachment.mimetype)
|
||||
return supportedTypes.has(type)
|
||||
})
|
||||
commit('setMedia', media)
|
||||
},
|
||||
setCurrentMedia ({ commit, state }, current) {
|
||||
const index = state.media.indexOf(current)
|
||||
commit('setCurrentMedia', index || 0)
|
||||
},
|
||||
closeMediaViewer ({ commit }) {
|
||||
commit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default mediaViewer
|
|
@ -1,69 +0,0 @@
|
|||
import { merge } from 'lodash'
|
||||
|
||||
const polls = {
|
||||
state: {
|
||||
// Contains key = id, value = number of trackers for this poll
|
||||
trackedPolls: {},
|
||||
pollsObject: {}
|
||||
},
|
||||
mutations: {
|
||||
mergeOrAddPoll (state, poll) {
|
||||
const existingPoll = state.pollsObject[poll.id]
|
||||
// Make expired-state change trigger re-renders properly
|
||||
poll.expired = Date.now() > Date.parse(poll.expires_at)
|
||||
if (existingPoll) {
|
||||
state.pollsObject[poll.id] = merge(existingPoll, poll)
|
||||
} else {
|
||||
state.pollsObject[poll.id] = poll
|
||||
}
|
||||
},
|
||||
trackPoll (state, pollId) {
|
||||
const currentValue = state.trackedPolls[pollId]
|
||||
if (currentValue) {
|
||||
state.trackedPolls[pollId] = currentValue + 1
|
||||
} else {
|
||||
state.trackedPolls[pollId] = 1
|
||||
}
|
||||
},
|
||||
untrackPoll (state, pollId) {
|
||||
const currentValue = state.trackedPolls[pollId]
|
||||
if (currentValue) {
|
||||
state.trackedPolls[pollId] = currentValue - 1
|
||||
} else {
|
||||
state.trackedPolls[pollId] = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
mergeOrAddPoll ({ commit }, poll) {
|
||||
commit('mergeOrAddPoll', poll)
|
||||
},
|
||||
updateTrackedPoll ({ rootState, dispatch, commit }, pollId) {
|
||||
rootState.api.backendInteractor.fetchPoll({ pollId }).then(poll => {
|
||||
setTimeout(() => {
|
||||
if (rootState.polls.trackedPolls[pollId]) {
|
||||
dispatch('updateTrackedPoll', pollId)
|
||||
}
|
||||
}, 30 * 1000)
|
||||
commit('mergeOrAddPoll', poll)
|
||||
})
|
||||
},
|
||||
trackPoll ({ rootState, commit, dispatch }, pollId) {
|
||||
if (!rootState.polls.trackedPolls[pollId]) {
|
||||
setTimeout(() => dispatch('updateTrackedPoll', pollId), 30 * 1000)
|
||||
}
|
||||
commit('trackPoll', pollId)
|
||||
},
|
||||
untrackPoll ({ commit }, pollId) {
|
||||
commit('untrackPoll', pollId)
|
||||
},
|
||||
votePoll ({ rootState, commit }, { id, pollId, choices }) {
|
||||
return rootState.api.backendInteractor.vote({ pollId, choices }).then(poll => {
|
||||
commit('mergeOrAddPoll', poll)
|
||||
return poll
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default polls
|
|
@ -1,25 +0,0 @@
|
|||
const postStatus = {
|
||||
state: {
|
||||
params: null,
|
||||
modalActivated: false
|
||||
},
|
||||
mutations: {
|
||||
openPostStatusModal (state, params) {
|
||||
state.params = params
|
||||
state.modalActivated = true
|
||||
},
|
||||
closePostStatusModal (state) {
|
||||
state.modalActivated = false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
openPostStatusModal ({ commit }, params) {
|
||||
commit('openPostStatusModal', params)
|
||||
},
|
||||
closePostStatusModal ({ commit }) {
|
||||
commit('closePostStatusModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default postStatus
|
|
@ -1,64 +0,0 @@
|
|||
import filter from 'lodash/filter'
|
||||
|
||||
const reports = {
|
||||
state: {
|
||||
reportModal: {
|
||||
userId: null,
|
||||
statuses: [],
|
||||
preTickedIds: [],
|
||||
activated: false
|
||||
},
|
||||
reports: {}
|
||||
},
|
||||
mutations: {
|
||||
openUserReportingModal (state, { userId, statuses, preTickedIds }) {
|
||||
state.reportModal.userId = userId
|
||||
state.reportModal.statuses = statuses
|
||||
state.reportModal.preTickedIds = preTickedIds
|
||||
state.reportModal.activated = true
|
||||
},
|
||||
closeUserReportingModal (state) {
|
||||
state.reportModal.activated = false
|
||||
},
|
||||
setReportState (reportsState, { id, state }) {
|
||||
reportsState.reports[id].state = state
|
||||
},
|
||||
addReport (state, report) {
|
||||
state.reports[report.id] = report
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
openUserReportingModal ({ rootState, commit }, { userId, statusIds = [] }) {
|
||||
const preTickedStatuses = statusIds.map(id => rootState.statuses.allStatusesObject[id])
|
||||
const preTickedIds = statusIds
|
||||
const statuses = preTickedStatuses.concat(
|
||||
filter(rootState.statuses.allStatuses,
|
||||
status => status.user.id === userId && !preTickedIds.includes(status.id)
|
||||
)
|
||||
)
|
||||
commit('openUserReportingModal', { userId, statuses, preTickedIds })
|
||||
},
|
||||
closeUserReportingModal ({ commit }) {
|
||||
commit('closeUserReportingModal')
|
||||
},
|
||||
setReportState ({ commit, dispatch, rootState }, { id, state }) {
|
||||
const oldState = rootState.reports.reports[id].state
|
||||
commit('setReportState', { id, state })
|
||||
rootState.api.backendInteractor.setReportState({ id, state }).catch(e => {
|
||||
console.error('Failed to set report state', e)
|
||||
dispatch('pushGlobalNotice', {
|
||||
level: 'error',
|
||||
messageKey: 'general.generic_error_message',
|
||||
messageArgs: [e.message],
|
||||
timeout: 5000
|
||||
})
|
||||
commit('setReportState', { id, state: oldState })
|
||||
})
|
||||
},
|
||||
addReport ({ commit }, report) {
|
||||
commit('addReport', report)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default reports
|
|
@ -1,46 +0,0 @@
|
|||
const shout = {
|
||||
state: {
|
||||
messages: [],
|
||||
channel: { state: '' },
|
||||
joined: false
|
||||
},
|
||||
mutations: {
|
||||
setChannel (state, channel) {
|
||||
state.channel = channel
|
||||
},
|
||||
addMessage (state, message) {
|
||||
state.messages.push(message)
|
||||
state.messages = state.messages.slice(-19, 20)
|
||||
},
|
||||
setMessages (state, messages) {
|
||||
state.messages = messages.slice(-19, 20)
|
||||
},
|
||||
setJoined (state, joined) {
|
||||
state.joined = joined
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
initializeShout (store, socket) {
|
||||
const channel = socket.channel('chat:public')
|
||||
channel.joinPush.receive('ok', () => {
|
||||
store.commit('setJoined', true)
|
||||
})
|
||||
channel.onClose(() => {
|
||||
store.commit('setJoined', false)
|
||||
})
|
||||
channel.onError(() => {
|
||||
store.commit('setJoined', false)
|
||||
})
|
||||
channel.on('new_msg', (msg) => {
|
||||
store.commit('addMessage', msg)
|
||||
})
|
||||
channel.on('messages', ({ messages }) => {
|
||||
store.commit('setMessages', messages)
|
||||
})
|
||||
channel.join()
|
||||
store.commit('setChannel', channel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default shout
|
|
@ -1,25 +0,0 @@
|
|||
const statusHistory = {
|
||||
state: {
|
||||
params: {},
|
||||
modalActivated: false
|
||||
},
|
||||
mutations: {
|
||||
openStatusHistoryModal (state, params) {
|
||||
state.params = params
|
||||
state.modalActivated = true
|
||||
},
|
||||
closeStatusHistoryModal (state) {
|
||||
state.modalActivated = false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
openStatusHistoryModal ({ commit }, params) {
|
||||
commit('openStatusHistoryModal', params)
|
||||
},
|
||||
closeStatusHistoryModal ({ commit }) {
|
||||
commit('closeStatusHistoryModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default statusHistory
|
|
@ -18,6 +18,7 @@ import {
|
|||
maybeShowNotification
|
||||
} from '../services/notification_utils/notification_utils.js'
|
||||
import apiService from '../services/api/api.service.js'
|
||||
import { useReportsStore } from '../stores/reports.js'
|
||||
|
||||
const emptyTl = (userId = 0) => ({
|
||||
statuses: [],
|
||||
|
@ -341,7 +342,7 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
|
|||
}
|
||||
|
||||
if (notification.type === 'pleroma:report') {
|
||||
dispatch('addReport', notification.report)
|
||||
useReportsStore().addReport(notification.report)
|
||||
}
|
||||
|
||||
if (notification.type === 'pleroma:emoji_reaction') {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { windowWidth, windowHeight } from '../services/window_utils/window_utils
|
|||
import oauthApi from '../services/new_api/oauth.js'
|
||||
import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash'
|
||||
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
|
||||
import { useInterfaceStore } from '../stores/interface.js'
|
||||
|
||||
// TODO: Unify with mergeOrAdd in statuses.js
|
||||
export const mergeOrAdd = (arr, obj, item) => {
|
||||
|
@ -542,9 +543,9 @@ const users = {
|
|||
store.commit('clearNotifications')
|
||||
store.commit('resetStatuses')
|
||||
store.dispatch('resetChats')
|
||||
store.dispatch('setLastTimeline', 'public-timeline')
|
||||
store.dispatch('setLayoutWidth', windowWidth())
|
||||
store.dispatch('setLayoutHeight', windowHeight())
|
||||
useInterfaceStore().setLastTimeline('public-timeline')
|
||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||
store.commit('clearServerSideStorage')
|
||||
})
|
||||
},
|
||||
|
@ -568,7 +569,7 @@ const users = {
|
|||
store.dispatch('fetchEmoji')
|
||||
|
||||
getNotificationPermission()
|
||||
.then(permission => commit('setNotificationPermission', permission))
|
||||
.then(permission => useInterfaceStore().setNotificationPermission(permission))
|
||||
|
||||
// Set our new backend interactor
|
||||
commit('setBackendInteractor', backendInteractorService(accessToken))
|
||||
|
@ -614,8 +615,8 @@ const users = {
|
|||
// Get user mutes
|
||||
store.dispatch('fetchMutes')
|
||||
|
||||
store.dispatch('setLayoutWidth', windowWidth())
|
||||
store.dispatch('setLayoutHeight', windowHeight())
|
||||
useInterfaceStore().setLayoutWidth(windowWidth())
|
||||
useInterfaceStore().setLayoutHeight(windowHeight())
|
||||
|
||||
// Fetch our friends
|
||||
store.rootState.api.backendInteractor.fetchFriends({ id: user.id })
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useListsStore } from '../../stores/lists.js'
|
||||
import apiService from '../api/api.service.js'
|
||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
||||
|
||||
const fetchAndUpdate = ({ store, credentials }) => {
|
||||
return apiService.fetchLists({ credentials })
|
||||
.then(lists => {
|
||||
store.commit('setLists', lists)
|
||||
useListsStore().setLists(lists)
|
||||
}, () => {})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { filter, sortBy, includes } from 'lodash'
|
||||
import { muteWordHits } from '../status_parser/status_parser.js'
|
||||
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
|
||||
import { useI18nStore } from '../../stores/i18n.js'
|
||||
|
||||
export const notificationsFromStore = store => store.state.statuses.notifications.data
|
||||
|
||||
|
@ -59,7 +60,7 @@ export const maybeShowNotification = (store, notification) => {
|
|||
if (!visibleTypes(store).includes(notification.type)) return
|
||||
if (notification.type === 'mention' && isMutedNotification(store, notification)) return
|
||||
|
||||
const notificationObject = prepareNotificationObject(notification, store.rootGetters.i18n)
|
||||
const notificationObject = prepareNotificationObject(notification, useI18nStore().i18n)
|
||||
showDesktopNotification(rootState, notificationObject)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { useInterfaceStore } from '../../stores/interface.js'
|
||||
import apiService from '../api/api.service.js'
|
||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
||||
|
||||
|
@ -70,7 +71,7 @@ const fetchNotifications = ({ store, args, older }) => {
|
|||
return notifications
|
||||
})
|
||||
.catch((error) => {
|
||||
store.dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'notifications.error',
|
||||
messageArgs: [error.message],
|
||||
|
|
|
@ -2,6 +2,7 @@ import { camelCase } from 'lodash'
|
|||
|
||||
import apiService from '../api/api.service.js'
|
||||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
||||
import { useInterfaceStore } from '../../stores/interface.js'
|
||||
|
||||
const update = ({ store, statuses, timeline, showImmediately, userId, listId, pagination }) => {
|
||||
const ccTimeline = camelCase(timeline)
|
||||
|
@ -69,7 +70,7 @@ const fetchAndUpdate = ({
|
|||
return { statuses, pagination }
|
||||
})
|
||||
.catch((error) => {
|
||||
store.dispatch('pushGlobalNotice', {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'timeline.error',
|
||||
messageArgs: [error.message],
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5
|
||||
|
||||
export const useAnnouncementsStore = defineStore('announcements', {
|
||||
state: () => ({
|
||||
announcements: [],
|
||||
supportsAnnouncements: true,
|
||||
fetchAnnouncementsTimer: undefined
|
||||
}),
|
||||
getters: {
|
||||
unreadAnnouncementCount () {
|
||||
if (!window.vuex.state.users.currentUser) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const unread = this.announcements.filter(announcement => !(announcement.inactive || announcement.read))
|
||||
return unread.length
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
fetchAnnouncements () {
|
||||
if (!this.supportsAnnouncements) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const currentUser = window.vuex.state.users.currentUser
|
||||
const isAdmin = currentUser && currentUser.privileges.includes('announcements_manage_announcements')
|
||||
|
||||
const getAnnouncements = async () => {
|
||||
if (!isAdmin) {
|
||||
return window.vuex.state.api.backendInteractor.fetchAnnouncements()
|
||||
}
|
||||
|
||||
const all = await window.vuex.state.api.backendInteractor.adminFetchAnnouncements()
|
||||
const visible = await window.vuex.state.api.backendInteractor.fetchAnnouncements()
|
||||
const visibleObject = visible.reduce((a, c) => {
|
||||
a[c.id] = c
|
||||
return a
|
||||
}, {})
|
||||
const getWithinVisible = announcement => visibleObject[announcement.id]
|
||||
|
||||
all.forEach(announcement => {
|
||||
const visibleAnnouncement = getWithinVisible(announcement)
|
||||
if (!visibleAnnouncement) {
|
||||
announcement.inactive = true
|
||||
} else {
|
||||
announcement.read = visibleAnnouncement.read
|
||||
}
|
||||
})
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
return getAnnouncements()
|
||||
.then(announcements => {
|
||||
this.announcements = announcements
|
||||
})
|
||||
.catch(error => {
|
||||
// If and only if backend does not support announcements, it would return 404.
|
||||
// In this case, silently ignores it.
|
||||
if (error && error.statusCode === 404) {
|
||||
this.supportsAnnouncements = false
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
},
|
||||
markAnnouncementAsRead (id) {
|
||||
return window.vuex.state.api.backendInteractor.dismissAnnouncement({ id })
|
||||
.then(() => {
|
||||
const index = this.announcements.findIndex(a => a.id === id)
|
||||
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
this.announcements[index].read = true
|
||||
})
|
||||
},
|
||||
startFetchingAnnouncements () {
|
||||
if (this.fetchAnnouncementsTimer) {
|
||||
return
|
||||
}
|
||||
|
||||
const interval = setInterval(() => this.fetchAnnouncements(), FETCH_ANNOUNCEMENT_INTERVAL_MS)
|
||||
this.fetchAnnouncementsTimer = interval
|
||||
|
||||
return this.fetchAnnouncements()
|
||||
},
|
||||
stopFetchingAnnouncements () {
|
||||
const interval = this.fetchAnnouncementsTimer
|
||||
this.fetchAnnouncementsTimer = undefined
|
||||
clearInterval(interval)
|
||||
},
|
||||
postAnnouncement ({ content, startsAt, endsAt, allDay }) {
|
||||
return window.vuex.state.api.backendInteractor.postAnnouncement({ content, startsAt, endsAt, allDay })
|
||||
.then(() => {
|
||||
return this.fetchAnnouncements()
|
||||
})
|
||||
},
|
||||
editAnnouncement ({ id, content, startsAt, endsAt, allDay }) {
|
||||
return window.vuex.state.api.backendInteractor.editAnnouncement({ id, content, startsAt, endsAt, allDay })
|
||||
.then(() => {
|
||||
return this.fetchAnnouncements()
|
||||
})
|
||||
},
|
||||
deleteAnnouncement (id) {
|
||||
return window.vuex.state.api.backendInteractor.deleteAnnouncement({ id })
|
||||
.then(() => {
|
||||
return this.fetchAnnouncements()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useEditStatusStore = defineStore('editStatus', {
|
||||
state: () => ({
|
||||
params: null,
|
||||
modalActivated: false
|
||||
}),
|
||||
actions: {
|
||||
openEditStatusModal (params) {
|
||||
this.params = params
|
||||
this.modalActivated = true
|
||||
},
|
||||
closeEditStatusModal () {
|
||||
this.modalActivated = false
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,14 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useI18nStore = defineStore('i18n', {
|
||||
state: () => ({
|
||||
i18n: null
|
||||
}),
|
||||
actions: {
|
||||
setI18n (newI18n) {
|
||||
this.$patch({
|
||||
i18n: newI18n.global
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,126 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useInterfaceStore = defineStore('interface', {
|
||||
state: () => ({
|
||||
settingsModalState: 'hidden',
|
||||
settingsModalLoaded: false,
|
||||
settingsModalTargetTab: null,
|
||||
settings: {
|
||||
currentSaveStateNotice: null,
|
||||
noticeClearTimeout: null,
|
||||
notificationPermission: null
|
||||
},
|
||||
browserSupport: {
|
||||
cssFilter: window.CSS && window.CSS.supports && (
|
||||
window.CSS.supports('filter', 'drop-shadow(0 0)') ||
|
||||
window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)')
|
||||
)
|
||||
},
|
||||
layoutType: 'normal',
|
||||
globalNotices: [],
|
||||
layoutHeight: 0,
|
||||
lastTimeline: null
|
||||
}),
|
||||
actions: {
|
||||
setPageTitle (option = '') {
|
||||
try {
|
||||
document.title = `${option} ${window.vuex.state.instance.name}`
|
||||
} catch (error) {
|
||||
console.error(`${error}`)
|
||||
}
|
||||
},
|
||||
settingsSaved ({ success, error }) {
|
||||
if (success) {
|
||||
if (this.noticeClearTimeout) {
|
||||
clearTimeout(this.noticeClearTimeout)
|
||||
}
|
||||
this.settings.currentSaveStateNotice = { error: false, data: success }
|
||||
this.settings.noticeClearTimeout = setTimeout(() => delete this.settings.currentSaveStateNotice, 2000)
|
||||
} else {
|
||||
this.settings.currentSaveStateNotice = { error: true, errorData: error }
|
||||
}
|
||||
},
|
||||
setNotificationPermission (permission) {
|
||||
this.notificationPermission = permission
|
||||
},
|
||||
closeSettingsModal () {
|
||||
this.settingsModalState = 'hidden'
|
||||
},
|
||||
openSettingsModal () {
|
||||
this.settingsModalState = 'visible'
|
||||
if (!this.settingsModalLoaded) {
|
||||
this.settingsModalLoaded = true
|
||||
}
|
||||
},
|
||||
togglePeekSettingsModal () {
|
||||
switch (this.settingsModalState) {
|
||||
case 'minimized':
|
||||
this.settingsModalState = 'visible'
|
||||
return
|
||||
case 'visible':
|
||||
this.settingsModalState = 'minimized'
|
||||
return
|
||||
default:
|
||||
throw new Error('Illegal minimization state of settings modal')
|
||||
}
|
||||
},
|
||||
clearSettingsModalTargetTab () {
|
||||
this.settingsModalTargetTab = null
|
||||
},
|
||||
openSettingsModalTab (value) {
|
||||
this.settingsModalTargetTab = value
|
||||
this.openSettingsModal()
|
||||
},
|
||||
removeGlobalNotice (notice) {
|
||||
this.globalNotices = this.globalNotices.filter(n => n !== notice)
|
||||
},
|
||||
pushGlobalNotice (
|
||||
{
|
||||
messageKey,
|
||||
messageArgs = {},
|
||||
level = 'error',
|
||||
timeout = 0
|
||||
}) {
|
||||
const notice = {
|
||||
messageKey,
|
||||
messageArgs,
|
||||
level
|
||||
}
|
||||
|
||||
this.globalNotices.push(notice)
|
||||
|
||||
// Adding a new element to array wraps it in a Proxy, which breaks the comparison
|
||||
// TODO: Generate UUID or something instead or relying on !== operator?
|
||||
const newNotice = this.globalNotices[this.globalNotices.length - 1]
|
||||
if (timeout) {
|
||||
setTimeout(() => this.removeGlobalNotice(newNotice), timeout)
|
||||
}
|
||||
|
||||
return newNotice
|
||||
},
|
||||
setLayoutHeight (value) {
|
||||
this.layoutHeight = value
|
||||
},
|
||||
setLayoutWidth (value) {
|
||||
let width = value
|
||||
if (value !== undefined) {
|
||||
this.layoutWidth = value
|
||||
} else {
|
||||
width = this.layoutWidth
|
||||
}
|
||||
|
||||
const mobileLayout = width <= 800
|
||||
const normalOrMobile = mobileLayout ? 'mobile' : 'normal'
|
||||
const { thirdColumnMode } = window.vuex.getters.mergedConfig
|
||||
if (thirdColumnMode === 'none' || !window.vuex.state.users.currentUser) {
|
||||
this.layoutType = normalOrMobile
|
||||
} else {
|
||||
const wideLayout = width >= 1300
|
||||
this.layoutType = wideLayout ? 'wide' : normalOrMobile
|
||||
}
|
||||
},
|
||||
setLastTimeline (value) {
|
||||
this.lastTimeline = value
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,110 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
import { remove, find } from 'lodash'
|
||||
|
||||
export const useListsStore = defineStore('lists', {
|
||||
state: () => ({
|
||||
allLists: [],
|
||||
allListsObject: {}
|
||||
}),
|
||||
getters: {
|
||||
findListTitle (state) {
|
||||
return (id) => {
|
||||
if (!this.allListsObject[id]) return
|
||||
return this.allListsObject[id].title
|
||||
}
|
||||
},
|
||||
findListAccounts (state) {
|
||||
return (id) => [...this.allListsObject[id].accountIds]
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setLists (value) {
|
||||
this.allLists = value
|
||||
},
|
||||
createList ({ title }) {
|
||||
return window.vuex.state.api.backendInteractor.createList({ title })
|
||||
.then((list) => {
|
||||
this.setList({ listId: list.id, title })
|
||||
return list
|
||||
})
|
||||
},
|
||||
fetchList ({ listId }) {
|
||||
return window.vuex.state.api.backendInteractor.getList({ listId })
|
||||
.then((list) => this.setList({ listId: list.id, title: list.title }))
|
||||
},
|
||||
fetchListAccounts ({ listId }) {
|
||||
return window.vuex.state.api.backendInteractor.getListAccounts({ listId })
|
||||
.then((accountIds) => {
|
||||
if (!this.allListsObject[listId]) {
|
||||
this.allListsObject[listId] = { accountIds: [] }
|
||||
}
|
||||
this.allListsObject[listId].accountIds = accountIds
|
||||
})
|
||||
},
|
||||
setList ({ listId, title }) {
|
||||
if (!this.allListsObject[listId]) {
|
||||
this.allListsObject[listId] = { accountIds: [] }
|
||||
}
|
||||
this.allListsObject[listId].title = title
|
||||
|
||||
const entry = find(this.allLists, { id: listId })
|
||||
if (!entry) {
|
||||
this.allLists.push({ id: listId, title })
|
||||
} else {
|
||||
entry.title = title
|
||||
}
|
||||
},
|
||||
setListAccounts ({ listId, accountIds }) {
|
||||
const saved = this.allListsObject[listId].accountIds || []
|
||||
const added = accountIds.filter(id => !saved.includes(id))
|
||||
const removed = saved.filter(id => !accountIds.includes(id))
|
||||
if (!this.allListsObject[listId]) {
|
||||
this.allListsObject[listId] = { accountIds: [] }
|
||||
}
|
||||
this.allListsObject[listId].accountIds = accountIds
|
||||
if (added.length > 0) {
|
||||
window.vuex.state.api.backendInteractor.addAccountsToList({ listId, accountIds: added })
|
||||
}
|
||||
if (removed.length > 0) {
|
||||
window.vuex.state.api.backendInteractor.removeAccountsFromList({ listId, accountIds: removed })
|
||||
}
|
||||
},
|
||||
addListAccount ({ listId, accountId }) {
|
||||
return window.vuex.state
|
||||
.api
|
||||
.backendInteractor
|
||||
.addAccountsToList({ listId, accountIds: [accountId] })
|
||||
.then((result) => {
|
||||
if (!this.allListsObject[listId]) {
|
||||
this.allListsObject[listId] = { accountIds: [] }
|
||||
}
|
||||
this.allListsObject[listId].accountIds.push(accountId)
|
||||
return result
|
||||
})
|
||||
},
|
||||
removeListAccount ({ listId, accountId }) {
|
||||
return window.vuex.state
|
||||
.api
|
||||
.backendInteractor
|
||||
.removeAccountsFromList({ listId, accountIds: [accountId] })
|
||||
.then((result) => {
|
||||
if (!this.allListsObject[listId]) {
|
||||
this.allListsObject[listId] = { accountIds: [] }
|
||||
}
|
||||
const { accountIds } = this.allListsObject[listId]
|
||||
const set = new Set(accountIds)
|
||||
set.delete(accountId)
|
||||
this.allListsObject[listId].accountIds = [...set]
|
||||
|
||||
return result
|
||||
})
|
||||
},
|
||||
deleteList ({ listId }) {
|
||||
window.vuex.state.api.backendInteractor.deleteList({ listId })
|
||||
|
||||
delete this.allListsObject[listId]
|
||||
remove(this.allLists, list => list.id === listId)
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,30 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import fileTypeService from '../services/file_type/file_type.service.js'
|
||||
|
||||
const supportedTypes = new Set(['image', 'video', 'audio', 'flash'])
|
||||
|
||||
export const useMediaViewerStore = defineStore('mediaViewer', {
|
||||
state: () => ({
|
||||
media: [],
|
||||
currentIndex: 0,
|
||||
activated: false
|
||||
}),
|
||||
actions: {
|
||||
setMedia (attachments) {
|
||||
const media = attachments.filter(attachment => {
|
||||
const type = fileTypeService.fileType(attachment.mimetype)
|
||||
return supportedTypes.has(type)
|
||||
})
|
||||
|
||||
this.media = media
|
||||
},
|
||||
setCurrentMedia (current) {
|
||||
const index = this.media.indexOf(current)
|
||||
this.activated = true
|
||||
this.currentIndex = index
|
||||
},
|
||||
closeMediaViewer () {
|
||||
this.activated = false
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,57 @@
|
|||
import { merge } from 'lodash'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const usePollsStore = defineStore('polls', {
|
||||
state: () => ({
|
||||
// Contains key = id, value = number of trackers for this poll
|
||||
trackedPolls: {},
|
||||
pollsObject: {}
|
||||
}),
|
||||
actions: {
|
||||
mergeOrAddPoll (poll) {
|
||||
const existingPoll = this.pollsObject[poll.id]
|
||||
// Make expired-state change trigger re-renders properly
|
||||
poll.expired = Date.now() > Date.parse(poll.expires_at)
|
||||
if (existingPoll) {
|
||||
this.pollsObject[poll.id] = merge(existingPoll, poll)
|
||||
} else {
|
||||
this.pollsObject[poll.id] = poll
|
||||
}
|
||||
},
|
||||
updateTrackedPoll (pollId) {
|
||||
window.vuex.state.api.backendInteractor.fetchPoll({ pollId }).then(poll => {
|
||||
setTimeout(() => {
|
||||
if (this.trackedPolls[pollId]) {
|
||||
this.updateTrackedPoll(pollId)
|
||||
}
|
||||
}, 30 * 1000)
|
||||
this.mergeOrAddPoll(poll)
|
||||
})
|
||||
},
|
||||
trackPoll (pollId) {
|
||||
if (!this.trackedPolls[pollId]) {
|
||||
setTimeout(() => this.updateTrackedPoll(pollId), 30 * 1000)
|
||||
}
|
||||
const currentValue = this.trackedPolls[pollId]
|
||||
if (currentValue) {
|
||||
this.trackedPolls[pollId] = currentValue + 1
|
||||
} else {
|
||||
this.trackedPolls[pollId] = 1
|
||||
}
|
||||
},
|
||||
untrackPoll (pollId) {
|
||||
const currentValue = this.trackedPolls[pollId]
|
||||
if (currentValue) {
|
||||
this.trackedPolls[pollId] = currentValue - 1
|
||||
} else {
|
||||
this.trackedPolls[pollId] = 0
|
||||
}
|
||||
},
|
||||
votePoll ({ id, pollId, choices }) {
|
||||
return window.vuex.state.api.backendInteractor.vote({ pollId, choices }).then(poll => {
|
||||
this.mergeOrAddPoll(poll)
|
||||
return poll
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export const usePostStatusStore = defineStore('postStatus', {
|
||||
state: () => ({
|
||||
params: null,
|
||||
modalActivated: false
|
||||
}),
|
||||
actions: {
|
||||
openPostStatusModal (params) {
|
||||
this.params = params
|
||||
this.modalActivated = true
|
||||
},
|
||||
closePostStatusModal () {
|
||||
this.modalActivated = false
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,52 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
import filter from 'lodash/filter'
|
||||
import { useInterfaceStore } from '../stores/interface'
|
||||
|
||||
export const useReportsStore = defineStore('reports', {
|
||||
state: () => ({
|
||||
reportModal: {
|
||||
userId: null,
|
||||
statuses: [],
|
||||
preTickedIds: [],
|
||||
activated: false
|
||||
},
|
||||
reports: {}
|
||||
}),
|
||||
actions: {
|
||||
openUserReportingModal ({ userId, statusIds = [] }) {
|
||||
const preTickedStatuses = statusIds.map(id => window.vuex.state.statuses.allStatusesObject[id])
|
||||
const preTickedIds = statusIds
|
||||
const statuses = preTickedStatuses.concat(
|
||||
filter(window.vuex.state.statuses.allStatuses,
|
||||
status => status.user.id === userId && !preTickedIds.includes(status.id)
|
||||
)
|
||||
)
|
||||
|
||||
this.reportModal.userId = userId
|
||||
this.reportModal.statuses = statuses
|
||||
this.reportModal.preTickedIds = preTickedIds
|
||||
this.reportModal.activated = true
|
||||
},
|
||||
closeUserReportingModal () {
|
||||
this.reportModal.activated = false
|
||||
},
|
||||
setReportState ({ id, state }) {
|
||||
const oldState = window.vuex.state.reports.reports[id].state
|
||||
this.reports[id].state = state
|
||||
window.vuex.state.api.backendInteractor.setReportState({ id, state }).catch(e => {
|
||||
console.error('Failed to set report state', e)
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'general.generic_error_message',
|
||||
messageArgs: [e.message],
|
||||
timeout: 5000
|
||||
})
|
||||
this.reports[id].state = oldState
|
||||
})
|
||||
},
|
||||
addReport (report) {
|
||||
this.reports[report.id] = report
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,32 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useShoutStore = defineStore('shout', {
|
||||
state: () => ({
|
||||
messages: [],
|
||||
channel: { state: '' },
|
||||
joined: false
|
||||
}),
|
||||
actions: {
|
||||
initializeShout (socket) {
|
||||
const channel = socket.channel('chat:public')
|
||||
channel.joinPush.receive('ok', () => {
|
||||
this.joined = true
|
||||
})
|
||||
channel.onClose(() => {
|
||||
this.joined = false
|
||||
})
|
||||
channel.onError(() => {
|
||||
this.joined = false
|
||||
})
|
||||
channel.on('new_msg', (msg) => {
|
||||
this.messages.push(msg)
|
||||
this.messages = this.messages.slice(-19, 20)
|
||||
})
|
||||
channel.on('messages', ({ messages }) => {
|
||||
this.messages = messages.slice(-19, 20)
|
||||
})
|
||||
channel.join()
|
||||
this.channel = channel
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useStatusHistoryStore = defineStore('statusHistory', {
|
||||
state: () => ({
|
||||
params: {},
|
||||
modalActivated: false
|
||||
}),
|
||||
actions: {
|
||||
openStatusHistoryModal (params) {
|
||||
this.params = params
|
||||
this.modalActivated = true
|
||||
},
|
||||
closeStatusHistoryModal () {
|
||||
this.modalActivated = false
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,83 +0,0 @@
|
|||
import { cloneDeep } from 'lodash'
|
||||
import { defaultState, mutations, getters } from '../../../../src/modules/lists.js'
|
||||
|
||||
describe('The lists module', () => {
|
||||
describe('mutations', () => {
|
||||
it('updates array of all lists', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
const list = { id: '1', title: 'testList' }
|
||||
|
||||
mutations.setLists(state, [list])
|
||||
expect(state.allLists).to.have.length(1)
|
||||
expect(state.allLists).to.eql([list])
|
||||
})
|
||||
|
||||
it('adds a new list with a title, updating the title for existing lists', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
const list = { id: '1', title: 'testList' }
|
||||
const modList = { id: '1', title: 'anotherTestTitle' }
|
||||
|
||||
mutations.setList(state, { listId: list.id, title: list.title })
|
||||
expect(state.allListsObject[list.id]).to.eql({ title: list.title, accountIds: [] })
|
||||
expect(state.allLists).to.have.length(1)
|
||||
expect(state.allLists[0]).to.eql(list)
|
||||
|
||||
mutations.setList(state, { listId: modList.id, title: modList.title })
|
||||
expect(state.allListsObject[modList.id]).to.eql({ title: modList.title, accountIds: [] })
|
||||
expect(state.allLists).to.have.length(1)
|
||||
expect(state.allLists[0]).to.eql(modList)
|
||||
})
|
||||
|
||||
it('adds a new list with an array of IDs, updating the IDs for existing lists', () => {
|
||||
const state = cloneDeep(defaultState)
|
||||
const list = { id: '1', accountIds: ['1', '2', '3'] }
|
||||
const modList = { id: '1', accountIds: ['3', '4', '5'] }
|
||||
|
||||
mutations.setListAccounts(state, { listId: list.id, accountIds: list.accountIds })
|
||||
expect(state.allListsObject[list.id]).to.eql({ accountIds: list.accountIds })
|
||||
|
||||
mutations.setListAccounts(state, { listId: modList.id, accountIds: modList.accountIds })
|
||||
expect(state.allListsObject[modList.id]).to.eql({ accountIds: modList.accountIds })
|
||||
})
|
||||
|
||||
it('deletes a list', () => {
|
||||
const state = {
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
1: { title: 'testList', accountIds: ['1', '2', '3'] }
|
||||
}
|
||||
}
|
||||
const listId = '1'
|
||||
|
||||
mutations.deleteList(state, { listId })
|
||||
expect(state.allLists).to.have.length(0)
|
||||
expect(state.allListsObject).to.eql({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getters', () => {
|
||||
it('returns list title', () => {
|
||||
const state = {
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
1: { title: 'testList', accountIds: ['1', '2', '3'] }
|
||||
}
|
||||
}
|
||||
const id = '1'
|
||||
|
||||
expect(getters.findListTitle(state)(id)).to.eql('testList')
|
||||
})
|
||||
|
||||
it('returns list accounts', () => {
|
||||
const state = {
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
1: { title: 'testList', accountIds: ['1', '2', '3'] }
|
||||
}
|
||||
}
|
||||
const id = '1'
|
||||
|
||||
expect(getters.findListAccounts(state)(id)).to.eql(['1', '2', '3'])
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,93 @@
|
|||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { useListsStore } from '../../../../src/stores/lists.js'
|
||||
import { createStore } from 'vuex'
|
||||
import apiModule from '../../../../src/modules/api.js'
|
||||
|
||||
setActivePinia(createPinia())
|
||||
const store = useListsStore()
|
||||
window.vuex = createStore({
|
||||
modules: {
|
||||
api: apiModule
|
||||
}
|
||||
})
|
||||
|
||||
describe('The lists store', () => {
|
||||
describe('actions', () => {
|
||||
it('updates array of all lists', () => {
|
||||
store.$reset()
|
||||
const list = { id: '1', title: 'testList' }
|
||||
|
||||
store.setLists([list])
|
||||
expect(store.allLists).to.have.length(1)
|
||||
expect(store.allLists).to.eql([list])
|
||||
})
|
||||
|
||||
it('adds a new list with a title, updating the title for existing lists', () => {
|
||||
store.$reset()
|
||||
const list = { id: '1', title: 'testList' }
|
||||
const modList = { id: '1', title: 'anotherTestTitle' }
|
||||
|
||||
store.setList({ listId: list.id, title: list.title })
|
||||
expect(store.allListsObject[list.id]).to.eql({ title: list.title, accountIds: [] })
|
||||
expect(store.allLists).to.have.length(1)
|
||||
expect(store.allLists[0]).to.eql(list)
|
||||
|
||||
store.setList({ listId: modList.id, title: modList.title })
|
||||
expect(store.allListsObject[modList.id]).to.eql({ title: modList.title, accountIds: [] })
|
||||
expect(store.allLists).to.have.length(1)
|
||||
expect(store.allLists[0]).to.eql(modList)
|
||||
})
|
||||
|
||||
it('adds a new list with an array of IDs, updating the IDs for existing lists', () => {
|
||||
store.$reset()
|
||||
const list = { id: '1', accountIds: ['1', '2', '3'] }
|
||||
const modList = { id: '1', accountIds: ['3', '4', '5'] }
|
||||
|
||||
store.setListAccounts({ listId: list.id, accountIds: list.accountIds })
|
||||
expect(store.allListsObject[list.id].accountIds).to.eql(list.accountIds)
|
||||
|
||||
store.setListAccounts({ listId: modList.id, accountIds: modList.accountIds })
|
||||
expect(store.allListsObject[modList.id].accountIds).to.eql(modList.accountIds)
|
||||
})
|
||||
|
||||
it('deletes a list', () => {
|
||||
store.$patch({
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
1: { title: 'testList', accountIds: ['1', '2', '3'] }
|
||||
}
|
||||
})
|
||||
const listId = '1'
|
||||
|
||||
store.deleteList({ listId })
|
||||
expect(store.allLists).to.have.length(0)
|
||||
expect(store.allListsObject).to.eql({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getters', () => {
|
||||
it('returns list title', () => {
|
||||
store.$patch({
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
1: { title: 'testList', accountIds: ['1', '2', '3'] }
|
||||
}
|
||||
})
|
||||
const id = '1'
|
||||
|
||||
expect(store.findListTitle(id)).to.eql('testList')
|
||||
})
|
||||
|
||||
it('returns list accounts', () => {
|
||||
store.$patch({
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
1: { title: 'testList', accountIds: ['1', '2', '3'] }
|
||||
}
|
||||
})
|
||||
const id = '1'
|
||||
|
||||
expect(store.findListAccounts(id)).to.eql(['1', '2', '3'])
|
||||
})
|
||||
})
|
||||
})
|
15
yarn.lock
15
yarn.lock
|
@ -2220,6 +2220,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380"
|
||||
integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==
|
||||
|
||||
"@vue/devtools-api@^6.5.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07"
|
||||
integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==
|
||||
|
||||
"@vue/reactivity-transform@3.2.45":
|
||||
version "3.2.45"
|
||||
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz#07ac83b8138550c83dfb50db43cde1e0e5e8124d"
|
||||
|
@ -7075,6 +7080,14 @@ pify@^4.0.1:
|
|||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
|
||||
|
||||
pinia@^2.0.33:
|
||||
version "2.0.33"
|
||||
resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.33.tgz#b70065be697874d5824e9792f59bd5d87ddb5e7d"
|
||||
integrity sha512-HOj1yVV2itw6rNIrR2f7+MirGNxhORjrULL8GWgRwXsGSvEqIQ+SE0MYt6cwtpegzCda3i+rVTZM+AM7CG+kRg==
|
||||
dependencies:
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
vue-demi "*"
|
||||
|
||||
pirates@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
|
||||
|
@ -8855,7 +8868,7 @@ void-elements@^2.0.0:
|
|||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
|
||||
vue-demi@^0.13.11:
|
||||
vue-demi@*, vue-demi@^0.13.11:
|
||||
version "0.13.11"
|
||||
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99"
|
||||
integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==
|
||||
|
|
Loading…
Reference in New Issue