@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.
@@ -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. Not translating the
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);
@@ -21,7 +21,6 @@ globalStyle(`${content} > :first-child`, {
21
21
 
22
22
  globalStyle(`${content} em`, {
23
23
  fontFamily: richTextTheme.emphasisFont,
24
- textTransform: "lowercase",
25
24
  });
26
25
 
27
26
  globalStyle(`${content} :is(h1, h2, h3, h4, h5, h6)`, {
@@ -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
  }
@@ -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 sync state.
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();
@@ -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({ type: "AUTHENTICATE" as const });
59
+ return new Success<ProductStatusSuccessPayload>({
60
+ type: "AUTHENTICATE",
61
+ });
37
62
  }
38
63
 
39
64
  if (failure.code === 403) {
40
- return new Success({ type: "PURCHASE" as const });
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({ type: "UNKNOWN_ERROR" as const });
79
+ return new Failure<FailurePayload>({
80
+ type: "UNKNOWN_ERROR",
81
+ });
53
82
  }
54
83
 
55
- return new Success({ type: "DOWNLOAD" as const, downloadUrl });
84
+ return new Success<ProductStatusSuccessPayload>({
85
+ type: "DOWNLOAD",
86
+ downloadUrl,
87
+ });
56
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indietabletop/appkit",
3
- "version": "7.0.0-rc.5",
3
+ "version": "7.0.0",
4
4
  "description": "A collection of modules used in apps built by Indie Tabletop Club",
5
5
  "private": false,
6
6
  "type": "module",