@indietabletop/appkit 6.1.6 → 7.0.0-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.
Files changed (269) hide show
  1. package/dist/AppConfig/AppConfig.d.ts +29 -0
  2. package/dist/AuthCard/AuthCard.d.ts +10 -0
  3. package/dist/AuthCard/AuthCard.stories.d.ts +34 -0
  4. package/dist/AuthCard/style.css.d.ts +23 -0
  5. package/dist/DialogTrigger/index.d.ts +13 -0
  6. package/dist/DocumentTitle/DocumentTitle.d.ts +3 -0
  7. package/dist/EnumMapper.d.ts +25 -0
  8. package/dist/ExternalLink.d.ts +3 -0
  9. package/dist/FullscreenDismissBlocker.d.ts +5 -0
  10. package/{lib/HistoryState.ts → dist/HistoryState.d.ts} +2 -5
  11. package/dist/IndieTabletopClubLogo.d.ts +7 -0
  12. package/dist/IndieTabletopClubSymbol.d.ts +7 -0
  13. package/dist/InfoPage/index.d.ts +8 -0
  14. package/dist/InfoPage/pages.d.ts +2 -0
  15. package/dist/InfoPage/style.css.d.ts +5 -0
  16. package/dist/Letterhead/index.d.ts +19 -0
  17. package/dist/Letterhead/stories.d.ts +13 -0
  18. package/dist/Letterhead/style.css.d.ts +46 -0
  19. package/dist/LetterheadForm/LetterheadReadonlyTextField.stories.d.ts +17 -0
  20. package/dist/LetterheadForm/LetterheadSubmitError.stories.d.ts +11 -0
  21. package/dist/LetterheadForm/LetterheadTextField.stories.d.ts +336 -0
  22. package/dist/LetterheadForm/index.d.ts +44 -0
  23. package/dist/LetterheadForm/style.css.d.ts +8 -0
  24. package/dist/LoadingIndicator.d.ts +3 -0
  25. package/dist/MiddotSeparated/MiddotSeparated.d.ts +8 -0
  26. package/dist/MiddotSeparated/MiddotSeparated.stories.d.ts +586 -0
  27. package/dist/MiddotSeparated/style.css.d.ts +1 -0
  28. package/dist/ModalDialog/index.d.ts +12 -0
  29. package/dist/ModalDialog/style.css.d.ts +58 -0
  30. package/dist/ModernIDB/Cursor.d.ts +56 -0
  31. package/dist/ModernIDB/ModernIDB.d.ts +66 -0
  32. package/dist/ModernIDB/ModernIDBError.d.ts +3 -0
  33. package/dist/ModernIDB/ObjectStore.d.ts +112 -0
  34. package/dist/ModernIDB/ObjectStoreIndex.d.ts +53 -0
  35. package/dist/ModernIDB/Transaction.d.ts +16 -0
  36. package/dist/ModernIDB/VersionChangeManager.d.ts +30 -0
  37. package/dist/ModernIDB/bindings/factory.d.ts +12 -0
  38. package/dist/ModernIDB/bindings/index.d.ts +2 -0
  39. package/{lib/ModernIDB/bindings/types.ts → dist/ModernIDB/bindings/types.d.ts} +13 -32
  40. package/dist/ModernIDB/bindings/utils.d.ts +2 -0
  41. package/dist/ModernIDB/index.d.ts +10 -0
  42. package/dist/ModernIDB/types.d.ts +88 -0
  43. package/dist/ModernIDB/utils.d.ts +4 -0
  44. package/dist/QRCode/QRCode.d.ts +7 -0
  45. package/dist/QRCode/QRCode.stories.d.ts +33 -0
  46. package/dist/QRCode/style.css.d.ts +4 -0
  47. package/dist/ReleaseInfo/index.d.ts +5 -0
  48. package/dist/RulesetResolver.d.ts +87 -0
  49. package/dist/SafariCheck/SafariCheck.d.ts +23 -0
  50. package/dist/SafariCheck/SafariCheck.stories.d.ts +73 -0
  51. package/dist/SafariCheck/style.css.d.ts +17 -0
  52. package/dist/ServiceWorkerHandler.d.ts +11 -0
  53. package/dist/ShareButton/ShareButton.d.ts +57 -0
  54. package/dist/ShareButton/ShareButton.stories.d.ts +1577 -0
  55. package/dist/ShareButton/test.css.d.ts +1 -0
  56. package/dist/SubscribeCard/LetterheadInfoCard.d.ts +2 -0
  57. package/dist/SubscribeCard/SubscribeByEmailCard.d.ts +24 -0
  58. package/dist/SubscribeCard/SubscribeByEmailCard.stories.d.ts +10 -0
  59. package/dist/SubscribeCard/SubscribeByPledgeCard.d.ts +36 -0
  60. package/dist/SubscribeCard/SubscribeByPledgeCard.stories.d.ts +65 -0
  61. package/dist/SubscribeCard/style.css.d.ts +4 -0
  62. package/dist/account/AccountIssueView.d.ts +3 -0
  63. package/dist/account/AlreadyLoggedInView.d.ts +5 -0
  64. package/dist/account/CurrentUserFetcher.d.ts +20 -0
  65. package/dist/account/CurrentUserFetcher.stories.d.ts +136 -0
  66. package/dist/account/FailureFallbackView.d.ts +1 -0
  67. package/dist/account/JoinCard.d.ts +14 -0
  68. package/dist/account/JoinCard.stories.d.ts +143 -0
  69. package/dist/account/LoadingView.d.ts +1 -0
  70. package/dist/account/LoginCard.d.ts +39 -0
  71. package/dist/account/LoginCard.stories.d.ts +217 -0
  72. package/dist/account/LoginView.d.ts +10 -0
  73. package/dist/account/NoConnectionView.d.ts +4 -0
  74. package/dist/account/PasswordResetCard.d.ts +15 -0
  75. package/dist/account/PasswordResetCard.stories.d.ts +128 -0
  76. package/dist/account/UserMismatchView.d.ts +6 -0
  77. package/dist/account/VerifyPage.d.ts +13 -0
  78. package/dist/account/style.css.d.ts +10 -0
  79. package/{lib/account/types.ts → dist/account/types.d.ts} +3 -6
  80. package/dist/account/useFetchCurrentUser.d.ts +28 -0
  81. package/dist/account/useRedirectPath.d.ts +6 -0
  82. package/dist/animations.css.d.ts +3 -0
  83. package/dist/append-copy-to-text.d.ts +10 -0
  84. package/dist/append-copy-to-text.test.d.ts +1 -0
  85. package/dist/appkit.css +1 -0
  86. package/dist/appkit.js +10692 -0
  87. package/dist/async-op.d.ts +101 -0
  88. package/dist/atomic.css.d.ts +6 -0
  89. package/{lib/caught-value.ts → dist/caught-value.d.ts} +1 -11
  90. package/{lib/class-names.ts → dist/class-names.d.ts} +6 -17
  91. package/dist/client.d.ts +424 -0
  92. package/dist/common.css.d.ts +5 -0
  93. package/dist/copyrightRange.d.ts +1 -0
  94. package/dist/copyrightRange.test.d.ts +1 -0
  95. package/dist/createSafeStorage.d.ts +34 -0
  96. package/dist/failureMessages.d.ts +20 -0
  97. package/dist/failureMessages.test.d.ts +1 -0
  98. package/dist/form/FormSubmitButton.d.ts +17 -0
  99. package/dist/form/SubmitErrorAlert.d.ts +5 -0
  100. package/dist/form/style.css.d.ts +3 -0
  101. package/dist/globals.css.d.ts +0 -0
  102. package/dist/groupBy.d.ts +1 -0
  103. package/dist/groupBy.test.d.ts +1 -0
  104. package/dist/hrefs.d.ts +32 -0
  105. package/dist/hrefs.test.d.ts +1 -0
  106. package/dist/idToDate.d.ts +5 -0
  107. package/dist/idToDate.test.d.ts +1 -0
  108. package/dist/ids.d.ts +1 -0
  109. package/dist/ids.test.d.ts +1 -0
  110. package/dist/index.d.ts +64 -0
  111. package/dist/internal.css.d.ts +2 -0
  112. package/dist/mailto.d.ts +8 -0
  113. package/dist/mailto.test.d.ts +1 -0
  114. package/dist/media.d.ts +39 -0
  115. package/dist/random.d.ts +3 -0
  116. package/dist/result/swr.d.ts +4 -0
  117. package/{lib/sleep.ts → dist/sleep.d.ts} +1 -3
  118. package/dist/store/index.d.ts +237 -0
  119. package/dist/store/store.d.ts +144 -0
  120. package/dist/store/types.d.ts +49 -0
  121. package/dist/store/utils.d.ts +10 -0
  122. package/dist/storybook/decorators.d.ts +3 -0
  123. package/dist/structs.d.ts +1 -0
  124. package/{lib/typeguards.ts → dist/typeguards.d.ts} +1 -3
  125. package/dist/typeguards.test.d.ts +1 -0
  126. package/{lib/types.ts → dist/types.d.ts} +12 -23
  127. package/dist/unique.d.ts +10 -0
  128. package/dist/unique.test.d.ts +1 -0
  129. package/dist/use-async-op.d.ts +6 -0
  130. package/dist/use-document-background-color.d.ts +4 -0
  131. package/dist/use-form.d.ts +29 -0
  132. package/dist/use-is-installed.d.ts +8 -0
  133. package/dist/use-media-query.d.ts +1 -0
  134. package/dist/use-reverting-state.d.ts +5 -0
  135. package/dist/use-scroll-restoration.d.ts +25 -0
  136. package/dist/useEnsureValue.d.ts +6 -0
  137. package/dist/useInvokeClient.d.ts +25 -0
  138. package/dist/useIsVisible.d.ts +4 -0
  139. package/dist/utm.d.ts +58 -0
  140. package/dist/utm.test.d.ts +1 -0
  141. package/dist/validations.d.ts +3 -0
  142. package/dist/vars.css.d.ts +10 -0
  143. package/package.json +12 -5
  144. package/lib/AppConfig/AppConfig.tsx +0 -61
  145. package/lib/AuthCard/AuthCard.stories.ts +0 -34
  146. package/lib/AuthCard/AuthCard.tsx +0 -64
  147. package/lib/AuthCard/style.css.ts +0 -49
  148. package/lib/DialogTrigger/index.tsx +0 -36
  149. package/lib/DocumentTitle/DocumentTitle.tsx +0 -10
  150. package/lib/EnumMapper.ts +0 -50
  151. package/lib/ExternalLink.tsx +0 -10
  152. package/lib/FullscreenDismissBlocker.tsx +0 -23
  153. package/lib/IndieTabletopClubLogo.tsx +0 -44
  154. package/lib/IndieTabletopClubSymbol.tsx +0 -37
  155. package/lib/InfoPage/index.tsx +0 -46
  156. package/lib/InfoPage/pages.tsx +0 -36
  157. package/lib/InfoPage/style.css.ts +0 -36
  158. package/lib/Letterhead/index.tsx +0 -85
  159. package/lib/Letterhead/stories.tsx +0 -41
  160. package/lib/Letterhead/style.css.ts +0 -152
  161. package/lib/LetterheadForm/LetterheadReadonlyTextField.stories.tsx +0 -17
  162. package/lib/LetterheadForm/LetterheadSubmitError.stories.tsx +0 -19
  163. package/lib/LetterheadForm/LetterheadTextField.stories.tsx +0 -19
  164. package/lib/LetterheadForm/index.tsx +0 -137
  165. package/lib/LetterheadForm/style.css.ts +0 -89
  166. package/lib/LoadingIndicator.tsx +0 -40
  167. package/lib/MiddotSeparated/MiddotSeparated.stories.ts +0 -26
  168. package/lib/MiddotSeparated/MiddotSeparated.tsx +0 -26
  169. package/lib/MiddotSeparated/style.css.ts +0 -10
  170. package/lib/ModalDialog/index.tsx +0 -28
  171. package/lib/ModalDialog/style.css.ts +0 -88
  172. package/lib/ModernIDB/Cursor.ts +0 -91
  173. package/lib/ModernIDB/ModernIDB.ts +0 -337
  174. package/lib/ModernIDB/ModernIDBError.ts +0 -9
  175. package/lib/ModernIDB/ObjectStore.ts +0 -195
  176. package/lib/ModernIDB/ObjectStoreIndex.ts +0 -102
  177. package/lib/ModernIDB/README.md +0 -9
  178. package/lib/ModernIDB/Transaction.ts +0 -40
  179. package/lib/ModernIDB/VersionChangeManager.ts +0 -57
  180. package/lib/ModernIDB/bindings/factory.tsx +0 -165
  181. package/lib/ModernIDB/bindings/index.ts +0 -2
  182. package/lib/ModernIDB/bindings/utils.tsx +0 -32
  183. package/lib/ModernIDB/index.ts +0 -10
  184. package/lib/ModernIDB/types.ts +0 -120
  185. package/lib/ModernIDB/utils.ts +0 -51
  186. package/lib/QRCode/QRCode.stories.tsx +0 -41
  187. package/lib/QRCode/QRCode.tsx +0 -54
  188. package/lib/QRCode/style.css.ts +0 -23
  189. package/lib/ReleaseInfo/index.tsx +0 -29
  190. package/lib/RulesetResolver.ts +0 -214
  191. package/lib/SafariCheck/SafariCheck.stories.tsx +0 -99
  192. package/lib/SafariCheck/SafariCheck.tsx +0 -273
  193. package/lib/SafariCheck/addToDock.svg +0 -13
  194. package/lib/SafariCheck/addToHomeScreen.svg +0 -12
  195. package/lib/SafariCheck/safari.svg +0 -32
  196. package/lib/SafariCheck/shareIcon.svg +0 -11
  197. package/lib/SafariCheck/style.css.ts +0 -106
  198. package/lib/ServiceWorkerHandler.tsx +0 -53
  199. package/lib/ShareButton/ShareButton.stories.tsx +0 -58
  200. package/lib/ShareButton/ShareButton.tsx +0 -153
  201. package/lib/ShareButton/test.css.ts +0 -3
  202. package/lib/SubscribeCard/LetterheadInfoCard.tsx +0 -23
  203. package/lib/SubscribeCard/SubscribeByEmailCard.stories.tsx +0 -69
  204. package/lib/SubscribeCard/SubscribeByEmailCard.tsx +0 -183
  205. package/lib/SubscribeCard/SubscribeByPledgeCard.stories.tsx +0 -133
  206. package/lib/SubscribeCard/SubscribeByPledgeCard.tsx +0 -127
  207. package/lib/SubscribeCard/style.css.ts +0 -14
  208. package/lib/account/AccountIssueView.tsx +0 -44
  209. package/lib/account/AlreadyLoggedInView.tsx +0 -47
  210. package/lib/account/CurrentUserFetcher.stories.tsx +0 -292
  211. package/lib/account/CurrentUserFetcher.tsx +0 -118
  212. package/lib/account/FailureFallbackView.tsx +0 -36
  213. package/lib/account/JoinCard.stories.tsx +0 -257
  214. package/lib/account/JoinCard.tsx +0 -301
  215. package/lib/account/LoadingView.tsx +0 -14
  216. package/lib/account/LoginCard.stories.tsx +0 -288
  217. package/lib/account/LoginCard.tsx +0 -100
  218. package/lib/account/LoginView.tsx +0 -151
  219. package/lib/account/NoConnectionView.tsx +0 -34
  220. package/lib/account/PasswordResetCard.stories.tsx +0 -242
  221. package/lib/account/PasswordResetCard.tsx +0 -296
  222. package/lib/account/UserMismatchView.tsx +0 -62
  223. package/lib/account/VerifyPage.tsx +0 -195
  224. package/lib/account/style.css.ts +0 -57
  225. package/lib/account/useFetchCurrentUser.tsx +0 -63
  226. package/lib/account/useRedirectPath.ts +0 -21
  227. package/lib/animations.css.ts +0 -17
  228. package/lib/append-copy-to-text.ts +0 -35
  229. package/lib/async-op.ts +0 -286
  230. package/lib/atomic.css.ts +0 -11
  231. package/lib/client.ts +0 -662
  232. package/lib/common.css.ts +0 -48
  233. package/lib/copyrightRange.ts +0 -10
  234. package/lib/createSafeStorage.ts +0 -91
  235. package/lib/failureMessages.ts +0 -108
  236. package/lib/form/FormSubmitButton.tsx +0 -58
  237. package/lib/form/SubmitErrorAlert.tsx +0 -21
  238. package/lib/form/style.css.ts +0 -9
  239. package/lib/globals.css.ts +0 -62
  240. package/lib/groupBy.ts +0 -25
  241. package/lib/hrefs.ts +0 -48
  242. package/lib/idToDate.ts +0 -8
  243. package/lib/ids.ts +0 -6
  244. package/lib/index.ts +0 -71
  245. package/lib/internal.css.ts +0 -10
  246. package/lib/mailto.ts +0 -40
  247. package/lib/media.ts +0 -50
  248. package/lib/random.ts +0 -19
  249. package/lib/result/swr.ts +0 -18
  250. package/lib/store/index.tsx +0 -241
  251. package/lib/store/store.ts +0 -479
  252. package/lib/store/types.ts +0 -45
  253. package/lib/store/utils.ts +0 -54
  254. package/lib/storybook/decorators.tsx +0 -10
  255. package/lib/structs.ts +0 -3
  256. package/lib/unique.ts +0 -24
  257. package/lib/use-async-op.ts +0 -16
  258. package/lib/use-document-background-color.ts +0 -16
  259. package/lib/use-form.ts +0 -78
  260. package/lib/use-is-installed.ts +0 -17
  261. package/lib/use-media-query.ts +0 -21
  262. package/lib/use-reverting-state.ts +0 -32
  263. package/lib/use-scroll-restoration.ts +0 -99
  264. package/lib/useEnsureValue.ts +0 -31
  265. package/lib/useInvokeClient.ts +0 -54
  266. package/lib/useIsVisible.ts +0 -27
  267. package/lib/utm.ts +0 -92
  268. package/lib/validations.ts +0 -25
  269. package/lib/vars.css.ts +0 -13
@@ -1,118 +0,0 @@
1
- import { useState, type ReactNode } from "react";
2
- import { ModalDialog } from "../ModalDialog/index.tsx";
3
- import { useCurrentUser } from "../store/index.tsx";
4
- import { AccountIssueView } from "./AccountIssueView.tsx";
5
- import { LoginView } from "./LoginView.tsx";
6
- import { useFetchCurrentUser } from "./useFetchCurrentUser.tsx";
7
- import { UserMismatchView } from "./UserMismatchView.tsx";
8
- import { VerifyAccountView } from "./VerifyPage.tsx";
9
-
10
- type CurrentUserFetcherProps = {
11
- children: ReactNode;
12
- };
13
-
14
- /**
15
- * Fetches fresh current user data if a local user exists in the app store.
16
- *
17
- * This component uses the Indie Tabletop Client under the hood, so if new
18
- * data is successfully fetched, the onCurrentUser callback will be invoked,
19
- * and it is up to the configuration of the client to store the data.
20
- *
21
- * Importantly, this component also handles the various user account issues
22
- * that we could run into: expired session, user mismatch and account deletion.
23
- *
24
- * All other errors are ignored. This allows users to use the app in offline
25
- * mode, and doesn't interrupt their session if some unexpected error happens,
26
- * which they cannot do anything about anyways.
27
- */
28
- export function CurrentUserFetcher(props: CurrentUserFetcherProps) {
29
- const { children } = props;
30
- const localUser = useCurrentUser();
31
- const [isOpen, setOpen] = useState(true);
32
-
33
- const { result, reload } = useFetchCurrentUser({
34
- // We only want to fetch the current user if they exist in the store, i.e.
35
- // they have logged into the app previously.
36
- performFetch: !!localUser,
37
-
38
- // If we are performing a fetch, we want to make sure that it is up to date
39
- // every time the user focuses the window.
40
- revalidateOnFocus: true,
41
- });
42
-
43
- if (result.isFailure && result.failure.type === "API_ERROR") {
44
- // The user's session has expired. They should be prompted to
45
- // re-authenticate, as any syncing attemps will fail.
46
- if (result.failure.code === 401) {
47
- return (
48
- <>
49
- <ModalDialog size="large" open>
50
- <LoginView
51
- currentUser={localUser}
52
- onLogin={() => reload()}
53
- description={undefined}
54
- reload={reload}
55
- />
56
- </ModalDialog>
57
- {children}
58
- </>
59
- );
60
- }
61
-
62
- if (result.failure.code === 404) {
63
- // The user account is not found. The user might have been deleted.
64
- // The user should be notified and instructed to log out, as many
65
- // interactions acrosss the app will be broken.
66
- return (
67
- <>
68
- <ModalDialog size="large" open>
69
- <AccountIssueView reload={reload} />
70
- </ModalDialog>
71
-
72
- {children}
73
- </>
74
- );
75
- }
76
- }
77
-
78
- if (localUser && result.isSuccess) {
79
- const serverUser = result.value;
80
-
81
- // The cookie (server) user and the user in local storage are a different
82
- // user. The current user needs to decide which account to use.
83
- if (serverUser.id !== localUser.id) {
84
- return (
85
- <>
86
- <ModalDialog size="large" open>
87
- <UserMismatchView
88
- serverUser={result.value}
89
- localUser={localUser}
90
- reload={reload}
91
- />
92
- </ModalDialog>
93
-
94
- {children}
95
- </>
96
- );
97
- }
98
-
99
- if (!serverUser.isVerified) {
100
- return (
101
- <>
102
- <ModalDialog size="large" open={isOpen}>
103
- <VerifyAccountView
104
- currentUser={serverUser}
105
- onClose={() => setOpen(false)}
106
- reload={reload}
107
- />
108
- </ModalDialog>
109
-
110
- {children}
111
- </>
112
- );
113
- }
114
- }
115
-
116
- // In all other cases we simply render the children
117
- return <>{children}</>;
118
- }
@@ -1,36 +0,0 @@
1
- import { Button } from "@ariakit/react";
2
- import { cx } from "../class-names.ts";
3
- import { interactiveText } from "../common.css.ts";
4
- import {
5
- Letterhead,
6
- LetterheadHeading,
7
- LetterheadParagraph,
8
- } from "../Letterhead/index.tsx";
9
-
10
- export function FailureFallbackView() {
11
- return (
12
- <Letterhead>
13
- <LetterheadHeading>Something went wrong</LetterheadHeading>
14
-
15
- <LetterheadParagraph>
16
- {"This is probably an issue on our side. Sorry about that!"}
17
- </LetterheadParagraph>
18
-
19
- <LetterheadParagraph>
20
- {"You can try "}
21
- <Button
22
- {...cx(interactiveText)}
23
- onClick={() => window.location.reload()}
24
- >
25
- reloading the app
26
- </Button>
27
- {". "}
28
- {"If the issue persists, please get in touch at "}
29
- <a {...cx(interactiveText)} href="mailto:support@indietabletop.club">
30
- support@indietabletop.club
31
- </a>
32
- {"."}
33
- </LetterheadParagraph>
34
- </Letterhead>
35
- );
36
- }
@@ -1,257 +0,0 @@
1
- import { http, HttpResponse } from "msw";
2
- import preview from "../../.storybook/preview.tsx";
3
- import { sleep } from "../sleep.ts";
4
- import type { CurrentUser, SessionInfo } from "../types.ts";
5
- import { JoinCard } from "./JoinCard.tsx";
6
-
7
- function createMocks(options?: { responseSpeed?: number }) {
8
- const simulateNetwork = () => sleep(options?.responseSpeed ?? 2000);
9
-
10
- const john: CurrentUser = {
11
- id: "john",
12
- email: "john@example.com",
13
- isVerified: true,
14
- };
15
-
16
- const sessionInfo: SessionInfo = {
17
- createdTs: 123,
18
- expiresTs: 123,
19
- };
20
-
21
- return {
22
- data: { john },
23
- handlers: {
24
- refreshTokens: {
25
- failed: () => {
26
- return http.post(
27
- "http://mock.api/v1/sessions/access-tokens",
28
- async () => {
29
- await simulateNetwork();
30
- return HttpResponse.text("Refresh token expired or missing", {
31
- status: 401,
32
- });
33
- },
34
- );
35
- },
36
- },
37
-
38
- getCurrentUser: {
39
- success: (currentUser: CurrentUser) => {
40
- return http.get("http://mock.api/v1/users/me", async () => {
41
- await simulateNetwork();
42
- return HttpResponse.json(currentUser);
43
- });
44
- },
45
-
46
- noConnection: () => {
47
- return http.get("http://mock.api/v1/users/me", async () => {
48
- return HttpResponse.error();
49
- });
50
- },
51
-
52
- unknownFailure: () => {
53
- return http.get("http://mock.api/v1/users/me", async () => {
54
- await simulateNetwork();
55
- return HttpResponse.text("Internal server error", { status: 500 });
56
- });
57
- },
58
-
59
- /**
60
- * Auth cookies no longer valid to make this request.
61
- */
62
- notAuthenticated: () => {
63
- return http.get("http://mock.api/v1/users/me", async () => {
64
- await simulateNetwork();
65
- return HttpResponse.text("Not authenticated", { status: 401 });
66
- });
67
- },
68
- },
69
-
70
- join: {
71
- success(currentUser: CurrentUser) {
72
- return http.post("http://mock.api/v1/users", async () => {
73
- await simulateNetwork();
74
-
75
- return HttpResponse.json({
76
- currentUser,
77
- sessionInfo,
78
- tokenId: "1",
79
- });
80
- });
81
- },
82
-
83
- emailTaken() {
84
- return http.post("http://mock.api/v1/users", async () => {
85
- await simulateNetwork();
86
-
87
- return HttpResponse.text("Duplicate unique key", { status: 409 });
88
- });
89
- },
90
-
91
- unknownFailure() {
92
- return http.post("http://mock.api/v1/users", async () => {
93
- await simulateNetwork();
94
- return HttpResponse.text("Internal server error", {
95
- status: 500,
96
- });
97
- });
98
- },
99
- },
100
- verify: {
101
- success() {
102
- return http.put(
103
- "http://mock.api/v1/user-verification-tokens/:id",
104
- async () => {
105
- await simulateNetwork();
106
- return HttpResponse.json({ message: "OK" });
107
- },
108
- );
109
- },
110
- notFoundOrExpired() {
111
- return http.put(
112
- "http://mock.api/v1/user-verification-tokens/:id",
113
- async () => {
114
- await simulateNetwork();
115
- return HttpResponse.text("Not found", { status: 404 });
116
- },
117
- );
118
- },
119
- unknownFailure() {
120
- return http.put(
121
- "http://mock.api/v1/user-verification-tokens/:id",
122
- async () => {
123
- await simulateNetwork();
124
- return HttpResponse.text("Internal server error", {
125
- status: 500,
126
- });
127
- },
128
- );
129
- },
130
- },
131
- },
132
- };
133
- }
134
-
135
- const { data, handlers } = createMocks({ responseSpeed: 700 });
136
-
137
- const meta = preview.meta({
138
- title: "Account/Join Card",
139
- component: JoinCard,
140
- tags: ["autodocs"],
141
- args: {
142
- defaultValues: {},
143
- },
144
- parameters: {
145
- msw: {
146
- handlers: {
147
- refresh: handlers.refreshTokens.failed(),
148
- currentUser: handlers.getCurrentUser.notAuthenticated(),
149
- join: handlers.join.success(data.john),
150
- verify: handlers.verify.success(),
151
- },
152
- },
153
- },
154
- });
155
-
156
- /**
157
- * The default case in which all steps of the flow succeed.
158
- */
159
- export const Success = meta.story({});
160
-
161
- /**
162
- * In this case, the initial step fails because the email address is associated
163
- * with an existing user.
164
- */
165
- export const EmailTakenOnJoin = meta.story({
166
- parameters: {
167
- msw: {
168
- handlers: {
169
- join: handlers.join.emailTaken(),
170
- },
171
- },
172
- },
173
- });
174
-
175
- /**
176
- * In this case, the initial step fails for a reason that doesn't have any
177
- * special handling in this location. E.g. network error, server error, etc.
178
- */
179
- export const UnknownFailureOnJoin = meta.story({
180
- parameters: {
181
- msw: {
182
- handlers: {
183
- join: handlers.join.unknownFailure(),
184
- },
185
- },
186
- },
187
- });
188
-
189
- /**
190
- * In this case, the verify step fails because the token has expired,
191
- * or is incorrect.
192
- */
193
- export const NotFoundOrExpiredOnVerify = meta.story({
194
- parameters: {
195
- msw: {
196
- handlers: {
197
- verify: handlers.verify.notFoundOrExpired(),
198
- },
199
- },
200
- },
201
- });
202
-
203
- /**
204
- * In this case, the verify step fails for a reason that doesn't have any
205
- * special handling in this location. E.g. network error, server error, etc.
206
- */
207
- export const UnknownFailureOnVerify = meta.story({
208
- parameters: {
209
- msw: {
210
- handlers: {
211
- verify: handlers.verify.unknownFailure(),
212
- },
213
- },
214
- },
215
- });
216
-
217
- /**
218
- * The proactive user-session check returns a user account.
219
- *
220
- * The user can continue to use the app, or log out.
221
- */
222
- export const AlreadyLoggedIn = meta.story({
223
- parameters: {
224
- msw: {
225
- handlers: {
226
- currentUser: handlers.getCurrentUser.success(data.john),
227
- },
228
- },
229
- },
230
- });
231
-
232
- /**
233
- * The proactive user session check has failed due to connection issues.
234
- */
235
- export const NoConnection = meta.story({
236
- parameters: {
237
- msw: {
238
- handlers: {
239
- currentUser: handlers.getCurrentUser.noConnection(),
240
- },
241
- },
242
- },
243
- });
244
-
245
- /**
246
- * The proactive user session check has failed due to an error that doesn't
247
- * carry any special meaning.
248
- */
249
- export const UnknownFailure = meta.story({
250
- parameters: {
251
- msw: {
252
- handlers: {
253
- currentUser: handlers.getCurrentUser.unknownFailure(),
254
- },
255
- },
256
- },
257
- });
@@ -1,301 +0,0 @@
1
- import { Form, useStoreState } from "@ariakit/react";
2
- import { type Dispatch, type SetStateAction, useState } from "react";
3
- import { Link } from "wouter";
4
- import { useAppConfig, useClient } from "../AppConfig/AppConfig.tsx";
5
- import { cx } from "../class-names.ts";
6
- import { interactiveText } from "../common.css.ts";
7
- import { getSubmitFailureMessage } from "../failureMessages.ts";
8
- import {
9
- Letterhead,
10
- LetterheadHeading,
11
- LetterheadParagraph,
12
- LetterheadSubmitButton,
13
- } from "../Letterhead/index.tsx";
14
- import { button } from "../Letterhead/style.css.ts";
15
- import {
16
- InputsStack,
17
- LetterheadCheckboxField,
18
- LetterheadFormActions,
19
- LetterheadHeader,
20
- LetterheadSubmitError,
21
- LetterheadTextField,
22
- } from "../LetterheadForm/index.tsx";
23
- import { useForm } from "../use-form.ts";
24
- import { validEmail, validPassword } from "../validations.ts";
25
- import { AlreadyLoggedInView } from "./AlreadyLoggedInView.tsx";
26
- import { FailureFallbackView } from "./FailureFallbackView.tsx";
27
- import { LoadingView } from "./LoadingView.tsx";
28
- import { NoConnectionView } from "./NoConnectionView.tsx";
29
- import type { DefaultFormValues } from "./types.ts";
30
- import { useFetchCurrentUser } from "./useFetchCurrentUser.tsx";
31
- import { useRedirectPath } from "./useRedirectPath.ts";
32
-
33
- type SetStep = Dispatch<SetStateAction<JoinStep>>;
34
-
35
- type InitialStepProps = {
36
- setStep: SetStep;
37
- defaultValues?: DefaultFormValues;
38
- };
39
-
40
- function InitialStep(props: InitialStepProps) {
41
- const { client, placeholders, hrefs } = useAppConfig();
42
- const { defaultValues, setStep } = props;
43
-
44
- const { form, submitName } = useForm({
45
- defaultValues: {
46
- email: defaultValues?.email ?? "",
47
- password: "",
48
- acceptedTos: false,
49
- subscribedToNewsletter: false,
50
- },
51
- validate: {
52
- email: validEmail,
53
- password: validPassword,
54
- },
55
- async onSubmit({ values }) {
56
- const op = await client.join(values);
57
-
58
- return op.mapFailure((failure) => {
59
- return getSubmitFailureMessage(failure, {
60
- 409: "🙀 This email address is already in use. Please choose a different one, or log in.",
61
- });
62
- });
63
- },
64
- onSuccess(response, form) {
65
- setStep({
66
- type: "SUBMIT_CODE",
67
- tokenId: response.tokenId,
68
- email: form.values.email,
69
- });
70
- },
71
- });
72
-
73
- const emailValue = useStoreState(form, (s) => s.values.email);
74
-
75
- return (
76
- <Letterhead>
77
- <LetterheadHeader>
78
- <LetterheadHeading>Join Indie Tabletop Club</LetterheadHeading>
79
- <LetterheadParagraph>
80
- Enter your email and choose a strong password. We will send you a
81
- one-time code to verify your account.
82
- </LetterheadParagraph>
83
- </LetterheadHeader>
84
-
85
- <Form store={form} resetOnSubmit={false}>
86
- <InputsStack>
87
- <LetterheadTextField
88
- name={form.names.email}
89
- label="Email"
90
- type="email"
91
- placeholder={placeholders.email}
92
- autoComplete="username"
93
- required
94
- />
95
-
96
- <LetterheadTextField
97
- name={form.names.password}
98
- label="Password"
99
- type="password"
100
- placeholder="Choose a strong password"
101
- hint="Must be at least 8 characters"
102
- autoComplete="new-password"
103
- required
104
- />
105
-
106
- <LetterheadCheckboxField
107
- name={form.names.subscribedToNewsletter}
108
- label={
109
- <>
110
- Subscribe to <em>The Changelog</em>, an at-most-once-a-month
111
- newsletter about updates and new releases. You can unsubscribe
112
- any time with one click.
113
- </>
114
- }
115
- />
116
-
117
- <LetterheadCheckboxField
118
- name={form.names.acceptedTos}
119
- required
120
- label={
121
- <>
122
- I accept the{" "}
123
- <a
124
- target="_blank"
125
- rel="noreferrer noopener"
126
- href={hrefs.terms({ content: "join" })}
127
- className={interactiveText}
128
- >
129
- Terms of Service
130
- </a>
131
- .
132
- </>
133
- }
134
- />
135
- </InputsStack>
136
-
137
- <LetterheadFormActions>
138
- <LetterheadSubmitError name={submitName} />
139
- <LetterheadSubmitButton>Join</LetterheadSubmitButton>
140
-
141
- <LetterheadParagraph align="start">
142
- {"Have an existing account? "}
143
- <Link
144
- {...cx(interactiveText)}
145
- href={hrefs.login()}
146
- state={{ emailValue }}
147
- >
148
- Log in
149
- </Link>
150
- {"."}
151
- </LetterheadParagraph>
152
- </LetterheadFormActions>
153
- </Form>
154
- </Letterhead>
155
- );
156
- }
157
-
158
- type SubmitCodeStepProps = {
159
- tokenId: string;
160
- setStep: SetStep;
161
- email: string;
162
- };
163
-
164
- function SubmitCodeStep(props: SubmitCodeStepProps) {
165
- const { tokenId, email, setStep } = props;
166
- const client = useClient();
167
-
168
- const { form, submitName } = useForm({
169
- defaultValues: {
170
- code: "",
171
- },
172
- async onSubmit({ values }) {
173
- const op = await client.verifyUser({ ...values, tokenId });
174
-
175
- return op.mapFailure((failure) => {
176
- return getSubmitFailureMessage(failure, {
177
- 404: "🚫 This code is incorrect or expired. Please try again.",
178
- });
179
- });
180
- },
181
- onSuccess() {
182
- setStep({ type: "SUCCESS" });
183
- },
184
- });
185
-
186
- return (
187
- <Form store={form} resetOnSubmit={false}>
188
- <Letterhead>
189
- <LetterheadHeader>
190
- <LetterheadHeading>Verify account</LetterheadHeading>
191
- <LetterheadParagraph>
192
- We've sent a one-time code to <em>{email}</em>. Please, enter the
193
- code in the field below to verify your account.
194
- </LetterheadParagraph>
195
- </LetterheadHeader>
196
-
197
- <LetterheadTextField
198
- name={form.names.code}
199
- label="Code"
200
- placeholder="E.g. 123123"
201
- autoComplete="one-time-code"
202
- required
203
- />
204
-
205
- <LetterheadFormActions>
206
- <LetterheadSubmitError name={submitName} />
207
- <LetterheadSubmitButton>Verify</LetterheadSubmitButton>
208
- </LetterheadFormActions>
209
- </Letterhead>
210
- </Form>
211
- );
212
- }
213
-
214
- function SuccessStep() {
215
- const { hrefs } = useAppConfig();
216
- const redirectPath = useRedirectPath();
217
-
218
- return (
219
- <Letterhead>
220
- <LetterheadHeader>
221
- <LetterheadHeading>Success!</LetterheadHeading>
222
- <LetterheadParagraph>
223
- Your Indie Tabletop Club account has been verified, yay!
224
- </LetterheadParagraph>
225
- </LetterheadHeader>
226
-
227
- <Link href={redirectPath ?? hrefs.dashboard()} {...cx(button())}>
228
- {redirectPath ? "Continue" : "Go to dashboard"}
229
- </Link>
230
- </Letterhead>
231
- );
232
- }
233
-
234
- type JoinStep =
235
- | { type: "INITIAL" }
236
- | { type: "SUBMIT_CODE"; tokenId: string; email: string }
237
- | { type: "SUCCESS" };
238
-
239
- type JoinFlowProps = {
240
- defaultValues?: DefaultFormValues;
241
- };
242
-
243
- function JoinFlow(props: JoinFlowProps) {
244
- const [step, setStep] = useState<JoinStep>({ type: "INITIAL" });
245
-
246
- switch (step.type) {
247
- case "INITIAL": {
248
- return <InitialStep {...props} setStep={setStep} />;
249
- }
250
-
251
- case "SUBMIT_CODE": {
252
- return <SubmitCodeStep {...step} setStep={setStep} />;
253
- }
254
-
255
- case "SUCCESS": {
256
- return <SuccessStep />;
257
- }
258
- }
259
- }
260
-
261
- export type JoinCardProps = {
262
- defaultValues?: DefaultFormValues;
263
- };
264
-
265
- /**
266
- * Allows the user to join Indie Tabletop Club.
267
- *
268
- * Will automatically use the `redirectTo` query param value as the redirect
269
- * location once the user creates and verifies their account.
270
- *
271
- * Otherwise a success screen will be shown and the user will be directed to
272
- * the dashboard.
273
- */
274
- export function JoinCard(props: JoinCardProps) {
275
- const { result, latestAttemptTs, reload } = useFetchCurrentUser();
276
-
277
- return result.unpack(
278
- (currentUser) => {
279
- return <AlreadyLoggedInView currentUser={currentUser} reload={reload} />;
280
- },
281
- (failure) => {
282
- if (failure.type === "API_ERROR" && failure.code === 401) {
283
- return <JoinFlow {...props} />;
284
- }
285
-
286
- if (failure.type === "NETWORK_ERROR") {
287
- return (
288
- <NoConnectionView
289
- latestAttemptTs={latestAttemptTs}
290
- onRetry={() => reload()}
291
- />
292
- );
293
- }
294
-
295
- return <FailureFallbackView />;
296
- },
297
- () => {
298
- return <LoadingView />;
299
- },
300
- );
301
- }