Migrate interface module to store

This commit is contained in:
Sean King 2023-04-05 21:06:37 -06:00
parent 872569ae8e
commit b1dcea0199
No known key found for this signature in database
GPG Key ID: 510C52BACD6E7257
33 changed files with 244 additions and 77 deletions

View File

@ -18,6 +18,7 @@ import { windowWidth, windowHeight } from './services/window_utils/window_utils'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { defineAsyncComponent } from 'vue' import { defineAsyncComponent } from 'vue'
import { useShoutStore } from './stores/shout' import { useShoutStore } from './stores/shout'
import { useInterfaceStore } from './stores/interface'
export default { export default {
name: 'app', name: 'app',
@ -113,7 +114,7 @@ export default {
hideShoutbox () { hideShoutbox () {
return this.$store.getters.mergedConfig.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 }, privateMode () { return this.$store.state.instance.private },
reverseLayout () { reverseLayout () {
const { thirdColumnMode, sidebarRight: reverseSetting } = this.$store.getters.mergedConfig const { thirdColumnMode, sidebarRight: reverseSetting } = this.$store.getters.mergedConfig
@ -129,8 +130,8 @@ export default {
}, },
methods: { methods: {
updateMobileState () { updateMobileState () {
this.$store.dispatch('setLayoutWidth', windowWidth()) useInterfaceStore().setLayoutWidth(windowWidth())
this.$store.dispatch('setLayoutHeight', windowHeight()) useInterfaceStore().setLayoutHeight(windowHeight())
} }
} }
} }

View File

@ -18,6 +18,7 @@ import { applyTheme, applyConfig } from '../services/style_setter/style_setter.j
import FaviconService from '../services/favicon_service/favicon_service.js' import FaviconService from '../services/favicon_service/favicon_service.js'
import { useI18nStore } from '../stores/i18n' import { useI18nStore } from '../stores/i18n'
import { useInterfaceStore } from '../stores/interface'
let staticInitialResults = null let staticInitialResults = null
@ -340,12 +341,16 @@ const checkOAuthToken = async ({ store }) => {
}) })
} }
const afterStoreSetup = async ({ pinia, store, i18n }) => { const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
const app = createApp(App) const app = createApp(App)
app.use(pinia) app.use(pinia)
store.dispatch('setLayoutWidth', windowWidth()) if (storageError) {
store.dispatch('setLayoutHeight', windowHeight()) useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error' })
}
useInterfaceStore().setLayoutWidth(windowWidth())
useInterfaceStore().setLayoutHeight(windowHeight())
FaviconService.initFaviconService() FaviconService.initFaviconService()

View File

@ -1,6 +1,7 @@
import _ from 'lodash' import _ from 'lodash'
import { WSConnectionStatus } from '../../services/api/api.service.js' import { WSConnectionStatus } from '../../services/api/api.service.js'
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import ChatMessage from '../chat_message/chat_message.vue' import ChatMessage from '../chat_message/chat_message.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue' import PostStatusForm from '../post_status_form/post_status_form.vue'
import ChatTitle from '../chat_title/chat_title.vue' import ChatTitle from '../chat_title/chat_title.vue'
@ -13,6 +14,7 @@ import {
faChevronLeft faChevronLeft
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js' import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js'
import { useInterfaceStore } from '../../stores/interface.js'
library.add( library.add(
faChevronDown, faChevronDown,
@ -90,10 +92,12 @@ const Chat = {
'findOpenedChatByRecipientId', 'findOpenedChatByRecipientId',
'mergedConfig' 'mergedConfig'
]), ]),
...mapPiniaState(useInterfaceStore, {
mobileLayout: store => store.layoutType === 'mobile'
}),
...mapState({ ...mapState({
backendInteractor: state => state.api.backendInteractor, backendInteractor: state => state.api.backendInteractor,
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus, mastoUserSocketStatus: state => state.api.mastoUserSocketStatus,
mobileLayout: state => state.interface.layoutType === 'mobile',
currentUser: state => state.users.currentUser currentUser: state => state.users.currentUser
}) })
}, },

View File

@ -1,4 +1,5 @@
import { mapState, mapGetters } from 'vuex' import { mapState, mapGetters } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import Popover from '../popover/popover.vue' import Popover from '../popover/popover.vue'
import Attachment from '../attachment/attachment.vue' import Attachment from '../attachment/attachment.vue'
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
@ -12,6 +13,7 @@ import {
faTimes, faTimes,
faEllipsisH faEllipsisH
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faTimes, faTimes,
@ -65,8 +67,10 @@ const ChatMessage = {
hasAttachment () { hasAttachment () {
return this.message.attachments.length > 0 return this.message.attachments.length > 0
}, },
...mapPiniaState(useInterfaceStore, {
betterShadow: store => store.browserSupport.cssFilter
}),
...mapState({ ...mapState({
betterShadow: state => state.interface.browserSupport.cssFilter,
currentUser: state => state.users.currentUser, currentUser: state => state.users.currentUser,
restrictedNicknames: state => state.instance.restrictedNicknames restrictedNicknames: state => state.instance.restrictedNicknames
}), }),

View File

@ -14,6 +14,7 @@ import {
faCog, faCog,
faInfoCircle faInfoCircle
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faSignInAlt, faSignInAlt,
@ -107,7 +108,7 @@ export default {
this.searchBarHidden = hidden this.searchBarHidden = hidden
}, },
openSettingsModal () { openSettingsModal () {
this.$store.dispatch('openSettingsModal') useInterfaceStore().openSettingsModal()
} }
} }
} }

View File

@ -2,6 +2,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
faTimes faTimes
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faTimes faTimes
@ -10,12 +11,12 @@ library.add(
const GlobalNoticeList = { const GlobalNoticeList = {
computed: { computed: {
notices () { notices () {
return this.$store.state.interface.globalNotices return useInterfaceStore().globalNotices
} }
}, },
methods: { methods: {
closeNotice (notice) { closeNotice (notice) {
this.$store.dispatch('removeGlobalNotice', notice) useInterfaceStore().removeGlobalNotice(notice)
} }
} }
} }

View File

@ -9,6 +9,7 @@ import {
faSearch, faSearch,
faChevronLeft faChevronLeft
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faSearch, faSearch,
@ -128,7 +129,7 @@ const ListsNew = {
this.$router.push({ name: 'lists-timeline', params: { id: listId } }) this.$router.push({ name: 'lists-timeline', params: { id: listId } })
}) })
.catch((e) => { .catch((e) => {
this.$store.dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
messageKey: 'lists.error', messageKey: 'lists.error',
messageArgs: [e.message], messageArgs: [e.message],
level: 'error' level: 'error'

View File

@ -25,6 +25,7 @@ import {
faExpandAlt, faExpandAlt,
faCompressAlt faCompressAlt
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faCheck, faCheck,
@ -43,7 +44,7 @@ const Notification = {
data () { data () {
return { return {
statusExpanded: false, statusExpanded: false,
betterShadow: this.$store.state.interface.browserSupport.cssFilter, betterShadow: useInterfaceStore().browserSupport.cssFilter,
unmuted: false, unmuted: false,
showingApproveConfirmDialog: false, showingApproveConfirmDialog: false,
showingDenyConfirmDialog: false showingDenyConfirmDialog: false

View File

@ -11,6 +11,7 @@ import {
import FaviconService from '../../services/favicon_service/favicon_service.js' import FaviconService from '../../services/favicon_service/favicon_service.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons' import { faCircleNotch, faArrowUp, faMinus } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faCircleNotch, faCircleNotch,
@ -75,11 +76,11 @@ const Notifications = {
return this.$store.state.statuses.notifications.loading return this.$store.state.statuses.notifications.loading
}, },
noHeading () { noHeading () {
const { layoutType } = this.$store.state.interface const { layoutType } = useInterfaceStore()
return this.minimalMode || layoutType === 'mobile' return this.minimalMode || layoutType === 'mobile'
}, },
teleportTarget () { teleportTarget () {
const { layoutType } = this.$store.state.interface const { layoutType } = useInterfaceStore()
const map = { const map = {
wide: '#notifs-column', wide: '#notifs-column',
mobile: '#mobile-notifications' mobile: '#mobile-notifications'
@ -87,7 +88,7 @@ const Notifications = {
return map[layoutType] || '#notifs-sidebar' return map[layoutType] || '#notifs-sidebar'
}, },
popoversZLayer () { popoversZLayer () {
const { layoutType } = this.$store.state.interface const { layoutType } = useInterfaceStore()
return layoutType === 'mobile' ? 'navbar' : null return layoutType === 'mobile' ? 'navbar' : null
}, },
notificationsToDisplay () { notificationsToDisplay () {
@ -114,10 +115,10 @@ const Notifications = {
unseenCountTitle (count) { unseenCountTitle (count) {
if (count > 0) { if (count > 0) {
FaviconService.drawFaviconBadge() FaviconService.drawFaviconBadge()
this.$store.dispatch('setPageTitle', `(${count})`) useInterfaceStore().setPageTitle(`(${count})`)
} else { } else {
FaviconService.clearFaviconBadge() FaviconService.clearFaviconBadge()
this.$store.dispatch('setPageTitle', '') useInterfaceStore().setPageTitle('')
} }
}, },
teleportTarget () { teleportTarget () {

View File

@ -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 { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
import { reject, map, uniqBy, debounce } from 'lodash' import { reject, map, uniqBy, debounce } from 'lodash'
import suggestor from '../emoji_input/suggestor.js' 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 Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue' import Select from '../select/select.vue'
@ -24,6 +25,7 @@ import {
faTimes, faTimes,
faCircleNotch faCircleNotch
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface.js'
library.add( library.add(
faSmileBeam, faSmileBeam,
@ -266,8 +268,8 @@ const PostStatusForm = {
return typeof this.statusId !== 'undefined' && this.statusId.trim() !== '' return typeof this.statusId !== 'undefined' && this.statusId.trim() !== ''
}, },
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState({ ...mapState(useInterfaceStore, {
mobileLayout: state => state.interface.mobileLayout mobileLayout: store => store.mobileLayout
}) })
}, },
watch: { watch: {
@ -629,7 +631,7 @@ const PostStatusForm = {
this.idempotencyKey = Date.now().toString() this.idempotencyKey = Date.now().toString()
}, },
openProfileTab () { openProfileTab () {
this.$store.dispatch('openSettingsModalTab', 'profile') useInterfaceStore().openSettingsModalTab('profile')
}, },
propsToNative (props) { propsToNative (props) {
return propsToNative(props) return propsToNative(props)

View File

@ -2,6 +2,7 @@ import Popover from '../popover/popover.vue'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons' import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faFilter, faFilter,
@ -22,7 +23,7 @@ const QuickFilterSettings = {
this.$store.dispatch('queueFlushAll') this.$store.dispatch('queueFlushAll')
}, },
openTab (tab) { openTab (tab) {
this.$store.dispatch('openSettingsModalTab', tab) useInterfaceStore().openSettingsModalTab(tab)
} }
}, },
computed: { computed: {

View File

@ -2,6 +2,7 @@ import Popover from '../popover/popover.vue'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faList, faFolderTree, faBars, faWrench } from '@fortawesome/free-solid-svg-icons' import { faList, faFolderTree, faBars, faWrench } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faList, faList,
@ -22,7 +23,7 @@ const QuickViewSettings = {
this.$store.dispatch('setOption', { name: 'conversationDisplay', value: visibility }) this.$store.dispatch('setOption', { name: 'conversationDisplay', value: visibility })
}, },
openTab (tab) { openTab (tab) {
this.$store.dispatch('openSettingsModalTab', tab) useInterfaceStore().openSettingsModalTab(tab)
} }
}, },
computed: { computed: {

View File

@ -19,6 +19,7 @@ import {
import { import {
faWindowMinimize faWindowMinimize
} from '@fortawesome/free-regular-svg-icons' } from '@fortawesome/free-regular-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1 const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1
const PLEROMAFE_SETTINGS_MINOR_VERSION = 0 const PLEROMAFE_SETTINGS_MINOR_VERSION = 0
@ -64,10 +65,10 @@ const SettingsModal = {
}, },
methods: { methods: {
closeModal () { closeModal () {
this.$store.dispatch('closeSettingsModal') useInterfaceStore().closeSettingsModal()
}, },
peekModal () { peekModal () {
this.$store.dispatch('togglePeekSettingsModal') useInterfaceStore().togglePeekSettingsModal()
}, },
importValidator (data) { importValidator (data) {
if (!Array.isArray(data._pleroma_settings_version)) { if (!Array.isArray(data._pleroma_settings_version)) {
@ -99,7 +100,7 @@ const SettingsModal = {
} }
if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) { if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) {
this.$store.dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
level: 'warning', level: 'warning',
messageKey: 'settings.file_export_import.errors.file_slightly_new' messageKey: 'settings.file_export_import.errors.file_slightly_new'
}) })
@ -109,9 +110,9 @@ const SettingsModal = {
}, },
onImportFailure (result) { onImportFailure (result) {
if (result.error) { if (result.error) {
this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_settings_imported', level: 'error' }) useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_settings_imported', level: 'error' })
} else { } else {
this.$store.dispatch('pushGlobalNotice', { ...result.validationResult, level: 'error' }) useInterfaceStore().pushGlobalNotice({ ...result.validationResult, level: 'error' })
} }
}, },
onImport (data) { onImport (data) {
@ -151,16 +152,16 @@ const SettingsModal = {
}, },
computed: { computed: {
currentSaveStateNotice () { currentSaveStateNotice () {
return this.$store.state.interface.settings.currentSaveStateNotice return useInterfaceStore().settings.currentSaveStateNotice
}, },
modalActivated () { modalActivated () {
return this.$store.state.interface.settingsModalState !== 'hidden' return useInterfaceStore().settingsModalState !== 'hidden'
}, },
modalOpenedOnce () { modalOpenedOnce () {
return this.$store.state.interface.settingsModalLoaded return useInterfaceStore().settingsModalLoaded
}, },
modalPeeked () { modalPeeked () {
return this.$store.state.interface.settingsModalState === 'minimized' return useInterfaceStore().settingsModalState === 'minimized'
}, },
expertLevel: { expertLevel: {
get () { get () {

View File

@ -21,6 +21,7 @@ import {
faEyeSlash, faEyeSlash,
faInfo faInfo
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faWrench, faWrench,
@ -52,15 +53,15 @@ const SettingsModalContent = {
return !!this.$store.state.users.currentUser return !!this.$store.state.users.currentUser
}, },
open () { open () {
return this.$store.state.interface.settingsModalState !== 'hidden' return useInterfaceStore().settingsModalState !== 'hidden'
}, },
bodyLock () { bodyLock () {
return this.$store.state.interface.settingsModalState === 'visible' return useInterfaceStore().settingsModalState === 'visible'
} }
}, },
methods: { methods: {
onOpen () { onOpen () {
const targetTab = this.$store.state.interface.settingsModalTargetTab const targetTab = useInterfaceStore().settingsModalTargetTab
// We're being told to open in specific tab // We're being told to open in specific tab
if (targetTab) { if (targetTab) {
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => { 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 // Clear the state of target tab, so that next time settings is opened
// it doesn't force it. // it doesn't force it.
this.$store.dispatch('clearSettingsModalTargetTab') useInterfaceStore().clearSettingsModalTargetTab()
} }
}, },
mounted () { mounted () {

View File

@ -20,6 +20,7 @@ import {
faPlus, faPlus,
faCircleNotch faCircleNotch
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../../stores/interface'
library.add( library.add(
faTimes, faTimes,
@ -166,7 +167,7 @@ const ProfileTab = {
if (file.size > this.$store.state.instance[slot + 'limit']) { if (file.size > this.$store.state.instance[slot + 'limit']) {
const filesize = fileSizeFormatService.fileSizeFormat(file.size) const filesize = fileSizeFormatService.fileSizeFormat(file.size)
const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit']) const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
this.$store.dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
messageKey: 'upload.error.message', messageKey: 'upload.error.message',
messageArgs: [ messageArgs: [
this.$t('upload.error.file_too_big', { this.$t('upload.error.file_too_big', {
@ -257,7 +258,7 @@ const ProfileTab = {
.finally(() => { this.backgroundUploading = false }) .finally(() => { this.backgroundUploading = false })
}, },
displayUploadError (error) { displayUploadError (error) {
this.$store.dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
messageKey: 'upload.error.message', messageKey: 'upload.error.message',
messageArgs: [error.message], messageArgs: [error.message],
level: 'error' level: 'error'

View File

@ -38,6 +38,7 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
import Select from 'src/components/select/select.vue' import Select from 'src/components/select/select.vue'
import Preview from './preview.vue' import Preview from './preview.vue'
import { useInterfaceStore } from '../../../../stores/interface'
// List of color values used in v1 // List of color values used in v1
const v1OnlyNames = [ const v1OnlyNames = [
@ -548,7 +549,7 @@ export default {
this.loadTheme(parsed, 'file', forceSource) this.loadTheme(parsed, 'file', forceSource)
}, },
onImportFailure (result) { onImportFailure (result) {
this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' }) useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
}, },
importValidator (parsed) { importValidator (parsed) {
const version = parsed._pleroma_theme_version const version = parsed._pleroma_theme_version

View File

@ -20,6 +20,7 @@ import {
faList faList
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useShoutStore } from '../../stores/shout' import { useShoutStore } from '../../stores/shout'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faSignInAlt, faSignInAlt,
@ -85,8 +86,8 @@ const SideDrawer = {
}, },
timelinesRoute () { timelinesRoute () {
let name let name
if (this.$store.state.interface.lastTimeline) { if (useInterfaceStore().lastTimeline) {
name = this.$store.state.interface.lastTimeline name = useInterfaceStore().lastTimeline
} }
name = this.currentUser ? 'friends' : 'public-timeline' name = this.currentUser ? 'friends' : 'public-timeline'
if (USERNAME_ROUTES.has(name)) { if (USERNAME_ROUTES.has(name)) {
@ -116,7 +117,7 @@ const SideDrawer = {
GestureService.updateSwipe(e, this.closeGesture) GestureService.updateSwipe(e, this.closeGesture)
}, },
openSettingsModal () { openSettingsModal () {
this.$store.dispatch('openSettingsModal') useInterfaceStore().openSettingsModal()
} }
} }
} }

View File

@ -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 { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import { muteWordHits } from '../../services/status_parser/status_parser.js' import { muteWordHits } from '../../services/status_parser/status_parser.js'
import { unescape, uniqBy } from 'lodash' import { unescape, uniqBy } from 'lodash'
import { useInterfaceStore } from '../../stores/interface'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -379,7 +380,7 @@ const Status = {
return this.$store.state.users.currentUser return this.$store.state.users.currentUser
}, },
betterShadow () { betterShadow () {
return this.$store.state.interface.browserSupport.cssFilter return useInterfaceStore().browserSupport.cssFilter
}, },
mergedConfig () { mergedConfig () {
return this.$store.getters.mergedConfig return this.$store.getters.mergedConfig

View File

@ -1,9 +1,10 @@
// eslint-disable-next-line no-unused // eslint-disable-next-line no-unused
import { h, Fragment } from 'vue' import { h, Fragment } from 'vue'
import { mapState } from 'vuex' import { mapState } from 'pinia'
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
import './tab_switcher.scss' import './tab_switcher.scss'
import { useInterfaceStore } from '../../stores/interface'
const findFirstUsable = (slots) => slots.findIndex(_ => _.props) const findFirstUsable = (slots) => slots.findIndex(_ => _.props)
@ -64,8 +65,8 @@ export default {
settingsModalVisible () { settingsModalVisible () {
return this.settingsModalState === 'visible' return this.settingsModalState === 'visible'
}, },
...mapState({ ...mapState(useInterfaceStore, {
settingsModalState: state => state.interface.settingsModalState settingsModalState: store => store.settingsModalState
}) })
}, },
beforeUpdate () { beforeUpdate () {

View File

@ -1,5 +1,5 @@
import Status from '../status/status.vue' 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 timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
import Conversation from '../conversation/conversation.vue' import Conversation from '../conversation/conversation.vue'
import TimelineMenu from '../timeline_menu/timeline_menu.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 { debounce, throttle, keyBy } from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons' import { faCircleNotch, faCirclePlus, faCog, faMinus, faArrowUp, faCheck } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faCircleNotch, faCircleNotch,
@ -101,8 +102,8 @@ const Timeline = {
virtualScrollingEnabled () { virtualScrollingEnabled () {
return this.$store.getters.mergedConfig.virtualScrolling return this.$store.getters.mergedConfig.virtualScrolling
}, },
...mapState({ ...mapState(useInterfaceStore, {
mobileLayout: state => state.interface.layoutType === 'mobile' mobileLayout: store => store.layoutType === 'mobile'
}) })
}, },
created () { created () {

View File

@ -8,6 +8,7 @@ import { filterNavigation } from 'src/components/navigation/filter.js'
import { import {
faChevronDown faChevronDown
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useInterfaceStore } from '../../stores/interface'
library.add(faChevronDown) library.add(faChevronDown)
@ -36,7 +37,7 @@ const TimelineMenu = {
}, },
created () { created () {
if (timelineNames()[this.$route.name]) { if (timelineNames()[this.$route.name]) {
this.$store.dispatch('setLastTimeline', this.$route.name) useInterfaceStore().setLastTimeline(this.$route.name)
} }
}, },
computed: { computed: {

View File

@ -24,6 +24,7 @@ import {
faExpandAlt faExpandAlt
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { useMediaViewerStore } from '../../stores/media_viewer' import { useMediaViewerStore } from '../../stores/media_viewer'
import { useInterfaceStore } from '../../stores/interface'
library.add( library.add(
faRss, faRss,
@ -50,7 +51,7 @@ export default {
data () { data () {
return { return {
followRequestInProgress: false, followRequestInProgress: false,
betterShadow: this.$store.state.interface.browserSupport.cssFilter, betterShadow: useInterfaceStore().browserSupport.cssFilter,
showingConfirmMute: false, showingConfirmMute: false,
muteExpiryAmount: 0, muteExpiryAmount: 0,
muteExpiryUnit: 'minutes' muteExpiryUnit: 'minutes'
@ -216,7 +217,7 @@ export default {
) )
}, },
openProfileTab () { openProfileTab () {
this.$store.dispatch('openSettingsModalTab', 'profile') useInterfaceStore().openSettingsModalTab('profile')
}, },
zoomAvatar () { zoomAvatar () {
const attachment = { const attachment = {

View File

@ -1,6 +1,7 @@
import merge from 'lodash.merge' import merge from 'lodash.merge'
import localforage from 'localforage' import localforage from 'localforage'
import { each, get, set, cloneDeep } from 'lodash' import { each, get, set, cloneDeep } from 'lodash'
import { useInterfaceStore } from '../stores/interface'
let loaded = false let loaded = false
@ -76,12 +77,12 @@ export default function createPersistedState ({
.then(success => { .then(success => {
if (typeof success !== 'undefined') { if (typeof success !== 'undefined') {
if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') { if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {
store.dispatch('settingsSaved', { success }) useInterfaceStore().settingsSaved({ success })
} }
} }
}, error => { }, error => {
if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') { if (mutation.type === 'setOption' || mutation.type === 'setCurrentUser') {
store.dispatch('settingsSaved', { error }) useInterfaceStore().settingsSaved({ error })
} }
}) })
} }

View File

@ -1,8 +1,10 @@
import { useInterfaceStore } from '../stores/interface'
export default (store) => { export default (store) => {
store.subscribe((mutation, state) => { store.subscribe((mutation, state) => {
const vapidPublicKey = state.instance.vapidPublicKey const vapidPublicKey = state.instance.vapidPublicKey
const webPushNotification = state.config.webPushNotifications const webPushNotification = state.config.webPushNotifications
const permission = state.interface.notificationPermission === 'granted' const permission = useInterfaceStore().notificationPermission === 'granted'
const user = state.users.currentUser const user = state.users.currentUser
const isUserMutation = mutation.type === 'setCurrentUser' const isUserMutation = mutation.type === 'setCurrentUser'

View File

@ -4,7 +4,6 @@ import { createPinia } from 'pinia'
import 'custom-event-polyfill' import 'custom-event-polyfill'
import './lib/event_target_polyfill.js' import './lib/event_target_polyfill.js'
import interfaceModule from './modules/interface.js'
import instanceModule from './modules/instance.js' import instanceModule from './modules/instance.js'
import statusesModule from './modules/statuses.js' import statusesModule from './modules/statuses.js'
import listsModule from './modules/lists.js' import listsModule from './modules/lists.js'
@ -62,9 +61,10 @@ const persistedStateOptions = {
console.error(e) console.error(e)
storageError = true storageError = true
} }
const store = createStore({
// Temporarily storing as a global variable while we migrate to Pinia
window.vuex = createStore({
modules: { modules: {
interface: interfaceModule,
instance: instanceModule, instance: instanceModule,
// TODO refactor users/statuses modules, they depend on each other // TODO refactor users/statuses modules, they depend on each other
users: usersModule, users: usersModule,
@ -87,12 +87,10 @@ const persistedStateOptions = {
// strict: process.env.NODE_ENV !== 'production' // strict: process.env.NODE_ENV !== 'production'
}) })
if (storageError) { const store = window.vuex
store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
}
// Temporarily passing both vuex and pinia stores until migration is fully complete. // Temporarily passing pinia and vuex stores along with storageError result until migration is fully complete.
afterStoreSetup({ pinia, store, i18n }) afterStoreSetup({ pinia, store, storageError, i18n })
})() })()
// These are inlined by webpack's DefinePlugin // These are inlined by webpack's DefinePlugin

View File

@ -3,6 +3,7 @@ import { WSConnectionStatus } from '../services/api/api.service.js'
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js' import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
import { Socket } from 'phoenix' import { Socket } from 'phoenix'
import { useShoutStore } from '../stores/shout.js' import { useShoutStore } from '../stores/shout.js'
import { useInterfaceStore } from '../stores/interface.js'
const retryTimeout = (multiplier) => 1000 * multiplier const retryTimeout = (multiplier) => 1000 * multiplier
@ -132,7 +133,7 @@ const api = {
state.mastoUserSocket.addEventListener('open', () => { state.mastoUserSocket.addEventListener('open', () => {
// Do not show notification when we just opened up the page // Do not show notification when we just opened up the page
if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) { if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) {
dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
level: 'success', level: 'success',
messageKey: 'timeline.socket_reconnected', messageKey: 'timeline.socket_reconnected',
timeout: 5000 timeout: 5000
@ -174,7 +175,7 @@ const api = {
dispatch('startFetchingTimeline', { timeline: 'friends' }) dispatch('startFetchingTimeline', { timeline: 'friends' })
dispatch('startFetchingNotifications') dispatch('startFetchingNotifications')
dispatch('startFetchingChats') dispatch('startFetchingChats')
dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
level: 'error', level: 'error',
messageKey: 'timeline.socket_broke', messageKey: 'timeline.socket_broke',
messageArgs: [code], messageArgs: [code],

View File

@ -3,6 +3,7 @@ import { setPreset, applyTheme, applyConfig } from '../services/style_setter/sty
import messages from '../i18n/messages' import messages from '../i18n/messages'
import localeService from '../services/locale/locale.service.js' import localeService from '../services/locale/locale.service.js'
import { useI18nStore } from '../stores/i18n.js' import { useI18nStore } from '../stores/i18n.js'
import { useInterfaceStore } from '../stores/interface.js'
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage' const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
@ -203,7 +204,7 @@ const config = {
) )
break break
case 'thirdColumnMode': case 'thirdColumnMode':
dispatch('setLayoutWidth', undefined) useInterfaceStore().setLayoutWidth(undefined)
break break
} }
} }

View File

@ -3,6 +3,7 @@ import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import apiService from '../services/api/api.service.js' import apiService from '../services/api/api.service.js'
import { instanceDefaultProperties } from './config.js' import { instanceDefaultProperties } from './config.js'
import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js' import { langCodeToCldrName, ensureFinalFallback } from '../i18n/languages.js'
import { useInterfaceStore } from '../stores/interface.js'
const SORTED_EMOJI_GROUP_IDS = [ const SORTED_EMOJI_GROUP_IDS = [
'smileys-and-emotion', 'smileys-and-emotion',
@ -261,7 +262,7 @@ const instance = {
commit('setInstanceOption', { name, value }) commit('setInstanceOption', { name, value })
switch (name) { switch (name) {
case 'name': case 'name':
dispatch('setPageTitle') useInterfaceStore().setPageTitle()
break break
case 'shoutAvailable': case 'shoutAvailable':
if (value) { if (value) {

View File

@ -1,4 +1,5 @@
import filter from 'lodash/filter' import filter from 'lodash/filter'
import { useInterfaceStore } from '../stores/interface'
const reports = { const reports = {
state: { state: {
@ -46,7 +47,7 @@ const reports = {
commit('setReportState', { id, state }) commit('setReportState', { id, state })
rootState.api.backendInteractor.setReportState({ id, state }).catch(e => { rootState.api.backendInteractor.setReportState({ id, state }).catch(e => {
console.error('Failed to set report state', e) console.error('Failed to set report state', e)
dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
level: 'error', level: 'error',
messageKey: 'general.generic_error_message', messageKey: 'general.generic_error_message',
messageArgs: [e.message], messageArgs: [e.message],

View File

@ -3,6 +3,7 @@ import { windowWidth, windowHeight } from '../services/window_utils/window_utils
import oauthApi from '../services/new_api/oauth.js' import oauthApi from '../services/new_api/oauth.js'
import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash' import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'lodash'
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js' import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
import { useInterfaceStore } from '../stores/interface.js'
// TODO: Unify with mergeOrAdd in statuses.js // TODO: Unify with mergeOrAdd in statuses.js
export const mergeOrAdd = (arr, obj, item) => { export const mergeOrAdd = (arr, obj, item) => {
@ -542,9 +543,9 @@ const users = {
store.commit('clearNotifications') store.commit('clearNotifications')
store.commit('resetStatuses') store.commit('resetStatuses')
store.dispatch('resetChats') store.dispatch('resetChats')
store.dispatch('setLastTimeline', 'public-timeline') useInterfaceStore().setLastTimeline('public-timeline')
store.dispatch('setLayoutWidth', windowWidth()) useInterfaceStore().setLayoutWidth(windowWidth())
store.dispatch('setLayoutHeight', windowHeight()) useInterfaceStore().setLayoutHeight(windowHeight())
store.commit('clearServerSideStorage') store.commit('clearServerSideStorage')
}) })
}, },
@ -568,7 +569,7 @@ const users = {
store.dispatch('fetchEmoji') store.dispatch('fetchEmoji')
getNotificationPermission() getNotificationPermission()
.then(permission => commit('setNotificationPermission', permission)) .then(permission => useInterfaceStore().setNotificationPermission(permission))
// Set our new backend interactor // Set our new backend interactor
commit('setBackendInteractor', backendInteractorService(accessToken)) commit('setBackendInteractor', backendInteractorService(accessToken))
@ -614,8 +615,8 @@ const users = {
// Get user mutes // Get user mutes
store.dispatch('fetchMutes') store.dispatch('fetchMutes')
store.dispatch('setLayoutWidth', windowWidth()) useInterfaceStore().setLayoutWidth(windowWidth())
store.dispatch('setLayoutHeight', windowHeight()) useInterfaceStore().setLayoutHeight(windowHeight())
// Fetch our friends // Fetch our friends
store.rootState.api.backendInteractor.fetchFriends({ id: user.id }) store.rootState.api.backendInteractor.fetchFriends({ id: user.id })

View File

@ -1,3 +1,4 @@
import { useInterfaceStore } from '../../stores/interface.js'
import apiService from '../api/api.service.js' import apiService from '../api/api.service.js'
import { promiseInterval } from '../promise_interval/promise_interval.js' import { promiseInterval } from '../promise_interval/promise_interval.js'
@ -70,7 +71,7 @@ const fetchNotifications = ({ store, args, older }) => {
return notifications return notifications
}) })
.catch((error) => { .catch((error) => {
store.dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
level: 'error', level: 'error',
messageKey: 'notifications.error', messageKey: 'notifications.error',
messageArgs: [error.message], messageArgs: [error.message],

View File

@ -2,6 +2,7 @@ import { camelCase } from 'lodash'
import apiService from '../api/api.service.js' import apiService from '../api/api.service.js'
import { promiseInterval } from '../promise_interval/promise_interval.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 update = ({ store, statuses, timeline, showImmediately, userId, listId, pagination }) => {
const ccTimeline = camelCase(timeline) const ccTimeline = camelCase(timeline)
@ -69,7 +70,7 @@ const fetchAndUpdate = ({
return { statuses, pagination } return { statuses, pagination }
}) })
.catch((error) => { .catch((error) => {
store.dispatch('pushGlobalNotice', { useInterfaceStore().pushGlobalNotice({
level: 'error', level: 'error',
messageKey: 'timeline.error', messageKey: 'timeline.error',
messageArgs: [error.message], messageArgs: [error.message],

126
src/stores/interface.js Normal file
View File

@ -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
}
}
})