Skip to content

Commit b2c2004

Browse files
committed
Make risk acknowledgement modal even better
1 parent b1feed6 commit b2c2004

File tree

3 files changed

+99
-34
lines changed

3 files changed

+99
-34
lines changed

src/lib/components/input/TextInput.svelte

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { GetValResColor, type ValidationResult } from '$lib/types/ValidationResult';
55
import { cn } from '$lib/utils/shadcn.js';
66
import type { Snippet } from 'svelte';
7-
import type { FocusEventHandler, FullAutoFill } from 'svelte/elements';
7+
import type { ClipboardEventHandler, FocusEventHandler, FullAutoFill } from 'svelte/elements';
88
99
interface Props {
1010
type?: 'text' | 'email' | 'password' | 'search' | 'url';
@@ -17,6 +17,7 @@
1717
after?: Snippet;
1818
popup?: Snippet;
1919
onblur?: FocusEventHandler<HTMLInputElement> | null;
20+
onpaste?: ClipboardEventHandler<HTMLInputElement> | null;
2021
}
2122
2223
const id = $props.id();
@@ -32,6 +33,7 @@
3233
after,
3334
popup,
3435
onblur,
36+
onpaste
3537
}: Props = $props();
3638
</script>
3739

@@ -51,6 +53,7 @@
5153
{autocomplete}
5254
bind:value
5355
{onblur}
56+
{onpaste}
5457
aria-invalid={validationResult ? !validationResult.valid : undefined}
5558
aria-describedby={validationResult?.message ? validationId : undefined}
5659
/>
@@ -69,7 +72,7 @@
6972
{#if validationResult?.message}
7073
<p
7174
id={validationId}
72-
class={cn('text-xs !mt-0 h-4 truncate', `text-${GetValResColor(validationResult)}`)}
75+
class={cn('text-xs mt-0! h-4 truncate', `text-${GetValResColor(validationResult)}`)}
7376
role="status"
7477
aria-atomic="true"
7578
aria-live="polite"

src/routes/flashtool/FirmwareFlasher.svelte

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
import { Microchip, TriangleAlert } from '@lucide/svelte';
33
import { DownloadAndVerifyBoardBinary } from '$lib/api/firmwareCDN';
44
import { Button } from '$lib/components/ui/button';
5-
import * as Dialog from '$lib/components/ui/dialog';
65
import { Progress } from '$lib/components/ui/progress';
76
import FlashManager from './FlashManager';
7+
import RiskAcknowledgementModal from './RiskAcknowledgementModal.svelte';
88
99
interface Props {
1010
version: string;
@@ -84,38 +84,8 @@
8484
}
8585
</script>
8686

87-
<!-- Risk acknowledgement modal -->
88-
<Dialog.Root open={riskAcknowledgeStatus === 'shown'}>
89-
<Dialog.Content>
90-
<Dialog.Header>
91-
<Dialog.Title>⚠️ Unstable Firmware Warning</Dialog.Title>
92-
<Dialog.Description>
93-
You are about to install <strong class="text-red-500">experimental, non-stable firmware</strong>.
94-
<br /><br />
95-
This firmware is <strong class="text-red-500">not intended for general use</strong> and may contain
96-
serious bugs, regressions, or incomplete features.
97-
<br /><br />
98-
<strong class="text-red-500">No support is provided for issues caused by non-stable firmware.</strong>
99-
</Dialog.Description>
100-
</Dialog.Header>
10187

102-
<div class="flex justify-end gap-2">
103-
<Button
104-
variant="secondary"
105-
onclick={() => (riskAcknowledgeStatus = 'none')}
106-
>
107-
Cancel
108-
</Button>
109-
110-
<Button
111-
variant="destructive"
112-
onclick={() => (riskAcknowledgeStatus = 'accepted')}
113-
>
114-
I UNDERSTAND AND ACCEPT ALL RISKS
115-
</Button>
116-
</div>
117-
</Dialog.Content>
118-
</Dialog.Root>
88+
<RiskAcknowledgementModal open={showNonStableWarning && riskAcknowledgeStatus !== 'accepted'} onAccept={() => riskAcknowledgeStatus = 'accepted'} onCancel={() => riskAcknowledgeStatus = 'none'} />
11989

12090
<div class="flex flex-col items-stretch justify-start gap-4">
12191
<!-- Flash button -->
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<script lang="ts">
2+
import * as Dialog from '$lib/components/ui/dialog';
3+
import { Button } from '$lib/components/ui/button';
4+
import TextInput from '$lib/components/input/TextInput.svelte';
5+
6+
interface Props {
7+
open: boolean;
8+
onAccept: () => void;
9+
onCancel?: () => void;
10+
}
11+
12+
let { open, onAccept, onCancel }: Props = $props();
13+
14+
const REQUIRED_TEXT = 'I UNDERSTAND THIS CAN BREAK MY DEVICE';
15+
const PASTE_REPLACEMENT = 'I DID NOT READ THE WARNING, I JUST COPY-PASTED';
16+
17+
let acknowledgeText = $state('');
18+
let acknowledgeChecked = $state(false);
19+
20+
let canAccept = $derived(
21+
acknowledgeChecked &&
22+
acknowledgeText === REQUIRED_TEXT
23+
);
24+
25+
// Reset + start countdown when dialog opens
26+
$effect(() => {
27+
if (!open) return;
28+
29+
acknowledgeText = '';
30+
acknowledgeChecked = false;
31+
});
32+
33+
function handlePaste(e: ClipboardEvent) {
34+
e.preventDefault();
35+
acknowledgeText = PASTE_REPLACEMENT;
36+
}
37+
</script>
38+
39+
<Dialog.Root {open}>
40+
<Dialog.Content>
41+
<Dialog.Header>
42+
<Dialog.Title>⚠️ UNSTABLE FIRMWARE</Dialog.Title>
43+
<Dialog.Description class="space-y-3">
44+
<p>
45+
You are about to flash <strong class="text-red-500">experimental firmware</strong>
46+
that is <strong class="text-red-500">known to be unstable</strong>.
47+
</p>
48+
49+
<p>
50+
This may permanently brick your device, require manual recovery,
51+
or render it unusable.
52+
</p>
53+
54+
<p class="text-red-500 font-semibold">
55+
We will NOT provide support for issues caused by this firmware.
56+
</p>
57+
58+
<p>
59+
To proceed, type the following phrase <strong>exactly</strong>:
60+
</p>
61+
62+
<code class="block rounded bg-muted p-2 text-sm font-mono">
63+
{REQUIRED_TEXT}
64+
</code>
65+
</Dialog.Description>
66+
</Dialog.Header>
67+
68+
<TextInput
69+
bind:value={acknowledgeText}
70+
onpaste={handlePaste}
71+
/>
72+
73+
<label class="flex items-center gap-2 text-sm">
74+
<input type="checkbox" bind:checked={acknowledgeChecked} />
75+
I understand this is entirely my responsibility
76+
</label>
77+
78+
<div class="flex justify-end gap-2">
79+
<Button variant="secondary" onclick={() => onCancel?.()}>
80+
Abort
81+
</Button>
82+
83+
<Button
84+
variant="destructive"
85+
disabled={!canAccept}
86+
onclick={onAccept}
87+
>
88+
I ACCEPT FULL RESPONSIBILITY
89+
</Button>
90+
</div>
91+
</Dialog.Content>
92+
</Dialog.Root>

0 commit comments

Comments
 (0)