@ttt-productions/upload-ui 0.7.17 → 0.7.19

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 (43) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +0 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/react/guard/guarded-link.d.ts +0 -27
  5. package/dist/react/guard/guarded-link.d.ts.map +1 -1
  6. package/dist/react/guard/guarded-link.js.map +1 -1
  7. package/dist/react/guard/local-upload-guard-provider.d.ts +0 -81
  8. package/dist/react/guard/local-upload-guard-provider.d.ts.map +1 -1
  9. package/dist/react/guard/local-upload-guard-provider.js +3 -60
  10. package/dist/react/guard/local-upload-guard-provider.js.map +1 -1
  11. package/dist/react/guard/use-guarded-navigation.d.ts +0 -7
  12. package/dist/react/guard/use-guarded-navigation.d.ts.map +1 -1
  13. package/dist/react/guard/use-guarded-navigation.js +0 -28
  14. package/dist/react/guard/use-guarded-navigation.js.map +1 -1
  15. package/dist/react/tray/in-flight-uploads-provider.d.ts +1 -126
  16. package/dist/react/tray/in-flight-uploads-provider.d.ts.map +1 -1
  17. package/dist/react/tray/in-flight-uploads-provider.js +10 -133
  18. package/dist/react/tray/in-flight-uploads-provider.js.map +1 -1
  19. package/dist/react/tray/use-clear-upload-activity.d.ts +1 -26
  20. package/dist/react/tray/use-clear-upload-activity.d.ts.map +1 -1
  21. package/dist/react/tray/use-clear-upload-activity.js +2 -20
  22. package/dist/react/tray/use-clear-upload-activity.js.map +1 -1
  23. package/dist/react/tray/use-mark-upload-activity-seen.d.ts +0 -4
  24. package/dist/react/tray/use-mark-upload-activity-seen.d.ts.map +1 -1
  25. package/dist/react/tray/use-mark-upload-activity-seen.js +5 -25
  26. package/dist/react/tray/use-mark-upload-activity-seen.js.map +1 -1
  27. package/dist/react/tray/use-upload-processing.d.ts +0 -10
  28. package/dist/react/tray/use-upload-processing.d.ts.map +1 -1
  29. package/dist/react/tray/use-upload-processing.js +0 -18
  30. package/dist/react/tray/use-upload-processing.js.map +1 -1
  31. package/dist/react/upload/deferred-upload-form-shell.d.ts +1 -19
  32. package/dist/react/upload/deferred-upload-form-shell.d.ts.map +1 -1
  33. package/dist/react/upload/deferred-upload-form-shell.js +0 -1
  34. package/dist/react/upload/deferred-upload-form-shell.js.map +1 -1
  35. package/dist/react/upload/use-guarded-upload.d.ts +0 -29
  36. package/dist/react/upload/use-guarded-upload.d.ts.map +1 -1
  37. package/dist/react/upload/use-guarded-upload.js +0 -13
  38. package/dist/react/upload/use-guarded-upload.js.map +1 -1
  39. package/dist/upload-display.d.ts +0 -14
  40. package/dist/upload-display.d.ts.map +1 -1
  41. package/dist/upload-display.js +2 -5
  42. package/dist/upload-display.js.map +1 -1
  43. package/package.json +13 -6
@@ -1 +1 @@
1
- {"version":3,"file":"use-guarded-upload.d.ts","sourceRoot":"","sources":["../../../src/react/upload/use-guarded-upload.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAGlE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,uGAAuG;IACvG,QAAQ,EAAE,MAAM,CAAC;IACjB,qHAAqH;IACrH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,WAIf,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAAC,CA+BjD"}
1
+ {"version":3,"file":"use-guarded-upload.d.ts","sourceRoot":"","sources":["../../../src/react/upload/use-guarded-upload.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAGlE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,wBAAgB,gBAAgB,WAIf,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAAC,CA+BjD"}
@@ -1,19 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { uploadFileResumable } from '@ttt-productions/upload-core/browser';
3
3
  import { useLocalUploadGuard } from '../guard/local-upload-guard-provider.js';
4
- /**
5
- * Wraps `uploadFileResumable` with the canonical local-upload guard + phase reporting.
6
- *
7
- * Every upload-capable mutation hook MUST use this helper instead of calling
8
- * `uploadFileResumable` directly. The helper owns:
9
- * - phase reporting: preparing → uploading (with percent) → finalizing
10
- * - beforeunload guard registration during the upload window only
11
- * - error safety: the guard is unregistered in a `finally` block
12
- *
13
- * The helper does NOT call `onProgress?.(null)` — callers do that from the
14
- * mutation's `onSuccess` and `onError` callbacks (because the upload window
15
- * ends when the upload completes, but the mutation may have more work after).
16
- */
17
4
  export function useGuardedUpload() {
18
5
  const { registerUpload, unregisterUpload } = useLocalUploadGuard();
19
6
  return useCallback(async (args) => {
@@ -1 +1 @@
1
- {"version":3,"file":"use-guarded-upload.js","sourceRoot":"","sources":["../../../src/react/upload/use-guarded-upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAG3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AA6B9E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,GAAG,mBAAmB,EAAE,CAAC;IAEnE,OAAO,WAAW,CAChB,KAAK,EAAE,IAAuB,EAAiB,EAAE;QAC/C,MAAM,EACJ,QAAQ,EACR,UAAU,EACV,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,uBAAuB,GACxB,GAAG,IAAI,CAAC;QAET,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,mBAAmB,CAAC;gBACxB,OAAO;gBACP,IAAI;gBACJ,IAAI;gBACJ,QAAQ;gBACR,MAAM;gBACN,GAAG,CAAC,uBAAuB,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9E,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;aAC3E,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC;QACD,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC,EACD,CAAC,cAAc,EAAE,gBAAgB,CAAC,CACnC,CAAC;AACJ,CAAC","sourcesContent":["import { useCallback } from 'react';\nimport { uploadFileResumable } from '@ttt-productions/upload-core/browser';\nimport type { FirebaseStorage } from 'firebase/storage';\nimport type { UploadState } from '@ttt-productions/media-schemas';\nimport { useLocalUploadGuard } from '../guard/local-upload-guard-provider.js';\n\nexport interface GuardedUploadArgs {\n storage: FirebaseStorage;\n path: string;\n file: File;\n metadata: { contentType: string };\n /** Stable identifier for the in-flight upload (used to register/unregister the local-upload guard). */\n uploadId: string;\n /** Optional progress callback. Fires with preparing → uploading (with percent) → finalizing → null on completion. */\n onProgress?: (state: UploadState | null) => void;\n /**\n * Optional cancellation signal. When aborted, the in-flight Storage upload\n * is canceled and `guardedUpload` rejects with the underlying AbortError.\n * Callers should treat AbortError as a user-initiated cancel (not a real\n * failure) — typically by checking `error.name === 'AbortError'` in the\n * mutation's onError and swallowing toasts/error UI for that case.\n */\n signal?: AbortSignal;\n /**\n * EXPLICIT opt-in for the NEUTRAL `application/octet-stream` content type\n * (canonical-upload-content-classification): only for advisory picker files\n * with unknown browser metadata, so the server byte inspector sees unknown\n * as unknown. Threaded verbatim to `uploadFileResumable`, whose media-only\n * default stays intact for every consumer that omits it.\n */\n allowNeutralContentType?: boolean;\n}\n\n/**\n * Wraps `uploadFileResumable` with the canonical local-upload guard + phase reporting.\n *\n * Every upload-capable mutation hook MUST use this helper instead of calling\n * `uploadFileResumable` directly. The helper owns:\n * - phase reporting: preparing → uploading (with percent) → finalizing\n * - beforeunload guard registration during the upload window only\n * - error safety: the guard is unregistered in a `finally` block\n *\n * The helper does NOT call `onProgress?.(null)` — callers do that from the\n * mutation's `onSuccess` and `onError` callbacks (because the upload window\n * ends when the upload completes, but the mutation may have more work after).\n */\nexport function useGuardedUpload() {\n const { registerUpload, unregisterUpload } = useLocalUploadGuard();\n\n return useCallback(\n async (args: GuardedUploadArgs): Promise<void> => {\n const {\n uploadId,\n onProgress,\n storage,\n path,\n file,\n metadata,\n signal,\n allowNeutralContentType,\n } = args;\n\n onProgress?.({ phase: 'preparing', percent: null });\n registerUpload(uploadId);\n try {\n await uploadFileResumable({\n storage,\n path,\n file,\n metadata,\n signal,\n ...(allowNeutralContentType === true ? { allowNeutralContentType: true } : {}),\n onProgress: ({ percent }) => onProgress?.({ phase: 'uploading', percent }),\n });\n } finally {\n unregisterUpload(uploadId);\n }\n onProgress?.({ phase: 'finalizing', percent: null });\n },\n [registerUpload, unregisterUpload],\n );\n}\n"]}
1
+ {"version":3,"file":"use-guarded-upload.js","sourceRoot":"","sources":["../../../src/react/upload/use-guarded-upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAG3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAa9E,MAAM,UAAU,gBAAgB;IAC9B,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,GAAG,mBAAmB,EAAE,CAAC;IAEnE,OAAO,WAAW,CAChB,KAAK,EAAE,IAAuB,EAAiB,EAAE;QAC/C,MAAM,EACJ,QAAQ,EACR,UAAU,EACV,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,uBAAuB,GACxB,GAAG,IAAI,CAAC;QAET,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,mBAAmB,CAAC;gBACxB,OAAO;gBACP,IAAI;gBACJ,IAAI;gBACJ,QAAQ;gBACR,MAAM;gBACN,GAAG,CAAC,uBAAuB,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9E,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;aAC3E,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC;QACD,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC,EACD,CAAC,cAAc,EAAE,gBAAgB,CAAC,CACnC,CAAC;AACJ,CAAC","sourcesContent":["import { useCallback } from 'react';\nimport { uploadFileResumable } from '@ttt-productions/upload-core/browser';\nimport type { FirebaseStorage } from 'firebase/storage';\nimport type { UploadState } from '@ttt-productions/media-schemas';\nimport { useLocalUploadGuard } from '../guard/local-upload-guard-provider.js';\n\nexport interface GuardedUploadArgs {\n storage: FirebaseStorage;\n path: string;\n file: File;\n metadata: { contentType: string };\n uploadId: string;\n onProgress?: (state: UploadState | null) => void;\n signal?: AbortSignal;\n allowNeutralContentType?: boolean;\n}\n\nexport function useGuardedUpload() {\n const { registerUpload, unregisterUpload } = useLocalUploadGuard();\n\n return useCallback(\n async (args: GuardedUploadArgs): Promise<void> => {\n const {\n uploadId,\n onProgress,\n storage,\n path,\n file,\n metadata,\n signal,\n allowNeutralContentType,\n } = args;\n\n onProgress?.({ phase: 'preparing', percent: null });\n registerUpload(uploadId);\n try {\n await uploadFileResumable({\n storage,\n path,\n file,\n metadata,\n signal,\n ...(allowNeutralContentType === true ? { allowNeutralContentType: true } : {}),\n onProgress: ({ percent }) => onProgress?.({ phase: 'uploading', percent }),\n });\n } finally {\n unregisterUpload(uploadId);\n }\n onProgress?.({ phase: 'finalizing', percent: null });\n },\n [registerUpload, unregisterUpload],\n );\n}\n"]}
@@ -1,16 +1,10 @@
1
- /** A byte count as a short size: `850 B`, `24 KB`, `1.2 MB`, `3 GB`. `null` for no usable count. */
2
1
  export declare function formatFileSize(bytes: number | undefined): string | null;
3
- /** The app's words for an upload row's time and file kind. */
4
2
  export interface UploadDisplayCopy {
5
- /** Under a minute old. */
6
3
  justNow: string;
7
4
  minutesAgo: (minutes: number) => string;
8
5
  hoursAgo: (hours: number) => string;
9
- /** Accepted on the previous calendar day, given the local clock time. */
10
6
  yesterdayAt: (time: string) => string;
11
- /** Accepted before yesterday, given the short local date and clock time. */
12
7
  dateAt: (date: string, time: string) => string;
13
- /** The file's kind, by its simplified media type; `other` for anything else. */
14
8
  fileType: {
15
9
  image: string;
16
10
  video: string;
@@ -20,19 +14,11 @@ export interface UploadDisplayCopy {
20
14
  }
21
15
  export interface UploadDisplayConfig {
22
16
  copy: UploadDisplayCopy;
23
- /** How long a row's time reads as relative before it reads as a clock time. */
24
17
  relativeTimeCutoffMs: number;
25
- /** Locale for the clock time and date; the runtime's own when omitted. */
26
18
  locale?: Intl.LocalesArgument;
27
19
  }
28
20
  export interface UploadDisplayFormatters {
29
- /**
30
- * An upload's accept time. Within the cutoff of now: just now, minutes ago, or hours ago. Older:
31
- * the clock time for today, yesterday and the clock time, or the short date and the clock time —
32
- * by local calendar day.
33
- */
34
21
  formatUploadTimestamp: (createdAtMs: number, nowMs: number) => string;
35
- /** The file's kind from its stored content type; `null` when it has none. */
36
22
  getFileTypeLabel: (contentType: string | undefined) => string | null;
37
23
  }
38
24
  export declare function createUploadDisplayFormatters(config: UploadDisplayConfig): UploadDisplayFormatters;
@@ -1 +1 @@
1
- {"version":3,"file":"upload-display.d.ts","sourceRoot":"","sources":["../src/upload-display.ts"],"names":[],"mappings":"AAKA,oGAAoG;AACpG,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAUvE;AAED,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,yEAAyE;IACzE,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,4EAA4E;IAC5E,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,gFAAgF;IAChF,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1E;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,+EAA+E;IAC/E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,qBAAqB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtE,6EAA6E;IAC7E,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,IAAI,CAAC;CACtE;AAUD,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,mBAAmB,GAC1B,uBAAuB,CA2CzB"}
1
+ {"version":3,"file":"upload-display.d.ts","sourceRoot":"","sources":["../src/upload-display.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAUvE;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1E;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,uBAAuB;IACtC,qBAAqB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtE,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,IAAI,CAAC;CACtE;AAUD,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,mBAAmB,GAC1B,uBAAuB,CA2CzB"}
@@ -1,7 +1,4 @@
1
- // What an upload row shows besides its title: the file's size, its kind, and when it was accepted.
2
- // The mechanics are shared; every word, the relative-time cutoff and the locale are the app's.
3
1
  import { getSimplifiedMediaType } from '@ttt-productions/media-schemas';
4
- /** A byte count as a short size: `850 B`, `24 KB`, `1.2 MB`, `3 GB`. `null` for no usable count. */
5
2
  export function formatFileSize(bytes) {
6
3
  if (bytes === undefined || !Number.isFinite(bytes) || bytes < 0)
7
4
  return null;
@@ -38,8 +35,8 @@ export function createUploadDisplayFormatters(config) {
38
35
  const time = created.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' });
39
36
  if (sameCalendarDay(created, now))
40
37
  return time;
41
- // The previous calendar day, found by stepping the date rather than subtracting 24 hours: a
42
- // day that crosses a daylight-saving change is 23 or 25 hours long.
38
+ // Stepping the date, not subtracting 24 hours: a day that crosses a daylight-saving change is 23
39
+ // or 25 hours long.
43
40
  const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
44
41
  if (sameCalendarDay(created, yesterday))
45
42
  return copy.yesterdayAt(time);
@@ -1 +1 @@
1
- {"version":3,"file":"upload-display.js","sourceRoot":"","sources":["../src/upload-display.ts"],"names":[],"mappings":"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","sourcesContent":["// What an upload row shows besides its title: the file's size, its kind, and when it was accepted.\n// The mechanics are shared; every word, the relative-time cutoff and the locale are the app's.\n\nimport { getSimplifiedMediaType } from '@ttt-productions/media-schemas';\n\n/** A byte count as a short size: `850 B`, `24 KB`, `1.2 MB`, `3 GB`. `null` for no usable count. */\nexport function formatFileSize(bytes: number | undefined): string | null {\n if (bytes === undefined || !Number.isFinite(bytes) || bytes < 0) return null;\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;\n if (bytes < 1024 * 1024 * 1024) {\n const mb = bytes / (1024 * 1024);\n return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;\n }\n const gb = bytes / (1024 * 1024 * 1024);\n return `${gb < 10 ? gb.toFixed(1) : Math.round(gb)} GB`;\n}\n\n/** The app's words for an upload row's time and file kind. */\nexport interface UploadDisplayCopy {\n /** Under a minute old. */\n justNow: string;\n minutesAgo: (minutes: number) => string;\n hoursAgo: (hours: number) => string;\n /** Accepted on the previous calendar day, given the local clock time. */\n yesterdayAt: (time: string) => string;\n /** Accepted before yesterday, given the short local date and clock time. */\n dateAt: (date: string, time: string) => string;\n /** The file's kind, by its simplified media type; `other` for anything else. */\n fileType: { image: string; video: string; audio: string; other: string };\n}\n\nexport interface UploadDisplayConfig {\n copy: UploadDisplayCopy;\n /** How long a row's time reads as relative before it reads as a clock time. */\n relativeTimeCutoffMs: number;\n /** Locale for the clock time and date; the runtime's own when omitted. */\n locale?: Intl.LocalesArgument;\n}\n\nexport interface UploadDisplayFormatters {\n /**\n * An upload's accept time. Within the cutoff of now: just now, minutes ago, or hours ago. Older:\n * the clock time for today, yesterday and the clock time, or the short date and the clock time —\n * by local calendar day.\n */\n formatUploadTimestamp: (createdAtMs: number, nowMs: number) => string;\n /** The file's kind from its stored content type; `null` when it has none. */\n getFileTypeLabel: (contentType: string | undefined) => string | null;\n}\n\nfunction sameCalendarDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\nexport function createUploadDisplayFormatters(\n config: UploadDisplayConfig,\n): UploadDisplayFormatters {\n const { copy, relativeTimeCutoffMs, locale } = config;\n\n const formatUploadTimestamp = (createdAtMs: number, nowMs: number): string => {\n const diffMs = Math.max(0, nowMs - createdAtMs);\n if (diffMs < relativeTimeCutoffMs) {\n if (diffMs < 60_000) return copy.justNow;\n const minutes = Math.floor(diffMs / 60_000);\n if (minutes < 60) return copy.minutesAgo(minutes);\n return copy.hoursAgo(Math.floor(diffMs / 3_600_000));\n }\n\n const created = new Date(createdAtMs);\n const now = new Date(nowMs);\n const time = created.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' });\n if (sameCalendarDay(created, now)) return time;\n\n // The previous calendar day, found by stepping the date rather than subtracting 24 hours: a\n // day that crosses a daylight-saving change is 23 or 25 hours long.\n const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);\n if (sameCalendarDay(created, yesterday)) return copy.yesterdayAt(time);\n\n return copy.dateAt(\n created.toLocaleDateString(locale, { month: 'short', day: 'numeric' }),\n time,\n );\n };\n\n const getFileTypeLabel = (contentType: string | undefined): string | null => {\n if (!contentType) return null;\n switch (getSimplifiedMediaType(contentType)) {\n case 'image':\n return copy.fileType.image;\n case 'video':\n return copy.fileType.video;\n case 'audio':\n return copy.fileType.audio;\n default:\n return copy.fileType.other;\n }\n };\n\n return { formatUploadTimestamp, getFileTypeLabel };\n}\n"]}
1
+ {"version":3,"file":"upload-display.js","sourceRoot":"","sources":["../src/upload-display.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSimplifiedMediaType } from '@ttt-productions/media-schemas';\n\nexport function formatFileSize(bytes: number | undefined): string | null {\n if (bytes === undefined || !Number.isFinite(bytes) || bytes < 0) return null;\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;\n if (bytes < 1024 * 1024 * 1024) {\n const mb = bytes / (1024 * 1024);\n return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;\n }\n const gb = bytes / (1024 * 1024 * 1024);\n return `${gb < 10 ? gb.toFixed(1) : Math.round(gb)} GB`;\n}\n\nexport interface UploadDisplayCopy {\n justNow: string;\n minutesAgo: (minutes: number) => string;\n hoursAgo: (hours: number) => string;\n yesterdayAt: (time: string) => string;\n dateAt: (date: string, time: string) => string;\n fileType: { image: string; video: string; audio: string; other: string };\n}\n\nexport interface UploadDisplayConfig {\n copy: UploadDisplayCopy;\n relativeTimeCutoffMs: number;\n locale?: Intl.LocalesArgument;\n}\n\nexport interface UploadDisplayFormatters {\n formatUploadTimestamp: (createdAtMs: number, nowMs: number) => string;\n getFileTypeLabel: (contentType: string | undefined) => string | null;\n}\n\nfunction sameCalendarDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\nexport function createUploadDisplayFormatters(\n config: UploadDisplayConfig,\n): UploadDisplayFormatters {\n const { copy, relativeTimeCutoffMs, locale } = config;\n\n const formatUploadTimestamp = (createdAtMs: number, nowMs: number): string => {\n const diffMs = Math.max(0, nowMs - createdAtMs);\n if (diffMs < relativeTimeCutoffMs) {\n if (diffMs < 60_000) return copy.justNow;\n const minutes = Math.floor(diffMs / 60_000);\n if (minutes < 60) return copy.minutesAgo(minutes);\n return copy.hoursAgo(Math.floor(diffMs / 3_600_000));\n }\n\n const created = new Date(createdAtMs);\n const now = new Date(nowMs);\n const time = created.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' });\n if (sameCalendarDay(created, now)) return time;\n\n // Stepping the date, not subtracting 24 hours: a day that crosses a daylight-saving change is 23\n // or 25 hours long.\n const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);\n if (sameCalendarDay(created, yesterday)) return copy.yesterdayAt(time);\n\n return copy.dateAt(\n created.toLocaleDateString(locale, { month: 'short', day: 'numeric' }),\n time,\n );\n };\n\n const getFileTypeLabel = (contentType: string | undefined): string | null => {\n if (!contentType) return null;\n switch (getSimplifiedMediaType(contentType)) {\n case 'image':\n return copy.fileType.image;\n case 'video':\n return copy.fileType.video;\n case 'audio':\n return copy.fileType.audio;\n default:\n return copy.fileType.other;\n }\n };\n\n return { formatUploadTimestamp, getFileTypeLabel };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/upload-ui",
3
- "version": "0.7.17",
3
+ "version": "0.7.19",
4
4
  "description": "Upload UI primitives: local upload guard provider, useGuardedUpload helper, DeferredUploadFormShell for forms that defer Firebase Storage upload until form submit.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,13 +40,14 @@
40
40
  "prepublishOnly": "npm run clean && npm run build"
41
41
  },
42
42
  "dependencies": {
43
- "@ttt-productions/file-input": "^0.11.16",
44
- "@ttt-productions/media-schemas": "^0.14.11",
45
- "@ttt-productions/ui-core": "^0.11.34",
46
- "@ttt-productions/upload-core": "^0.7.6"
43
+ "@ttt-productions/file-input": "^0.11.17",
44
+ "@ttt-productions/media-schemas": "^0.14.12",
45
+ "@ttt-productions/ui-core": "^0.11.35",
46
+ "@ttt-productions/upload-core": "^0.7.8"
47
47
  },
48
48
  "peerDependencies": {
49
- "@tanstack/react-query": ">=5.0.0",
49
+ "@tanstack/react-query": ">=5.90.0",
50
+ "@ttt-productions/query-core": "^0.14.24",
50
51
  "firebase": ">=12.0.0",
51
52
  "react": ">=19.0.0",
52
53
  "react-dom": ">=19.0.0"
@@ -55,6 +56,9 @@
55
56
  "@tanstack/react-query": {
56
57
  "optional": true
57
58
  },
59
+ "@ttt-productions/query-core": {
60
+ "optional": true
61
+ },
58
62
  "firebase": {
59
63
  "optional": true
60
64
  },
@@ -65,6 +69,9 @@
65
69
  "optional": true
66
70
  }
67
71
  },
72
+ "devDependencies": {
73
+ "@ttt-productions/query-core": "^0.14.24"
74
+ },
68
75
  "author": "DJ (TTT Productions)",
69
76
  "license": "MIT"
70
77
  }