From 90deb7310e673addbf3cd2c0d70da3fab1e44867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Talbot?= <108630700+fetchfern@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:53:29 -0400 Subject: [PATCH 1/2] Fix daedalus NeoForge (#5713) * Support NeoForge version for year-based Minecraft versions * Run on CI * Fix * Fix branch * Undo branch switch --- apps/daedalus_client/src/error.rs | 2 ++ apps/daedalus_client/src/forge.rs | 36 ++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/apps/daedalus_client/src/error.rs b/apps/daedalus_client/src/error.rs index 586c7fc567..6765341381 100644 --- a/apps/daedalus_client/src/error.rs +++ b/apps/daedalus_client/src/error.rs @@ -33,6 +33,8 @@ pub enum ErrorKind { Tracing(#[from] tracing::subscriber::SetGlobalDefaultError), #[error("Zip error: {0}")] Zip(#[from] async_zip::error::ZipError), + #[error("Failed to parse an integer: {0}")] + ParseIntError(#[from] std::num::ParseIntError), } #[derive(Debug)] diff --git a/apps/daedalus_client/src/forge.rs b/apps/daedalus_client/src/forge.rs index 1549f186cd..3e9293281f 100644 --- a/apps/daedalus_client/src/forge.rs +++ b/apps/daedalus_client/src/forge.rs @@ -141,9 +141,16 @@ pub async fn fetch_neo( }).chain(neo_versions.versioning.versions.version.into_iter().map(|loader_version| { let mut parts = loader_version.split('.'); - // NeoForge Forge versions are in this format: 20.2.29-beta, 20.6.119 - // Where the first number is the major MC version, the second is the minor MC version, and the third is the NeoForge version - let major = parts.next().ok_or_else( + // NeoForge Forge versions are in either of these formats: + // - 20.2.29-beta, 20.6.119 + // - 26.1.0.10-beta, 26.1.0.16 + // + // The first format is the "modern" format for Minecraft versions starting with 1, where the first number is the major MC version, + // the second is the minor MC version, and the third is the NeoForge version. + // + // The second format is the "new-modern" format for Minecraft versions in year-based format, where the first three numbers + // are the Minecraft version (year.release.hotfix), and the fourth, the NeoForge release version, with an optional "beta" suffix. + let major_or_year = parts.next().ok_or_else( || crate::ErrorKind::InvalidInput(format!("Unable to find major game version for NeoForge {loader_version}")) )?; @@ -151,11 +158,24 @@ pub async fn fetch_neo( || crate::ErrorKind::InvalidInput(format!("Unable to find minor game version for NeoForge {loader_version}")) )?; - let game_version = if minor == "0" { - format!("1.{major}") - } else { - format!("1.{major}.{minor}") - }; + let major_or_year = major_or_year.parse::()?; + + // Year-based MC versions started in 2026 + let game_version = match major_or_year { + 26.. => { + let hotfix = parts.next().ok_or_else( + || crate::ErrorKind::InvalidInput(format!("Unable to find hotfix version for NeoForge {loader_version}")) + )?; + + if hotfix == "0" { + format!("{major_or_year}.{minor}") + } else { + format!("{major_or_year}.{minor}.{hotfix}") + } + } + ..26 if minor == "0" => format!("1.{major_or_year}"), + ..26 => format!("1.{major_or_year}.{minor}"), + }; Ok(ForgeVersion { format_version: 2, From d401f1183e292855925e720ae115848d355d9bc5 Mon Sep 17 00:00:00 2001 From: tdgao Date: Tue, 31 Mar 2026 00:15:32 -0600 Subject: [PATCH 2/2] feat: add shared UI package auth DI --- apps/app-frontend/src/App.vue | 18 +++++++++++++++- apps/frontend/src/providers/setup.ts | 2 ++ apps/frontend/src/providers/setup/auth.ts | 26 +++++++++++++++++++++++ packages/ui/src/providers/auth.ts | 11 ++++++++++ packages/ui/src/providers/index.ts | 1 + 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 apps/frontend/src/providers/setup/auth.ts create mode 100644 packages/ui/src/providers/auth.ts diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index 63fbe35edb..1e165fcec5 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -40,6 +40,7 @@ import { OverflowMenu, PopupNotificationPanel, ProgressSpinner, + provideAuth, provideModalBehavior, provideModrinthClient, provideNotificationManager, @@ -57,7 +58,7 @@ import { openUrl } from '@tauri-apps/plugin-opener' import { type } from '@tauri-apps/plugin-os' import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state' import { $fetch } from 'ofetch' -import { computed, onMounted, onUnmounted, provide, ref, watch } from 'vue' +import { computed, onMounted, onUnmounted, provide, reactive, ref, watch, watchEffect } from 'vue' import { RouterView, useRoute, useRouter } from 'vue-router' import ModrinthAppLogo from '@/assets/modrinth_app.svg?component' @@ -454,6 +455,21 @@ const credentials = ref() const modrinthLoginFlowWaitModal = ref() +const authProvider = reactive({ + session_token: null, + user: null, + requestSignIn: async (_redirectPath) => { + await signIn() + }, +}) + +watchEffect(() => { + authProvider.session_token = credentials.value?.session ?? null + authProvider.user = credentials.value?.user ?? null +}) + +provideAuth(authProvider) + async function fetchCredentials() { const creds = await getCreds().catch(handleError) if (creds && creds.user_id) { diff --git a/apps/frontend/src/providers/setup.ts b/apps/frontend/src/providers/setup.ts index c558dc4c79..9b68c64e98 100644 --- a/apps/frontend/src/providers/setup.ts +++ b/apps/frontend/src/providers/setup.ts @@ -1,6 +1,7 @@ import { provideNotificationManager } from '@modrinth/ui' import { FrontendNotificationManager } from './frontend-notifications' +import { setupAuthProvider } from './setup/auth' import { setupFilePickerProvider } from './setup/file-picker' import { setupModrinthClientProvider } from './setup/modrinth-client' import { setupPageContextProvider } from './setup/page-context' @@ -9,6 +10,7 @@ import { setupTagsProvider } from './setup/tags' export function setupProviders(auth: Awaited>) { provideNotificationManager(new FrontendNotificationManager()) + setupAuthProvider(auth) setupModrinthClientProvider(auth) setupTagsProvider() setupFilePickerProvider() diff --git a/apps/frontend/src/providers/setup/auth.ts b/apps/frontend/src/providers/setup/auth.ts new file mode 100644 index 0000000000..3681f6e4f1 --- /dev/null +++ b/apps/frontend/src/providers/setup/auth.ts @@ -0,0 +1,26 @@ +import type { Labrinth } from '@modrinth/api-client' +import { type AuthProvider, provideAuth } from '@modrinth/ui' +import { reactive, watchEffect } from 'vue' + +export function setupAuthProvider(auth: Awaited>) { + const router = useRouter() + const authProvider = reactive({ + session_token: null, + user: null, + requestSignIn: async (redirectPath: string) => { + await router.push({ + path: '/auth/sign-in', + query: { + redirect: redirectPath, + }, + }) + }, + }) + + watchEffect(() => { + authProvider.session_token = auth.value.token || null + authProvider.user = (auth.value.user as Labrinth.Users.v2.User | null) ?? null + }) + + provideAuth(authProvider) +} diff --git a/packages/ui/src/providers/auth.ts b/packages/ui/src/providers/auth.ts new file mode 100644 index 0000000000..8e2708a8be --- /dev/null +++ b/packages/ui/src/providers/auth.ts @@ -0,0 +1,11 @@ +import type { Labrinth } from '@modrinth/api-client/src/modules/labrinth/types' + +import { createContext } from './create-context' + +export interface AuthProvider { + session_token: string | null + user: Labrinth.Users.v2.User | null + requestSignIn: (redirectPath: string) => void | Promise +} + +export const [injectAuth, provideAuth] = createContext('root', 'auth') diff --git a/packages/ui/src/providers/index.ts b/packages/ui/src/providers/index.ts index 3eac3d13f9..8765728c30 100644 --- a/packages/ui/src/providers/index.ts +++ b/packages/ui/src/providers/index.ts @@ -1,4 +1,5 @@ export * from './api-client' +export * from './auth' export * from './app-backup' export * from './content-manager' export { createContext } from './create-context'