@indietabletop/appkit 7.0.0-0 → 7.0.0-rc.1

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.
Files changed (286) hide show
  1. package/lib/AppConfig/AppConfig.tsx +80 -0
  2. package/lib/AppConfig/formatters.tsx +48 -0
  3. package/lib/AuthCard/AuthCard.stories.ts +34 -0
  4. package/lib/AuthCard/AuthCard.tsx +64 -0
  5. package/lib/AuthCard/style.css.ts +49 -0
  6. package/lib/CacheProvider.tsx +20 -0
  7. package/lib/DialogTrigger/index.tsx +36 -0
  8. package/lib/DocumentTitle/DocumentTitle.tsx +10 -0
  9. package/lib/EnumMapper.ts +50 -0
  10. package/lib/ExternalLink.tsx +10 -0
  11. package/lib/FullscreenDismissBlocker.tsx +23 -0
  12. package/{dist/HistoryState.d.ts → lib/HistoryState.ts} +5 -2
  13. package/lib/IndieTabletopClubLogo.tsx +44 -0
  14. package/lib/IndieTabletopClubSymbol.tsx +37 -0
  15. package/lib/InfoPage/index.tsx +46 -0
  16. package/lib/InfoPage/pages.tsx +36 -0
  17. package/lib/InfoPage/style.css.ts +36 -0
  18. package/lib/Letterhead/index.tsx +85 -0
  19. package/lib/Letterhead/stories.tsx +41 -0
  20. package/lib/Letterhead/style.css.ts +152 -0
  21. package/lib/LetterheadForm/LetterheadReadonlyTextField.stories.tsx +17 -0
  22. package/lib/LetterheadForm/LetterheadSubmitError.stories.tsx +19 -0
  23. package/lib/LetterheadForm/LetterheadTextField.stories.tsx +19 -0
  24. package/lib/LetterheadForm/index.tsx +137 -0
  25. package/lib/LetterheadForm/style.css.ts +89 -0
  26. package/lib/ListView/ListView.stories.tsx +36 -0
  27. package/lib/ListView/ListView.tsx +80 -0
  28. package/lib/ListView/style.css.ts +90 -0
  29. package/lib/LoadingIndicator.tsx +40 -0
  30. package/lib/MiddotSeparated/MiddotSeparated.stories.ts +26 -0
  31. package/lib/MiddotSeparated/MiddotSeparated.tsx +26 -0
  32. package/lib/MiddotSeparated/style.css.ts +10 -0
  33. package/lib/ModalDialog/index.tsx +28 -0
  34. package/lib/ModalDialog/style.css.ts +88 -0
  35. package/lib/ModernIDB/Cursor.ts +91 -0
  36. package/lib/ModernIDB/ModernIDB.ts +337 -0
  37. package/lib/ModernIDB/ModernIDBError.ts +9 -0
  38. package/lib/ModernIDB/ObjectStore.ts +195 -0
  39. package/lib/ModernIDB/ObjectStoreIndex.ts +102 -0
  40. package/lib/ModernIDB/README.md +9 -0
  41. package/lib/ModernIDB/Transaction.ts +40 -0
  42. package/lib/ModernIDB/VersionChangeManager.ts +57 -0
  43. package/lib/ModernIDB/bindings/factory.tsx +165 -0
  44. package/lib/ModernIDB/bindings/index.ts +2 -0
  45. package/{dist/ModernIDB/bindings/types.d.ts → lib/ModernIDB/bindings/types.ts} +32 -13
  46. package/lib/ModernIDB/bindings/utils.tsx +32 -0
  47. package/lib/ModernIDB/index.ts +10 -0
  48. package/lib/ModernIDB/types.ts +120 -0
  49. package/lib/ModernIDB/utils.ts +51 -0
  50. package/lib/QRCode/QRCode.stories.tsx +41 -0
  51. package/lib/QRCode/QRCode.tsx +54 -0
  52. package/lib/QRCode/style.css.ts +23 -0
  53. package/lib/ReleaseInfo/index.tsx +29 -0
  54. package/lib/RichText/RichText.tsx +55 -0
  55. package/lib/RichText/style.css.ts +147 -0
  56. package/lib/RulesetResolver.ts +214 -0
  57. package/lib/SafariCheck/SafariCheck.stories.tsx +99 -0
  58. package/lib/SafariCheck/SafariCheck.tsx +273 -0
  59. package/lib/SafariCheck/addToDock.svg +13 -0
  60. package/lib/SafariCheck/addToHomeScreen.svg +12 -0
  61. package/lib/SafariCheck/safari.svg +32 -0
  62. package/lib/SafariCheck/shareIcon.svg +11 -0
  63. package/lib/SafariCheck/style.css.ts +106 -0
  64. package/lib/ServiceWorkerHandler.tsx +53 -0
  65. package/lib/ShareButton/ShareButton.stories.tsx +58 -0
  66. package/lib/ShareButton/ShareButton.tsx +153 -0
  67. package/lib/ShareButton/test.css.ts +3 -0
  68. package/lib/SubscribeCard/LetterheadInfoCard.tsx +23 -0
  69. package/lib/SubscribeCard/SubscribeByEmailCard.stories.tsx +69 -0
  70. package/lib/SubscribeCard/SubscribeByEmailCard.tsx +183 -0
  71. package/lib/SubscribeCard/SubscribeByPledgeCard.stories.tsx +133 -0
  72. package/lib/SubscribeCard/SubscribeByPledgeCard.tsx +127 -0
  73. package/lib/SubscribeCard/style.css.ts +14 -0
  74. package/lib/Sync/SyncIcon.stories.tsx +67 -0
  75. package/lib/Sync/SyncIcon.tsx +102 -0
  76. package/lib/Sync/SyncLog.tsx +222 -0
  77. package/lib/Sync/SyncLogList.stories.tsx +219 -0
  78. package/lib/Sync/style.css.ts +126 -0
  79. package/lib/account/AccountIcon.tsx +15 -0
  80. package/lib/account/AccountIssueView.tsx +44 -0
  81. package/lib/account/AlreadyLoggedInView.tsx +47 -0
  82. package/lib/account/CurrentUserFetcher.stories.tsx +292 -0
  83. package/lib/account/CurrentUserFetcher.tsx +118 -0
  84. package/lib/account/FailureFallbackView.tsx +36 -0
  85. package/lib/account/JoinCard.stories.tsx +257 -0
  86. package/lib/account/JoinCard.tsx +301 -0
  87. package/lib/account/LoadingView.tsx +14 -0
  88. package/lib/account/LoginCard.stories.tsx +288 -0
  89. package/lib/account/LoginCard.tsx +100 -0
  90. package/lib/account/LoginView.tsx +151 -0
  91. package/lib/account/NoConnectionView.tsx +34 -0
  92. package/lib/account/PasswordResetCard.stories.tsx +242 -0
  93. package/lib/account/PasswordResetCard.tsx +296 -0
  94. package/lib/account/UserMismatchView.tsx +62 -0
  95. package/lib/account/VerifyPage.tsx +195 -0
  96. package/lib/account/iconMask.svg +3 -0
  97. package/lib/account/style.css.ts +81 -0
  98. package/{dist/account/types.d.ts → lib/account/types.ts} +6 -3
  99. package/lib/account/useFetchCurrentUser.tsx +63 -0
  100. package/lib/account/useRedirectPath.ts +21 -0
  101. package/lib/animations.css.ts +17 -0
  102. package/lib/append-copy-to-text.ts +35 -0
  103. package/lib/async-op.ts +298 -0
  104. package/lib/atomic.css.ts +11 -0
  105. package/{dist/caught-value.d.ts → lib/caught-value.ts} +11 -1
  106. package/{dist/class-names.d.ts → lib/class-names.ts} +17 -6
  107. package/lib/client.ts +662 -0
  108. package/lib/common.css.ts +48 -0
  109. package/lib/copyrightRange.ts +10 -0
  110. package/lib/createSafeStorage.ts +91 -0
  111. package/lib/createStrictContext.ts +15 -0
  112. package/lib/failureMessages.ts +108 -0
  113. package/lib/form/FormSubmitButton.tsx +58 -0
  114. package/lib/form/SubmitErrorAlert.tsx +21 -0
  115. package/lib/form/style.css.ts +9 -0
  116. package/lib/globals.css.ts +62 -0
  117. package/lib/groupBy.ts +25 -0
  118. package/lib/hrefs.ts +48 -0
  119. package/lib/idToDate.ts +8 -0
  120. package/lib/ids.ts +6 -0
  121. package/lib/index.ts +78 -0
  122. package/lib/internal.css.ts +10 -0
  123. package/lib/layers.css.ts +3 -0
  124. package/lib/mailto.ts +40 -0
  125. package/lib/media.ts +50 -0
  126. package/lib/random.ts +19 -0
  127. package/lib/result/swr.ts +18 -0
  128. package/{dist/sleep.d.ts → lib/sleep.ts} +3 -1
  129. package/lib/store/index.tsx +292 -0
  130. package/lib/store/store.ts +482 -0
  131. package/lib/store/types.ts +45 -0
  132. package/lib/store/utils.ts +54 -0
  133. package/lib/storybook/decorators.tsx +10 -0
  134. package/lib/structs.ts +3 -0
  135. package/{dist/typeguards.d.ts → lib/typeguards.ts} +3 -1
  136. package/{dist/types.d.ts → lib/types.ts} +23 -12
  137. package/lib/unique.ts +24 -0
  138. package/lib/use-async-op.ts +16 -0
  139. package/lib/use-document-background-color.ts +16 -0
  140. package/lib/use-form.ts +78 -0
  141. package/lib/use-is-installed.ts +17 -0
  142. package/lib/use-media-query.ts +21 -0
  143. package/lib/use-reverting-state.ts +32 -0
  144. package/lib/use-scroll-restoration.ts +99 -0
  145. package/lib/useEnsureValue.ts +31 -0
  146. package/lib/useFetchJson.tsx +52 -0
  147. package/lib/useInvokeClient.ts +54 -0
  148. package/lib/useIsVisible.ts +27 -0
  149. package/lib/utm.ts +92 -0
  150. package/lib/validations.ts +25 -0
  151. package/lib/vars.css.ts +13 -0
  152. package/package.json +23 -29
  153. package/dist/AppConfig/AppConfig.d.ts +0 -29
  154. package/dist/AuthCard/AuthCard.d.ts +0 -10
  155. package/dist/AuthCard/AuthCard.stories.d.ts +0 -34
  156. package/dist/AuthCard/style.css.d.ts +0 -23
  157. package/dist/DialogTrigger/index.d.ts +0 -13
  158. package/dist/DocumentTitle/DocumentTitle.d.ts +0 -3
  159. package/dist/EnumMapper.d.ts +0 -25
  160. package/dist/ExternalLink.d.ts +0 -3
  161. package/dist/FullscreenDismissBlocker.d.ts +0 -5
  162. package/dist/IndieTabletopClubLogo.d.ts +0 -7
  163. package/dist/IndieTabletopClubSymbol.d.ts +0 -7
  164. package/dist/InfoPage/index.d.ts +0 -8
  165. package/dist/InfoPage/pages.d.ts +0 -2
  166. package/dist/InfoPage/style.css.d.ts +0 -5
  167. package/dist/Letterhead/index.d.ts +0 -19
  168. package/dist/Letterhead/stories.d.ts +0 -13
  169. package/dist/Letterhead/style.css.d.ts +0 -46
  170. package/dist/LetterheadForm/LetterheadReadonlyTextField.stories.d.ts +0 -17
  171. package/dist/LetterheadForm/LetterheadSubmitError.stories.d.ts +0 -11
  172. package/dist/LetterheadForm/LetterheadTextField.stories.d.ts +0 -336
  173. package/dist/LetterheadForm/index.d.ts +0 -44
  174. package/dist/LetterheadForm/style.css.d.ts +0 -8
  175. package/dist/LoadingIndicator.d.ts +0 -3
  176. package/dist/MiddotSeparated/MiddotSeparated.d.ts +0 -8
  177. package/dist/MiddotSeparated/MiddotSeparated.stories.d.ts +0 -586
  178. package/dist/MiddotSeparated/style.css.d.ts +0 -1
  179. package/dist/ModalDialog/index.d.ts +0 -12
  180. package/dist/ModalDialog/style.css.d.ts +0 -58
  181. package/dist/ModernIDB/Cursor.d.ts +0 -56
  182. package/dist/ModernIDB/ModernIDB.d.ts +0 -66
  183. package/dist/ModernIDB/ModernIDBError.d.ts +0 -3
  184. package/dist/ModernIDB/ObjectStore.d.ts +0 -112
  185. package/dist/ModernIDB/ObjectStoreIndex.d.ts +0 -53
  186. package/dist/ModernIDB/Transaction.d.ts +0 -16
  187. package/dist/ModernIDB/VersionChangeManager.d.ts +0 -30
  188. package/dist/ModernIDB/bindings/factory.d.ts +0 -12
  189. package/dist/ModernIDB/bindings/index.d.ts +0 -2
  190. package/dist/ModernIDB/bindings/utils.d.ts +0 -2
  191. package/dist/ModernIDB/index.d.ts +0 -10
  192. package/dist/ModernIDB/types.d.ts +0 -88
  193. package/dist/ModernIDB/utils.d.ts +0 -4
  194. package/dist/QRCode/QRCode.d.ts +0 -7
  195. package/dist/QRCode/QRCode.stories.d.ts +0 -33
  196. package/dist/QRCode/style.css.d.ts +0 -4
  197. package/dist/ReleaseInfo/index.d.ts +0 -5
  198. package/dist/RulesetResolver.d.ts +0 -87
  199. package/dist/SafariCheck/SafariCheck.d.ts +0 -23
  200. package/dist/SafariCheck/SafariCheck.stories.d.ts +0 -73
  201. package/dist/SafariCheck/style.css.d.ts +0 -17
  202. package/dist/ServiceWorkerHandler.d.ts +0 -11
  203. package/dist/ShareButton/ShareButton.d.ts +0 -57
  204. package/dist/ShareButton/ShareButton.stories.d.ts +0 -1577
  205. package/dist/ShareButton/test.css.d.ts +0 -1
  206. package/dist/SubscribeCard/LetterheadInfoCard.d.ts +0 -2
  207. package/dist/SubscribeCard/SubscribeByEmailCard.d.ts +0 -24
  208. package/dist/SubscribeCard/SubscribeByEmailCard.stories.d.ts +0 -10
  209. package/dist/SubscribeCard/SubscribeByPledgeCard.d.ts +0 -36
  210. package/dist/SubscribeCard/SubscribeByPledgeCard.stories.d.ts +0 -65
  211. package/dist/SubscribeCard/style.css.d.ts +0 -4
  212. package/dist/account/AccountIssueView.d.ts +0 -3
  213. package/dist/account/AlreadyLoggedInView.d.ts +0 -5
  214. package/dist/account/CurrentUserFetcher.d.ts +0 -20
  215. package/dist/account/CurrentUserFetcher.stories.d.ts +0 -136
  216. package/dist/account/FailureFallbackView.d.ts +0 -1
  217. package/dist/account/JoinCard.d.ts +0 -14
  218. package/dist/account/JoinCard.stories.d.ts +0 -143
  219. package/dist/account/LoadingView.d.ts +0 -1
  220. package/dist/account/LoginCard.d.ts +0 -39
  221. package/dist/account/LoginCard.stories.d.ts +0 -217
  222. package/dist/account/LoginView.d.ts +0 -10
  223. package/dist/account/NoConnectionView.d.ts +0 -4
  224. package/dist/account/PasswordResetCard.d.ts +0 -15
  225. package/dist/account/PasswordResetCard.stories.d.ts +0 -128
  226. package/dist/account/UserMismatchView.d.ts +0 -6
  227. package/dist/account/VerifyPage.d.ts +0 -13
  228. package/dist/account/style.css.d.ts +0 -10
  229. package/dist/account/useFetchCurrentUser.d.ts +0 -28
  230. package/dist/account/useRedirectPath.d.ts +0 -6
  231. package/dist/animations.css.d.ts +0 -3
  232. package/dist/append-copy-to-text.d.ts +0 -10
  233. package/dist/append-copy-to-text.test.d.ts +0 -1
  234. package/dist/appkit.css +0 -1
  235. package/dist/appkit.js +0 -10692
  236. package/dist/async-op.d.ts +0 -101
  237. package/dist/atomic.css.d.ts +0 -6
  238. package/dist/client.d.ts +0 -424
  239. package/dist/common.css.d.ts +0 -5
  240. package/dist/copyrightRange.d.ts +0 -1
  241. package/dist/copyrightRange.test.d.ts +0 -1
  242. package/dist/createSafeStorage.d.ts +0 -34
  243. package/dist/failureMessages.d.ts +0 -20
  244. package/dist/failureMessages.test.d.ts +0 -1
  245. package/dist/form/FormSubmitButton.d.ts +0 -17
  246. package/dist/form/SubmitErrorAlert.d.ts +0 -5
  247. package/dist/form/style.css.d.ts +0 -3
  248. package/dist/globals.css.d.ts +0 -0
  249. package/dist/groupBy.d.ts +0 -1
  250. package/dist/groupBy.test.d.ts +0 -1
  251. package/dist/hrefs.d.ts +0 -32
  252. package/dist/hrefs.test.d.ts +0 -1
  253. package/dist/idToDate.d.ts +0 -5
  254. package/dist/idToDate.test.d.ts +0 -1
  255. package/dist/ids.d.ts +0 -1
  256. package/dist/ids.test.d.ts +0 -1
  257. package/dist/index.d.ts +0 -64
  258. package/dist/internal.css.d.ts +0 -2
  259. package/dist/mailto.d.ts +0 -8
  260. package/dist/mailto.test.d.ts +0 -1
  261. package/dist/media.d.ts +0 -39
  262. package/dist/random.d.ts +0 -3
  263. package/dist/result/swr.d.ts +0 -4
  264. package/dist/store/index.d.ts +0 -237
  265. package/dist/store/store.d.ts +0 -144
  266. package/dist/store/types.d.ts +0 -49
  267. package/dist/store/utils.d.ts +0 -10
  268. package/dist/storybook/decorators.d.ts +0 -3
  269. package/dist/structs.d.ts +0 -1
  270. package/dist/typeguards.test.d.ts +0 -1
  271. package/dist/unique.d.ts +0 -10
  272. package/dist/unique.test.d.ts +0 -1
  273. package/dist/use-async-op.d.ts +0 -6
  274. package/dist/use-document-background-color.d.ts +0 -4
  275. package/dist/use-form.d.ts +0 -29
  276. package/dist/use-is-installed.d.ts +0 -8
  277. package/dist/use-media-query.d.ts +0 -1
  278. package/dist/use-reverting-state.d.ts +0 -5
  279. package/dist/use-scroll-restoration.d.ts +0 -25
  280. package/dist/useEnsureValue.d.ts +0 -6
  281. package/dist/useInvokeClient.d.ts +0 -25
  282. package/dist/useIsVisible.d.ts +0 -4
  283. package/dist/utm.d.ts +0 -58
  284. package/dist/utm.test.d.ts +0 -1
  285. package/dist/validations.d.ts +0 -3
  286. package/dist/vars.css.d.ts +0 -10
@@ -0,0 +1,67 @@
1
+ import { assignInlineVars } from "@vanilla-extract/dynamic";
2
+ import preview from "../../.storybook/preview.tsx";
3
+ import { SyncIcon } from "./SyncIcon.tsx";
4
+ import { syncTheme } from "./style.css.ts";
5
+
6
+ const meta = preview.meta({
7
+ title: "Components/SyncIcon",
8
+ component: SyncIcon,
9
+ });
10
+
11
+ export const Inactive = meta.story({
12
+ args: {
13
+ status: "INACTIVE",
14
+ },
15
+ });
16
+
17
+ export const Authenticating = meta.story({
18
+ args: {
19
+ status: "AUTHENTICATING",
20
+ },
21
+ });
22
+
23
+ export const Idle = meta.story({
24
+ args: {
25
+ status: "IDLE",
26
+ },
27
+ });
28
+
29
+ export const Error = meta.story({
30
+ args: {
31
+ status: "ERROR",
32
+ },
33
+ });
34
+
35
+ export const Syncing = meta.story({
36
+ args: {
37
+ status: "SYNCING",
38
+ },
39
+ });
40
+
41
+ export const Waiting = meta.story({
42
+ args: {
43
+ status: "WAITING",
44
+ },
45
+ });
46
+
47
+ export const Overrides = meta.story({
48
+ args: {
49
+ status: "AUTHENTICATING",
50
+ },
51
+ decorators: [
52
+ (Story) => {
53
+ return (
54
+ <div
55
+ style={assignInlineVars(syncTheme, {
56
+ idle: "black",
57
+ inactive: "black",
58
+ syncing: "black",
59
+ error: "black",
60
+ })}
61
+ >
62
+ <Story />
63
+ </div>
64
+ );
65
+ },
66
+ ],
67
+ });
@@ -0,0 +1,102 @@
1
+ import { cx } from "../class-names.ts";
2
+ import type { SyncState } from "../store/index.tsx";
3
+ import { icon, syncTheme } from "./style.css.ts";
4
+
5
+ export { syncTheme };
6
+
7
+ function Icon(props: { variant: SyncState }) {
8
+ switch (props.variant) {
9
+ case "IDLE": {
10
+ return (
11
+ <g className={icon.group}>
12
+ <circle className={icon.idle} />
13
+
14
+ <path
15
+ d="M9.0026 14L16.0737 6.92893L14.6595 5.51472L9.0026 11.1716L6.17421 8.3431L4.75999 9.7574L9.0026 14Z"
16
+ className={icon.shape}
17
+ />
18
+ </g>
19
+ );
20
+ }
21
+
22
+ case "AUTHENTICATING":
23
+ case "WAITING": {
24
+ return (
25
+ <g className={icon.group}>
26
+ <circle className={icon.syncing} />
27
+
28
+ <g>
29
+ <circle cx="14" cy="10" className={icon.bounce} />
30
+ <circle cx="10" cy="10" className={icon.bounce} />
31
+ <circle cx="6" cy="10" className={icon.bounce} />
32
+ </g>
33
+ </g>
34
+ );
35
+ }
36
+
37
+ case "SYNCING": {
38
+ return (
39
+ <g className={icon.group}>
40
+ <circle className={icon.idle} />
41
+
42
+ <path
43
+ d="M14.8201 15.0761C16.1628 13.8007 17 11.9981 17 10C17 6.13401 13.866 3 10 3C8.9391 3 7.9334 3.23599 7.03241 3.65834L8.0072 5.41292C8.6177 5.14729 9.2917 5 10 5C12.7614 5 15 7.23858 15 10H12L14.8201 15.0761ZM12.9676 16.3417L11.9928 14.5871C11.3823 14.8527 10.7083 15 10 15C7.23858 15 5 12.7614 5 10H8L5.17993 4.92387C3.83719 6.19929 3 8.0019 3 10C3 13.866 6.13401 17 10 17C11.0609 17 12.0666 16.764 12.9676 16.3417Z"
44
+ className={icon.shape}
45
+ />
46
+ </g>
47
+ );
48
+ }
49
+
50
+ case "ERROR": {
51
+ return (
52
+ <g className={icon.group}>
53
+ <circle className={icon.error} />
54
+
55
+ <path
56
+ d="M10 8.58579L7.17157 5.75735L5.75736 7.17156L8.5858 9.99999L5.75736 12.8284L7.17157 14.2426L10 11.4142L12.8284 14.2426L14.2426 12.8284L11.4142 9.99999L14.2426 7.17156L12.8284 5.75735L10 8.58579Z"
57
+ className={icon.shape}
58
+ />
59
+ </g>
60
+ );
61
+ }
62
+
63
+ case "INACTIVE": {
64
+ return (
65
+ <g className={icon.group}>
66
+ <circle className={icon.inactive} />
67
+
68
+ <path
69
+ d="M9 13V15H11V13H9ZM11 11.3551C12.4457 10.9248 13.5 9.5855 13.5 8C13.5 6.067 11.933 4.5 10 4.5C8.302 4.5 6.88637 5.70919 6.56731 7.31346L8.5288 7.70577C8.6656 7.01823 9.2723 6.5 10 6.5C10.8284 6.5 11.5 7.17157 11.5 8C11.5 8.8284 10.8284 9.5 10 9.5C9.4477 9.5 9 9.9477 9 10.5V12H11V11.3551Z"
70
+ className={icon.shape}
71
+ />
72
+ </g>
73
+ );
74
+ }
75
+
76
+ default: {
77
+ return null;
78
+ }
79
+ }
80
+ }
81
+
82
+ export function SyncIcon(props: {
83
+ status: SyncState;
84
+ title?: string;
85
+ className?: string;
86
+ }) {
87
+ const { status, title } = props;
88
+
89
+ return (
90
+ <svg
91
+ {...cx(props, icon.container({ rotate: status === "SYNCING" }))}
92
+ width="20px"
93
+ height="20px"
94
+ viewBox="0 0 20 20"
95
+ version="1.1"
96
+ xmlns="http://www.w3.org/2000/svg"
97
+ >
98
+ <title>{title}</title>
99
+ <Icon variant={status} />
100
+ </svg>
101
+ );
102
+ }
@@ -0,0 +1,222 @@
1
+ import {
2
+ Disclosure,
3
+ DisclosureContent,
4
+ DisclosureProvider,
5
+ } from "@ariakit/react";
6
+ import { useAppConfig } from "../AppConfig/AppConfig.tsx";
7
+ import {
8
+ useLastSuccessfulSyncTs,
9
+ useSyncLog,
10
+ useSyncState,
11
+ type SyncState,
12
+ } from "../store/index.tsx";
13
+ import type { SyncAttempt } from "../store/types.ts";
14
+ import type { FailurePayload } from "../types.ts";
15
+ import { log } from "./style.css.ts";
16
+ import { SyncIcon } from "./SyncIcon.tsx";
17
+
18
+ function SyncActionSuccess(props: {
19
+ heading: string;
20
+ items: { id: string; name: string; deleted: boolean }[];
21
+ }) {
22
+ const { heading, items } = props;
23
+
24
+ return (
25
+ <div>
26
+ <div>
27
+ <strong>{heading}</strong>
28
+ </div>
29
+
30
+ <div style={{ marginBlockStart: "0.25rem" }}>
31
+ {items.length > 0 ?
32
+ <ul
33
+ style={{
34
+ listStyle: "initial",
35
+ paddingInlineStart: "1.5rem",
36
+ }}
37
+ >
38
+ {items.map((item) => {
39
+ const name = item.name || "Unnamed item";
40
+
41
+ if (item.deleted) {
42
+ return (
43
+ <li key={item.id}>
44
+ Synced deletion of {name} (<code>{item.id}</code>)
45
+ </li>
46
+ );
47
+ }
48
+
49
+ return (
50
+ <li key={item.id}>
51
+ Synced {name} (<code>{item.id}</code>)
52
+ </li>
53
+ );
54
+ })}
55
+ </ul>
56
+ : <div style={{ opacity: 0.5 }}>
57
+ <em>Everything up to date.</em>
58
+ </div>
59
+ }
60
+ </div>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ const failureToLabel: Record<FailurePayload["type"], string> = {
66
+ NETWORK_ERROR: "A network error occurred.",
67
+ API_ERROR: "Server responded with error ",
68
+ UNKNOWN_ERROR: "An unexpected error ocurred. Try restarting the app.",
69
+ VALIDATION_ERROR: "Data validation error occurred. Try restarting the app.",
70
+ };
71
+
72
+ function SyncActionFailure(props: {
73
+ heading: string;
74
+ failure: FailurePayload;
75
+ }) {
76
+ const { heading, failure } = props;
77
+ const label = failureToLabel[failure.type];
78
+ return (
79
+ <div>
80
+ {heading}
81
+ {": "}
82
+ {failure.type === "API_ERROR" ?
83
+ ` ${label} ${failure.code}.`
84
+ : ` ${label}`}
85
+ </div>
86
+ );
87
+ }
88
+
89
+ const syncStateToLabel: Record<SyncState, string> = {
90
+ WAITING: "Waiting for additional changes...",
91
+ IDLE: "Sync enabled. Waiting for changes.",
92
+ AUTHENTICATING: "Verifying identity...",
93
+ ERROR: "Last sync unsuccessful. Waiting for changes.",
94
+ INACTIVE: "Sync not enabled. Log in or join to enable.",
95
+ SYNCING: "Syncing...",
96
+ };
97
+
98
+ export function SyncLogList(props: {
99
+ syncState: SyncState;
100
+ syncLog: SyncAttempt[];
101
+ lastSuccessfulSyncTs: number | null;
102
+ }) {
103
+ const { syncLog, syncState, lastSuccessfulSyncTs } = props;
104
+ const { fmt } = useAppConfig();
105
+
106
+ return (
107
+ <div>
108
+ <div className={log.item}>
109
+ <SyncIcon status={syncState} />
110
+
111
+ <div>
112
+ <div>{syncStateToLabel[syncState]}</div>
113
+
114
+ <div className={log.itemTimestamp}>
115
+ {lastSuccessfulSyncTs ?
116
+ `Last successful sync at: ${fmt.dateTime(lastSuccessfulSyncTs)}`
117
+ : "Not synced yet."}
118
+ </div>
119
+ </div>
120
+ </div>
121
+
122
+ {syncLog.map((logItem) => {
123
+ const isFailure =
124
+ logItem.pull?.type === "FAILURE" || logItem.push?.type == "FAILURE";
125
+
126
+ const itemsSynced =
127
+ (logItem.pull?.type === "SUCCESS" ?
128
+ logItem.pull.value.pulled.length
129
+ : 0) +
130
+ (logItem.push?.type === "SUCCESS" ?
131
+ logItem.push.value.pushed.length + logItem.push.value.pulled.length
132
+ : 0);
133
+
134
+ return (
135
+ <DisclosureProvider key={logItem.startedTs}>
136
+ <Disclosure className={log.item}>
137
+ {isFailure ?
138
+ <>
139
+ <SyncIcon status="ERROR" />
140
+ <div>
141
+ Sync attempt failed
142
+ <div className={log.itemTimestamp}>
143
+ {fmt.dateTime(logItem.startedTs)}
144
+ </div>
145
+ </div>
146
+ </>
147
+ : <>
148
+ <SyncIcon status="IDLE" />
149
+ <div>
150
+ {"Sync successful. "}
151
+ {itemsSynced === 0 ?
152
+ "Everything up to date."
153
+ : fmt.plural(itemsSynced, {
154
+ one: "1 item synced.",
155
+ other: "# items synced.",
156
+ })
157
+ }
158
+ <div className={log.itemTimestamp}>
159
+ {fmt.dateTime(logItem.startedTs)}
160
+ </div>
161
+ </div>
162
+ </>
163
+ }
164
+ </Disclosure>
165
+
166
+ <DisclosureContent className={log.disclosureContent}>
167
+ {logItem.pull && (
168
+ <>
169
+ {logItem.pull.type === "SUCCESS" ?
170
+ <SyncActionSuccess
171
+ heading="Pull successful"
172
+ items={logItem.pull.value.pulled}
173
+ />
174
+ : <SyncActionFailure
175
+ heading="Pull failed"
176
+ failure={logItem.pull.failure}
177
+ />
178
+ }
179
+ </>
180
+ )}
181
+
182
+ {logItem.push && (
183
+ <>
184
+ {logItem.push.type === "SUCCESS" ?
185
+ <>
186
+ <SyncActionSuccess
187
+ heading="Push successful"
188
+ items={logItem.push.value.pushed}
189
+ />
190
+ <SyncActionSuccess
191
+ heading="Patch successful"
192
+ items={logItem.push.value.pulled}
193
+ />
194
+ </>
195
+ : <SyncActionFailure
196
+ heading="Push failed"
197
+ failure={logItem.push.failure}
198
+ />
199
+ }
200
+ </>
201
+ )}
202
+ </DisclosureContent>
203
+ </DisclosureProvider>
204
+ );
205
+ })}
206
+ </div>
207
+ );
208
+ }
209
+
210
+ export function SyncLog() {
211
+ const syncState = useSyncState();
212
+ const syncLog = useSyncLog();
213
+ const lastSuccessfulSyncTs = useLastSuccessfulSyncTs();
214
+
215
+ return (
216
+ <SyncLogList
217
+ syncState={syncState}
218
+ syncLog={syncLog}
219
+ lastSuccessfulSyncTs={lastSuccessfulSyncTs}
220
+ />
221
+ );
222
+ }
@@ -0,0 +1,219 @@
1
+ import preview from "../../.storybook/preview.tsx";
2
+ import { Failure, Success } from "../async-op.ts";
3
+ import { SyncLogList } from "./SyncLog.tsx";
4
+
5
+ const meta = preview.meta({
6
+ title: "Components/SyncLogList",
7
+ component: SyncLogList,
8
+ });
9
+
10
+ export const Inactive = meta.story({
11
+ args: {
12
+ syncState: "INACTIVE",
13
+ lastSuccessfulSyncTs: null,
14
+ syncLog: [],
15
+ },
16
+ });
17
+
18
+ export const Syncing = meta.story({
19
+ args: {
20
+ syncState: "SYNCING",
21
+ lastSuccessfulSyncTs: 1773753718052,
22
+ syncLog: [
23
+ {
24
+ startedTs: 1773753718052,
25
+ pull: null,
26
+ push: new Success({
27
+ pushed: [
28
+ {
29
+ id: "123",
30
+ name: "Test Item",
31
+ updatedTs: 1773753718052,
32
+ deleted: false,
33
+ },
34
+ ],
35
+ pulled: [],
36
+ }),
37
+ },
38
+ ],
39
+ },
40
+ });
41
+
42
+ export const Idle = meta.story({
43
+ args: {
44
+ syncState: "IDLE",
45
+ lastSuccessfulSyncTs: 1773753718052,
46
+ syncLog: [],
47
+ },
48
+ });
49
+
50
+ export const Waiting = meta.story({
51
+ args: {
52
+ syncState: "WAITING",
53
+ lastSuccessfulSyncTs: 1773753718052,
54
+ syncLog: [],
55
+ },
56
+ });
57
+
58
+ export const Authenticating = meta.story({
59
+ args: {
60
+ syncState: "AUTHENTICATING",
61
+ lastSuccessfulSyncTs: null,
62
+ syncLog: [],
63
+ },
64
+ });
65
+
66
+ export const Error = meta.story({
67
+ args: {
68
+ syncState: "ERROR",
69
+ lastSuccessfulSyncTs: 1773753718052,
70
+ syncLog: [
71
+ {
72
+ startedTs: 1773753718052,
73
+ pull: null,
74
+ push: new Failure({ type: "API_ERROR" as const, code: 500 }),
75
+ },
76
+ ],
77
+ },
78
+ });
79
+
80
+ export const SuccessfulPullAndPush = meta.story({
81
+ args: {
82
+ syncState: "IDLE",
83
+ lastSuccessfulSyncTs: 1773753718052,
84
+ syncLog: [
85
+ {
86
+ startedTs: 1773753718052,
87
+ pull: new Success({
88
+ pulled: [
89
+ {
90
+ id: "p1",
91
+ name: "Pulled Item",
92
+ updatedTs: 1773753718052,
93
+ deleted: false,
94
+ },
95
+ ],
96
+ }),
97
+ push: new Success({
98
+ pushed: [
99
+ {
100
+ id: "s1",
101
+ name: "Pushed Item",
102
+ updatedTs: 1773753718052,
103
+ deleted: false,
104
+ },
105
+ ],
106
+ pulled: [
107
+ {
108
+ id: "s2",
109
+ name: "Patch Item",
110
+ updatedTs: 1773753718052,
111
+ deleted: false,
112
+ },
113
+ ],
114
+ }),
115
+ },
116
+ ],
117
+ },
118
+ });
119
+
120
+ export const AllUpToDate = meta.story({
121
+ args: {
122
+ syncState: "IDLE",
123
+ lastSuccessfulSyncTs: 1773753718052,
124
+ syncLog: [
125
+ {
126
+ startedTs: 1773753718052,
127
+ pull: new Success({ pulled: [] }),
128
+ push: new Success({ pushed: [], pulled: [] }),
129
+ },
130
+ ],
131
+ },
132
+ });
133
+
134
+ export const PullFailure = meta.story({
135
+ args: {
136
+ syncState: "ERROR",
137
+ lastSuccessfulSyncTs: 1773753718052,
138
+ syncLog: [
139
+ {
140
+ startedTs: 1773753718052,
141
+ pull: new Failure({ type: "NETWORK_ERROR" as const }),
142
+ push: null,
143
+ },
144
+ ],
145
+ },
146
+ });
147
+
148
+ export const DeletedAndUnnamedItems = meta.story({
149
+ args: {
150
+ syncState: "IDLE",
151
+ lastSuccessfulSyncTs: 1773753718052,
152
+ syncLog: [
153
+ {
154
+ startedTs: 1773753718052,
155
+ pull: null,
156
+ push: new Success({
157
+ pushed: [
158
+ {
159
+ id: "d1",
160
+ name: "Removed Campaign",
161
+ updatedTs: 1773753718052,
162
+ deleted: true,
163
+ },
164
+ {
165
+ id: "d2",
166
+ name: "",
167
+ updatedTs: 1773753718052,
168
+ deleted: false,
169
+ },
170
+ ],
171
+ pulled: [],
172
+ }),
173
+ },
174
+ ],
175
+ },
176
+ });
177
+
178
+ export const MultipleSyncEntries = meta.story({
179
+ args: {
180
+ syncState: "IDLE",
181
+ lastSuccessfulSyncTs: 1773753718052,
182
+ syncLog: [
183
+ {
184
+ startedTs: 1773753718052,
185
+ pull: new Success({ pulled: [] }),
186
+ push: new Success({ pushed: [], pulled: [] }),
187
+ },
188
+ {
189
+ startedTs: 1773753608052,
190
+ pull: new Success({
191
+ pulled: [
192
+ {
193
+ id: "m1",
194
+ name: "Synced Item",
195
+ updatedTs: 1773753608052,
196
+ deleted: false,
197
+ },
198
+ ],
199
+ }),
200
+ push: new Success({
201
+ pushed: [
202
+ {
203
+ id: "m2",
204
+ name: "Pushed Item",
205
+ updatedTs: 1773753608052,
206
+ deleted: false,
207
+ },
208
+ ],
209
+ pulled: [],
210
+ }),
211
+ },
212
+ {
213
+ startedTs: 1773753508052,
214
+ pull: new Failure({ type: "NETWORK_ERROR" as const }),
215
+ push: null,
216
+ },
217
+ ],
218
+ },
219
+ });