@indietabletop/appkit 7.0.0-0 → 7.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/AppConfig/AppConfig.tsx +80 -0
- package/lib/AppConfig/formatters.tsx +43 -0
- package/lib/AuthCard/AuthCard.stories.ts +34 -0
- package/lib/AuthCard/AuthCard.tsx +64 -0
- package/lib/AuthCard/style.css.ts +49 -0
- package/lib/CacheProvider.tsx +20 -0
- package/lib/DialogTrigger/index.tsx +36 -0
- package/lib/DocumentTitle/DocumentTitle.tsx +10 -0
- package/lib/EnumMapper.ts +50 -0
- package/lib/ExternalLink.tsx +10 -0
- package/lib/FullscreenDismissBlocker.tsx +23 -0
- package/{dist/HistoryState.d.ts → lib/HistoryState.ts} +5 -2
- package/lib/IndieTabletopClubLogo.tsx +44 -0
- package/lib/IndieTabletopClubSymbol.tsx +37 -0
- package/lib/InfoPage/index.tsx +46 -0
- package/lib/InfoPage/pages.tsx +36 -0
- package/lib/InfoPage/style.css.ts +36 -0
- package/lib/Letterhead/index.tsx +85 -0
- package/lib/Letterhead/stories.tsx +41 -0
- package/lib/Letterhead/style.css.ts +152 -0
- package/lib/LetterheadForm/LetterheadReadonlyTextField.stories.tsx +17 -0
- package/lib/LetterheadForm/LetterheadSubmitError.stories.tsx +19 -0
- package/lib/LetterheadForm/LetterheadTextField.stories.tsx +19 -0
- package/lib/LetterheadForm/index.tsx +137 -0
- package/lib/LetterheadForm/style.css.ts +89 -0
- package/lib/LoadingIndicator.tsx +40 -0
- package/lib/MiddotSeparated/MiddotSeparated.stories.ts +26 -0
- package/lib/MiddotSeparated/MiddotSeparated.tsx +26 -0
- package/lib/MiddotSeparated/style.css.ts +10 -0
- package/lib/ModalDialog/index.tsx +28 -0
- package/lib/ModalDialog/style.css.ts +88 -0
- package/lib/ModernIDB/Cursor.ts +91 -0
- package/lib/ModernIDB/ModernIDB.ts +337 -0
- package/lib/ModernIDB/ModernIDBError.ts +9 -0
- package/lib/ModernIDB/ObjectStore.ts +195 -0
- package/lib/ModernIDB/ObjectStoreIndex.ts +102 -0
- package/lib/ModernIDB/README.md +9 -0
- package/lib/ModernIDB/Transaction.ts +40 -0
- package/lib/ModernIDB/VersionChangeManager.ts +57 -0
- package/lib/ModernIDB/bindings/factory.tsx +165 -0
- package/lib/ModernIDB/bindings/index.ts +2 -0
- package/{dist/ModernIDB/bindings/types.d.ts → lib/ModernIDB/bindings/types.ts} +32 -13
- package/lib/ModernIDB/bindings/utils.tsx +32 -0
- package/lib/ModernIDB/index.ts +10 -0
- package/lib/ModernIDB/types.ts +120 -0
- package/lib/ModernIDB/utils.ts +51 -0
- package/lib/QRCode/QRCode.stories.tsx +41 -0
- package/lib/QRCode/QRCode.tsx +54 -0
- package/lib/QRCode/style.css.ts +23 -0
- package/lib/ReleaseInfo/index.tsx +29 -0
- package/lib/RulesetResolver.ts +214 -0
- package/lib/SafariCheck/SafariCheck.stories.tsx +99 -0
- package/lib/SafariCheck/SafariCheck.tsx +273 -0
- package/lib/SafariCheck/addToDock.svg +13 -0
- package/lib/SafariCheck/addToHomeScreen.svg +12 -0
- package/lib/SafariCheck/safari.svg +32 -0
- package/lib/SafariCheck/shareIcon.svg +11 -0
- package/lib/SafariCheck/style.css.ts +106 -0
- package/lib/ServiceWorkerHandler.tsx +53 -0
- package/lib/ShareButton/ShareButton.stories.tsx +58 -0
- package/lib/ShareButton/ShareButton.tsx +153 -0
- package/lib/ShareButton/test.css.ts +3 -0
- package/lib/SubscribeCard/LetterheadInfoCard.tsx +23 -0
- package/lib/SubscribeCard/SubscribeByEmailCard.stories.tsx +69 -0
- package/lib/SubscribeCard/SubscribeByEmailCard.tsx +183 -0
- package/lib/SubscribeCard/SubscribeByPledgeCard.stories.tsx +133 -0
- package/lib/SubscribeCard/SubscribeByPledgeCard.tsx +127 -0
- package/lib/SubscribeCard/style.css.ts +14 -0
- package/lib/Sync/SyncIcon.stories.tsx +67 -0
- package/lib/Sync/SyncIcon.tsx +102 -0
- package/lib/Sync/SyncLog.tsx +222 -0
- package/lib/Sync/SyncLogList.stories.tsx +219 -0
- package/lib/Sync/style.css.ts +126 -0
- package/lib/account/AccountIcon.tsx +15 -0
- package/lib/account/AccountIssueView.tsx +44 -0
- package/lib/account/AlreadyLoggedInView.tsx +47 -0
- package/lib/account/CurrentUserFetcher.stories.tsx +292 -0
- package/lib/account/CurrentUserFetcher.tsx +118 -0
- package/lib/account/FailureFallbackView.tsx +36 -0
- package/lib/account/JoinCard.stories.tsx +257 -0
- package/lib/account/JoinCard.tsx +301 -0
- package/lib/account/LoadingView.tsx +14 -0
- package/lib/account/LoginCard.stories.tsx +288 -0
- package/lib/account/LoginCard.tsx +100 -0
- package/lib/account/LoginView.tsx +151 -0
- package/lib/account/NoConnectionView.tsx +34 -0
- package/lib/account/PasswordResetCard.stories.tsx +242 -0
- package/lib/account/PasswordResetCard.tsx +296 -0
- package/lib/account/UserMismatchView.tsx +62 -0
- package/lib/account/VerifyPage.tsx +195 -0
- package/lib/account/iconMask.svg +3 -0
- package/lib/account/style.css.ts +81 -0
- package/{dist/account/types.d.ts → lib/account/types.ts} +6 -3
- package/lib/account/useFetchCurrentUser.tsx +63 -0
- package/lib/account/useRedirectPath.ts +21 -0
- package/lib/animations.css.ts +17 -0
- package/lib/append-copy-to-text.ts +35 -0
- package/lib/async-op.ts +286 -0
- package/lib/atomic.css.ts +11 -0
- package/{dist/caught-value.d.ts → lib/caught-value.ts} +11 -1
- package/{dist/class-names.d.ts → lib/class-names.ts} +17 -6
- package/lib/client.ts +662 -0
- package/lib/common.css.ts +48 -0
- package/lib/copyrightRange.ts +10 -0
- package/lib/createSafeStorage.ts +91 -0
- package/lib/createStrictContext.ts +15 -0
- package/lib/failureMessages.ts +108 -0
- package/lib/form/FormSubmitButton.tsx +58 -0
- package/lib/form/SubmitErrorAlert.tsx +21 -0
- package/lib/form/style.css.ts +9 -0
- package/lib/globals.css.ts +62 -0
- package/lib/groupBy.ts +25 -0
- package/lib/hrefs.ts +48 -0
- package/lib/idToDate.ts +8 -0
- package/lib/ids.ts +6 -0
- package/lib/index.ts +75 -0
- package/lib/internal.css.ts +10 -0
- package/lib/mailto.ts +40 -0
- package/lib/media.ts +50 -0
- package/lib/random.ts +19 -0
- package/lib/result/swr.ts +18 -0
- package/{dist/sleep.d.ts → lib/sleep.ts} +3 -1
- package/lib/store/index.tsx +294 -0
- package/lib/store/store.ts +482 -0
- package/lib/store/types.ts +45 -0
- package/lib/store/utils.ts +54 -0
- package/lib/storybook/decorators.tsx +10 -0
- package/lib/structs.ts +3 -0
- package/{dist/typeguards.d.ts → lib/typeguards.ts} +3 -1
- package/{dist/types.d.ts → lib/types.ts} +23 -12
- package/lib/unique.ts +24 -0
- package/lib/use-async-op.ts +16 -0
- package/lib/use-document-background-color.ts +16 -0
- package/lib/use-form.ts +78 -0
- package/lib/use-is-installed.ts +17 -0
- package/lib/use-media-query.ts +21 -0
- package/lib/use-reverting-state.ts +32 -0
- package/lib/use-scroll-restoration.ts +99 -0
- package/lib/useEnsureValue.ts +31 -0
- package/lib/useInvokeClient.ts +54 -0
- package/lib/useIsVisible.ts +27 -0
- package/lib/utm.ts +92 -0
- package/lib/validations.ts +25 -0
- package/lib/vars.css.ts +13 -0
- package/package.json +23 -29
- package/dist/AppConfig/AppConfig.d.ts +0 -29
- package/dist/AuthCard/AuthCard.d.ts +0 -10
- package/dist/AuthCard/AuthCard.stories.d.ts +0 -34
- package/dist/AuthCard/style.css.d.ts +0 -23
- package/dist/DialogTrigger/index.d.ts +0 -13
- package/dist/DocumentTitle/DocumentTitle.d.ts +0 -3
- package/dist/EnumMapper.d.ts +0 -25
- package/dist/ExternalLink.d.ts +0 -3
- package/dist/FullscreenDismissBlocker.d.ts +0 -5
- package/dist/IndieTabletopClubLogo.d.ts +0 -7
- package/dist/IndieTabletopClubSymbol.d.ts +0 -7
- package/dist/InfoPage/index.d.ts +0 -8
- package/dist/InfoPage/pages.d.ts +0 -2
- package/dist/InfoPage/style.css.d.ts +0 -5
- package/dist/Letterhead/index.d.ts +0 -19
- package/dist/Letterhead/stories.d.ts +0 -13
- package/dist/Letterhead/style.css.d.ts +0 -46
- package/dist/LetterheadForm/LetterheadReadonlyTextField.stories.d.ts +0 -17
- package/dist/LetterheadForm/LetterheadSubmitError.stories.d.ts +0 -11
- package/dist/LetterheadForm/LetterheadTextField.stories.d.ts +0 -336
- package/dist/LetterheadForm/index.d.ts +0 -44
- package/dist/LetterheadForm/style.css.d.ts +0 -8
- package/dist/LoadingIndicator.d.ts +0 -3
- package/dist/MiddotSeparated/MiddotSeparated.d.ts +0 -8
- package/dist/MiddotSeparated/MiddotSeparated.stories.d.ts +0 -586
- package/dist/MiddotSeparated/style.css.d.ts +0 -1
- package/dist/ModalDialog/index.d.ts +0 -12
- package/dist/ModalDialog/style.css.d.ts +0 -58
- package/dist/ModernIDB/Cursor.d.ts +0 -56
- package/dist/ModernIDB/ModernIDB.d.ts +0 -66
- package/dist/ModernIDB/ModernIDBError.d.ts +0 -3
- package/dist/ModernIDB/ObjectStore.d.ts +0 -112
- package/dist/ModernIDB/ObjectStoreIndex.d.ts +0 -53
- package/dist/ModernIDB/Transaction.d.ts +0 -16
- package/dist/ModernIDB/VersionChangeManager.d.ts +0 -30
- package/dist/ModernIDB/bindings/factory.d.ts +0 -12
- package/dist/ModernIDB/bindings/index.d.ts +0 -2
- package/dist/ModernIDB/bindings/utils.d.ts +0 -2
- package/dist/ModernIDB/index.d.ts +0 -10
- package/dist/ModernIDB/types.d.ts +0 -88
- package/dist/ModernIDB/utils.d.ts +0 -4
- package/dist/QRCode/QRCode.d.ts +0 -7
- package/dist/QRCode/QRCode.stories.d.ts +0 -33
- package/dist/QRCode/style.css.d.ts +0 -4
- package/dist/ReleaseInfo/index.d.ts +0 -5
- package/dist/RulesetResolver.d.ts +0 -87
- package/dist/SafariCheck/SafariCheck.d.ts +0 -23
- package/dist/SafariCheck/SafariCheck.stories.d.ts +0 -73
- package/dist/SafariCheck/style.css.d.ts +0 -17
- package/dist/ServiceWorkerHandler.d.ts +0 -11
- package/dist/ShareButton/ShareButton.d.ts +0 -57
- package/dist/ShareButton/ShareButton.stories.d.ts +0 -1577
- package/dist/ShareButton/test.css.d.ts +0 -1
- package/dist/SubscribeCard/LetterheadInfoCard.d.ts +0 -2
- package/dist/SubscribeCard/SubscribeByEmailCard.d.ts +0 -24
- package/dist/SubscribeCard/SubscribeByEmailCard.stories.d.ts +0 -10
- package/dist/SubscribeCard/SubscribeByPledgeCard.d.ts +0 -36
- package/dist/SubscribeCard/SubscribeByPledgeCard.stories.d.ts +0 -65
- package/dist/SubscribeCard/style.css.d.ts +0 -4
- package/dist/account/AccountIssueView.d.ts +0 -3
- package/dist/account/AlreadyLoggedInView.d.ts +0 -5
- package/dist/account/CurrentUserFetcher.d.ts +0 -20
- package/dist/account/CurrentUserFetcher.stories.d.ts +0 -136
- package/dist/account/FailureFallbackView.d.ts +0 -1
- package/dist/account/JoinCard.d.ts +0 -14
- package/dist/account/JoinCard.stories.d.ts +0 -143
- package/dist/account/LoadingView.d.ts +0 -1
- package/dist/account/LoginCard.d.ts +0 -39
- package/dist/account/LoginCard.stories.d.ts +0 -217
- package/dist/account/LoginView.d.ts +0 -10
- package/dist/account/NoConnectionView.d.ts +0 -4
- package/dist/account/PasswordResetCard.d.ts +0 -15
- package/dist/account/PasswordResetCard.stories.d.ts +0 -128
- package/dist/account/UserMismatchView.d.ts +0 -6
- package/dist/account/VerifyPage.d.ts +0 -13
- package/dist/account/style.css.d.ts +0 -10
- package/dist/account/useFetchCurrentUser.d.ts +0 -28
- package/dist/account/useRedirectPath.d.ts +0 -6
- package/dist/animations.css.d.ts +0 -3
- package/dist/append-copy-to-text.d.ts +0 -10
- package/dist/append-copy-to-text.test.d.ts +0 -1
- package/dist/appkit.css +0 -1
- package/dist/appkit.js +0 -10692
- package/dist/async-op.d.ts +0 -101
- package/dist/atomic.css.d.ts +0 -6
- package/dist/client.d.ts +0 -424
- package/dist/common.css.d.ts +0 -5
- package/dist/copyrightRange.d.ts +0 -1
- package/dist/copyrightRange.test.d.ts +0 -1
- package/dist/createSafeStorage.d.ts +0 -34
- package/dist/failureMessages.d.ts +0 -20
- package/dist/failureMessages.test.d.ts +0 -1
- package/dist/form/FormSubmitButton.d.ts +0 -17
- package/dist/form/SubmitErrorAlert.d.ts +0 -5
- package/dist/form/style.css.d.ts +0 -3
- package/dist/globals.css.d.ts +0 -0
- package/dist/groupBy.d.ts +0 -1
- package/dist/groupBy.test.d.ts +0 -1
- package/dist/hrefs.d.ts +0 -32
- package/dist/hrefs.test.d.ts +0 -1
- package/dist/idToDate.d.ts +0 -5
- package/dist/idToDate.test.d.ts +0 -1
- package/dist/ids.d.ts +0 -1
- package/dist/ids.test.d.ts +0 -1
- package/dist/index.d.ts +0 -64
- package/dist/internal.css.d.ts +0 -2
- package/dist/mailto.d.ts +0 -8
- package/dist/mailto.test.d.ts +0 -1
- package/dist/media.d.ts +0 -39
- package/dist/random.d.ts +0 -3
- package/dist/result/swr.d.ts +0 -4
- package/dist/store/index.d.ts +0 -237
- package/dist/store/store.d.ts +0 -144
- package/dist/store/types.d.ts +0 -49
- package/dist/store/utils.d.ts +0 -10
- package/dist/storybook/decorators.d.ts +0 -3
- package/dist/structs.d.ts +0 -1
- package/dist/typeguards.test.d.ts +0 -1
- package/dist/unique.d.ts +0 -10
- package/dist/unique.test.d.ts +0 -1
- package/dist/use-async-op.d.ts +0 -6
- package/dist/use-document-background-color.d.ts +0 -4
- package/dist/use-form.d.ts +0 -29
- package/dist/use-is-installed.d.ts +0 -8
- package/dist/use-media-query.d.ts +0 -1
- package/dist/use-reverting-state.d.ts +0 -5
- package/dist/use-scroll-restoration.d.ts +0 -25
- package/dist/useEnsureValue.d.ts +0 -6
- package/dist/useInvokeClient.d.ts +0 -25
- package/dist/useIsVisible.d.ts +0 -4
- package/dist/utm.d.ts +0 -58
- package/dist/utm.test.d.ts +0 -1
- package/dist/validations.d.ts +0 -3
- package/dist/vars.css.d.ts +0 -10
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { Button, Form } from "@ariakit/react";
|
|
2
|
+
import { useState, type Dispatch, type SetStateAction } from "react";
|
|
3
|
+
import { Link } from "wouter";
|
|
4
|
+
import { useAppConfig, useClient } from "../AppConfig/AppConfig.tsx";
|
|
5
|
+
import { interactiveText } from "../common.css.ts";
|
|
6
|
+
import { getSubmitFailureMessage } from "../failureMessages.ts";
|
|
7
|
+
import {
|
|
8
|
+
Letterhead,
|
|
9
|
+
LetterheadHeading,
|
|
10
|
+
LetterheadParagraph,
|
|
11
|
+
LetterheadSubmitButton,
|
|
12
|
+
} from "../Letterhead/index.tsx";
|
|
13
|
+
import { button } from "../Letterhead/style.css.ts";
|
|
14
|
+
import {
|
|
15
|
+
LetterheadFormActions,
|
|
16
|
+
LetterheadHeader,
|
|
17
|
+
LetterheadSubmitError,
|
|
18
|
+
LetterheadTextField,
|
|
19
|
+
} from "../LetterheadForm/index.tsx";
|
|
20
|
+
import { useAppActions } from "../store/index.tsx";
|
|
21
|
+
import type { CurrentUser } from "../types.ts";
|
|
22
|
+
import { useForm } from "../use-form.ts";
|
|
23
|
+
import type { AuthEventHandler } from "./types.ts";
|
|
24
|
+
|
|
25
|
+
type SetStep = Dispatch<SetStateAction<VerifyStep>>;
|
|
26
|
+
|
|
27
|
+
function InitialStep(props: {
|
|
28
|
+
setStep: SetStep;
|
|
29
|
+
currentUser: CurrentUser;
|
|
30
|
+
reload: () => void;
|
|
31
|
+
}) {
|
|
32
|
+
const { setStep, currentUser, reload } = props;
|
|
33
|
+
const { email } = currentUser;
|
|
34
|
+
const { logout } = useAppActions();
|
|
35
|
+
|
|
36
|
+
const client = useClient();
|
|
37
|
+
const { form, submitName } = useForm({
|
|
38
|
+
defaultValues: {},
|
|
39
|
+
async onSubmit() {
|
|
40
|
+
const op = await client.requestUserVerification();
|
|
41
|
+
return op.mapFailure(getSubmitFailureMessage);
|
|
42
|
+
},
|
|
43
|
+
onSuccess(value) {
|
|
44
|
+
setStep({ type: "SUBMIT_CODE", tokenId: value.tokenId });
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Form store={form}>
|
|
50
|
+
<Letterhead>
|
|
51
|
+
<LetterheadHeader>
|
|
52
|
+
<LetterheadHeading>Verify account</LetterheadHeading>
|
|
53
|
+
<LetterheadParagraph>
|
|
54
|
+
Your account is currently not verified. We will send a one-time code
|
|
55
|
+
to your email ({email}) to complete the verification.
|
|
56
|
+
</LetterheadParagraph>
|
|
57
|
+
</LetterheadHeader>
|
|
58
|
+
|
|
59
|
+
<LetterheadFormActions>
|
|
60
|
+
<LetterheadSubmitError name={submitName} />
|
|
61
|
+
<LetterheadSubmitButton>Send code</LetterheadSubmitButton>
|
|
62
|
+
<LetterheadParagraph>
|
|
63
|
+
Cannot complete verification?{" "}
|
|
64
|
+
<Button
|
|
65
|
+
className={interactiveText}
|
|
66
|
+
onClick={async () => {
|
|
67
|
+
await logout();
|
|
68
|
+
reload();
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
Log out
|
|
72
|
+
</Button>
|
|
73
|
+
.
|
|
74
|
+
</LetterheadParagraph>
|
|
75
|
+
</LetterheadFormActions>
|
|
76
|
+
</Letterhead>
|
|
77
|
+
</Form>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function SubmitCodeStep(props: {
|
|
82
|
+
tokenId: string;
|
|
83
|
+
setStep: SetStep;
|
|
84
|
+
currentUser: CurrentUser;
|
|
85
|
+
}) {
|
|
86
|
+
const client = useClient();
|
|
87
|
+
const { email } = props.currentUser;
|
|
88
|
+
const { form, submitName } = useForm({
|
|
89
|
+
defaultValues: {
|
|
90
|
+
code: "",
|
|
91
|
+
},
|
|
92
|
+
async onSubmit({ values }) {
|
|
93
|
+
const op = await client.verifyUser({
|
|
94
|
+
...values,
|
|
95
|
+
tokenId: props.tokenId,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return op.mapFailure((failure) => {
|
|
99
|
+
return getSubmitFailureMessage(failure, {
|
|
100
|
+
404: "🚫 This code is incorrect or expired. Please try again.",
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
onSuccess() {
|
|
105
|
+
props.setStep({ type: "SUCCESS" });
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Form store={form}>
|
|
111
|
+
<Letterhead>
|
|
112
|
+
<LetterheadHeader>
|
|
113
|
+
<LetterheadHeading>Verify Account</LetterheadHeading>
|
|
114
|
+
<LetterheadParagraph>
|
|
115
|
+
We've sent a one-time code to {email}. Please, enter the code in the
|
|
116
|
+
field below to complete verification.
|
|
117
|
+
</LetterheadParagraph>
|
|
118
|
+
</LetterheadHeader>
|
|
119
|
+
|
|
120
|
+
<LetterheadTextField
|
|
121
|
+
name={form.names.code}
|
|
122
|
+
label="Code"
|
|
123
|
+
placeholder="E.g. 123123"
|
|
124
|
+
autoComplete="one-time-code"
|
|
125
|
+
required
|
|
126
|
+
/>
|
|
127
|
+
|
|
128
|
+
<LetterheadFormActions>
|
|
129
|
+
<LetterheadSubmitError name={submitName} />
|
|
130
|
+
<LetterheadSubmitButton>Verify</LetterheadSubmitButton>
|
|
131
|
+
</LetterheadFormActions>
|
|
132
|
+
</Letterhead>
|
|
133
|
+
</Form>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function SuccessStep(props: { onClose?: AuthEventHandler }) {
|
|
138
|
+
const { onClose } = props;
|
|
139
|
+
const { hrefs } = useAppConfig();
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<Letterhead>
|
|
143
|
+
<LetterheadHeader>
|
|
144
|
+
<LetterheadHeading>Success!</LetterheadHeading>
|
|
145
|
+
<LetterheadParagraph>
|
|
146
|
+
Your Indie Tabletop Club account has been verified. Yay!
|
|
147
|
+
</LetterheadParagraph>
|
|
148
|
+
</LetterheadHeader>
|
|
149
|
+
|
|
150
|
+
{onClose ? (
|
|
151
|
+
<Button onClick={() => onClose()} className={button()}>
|
|
152
|
+
Done
|
|
153
|
+
</Button>
|
|
154
|
+
) : (
|
|
155
|
+
<Link href={hrefs.dashboard()} className={button()}>
|
|
156
|
+
Go to dashboard
|
|
157
|
+
</Link>
|
|
158
|
+
)}
|
|
159
|
+
</Letterhead>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
type VerifyStep =
|
|
164
|
+
| { type: "INITIAL" }
|
|
165
|
+
| { type: "SUBMIT_CODE"; tokenId: string }
|
|
166
|
+
| { type: "SUCCESS" };
|
|
167
|
+
|
|
168
|
+
export function VerifyAccountView(props: {
|
|
169
|
+
currentUser: CurrentUser;
|
|
170
|
+
reload: () => void;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* If provided, will cause the success step to render a close dialog button
|
|
174
|
+
* instead of "Go to Dashboard" button.
|
|
175
|
+
*
|
|
176
|
+
* This is useful if this view is used within a modal dialog context.
|
|
177
|
+
*/
|
|
178
|
+
onClose?: AuthEventHandler;
|
|
179
|
+
}) {
|
|
180
|
+
const [step, setStep] = useState<VerifyStep>({ type: "INITIAL" });
|
|
181
|
+
|
|
182
|
+
switch (step.type) {
|
|
183
|
+
case "INITIAL": {
|
|
184
|
+
return <InitialStep {...props} setStep={setStep} />;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
case "SUBMIT_CODE": {
|
|
188
|
+
return <SubmitCodeStep {...props} {...step} setStep={setStep} />;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
case "SUCCESS": {
|
|
192
|
+
return <SuccessStep {...props} />;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M28 0C43.464 0 56 12.536 56 28C56 31.3019 55.4265 34.4697 54.377 37.4111C52.2143 35.3015 49.2598 34 46 34C39.3726 34 34 39.3726 34 46C34 49.2598 35.3015 52.2143 37.4111 54.377C34.4697 55.4265 31.3019 56 28 56C12.536 56 0 43.464 0 28C0 12.536 12.536 0 28 0Z" fill="white"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { style } from "@vanilla-extract/css";
|
|
2
|
+
import { Hover, MinWidth } from "../media.ts";
|
|
3
|
+
import iconMask from "./iconMask.svg";
|
|
4
|
+
|
|
5
|
+
export const accountIcon = {
|
|
6
|
+
container: style({
|
|
7
|
+
position: "relative",
|
|
8
|
+
marginInline: "auto",
|
|
9
|
+
|
|
10
|
+
inlineSize: "56px",
|
|
11
|
+
blockSize: "56px",
|
|
12
|
+
}),
|
|
13
|
+
|
|
14
|
+
syncIcon: style({
|
|
15
|
+
position: "absolute",
|
|
16
|
+
right: "0",
|
|
17
|
+
bottom: "0",
|
|
18
|
+
}),
|
|
19
|
+
|
|
20
|
+
image: style({
|
|
21
|
+
mask: `url("${iconMask}") no-repeat`,
|
|
22
|
+
inlineSize: "100%",
|
|
23
|
+
blockSize: "100%",
|
|
24
|
+
borderRadius: "50%",
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const page = style({
|
|
29
|
+
backgroundColor: "white",
|
|
30
|
+
|
|
31
|
+
"@media": {
|
|
32
|
+
[MinWidth.SMALL]: {
|
|
33
|
+
backgroundColor: "transparent",
|
|
34
|
+
padding: "clamp(1rem, 5vw, 5rem)",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const accountPicker = {
|
|
40
|
+
container: style({
|
|
41
|
+
backgroundColor: "#fafafa",
|
|
42
|
+
borderRadius: "0.5rem",
|
|
43
|
+
}),
|
|
44
|
+
|
|
45
|
+
button: style({
|
|
46
|
+
padding: "1rem 1.5rem",
|
|
47
|
+
textAlign: "start",
|
|
48
|
+
inlineSize: "100%",
|
|
49
|
+
borderRadius: "inherit",
|
|
50
|
+
|
|
51
|
+
"@media": {
|
|
52
|
+
[Hover.HOVER]: {
|
|
53
|
+
transition: "200ms background-color",
|
|
54
|
+
|
|
55
|
+
":hover": {
|
|
56
|
+
backgroundColor: "hsl(from #fafafa h s calc(l - 3))",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
|
|
62
|
+
buttonLabel: style({
|
|
63
|
+
fontWeight: 600,
|
|
64
|
+
fontSize: "1.125rem",
|
|
65
|
+
}),
|
|
66
|
+
|
|
67
|
+
divider: style({
|
|
68
|
+
marginInline: "1.5rem",
|
|
69
|
+
backgroundColor: "#e2e2e2",
|
|
70
|
+
blockSize: "1px",
|
|
71
|
+
}),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const loadingView = {
|
|
75
|
+
container: style({
|
|
76
|
+
display: "flex",
|
|
77
|
+
justifyContent: "center",
|
|
78
|
+
alignItems: "center",
|
|
79
|
+
height: "12rem",
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { CurrentUser } from
|
|
1
|
+
import type { CurrentUser } from "../types.ts";
|
|
2
|
+
|
|
2
3
|
export type AuthEventHandler = () => Promise<void> | void;
|
|
4
|
+
|
|
3
5
|
export type ClientLogoutHandler = (options: {
|
|
4
|
-
|
|
6
|
+
serverUser: CurrentUser;
|
|
5
7
|
}) => Promise<void> | void;
|
|
8
|
+
|
|
6
9
|
export type DefaultFormValues = {
|
|
7
|
-
|
|
10
|
+
email?: string;
|
|
8
11
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useClient } from "../AppConfig/AppConfig.tsx";
|
|
3
|
+
import { type Failure, Pending, type Success } from "../async-op.ts";
|
|
4
|
+
import type { CurrentUser, FailurePayload } from "../types.ts";
|
|
5
|
+
|
|
6
|
+
export function useFetchCurrentUser(options?: {
|
|
7
|
+
/**
|
|
8
|
+
* Optionally disable immediate fetch action.
|
|
9
|
+
*
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
performFetch?: boolean;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetch new data every time the window is focused.
|
|
16
|
+
*
|
|
17
|
+
* Note that `performFetch` has to be enabled for this option to have an
|
|
18
|
+
* effect — we are not revalidating when there is no fetch to do!
|
|
19
|
+
*
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
revalidateOnFocus?: boolean;
|
|
23
|
+
}) {
|
|
24
|
+
const { performFetch = true, revalidateOnFocus = false } = options ?? {};
|
|
25
|
+
const client = useClient();
|
|
26
|
+
const [latestAttemptTs, setLatestAttemptTs] = useState(Date.now());
|
|
27
|
+
|
|
28
|
+
const [result, setResult] = useState<
|
|
29
|
+
Success<CurrentUser> | Failure<FailurePayload> | Pending
|
|
30
|
+
>(new Pending());
|
|
31
|
+
|
|
32
|
+
// We are intentionally not using SWR in this case, as we don't want to deal
|
|
33
|
+
// with caching or any of that business.
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (performFetch) {
|
|
36
|
+
const fetchUserAndStoreResult = async () => {
|
|
37
|
+
const result = await client.getCurrentUser();
|
|
38
|
+
setResult(result);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Invoke the fetch action
|
|
42
|
+
fetchUserAndStoreResult();
|
|
43
|
+
|
|
44
|
+
if (revalidateOnFocus) {
|
|
45
|
+
const controller = new AbortController();
|
|
46
|
+
window.addEventListener("focus", fetchUserAndStoreResult, controller);
|
|
47
|
+
|
|
48
|
+
return () => {
|
|
49
|
+
controller.abort();
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}, [client, latestAttemptTs, performFetch]);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
result,
|
|
57
|
+
latestAttemptTs,
|
|
58
|
+
reload: () => {
|
|
59
|
+
setResult(new Pending());
|
|
60
|
+
setLatestAttemptTs(Date.now());
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useSearchParams } from "wouter";
|
|
2
|
+
|
|
3
|
+
function isValidRedirect(value: string | null | undefined): value is string {
|
|
4
|
+
return !!(value && /^[~/]/.test(value));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Gets redirect path from query params and validates it.
|
|
9
|
+
*
|
|
10
|
+
* Returns `null` if no path is found, or it is invalid.
|
|
11
|
+
*/
|
|
12
|
+
export function useRedirectPath() {
|
|
13
|
+
const [params] = useSearchParams();
|
|
14
|
+
const redirectTo = params.get("redirectTo");
|
|
15
|
+
|
|
16
|
+
if (isValidRedirect(redirectTo)) {
|
|
17
|
+
return redirectTo;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { keyframes } from "@vanilla-extract/css";
|
|
2
|
+
|
|
3
|
+
export const fadeIn = keyframes({
|
|
4
|
+
from: { opacity: 0 },
|
|
5
|
+
to: { opacity: 1 },
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const slideUp = keyframes({
|
|
9
|
+
from: { transform: `translateY(100%)` },
|
|
10
|
+
to: { transform: `translateY(0)` },
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const bounce = keyframes({
|
|
14
|
+
"0%": { transform: "translateY(0)" },
|
|
15
|
+
"20%": { transform: "translateY(-20%)" },
|
|
16
|
+
"50%": { transform: "translateY(0)" },
|
|
17
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Appends " (Copy)" to the end of the input string if it doesn't already end
|
|
3
|
+
* with " (Copy)", otherwise it appends a number after "Copy", incrementing it
|
|
4
|
+
* if necessary.
|
|
5
|
+
*/
|
|
6
|
+
export function appendCopyToText(input: string): string {
|
|
7
|
+
const regex = /^(?<value>.*) \(Copy(?: (?<count>\d+))?\)$/;
|
|
8
|
+
const match = input.match(regex);
|
|
9
|
+
|
|
10
|
+
// If there isn't a match, we directly append to the input.
|
|
11
|
+
if (!match) {
|
|
12
|
+
return `${input.trim()} (Copy)`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { value, count } = match.groups ?? {};
|
|
16
|
+
|
|
17
|
+
// If `count` capturing group is not present, it means that the input ends
|
|
18
|
+
// with the copy suffix, but it doesn't contain count.
|
|
19
|
+
const nextCount = !count ? 2 : parseInt(count, 10) + 1;
|
|
20
|
+
|
|
21
|
+
return `${value?.trim()} (Copy ${nextCount})`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Works like {@link appendCopyToText}, but ignores empty strings.
|
|
26
|
+
*/
|
|
27
|
+
export function maybeAppendCopyToText(input: string) {
|
|
28
|
+
// If input is falsy (i.e. empty string) then we don't want to append
|
|
29
|
+
// anything to it.
|
|
30
|
+
if (!input) {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return appendCopyToText(input);
|
|
35
|
+
}
|