@indietabletop/appkit 5.2.1 → 5.3.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/EnumMapper.ts +50 -0
- package/lib/client.ts +63 -5
- package/lib/index.ts +2 -0
- package/lib/structs.ts +3 -27
- package/lib/useInvokeClient.ts +54 -0
- package/package.json +2 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type InputValues<K extends string | number, V> = Record<K, V> | Array<[K, V]>;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles mapping enums to arbitrary values.
|
|
5
|
+
*/
|
|
6
|
+
export class EnumMapper<K extends string | number, V> {
|
|
7
|
+
private map: Map<K, V>;
|
|
8
|
+
private fallback: V;
|
|
9
|
+
|
|
10
|
+
constructor(values: InputValues<K, V>, fallback: V) {
|
|
11
|
+
const entries = Array.isArray(values)
|
|
12
|
+
? values
|
|
13
|
+
: (Object.entries(values) as Array<[K, V]>);
|
|
14
|
+
this.map = new Map(entries);
|
|
15
|
+
this.fallback = fallback;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns a value corresponding to the provided key.
|
|
20
|
+
*
|
|
21
|
+
* If no match is found, returns the fallback value provided in the constructor.
|
|
22
|
+
*
|
|
23
|
+
* Note that TypeScript will make sure that we only ever use the right types
|
|
24
|
+
* as the enum param, but it can still happen at runtime that different
|
|
25
|
+
* values are provided.
|
|
26
|
+
*/
|
|
27
|
+
get(key: K | undefined): V {
|
|
28
|
+
if (key === undefined) {
|
|
29
|
+
return this.fallback;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const value = this.map.get(key);
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
return this.fallback;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* All enums known by this mapper.
|
|
42
|
+
*/
|
|
43
|
+
get enums() {
|
|
44
|
+
return Array.from(this.map.keys());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static from<K extends string>(values: InputValues<K, string>) {
|
|
48
|
+
return new EnumMapper(values, "Unknown");
|
|
49
|
+
}
|
|
50
|
+
}
|
package/lib/client.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
currentUser,
|
|
3
|
+
featureUnlock,
|
|
4
|
+
ownedProduct,
|
|
5
|
+
redeemedPledge,
|
|
6
|
+
sessionInfo,
|
|
7
|
+
type FeatureUnlock,
|
|
8
|
+
type SpaceGits,
|
|
9
|
+
} from "@indietabletop/types";
|
|
10
|
+
import {
|
|
11
|
+
array,
|
|
12
|
+
mask,
|
|
13
|
+
object,
|
|
14
|
+
string,
|
|
15
|
+
Struct,
|
|
16
|
+
unknown,
|
|
17
|
+
type Infer,
|
|
18
|
+
} from "superstruct";
|
|
19
|
+
import { Failure, Success } from "./async-op.ts";
|
|
20
|
+
import type { CurrentUser, FailurePayload, SessionInfo } from "./types.ts";
|
|
6
21
|
|
|
7
22
|
export type UserGameData = {
|
|
8
23
|
spacegits?: {
|
|
@@ -571,4 +586,47 @@ export class IndieTabletopClient {
|
|
|
571
586
|
},
|
|
572
587
|
);
|
|
573
588
|
}
|
|
589
|
+
|
|
590
|
+
async getUnlocks<T extends FeatureUnlock["gameCode"]>(gameCode: T) {
|
|
591
|
+
return this.fetch(
|
|
592
|
+
`/v1/me/unlocks/${gameCode}`,
|
|
593
|
+
object({
|
|
594
|
+
unlocks: array(
|
|
595
|
+
featureUnlock() as unknown as Struct<
|
|
596
|
+
Extract<FeatureUnlock, { gameCode: T }>,
|
|
597
|
+
null
|
|
598
|
+
>,
|
|
599
|
+
),
|
|
600
|
+
}),
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async getOwnedProduct(productCode: string) {
|
|
605
|
+
return await this.fetch(`/v1/me/products/${productCode}`, ownedProduct());
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async startCheckoutSession(payload: {
|
|
609
|
+
products: { code: string; quantity: number }[];
|
|
610
|
+
successUrl: string;
|
|
611
|
+
cancelUrl: string;
|
|
612
|
+
}) {
|
|
613
|
+
return await this.fetch(
|
|
614
|
+
`/v1/stripe/sessions`,
|
|
615
|
+
object({ redirectTo: string() }),
|
|
616
|
+
{
|
|
617
|
+
method: "POST",
|
|
618
|
+
json: payload,
|
|
619
|
+
},
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async getCheckoutSession(sessionId: string) {
|
|
624
|
+
return await this.fetch(
|
|
625
|
+
`/v1/stripe/sessions/${sessionId}`,
|
|
626
|
+
object({
|
|
627
|
+
status: string(),
|
|
628
|
+
products: array(ownedProduct()),
|
|
629
|
+
}),
|
|
630
|
+
);
|
|
631
|
+
}
|
|
574
632
|
}
|
package/lib/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ export * from "./use-media-query.ts";
|
|
|
35
35
|
export * from "./use-reverting-state.ts";
|
|
36
36
|
export * from "./use-scroll-restoration.ts";
|
|
37
37
|
export * from "./useEnsureValue.ts";
|
|
38
|
+
export * from "./useInvokeClient.ts";
|
|
38
39
|
export * from "./useIsVisible.ts";
|
|
39
40
|
|
|
40
41
|
// Utils
|
|
@@ -64,5 +65,6 @@ export * from "./utm.ts";
|
|
|
64
65
|
export * from "./validations.ts";
|
|
65
66
|
|
|
66
67
|
// Other
|
|
68
|
+
export * from "./EnumMapper.ts";
|
|
67
69
|
export * from "./ModernIDB/index.ts";
|
|
68
70
|
export * from "./store/index.tsx";
|
package/lib/structs.ts
CHANGED
|
@@ -1,27 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
return object({
|
|
5
|
-
id: string(),
|
|
6
|
-
email: string(),
|
|
7
|
-
isVerified: boolean(),
|
|
8
|
-
prefersScrollbarVisibility: optional(enums(["ALWAYS"])),
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function sessionInfo() {
|
|
13
|
-
return object({
|
|
14
|
-
expiresTs: number(),
|
|
15
|
-
createdTs: number(),
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function redeemedPledge() {
|
|
20
|
-
return object({
|
|
21
|
-
id: string(),
|
|
22
|
-
downloadUrl: string(),
|
|
23
|
-
downloadUrlExpiresTs: number(),
|
|
24
|
-
contactSubscribed: boolean(),
|
|
25
|
-
email: string(),
|
|
26
|
-
});
|
|
27
|
-
}
|
|
1
|
+
// Structs are exported from here for backwards compat. Generally, types
|
|
2
|
+
// should be used directly from the types package.
|
|
3
|
+
export { currentUser, redeemedPledge, sessionInfo } from "@indietabletop/types";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import type { SWRConfiguration, SWRResponse } from "swr";
|
|
3
|
+
import useSWR from "swr";
|
|
4
|
+
import type { Pending } from "./async-op.ts";
|
|
5
|
+
import type { IndieTabletopClient } from "./client.ts";
|
|
6
|
+
import { swrResponseToResult } from "./result/swr.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a `useInvokeClient` hook that makes it ergonomic to invoke
|
|
10
|
+
* client methods.
|
|
11
|
+
*
|
|
12
|
+
* Note that only methods starting with `get` are picked up. This hook is
|
|
13
|
+
* intended for making easy fetches, not for form submissions.
|
|
14
|
+
*/
|
|
15
|
+
export function createUseInvokeClient<C extends IndieTabletopClient>(c: C) {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
type Fn = (...args: any) => any;
|
|
18
|
+
|
|
19
|
+
return function useInvokeClient<K extends keyof C & `get${string}`>(
|
|
20
|
+
/**
|
|
21
|
+
* ITC Client's method name.
|
|
22
|
+
*/
|
|
23
|
+
method: K,
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Method's arguments.
|
|
27
|
+
*
|
|
28
|
+
* **IMPORTANT**: Must be an array or primitive values as it is directly
|
|
29
|
+
* used as a `useCallback` dependecy array.
|
|
30
|
+
*/
|
|
31
|
+
args: C[K] extends Fn ? Parameters<C[K]> : never,
|
|
32
|
+
|
|
33
|
+
options?: {
|
|
34
|
+
performFetch?: boolean;
|
|
35
|
+
swr?: SWRConfiguration;
|
|
36
|
+
},
|
|
37
|
+
): C[K] extends Fn
|
|
38
|
+
? [Awaited<ReturnType<C[K]>> | Pending, SWRResponse<unknown, unknown>]
|
|
39
|
+
: never {
|
|
40
|
+
const { performFetch = true } = options ?? {};
|
|
41
|
+
|
|
42
|
+
const callback = useCallback(() => {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
44
|
+
return (c[method] as Fn)(...args);
|
|
45
|
+
|
|
46
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
47
|
+
}, [method, ...args]);
|
|
48
|
+
|
|
49
|
+
const cacheKey = [method, ...args].join(":");
|
|
50
|
+
const swr = useSWR(performFetch ? cacheKey : null, callback, options?.swr);
|
|
51
|
+
|
|
52
|
+
return [swrResponseToResult(swr), swr] as never;
|
|
53
|
+
};
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indietabletop/appkit",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "A collection of modules used in apps built by Indie Tabletop Club",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@ariakit/react": "^0.4.17",
|
|
46
46
|
"@indietabletop/tooling": "^5.0.0",
|
|
47
|
-
"@indietabletop/types": "^1.
|
|
47
|
+
"@indietabletop/types": "^1.1.0",
|
|
48
48
|
"@vanilla-extract/css": "^1.17.2",
|
|
49
49
|
"@vanilla-extract/dynamic": "^2.1.3",
|
|
50
50
|
"@vanilla-extract/recipes": "^0.5.7",
|