@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.
- package/lib/ModernIDB/ModernIDB.ts +2 -2
- package/lib/ModernIDB/types.ts +4 -0
- package/lib/SubscribeCard/SubscribeByEmailCard.stories.tsx +0 -6
- package/lib/account/AccountIssueView.tsx +11 -7
- package/lib/account/AlreadyLoggedInView.tsx +8 -5
- package/lib/account/CurrentUserFetcher.stories.tsx +0 -28
- package/lib/account/CurrentUserFetcher.tsx +11 -32
- package/lib/account/JoinCard.stories.tsx +0 -2
- package/lib/account/JoinCard.tsx +4 -11
- package/lib/account/LoginCard.stories.tsx +0 -23
- package/lib/account/LoginCard.tsx +11 -59
- package/lib/account/LoginView.tsx +10 -7
- package/lib/account/UserMismatchView.tsx +8 -7
- package/lib/account/VerifyPage.tsx +10 -7
- package/lib/account/types.ts +5 -3
- package/lib/account/{useCurrentUserResult.tsx → useFetchCurrentUser.tsx} +1 -1
- package/lib/async-op.ts +32 -0
- package/lib/client.ts +213 -43
- package/lib/createSafeStorage.ts +91 -0
- package/lib/index.ts +5 -0
- package/lib/store/index.tsx +227 -0
- package/lib/store/store.ts +453 -0
- package/lib/store/types.ts +45 -0
- package/lib/store/utils.ts +46 -0
- package/lib/types/spacegits.ts +108 -0
- package/package.json +6 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Failure, Success } from "../async-op.ts";
|
|
2
|
+
import type { CurrentUser, FailurePayload, SessionInfo } from "../types.ts";
|
|
3
|
+
|
|
4
|
+
export type EmitterActor<Outgoing> = {
|
|
5
|
+
sendBack: (event: Outgoing) => void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type PullResult =
|
|
9
|
+
| Success<{ pulled: SyncedItem[] }>
|
|
10
|
+
| Failure<FailurePayload>;
|
|
11
|
+
|
|
12
|
+
export type PushResult =
|
|
13
|
+
| Success<{ pushed: SyncedItem[]; pulled: SyncedItem[] }>
|
|
14
|
+
| Failure<FailurePayload>;
|
|
15
|
+
|
|
16
|
+
export type SyncedItem = {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
updatedTs: number;
|
|
20
|
+
deleted: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type SyncAttempt = {
|
|
24
|
+
startedTs: number;
|
|
25
|
+
pull: PullResult | null;
|
|
26
|
+
push: PushResult | null;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type MachineContext = {
|
|
30
|
+
currentSync: SyncAttempt | null;
|
|
31
|
+
lastSuccessfulSyncTs: number | null;
|
|
32
|
+
currentUser: CurrentUser | null;
|
|
33
|
+
sessionInfo: SessionInfo | null;
|
|
34
|
+
pushOnceIdle: boolean;
|
|
35
|
+
syncLog: SyncAttempt[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type MachineEvent =
|
|
39
|
+
| { type: "push" }
|
|
40
|
+
| { type: "pull" }
|
|
41
|
+
| { type: "currentUser"; currentUser: CurrentUser }
|
|
42
|
+
| { type: "serverUser"; serverUser: CurrentUser }
|
|
43
|
+
| { type: "sessionInfo"; sessionInfo: SessionInfo }
|
|
44
|
+
| { type: "sessionExpired" }
|
|
45
|
+
| { type: "reset" };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Failure } from "../async-op.js";
|
|
2
|
+
import type { UserGameData } from "../client.ts";
|
|
3
|
+
import type { FailurePayload } from "../types.ts";
|
|
4
|
+
import type { SyncedItem } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Flattens the UserGameData structure to a flat SyncedItem[].
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export function toSyncedItems(data: UserGameData) {
|
|
11
|
+
return Object.values(data).flatMap((groups) => {
|
|
12
|
+
return Object.values(groups).flatMap((items) => {
|
|
13
|
+
return items.map((item): SyncedItem => {
|
|
14
|
+
return {
|
|
15
|
+
id: item.id,
|
|
16
|
+
name: item.name,
|
|
17
|
+
deleted: item.deleted,
|
|
18
|
+
updatedTs: item.updatedTs,
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function assertNonNullish<T>(
|
|
26
|
+
value: T,
|
|
27
|
+
message: string,
|
|
28
|
+
): asserts value is NonNullable<T> {
|
|
29
|
+
if (value == null) {
|
|
30
|
+
throw new Error(message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isFailurePayload(
|
|
35
|
+
error: unknown,
|
|
36
|
+
): error is { cause: Failure<FailurePayload> } {
|
|
37
|
+
return error instanceof Error && error.cause instanceof Failure;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function caughtToResult(error: unknown): Failure<FailurePayload> {
|
|
41
|
+
if (isFailurePayload(error)) {
|
|
42
|
+
return error.cause;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return new Failure<FailurePayload>({ type: "UNKNOWN_ERROR" });
|
|
46
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
array,
|
|
3
|
+
assign,
|
|
4
|
+
enums,
|
|
5
|
+
literal,
|
|
6
|
+
nullable,
|
|
7
|
+
number,
|
|
8
|
+
object,
|
|
9
|
+
omit,
|
|
10
|
+
string,
|
|
11
|
+
type Infer,
|
|
12
|
+
} from "superstruct";
|
|
13
|
+
|
|
14
|
+
export type Ref = Infer<ReturnType<typeof ref>>;
|
|
15
|
+
|
|
16
|
+
export type VehicleWeaponRef = Infer<ReturnType<typeof vehicleWeaponRef>>;
|
|
17
|
+
|
|
18
|
+
export type TreatmentRef = Infer<ReturnType<typeof treatmentRef>>;
|
|
19
|
+
|
|
20
|
+
export type RepairRef = Infer<ReturnType<typeof repairRef>>;
|
|
21
|
+
|
|
22
|
+
export type VehicleData = Infer<ReturnType<typeof vehicleData>>;
|
|
23
|
+
|
|
24
|
+
export type GitData = Infer<ReturnType<typeof gitData>>;
|
|
25
|
+
|
|
26
|
+
export type GangTombstone = Infer<ReturnType<typeof gangTombstone>>;
|
|
27
|
+
|
|
28
|
+
export type GangData = Infer<ReturnType<typeof gangData>>;
|
|
29
|
+
|
|
30
|
+
export type GangSnapshotData = Infer<ReturnType<typeof gangSnapshotData>>;
|
|
31
|
+
|
|
32
|
+
export function ref() {
|
|
33
|
+
return object({ id: string() });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function treatmentRef() {
|
|
37
|
+
return assign(ref(), object({ locationId: string() }));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function vehicleWeaponRef() {
|
|
41
|
+
return assign(
|
|
42
|
+
ref(),
|
|
43
|
+
object({ placement: enums(["FIXED", "PINTLE_MOUNTED"]) }),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function repairRef() {
|
|
48
|
+
return assign(ref(), object({ locationId: string() }));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function gitData() {
|
|
52
|
+
return object({
|
|
53
|
+
id: string(),
|
|
54
|
+
type: ref(),
|
|
55
|
+
name: string(),
|
|
56
|
+
weapons: array(ref()),
|
|
57
|
+
gear: array(ref()),
|
|
58
|
+
offenses: array(ref()),
|
|
59
|
+
treatments: array(treatmentRef()),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function vehicleData() {
|
|
64
|
+
return object({
|
|
65
|
+
id: string(),
|
|
66
|
+
type: ref(),
|
|
67
|
+
name: string(),
|
|
68
|
+
mods: array(ref()),
|
|
69
|
+
repairs: array(repairRef()),
|
|
70
|
+
weapons: array(vehicleWeaponRef()),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function gangTombstone() {
|
|
75
|
+
return object({
|
|
76
|
+
id: string(),
|
|
77
|
+
name: string(),
|
|
78
|
+
updatedTs: number(),
|
|
79
|
+
deleted: literal(true),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function gangData() {
|
|
84
|
+
return object({
|
|
85
|
+
id: string(),
|
|
86
|
+
name: string(),
|
|
87
|
+
description: string(),
|
|
88
|
+
gits: array(gitData()),
|
|
89
|
+
vehicles: array(vehicleData()),
|
|
90
|
+
buildMode: enums([
|
|
91
|
+
"A_LA_CARTE",
|
|
92
|
+
"CHEFS_RECOMMENDATION",
|
|
93
|
+
"CHAOS_MODE",
|
|
94
|
+
"SANDBOX",
|
|
95
|
+
]),
|
|
96
|
+
snapshotId: nullable(string()),
|
|
97
|
+
faction: nullable(ref()),
|
|
98
|
+
stash: number(),
|
|
99
|
+
createdTs: number(),
|
|
100
|
+
updatedTs: number(),
|
|
101
|
+
rulesetVersion: string(),
|
|
102
|
+
deleted: literal(false),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function gangSnapshotData() {
|
|
107
|
+
return omit(gangData(), ["snapshotId", "deleted"]);
|
|
108
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indietabletop/appkit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "A collection of modules used in apps built by Indie Tabletop Club",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"dev": "tsc --watch",
|
|
11
11
|
"test": "vitest",
|
|
12
|
-
"storybook": "storybook dev"
|
|
12
|
+
"storybook": "storybook dev",
|
|
13
|
+
"typecheck": "tsc --noEmit"
|
|
13
14
|
},
|
|
14
15
|
"exports": {
|
|
15
16
|
".": "./lib/index.ts",
|
|
@@ -44,10 +45,12 @@
|
|
|
44
45
|
"@vanilla-extract/dynamic": "^2.1.3",
|
|
45
46
|
"@vanilla-extract/recipes": "^0.5.7",
|
|
46
47
|
"@vanilla-extract/sprinkles": "^1.6.3",
|
|
48
|
+
"@xstate/react": "^6.0.0",
|
|
47
49
|
"nanoid": "^5.1.5",
|
|
48
50
|
"superstruct": "^2.0.2",
|
|
49
51
|
"swr": "^2.3.6",
|
|
50
|
-
"wouter": "^3.7.1"
|
|
52
|
+
"wouter": "^3.7.1",
|
|
53
|
+
"xstate": "^5.23.0"
|
|
51
54
|
},
|
|
52
55
|
"msw": {
|
|
53
56
|
"workerDirectory": [
|