@streamplace/components 0.10.6 → 0.10.8

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 (79) hide show
  1. package/dist/components/chat/chat.js +1 -1
  2. package/dist/components/chat/mod-view.d.ts.map +1 -1
  3. package/dist/components/chat/mod-view.js +31 -6
  4. package/dist/components/chat/mod-view.js.map +1 -1
  5. package/dist/components/dashboard/moderator-panel.js +7 -1
  6. package/dist/components/dashboard/moderator-panel.js.map +1 -1
  7. package/dist/components/mobile-player/props.d.ts +1 -0
  8. package/dist/components/mobile-player/props.d.ts.map +1 -1
  9. package/dist/components/mobile-player/rotation-lock.js +2 -2
  10. package/dist/components/mobile-player/rotation-lock.js.map +1 -1
  11. package/dist/components/mobile-player/use-webrtc.d.ts +2 -1
  12. package/dist/components/mobile-player/use-webrtc.d.ts.map +1 -1
  13. package/dist/components/mobile-player/use-webrtc.js +30 -15
  14. package/dist/components/mobile-player/use-webrtc.js.map +1 -1
  15. package/dist/components/mobile-player/video-async.native.d.ts.map +1 -1
  16. package/dist/components/mobile-player/video-async.native.js +5 -1
  17. package/dist/components/mobile-player/video-async.native.js.map +1 -1
  18. package/dist/components/stream-notification/pin-notification.d.ts +7 -0
  19. package/dist/components/stream-notification/pin-notification.d.ts.map +1 -0
  20. package/dist/components/stream-notification/pin-notification.js +63 -0
  21. package/dist/components/stream-notification/pin-notification.js.map +1 -0
  22. package/dist/components/ui/dropdown.d.ts.map +1 -1
  23. package/dist/components/ui/dropdown.js +3 -2
  24. package/dist/components/ui/dropdown.js.map +1 -1
  25. package/dist/components/ui/resizeable.js +2 -2
  26. package/dist/components/ui/resizeable.js.map +1 -1
  27. package/dist/lib/stream-notifications.d.ts +7 -0
  28. package/dist/lib/stream-notifications.d.ts.map +1 -1
  29. package/dist/lib/stream-notifications.js +21 -0
  30. package/dist/lib/stream-notifications.js.map +1 -1
  31. package/dist/lib/theme/atoms.d.ts +141 -141
  32. package/dist/livestream-provider/index.d.ts +1 -0
  33. package/dist/livestream-provider/index.d.ts.map +1 -1
  34. package/dist/livestream-provider/index.js +35 -3
  35. package/dist/livestream-provider/index.js.map +1 -1
  36. package/dist/livestream-store/chat.d.ts +2 -0
  37. package/dist/livestream-store/chat.d.ts.map +1 -1
  38. package/dist/livestream-store/chat.js +80 -1
  39. package/dist/livestream-store/chat.js.map +1 -1
  40. package/dist/livestream-store/livestream-state.d.ts +2 -1
  41. package/dist/livestream-store/livestream-state.d.ts.map +1 -1
  42. package/dist/livestream-store/livestream-store.d.ts +1 -0
  43. package/dist/livestream-store/livestream-store.d.ts.map +1 -1
  44. package/dist/livestream-store/livestream-store.js +4 -1
  45. package/dist/livestream-store/livestream-store.js.map +1 -1
  46. package/dist/livestream-store/websocket-consumer.d.ts.map +1 -1
  47. package/dist/livestream-store/websocket-consumer.js +14 -0
  48. package/dist/livestream-store/websocket-consumer.js.map +1 -1
  49. package/dist/streamplace-store/moderation.d.ts +1 -0
  50. package/dist/streamplace-store/moderation.d.ts.map +1 -1
  51. package/dist/streamplace-store/moderation.js +1 -0
  52. package/dist/streamplace-store/moderation.js.map +1 -1
  53. package/dist/streamplace-store/moderator-management.d.ts +1 -1
  54. package/dist/streamplace-store/moderator-management.d.ts.map +1 -1
  55. package/dist/streamplace-store/xrpc.d.ts +2 -0
  56. package/dist/streamplace-store/xrpc.d.ts.map +1 -1
  57. package/dist/streamplace-store/xrpc.js +18 -0
  58. package/dist/streamplace-store/xrpc.js.map +1 -1
  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.tsx +1 -1
  62. package/src/components/chat/mod-view.tsx +82 -3
  63. package/src/components/dashboard/moderator-panel.tsx +13 -2
  64. package/src/components/mobile-player/props.tsx +1 -0
  65. package/src/components/mobile-player/rotation-lock.tsx +2 -2
  66. package/src/components/mobile-player/use-webrtc.tsx +47 -12
  67. package/src/components/mobile-player/video-async.native.tsx +5 -0
  68. package/src/components/stream-notification/pin-notification.tsx +135 -0
  69. package/src/components/ui/dropdown.tsx +3 -2
  70. package/src/components/ui/resizeable.tsx +2 -2
  71. package/src/lib/stream-notifications.ts +28 -0
  72. package/src/livestream-provider/index.tsx +38 -2
  73. package/src/livestream-store/chat.tsx +92 -0
  74. package/src/livestream-store/livestream-state.tsx +2 -0
  75. package/src/livestream-store/livestream-store.tsx +4 -0
  76. package/src/livestream-store/websocket-consumer.tsx +15 -0
  77. package/src/streamplace-store/moderation.tsx +2 -0
  78. package/src/streamplace-store/moderator-management.tsx +1 -1
  79. package/src/streamplace-store/xrpc.tsx +22 -0
@@ -437,3 +437,95 @@ export const useReportChatMessage = () => {
437
437
  };
438
438
 
439
439
  export const reduceChat = reduceChatIncremental;
440
+
441
+ export const usePinChatMessage = () => {
442
+ const agent = usePDSAgent();
443
+ const store = getStoreFromContext();
444
+
445
+ return async (
446
+ messageUri: string,
447
+ streamerDID: string,
448
+ expiresAt?: string,
449
+ ) => {
450
+ if (!agent || !agent.did) {
451
+ throw new Error("No PDS agent or user DID found");
452
+ }
453
+
454
+ // If streamer, create directly
455
+ if (agent.did === streamerDID) {
456
+ // First delete any existing pinned records
457
+ const listResult = await agent.com.atproto.repo.listRecords({
458
+ repo: streamerDID,
459
+ collection: "place.stream.chat.pinnedRecord",
460
+ });
461
+ for (const rec of listResult.data.records) {
462
+ const rkey = rec.uri.split("/").pop();
463
+ if (rkey) {
464
+ await agent.com.atproto.repo.deleteRecord({
465
+ repo: streamerDID,
466
+ collection: "place.stream.chat.pinnedRecord",
467
+ rkey,
468
+ });
469
+ }
470
+ }
471
+
472
+ const record = {
473
+ $type: "place.stream.chat.pinnedRecord",
474
+ pinnedMessage: messageUri,
475
+ createdAt: new Date().toISOString(),
476
+ ...(expiresAt ? { expiresAt } : {}),
477
+ };
478
+
479
+ const result = await agent.com.atproto.repo.createRecord({
480
+ repo: streamerDID,
481
+ collection: "place.stream.chat.pinnedRecord",
482
+ record,
483
+ });
484
+ return result;
485
+ }
486
+
487
+ // Otherwise, use delegated moderation endpoint
488
+ const result = await agent.place.stream.moderation.createPin({
489
+ streamer: streamerDID,
490
+ messageUri,
491
+ ...(expiresAt ? { expiresAt } : {}),
492
+ });
493
+ return result;
494
+ };
495
+ };
496
+
497
+ export const useUnpinChatMessage = () => {
498
+ const agent = usePDSAgent();
499
+ const store = getStoreFromContext();
500
+
501
+ return async (pinUri: string, streamerDID: string) => {
502
+ if (!agent || !agent.did) {
503
+ throw new Error("No PDS agent or user DID found");
504
+ }
505
+
506
+ // If streamer, delete directly
507
+ if (agent.did === streamerDID) {
508
+ const rkey = pinUri.split("/").pop();
509
+ if (!rkey) {
510
+ throw new Error("Invalid pin URI");
511
+ }
512
+
513
+ await agent.com.atproto.repo.deleteRecord({
514
+ repo: streamerDID,
515
+ collection: "place.stream.chat.pinnedRecord",
516
+ rkey,
517
+ });
518
+ // Optimistically clear the pinned comment
519
+ store.setState({ pinnedComment: null });
520
+ return;
521
+ }
522
+
523
+ // Otherwise, use delegated moderation endpoint
524
+ await agent.place.stream.moderation.deletePin({
525
+ streamer: streamerDID,
526
+ pinUri,
527
+ });
528
+ // Optimistically clear the pinned comment
529
+ store.setState({ pinnedComment: null });
530
+ };
531
+ };
@@ -2,6 +2,7 @@ import { AppBskyActorDefs } from "@atproto/api";
2
2
  import {
3
3
  ChatMessageViewHydrated,
4
4
  LivestreamViewHydrated,
5
+ PinnedRecordViewHydrated,
5
6
  PlaceStreamDefs,
6
7
  PlaceStreamLiveTeleport,
7
8
  PlaceStreamModerationPermission,
@@ -28,6 +29,7 @@ export interface LivestreamState {
28
29
  setActiveTeleportUri: (uri: string | null) => void;
29
30
  websocketConnected: boolean;
30
31
  hasReceivedSegment: boolean;
32
+ pinnedComment: PinnedRecordViewHydrated | null;
31
33
  moderationPermissions: PlaceStreamModerationPermission.Record[];
32
34
  setModerationPermissions: (
33
35
  permissions: PlaceStreamModerationPermission.Record[],
@@ -27,6 +27,7 @@ export const makeLivestreamStore = (): StoreApi<LivestreamState> => {
27
27
  setActiveTeleportUri: (uri) => set({ activeTeleportUri: uri }),
28
28
  websocketConnected: false,
29
29
  hasReceivedSegment: false,
30
+ pinnedComment: null,
30
31
  moderationPermissions: [],
31
32
  setModerationPermissions: (perms) => set({ moderationPermissions: perms }),
32
33
  localLivestreamURI: null,
@@ -60,6 +61,9 @@ export const useHandleWebsocketMessages = () => {
60
61
 
61
62
  export const useChat = () => useLivestreamStore((x) => x.chat);
62
63
 
64
+ export const usePinnedComment = () =>
65
+ useLivestreamStore((x) => x.pinnedComment);
66
+
63
67
  export const useProfile = () => useLivestreamStore((x) => x.profile);
64
68
 
65
69
  export const useViewers = () => useLivestreamStore((x) => x.viewers);
@@ -2,6 +2,7 @@ import { AppBskyActorDefs } from "@atproto/api";
2
2
  import {
3
3
  ChatMessageViewHydrated,
4
4
  LivestreamViewHydrated,
5
+ PinnedRecordViewHydrated,
5
6
  PlaceStreamChatDefs,
6
7
  PlaceStreamChatGate,
7
8
  PlaceStreamChatMessage,
@@ -123,6 +124,20 @@ export const handleWebSocketMessages = (
123
124
  pendingHides: newPendingHides,
124
125
  };
125
126
  state = reduceChat(state, [], [], [hiddenMessageUri]);
127
+ } else if (PlaceStreamChatDefs.isPinnedRecordView(message)) {
128
+ const pinnedView = message as PinnedRecordViewHydrated;
129
+ state = {
130
+ ...state,
131
+ pinnedComment: pinnedView,
132
+ };
133
+ } else if (
134
+ (message as any).$type === "place.stream.chat.pinnedRecord" &&
135
+ (message as any).deleted === true
136
+ ) {
137
+ state = {
138
+ ...state,
139
+ pinnedComment: null,
140
+ };
126
141
  } else if (PlaceStreamLiveTeleport.isRecord(message)) {
127
142
  const teleportRecord = message as PlaceStreamLiveTeleport.Record;
128
143
  state = {
@@ -6,6 +6,7 @@ import { usePDSAgent } from "./xrpc";
6
6
  export interface ModerationPermissions {
7
7
  canBan: boolean;
8
8
  canHide: boolean;
9
+ canPin: boolean;
9
10
  canManageLivestream: boolean;
10
11
  isOwner: boolean;
11
12
  isLoading: boolean;
@@ -177,6 +178,7 @@ export function useCanModerate(
177
178
  return {
178
179
  canBan: isOwner || permissions.includes("ban"),
179
180
  canHide: isOwner || permissions.includes("hide"),
181
+ canPin: isOwner || permissions.includes("message.pin"),
180
182
  canManageLivestream: isOwner || permissions.includes("livestream.manage"),
181
183
  isOwner,
182
184
  isLoading,
@@ -81,7 +81,7 @@ export function useListModerators(): ListModeratorsResult {
81
81
 
82
82
  interface AddModeratorParams {
83
83
  moderatorDID: string;
84
- permissions: ("ban" | "hide" | "livestream.manage")[];
84
+ permissions: ("ban" | "hide" | "livestream.manage" | "message.pin")[];
85
85
  expirationTime?: string; // ISO 8601 datetime string
86
86
  }
87
87
 
@@ -1,3 +1,4 @@
1
+ import { SessionManager } from "@atproto/api/dist/session-manager";
1
2
  import { useMemo } from "react";
2
3
  import { StreamplaceAgent } from "streamplace";
3
4
  import { useStreamplaceStore, useUrl } from "./streamplace-store";
@@ -38,6 +39,27 @@ export function usePossiblyUnauthedPDSAgent(): StreamplaceAgent | null {
38
39
  }, [oauthSession]);
39
40
  }
40
41
 
42
+ // Creates an agent that targets a specific server URL. When authenticated,
43
+ // the OAuth session's DPoP fetch is reused but requests are redirected to the
44
+ // target server by passing an absolute URL to the session's fetchHandler
45
+ // (absolute URLs in `new URL(abs, base)` ignore the base).
46
+ export function createAgentForServer(
47
+ oauthSession: SessionManager | null | undefined,
48
+ serverUrl: string,
49
+ ): StreamplaceAgent {
50
+ if (!oauthSession) {
51
+ return new StreamplaceAgent(serverUrl);
52
+ }
53
+ const wrappedSession: SessionManager = {
54
+ did: oauthSession.did,
55
+ fetchHandler(pathname: string, init: RequestInit): Promise<Response> {
56
+ const fullUrl = new URL(pathname, serverUrl).toString();
57
+ return oauthSession.fetchHandler.call(oauthSession, fullUrl, init);
58
+ },
59
+ };
60
+ return new StreamplaceAgent(wrappedSession);
61
+ }
62
+
41
63
  // always returns an unauthenticated agent pointed at the public bluesky API
42
64
  // probably should not be used in most places, but in case we have a bug it may be useful
43
65
  export function useUnauthenticatedBlueskyAppViewAgent(): StreamplaceAgent {