Skip to content

Commit bb87ee4

Browse files
committed
fix(frontend): defer startup modals until onboarding completes
1 parent ee99bf2 commit bb87ee4

8 files changed

Lines changed: 46 additions & 142 deletions

File tree

frontend/src/App.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ function App() {
154154
} = useAppModals({
155155
hasBackend,
156156
isUpdatingMode,
157+
isOnboardingMode,
157158
});
158159

159160
// Counter/refresh for LauncherPage
@@ -406,19 +407,19 @@ function App() {
406407
</div>
407408

408409
<TermsModal
409-
isOpen={termsOpen}
410+
isOpen={termsOpen && !isOnboardingMode}
410411
countdown={termsCountdown}
411412
onAccept={acceptTerms}
412413
/>
413414

414415
<ClarityConsentModal
415-
isOpen={clarityPromptOpen}
416+
isOpen={clarityPromptOpen && !isOnboardingMode}
416417
onEnable={acceptClarity}
417418
onKeepDisabled={declineClarity}
418419
/>
419420

420421
<UpdateModal
421-
isOpen={updateOpen}
422+
isOpen={updateOpen && !isOnboardingMode}
422423
version={updateVersion}
423424
body={updateBody}
424425
loading={updateLoading}
@@ -446,7 +447,7 @@ function App() {
446447
/>
447448

448449
<LipUpdateModal
449-
isOpen={lipUpdateOpen}
450+
isOpen={lipUpdateOpen && !isOnboardingMode}
450451
currentVersion={lipCurrentVersion}
451452
latestVersion={lipLatestVersion}
452453
onDismiss={() => {

frontend/src/assets/locales/en_US.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,6 @@
314314
"body": "You have unsaved changes to the content path. Do you want to save before finishing?",
315315
"cancel": "Cancel",
316316
"save": "Save and Finish"
317-
},
318-
"privacy": {
319-
"title": "Privacy & Usage Data",
320-
"subtitle": "Choose whether to enable anonymous analytics",
321-
"desc": "If enabled, LeviLauncher uses Microsoft Clarity to collect anonymous interaction data (such as clicks and page flow) to improve usability. It does not collect account information, game information, or disk content, and only applies to frontend pages.",
322-
"links": {
323-
"clarity_terms": "Clarity Terms",
324-
"microsoft_privacy": "Microsoft Privacy Statement"
325-
}
326317
}
327318
},
328319
"settings": {

frontend/src/assets/locales/ja_JP.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,15 +1105,6 @@
11051105
"body": "コンテンツパスに未保存の変更があります。保存して終了しますか?",
11061106
"cancel": "キャンセル",
11071107
"save": "保存して完了"
1108-
},
1109-
"privacy": {
1110-
"title": "Privacy & Usage Data",
1111-
"subtitle": "Choose whether to enable anonymous analytics",
1112-
"desc": "If enabled, LeviLauncher uses Microsoft Clarity to collect anonymous interaction data (such as clicks and page flow) to improve usability. It does not collect account information, game information, or disk content, and only applies to frontend pages.",
1113-
"links": {
1114-
"clarity_terms": "Clarity Terms",
1115-
"microsoft_privacy": "Microsoft Privacy Statement"
1116-
}
11171108
}
11181109
},
11191110
"contentdownload": {

frontend/src/assets/locales/ru_RU.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,15 +1105,6 @@
11051105
"body": "У вас есть несохранённые изменения пути к контенту. Сохранить перед завершением?",
11061106
"cancel": "Отмена",
11071107
"save": "Сохранить и завершить"
1108-
},
1109-
"privacy": {
1110-
"title": "Privacy & Usage Data",
1111-
"subtitle": "Choose whether to enable anonymous analytics",
1112-
"desc": "If enabled, LeviLauncher uses Microsoft Clarity to collect anonymous interaction data (such as clicks and page flow) to improve usability. It does not collect account information, game information, or disk content, and only applies to frontend pages.",
1113-
"links": {
1114-
"clarity_terms": "Clarity Terms",
1115-
"microsoft_privacy": "Microsoft Privacy Statement"
1116-
}
11171108
}
11181109
},
11191110
"contentdownload": {

frontend/src/assets/locales/zh_CN.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,6 @@
314314
"body": "您更改了内容路径但尚未保存。是否保存后完成引导?",
315315
"cancel": "取消",
316316
"save": "保存并完成"
317-
},
318-
"privacy": {
319-
"title": "隐私与使用数据",
320-
"subtitle": "请选择是否开启匿名分析",
321-
"desc": "开启后,LeviLauncher 会通过 Microsoft Clarity 收集匿名操作数据(如点击、页面停留)来改进易用性。不会收集账号信息、游戏信息、磁盘内容,仅应用于前端页面。",
322-
"links": {
323-
"clarity_terms": "Clarity 条款",
324-
"microsoft_privacy": "Microsoft 隐私声明"
325-
}
326317
}
327318
},
328319
"settings": {

frontend/src/assets/locales/zh_HK.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,15 +1105,6 @@
11051105
"body": "您更改了內容路徑但尚未儲存。是否儲存後完成引導?",
11061106
"cancel": "取消",
11071107
"save": "儲存並完成"
1108-
},
1109-
"privacy": {
1110-
"title": "私隱與使用資料",
1111-
"subtitle": "請選擇是否啟用匿名分析",
1112-
"desc": "啟用後,LeviLauncher 會透過 Microsoft Clarity 收集匿名操作資料(例如點擊、頁面停留)以改善易用性。不會收集帳號資料、遊戲資訊、磁碟內容,僅適用於前端頁面。",
1113-
"links": {
1114-
"clarity_terms": "Clarity 服務條款",
1115-
"microsoft_privacy": "Microsoft 私隱聲明"
1116-
}
11171108
}
11181109
},
11191110
"contentdownload": {

frontend/src/hooks/useAppModals.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useCallback } from "react";
1+
import { useState, useEffect, useCallback, useRef } from "react";
22
import * as minecraft from "bindings/github.com/liteldev/LeviLauncher/minecraft";
33
import { setNavLockReason } from "@/hooks/useAppNavigation";
44
import {
@@ -10,6 +10,7 @@ import { useStartupInteractive } from "@/utils/startupState";
1010
interface UseAppModalsOptions {
1111
hasBackend: boolean;
1212
isUpdatingMode: boolean;
13+
isOnboardingMode: boolean;
1314
}
1415

1516
const LIP_IGNORE_VERSION_KEY = "ll.ignoreLipVersion";
@@ -22,8 +23,11 @@ const normalizeVersion = (value: unknown): string =>
2223
export const useAppModals = ({
2324
hasBackend,
2425
isUpdatingMode,
26+
isOnboardingMode,
2527
}: UseAppModalsOptions) => {
2628
const startupInteractive = useStartupInteractive();
29+
const previousOnboardingModeRef = useRef<boolean>(isOnboardingMode);
30+
const startupFlowArmedRef = useRef<boolean>(true);
2731
const [termsOpen, setTermsOpen] = useState<boolean>(false);
2832
const [termsCountdown, setTermsCountdown] = useState<number>(0);
2933
const [clarityPromptOpen, setClarityPromptOpen] = useState<boolean>(false);
@@ -131,10 +135,13 @@ export const useAppModals = ({
131135
applyClarityChoice(false);
132136
}, [applyClarityChoice]);
133137

134-
useEffect(() => {
135-
if (!hasBackend) return;
136-
if (!startupInteractive) return;
137-
if (isUpdatingMode) return;
138+
const runStartupModalFlow = useCallback(() => {
139+
if (!startupFlowArmedRef.current) {
140+
return;
141+
}
142+
143+
startupFlowArmedRef.current = false;
144+
138145
try {
139146
const accepted = localStorage.getItem("ll.termsAccepted");
140147
if (!accepted) {
@@ -143,11 +150,37 @@ export const useAppModals = ({
143150
}
144151
runPostTermsFlow();
145152
} catch {}
153+
}, [runPostTermsFlow]);
154+
155+
useEffect(() => {
156+
if (!hasBackend) return;
157+
if (!startupInteractive) return;
158+
if (isUpdatingMode) return;
159+
if (isOnboardingMode) return;
160+
runStartupModalFlow();
146161
}, [
147162
hasBackend,
148163
startupInteractive,
149164
isUpdatingMode,
150-
runPostTermsFlow,
165+
isOnboardingMode,
166+
runStartupModalFlow,
167+
]);
168+
169+
useEffect(() => {
170+
const wasOnboardingMode = previousOnboardingModeRef.current;
171+
previousOnboardingModeRef.current = isOnboardingMode;
172+
173+
if (!wasOnboardingMode || isOnboardingMode) return;
174+
if (!hasBackend) return;
175+
if (!startupInteractive) return;
176+
if (isUpdatingMode) return;
177+
runStartupModalFlow();
178+
}, [
179+
hasBackend,
180+
isOnboardingMode,
181+
isUpdatingMode,
182+
runStartupModalFlow,
183+
startupInteractive,
151184
]);
152185

153186
useEffect(() => {

frontend/src/pages/OnboardingPage.tsx

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
DropdownMenu,
1111
DropdownItem,
1212
Divider,
13-
Switch,
1413
useDisclosure,
1514
} from "@heroui/react";
1615
import { UnifiedModal } from "@/components/UnifiedModal";
@@ -27,15 +26,10 @@ import {
2726
ResetBaseRoot,
2827
CanWriteToDir,
2928
} from "bindings/github.com/liteldev/LeviLauncher/minecraft";
30-
import {
31-
GetInstallerDir,
32-
GetVersionsDir,
33-
} from "bindings/github.com/liteldev/LeviLauncher/versionservice";
3429
import * as minecraft from "bindings/github.com/liteldev/LeviLauncher/minecraft";
3530
import { normalizeLanguage } from "@/utils/i18nUtils";
36-
import { persistClarityChoice } from "@/utils/clarityConsent";
37-
import { Browser, Dialogs } from "@wailsio/runtime";
38-
import { LuHardDrive, LuLanguages, LuShield } from "react-icons/lu";
31+
import { Dialogs } from "@wailsio/runtime";
32+
import { LuHardDrive, LuLanguages } from "react-icons/lu";
3933

4034
export default function OnboardingPage() {
4135
const { t, i18n } = useTranslation();
@@ -47,17 +41,8 @@ export default function OnboardingPage() {
4741
const [selectedLang, setSelectedLang] = React.useState<string>("en_US");
4842
const [baseRoot, setBaseRoot] = React.useState<string>("");
4943
const [newBaseRoot, setNewBaseRoot] = React.useState<string>("");
50-
const [installerDir, setInstallerDir] = React.useState<string>("");
51-
const [versionsDir, setVersionsDir] = React.useState<string>("");
5244
const [baseRootWritable, setBaseRootWritable] = React.useState<boolean>(true);
5345
const [savingBaseRoot, setSavingBaseRoot] = React.useState<boolean>(false);
54-
const [clarityEnabled, setClarityEnabled] = React.useState<boolean>(() => {
55-
try {
56-
return localStorage.getItem("ll.clarity.enabled") === "true";
57-
} catch {
58-
return false;
59-
}
60-
});
6146
const {
6247
isOpen: unsavedOpen,
6348
onOpen: unsavedOnOpen,
@@ -75,10 +60,6 @@ export default function OnboardingPage() {
7560
const br = await GetBaseRoot();
7661
setBaseRoot(String(br || ""));
7762
setNewBaseRoot(String(br || ""));
78-
const id = await GetInstallerDir();
79-
setInstallerDir(String(id || ""));
80-
const vd = await GetVersionsDir();
81-
setVersionsDir(String(vd || ""));
8263
}
8364
} catch {}
8465
})();
@@ -88,13 +69,8 @@ export default function OnboardingPage() {
8869
setBaseRootWritable(true);
8970
}, [newBaseRoot]);
9071

91-
const persistClaritySelection = React.useCallback((enabled: boolean) => {
92-
persistClarityChoice(enabled);
93-
}, []);
94-
9572
const proceedHome = () => {
9673
try {
97-
persistClaritySelection(clarityEnabled);
9874
localStorage.setItem("ll.onboarded", "1");
9975
} catch {}
10076
navigate(ROUTES.home, { replace: true });
@@ -181,10 +157,6 @@ export default function OnboardingPage() {
181157
const br = await GetBaseRoot();
182158
setBaseRoot(String(br || ""));
183159
setNewBaseRoot(String(br || ""));
184-
const id = await GetInstallerDir();
185-
setInstallerDir(String(id || ""));
186-
const vd = await GetVersionsDir();
187-
setVersionsDir(String(vd || ""));
188160
setBaseRootWritable(true);
189161
}
190162
} catch {}
@@ -214,10 +186,6 @@ export default function OnboardingPage() {
214186
if (!err) {
215187
const br = await GetBaseRoot();
216188
setBaseRoot(String(br || ""));
217-
const id = await GetInstallerDir();
218-
setInstallerDir(String(id || ""));
219-
const vd = await GetVersionsDir();
220-
setVersionsDir(String(vd || ""));
221189
}
222190
}
223191
} catch {}
@@ -342,55 +310,6 @@ export default function OnboardingPage() {
342310
}
343311
/>
344312
</div>
345-
346-
<Divider className="opacity-50" />
347-
348-
<div className="space-y-4">
349-
<SectionHeader
350-
title={t("onboarding.privacy.title")}
351-
description={t("onboarding.privacy.subtitle")}
352-
icon={<LuShield className="w-5 h-5" />}
353-
action={
354-
<Switch
355-
size="sm"
356-
isSelected={clarityEnabled}
357-
onValueChange={setClarityEnabled}
358-
classNames={{
359-
wrapper: "group-data-[selected=true]:bg-primary-500",
360-
}}
361-
/>
362-
}
363-
/>
364-
<div className="text-sm text-default-600 dark:text-zinc-300 leading-6">
365-
{t("onboarding.privacy.desc")}
366-
</div>
367-
<div className="flex flex-wrap gap-2">
368-
<Button
369-
size="sm"
370-
radius="full"
371-
variant="flat"
372-
className="bg-default-200/50 dark:bg-white/10 font-bold"
373-
onPress={() =>
374-
Browser.OpenURL("https://clarity.microsoft.com/terms")
375-
}
376-
>
377-
{t("onboarding.privacy.links.clarity_terms")}
378-
</Button>
379-
<Button
380-
size="sm"
381-
radius="full"
382-
variant="light"
383-
className="font-bold"
384-
onPress={() =>
385-
Browser.OpenURL(
386-
"https://privacy.microsoft.com/privacystatement",
387-
)
388-
}
389-
>
390-
{t("onboarding.privacy.links.microsoft_privacy")}
391-
</Button>
392-
</div>
393-
</div>
394313
</CardBody>
395314
</Card>
396315
</div>
@@ -420,10 +339,6 @@ export default function OnboardingPage() {
420339
if (!err) {
421340
const br = await GetBaseRoot();
422341
setBaseRoot(String(br || ""));
423-
const id = await GetInstallerDir();
424-
setInstallerDir(String(id || ""));
425-
const vd = await GetVersionsDir();
426-
setVersionsDir(String(vd || ""));
427342
unsavedOnClose();
428343
proceedHome();
429344
}

0 commit comments

Comments
 (0)