diff --git a/changelog.d/migrate-auth-flow-pinia.skip b/changelog.d/migrate-auth-flow-pinia.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/small-fixes.skip b/changelog.d/small-fixes.skip new file mode 100644 index 000000000..e69de29bb diff --git a/changelog.d/unify-show-hide-buttons.add b/changelog.d/unify-show-hide-buttons.add new file mode 100644 index 000000000..663bc38a5 --- /dev/null +++ b/changelog.d/unify-show-hide-buttons.add @@ -0,0 +1 @@ +Unify show/hide content buttons diff --git a/index.html b/index.html index f279ed01a..86d15971b 100644 --- a/index.html +++ b/index.html @@ -135,6 +135,7 @@ + diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 39fc6f6f0..05ddda982 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -21,6 +21,7 @@ import { useOAuthStore } from 'src/stores/oauth' import { useI18nStore } from 'src/stores/i18n' import { useInterfaceStore } from 'src/stores/interface' import { useAnnouncementsStore } from 'src/stores/announcements' +import { useAuthFlowStore } from 'src/stores/auth_flow' let staticInitialResults = null @@ -156,7 +157,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { : config.logoMargin }) copyInstanceOption('logoLeft') - store.commit('authFlow/setInitialStrategy', config.loginMethod) + useAuthFlowStore().setInitialStrategy(config.loginMethod) copyInstanceOption('redirectRootNoLogin') copyInstanceOption('redirectRootLogin') diff --git a/src/components/auth_form/auth_form.js b/src/components/auth_form/auth_form.js index a86a3dca2..243cbf574 100644 --- a/src/components/auth_form/auth_form.js +++ b/src/components/auth_form/auth_form.js @@ -2,7 +2,8 @@ import { h, resolveComponent } from 'vue' import LoginForm from '../login_form/login_form.vue' import MFARecoveryForm from '../mfa_form/recovery_form.vue' import MFATOTPForm from '../mfa_form/totp_form.vue' -import { mapGetters } from 'vuex' +import { mapState } from 'pinia' +import { useAuthFlowStore } from 'src/stores/auth_flow' const AuthForm = { name: 'AuthForm', @@ -15,7 +16,7 @@ const AuthForm = { if (this.requiredRecovery) { return 'MFARecoveryForm' } return 'LoginForm' }, - ...mapGetters('authFlow', ['requiredTOTP', 'requiredRecovery']) + ...mapState(useAuthFlowStore, ['requiredTOTP', 'requiredRecovery']) }, components: { MFARecoveryForm, diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index 26b67cfe8..bcdf435fc 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -26,7 +26,7 @@ class="textColor unstyled" :class="{ disabled: !present || disabled }" type="text" - :value="modelValue || fallback" + :value="modelValue ?? fallback" :disabled="!present || disabled" @input="updateValue($event.target.value)" > diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index ed3f5dfc6..9566aa903 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -1,7 +1,8 @@ -import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' -import { mapStores } from 'pinia' +import { mapState } from 'vuex' +import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import oauthApi from '../../services/new_api/oauth.js' import { useOAuthStore } from 'src/stores/oauth.js' +import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes @@ -25,13 +26,10 @@ const LoginForm = { instance: state => state.instance, loggingIn: state => state.users.loggingIn, }), - ...mapGetters( - 'authFlow', ['requiredPassword', 'requiredToken', 'requiredMFA'] - ) + ...mapPiniaState(useAuthFlowStore, ['requiredPassword', 'requiredToken', 'requiredMFA']) }, methods: { - ...mapMutations('authFlow', ['requireMFA']), - ...mapActions({ login: 'authFlow/login' }), + ...mapActions(useAuthFlowStore, ['requireMFA', 'login']), submit () { this.isTokenAuth ? this.submitToken() : this.submitPassword() }, diff --git a/src/components/mfa_form/recovery_form.js b/src/components/mfa_form/recovery_form.js index 2d0f4fdff..84479b1ec 100644 --- a/src/components/mfa_form/recovery_form.js +++ b/src/components/mfa_form/recovery_form.js @@ -1,7 +1,8 @@ import mfaApi from '../../services/new_api/mfa.js' -import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' -import { mapStores } from 'pinia' +import { mapState } from 'vuex' +import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' +import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes @@ -17,8 +18,8 @@ export default { error: false }), computed: { - ...mapGetters({ - authSettings: 'authFlow/settings' + ...mapPiniaState(useAuthFlowStore, { + authSettings: store => store.settings }), ...mapStores(useOAuthStore), ...mapState({ @@ -26,8 +27,7 @@ export default { }) }, methods: { - ...mapMutations('authFlow', ['requireTOTP', 'abortMFA']), - ...mapActions({ login: 'authFlow/login' }), + ...mapActions(useAuthFlowStore, ['requireTOTP', 'abortMFA', 'login']), clearError () { this.error = false }, focusOnCodeInput () { diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js index 857d055ff..e369d8a5d 100644 --- a/src/components/mfa_form/totp_form.js +++ b/src/components/mfa_form/totp_form.js @@ -1,7 +1,8 @@ import mfaApi from '../../services/new_api/mfa.js' -import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' -import { mapStores } from 'pinia' +import { mapState } from 'vuex' +import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' +import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes @@ -17,8 +18,8 @@ export default { error: false }), computed: { - ...mapGetters({ - authSettings: 'authFlow/settings' + ...mapPiniaState(useAuthFlowStore, { + authSettings: store => store.settings }), ...mapStores(useOAuthStore), ...mapState({ @@ -26,8 +27,7 @@ export default { }) }, methods: { - ...mapMutations('authFlow', ['requireRecovery', 'abortMFA']), - ...mapActions({ login: 'authFlow/login' }), + ...mapActions(useAuthFlowStore, ['requireRecovery', 'abortMFA', 'login']), clearError () { this.error = false }, focusOnCodeInput () { diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.js b/src/components/settings_modal/tabs/style_tab/style_tab.js index 8324263cd..82c63f503 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.js +++ b/src/components/settings_modal/tabs/style_tab/style_tab.js @@ -372,6 +372,9 @@ export default { const path = getPath(component, directive) usedRule = get(real, path) // get real + if (usedRule === '') { + return usedRule + } if (!usedRule) { usedRule = get(fallback, path) } @@ -379,7 +382,7 @@ export default { return postProcess(usedRule) }, set (value) { - if (value) { + if (value != null) { set(allEditedRules.value, getPath(component, directive), value) } else { unset(allEditedRules.value, getPath(component, directive)) diff --git a/src/components/status_body/status_body.js b/src/components/status_body/status_body.js index b8f6f9a0b..aa334499e 100644 --- a/src/components/status_body/status_body.js +++ b/src/components/status_body/status_body.js @@ -72,6 +72,23 @@ const StatusContent = { hideTallStatus () { return this.mightHideBecauseTall && !this.showingTall }, + shouldShowToggle () { + return this.mightHideBecauseSubject || this.mightHideBecauseTall + }, + toggleButtonClasses () { + return { + 'cw-status-hider': !this.showingMore && this.mightHideBecauseSubject, + 'tall-status-hider': !this.showingMore && this.mightHideBecauseTall, + 'status-unhider': this.showingMore, + } + }, + toggleText () { + if (this.showingMore) { + return this.mightHideBecauseSubject ? this.$t('status.hide_content') : this.$t('general.show_less') + } else { + return this.mightHideBecauseSubject ? this.$t('status.show_content') : this.$t('general.show_more') + } + }, showingMore () { return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject) }, diff --git a/src/components/status_body/status_body.scss b/src/components/status_body/status_body.scss index 07fc8ef45..f89ea8d89 100644 --- a/src/components/status_body/status_body.scss +++ b/src/components/status_body/status_body.scss @@ -62,7 +62,6 @@ &.-tall-status { position: relative; height: 16em; - overflow: hidden; z-index: 1; .media-body { @@ -82,6 +81,10 @@ mask-composite: exclude; } } + + &.-expanded { + overflow: visible; + } } & .tall-status-hider, @@ -95,6 +98,13 @@ text-align: center; } + .status-unhider { + margin-top: auto; + position: sticky; + bottom: 0; + padding-bottom: 1em; + } + .tall-status-hider { position: absolute; height: 5em; @@ -118,6 +128,10 @@ } } + .toggle-button { + padding: 0.5em; + } + &.-compact { align-items: start; flex-direction: row; @@ -166,11 +180,11 @@ line-height: inherit; margin: 0; border: none; - display: inline-block; } .text-wrapper { display: inline-block; + width: 100%; } } } diff --git a/src/components/status_body/status_body.vue b/src/components/status_body/status_body.vue index 16894021b..0fc024b04 100644 --- a/src/components/status_body/status_body.vue +++ b/src/components/status_body/status_body.vue @@ -31,17 +31,9 @@
- - - - + +
diff --git a/src/modules/auth_flow.js b/src/modules/auth_flow.js deleted file mode 100644 index dfa673db9..000000000 --- a/src/modules/auth_flow.js +++ /dev/null @@ -1,86 +0,0 @@ -import { useOAuthStore } from 'src/stores/oauth.js' - -const PASSWORD_STRATEGY = 'password' -const TOKEN_STRATEGY = 'token' - -// MFA strategies -const TOTP_STRATEGY = 'totp' -const RECOVERY_STRATEGY = 'recovery' - -// initial state -const state = { - settings: {}, - strategy: PASSWORD_STRATEGY, - initStrategy: PASSWORD_STRATEGY // default strategy from config -} - -const resetState = (state) => { - state.strategy = state.initStrategy - state.settings = {} -} - -// getters -const getters = { - settings: (state) => { - return state.settings - }, - requiredPassword: (state) => { - return state.strategy === PASSWORD_STRATEGY - }, - requiredToken: (state) => { - return state.strategy === TOKEN_STRATEGY - }, - requiredTOTP: (state) => { - return state.strategy === TOTP_STRATEGY - }, - requiredRecovery: (state) => { - return state.strategy === RECOVERY_STRATEGY - } -} - -// mutations -const mutations = { - setInitialStrategy (state, strategy) { - if (strategy) { - state.initStrategy = strategy - state.strategy = strategy - } - }, - requirePassword (state) { - state.strategy = PASSWORD_STRATEGY - }, - requireToken (state) { - state.strategy = TOKEN_STRATEGY - }, - requireMFA (state, { settings }) { - state.settings = settings - state.strategy = TOTP_STRATEGY // default strategy of MFA - }, - requireRecovery (state) { - state.strategy = RECOVERY_STRATEGY - }, - requireTOTP (state) { - state.strategy = TOTP_STRATEGY - }, - abortMFA (state) { - resetState(state) - } -} - -// actions -const actions = { - - async login ({ state, dispatch }, { access_token: accessToken }) { - useOAuthStore().setToken(accessToken) - await dispatch('loginUser', accessToken, { root: true }) - resetState(state) - } -} - -export default { - namespaced: true, - state, - getters, - mutations, - actions -} diff --git a/src/modules/index.js b/src/modules/index.js index 7f0aa3706..fd233585d 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -6,7 +6,6 @@ import api from './api.js' import config from './config.js' import profileConfig from './profileConfig.js' import adminSettings from './adminSettings.js' -import authFlow from './auth_flow.js' import drafts from './drafts.js' import chats from './chats.js' @@ -19,7 +18,6 @@ export default { config, profileConfig, adminSettings, - authFlow, drafts, chats } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 13606cdd9..ef4a6f5c4 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -242,7 +242,7 @@ export const applyConfig = (input) => { .map(([k, v]) => `--${k}: ${v}`).join(';') document.getElementById('style-config')?.remove() - const styleEl = document.createElement('style') + const styleEl = document.getElementById('theme-holder') styleEl.id = 'style-config' head.appendChild(styleEl) const styleSheet = styleEl.sheet diff --git a/src/stores/auth_flow.js b/src/stores/auth_flow.js new file mode 100644 index 000000000..e41a9bc5e --- /dev/null +++ b/src/stores/auth_flow.js @@ -0,0 +1,69 @@ +import { useOAuthStore } from 'src/stores/oauth.js' +import { defineStore } from 'pinia' + +const PASSWORD_STRATEGY = 'password' +const TOKEN_STRATEGY = 'token' + +// MFA strategies +const TOTP_STRATEGY = 'totp' +const RECOVERY_STRATEGY = 'recovery' + +export const useAuthFlowStore = defineStore('authFlow', { + // initial state + state: () => ({ + settings: {}, + strategy: PASSWORD_STRATEGY, + initStrategy: PASSWORD_STRATEGY // default strategy from config + }), + // getters + getters: { + requiredPassword: (state) => { + return state.strategy === PASSWORD_STRATEGY + }, + requiredToken: (state) => { + return state.strategy === TOKEN_STRATEGY + }, + requiredTOTP: (state) => { + return state.strategy === TOTP_STRATEGY + }, + requiredRecovery: (state) => { + return state.strategy === RECOVERY_STRATEGY + }, + }, + actions: { + setInitialStrategy (strategy) { + if (strategy) { + this.initStrategy = strategy + this.strategy = strategy + } + }, + requirePassword () { + this.strategy = PASSWORD_STRATEGY + }, + requireToken () { + this.strategy = TOKEN_STRATEGY + }, + requireMFA ({ settings }) { + this.settings = settings + this.strategy = TOTP_STRATEGY // default strategy of MFA + }, + requireRecovery () { + this.strategy = RECOVERY_STRATEGY + }, + requireTOTP () { + this.strategy = TOTP_STRATEGY + }, + abortMFA () { + this.resetState() + }, + resetState () { + this.strategy = this.initStrategy + this.settings = {} + }, + async login ({ access_token: accessToken }) { + useOAuthStore().setToken(accessToken) + await window.vuex.dispatch('loginUser', accessToken, { root: true }) + this.resetState() + } + } +}) \ No newline at end of file diff --git a/src/sw.js b/src/sw.js index 7ba910589..834c68e7f 100644 --- a/src/sw.js +++ b/src/sw.js @@ -223,7 +223,8 @@ self.addEventListener('fetch', (event) => { // Do not mess up with remote things const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin if (shouldCache && event.request.method === 'GET' && isSameOrigin && isNotMedia(event.request)) { - console.debug('[Service worker] fetch:', event.request.url) + // this is a bit spammy + // console.debug('[Service worker] fetch:', event.request.url) event.respondWith((async () => { const r = await caches.match(event.request) const isEmojiReq = isEmoji(event.request)