From aecbc714c409ed42e17d3c8479bd74ecb4be64da Mon Sep 17 00:00:00 2001 From: Sasha Sorokin <10401817+brawaru@users.noreply.github.com> Date: Thu, 11 Jul 2024 20:58:18 +0200 Subject: [PATCH 1/3] Update @vintl/nuxt to ^1.9.2 (#1286) This fixes the issue where types for NuxtApp would be incorrect due to the incorrect Plugin return type by @vintl/nuxt. --- apps/frontend/package.json | 4 ++-- pnpm-lock.yaml | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 90b5877e0..914d33bd0 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -13,14 +13,14 @@ "intl:extract": "formatjs extract \"{,components,composables,layouts,middleware,modules,pages,plugins,utils}/**/*.{vue,ts,tsx,js,jsx,mts,cts,mjs,cjs}\" --ignore '**/*.d.ts' --ignore 'node_modules' --out-file locales/en-US/index.json --format crowdin --preserve-whitespace" }, "devDependencies": { - "eslint": "^8.57.0", "@nuxt/devtools": "^1.3.3", "@nuxtjs/turnstile": "^0.8.0", "@types/node": "^20.1.0", "@vintl/compact-number": "^2.0.5", "@vintl/how-ago": "^3.0.1", - "@vintl/nuxt": "^1.8.0", + "@vintl/nuxt": "^1.9.2", "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", "glob": "^10.2.7", "nuxt": "^3.12.3", "postcss": "^8.4.39", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e33f22880..d9023ed8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -196,8 +196,8 @@ importers: specifier: ^3.0.1 version: 3.0.1(@formatjs/intl@2.10.4(typescript@5.5.3)) '@vintl/nuxt': - specifier: ^1.8.0 - version: 1.9.0(@vue/compiler-core@3.4.31)(magicast@0.3.4)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))(webpack@5.92.1) + specifier: ^1.9.2 + version: 1.9.2(@vue/compiler-core@3.4.31)(magicast@0.3.4)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))(webpack@5.92.1) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) @@ -2155,8 +2155,8 @@ packages: peerDependencies: '@formatjs/intl': ^2.7.1 - '@vintl/nuxt@1.9.0': - resolution: {integrity: sha512-W0RPt6vfk0eWbrPmC/O5PuPEyVfQD6IlgxFw+U3k+K5//miHIZHEQ+fzSv4yLTm9at0/PwLVu74jTgKI4LDt3Q==} + '@vintl/nuxt@1.9.2': + resolution: {integrity: sha512-+2MRe1ikAVMAoCsvkwmm1Z4Ap2GG0+OhVHEJEp324CIGwS98VLM4j8Y4sTItwKBzQAsa3t63AZpbBIp2SsuC3w==} '@vintl/unplugin@1.5.2': resolution: {integrity: sha512-mNOu/Y0elbATzD852ZhzI0Yw12VFuMnE/P1EbX/N+qLj7F2OWOefCtc88fi9NnaL13wsDSaH9PCuA7VJ/KA5iQ==} @@ -7912,7 +7912,7 @@ snapshots: '@formatjs/intl': 2.10.4(typescript@5.5.3) intl-messageformat: 10.5.14 - '@vintl/nuxt@1.9.0(@vue/compiler-core@3.4.31)(magicast@0.3.4)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))(webpack@5.92.1)': + '@vintl/nuxt@1.9.2(@vue/compiler-core@3.4.31)(magicast@0.3.4)(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))(webpack@5.92.1)': dependencies: '@formatjs/intl': 2.10.4(typescript@5.5.3) '@formatjs/intl-localematcher': 0.5.4 @@ -11883,7 +11883,7 @@ snapshots: unimport@3.7.2: dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@rollup/pluginutils': 5.1.0(rollup@3.29.4) acorn: 8.12.1 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 @@ -11962,7 +11962,7 @@ snapshots: unplugin@1.11.0: dependencies: - acorn: 8.12.0 + acorn: 8.12.1 chokidar: 3.6.0 webpack-sources: 3.2.3 webpack-virtual-modules: 0.6.2 From ce4250281fe6561f6d1c3730675b179b3b8e4b4b Mon Sep 17 00:00:00 2001 From: Sasha Sorokin <10401817+brawaru@users.noreply.github.com> Date: Thu, 11 Jul 2024 21:02:00 +0200 Subject: [PATCH 2/3] Refactor auth middleware (#1279) - Switch to TypeScript - Use early return - Switch to regular for loop Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com> --- apps/frontend/src/middleware/auth.js | 35 ---------------------------- apps/frontend/src/middleware/auth.ts | 35 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 35 deletions(-) delete mode 100644 apps/frontend/src/middleware/auth.js create mode 100644 apps/frontend/src/middleware/auth.ts diff --git a/apps/frontend/src/middleware/auth.js b/apps/frontend/src/middleware/auth.js deleted file mode 100644 index 6244ee721..000000000 --- a/apps/frontend/src/middleware/auth.js +++ /dev/null @@ -1,35 +0,0 @@ -const whitelistedParams = ["flow", "error"]; - -export default defineNuxtRouteMiddleware(async (_to, from) => { - const config = useRuntimeConfig(); - const auth = await useAuth(); - - if (!auth.value.user) { - const fullPath = from.fullPath; - - const url = new URL(fullPath, config.public.apiBaseUrl); - - const extractedParams = whitelistedParams.reduce((acc, param) => { - if (url.searchParams.has(param)) { - acc[param] = url.searchParams.get(param); - url.searchParams.delete(param); - } - return acc; - }, {}); - - const redirectPath = encodeURIComponent(url.pathname + url.search); - - return await navigateTo( - { - path: "/auth/sign-in", - query: { - redirect: redirectPath, - ...extractedParams, - }, - }, - { - replace: true, - }, - ); - } -}); diff --git a/apps/frontend/src/middleware/auth.ts b/apps/frontend/src/middleware/auth.ts new file mode 100644 index 000000000..5cc782611 --- /dev/null +++ b/apps/frontend/src/middleware/auth.ts @@ -0,0 +1,35 @@ +const whitelistedParams = ["flow", "error"]; + +export default defineNuxtRouteMiddleware(async (_to, from) => { + const config = useRuntimeConfig(); + const auth = await useAuth(); + + if (auth.value.user) return; + + const fullPath = from.fullPath; + + const url = new URL(fullPath, config.public.apiBaseUrl); + + const extractedParams = Object.create(null) as Record; + + for (const param of whitelistedParams) { + const val = url.searchParams.get(param); + if (val != null) { + extractedParams[param] = val; + url.searchParams.delete(param); + } + } + + const redirect = encodeURIComponent(url.pathname + url.search); + + return await navigateTo( + { + path: "/auth/sign-in", + query: { + redirect, + ...extractedParams, + }, + }, + { replace: true }, + ); +}); From 8704d3acb35e8a0c5c5a53c4f5c4f5532aae749a Mon Sep 17 00:00:00 2001 From: Sasha Sorokin <10401817+brawaru@users.noreply.github.com> Date: Sat, 13 Jul 2024 21:20:43 +0200 Subject: [PATCH 3/3] Rewrite cosmetics and theme preferences (#1292) - Cosmetics and theme preferences are now only stored in cookies instead of a combination of both cookies and state. - The theme plugin now supports client hints. This allows the server to render a page using the client-preferred theme provided it supplies this information (any browser other than Firefox), helping to avoid an annoying flash while the page is hydrating. - The previous workaround using the Nitro plugin has been removed. Its functionality is now handled by the Nuxt theme plugin with cleaner code. - All pages and components now use the new plugins. --- apps/frontend/nuxt.config.ts | 8 ++ .../src/components/ui/charts/ChartDisplay.vue | 2 +- apps/frontend/src/composables/cosmetics.js | 52 ------- .../src/composables/nuxt-accessors.ts | 7 + apps/frontend/src/composables/theme.js | 58 -------- apps/frontend/src/composables/vue.ts | 14 ++ apps/frontend/src/layouts/default.vue | 18 +-- apps/frontend/src/pages/app.vue | 2 +- .../src/pages/auth/reset-password.vue | 2 +- apps/frontend/src/pages/auth/sign-in.vue | 2 +- apps/frontend/src/pages/auth/sign-up.vue | 2 +- apps/frontend/src/pages/collection/[id].vue | 1 - apps/frontend/src/pages/index.vue | 4 +- .../src/pages/search/[searchProjectType].vue | 1 - apps/frontend/src/pages/settings/index.vue | 69 ++++------ apps/frontend/src/pages/user/[id].vue | 1 - apps/frontend/src/plugins/1.theme.js | 27 ---- apps/frontend/src/plugins/cosmetics.ts | 57 ++++++++ apps/frontend/src/plugins/theme.ts | 130 ++++++++++++++++++ apps/frontend/src/server/plugins/theme.js | 38 ----- apps/frontend/src/utils/analytics.js | 21 +-- 21 files changed, 268 insertions(+), 248 deletions(-) delete mode 100644 apps/frontend/src/composables/cosmetics.js create mode 100644 apps/frontend/src/composables/nuxt-accessors.ts delete mode 100644 apps/frontend/src/composables/theme.js create mode 100644 apps/frontend/src/composables/vue.ts delete mode 100644 apps/frontend/src/plugins/1.theme.js create mode 100644 apps/frontend/src/plugins/cosmetics.ts create mode 100644 apps/frontend/src/plugins/theme.ts delete mode 100644 apps/frontend/src/server/plugins/theme.js diff --git a/apps/frontend/nuxt.config.ts b/apps/frontend/nuxt.config.ts index e8de813c9..513fd5e93 100644 --- a/apps/frontend/nuxt.config.ts +++ b/apps/frontend/nuxt.config.ts @@ -392,6 +392,14 @@ export default defineNuxtConfig({ autoprefixer: {}, }, }, + routeRules: { + "/**": { + headers: { + "Accept-CH": "Sec-CH-Prefers-Color-Scheme", + "Critical-CH": "Sec-CH-Prefers-Color-Scheme", + }, + }, + }, compatibilityDate: "2024-07-03", }); diff --git a/apps/frontend/src/components/ui/charts/ChartDisplay.vue b/apps/frontend/src/components/ui/charts/ChartDisplay.vue index 852c687de..36a603f58 100644 --- a/apps/frontend/src/components/ui/charts/ChartDisplay.vue +++ b/apps/frontend/src/components/ui/charts/ChartDisplay.vue @@ -162,7 +162,7 @@
- useState("cosmetics", () => { - const cosmetics = useCookie("cosmetics", { - maxAge: 60 * 60 * 24 * 365 * 10, - sameSite: "lax", - secure: true, - httpOnly: false, - path: "/", - }); - - if (!cosmetics.value) { - cosmetics.value = { - searchLayout: false, - projectLayout: false, - advancedRendering: true, - externalLinksNewTab: true, - notUsingBlockers: false, - hideModrinthAppPromos: false, - preferredDarkTheme: "dark", - searchDisplayMode: { - mod: "list", - plugin: "list", - resourcepack: "gallery", - modpack: "list", - shader: "gallery", - datapack: "list", - user: "list", - collection: "list", - }, - hideStagingBanner: false, - }; - } - - return cosmetics.value; - }); - -export const saveCosmetics = () => { - const cosmetics = useCosmetics(); - - console.log("SAVING COSMETICS:"); - console.log(cosmetics); - - const cosmeticsCookie = useCookie("cosmetics", { - maxAge: 60 * 60 * 24 * 365 * 10, - sameSite: "lax", - secure: true, - httpOnly: false, - path: "/", - }); - - cosmeticsCookie.value = cosmetics.value; -}; diff --git a/apps/frontend/src/composables/nuxt-accessors.ts b/apps/frontend/src/composables/nuxt-accessors.ts new file mode 100644 index 000000000..5130f6268 --- /dev/null +++ b/apps/frontend/src/composables/nuxt-accessors.ts @@ -0,0 +1,7 @@ +export function useTheme() { + return useNuxtApp().$theme; +} + +export function useCosmetics() { + return useNuxtApp().$cosmetics; +} diff --git a/apps/frontend/src/composables/theme.js b/apps/frontend/src/composables/theme.js deleted file mode 100644 index 460ebdf1f..000000000 --- a/apps/frontend/src/composables/theme.js +++ /dev/null @@ -1,58 +0,0 @@ -export const useTheme = () => - useState("theme", () => { - const colorMode = useCookie("color-mode", { - maxAge: 60 * 60 * 24 * 365 * 10, - sameSite: "lax", - secure: true, - httpOnly: false, - path: "/", - }); - - if (!colorMode.value) { - colorMode.value = { - value: "dark", - preference: "system", - }; - } - - if (colorMode.value.preference !== "system") { - colorMode.value.value = colorMode.value.preference; - } - - return colorMode.value; - }); - -export const updateTheme = (value, updatePreference = false) => { - const theme = useTheme(); - const cosmetics = useCosmetics(); - - const themeCookie = useCookie("color-mode", { - maxAge: 60 * 60 * 24 * 365 * 10, - sameSite: "lax", - secure: true, - httpOnly: false, - path: "/", - }); - - if (value === "system") { - theme.value.preference = "system"; - - const colorSchemeQueryList = window.matchMedia("(prefers-color-scheme: light)"); - if (colorSchemeQueryList.matches) { - theme.value.value = "light"; - } else { - theme.value.value = cosmetics.value.preferredDarkTheme; - } - } else { - theme.value.value = value; - if (updatePreference) theme.value.preference = value; - } - - if (import.meta.client) { - document.documentElement.className = `${theme.value.value}-mode`; - } - - themeCookie.value = theme.value; -}; - -export const DARK_THEMES = ["dark", "oled", "retro"]; diff --git a/apps/frontend/src/composables/vue.ts b/apps/frontend/src/composables/vue.ts new file mode 100644 index 000000000..cf49bc462 --- /dev/null +++ b/apps/frontend/src/composables/vue.ts @@ -0,0 +1,14 @@ +/** + * Creates a computed reference that uses a provide getter function called with an argument representing the current mount state of the component. + * @param getter A getter function that will run with `mounted` argument representing whether or not the component is mounted. + * @returns A computed reference that changes when component becomes mounted or unmounted. + */ +export function useMountedValue(getter: (isMounted: boolean) => T) { + const mounted = ref(getCurrentInstance()?.isMounted ?? false); + + onMounted(() => (mounted.value = true)); + + onUnmounted(() => (mounted.value = false)); + + return computed(() => getter(mounted.value)); +} diff --git a/apps/frontend/src/layouts/default.vue b/apps/frontend/src/layouts/default.vue index 4be98be2a..863ad8b31 100644 --- a/apps/frontend/src/layouts/default.vue +++ b/apps/frontend/src/layouts/default.vue @@ -62,7 +62,7 @@ :title="formatMessage(messages.changeTheme)" @click="changeTheme" > -