@streamplace/components 0.8.11 → 0.8.13

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 (73) hide show
  1. package/dist/components/chat/chat-message.d.ts.map +1 -1
  2. package/dist/components/chat/chat-message.js +5 -4
  3. package/dist/components/chat/chat-message.js.map +1 -1
  4. package/dist/components/chat/mod-view.d.ts.map +1 -1
  5. package/dist/components/chat/mod-view.js +4 -3
  6. package/dist/components/chat/mod-view.js.map +1 -1
  7. package/dist/components/mobile-player/ui/viewer-context-menu.d.ts.map +1 -1
  8. package/dist/components/mobile-player/ui/viewer-context-menu.js +3 -3
  9. package/dist/components/mobile-player/ui/viewer-context-menu.js.map +1 -1
  10. package/dist/components/share/sharesheet.d.ts.map +1 -1
  11. package/dist/components/share/sharesheet.js +5 -14
  12. package/dist/components/share/sharesheet.js.map +1 -1
  13. package/dist/components/ui/text.d.ts +2 -2
  14. package/dist/components/ui/view.d.ts +1 -1
  15. package/dist/i18n/i18n-loader.d.ts +2 -0
  16. package/dist/i18n/i18n-loader.d.ts.map +1 -0
  17. package/dist/i18n/i18n-loader.js +17 -0
  18. package/dist/i18n/i18n-loader.js.map +1 -0
  19. package/dist/i18n/i18n-loader.native.d.ts +2 -0
  20. package/dist/i18n/i18n-loader.native.d.ts.map +1 -0
  21. package/dist/i18n/i18n-loader.native.js +51 -0
  22. package/dist/i18n/i18n-loader.native.js.map +1 -0
  23. package/dist/i18n/i18next-config.d.ts.map +1 -1
  24. package/dist/i18n/i18next-config.js +5 -49
  25. package/dist/i18n/i18next-config.js.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/streamplace-provider/poller.d.ts.map +1 -1
  31. package/dist/streamplace-provider/poller.js +2 -0
  32. package/dist/streamplace-provider/poller.js.map +1 -1
  33. package/dist/time-sync/index.d.ts +3 -0
  34. package/dist/time-sync/index.d.ts.map +1 -0
  35. package/dist/time-sync/index.js +15 -0
  36. package/dist/time-sync/index.js.map +1 -0
  37. package/dist/time-sync/time-sync.d.ts +13 -0
  38. package/dist/time-sync/time-sync.d.ts.map +1 -0
  39. package/dist/time-sync/time-sync.js +95 -0
  40. package/dist/time-sync/time-sync.js.map +1 -0
  41. package/dist/time-sync/useTimeSync.d.ts +2 -0
  42. package/dist/time-sync/useTimeSync.d.ts.map +1 -0
  43. package/dist/time-sync/useTimeSync.js +49 -0
  44. package/dist/time-sync/useTimeSync.js.map +1 -0
  45. package/dist/utils/format-handle.d.ts +11 -0
  46. package/dist/utils/format-handle.d.ts.map +1 -0
  47. package/dist/utils/format-handle.js +21 -0
  48. package/dist/utils/format-handle.js.map +1 -0
  49. package/locales/en-US/common.ftl +5 -0
  50. package/locales/en-US/settings.ftl +7 -0
  51. package/locales/es-ES/common.ftl +5 -0
  52. package/locales/es-ES/settings.ftl +7 -0
  53. package/locales/fr-FR/common.ftl +5 -0
  54. package/locales/fr-FR/settings.ftl +7 -0
  55. package/locales/pt-BR/common.ftl +5 -0
  56. package/locales/pt-BR/settings.ftl +7 -0
  57. package/locales/zh-Hant/common.ftl +5 -0
  58. package/locales/zh-Hant/settings.ftl +7 -0
  59. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  60. package/package.json +3 -3
  61. package/src/components/chat/chat-message.tsx +3 -2
  62. package/src/components/chat/mod-view.tsx +4 -3
  63. package/src/components/mobile-player/ui/viewer-context-menu.tsx +5 -3
  64. package/src/components/share/sharesheet.tsx +11 -27
  65. package/src/i18n/i18n-loader.native.ts +56 -0
  66. package/src/i18n/i18n-loader.ts +19 -0
  67. package/src/i18n/i18next-config.ts +6 -57
  68. package/src/index.tsx +2 -0
  69. package/src/streamplace-provider/poller.tsx +3 -0
  70. package/src/time-sync/index.ts +12 -0
  71. package/src/time-sync/time-sync.ts +112 -0
  72. package/src/time-sync/useTimeSync.tsx +58 -0
  73. package/src/utils/format-handle.ts +24 -0
@@ -0,0 +1,3 @@
1
+ export { checkClockDrift, getSyncedDate, getSystemDate, getSystemTime, getTimeOffset, initializeTimeSync, setTimeOffset, syncTimeWithServer, } from "./time-sync";
2
+ export { useTimeSync } from "./useTimeSync";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/time-sync/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTimeSync = exports.syncTimeWithServer = exports.setTimeOffset = exports.initializeTimeSync = exports.getTimeOffset = exports.getSystemTime = exports.getSystemDate = exports.getSyncedDate = exports.checkClockDrift = void 0;
4
+ var time_sync_1 = require("./time-sync");
5
+ Object.defineProperty(exports, "checkClockDrift", { enumerable: true, get: function () { return time_sync_1.checkClockDrift; } });
6
+ Object.defineProperty(exports, "getSyncedDate", { enumerable: true, get: function () { return time_sync_1.getSyncedDate; } });
7
+ Object.defineProperty(exports, "getSystemDate", { enumerable: true, get: function () { return time_sync_1.getSystemDate; } });
8
+ Object.defineProperty(exports, "getSystemTime", { enumerable: true, get: function () { return time_sync_1.getSystemTime; } });
9
+ Object.defineProperty(exports, "getTimeOffset", { enumerable: true, get: function () { return time_sync_1.getTimeOffset; } });
10
+ Object.defineProperty(exports, "initializeTimeSync", { enumerable: true, get: function () { return time_sync_1.initializeTimeSync; } });
11
+ Object.defineProperty(exports, "setTimeOffset", { enumerable: true, get: function () { return time_sync_1.setTimeOffset; } });
12
+ Object.defineProperty(exports, "syncTimeWithServer", { enumerable: true, get: function () { return time_sync_1.syncTimeWithServer; } });
13
+ var useTimeSync_1 = require("./useTimeSync");
14
+ Object.defineProperty(exports, "useTimeSync", { enumerable: true, get: function () { return useTimeSync_1.useTimeSync; } });
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/time-sync/index.ts"],"names":[],"mappings":";;;AAAA,yCASqB;AARnB,4GAAA,eAAe,OAAA;AACf,0GAAA,aAAa,OAAA;AACb,0GAAA,aAAa,OAAA;AACb,0GAAA,aAAa,OAAA;AACb,0GAAA,aAAa,OAAA;AACb,+GAAA,kBAAkB,OAAA;AAClB,0GAAA,aAAa,OAAA;AACb,+GAAA,kBAAkB,OAAA;AAGpB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA"}
@@ -0,0 +1,13 @@
1
+ export declare function getTimeOffset(): number;
2
+ export declare function setTimeOffset(offset: number): void;
3
+ export declare function checkClockDrift(serverTime: string): {
4
+ hasDrift: boolean;
5
+ driftMs: number;
6
+ driftSeconds: number;
7
+ };
8
+ export declare function syncTimeWithServer(serverTime: string, networkLatencyMs: number): void;
9
+ export declare function getSyncedDate(): Date;
10
+ export declare function getSystemDate(): Date;
11
+ export declare function getSystemTime(): number;
12
+ export declare function initializeTimeSync(): void;
13
+ //# sourceMappingURL=time-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-sync.d.ts","sourceRoot":"","sources":["../../src/time-sync/time-sync.ts"],"names":[],"mappings":"AAQA,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG;IACnD,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,CAuBA;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,GACvB,IAAI,CAMN;AAED,wBAAgB,aAAa,IAAI,IAAI,CAMpC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAuCzC"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTimeOffset = getTimeOffset;
4
+ exports.setTimeOffset = setTimeOffset;
5
+ exports.checkClockDrift = checkClockDrift;
6
+ exports.syncTimeWithServer = syncTimeWithServer;
7
+ exports.getSyncedDate = getSyncedDate;
8
+ exports.getSystemDate = getSystemDate;
9
+ exports.getSystemTime = getSystemTime;
10
+ exports.initializeTimeSync = initializeTimeSync;
11
+ const react_native_1 = require("react-native");
12
+ let timeOffset = 0;
13
+ let hasWarned = false;
14
+ let OriginalDate = Date;
15
+ const CLOCK_DRIFT_THRESHOLD_MS = 5000; // 5 seconds
16
+ function getTimeOffset() {
17
+ return timeOffset;
18
+ }
19
+ function setTimeOffset(offset) {
20
+ timeOffset = offset;
21
+ }
22
+ function checkClockDrift(serverTime) {
23
+ const serverDate = new Date(serverTime);
24
+ const clientDate = new Date();
25
+ const drift = Math.abs(serverDate.getTime() - clientDate.getTime());
26
+ if (drift > CLOCK_DRIFT_THRESHOLD_MS) {
27
+ const driftSeconds = Math.round(drift / 1000);
28
+ if (!hasWarned) {
29
+ hasWarned = true;
30
+ console.warn(`clock drift detected: ${driftSeconds}s difference from server time. ` +
31
+ `this may cause issues with time-sensitive operations. ` +
32
+ `please sync your system clock.`);
33
+ }
34
+ return { hasDrift: true, driftMs: drift, driftSeconds };
35
+ }
36
+ else {
37
+ return {
38
+ hasDrift: false,
39
+ driftMs: drift,
40
+ driftSeconds: Math.round(drift / 1000),
41
+ };
42
+ }
43
+ }
44
+ function syncTimeWithServer(serverTime, networkLatencyMs) {
45
+ const serverDate = new OriginalDate(serverTime);
46
+ const clientDate = new OriginalDate();
47
+ const offset = serverDate.getTime() - clientDate.getTime() - networkLatencyMs;
48
+ setTimeOffset(offset);
49
+ }
50
+ function getSyncedDate() {
51
+ const now = new Date();
52
+ if (timeOffset !== 0) {
53
+ return new Date(now.getTime() + timeOffset);
54
+ }
55
+ return now;
56
+ }
57
+ function getSystemDate() {
58
+ return new OriginalDate();
59
+ }
60
+ function getSystemTime() {
61
+ return OriginalDate.now();
62
+ }
63
+ function initializeTimeSync() {
64
+ if (react_native_1.Platform.OS !== "web") {
65
+ return;
66
+ }
67
+ // store original Date
68
+ OriginalDate = Date;
69
+ const OriginalDatePrototype = OriginalDate.prototype;
70
+ // create patched Date constructor
71
+ function PatchedDate(...args) {
72
+ // If called as a function (no `new`), forward to original Date to get the string form
73
+ if (!(this instanceof PatchedDate)) {
74
+ return OriginalDate.apply(undefined, args);
75
+ }
76
+ // If called as a constructor, construct a Date with synced time when no args provided
77
+ if (args.length === 0) {
78
+ const syncedTime = OriginalDate.now() + timeOffset;
79
+ return Reflect.construct(OriginalDate, [syncedTime], PatchedDate);
80
+ }
81
+ // Otherwise construct with the provided arguments
82
+ return Reflect.construct(OriginalDate, args, PatchedDate);
83
+ }
84
+ // copy static methods
85
+ PatchedDate.now = function () {
86
+ return OriginalDate.now() + timeOffset;
87
+ };
88
+ PatchedDate.parse = OriginalDate.parse;
89
+ PatchedDate.UTC = OriginalDate.UTC;
90
+ // copy prototype
91
+ PatchedDate.prototype = OriginalDatePrototype;
92
+ // replace global Date
93
+ globalThis.Date = PatchedDate;
94
+ }
95
+ //# sourceMappingURL=time-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-sync.js","sourceRoot":"","sources":["../../src/time-sync/time-sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function useTimeSync(): void;
2
+ //# sourceMappingURL=useTimeSync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTimeSync.d.ts","sourceRoot":"","sources":["../../src/time-sync/useTimeSync.tsx"],"names":[],"mappings":"AAQA,wBAAgB,WAAW,SAiD1B"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTimeSync = useTimeSync;
4
+ const lucide_react_native_1 = require("lucide-react-native");
5
+ const react_1 = require("react");
6
+ const react_native_1 = require("react-native");
7
+ const streamplace_1 = require("streamplace");
8
+ const toast_1 = require("../components/ui/toast");
9
+ const streamplace_store_1 = require("../streamplace-store/streamplace-store");
10
+ const time_sync_1 = require("./time-sync");
11
+ function useTimeSync() {
12
+ const url = (0, streamplace_store_1.useUrl)();
13
+ const t = (0, toast_1.useToast)();
14
+ const hasShownWarning = (0, react_1.useRef)(false);
15
+ (0, react_1.useEffect)(() => {
16
+ const checkTime = async () => {
17
+ if (react_native_1.Platform.OS !== "web") {
18
+ return;
19
+ }
20
+ try {
21
+ const agent = new streamplace_1.StreamplaceAgent(url);
22
+ const start = new Date().getTime();
23
+ const response = await agent.place.stream.server.getServerTime();
24
+ const roundTripLatency = new Date().getTime() - start;
25
+ const serverTime = response.data.serverTime;
26
+ // always sync with server time
27
+ (0, time_sync_1.syncTimeWithServer)(serverTime, roundTripLatency / 2);
28
+ const driftInfo = (0, time_sync_1.checkClockDrift)(serverTime);
29
+ // only show warning if drift is significant
30
+ if (driftInfo.hasDrift && !hasShownWarning.current) {
31
+ hasShownWarning.current = true;
32
+ t.show("Clock drift detected!", `Your device clock is ${driftInfo.driftSeconds}s off from server time. Please sync your system clock to avoid issues.`, {
33
+ variant: "info",
34
+ iconLeft: lucide_react_native_1.TriangleAlert,
35
+ duration: 25,
36
+ });
37
+ console.log(`time sync applied: offset ${driftInfo.driftMs}ms. Date() calls will now use server time.`);
38
+ }
39
+ }
40
+ catch (error) {
41
+ console.error("failed to sync time with server:", error);
42
+ }
43
+ };
44
+ checkTime();
45
+ const interval = setInterval(checkTime, 1800000); // every 30m
46
+ return () => clearInterval(interval);
47
+ }, [url, t]);
48
+ }
49
+ //# sourceMappingURL=useTimeSync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTimeSync.js","sourceRoot":"","sources":["../../src/time-sync/useTimeSync.tsx"],"names":[],"mappings":";;AAQA,kCAiDC;AAzDD,6DAAoD;AACpD,iCAA0C;AAC1C,+CAAwC;AACxC,6CAA+C;AAC/C,kDAAkD;AAClD,8EAAgE;AAChE,2CAAkE;AAElE,SAAgB,WAAW;IACzB,MAAM,GAAG,GAAG,IAAA,0BAAM,GAAE,CAAC;IACrB,MAAM,CAAC,GAAG,IAAA,gBAAQ,GAAE,CAAC;IACrB,MAAM,eAAe,GAAG,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;IAEtC,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;YAC3B,IAAI,uBAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBAC1B,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,8BAAgB,CAAC,GAAG,CAAC,CAAC;gBACxC,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;gBACnC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;gBACjE,MAAM,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC;gBACtD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;gBAE5C,+BAA+B;gBAC/B,IAAA,8BAAkB,EAAC,UAAU,EAAE,gBAAgB,GAAG,CAAC,CAAC,CAAC;gBAErD,MAAM,SAAS,GAAG,IAAA,2BAAe,EAAC,UAAU,CAAC,CAAC;gBAE9C,4CAA4C;gBAC5C,IAAI,SAAS,CAAC,QAAQ,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;oBACnD,eAAe,CAAC,OAAO,GAAG,IAAI,CAAC;oBAC/B,CAAC,CAAC,IAAI,CACJ,uBAAuB,EACvB,wBAAwB,SAAS,CAAC,YAAY,wEAAwE,EACtH;wBACE,OAAO,EAAE,MAAM;wBACf,QAAQ,EAAE,mCAAa;wBACvB,QAAQ,EAAE,EAAE;qBACb,CACF,CAAC;oBACF,OAAO,CAAC,GAAG,CACT,6BAA6B,SAAS,CAAC,OAAO,4CAA4C,CAC3F,CAAC;gBACJ,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC,CAAC;QAEF,SAAS,EAAE,CAAC;QAEZ,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY;QAE9D,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { AppBskyActorDefs } from "@atproto/api";
2
+ /**
3
+ * formats a user's handle for display, falling back to DID if handle is invalid
4
+ */
5
+ export declare function formatHandle(profile: Pick<AppBskyActorDefs.ProfileViewBasic, "handle" | "did">, prefix?: string): string;
6
+ /**
7
+ * convenience function for formatting a user's handle with @ prefix for display,
8
+ * falling back to DID if handle is invalid
9
+ */
10
+ export declare function formatHandleWithAt(profile: Pick<AppBskyActorDefs.ProfileViewBasic, "handle" | "did">): string;
11
+ //# sourceMappingURL=format-handle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-handle.d.ts","sourceRoot":"","sources":["../../src/utils/format-handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,QAAQ,GAAG,KAAK,CAAC,EAClE,MAAM,GAAE,MAAW,GAClB,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,QAAQ,GAAG,KAAK,CAAC,GACjE,MAAM,CAER"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatHandle = formatHandle;
4
+ exports.formatHandleWithAt = formatHandleWithAt;
5
+ /**
6
+ * formats a user's handle for display, falling back to DID if handle is invalid
7
+ */
8
+ function formatHandle(profile, prefix = "") {
9
+ if (profile.handle === "handle.invalid") {
10
+ return profile.did;
11
+ }
12
+ return prefix + profile.handle;
13
+ }
14
+ /**
15
+ * convenience function for formatting a user's handle with @ prefix for display,
16
+ * falling back to DID if handle is invalid
17
+ */
18
+ function formatHandleWithAt(profile) {
19
+ return formatHandle(profile, "@");
20
+ }
21
+ //# sourceMappingURL=format-handle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-handle.js","sourceRoot":"","sources":["../../src/utils/format-handle.ts"],"names":[],"mappings":";;AAKA,oCAQC;AAMD,gDAIC;AArBD;;GAEG;AACH,SAAgB,YAAY,CAC1B,OAAkE,EAClE,SAAiB,EAAE;IAEnB,IAAI,OAAO,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;QACxC,OAAO,OAAO,CAAC,GAAG,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB,CAChC,OAAkE;IAElE,OAAO,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACpC,CAAC"}
@@ -32,6 +32,11 @@ info = Information
32
32
  search-placeholder = Search...
33
33
  message-input = Enter your message...
34
34
 
35
+ ## Authentication & Access
36
+ please-log-in-to-access-this-page = Please log in to access this page
37
+ go-to-settings = Go to Settings
38
+ go-back = Go Back
39
+
35
40
  ## Demo and Testing
36
41
  welcome-user = Welcome, { $username }!
37
42
  notification-count = { $count ->
@@ -45,6 +45,7 @@ settings-title = Settings
45
45
 
46
46
  ## Navigation Categories
47
47
  about = About
48
+ account = Account
48
49
  advanced = Advanced
49
50
  danmu = Danmu
50
51
  developer = Developer
@@ -60,6 +61,12 @@ refresh = Refresh
60
61
  save-button = Save
61
62
  sign-in = Sign In
62
63
  update = Update
64
+ log-out = Log out
65
+
66
+ ## Account Settings
67
+ account-greeting = Hey, @{ $handle }.
68
+ edit-profile-bluesky = Edit profile (on Bluesky)
69
+ change-name-color = Change name color
63
70
 
64
71
  ## Key Management
65
72
  key-management = Key Management
@@ -32,6 +32,11 @@ info = Información
32
32
  search-placeholder = Buscar...
33
33
  message-input = Escribe tu mensaje...
34
34
 
35
+ ## Authentication & Access
36
+ please-log-in-to-access-this-page = Por favor, inicia sesión para acceder a esta página
37
+ go-to-settings = Ir a Configuración
38
+ go-back = Volver
39
+
35
40
  ## Demo and Testing
36
41
  welcome-user = ¡Bienvenido, { $username }!
37
42
  notification-count = { $count ->
@@ -84,6 +84,7 @@ finish = Finalizar
84
84
 
85
85
  ## Categorías de Navegación
86
86
  about = Acerca de
87
+ account = Cuenta
87
88
  advanced = Avanzado
88
89
  danmu = Danmu
89
90
  developer = Desarrollador
@@ -99,6 +100,12 @@ refresh = Actualizar
99
100
  save-button = Guardar
100
101
  sign-in = Iniciar Sesión
101
102
  update = Actualizar
103
+ log-out = Cerrar sesión
104
+
105
+ ## Configuración de Cuenta
106
+ account-greeting = Hola, @{ $handle }.
107
+ edit-profile-bluesky = Editar perfil (en Bluesky)
108
+ change-name-color = Cambiar color de nombre
102
109
 
103
110
  ## Gestión de Claves
104
111
  key-management = Gestión de Claves
@@ -32,6 +32,11 @@ info = Information
32
32
  search-placeholder = Rechercher...
33
33
  message-input = Entrez votre message...
34
34
 
35
+ ## Authentication & Access
36
+ please-log-in-to-access-this-page = Veuillez vous connecter pour accéder à cette page
37
+ go-to-settings = Aller aux Paramètres
38
+ go-back = Retour
39
+
35
40
  ## Demo and Testing
36
41
  welcome-user = Bienvenue, { $username } !
37
42
  notification-count = { $count ->
@@ -82,6 +82,7 @@ finish = Terminer
82
82
 
83
83
  ## Catégories de Navigation
84
84
  about = À propos
85
+ account = Compte
85
86
  advanced = Avancé
86
87
  danmu = Danmu
87
88
  developer = Développeur
@@ -97,6 +98,12 @@ refresh = Actualiser
97
98
  save-button = Enregistrer
98
99
  sign-in = Se connecter
99
100
  update = Mettre à jour
101
+ log-out = Se déconnecter
102
+
103
+ ## Paramètres du Compte
104
+ account-greeting = Salut, @{ $handle }.
105
+ edit-profile-bluesky = Modifier le profil (sur Bluesky)
106
+ change-name-color = Changer la couleur du nom
100
107
 
101
108
  ## Gestion des Clés
102
109
  key-management = Gestion des Clés
@@ -32,6 +32,11 @@ info = Informação
32
32
  search-placeholder = Pesquisar...
33
33
  message-input = Digite sua mensagem...
34
34
 
35
+ ## Authentication & Access
36
+ please-log-in-to-access-this-page = Por favor, faça login para acessar esta página
37
+ go-to-settings = Ir para Configurações
38
+ go-back = Voltar
39
+
35
40
  ## Demo and Testing
36
41
  welcome-user = Bem-vindo, { $username }!
37
42
  notification-count = { $count ->
@@ -82,6 +82,7 @@ finish = Finalizar
82
82
 
83
83
  ## Categorias de Navegação
84
84
  about = Sobre
85
+ account = Conta
85
86
  advanced = Avançado
86
87
  danmu = Danmu
87
88
  developer = Desenvolvedor
@@ -97,6 +98,12 @@ refresh = Atualizar
97
98
  save-button = Salvar
98
99
  sign-in = Entrar
99
100
  update = Atualizar
101
+ log-out = Sair
102
+
103
+ ## Configurações da Conta
104
+ account-greeting = Olá, @{ $handle }.
105
+ edit-profile-bluesky = Editar perfil (no Bluesky)
106
+ change-name-color = Mudar cor do nome
100
107
 
101
108
  ## Gerenciamento de Chaves
102
109
  key-management = Gerenciamento de Chaves
@@ -32,6 +32,11 @@ info = 資訊
32
32
  search-placeholder = 搜尋...
33
33
  message-input = 輸入您的訊息...
34
34
 
35
+ ## Authentication & Access
36
+ please-log-in-to-access-this-page = 請登入以存取此頁面
37
+ go-to-settings = 前往設定
38
+ go-back = 返回
39
+
35
40
  ## Demo and Testing
36
41
  welcome-user = 歡迎,{ $username }!
37
42
  notification-count = { $count ->
@@ -83,6 +83,7 @@ finish = 完成
83
83
 
84
84
  ## 導航類別
85
85
  about = 關於
86
+ account = 帳戶
86
87
  advanced = 進階
87
88
  danmu = 彈幕
88
89
  developer = 開發者
@@ -98,6 +99,12 @@ refresh = 重新整理
98
99
  save-button = 儲存
99
100
  sign-in = 登入
100
101
  update = 更新
102
+ log-out = 登出
103
+
104
+ ## 帳戶設定
105
+ account-greeting = 嗨,@{ $handle }。
106
+ edit-profile-bluesky = 編輯個人資料(在 Bluesky)
107
+ change-name-color = 變更名稱顏色
101
108
 
102
109
  ## 金鑰管理
103
110
  key-management = 金鑰管理
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamplace/components",
3
- "version": "0.8.11",
3
+ "version": "0.8.13",
4
4
  "description": "Streamplace React (Native) Components",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.tsx",
@@ -72,10 +72,10 @@
72
72
  "scripts": {
73
73
  "build": "tsc",
74
74
  "start": "tsc --watch --preserveWatchOutput",
75
- "prepare": "tsc && node scripts/compile-translations.js",
75
+ "prepare": "node scripts/compile-translations.js && tsc",
76
76
  "i18n:compile": "node scripts/compile-translations.js",
77
77
  "i18n:watch": "nodemon scripts/compile-translations.js --watch locales/**/*.ftl",
78
78
  "i18n:extract": "node scripts/extract-i18n.js"
79
79
  },
80
- "gitHead": "deec4cd1bea51452762e5463462550336057e156"
80
+ "gitHead": "abc60b0b81cfa69ae99e78781976199f0762f941"
81
81
  }
@@ -8,6 +8,7 @@ import { Linking, View } from "react-native";
8
8
  import { ChatMessageViewHydrated } from "streamplace";
9
9
  import { RichtextSegment, segmentize } from "../../lib/facet";
10
10
  import { borders, flex, gap, ml, mr, opacity, pl } from "../../lib/theme/atoms";
11
+ import { formatHandleWithAt } from "../../utils/format-handle";
11
12
  import { atoms, colors, layout } from "../ui";
12
13
 
13
14
  interface Facet {
@@ -138,7 +139,7 @@ export const RenderChatMessage = memo(
138
139
  fontWeight: "thin",
139
140
  }}
140
141
  >
141
- @{(replyTo.author as any).handle}
142
+ {formatHandleWithAt(replyTo.author)}
142
143
  </Text>{" "}
143
144
  <Text
144
145
  style={{
@@ -181,7 +182,7 @@ export const RenderChatMessage = memo(
181
182
  },
182
183
  ]}
183
184
  >
184
- @{item.author.handle}
185
+ {formatHandleWithAt(item.author)}
185
186
  </Text>
186
187
  :{" "}
187
188
  <RichTextMessage
@@ -12,6 +12,7 @@ import { Linking } from "react-native";
12
12
  import { ChatMessageViewHydrated } from "streamplace";
13
13
  import { useDeleteChatMessage } from "../../livestream-store";
14
14
  import { useStreamplaceStore } from "../../streamplace-store";
15
+ import { formatHandle, formatHandleWithAt } from "../../utils/format-handle";
15
16
  import {
16
17
  atoms,
17
18
  DropdownMenu,
@@ -113,7 +114,7 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
113
114
  minute: "2-digit",
114
115
  hour12: false,
115
116
  })}{" "}
116
- @{message.author.handle}: {message.record.text}
117
+ {formatHandleWithAt(message.author)}: {message.record.text}
117
118
  </Text>
118
119
  </View>
119
120
  </DropdownMenuItem>
@@ -159,7 +160,7 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
159
160
  <Text color="destructive">
160
161
  {isBlockLoading
161
162
  ? "Blocking..."
162
- : `Block user @${message.author.handle} from this channel`}
163
+ : `Block user ${formatHandleWithAt(message.author)} from this channel`}
163
164
  </Text>
164
165
  )}
165
166
  </DropdownMenuItem>
@@ -170,7 +171,7 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
170
171
  <DropdownMenuItem
171
172
  onPress={() => {
172
173
  Linking.openURL(
173
- `https://${BSKY_FRONTEND_DOMAIN}/profile/${message.author.handle}`,
174
+ `https://${BSKY_FRONTEND_DOMAIN}/profile/${formatHandle(message.author)}`,
174
175
  );
175
176
  }}
176
177
  >
@@ -4,6 +4,8 @@ import { Image, Linking, Platform, Pressable, View } from "react-native";
4
4
  import {
5
5
  ContentRights,
6
6
  ContentWarnings,
7
+ formatHandle,
8
+ formatHandleWithAt,
7
9
  useAvatars,
8
10
  useLivestreamInfo,
9
11
  zero,
@@ -113,12 +115,12 @@ export function ContextMenu({
113
115
  <Pressable
114
116
  onPress={() => {
115
117
  if (profile?.handle) {
116
- const url = `https://bsky.app/profile/${profile.handle}`;
118
+ const url = `https://bsky.app/profile/${formatHandle(profile)}`;
117
119
  Linking.openURL(url);
118
120
  }
119
121
  }}
120
122
  >
121
- <Text>@{profile?.handle || "user"}</Text>
123
+ <Text>{profile && formatHandleWithAt(profile)}</Text>
122
124
  </Pressable>
123
125
  {/*{did && profile && (
124
126
  <FollowButton streamerDID={profile?.did} currentUserDID={did} />
@@ -163,7 +165,7 @@ export function ContextMenu({
163
165
  <DropdownMenuItem
164
166
  onPress={() => {
165
167
  if (profile?.handle) {
166
- const url = `https://bsky.app/profile/${profile.handle}`;
168
+ const url = `https://bsky.app/profile/${formatHandle(profile)}`;
167
169
  Linking.openURL(url);
168
170
  }
169
171
  }}
@@ -4,6 +4,7 @@ import { Clipboard, Linking, Platform, View } from "react-native";
4
4
  import { colors } from "../../lib/theme";
5
5
  import { useLivestreamStore } from "../../livestream-store";
6
6
  import { useUrl } from "../../streamplace-store";
7
+ import { formatHandle } from "../../utils/format-handle";
7
8
  import { BlueskyIcon } from "../icons/bluesky-icon";
8
9
  import {
9
10
  DropdownMenu,
@@ -26,12 +27,12 @@ export function ShareSheet({ onShare }: ShareSheetProps = {}) {
26
27
 
27
28
  // Get the current stream URL
28
29
  const getStreamUrl = useCallback(() => {
29
- return url + (profile ? `/@${profile.handle}` : "");
30
+ return url + (profile ? `/${formatHandle(profile)}` : "");
30
31
  }, [profile]);
31
32
 
32
33
  // Get the embed URL
33
34
  const getEmbedUrl = useCallback(() => {
34
- return url + (profile ? `/embed/${profile.handle}` : "");
35
+ return url + (profile ? `/embed/${formatHandle(profile)}` : "");
35
36
  }, [profile]);
36
37
 
37
38
  // Get embed code
@@ -63,31 +64,22 @@ export function ShareSheet({ onShare }: ShareSheetProps = {}) {
63
64
  // Share to Bluesky
64
65
  const shareToBluesky = useCallback(() => {
65
66
  const streamUrl = getStreamUrl();
66
- const text = profile
67
- ? `Check out @${profile.handle} live on Streamplace! ${streamUrl}`
68
- : `Check out this stream on Streamplace! ${streamUrl}`;
67
+ const text =
68
+ profile && profile.handle
69
+ ? `Check out @${profile.handle} live on Streamplace! ${streamUrl}`
70
+ : `Check out this stream on Streamplace! ${streamUrl}`;
69
71
  const blueskyUrl = `https://bsky.app/intent/compose?text=${encodeURIComponent(text)}`;
70
72
  Linking.openURL(blueskyUrl);
71
73
  onShare?.("share_bluesky", true);
72
74
  }, [profile, getStreamUrl, onShare]);
73
75
 
74
- // Share to Twitter/X
75
- const shareToTwitter = useCallback(() => {
76
- const streamUrl = getStreamUrl();
77
- const text = profile
78
- ? `Check out @${profile.handle} live on Streamplace!`
79
- : `Check out this stream on Streamplace!`;
80
- const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(streamUrl)}`;
81
- Linking.openURL(twitterUrl);
82
- onShare?.("share_twitter", true);
83
- }, [profile, getStreamUrl, onShare]);
84
-
85
76
  // Native share (mobile)
86
77
  const nativeShare = useCallback(async () => {
87
78
  const streamUrl = getStreamUrl();
88
- const text = profile
89
- ? `Check out @${profile.handle} live on Streamplace!`
90
- : `Check out this stream on Streamplace!`;
79
+ const text =
80
+ profile && profile.handle
81
+ ? `Check out @${profile.handle} live on Streamplace!`
82
+ : `Check out this stream on Streamplace!`;
91
83
 
92
84
  if (Platform.OS === "web" && navigator.share) {
93
85
  try {
@@ -119,14 +111,6 @@ export function ShareSheet({ onShare }: ShareSheetProps = {}) {
119
111
  <Text>Share to Bluesky</Text>
120
112
  </View>
121
113
  </DropdownMenuItem>
122
- {/* <DropdownMenuItem onPress={shareToTwitter}>
123
- <View
124
- style={{ flexDirection: "row", alignItems: "center", gap: 12 }}
125
- >
126
- <MessageCircle size={20} color={colors.gray[400]} />
127
- <Text>Share to X</Text>
128
- </View>
129
- </DropdownMenuItem> */}
130
114
  {/* navigator isn't on non-web */}
131
115
  {Platform.OS !== "web" || (navigator && (navigator as any).share) ? (
132
116
  <DropdownMenuItem onPress={nativeShare}>