@indietabletop/appkit 7.0.0-rc.5 → 7.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.
- package/lib/AppConfig/formatters.tsx +4 -2
- package/lib/RichText/style.css.ts +0 -1
- package/lib/Sync/SyncIcon.tsx +22 -0
- package/lib/account/AccountIcon.tsx +5 -0
- package/lib/async-op.ts +5 -0
- package/lib/createStrictContext.ts +12 -0
- package/lib/fathom.ts +5 -1
- package/lib/store/index.tsx +5 -1
- package/lib/useFetchJson.tsx +9 -0
- package/lib/useGetProductStatus.ts +36 -4
- package/package.json +1 -1
|
@@ -3,12 +3,14 @@ type EnglishCardinalRule = Extract<Intl.LDMLPluralRule, "one" | "other">;
|
|
|
3
3
|
|
|
4
4
|
export type AppkitFormatters = ReturnType<typeof createFormatters>;
|
|
5
5
|
|
|
6
|
-
// Currently, we only support localizing English variants.
|
|
7
|
-
// app into other locales.
|
|
6
|
+
// Currently, we only support localizing English variants.
|
|
8
7
|
type EnglishLocale = "en" | `en-${string}`;
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Creates formatters that can be used across the app.
|
|
11
|
+
*
|
|
12
|
+
* These can be either used directly when imported, or via the `useAppConfig`
|
|
13
|
+
* hook when using inside of reusable components like e.g. the `SyncLog`.
|
|
12
14
|
*/
|
|
13
15
|
export function createFormatters(locale: EnglishLocale) {
|
|
14
16
|
const cardinalRules = new Intl.PluralRules(locale);
|
package/lib/Sync/SyncIcon.tsx
CHANGED
|
@@ -79,6 +79,28 @@ function Icon(props: { variant: SyncState }) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Given {@link SyncState}, will render the appropriate matching icon.
|
|
84
|
+
*
|
|
85
|
+
* You can use {@link syncTheme} to configure its colors. Generally, you would
|
|
86
|
+
* do that at the top level in a Vanilla Extract (.css.ts) file
|
|
87
|
+
* using `assignVars`.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* globalStyle(":root", {
|
|
92
|
+
* vars: assignVars(syncTheme, {
|
|
93
|
+
* idle: vars.color.blue,
|
|
94
|
+
* inactive: vars.color.gray,
|
|
95
|
+
* syncing: vars.color.blue,
|
|
96
|
+
* error: vars.color.red,
|
|
97
|
+
* }),
|
|
98
|
+
* )];
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* It is also possible to use inline styles to set the vars if it's necessary.
|
|
102
|
+
*/
|
|
103
|
+
|
|
82
104
|
export function SyncIcon(props: {
|
|
83
105
|
status: SyncState;
|
|
84
106
|
title?: string;
|
|
@@ -2,6 +2,11 @@ import { useSyncState } from "../store/index.tsx";
|
|
|
2
2
|
import { SyncIcon } from "../Sync/SyncIcon.tsx";
|
|
3
3
|
import { accountIcon } from "./style.css.ts";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Displays the user's icon, along with the sync status of the app.
|
|
7
|
+
*
|
|
8
|
+
* See {@link SyncIcon} to learn how to configure that component.
|
|
9
|
+
*/
|
|
5
10
|
export function AccountIcon(props: { imgSrc: string }) {
|
|
6
11
|
const { imgSrc } = props;
|
|
7
12
|
const syncState = useSyncState();
|
package/lib/async-op.ts
CHANGED
|
@@ -287,6 +287,11 @@ export function fromTryCatch<T>(callback: () => T) {
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Creates a Result from a promise-returning callback function.
|
|
292
|
+
*
|
|
293
|
+
* This is a great way to avoid try/catch statements inside of async functions.
|
|
294
|
+
*/
|
|
290
295
|
export async function fromPromise<T>(
|
|
291
296
|
callback: () => Promise<T>,
|
|
292
297
|
): Promise<Success<T> | Failure<unknown>> {
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { createContext, use } from "react";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Creates context values along with a pre-bound use(Context) hook that throws
|
|
5
|
+
* if context value is falsy.
|
|
6
|
+
*
|
|
7
|
+
* @param [debugName="Value"] Optionally, provide a debug name for this context
|
|
8
|
+
* value to be used within error messages if context value is nullish.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* export const [KnightContext, useKnight] = createStrictContext<Knight>("KnightContext");
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
3
15
|
export function createStrictContext<T>(debugName: string = "Value") {
|
|
4
16
|
const Context = createContext<T | null>(null);
|
|
5
17
|
|
package/lib/fathom.ts
CHANGED
|
@@ -4,12 +4,16 @@ declare global {
|
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Sends an arbitrary event to Fathom Analytics. Will log a warning if Fathom
|
|
9
|
+
* is not installed.
|
|
10
|
+
*/
|
|
7
11
|
export function trackEvent(event: string) {
|
|
8
12
|
if (window.fathom) {
|
|
9
13
|
window.fathom.trackEvent(event);
|
|
10
14
|
} else {
|
|
11
15
|
console.warn(
|
|
12
|
-
`Attempting to track event ${event}, but Fathom not installed.`,
|
|
16
|
+
`Attempting to track event ${event}, but Fathom is not installed.`,
|
|
13
17
|
);
|
|
14
18
|
}
|
|
15
19
|
}
|
package/lib/store/index.tsx
CHANGED
|
@@ -61,7 +61,11 @@ export function useLastSuccessfulSyncTs() {
|
|
|
61
61
|
export type SyncState = ReturnType<typeof useSyncState>;
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* Returns the app's current
|
|
64
|
+
* Returns the app's current {@link SyncState}.
|
|
65
|
+
*
|
|
66
|
+
* Note that this is not a property within the app's machine context, it is a
|
|
67
|
+
* derived value based on several factors.
|
|
68
|
+
*
|
|
65
69
|
*/
|
|
66
70
|
export function useSyncState() {
|
|
67
71
|
const { actorRef } = useAppMachineContext();
|
package/lib/useFetchJson.tsx
CHANGED
|
@@ -37,6 +37,10 @@ export async function fetchJson<T>(url: string): Promise<FetchJsonResult<T>> {
|
|
|
37
37
|
return parsed;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Fetches a JSON document at provided URL. Note that the return type can be
|
|
42
|
+
* explicitly provided, but it is not validated at runtime — beware!
|
|
43
|
+
*/
|
|
40
44
|
export function useFetchJson<T = unknown>(
|
|
41
45
|
url: string,
|
|
42
46
|
config?: SWRConfiguration,
|
|
@@ -44,6 +48,11 @@ export function useFetchJson<T = unknown>(
|
|
|
44
48
|
return useSWR<FetchJsonResult<T>>(url, fetchJson, config);
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Fetches a JSON document at provided URL. Response will be wrapped in a
|
|
53
|
+
* Result. Note that the return type can be explicitly provided, but it is
|
|
54
|
+
* not validated at runtime — beware!
|
|
55
|
+
*/
|
|
47
56
|
export function useFetchJsonResult<T = unknown>(
|
|
48
57
|
url: string,
|
|
49
58
|
config?: SWRConfiguration,
|
|
@@ -4,6 +4,7 @@ import { useClient } from "./AppConfig/AppConfig.tsx";
|
|
|
4
4
|
import { Failure, Success } from "./async-op.ts";
|
|
5
5
|
import { swrResponseToResult } from "./result/swr.ts";
|
|
6
6
|
import { useCurrentUser } from "./store/index.tsx";
|
|
7
|
+
import type { FailurePayload } from "./types.ts";
|
|
7
8
|
|
|
8
9
|
function useGetOwnedProduct(productCode: string) {
|
|
9
10
|
const client = useClient();
|
|
@@ -18,6 +19,28 @@ function useGetOwnedProduct(productCode: string) {
|
|
|
18
19
|
return swrResponseToResult(swr);
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
export type ProductStatusSuccessPayload =
|
|
23
|
+
/**
|
|
24
|
+
* The user should authenticate before proceeding to checkout.
|
|
25
|
+
*/
|
|
26
|
+
| { type: "AUTHENTICATE" }
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The user can proceed to checkout.
|
|
30
|
+
*/
|
|
31
|
+
| { type: "PURCHASE" }
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The user already owns this product. The `downloadUrl` property should be
|
|
35
|
+
* used to let the user download their files.
|
|
36
|
+
*/
|
|
37
|
+
| { type: "DOWNLOAD"; downloadUrl: string };
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Wraps ITC client's getOwnedProduct call and maps it's responses into
|
|
41
|
+
* {@link ProductStatusSuccessPayload} which is more appropriate for product
|
|
42
|
+
* purchase pages.
|
|
43
|
+
*/
|
|
21
44
|
export function useGetProductStatus(productCode: string) {
|
|
22
45
|
const result = useGetOwnedProduct(productCode);
|
|
23
46
|
const currentUser = useCurrentUser();
|
|
@@ -33,11 +56,15 @@ export function useGetProductStatus(productCode: string) {
|
|
|
33
56
|
// The no currentUser case is possible in case the user is logged into ITC
|
|
34
57
|
// via a domain-wide cookie, but they have not yet logged in this app.
|
|
35
58
|
if (failure.code === 401 || !currentUser) {
|
|
36
|
-
return new Success({
|
|
59
|
+
return new Success<ProductStatusSuccessPayload>({
|
|
60
|
+
type: "AUTHENTICATE",
|
|
61
|
+
});
|
|
37
62
|
}
|
|
38
63
|
|
|
39
64
|
if (failure.code === 403) {
|
|
40
|
-
return new Success({
|
|
65
|
+
return new Success<ProductStatusSuccessPayload>({
|
|
66
|
+
type: "PURCHASE",
|
|
67
|
+
});
|
|
41
68
|
}
|
|
42
69
|
}
|
|
43
70
|
|
|
@@ -49,8 +76,13 @@ export function useGetProductStatus(productCode: string) {
|
|
|
49
76
|
// if a product link fails to generate.
|
|
50
77
|
const { downloadUrl } = result.value;
|
|
51
78
|
if (!downloadUrl) {
|
|
52
|
-
return new Failure({
|
|
79
|
+
return new Failure<FailurePayload>({
|
|
80
|
+
type: "UNKNOWN_ERROR",
|
|
81
|
+
});
|
|
53
82
|
}
|
|
54
83
|
|
|
55
|
-
return new Success({
|
|
84
|
+
return new Success<ProductStatusSuccessPayload>({
|
|
85
|
+
type: "DOWNLOAD",
|
|
86
|
+
downloadUrl,
|
|
87
|
+
});
|
|
56
88
|
}
|