diff --git a/.github/workflows/frontend-deploy.yml b/.github/workflows/frontend-deploy.yml index 703431d240..79b73c57ef 100644 --- a/.github/workflows/frontend-deploy.yml +++ b/.github/workflows/frontend-deploy.yml @@ -125,8 +125,7 @@ jobs: run: pnpm install - name: Build frontend - working-directory: ./apps/frontend - run: pnpm build + run: pnpm web:build env: CF_PAGES_BRANCH: ${{ inputs.head_ref || github.ref_name }} CF_PAGES_COMMIT_SHA: ${{ inputs.head_sha || github.sha }} diff --git a/.github/workflows/frontend-preview.yml b/.github/workflows/frontend-preview.yml index df17e1786c..16e76136fd 100644 --- a/.github/workflows/frontend-preview.yml +++ b/.github/workflows/frontend-preview.yml @@ -70,10 +70,9 @@ jobs: run: pnpm install - name: Build Storybook - working-directory: ./packages/ui env: NODE_OPTIONS: --max-old-space-size=6144 - run: pnpm run build-storybook + run: pnpm build-storybook - name: Configure short SHA id: meta diff --git a/.gitignore b/.gitignore index 9d27dcaf5e..d442251099 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ Thumbs.db # frontend generated files apps/frontend/src/generated +/packages/ui/src/layouts/wrapped/settings/language-settings/language-settings-coverage.generated.ts .turbo target diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index 5a88bd2d92..abe6e55c0f 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -38,7 +38,6 @@ "intl-messageformat": "^10.7.7", "ofetch": "^1.3.4", "overlayscrollbars": "^2.15.1", - "pinia": "^3.0.0", "posthog-js": "^1.158.2", "three": "^0.172.0", "vite-svg-loader": "^5.1.0", diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index eeb37273f6..7456fa2dec 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -94,6 +94,9 @@ import SurveyPopup from '@/components/ui/SurveyPopup.vue' import WindowControls from '@/components/ui/WindowControls.vue' import { useCheckDisableMouseover } from '@/composables/macCssFix.js' import { useAppEvent } from '@/composables/use-app-event' +import { useAppSettings } from '@/composables/use-app-settings.ts' +import { useError } from '@/composables/use-error.js' +import { useTheme } from '@/composables/use-theme.ts' import { config } from '@/config' import { hide_ads_window, @@ -147,8 +150,7 @@ import { setupProviders } from '@/providers/setup' import { setupAppEventsProvider } from '@/providers/setup/app-events' import { setupAuthProvider } from '@/providers/setup/auth' import { setupLoadingStateProvider } from '@/providers/setup/loading-state' -import { useError } from '@/store/error.js' -import { useTheming } from '@/store/state' +import { setupAppUserPreferencesProvider } from '@/providers/setup/user-preferences.ts' import { appMessages } from '@/utils/app-messages' import { generateSkinPreviews } from './helpers/rendering/batch-skin-renderer' @@ -157,7 +159,8 @@ import { AppNotificationManager } from './providers/app-notifications' import { AppPopupNotificationManager } from './providers/app-popup-notifications' import { appSettingsModalOpenProfileKey } from './providers/app-settings-modal' -const themeStore = useTheming() +const appSettings = useAppSettings() +const appTheme = useTheme() const router = useRouter() const route = useRoute() const { channel: appEventChannel, events: appEvents } = setupAppEventsProvider() @@ -201,7 +204,7 @@ const credentials = ref() let credentialsRefreshId = 0 const sidebarToggled = ref(true) watch( - () => themeStore.toggleSidebar, + () => appSettings.toggleSidebar, (toggleSidebar) => { sidebarToggled.value = !toggleSidebar }, @@ -221,7 +224,7 @@ const hostingUpdateRequired = computed( appUpdateState.updatesEnabled.value, ) const prideFundraiserEnabled = computed( - () => themeStore.getFeatureFlag('pride_fundraiser') && Date.now() < PRIDE_FUNDRAISER_END_DATE, + () => appSettings.getFeatureFlag('pride_fundraiser') && Date.now() < PRIDE_FUNDRAISER_END_DATE, ) const hostingIntercomIdentityKey = computed(() => { const rawServerId = route.params.id @@ -324,13 +327,13 @@ providePageContext({ intercomBubble: hostingIntercom.intercomBubble, featureFlags: { serverRamAsBytesAlwaysOn: computed(() => - themeStore.getFeatureFlag('server_ram_as_bytes_always_on'), + appSettings.getFeatureFlag('server_ram_as_bytes_always_on'), ), }, openExternalUrl: (url) => void openUrl(url), }) provideModalBehavior({ - noblur: computed(() => !themeStore.advancedRendering), + noblur: computed(() => !appTheme.advancedRendering), onShow: () => take_ads_window_hold(), onHide: () => release_ads_window_hold(), }) @@ -634,10 +637,11 @@ async function setupApp() { theme, locale, telemetry, - collapsed_navigation, hide_nametag_skins_page, advanced_rendering, toggle_sidebar, + sync_theme_across_devices, + sync_behavior_across_devices, developer_mode, feature_flags, pending_update_toast_for_version, @@ -655,13 +659,14 @@ async function setupApp() { nativeDecorations.value = native_decorations if (os.value !== 'MacOS') await getCurrentWindow().setDecorations(native_decorations) - themeStore.setThemeState(theme) - themeStore.collapsedNavigation = collapsed_navigation - themeStore.advancedRendering = advanced_rendering - themeStore.hideNametagSkinsPage = hide_nametag_skins_page - themeStore.toggleSidebar = toggle_sidebar - themeStore.devMode = developer_mode - themeStore.featureFlags = feature_flags + appTheme.preferred = theme + appTheme.advancedRendering = advanced_rendering + appTheme.syncAcrossDevices = sync_theme_across_devices + appSettings.syncBehaviorAcrossDevices = sync_behavior_across_devices + appSettings.hideNametagSkinsPage = hide_nametag_skins_page + appSettings.toggleSidebar = toggle_sidebar + appSettings.devMode = developer_mode + Object.assign(appSettings.featureFlags, feature_flags) stateInitialized.value = true isMaximized.value = await getCurrentWindow().isMaximized() @@ -959,7 +964,7 @@ watch(incompatibilityWarningModal, (modal) => { } }) -setupAuthProvider(credentials, async (_redirectPath, flow, options) => { +const authProvider = setupAuthProvider(credentials, async (_redirectPath, flow, options) => { if (options?.showModal === false) { await signIn(flow) } else { @@ -967,6 +972,79 @@ setupAuthProvider(credentials, async (_redirectPath, flow, options) => { } }) +const userPreferences = setupAppUserPreferencesProvider(authProvider, notificationManager) +let userPreferencesSync = Promise.resolve() + +watch( + [userPreferences.preferences, stateInitialized], + ([preferences, initialized]) => { + if (!preferences || !initialized) return + + userPreferencesSync = userPreferencesSync + .then(async () => { + const settings = await getSettings() + const selectedTheme = preferences.appearance.auto ? 'system' : preferences.appearance.theme + const locale = preferences.localization.locale + const behavior = preferences.behavior + let settingsChanged = false + + if (appTheme.syncAcrossDevices && appTheme.preferred !== selectedTheme) { + appTheme.preferred = selectedTheme + } + if (i18n.global.locale.value !== locale) { + i18n.global.locale.value = locale + } + + if (appTheme.syncAcrossDevices && settings.theme !== selectedTheme) { + settings.theme = selectedTheme + settingsChanged = true + } + if (settings.locale !== locale) { + settings.locale = locale + settingsChanged = true + } + + if (behavior && appSettings.syncBehaviorAcrossDevices) { + const behaviorFeatureFlags = { + worlds_in_home: behavior.show_jump_in, + show_instance_play_time: behavior.show_play_time, + skip_unknown_pack_warning: !behavior.warn_on_unknown_modpacks, + skip_non_essential_warnings: behavior.skip_non_essential_warnings, + } + + appSettings.toggleSidebar = behavior.hide_right_sidebar + appSettings.hideNametagSkinsPage = behavior.hide_nametag + Object.assign(appSettings.featureFlags, behaviorFeatureFlags) + + if (settings.hide_on_process_start !== behavior.minimize_app) { + settings.hide_on_process_start = behavior.minimize_app + settingsChanged = true + } + if (settings.toggle_sidebar !== behavior.hide_right_sidebar) { + settings.toggle_sidebar = behavior.hide_right_sidebar + settingsChanged = true + } + if (settings.hide_nametag_skins_page !== behavior.hide_nametag) { + settings.hide_nametag_skins_page = behavior.hide_nametag + settingsChanged = true + } + for (const [flag, value] of Object.entries(behaviorFeatureFlags)) { + if (settings.feature_flags[flag] !== value) { + settings.feature_flags[flag] = value + settingsChanged = true + } + } + } + + if (settingsChanged) { + await setSettings(settings) + } + }) + .catch(handleError) + }, + { immediate: true }, +) + async function validateSession(sessionToken) { try { const response = await tauriFetch(`${config.labrinthBaseUrl}/v2/user`, { @@ -1642,7 +1720,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
+
{{ formatMessage(developerModeEnabled) }}
{{ formatMessage(messages.colorThemeDescription) }}
+async function saveAppearanceSettings(): Promise- {{ formatMessage(messages.advancedRenderingDescription) }} -
-{{ formatMessage(messages.nativeDecorationsDescription) }}
-+ {{ formatMessage(messages.syncAcrossDevicesDescription) }} +
+{{ formatMessage(messages.toggleSidebarDescription) }}
{{ formatMessage(messages.showPlayTimeDescription) }}
{{ formatMessage(messages.hideNametagDescription) }}
-
+ {{ description }} +
+