diff --git a/apps/app-frontend/.env b/apps/app-frontend/.env deleted file mode 100644 index 6684c4059..000000000 --- a/apps/app-frontend/.env +++ /dev/null @@ -1,3 +0,0 @@ -BASE_URL=https://staging-api.modrinth.com/v2/ -BROWSER_BASE_URL=https://staging-api.modrinth.com/v2/ -BASE_URL= diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index e7d5ec9a7..94ef6d549 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -23,7 +23,7 @@ "tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1", "vite-svg-loader": "^5.1.0", "vue": "^3.4.21", - "vue-multiselect": "3.0.0-beta.3", + "vue-multiselect": "3.0.0", "vue-router": "4.3.0", "vue-typed-virtual-list": "^1.0.10" }, diff --git a/apps/frontend/nuxt.config.ts b/apps/frontend/nuxt.config.ts index 3088e7885..b5f32c734 100644 --- a/apps/frontend/nuxt.config.ts +++ b/apps/frontend/nuxt.config.ts @@ -77,6 +77,13 @@ export default defineNuxtConfig({ title: "Modrinth mods", }, ], + script: [ + { + src: "https://js.stripe.com/v3/", + defer: true, + async: true, + }, + ], }, }, vite: { @@ -125,6 +132,7 @@ export default defineNuxtConfig({ homePageProjects?: any[]; homePageSearch?: any[]; homePageNotifs?: any[]; + products?: any[]; } = {}; try { @@ -165,6 +173,7 @@ export default defineNuxtConfig({ homePageProjects, homePageSearch, homePageNotifs, + products, ] = await Promise.all([ $fetch(`${API_URL}tag/category`, headers), $fetch(`${API_URL}tag/loader`, headers), @@ -174,6 +183,8 @@ export default defineNuxtConfig({ $fetch(`${API_URL}projects_random?count=60`, headers), $fetch(`${API_URL}search?limit=3&query=leave&index=relevance`, headers), $fetch(`${API_URL}search?limit=3&query=&index=updated`, headers), + // TODO: dehardcode + $fetch(`${API_URL.replace("/v2/", "/_internal/")}billing/products`, headers), ]); state.categories = categories; @@ -184,6 +195,7 @@ export default defineNuxtConfig({ state.homePageProjects = homePageProjects; state.homePageSearch = homePageSearch; state.homePageNotifs = homePageNotifs; + state.products = products; await fs.writeFile("./src/generated/state.json", JSON.stringify(state)); @@ -236,7 +248,7 @@ export default defineNuxtConfig({ const omorphiaLocales: string[] = []; const omorphiaLocaleSets = new Map(); - for await (const localeDir of globIterate("node_modules/omorphia/locales/*", { + for await (const localeDir of globIterate("node_modules/@modrinth/ui/src/locales/*", { posix: true, })) { const tag = basename(localeDir); diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 914d33bd0..fca6cc271 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -10,9 +10,10 @@ "postinstall": "nuxi prepare", "lint": "eslint . && prettier --check .", "fix": "eslint . --fix && prettier --write .", - "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" + "intl:extract": "formatjs extract \"{,src/components,src/composables,src/layouts,src/middleware,src/modules,src/pages,src/plugins,src/utils}/**/*.{vue,ts,tsx,js,jsx,mts,cts,mjs,cjs}\" --ignore '**/*.d.ts' --ignore 'node_modules' --out-file src/locales/en-US/index.json --format crowdin --preserve-whitespace" }, "devDependencies": { + "@formatjs/cli": "^6.2.12", "@nuxt/devtools": "^1.3.3", "@nuxtjs/turnstile": "^0.8.0", "@types/node": "^20.1.0", @@ -49,7 +50,7 @@ "pathe": "^1.1.2", "qrcode.vue": "^3.4.0", "semver": "^7.5.4", - "vue-multiselect": "3.0.0-alpha.2", + "vue-multiselect": "3.0.0", "vue3-apexcharts": "^1.5.2", "xss": "^1.0.14" } diff --git a/apps/frontend/src/assets/styles/layout.scss b/apps/frontend/src/assets/styles/layout.scss index c33bd8f5b..d6c02a4f0 100644 --- a/apps/frontend/src/assets/styles/layout.scss +++ b/apps/frontend/src/assets/styles/layout.scss @@ -116,6 +116,5 @@ .normal-page__content { max-width: calc(60rem - 0.75rem); - overflow-x: hidden; } } diff --git a/apps/frontend/src/assets/styles/utils.scss b/apps/frontend/src/assets/styles/utils.scss index afeeb8b89..23c6ccc41 100644 --- a/apps/frontend/src/assets/styles/utils.scss +++ b/apps/frontend/src/assets/styles/utils.scss @@ -2,10 +2,6 @@ display: none !important; } -.w-100 { - width: 100%; -} - body { overflow-y: scroll; overflow-x: hidden; diff --git a/apps/frontend/src/composables/auth.js b/apps/frontend/src/composables/auth.js index e5483cf2b..606e401e5 100644 --- a/apps/frontend/src/composables/auth.js +++ b/apps/frontend/src/composables/auth.js @@ -39,6 +39,16 @@ export const initAuth = async (oldToken = null) => { authCookie.value = route.query.code; } + if (route.fullPath.includes("new_account=true") && route.path !== "/auth/welcome") { + const redirect = route.path.startsWith("/auth/") ? null : route.fullPath; + + await navigateTo( + `/auth/welcome?authToken=${route.query.code}${ + redirect ? `&redirect=${encodeURIComponent(redirect)}` : "" + }`, + ); + } + if (authCookie.value) { auth.token = authCookie.value; diff --git a/apps/frontend/src/composables/country.js b/apps/frontend/src/composables/country.js new file mode 100644 index 000000000..19a0aca27 --- /dev/null +++ b/apps/frontend/src/composables/country.js @@ -0,0 +1,6 @@ +export const useUserCountry = () => + useState("userCountry", () => { + const headers = useRequestHeaders(["cf-ipcountry"]); + + return headers["cf-ipcountry"] ?? "US"; + }); diff --git a/apps/frontend/src/composables/user.js b/apps/frontend/src/composables/user.js index e28b96938..fc28156d1 100644 --- a/apps/frontend/src/composables/user.js +++ b/apps/frontend/src/composables/user.js @@ -12,20 +12,27 @@ export const initUser = async () => { const auth = (await useAuth()).value; const user = { - notifications: [], + collections: [], follows: [], + subscriptions: [], lastUpdated: 0, }; if (auth.user && auth.user.id) { try { - const [follows, collections] = await Promise.all([ - useBaseFetch(`user/${auth.user.id}/follows`), - useBaseFetch(`user/${auth.user.id}/collections`, { apiVersion: 3 }), + const headers = { + Authorization: auth.token, + }; + + const [follows, collections, subscriptions] = await Promise.all([ + useBaseFetch(`user/${auth.user.id}/follows`, { headers }, true), + useBaseFetch(`user/${auth.user.id}/collections`, { apiVersion: 3, headers }, true), + useBaseFetch(`billing/subscriptions`, { internal: true, headers }, true), ]); user.collections = collections; user.follows = follows; + user.subscriptions = subscriptions; user.lastUpdated = Date.now(); } catch (err) { console.error(err); @@ -170,6 +177,5 @@ export const logout = async () => { await useAuth("none"); useCookie("auth-token").value = null; - await navigateTo("/"); stopLoading(); }; diff --git a/apps/frontend/src/layouts/default.vue b/apps/frontend/src/layouts/default.vue index 863ad8b31..19cfdf48d 100644 --- a/apps/frontend/src/layouts/default.vue +++ b/apps/frontend/src/layouts/default.vue @@ -18,6 +18,21 @@ +
+ {{ formatMessage(subscriptionPaymentFailedBannerMessages.title) }} + + + {{ formatMessage(subscriptionPaymentFailedBannerMessages.action) }} + +
-