From 696aba75639a5ef78777ac4de3259f51d3065ee6 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Mon, 9 Nov 2026 11:45:31 +0000 Subject: [PATCH 1/7] feat: remove the override toggles and use a reset to default button in instance settings --- .../src/components/ui/JavaSelector.vue | 2 +- .../settings-modal/default-settings-intro.vue | 4 +- .../settings-modal/hooks-settings.vue | 139 ++++---- .../instances/settings-modal/index.vue | 43 +++ .../instance-settings-context.ts | 2 + .../settings-modal/java-settings.vue | 332 ++++++------------ .../reset-to-default-button.vue | 41 +++ .../synced-options-settings.vue | 4 +- .../use-instance-settings-draft.ts | 59 ++++ .../settings-modal/window-settings.vue | 152 ++++---- apps/app-frontend/src/helpers/instance.ts | 4 + .../app-frontend/src/locales/ar-SA/index.json | 6 - .../app-frontend/src/locales/de-CH/index.json | 6 - .../app-frontend/src/locales/de-DE/index.json | 6 - .../app-frontend/src/locales/en-US/index.json | 26 +- .../src/locales/es-419/index.json | 6 - .../app-frontend/src/locales/es-ES/index.json | 6 - .../app-frontend/src/locales/fr-FR/index.json | 6 - .../app-frontend/src/locales/hu-HU/index.json | 6 - .../app-frontend/src/locales/it-IT/index.json | 6 - .../app-frontend/src/locales/ja-JP/index.json | 6 - .../app-frontend/src/locales/ko-KR/index.json | 6 - .../app-frontend/src/locales/nl-NL/index.json | 6 - .../app-frontend/src/locales/pl-PL/index.json | 6 - .../app-frontend/src/locales/pt-BR/index.json | 6 - .../app-frontend/src/locales/ru-RU/index.json | 6 - .../app-frontend/src/locales/sv-SE/index.json | 3 - .../app-frontend/src/locales/tr-TR/index.json | 6 - .../app-frontend/src/locales/uk-UA/index.json | 6 - .../app-frontend/src/locales/vi-VN/index.json | 6 - .../app-frontend/src/locales/zh-CN/index.json | 6 - .../app-frontend/src/locales/zh-TW/index.json | 6 - apps/app/build.rs | 1 + apps/app/src/api/instance.rs | 8 + packages/app-lib/src/api/instance.rs | 2 +- packages/app-lib/src/api/instance/install.rs | 35 +- packages/app-lib/src/launcher/mod.rs | 12 +- 37 files changed, 466 insertions(+), 517 deletions(-) create mode 100644 apps/app-frontend/src/features/instances/settings-modal/reset-to-default-button.vue create mode 100644 apps/app-frontend/src/features/instances/settings-modal/use-instance-settings-draft.ts diff --git a/apps/app-frontend/src/components/ui/JavaSelector.vue b/apps/app-frontend/src/components/ui/JavaSelector.vue index ff0631397e4..5f7b42e330e 100644 --- a/apps/app-frontend/src/components/ui/JavaSelector.vue +++ b/apps/app-frontend/src/components/ui/JavaSelector.vue @@ -215,7 +215,7 @@ async function handleJavaFileInput() { if (!result) { result = { path: filePath.path ?? filePath, - version: props.version.toString(), + version: props.version?.toString() ?? '', parsed_version: props.version, architecture: 'x86', } diff --git a/apps/app-frontend/src/features/instances/settings-modal/default-settings-intro.vue b/apps/app-frontend/src/features/instances/settings-modal/default-settings-intro.vue index c9e638e67ce..ef664c45ee7 100644 --- a/apps/app-frontend/src/features/instances/settings-modal/default-settings-intro.vue +++ b/apps/app-frontend/src/features/instances/settings-modal/default-settings-intro.vue @@ -30,7 +30,9 @@ function showDefaultSettings() { -
+

{{ formatMessage(messages.sharedSettingsDescription) }}

diff --git a/apps/app-frontend/src/features/instances/settings-modal/use-instance-settings-draft.ts b/apps/app-frontend/src/features/instances/settings-modal/use-instance-settings-draft.ts new file mode 100644 index 00000000000..772eedbea4e --- /dev/null +++ b/apps/app-frontend/src/features/instances/settings-modal/use-instance-settings-draft.ts @@ -0,0 +1,59 @@ +import { injectNotificationManager } from '@modrinth/ui' +import { onBeforeUnmount, onMounted, type Ref, ref } from 'vue' + +import { injectInstanceSettings } from './instance-settings-context' + +function clone(value: T): T { + return JSON.parse(JSON.stringify(value)) +} + +export function useInstanceSettingsDraft>( + initial: T, + persist: (value: T) => Promise, +) { + const { handleError } = injectNotificationManager() + const { registerUnsavedChangesController } = injectInstanceSettings() + + const original = ref(clone(initial)) as Ref + const draft = ref(clone(initial)) as Ref + const saving = ref(false) + + function hasChanges() { + return JSON.stringify(original.value) !== JSON.stringify(draft.value) + } + + function reset() { + draft.value = clone(original.value) + } + + async function save() { + if (saving.value) return + saving.value = true + const value = clone(draft.value) + try { + await persist(value) + original.value = value + } catch (error) { + handleError(error as Error) + } finally { + saving.value = false + } + } + + onMounted(() => { + registerUnsavedChangesController({ + hasChanges, + getOriginal: () => original.value, + getModified: () => draft.value, + isSaving: () => saving.value, + reset, + save, + }) + }) + + onBeforeUnmount(() => { + registerUnsavedChangesController(null) + }) + + return { draft } +} diff --git a/apps/app-frontend/src/features/instances/settings-modal/window-settings.vue b/apps/app-frontend/src/features/instances/settings-modal/window-settings.vue index 0c65199214e..90fe4d38694 100644 --- a/apps/app-frontend/src/features/instances/settings-modal/window-settings.vue +++ b/apps/app-frontend/src/features/instances/settings-modal/window-settings.vue @@ -1,6 +1,6 @@ - + diff --git a/apps/app-frontend/src/composables/useJavaTest.ts b/apps/app-frontend/src/composables/useJavaTest.ts index 448854454c6..9e5575148a2 100644 --- a/apps/app-frontend/src/composables/useJavaTest.ts +++ b/apps/app-frontend/src/composables/useJavaTest.ts @@ -1,53 +1,30 @@ import { ref } from 'vue' -import { trackEvent } from '@/helpers/analytics' -import { get_jre, test_jre } from '@/helpers/jre.js' +import type { JavaVersion } from '@/helpers/instance' +import { get_jre } from '@/helpers/jre.js' + +export type JavaStatus = 'empty' | 'checking' | 'valid' | 'invalid' export default function useJavaTest() { - const testingJava = ref(false) - const javaTestResult = ref(null) - let testDebounceTimer: ReturnType | null = null + const status = ref('empty') + let latestCheck = 0 - async function runJavaTest(path: string, version: number | null, track = true) { - if (testDebounceTimer) { - clearTimeout(testDebounceTimer) - testDebounceTimer = null - } + async function checkJava(path: string | undefined, majorVersion: number | null) { + const check = ++latestCheck if (!path) { - javaTestResult.value = null + status.value = 'empty' return } - testingJava.value = true - try { - javaTestResult.value = - version === null ? !!(await get_jre(path)) : await test_jre(path, version) - } catch { - javaTestResult.value = false - } - testingJava.value = false - if (track) { - trackEvent('JavaTest', { path, success: javaTestResult.value }) - } - } + status.value = 'checking' + const result: JavaVersion | null = await get_jre(path).catch(() => null) + if (check !== latestCheck) return - function testJavaInstallationDebounced(path: string, version: number | null, delay = 600) { - if (testDebounceTimer) clearTimeout(testDebounceTimer) - if (!path) { - javaTestResult.value = null - return - } - testDebounceTimer = setTimeout(() => runJavaTest(path, version, false), delay) + status.value = + result && (majorVersion === null || result.parsed_version === majorVersion) + ? 'valid' + : 'invalid' } - async function testJavaInstallation(path: string, version: number | null, track = false) { - await runJavaTest(path, version, track) - } - - return { - testingJava, - javaTestResult, - testJavaInstallationDebounced, - testJavaInstallation, - } + return { status, checkJava } } diff --git a/apps/app-frontend/src/features/settings/app-settings-modal/index.vue b/apps/app-frontend/src/features/settings/app-settings-modal/index.vue index 567bc7d7247..e6930ec89f2 100644 --- a/apps/app-frontend/src/features/settings/app-settings-modal/index.vue +++ b/apps/app-frontend/src/features/settings/app-settings-modal/index.vue @@ -385,13 +385,11 @@ const messages = defineMessages({