diff --git a/components/nav/NavFooter.vue b/components/nav/NavFooter.vue index 81ae1394..7a9a416e 100644 --- a/components/nav/NavFooter.vue +++ b/components/nav/NavFooter.vue @@ -22,9 +22,9 @@ function toggleDark() { diff --git a/components/status/StatusActionsMore.vue b/components/status/StatusActionsMore.vue index 4c0c3aa9..fe183b84 100644 --- a/components/status/StatusActionsMore.vue +++ b/components/status/StatusActionsMore.vue @@ -126,7 +126,7 @@ async function editStatus() { - + status.visibility === 'direct') - + @@ -155,7 +155,7 @@ const isDM = $computed(() => status.visibility === 'direct') - + diff --git a/components/status/StatusPreviewCard.vue b/components/status/StatusPreviewCard.vue index 58f4ba8d..f68425c3 100644 --- a/components/status/StatusPreviewCard.vue +++ b/components/status/StatusPreviewCard.vue @@ -21,7 +21,7 @@ const isSquare = $computed(() => ( )) const providerName = $computed(() => props.card.providerName ? props.card.providerName : new URL(props.card.url).hostname) -const gitHubCards = $(computedEager(() => useFeatureFlags().experimentalGitHubCards)) +const gitHubCards = $(useFeatureFlag('experimentalGitHubCards')) // TODO: handle card.type: 'photo' | 'video' | 'rich'; const cardTypeIconMap: Record = { diff --git a/components/timeline/TimelinePaginator.vue b/components/timeline/TimelinePaginator.vue index 89fe3035..23a0454c 100644 --- a/components/timeline/TimelinePaginator.vue +++ b/components/timeline/TimelinePaginator.vue @@ -12,7 +12,7 @@ const { paginator, stream } = defineProps<{ }>() const { formatNumber } = useHumanReadableNumber() -const virtualScroller = $(computedEager(() => useFeatureFlags().experimentalVirtualScroll)) +const virtualScroller = $(useFeatureFlag('experimentalVirtualScroll')) diff --git a/composables/command.ts b/composables/command.ts index 0b37f4f1..aa74e284 100644 --- a/composables/command.ts +++ b/composables/command.ts @@ -271,10 +271,10 @@ export const provideGlobalCommands = () => { scope: 'Preferences', name: () => t('command.toggle_zen_mode'), - icon: () => isZenMode.value ? 'i-ri:layout-right-2-line' : 'i-ri:layout-right-line', + icon: () => userSettings.value.zenMode ? 'i-ri:layout-right-2-line' : 'i-ri:layout-right-line', onActivate() { - toggleZenMode() + userSettings.value.zenMode = !userSettings.value.zenMode }, }) diff --git a/composables/dialog.ts b/composables/dialog.ts index db0dede0..b7d07a98 100644 --- a/composables/dialog.ts +++ b/composables/dialog.ts @@ -1,6 +1,6 @@ import type { Attachment, Status, StatusEdit } from 'masto' import type { Draft } from '~/types' -import { STORAGE_KEY_FIRST_VISIT, STORAGE_KEY_ZEN_MODE } from '~/constants' +import { STORAGE_KEY_FIRST_VISIT } from '~/constants' export const mediaPreviewList = ref([]) export const mediaPreviewIndex = ref(0) @@ -11,7 +11,6 @@ export const dialogDraftKey = ref() export const commandPanelInput = ref('') export const isFirstVisit = useLocalStorage(STORAGE_KEY_FIRST_VISIT, !process.mock) -export const isZenMode = useLocalStorage(STORAGE_KEY_ZEN_MODE, false) export const isSigninDialogOpen = ref(false) export const isPublishDialogOpen = ref(false) @@ -22,8 +21,6 @@ export const isCommandPanelOpen = ref(false) export const lastPublishDialogStatus = ref(null) -export const toggleZenMode = useToggle(isZenMode) - export function openSigninDialog() { isSigninDialogOpen.value = true } diff --git a/composables/featureFlags.ts b/composables/featureFlags.ts deleted file mode 100644 index d050f790..00000000 --- a/composables/featureFlags.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { STORAGE_KEY_FEATURE_FLAGS } from '~/constants' - -export interface FeatureFlags { - experimentalVirtualScroll: boolean - experimentalGitHubCards: boolean - experimentalUserPicker: boolean -} -export type FeatureFlagsMap = Record - -export function getDefaultFeatureFlags(): FeatureFlags { - return { - experimentalVirtualScroll: false, - experimentalGitHubCards: true, - experimentalUserPicker: true, - } -} - -export const currentUserFeatureFlags = process.server - ? computed(getDefaultFeatureFlags) - : useUserLocalStorage(STORAGE_KEY_FEATURE_FLAGS, getDefaultFeatureFlags) - -export function useFeatureFlags() { - const featureFlags = currentUserFeatureFlags.value - - return featureFlags -} - -export function toggleFeatureFlag(key: keyof FeatureFlags) { - const featureFlags = currentUserFeatureFlags.value - - if (featureFlags[key]) - featureFlags[key] = !featureFlags[key] - else - featureFlags[key] = true -} - -const userPicker = eagerComputed(() => useFeatureFlags().experimentalUserPicker) -export const showUserPicker = computed(() => useUsers().value.length > 1 && userPicker.value) diff --git a/composables/settings/featureFlags.ts b/composables/settings/featureFlags.ts new file mode 100644 index 00000000..c43cd22d --- /dev/null +++ b/composables/settings/featureFlags.ts @@ -0,0 +1,36 @@ +import type { Ref } from 'vue' +import { userSettings } from '.' + +export interface FeatureFlags { + experimentalVirtualScroll: boolean + experimentalGitHubCards: boolean + experimentalUserPicker: boolean +} +export type FeatureFlagsMap = Record + +const DEFAULT_FEATURE_FLAGS: FeatureFlags = { + experimentalVirtualScroll: false, + experimentalGitHubCards: true, + experimentalUserPicker: true, +} + +export function useFeatureFlag(name: T): Ref { + return computed({ + get() { + return getFeatureFlag(name) + }, + set(value) { + if (userSettings.value) + userSettings.value.featureFlags[name] = value + }, + }) +} + +export function getFeatureFlag(name: T): FeatureFlags[T] { + return userSettings.value?.featureFlags?.[name] ?? DEFAULT_FEATURE_FLAGS[name] +} + +export function toggleFeatureFlag(key: keyof FeatureFlags) { + const flag = useFeatureFlag(key) + flag.value = !flag.value +} diff --git a/composables/settings/index.ts b/composables/settings/index.ts new file mode 100644 index 00000000..c59283d3 --- /dev/null +++ b/composables/settings/index.ts @@ -0,0 +1,21 @@ +import type { FeatureFlags } from './featureFlags' +import type { ColorMode, FontSize } from '~/types' +import { STORAGE_KEY_SETTINGS } from '~/constants' + +export interface UserSettings { + featureFlags: Partial + colorMode?: ColorMode + fontSize?: FontSize + lang?: string + zenMode?: boolean +} + +export function getDefaultUserSettings(): UserSettings { + return { + featureFlags: {}, + } +} + +export const userSettings = process.server + ? computed(getDefaultUserSettings) + : useUserLocalStorage(STORAGE_KEY_SETTINGS, getDefaultUserSettings) diff --git a/constants/index.ts b/constants/index.ts index becf0d0d..7770ca1d 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -10,9 +10,7 @@ export const STORAGE_KEY_SERVERS = 'elk-servers' export const STORAGE_KEY_CURRENT_USER = 'elk-current-user' export const STORAGE_KEY_NOTIFY_TAB = 'elk-notify-tab' export const STORAGE_KEY_FIRST_VISIT = 'elk-first-visit' -export const STORAGE_KEY_ZEN_MODE = 'elk-zenmode' -export const STORAGE_KEY_LANG = 'elk-lang' -export const STORAGE_KEY_FEATURE_FLAGS = 'elk-feature-flags' +export const STORAGE_KEY_SETTINGS = 'elk-settings' export const STORAGE_KEY_CUSTOM_EMOJIS = 'elk-custom-emojis' export const STORAGE_KEY_HIDE_EXPLORE_POSTS_TIPS = 'elk-hide-explore-posts-tips' export const STORAGE_KEY_HIDE_EXPLORE_NEWS_TIPS = 'elk-hide-explore-news-tips' @@ -24,6 +22,6 @@ export const COOKIE_MAX_AGE = 10 * 365 * 24 * 60 * 60 * 1000 export const COOKIE_KEY_FONT_SIZE = 'elk-font-size' export const COOKIE_KEY_COLOR_MODE = 'elk-color-mode' -export const COOKIE_KEY_LOCALE = 'elk-locale' +export const COOKIE_KEY_LOCALE = 'elk-lang' export const HANDLED_MASTO_URLS = /^(https?:\/\/)?([\w\d-]+\.)+\w+\/(@[@\w\d-\.]+)(\/objects)?(\/\d+)?$/ diff --git a/layouts/default.vue b/layouts/default.vue index d51552fa..2c09b010 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -1,11 +1,18 @@ - +