diff --git a/changelog.d/oauth-store-to-pinia.change b/changelog.d/oauth-store-to-pinia.change deleted file mode 100644 index b18a489b5..000000000 --- a/changelog.d/oauth-store-to-pinia.change +++ /dev/null @@ -1 +0,0 @@ -Internal: Migrate OAuth store to pinia diff --git a/changelog.d/roundup5.skip b/changelog.d/roundup5.skip deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 1b133a089..05aaf7d3b 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -1,4 +1,3 @@ -/* global process */ import { createApp } from 'vue' import { createRouter, createWebHistory } from 'vue-router' import vClickOutside from 'click-outside-vue3' @@ -17,7 +16,6 @@ import { applyConfig } from '../services/style_setter/style_setter.js' import FaviconService from '../services/favicon_service/favicon_service.js' import { initServiceWorker, updateFocus } from '../services/sw/sw.js' -import { useOAuthStore } from 'src/stores/oauth' import { useI18nStore } from 'src/stores/i18n' import { useInterfaceStore } from 'src/stores/interface' import { useAnnouncementsStore } from 'src/stores/announcements' @@ -229,9 +227,8 @@ const getStickers = async ({ store }) => { } const getAppSecret = async ({ store }) => { - const oauth = useOAuthStore() - if (oauth.userToken) { - store.commit('setBackendInteractor', backendInteractorService(oauth.getToken)) + if (store.state.oauth.userToken) { + store.commit('setBackendInteractor', backendInteractorService(store.getters.getToken())) } } @@ -325,65 +322,20 @@ const setConfig = async ({ store }) => { const apiConfig = configInfos[0] const staticConfig = configInfos[1] - getAppSecret({ store }) - await setSettings({ store, apiConfig, staticConfig }) + await setSettings({ store, apiConfig, staticConfig }).then(getAppSecret({ store })) } const checkOAuthToken = async ({ store }) => { - const oauth = useOAuthStore() - if (oauth.getUserToken) { - return store.dispatch('loginUser', oauth.getUserToken) + if (store.getters.getUserToken()) { + return store.dispatch('loginUser', store.getters.getUserToken()) } return Promise.resolve() } const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => { const app = createApp(App) - // Must have app use pinia before we do anything that touches the store - // https://pinia.vuejs.org/core-concepts/plugins.html#Introduction - // "Plugins are only applied to stores created after the plugins themselves, and after pinia is passed to the app, otherwise they won't be applied." app.use(pinia) - const waitForAllStoresToLoad = async () => { - // the stores that do not persist technically do not need to be awaited here, - // but that involves either hard-coding the stores in some place (prone to errors) - // or writing another vite plugin to analyze which stores needs persisting (++load time) - const allStores = import.meta.glob('../stores/*.js', { eager: true }) - if (process.env.NODE_ENV === 'development') { - // do some checks to avoid common errors - if (!Object.keys(allStores).length) { - throw new Error('No stores are available. Check the code in src/boot/after_store.js') - } - } - await Promise.all( - Object.entries(allStores) - .map(async ([name, mod]) => { - const isStoreName = name => name.startsWith('use') - if (process.env.NODE_ENV === 'development') { - if (Object.keys(mod).filter(isStoreName).length !== 1) { - throw new Error('Each store file must export exactly one store as a named export. Check your code in src/stores/') - } - } - const storeFuncName = Object.keys(mod).find(isStoreName) - if (storeFuncName && typeof mod[storeFuncName] === 'function') { - const p = mod[storeFuncName]().$persistLoaded - if (!(p instanceof Promise)) { - throw new Error(`${name} store's $persistLoaded is not a Promise. The persist plugin is not applied.`) - } - await p - } else { - throw new Error(`Store module ${name} does not export a 'use...' function`) - } - })) - } - - try { - await waitForAllStoresToLoad() - } catch (e) { - console.error('Cannot load stores:', e) - storageError = e - } - if (storageError) { useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error' }) } diff --git a/src/components/checkbox/checkbox.vue b/src/components/checkbox/checkbox.vue index c8bba4c44..f0a4b7322 100644 --- a/src/components/checkbox/checkbox.vue +++ b/src/components/checkbox/checkbox.vue @@ -69,6 +69,15 @@ export default { display: inline-block; min-height: 1.2em; + &.-radio { + .checkbox-indicator { + &, + &::before { + border-radius: 9999px; + } + } + } + &-indicator, & .label { vertical-align: middle; @@ -108,19 +117,6 @@ export default { box-sizing: border-box; } - &.-radio { - .checkbox-indicator { - &, - &::before { - border-radius: 9999px; - } - - &::before { - content: "•"; - } - } - } - .disabled { .checkbox-indicator::before { background-color: var(--background); diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index ed3f5dfc6..23ac86bfb 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -1,7 +1,5 @@ import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' -import { mapStores } from 'pinia' import oauthApi from '../../services/new_api/oauth.js' -import { useOAuthStore } from 'src/stores/oauth.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes @@ -19,11 +17,11 @@ const LoginForm = { computed: { isPasswordAuth () { return this.requiredPassword }, isTokenAuth () { return this.requiredToken }, - ...mapStores(useOAuthStore), ...mapState({ registrationOpen: state => state.instance.registrationOpen, instance: state => state.instance, loggingIn: state => state.users.loggingIn, + oauth: state => state.oauth }), ...mapGetters( 'authFlow', ['requiredPassword', 'requiredToken', 'requiredMFA'] @@ -43,30 +41,37 @@ const LoginForm = { // NOTE: we do not really need the app token, but obtaining a token and // calling verify_credentials is the only way to ensure the app still works. - this.oauthStore.ensureAppToken() + this.$store.dispatch('ensureAppToken') .then(() => { const app = { - clientId: this.oauthStore.clientId, - clientSecret: this.oauthStore.clientSecret, + clientId: this.oauth.clientId, + clientSecret: this.oauth.clientSecret, } oauthApi.login({ ...app, ...data }) }) }, submitPassword () { + const { clientId } = this.oauth + const data = { + clientId, + oauth: this.oauth, + instance: this.instance.server, + commit: this.$store.commit + } this.error = false // NOTE: we do not really need the app token, but obtaining a token and // calling verify_credentials is the only way to ensure the app still works. - this.oauthStore.ensureAppToken().then(() => { + this.$store.dispatch('ensureAppToken').then(() => { const app = { - clientId: this.oauthStore.clientId, - clientSecret: this.oauthStore.clientSecret, + clientId: this.oauth.clientId, + clientSecret: this.oauth.clientSecret, } oauthApi.getTokenWithCredentials( { ...app, - instance: this.instance.server, + instance: data.instance, username: this.user.username, password: this.user.password } diff --git a/src/components/oauth_callback/oauth_callback.js b/src/components/oauth_callback/oauth_callback.js index 02e4c9ffc..a3c7b7f98 100644 --- a/src/components/oauth_callback/oauth_callback.js +++ b/src/components/oauth_callback/oauth_callback.js @@ -1,12 +1,10 @@ import oauth from '../../services/new_api/oauth.js' -import { useOAuthStore } from 'src/stores/oauth.js' const oac = { props: ['code'], mounted () { if (this.code) { - const oauthStore = useOAuthStore() - const { clientId, clientSecret } = oauthStore + const { clientId, clientSecret } = this.$store.state.oauth oauth.getToken({ clientId, @@ -14,7 +12,7 @@ const oac = { instance: this.$store.state.instance.server, code: this.code }).then((result) => { - oauthStore.setToken(result.access_token) + this.$store.commit('setToken', result.access_token) this.$store.dispatch('loginUser', result.access_token) this.$router.push({ name: 'friends' }) }) diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index a1b7808f2..f56b68b83 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -2,6 +2,7 @@ import Timeago from 'components/timeago/timeago.vue' import genRandomSeed from '../../services/random_seed/random_seed.service.js' import RichContent from 'components/rich_content/rich_content.jsx' import Checkbox from 'components/checkbox/checkbox.vue' +import { forEach, map } from 'lodash' import { usePollsStore } from 'src/stores/polls' export default { @@ -45,13 +46,6 @@ export default { expired () { return (this.poll && this.poll.expired) || false }, - expirationLabel () { - if (this.$store.getters.mergedConfig.useAbsoluteTimeFormat) { - return this.expired ? 'polls.expired_at' : 'polls.expires_at' - } else { - return this.expired ? 'polls.expired' : 'polls.expires_in' - } - }, loggedIn () { return this.$store.state.users.currentUser }, diff --git a/src/components/poll/poll.scss b/src/components/poll/poll.scss index d56358420..4cf13a1fd 100644 --- a/src/components/poll/poll.scss +++ b/src/components/poll/poll.scss @@ -47,12 +47,18 @@ width: 3.5em; } + .footer { + display: flex; + align-items: center; + flex-wrap: wrap; + } + &.loading * { cursor: progress; } .poll-vote-button { - padding: 0 1em; + padding: 0 0.5em; margin-right: 0.5em; } diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue index 24de14200..b3289c89c 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -44,6 +44,7 @@ :model-value="choices[index]" @update:model-value="value => activateOption(index, value)" > + {{ choices[index] }} -