@indietabletop/appkit 4.0.0-2 → 4.1.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.
@@ -5,6 +5,7 @@ import { ModernIDBError } from "./ModernIDBError.ts";
5
5
  import { ObjectStore } from "./ObjectStore.ts";
6
6
  import type {
7
7
  BlockingHandler,
8
+ ModernIDBIndexes,
8
9
  ModernIDBSchema,
9
10
  ModernIDBState,
10
11
  OpenRequestHandlers,
@@ -36,7 +37,7 @@ type DatabaseProps = {
36
37
 
37
38
  export class ModernIDB<
38
39
  Schema extends ModernIDBSchema,
39
- IndexNames extends { [K in keyof Schema]?: string },
40
+ IndexNames extends ModernIDBIndexes<Schema>,
40
41
  > {
41
42
  readonly name: string;
42
43
  readonly version: number;
@@ -110,7 +111,6 @@ export class ModernIDB<
110
111
  if (this.state !== "closed") {
111
112
  throw new ModernIDBError(
112
113
  "InvalidConnectionStateError",
113
-
114
114
  `Cannot open connection to database '${this.name}'. Instance must ` +
115
115
  `be in a 'closed' state in order to be opened, but current ` +
116
116
  `state is '${this.state}'.`,
@@ -13,6 +13,10 @@ export type ModernIDBSchema = {
13
13
  [key: string]: unknown;
14
14
  };
15
15
 
16
+ export type ModernIDBIndexes<Schema extends ModernIDBSchema> = {
17
+ [K in keyof Schema]?: string;
18
+ };
19
+
16
20
  export type VersionChangeHandler<
17
21
  Schema extends ModernIDBSchema,
18
22
  IndexNames extends {
@@ -4,12 +4,6 @@ import { LetterheadParagraph } from "../Letterhead/index.tsx";
4
4
  import { sleep } from "../sleep.ts";
5
5
  import { SubscribeByEmailCard } from "./SubscribeByEmailCard.tsx";
6
6
 
7
- const pledge = {
8
- id: "1",
9
- email: "test@example.com",
10
- contactSubscribed: false,
11
- };
12
-
13
7
  const handlers = {
14
8
  subscribe() {
15
9
  return http.post(
@@ -6,13 +6,11 @@ import {
6
6
  LetterheadHeading,
7
7
  LetterheadParagraph,
8
8
  } from "../Letterhead/index.tsx";
9
- import type { EventHandlerWithReload } from "./types.ts";
9
+ import { useAppActions } from "../store/index.tsx";
10
10
 
11
- export function AccountIssueView(props: {
12
- onLogout: EventHandlerWithReload;
13
- reload: () => void;
14
- }) {
15
- const { onLogout, reload } = props;
11
+ export function AccountIssueView(props: { reload: () => void }) {
12
+ const { reload } = props;
13
+ const { logout } = useAppActions();
16
14
 
17
15
  return (
18
16
  <Letterhead>
@@ -25,7 +23,13 @@ export function AccountIssueView(props: {
25
23
 
26
24
  <LetterheadParagraph>
27
25
  {"You can try "}
28
- <Button {...cx(interactiveText)} onClick={() => onLogout({ reload })}>
26
+ <Button
27
+ {...cx(interactiveText)}
28
+ onClick={async () => {
29
+ await logout();
30
+ reload();
31
+ }}
32
+ >
29
33
  logging out
30
34
  </Button>
31
35
  {" and in again. "}
@@ -7,16 +7,16 @@ import {
7
7
  LetterheadHeading,
8
8
  LetterheadParagraph,
9
9
  } from "../Letterhead/index.tsx";
10
+ import { useAppActions } from "../store/index.tsx";
10
11
  import type { CurrentUser } from "../types.ts";
11
- import type { EventHandlerWithReload } from "./types.ts";
12
12
 
13
13
  export function AlreadyLoggedInView(props: {
14
14
  currentUser: CurrentUser;
15
- onLogout: EventHandlerWithReload;
16
15
  reload: () => void;
17
16
  }) {
17
+ const { currentUser, reload } = props;
18
18
  const { hrefs } = useAppConfig();
19
- const { currentUser, onLogout, reload } = props;
19
+ const { logout } = useAppActions();
20
20
 
21
21
  return (
22
22
  <Letterhead>
@@ -30,10 +30,13 @@ export function AlreadyLoggedInView(props: {
30
30
  <Link className={interactiveText} href={hrefs.dashboard()}>
31
31
  Continue
32
32
  </Link>
33
- {` as current user, or `}
33
+ {` as the current user, or `}
34
34
  <Button
35
35
  className={interactiveText}
36
- onClick={() => onLogout({ reload })}
36
+ onClick={async () => {
37
+ await logout();
38
+ reload();
39
+ }}
37
40
  >
38
41
  log out
39
42
  </Button>
@@ -1,7 +1,6 @@
1
1
  import { Story } from "@storybook/addon-docs/blocks";
2
2
  import type { Meta, StoryObj } from "@storybook/react-vite";
3
3
  import { http, HttpResponse } from "msw";
4
- import { fn } from "storybook/test";
5
4
  import { sleep } from "../sleep.ts";
6
5
  import type { CurrentUser, SessionInfo } from "../types.ts";
7
6
  import { CurrentUserFetcher } from "./CurrentUserFetcher.tsx";
@@ -168,11 +167,6 @@ const meta = {
168
167
  component: CurrentUserFetcher,
169
168
  tags: ["autodocs"],
170
169
  args: {
171
- localUser: null,
172
- onLogout: fn(),
173
- onClearLocalContent: fn(),
174
- onLogin: fn(),
175
- onServerLogout: fn(),
176
170
  children: (
177
171
  <div
178
172
  style={{
@@ -207,9 +201,6 @@ type Story = StoryObj<typeof meta>;
207
201
  * the local user (determined by the user id).
208
202
  */
209
203
  export const Default: Story = {
210
- args: {
211
- localUser: data.john,
212
- },
213
204
  parameters: {
214
205
  msw: {
215
206
  handlers: {
@@ -230,9 +221,6 @@ export const NoLocalUser: Story = {};
230
221
  * error 401. The user should supply their credentials again.
231
222
  */
232
223
  export const SessionExpired: Story = {
233
- args: {
234
- localUser: data.john,
235
- },
236
224
  parameters: {
237
225
  msw: {
238
226
  handlers: {
@@ -254,10 +242,6 @@ export const SessionExpired: Story = {
254
242
  * if not handled correctly.
255
243
  */
256
244
  export const UserMismatch: Story = {
257
- args: {
258
- localUser: data.john,
259
- },
260
-
261
245
  parameters: {
262
246
  msw: {
263
247
  handlers: {
@@ -270,10 +254,6 @@ export const UserMismatch: Story = {
270
254
  /**
271
255
  */
272
256
  export const UserUnverified: Story = {
273
- args: {
274
- localUser: data.vernon,
275
- },
276
-
277
257
  parameters: {
278
258
  msw: {
279
259
  handlers: {
@@ -293,10 +273,6 @@ export const UserUnverified: Story = {
293
273
  * This can happen if users delete their accounts but
294
274
  */
295
275
  export const UserNotFound: Story = {
296
- args: {
297
- localUser: data.john,
298
- },
299
-
300
276
  parameters: {
301
277
  msw: {
302
278
  handlers: {
@@ -311,10 +287,6 @@ export const UserNotFound: Story = {
311
287
  * carry any special meaning.
312
288
  */
313
289
  export const UnknownFailure: Story = {
314
- args: {
315
- localUser: data.john,
316
- },
317
-
318
290
  parameters: {
319
291
  msw: {
320
292
  handlers: {
@@ -1,30 +1,18 @@
1
1
  import { useState, type ReactNode } from "react";
2
2
  import { ModalDialog } from "../ModalDialog/index.tsx";
3
- import type { CurrentUser } from "../types.ts";
3
+ import { useCurrentUser } from "../store/index.tsx";
4
4
  import { AccountIssueView } from "./AccountIssueView.tsx";
5
5
  import { LoginView } from "./LoginView.tsx";
6
- import type { EventHandler, EventHandlerWithReload } from "./types.ts";
7
- import { useCurrentUserResult } from "./useCurrentUserResult.tsx";
6
+ import { useFetchCurrentUser } from "./useFetchCurrentUser.tsx";
8
7
  import { UserMismatchView } from "./UserMismatchView.tsx";
9
8
  import { VerifyAccountView } from "./VerifyPage.tsx";
10
9
 
11
10
  type CurrentUserFetcherProps = {
12
- /**
13
- * Current user as stored in persistent storage.
14
- *
15
- * If this property is set, the component will attempt to fetch latest data
16
- * from the server and store it (via ITC client).
17
- */
18
- localUser: CurrentUser | null;
19
- onLogin: EventHandlerWithReload;
20
- onClearLocalContent: EventHandler;
21
- onLogout: EventHandlerWithReload;
22
- onServerLogout: EventHandlerWithReload;
23
11
  children: ReactNode;
24
12
  };
25
13
 
26
14
  /**
27
- * Fetches fresh current user data if local data is provided.
15
+ * Fetches fresh current user data if a local user exists in the app store.
28
16
  *
29
17
  * This component uses the Indie Tabletop Client under the hood, so if new
30
18
  * data is successfully fetched, the onCurrentUser callback will be invoked,
@@ -34,22 +22,17 @@ type CurrentUserFetcherProps = {
34
22
  * that we could run into: expired session, user mismatch and account deletion.
35
23
  *
36
24
  * All other errors are ignored. This allows users to use the app in offline
37
- * more, and doesn't interrupt their session if some unexpected error happens,
25
+ * mode, and doesn't interrupt their session if some unexpected error happens,
38
26
  * which they cannot do anything about anyways.
39
27
  */
40
28
  export function CurrentUserFetcher(props: CurrentUserFetcherProps) {
41
- const {
42
- localUser,
43
- children,
44
- onLogin,
45
- onLogout,
46
- onClearLocalContent,
47
- onServerLogout,
48
- } = props;
29
+ const { children } = props;
30
+ const localUser = useCurrentUser();
49
31
  const [isOpen, setOpen] = useState(true);
50
32
 
51
- const { result, reload } = useCurrentUserResult({
52
- // We only want to fetch the current user if they exist in local storage
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.
53
36
  performFetch: !!localUser,
54
37
  });
55
38
 
@@ -62,8 +45,7 @@ export function CurrentUserFetcher(props: CurrentUserFetcherProps) {
62
45
  <ModalDialog size="large" open>
63
46
  <LoginView
64
47
  currentUser={localUser}
65
- onLogin={onLogin}
66
- onLogout={onLogout}
48
+ onLogin={() => reload()}
67
49
  description={undefined}
68
50
  reload={reload}
69
51
  />
@@ -80,7 +62,7 @@ export function CurrentUserFetcher(props: CurrentUserFetcherProps) {
80
62
  return (
81
63
  <>
82
64
  <ModalDialog size="large" open>
83
- <AccountIssueView onLogout={onLogout} reload={reload} />
65
+ <AccountIssueView reload={reload} />
84
66
  </ModalDialog>
85
67
 
86
68
  {children}
@@ -101,8 +83,6 @@ export function CurrentUserFetcher(props: CurrentUserFetcherProps) {
101
83
  <UserMismatchView
102
84
  serverUser={result.value}
103
85
  localUser={localUser}
104
- onClearLocalContent={onClearLocalContent}
105
- onServerLogout={onServerLogout}
106
86
  reload={reload}
107
87
  />
108
88
  </ModalDialog>
@@ -119,7 +99,6 @@ export function CurrentUserFetcher(props: CurrentUserFetcherProps) {
119
99
  <VerifyAccountView
120
100
  currentUser={serverUser}
121
101
  onClose={() => setOpen(false)}
122
- onLogout={onLogout}
123
102
  reload={reload}
124
103
  />
125
104
  </ModalDialog>
@@ -1,7 +1,6 @@
1
1
  import { Story } from "@storybook/addon-docs/blocks";
2
2
  import type { Meta, StoryObj } from "@storybook/react-vite";
3
3
  import { http, HttpResponse } from "msw";
4
- import { fn } from "storybook/test";
5
4
  import { sleep } from "../sleep.ts";
6
5
  import type { CurrentUser, SessionInfo } from "../types.ts";
7
6
  import { JoinCard } from "./JoinCard.tsx";
@@ -142,7 +141,6 @@ const meta = {
142
141
  tags: ["autodocs"],
143
142
  args: {
144
143
  defaultValues: {},
145
- onLogout: fn(),
146
144
  },
147
145
  parameters: {
148
146
  msw: {
@@ -26,8 +26,8 @@ import { AlreadyLoggedInView } from "./AlreadyLoggedInView.tsx";
26
26
  import { FailureFallbackView } from "./FailureFallbackView.tsx";
27
27
  import { LoadingView } from "./LoadingView.tsx";
28
28
  import { NoConnectionView } from "./NoConnectionView.tsx";
29
- import type { DefaultFormValues, EventHandlerWithReload } from "./types.ts";
30
- import { useCurrentUserResult } from "./useCurrentUserResult.tsx";
29
+ import type { DefaultFormValues } from "./types.ts";
30
+ import { useFetchCurrentUser } from "./useFetchCurrentUser.tsx";
31
31
 
32
32
  type SetStep = Dispatch<SetStateAction<JoinStep>>;
33
33
 
@@ -248,7 +248,6 @@ function JoinFlow(props: { defaultValues?: DefaultFormValues }) {
248
248
  }
249
249
 
250
250
  export type JoinCardProps = {
251
- onLogout: EventHandlerWithReload;
252
251
  defaultValues?: DefaultFormValues;
253
252
  };
254
253
 
@@ -256,17 +255,11 @@ export type JoinCardProps = {
256
255
  * Allows the user to join Indie Tabletop Club.
257
256
  */
258
257
  export function JoinCard(props: JoinCardProps) {
259
- const { result, latestAttemptTs, reload } = useCurrentUserResult();
258
+ const { result, latestAttemptTs, reload } = useFetchCurrentUser();
260
259
 
261
260
  return result.unpack(
262
261
  (currentUser) => {
263
- return (
264
- <AlreadyLoggedInView
265
- {...props}
266
- currentUser={currentUser}
267
- reload={reload}
268
- />
269
- );
262
+ return <AlreadyLoggedInView currentUser={currentUser} reload={reload} />;
270
263
  },
271
264
  (failure) => {
272
265
  if (failure.type === "API_ERROR" && failure.code === 401) {
@@ -128,13 +128,9 @@ const meta = {
128
128
  component: LoginCard,
129
129
  tags: ["autodocs"],
130
130
  args: {
131
- currentUser: null,
132
131
  description: "Log in to Indie Tabletop Club to enable backup & sync.",
133
132
  defaultValues: {},
134
133
  onLogin: fn(),
135
- onLogout: fn(),
136
- onClearLocalContent: fn(),
137
- onServerLogout: fn(),
138
134
  },
139
135
  parameters: {
140
136
  msw: {
@@ -169,9 +165,6 @@ export const Default: Story = {
169
165
  * attempting to create a new session.
170
166
  */
171
167
  export const InvalidCredentialsOnSubmit: Story = {
172
- args: {
173
- currentUser: null,
174
- },
175
168
  parameters: {
176
169
  msw: {
177
170
  handlers: {
@@ -187,9 +180,6 @@ export const InvalidCredentialsOnSubmit: Story = {
187
180
  * email is used that is not currently in the database.
188
181
  */
189
182
  export const UserNotFoundOnSubmit: Story = {
190
- args: {
191
- currentUser: null,
192
- },
193
183
  parameters: {
194
184
  msw: {
195
185
  handlers: {
@@ -205,9 +195,6 @@ export const UserNotFoundOnSubmit: Story = {
205
195
  * that doesn't have a specific meaning in the context of the login page.
206
196
  */
207
197
  export const UnknownFailureOnSubmit: Story = {
208
- args: {
209
- currentUser: null,
210
- },
211
198
  parameters: {
212
199
  msw: {
213
200
  handlers: {
@@ -222,9 +209,6 @@ export const UnknownFailureOnSubmit: Story = {
222
209
  * A case when user is stored locally, but their server session has expired.
223
210
  */
224
211
  export const ReauthenticateSession: Story = {
225
- args: {
226
- currentUser: data.john,
227
- },
228
212
  parameters: {
229
213
  msw: {
230
214
  handlers: {
@@ -242,9 +226,6 @@ export const ReauthenticateSession: Story = {
242
226
  * The user is directed to app without any further steps.
243
227
  */
244
228
  export const AlreadyLoggedIn: Story = {
245
- args: {
246
- currentUser: data.john,
247
- },
248
229
  parameters: {
249
230
  msw: {
250
231
  handlers: {
@@ -260,10 +241,6 @@ export const AlreadyLoggedIn: Story = {
260
241
  * different credentials.
261
242
  */
262
243
  export const UserMismatch: Story = {
263
- args: {
264
- currentUser: data.john,
265
- },
266
-
267
244
  parameters: {
268
245
  msw: {
269
246
  handlers: {
@@ -1,29 +1,16 @@
1
1
  import { type ReactNode } from "react";
2
- import type { CurrentUser } from "../types.ts";
2
+ import { useCurrentUser } from "../store/index.tsx";
3
3
  import { AccountIssueView } from "./AccountIssueView.tsx";
4
4
  import { AlreadyLoggedInView } from "./AlreadyLoggedInView.tsx";
5
5
  import { FailureFallbackView } from "./FailureFallbackView.tsx";
6
6
  import { LoadingView } from "./LoadingView.tsx";
7
7
  import { LoginView } from "./LoginView.tsx";
8
8
  import { NoConnectionView } from "./NoConnectionView.tsx";
9
- import type {
10
- DefaultFormValues,
11
- EventHandler,
12
- EventHandlerWithReload,
13
- } from "./types.ts";
14
- import { useCurrentUserResult } from "./useCurrentUserResult.tsx";
9
+ import type { AuthEventHandler, DefaultFormValues } from "./types.ts";
10
+ import { useFetchCurrentUser } from "./useFetchCurrentUser.tsx";
15
11
  import { UserMismatchView } from "./UserMismatchView.tsx";
16
12
 
17
13
  export type LoginCardProps = {
18
- /**
19
- * Any user data that might currently be stored in persistent storage like
20
- * `localStorage` or IndexedDB.
21
- *
22
- * If the app contains any local data, it is important that this value is
23
- * provided so that we don't run into strange user mismatch issues!
24
- */
25
- currentUser: CurrentUser | null;
26
-
27
14
  /**
28
15
  * A description that will appear in the default login case (i.e. when an
29
16
  * unauthenticated user is prompted to log in).
@@ -48,53 +35,22 @@ export type LoginCardProps = {
48
35
  *
49
36
  * Typically you want to redirect the user at this point, and possibly
50
37
  * run additional side-effects.
51
- *
52
- * Note that it is not necessary to save the new current user in any way,
53
- * as they will already be saved via the client onCurrentUser handler.
54
- */
55
- onLogin: EventHandler;
56
-
57
- /**
58
- * Called when the user indicates that they would like to log out.
59
- *
60
- * Typically, you might want to clear all local data, perform
61
- * a server logout, and redirect to some sensible new location.
62
- */
63
- onLogout: EventHandlerWithReload;
64
-
65
- /**
66
- * Called when there is a mismatch between local user data and data returned
67
- * from the server and the user chooses to use the server account.
68
- *
69
- * Local content should be cleared in response to this action, but **not**
70
- * server logout (as the user is choosing to continue as the user that is
71
- * currently referenced in the cookie).
72
- */
73
- onClearLocalContent: EventHandler;
74
-
75
- /**
76
- * Called when there is a mismatch between local user data and data returned
77
- * from the server and the user chooses to use the local account.
78
- *
79
- * A server logout should be performed in response to this event, but local
80
- * data should not be touched.
81
38
  */
82
- onServerLogout: EventHandlerWithReload;
39
+ onLogin: AuthEventHandler;
83
40
  };
84
41
 
85
42
  /**
86
43
  * Allows the user to log into Indie Tabletop Club.
87
44
  */
88
45
  export function LoginCard(props: LoginCardProps) {
89
- const { currentUser } = props;
90
- const { result, latestAttemptTs, reload } = useCurrentUserResult();
46
+ const currentUser = useCurrentUser();
47
+ const { result, latestAttemptTs, reload } = useFetchCurrentUser();
91
48
 
92
49
  return result.unpack(
93
50
  (serverUser) => {
94
51
  if (currentUser && currentUser.id !== serverUser.id) {
95
52
  return (
96
53
  <UserMismatchView
97
- {...props}
98
54
  serverUser={serverUser}
99
55
  localUser={currentUser}
100
56
  reload={reload}
@@ -102,23 +58,19 @@ export function LoginCard(props: LoginCardProps) {
102
58
  );
103
59
  }
104
60
 
105
- return (
106
- <AlreadyLoggedInView
107
- {...props}
108
- currentUser={serverUser}
109
- reload={reload}
110
- />
111
- );
61
+ return <AlreadyLoggedInView currentUser={serverUser} reload={reload} />;
112
62
  },
113
63
 
114
64
  (failure) => {
115
65
  if (failure.type === "API_ERROR") {
116
66
  if (failure.code === 401) {
117
- return <LoginView {...props} reload={reload} />;
67
+ return (
68
+ <LoginView {...props} currentUser={currentUser} reload={reload} />
69
+ );
118
70
  }
119
71
 
120
72
  if (failure.code === 404) {
121
- return <AccountIssueView {...props} reload={reload} />;
73
+ return <AccountIssueView reload={reload} />;
122
74
  }
123
75
  }
124
76
 
@@ -17,22 +17,22 @@ import {
17
17
  LetterheadSubmitError,
18
18
  LetterheadTextField,
19
19
  } from "../LetterheadForm/index.tsx";
20
+ import { useAppActions } from "../store/index.tsx";
20
21
  import type { CurrentUser } from "../types.ts";
21
22
  import { useForm } from "../use-form.ts";
22
23
  import { validEmail } from "../validations.ts";
23
- import type { DefaultFormValues, EventHandlerWithReload } from "./types.ts";
24
+ import type { AuthEventHandler, DefaultFormValues } from "./types.ts";
24
25
 
25
26
  export function LoginView(props: {
26
27
  defaultValues?: DefaultFormValues;
27
- onLogin: EventHandlerWithReload;
28
- onLogout: EventHandlerWithReload;
28
+ onLogin: AuthEventHandler;
29
29
  currentUser: CurrentUser | null;
30
30
  description: ReactNode;
31
31
  reload: () => void;
32
32
  }) {
33
+ const { currentUser, defaultValues, description, onLogin, reload } = props;
33
34
  const { placeholders, client, hrefs } = useAppConfig();
34
- const { defaultValues, description, currentUser, onLogin, onLogout, reload } =
35
- props;
35
+ const { logout } = useAppActions();
36
36
  const localUserPresent = !!currentUser?.email;
37
37
  const defaultEmailValue = currentUser?.email ?? defaultValues?.email ?? "";
38
38
 
@@ -50,7 +50,7 @@ export function LoginView(props: {
50
50
  });
51
51
  },
52
52
  onSuccess() {
53
- onLogin({ reload });
53
+ onLogin();
54
54
  },
55
55
  });
56
56
 
@@ -72,7 +72,10 @@ export function LoginView(props: {
72
72
  {"To use a different account, please "}
73
73
  <Button
74
74
  className={interactiveText}
75
- onClick={() => onLogout({ reload })}
75
+ onClick={async () => {
76
+ await logout();
77
+ reload();
78
+ }}
76
79
  >
77
80
  log out
78
81
  </Button>
@@ -6,19 +6,17 @@ import {
6
6
  LetterheadParagraph,
7
7
  } from "../Letterhead/index.tsx";
8
8
  import { LetterheadHeader } from "../LetterheadForm/index.tsx";
9
+ import { useAppActions } from "../store/index.tsx";
9
10
  import type { CurrentUser } from "../types.ts";
10
11
  import { accountPicker } from "./style.css.ts";
11
- import type { EventHandler, EventHandlerWithReload } from "./types.ts";
12
12
 
13
13
  export function UserMismatchView(props: {
14
14
  serverUser: CurrentUser;
15
15
  localUser: CurrentUser;
16
- onClearLocalContent: EventHandler;
17
- onServerLogout: EventHandlerWithReload;
18
16
  reload: () => void;
19
17
  }) {
20
- const { localUser, serverUser, onClearLocalContent, onServerLogout, reload } =
21
- props;
18
+ const { localUser, serverUser, reload } = props;
19
+ const { clientLogout, serverLogout } = useAppActions();
22
20
 
23
21
  return (
24
22
  <Letterhead>
@@ -39,7 +37,7 @@ export function UserMismatchView(props: {
39
37
  <Button
40
38
  {...cx(accountPicker.button)}
41
39
  type="button"
42
- onClick={() => onClearLocalContent()}
40
+ onClick={async () => await clientLogout({ serverUser })}
43
41
  >
44
42
  <div {...cx(accountPicker.buttonLabel)}>{serverUser.email}</div>
45
43
  <div>Local data will be deleted.</div>
@@ -50,7 +48,10 @@ export function UserMismatchView(props: {
50
48
  <Button
51
49
  {...cx(accountPicker.button)}
52
50
  type="button"
53
- onClick={() => onServerLogout({ reload })}
51
+ onClick={async () => {
52
+ await serverLogout();
53
+ reload();
54
+ }}
54
55
  >
55
56
  <div {...cx(accountPicker.buttonLabel)}>{localUser.email}</div>
56
57
  <div>You will be asked to log in again.</div>