@xipkg/calls-risehand 0.1.7 → 0.1.9

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.
package/dist/index.mjs CHANGED
@@ -112,15 +112,15 @@ var RaiseHandButton = ({ className }) => {
112
112
  variant: "none",
113
113
  onClick: toggleHand,
114
114
  className: cn(
115
- "bg-gray-0 hover:bg-gray-5 relative m-0 rounded-xl p-0 text-gray-100",
115
+ "bg-background-surface hover:bg-background-page text-text-primary relative m-0 rounded-xl p-0",
116
116
  !className && "h-10 w-10",
117
- isHandRaised && "text-brand-100",
117
+ isHandRaised && "text-text-link",
118
118
  className
119
119
  ),
120
120
  "data-umami-event": "call-raise-hand",
121
121
  "data-umami-event-state": isHandRaised ? "lower" : "raise",
122
122
  disabled: isPending,
123
- children: /* @__PURE__ */ jsx(Hand, { className: cn("h-6 w-6", isHandRaised ? "fill-brand-100" : "fill-gray-100") })
123
+ children: /* @__PURE__ */ jsx(Hand, { className: cn("h-6 w-6", isHandRaised ? "fill-icon-brand" : "fill-icon-primary") })
124
124
  }
125
125
  );
126
126
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/useRaisedHands.ts","../src/hooks/useHandFocus.ts","../src/ui/RaiseHandButton.tsx"],"names":["useCallStore","useRoom","useEffect"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useEffect, useCallback, useMemo, useRef } from 'react';\nimport { Participant, RoomEvent } from 'livekit-client';\nimport { useCallStore, useSoundEffectsStore } from '@xipkg/calls-store';\nimport { useCalls, useRoom, useCallsNavigation } from '@xipkg/calls-providers';\nimport { playSound } from '@xipkg/calls-utils';\n\nexport const useRaisedHands = () => {\n const navigation = useCallsNavigation();\n const prevHands = useRef<Record<string, boolean>>({});\n const handRaiseSoundVolume = useSoundEffectsStore((s) => s.handRaiseVolume);\n\n const callId = navigation.getCallId();\n const { room } = useRoom();\n\n const { addRaisedHand, removeRaisedHand } = useCallStore();\n\n const { updateParticipantMetadata, isPending } = useCalls().updateParticipantMetadata;\n\n const isHandRaised = useMemo(() => {\n try {\n const parsed = JSON.parse(room?.localParticipant?.metadata ?? '{}');\n return !!parsed.is_hand_raised;\n } catch {\n return false;\n }\n }, [room?.localParticipant?.metadata]);\n\n const toggleHand = useCallback(async () => {\n if (!room?.localParticipant || !callId) return;\n\n try {\n await updateParticipantMetadata({\n classroom_id: callId,\n is_hand_raised: !isHandRaised,\n });\n } catch (e) {\n console.error('raise hand error', e);\n }\n }, [room?.localParticipant, callId, updateParticipantMetadata, isHandRaised]);\n\n useEffect(() => {\n if (!room) return;\n\n const syncParticipant = (participant: Participant) => {\n try {\n const parsed = JSON.parse(participant.metadata || '{}');\n\n if (!('is_hand_raised' in parsed)) return;\n\n const prev = prevHands.current[participant.identity];\n\n if (parsed.is_hand_raised) {\n addRaisedHand({\n participantId: participant.identity,\n participantName: participant.name ?? participant.identity,\n timestamp: Date.now(),\n });\n\n if (!prev) {\n playSound('handRaise', handRaiseSoundVolume);\n }\n } else {\n removeRaisedHand(participant.identity);\n }\n } catch (e) {\n console.error('metadata parse error', e);\n }\n };\n\n const handler = (_metadata: string | undefined, participant: Participant) => {\n syncParticipant(participant);\n };\n\n room.on(RoomEvent.ParticipantMetadataChanged, handler);\n\n syncParticipant(room.localParticipant);\n room.remoteParticipants.forEach(syncParticipant);\n\n return () => {\n room.off(RoomEvent.ParticipantMetadataChanged, handler);\n };\n }, [room, addRaisedHand, removeRaisedHand, handRaiseSoundVolume]);\n\n return {\n toggleHand,\n isHandRaised,\n isPending: isPending,\n };\n};\n","import { useEffect } from 'react';\nimport { Track } from 'livekit-client';\nimport { useCallStore } from '@xipkg/calls-store';\nimport { useRoom } from '@xipkg/calls-providers';\nimport { useMaybeLayoutContext } from '@livekit/components-react';\n\nexport const useHandFocus = () => {\n const { raisedHands } = useCallStore();\n const { room } = useRoom();\n const layoutContext = useMaybeLayoutContext();\n\n useEffect(() => {\n if (!room || raisedHands.length === 0 || !layoutContext?.pin.dispatch) return;\n\n const earliestHand = raisedHands.reduce((earliest, current) =>\n current.timestamp < earliest.timestamp ? current : earliest,\n );\n\n const participant = room.getParticipantByIdentity(earliestHand.participantId);\n\n if (participant) {\n const cameraTrack = Array.from(participant.videoTrackPublications.values()).find(\n (track) => track.source === 'camera',\n );\n\n if (cameraTrack) {\n layoutContext.pin.dispatch({\n msg: 'set_pin',\n trackReference: {\n participant,\n source: Track.Source.Camera,\n publication: cameraTrack,\n },\n });\n }\n }\n }, [raisedHands, room, layoutContext]);\n};\n","import { Button } from '@xipkg/button';\nimport { Hand } from '@xipkg/icons';\nimport { cn } from '@xipkg/utils';\nimport { useRaisedHands } from '../hooks';\n\nexport const RaiseHandButton = ({ className }: { className?: string }) => {\n const { toggleHand, isHandRaised, isPending } = useRaisedHands();\n\n return (\n <Button\n size=\"icon\"\n variant=\"none\"\n onClick={toggleHand}\n className={cn(\n 'bg-gray-0 hover:bg-gray-5 relative m-0 rounded-xl p-0 text-gray-100',\n !className && 'h-10 w-10',\n isHandRaised && 'text-brand-100',\n className,\n )}\n data-umami-event=\"call-raise-hand\"\n data-umami-event-state={isHandRaised ? 'lower' : 'raise'}\n disabled={isPending}\n >\n <Hand className={cn('h-6 w-6', isHandRaised ? 'fill-brand-100' : 'fill-gray-100')} />\n </Button>\n );\n};\n"]}
1
+ {"version":3,"sources":["../src/hooks/useRaisedHands.ts","../src/hooks/useHandFocus.ts","../src/ui/RaiseHandButton.tsx"],"names":["useCallStore","useRoom","useEffect"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useEffect, useCallback, useMemo, useRef } from 'react';\nimport { Participant, RoomEvent } from 'livekit-client';\nimport { useCallStore, useSoundEffectsStore } from '@xipkg/calls-store';\nimport { useCalls, useRoom, useCallsNavigation } from '@xipkg/calls-providers';\nimport { playSound } from '@xipkg/calls-utils';\n\nexport const useRaisedHands = () => {\n const navigation = useCallsNavigation();\n const prevHands = useRef<Record<string, boolean>>({});\n const handRaiseSoundVolume = useSoundEffectsStore((s) => s.handRaiseVolume);\n\n const callId = navigation.getCallId();\n const { room } = useRoom();\n\n const { addRaisedHand, removeRaisedHand } = useCallStore();\n\n const { updateParticipantMetadata, isPending } = useCalls().updateParticipantMetadata;\n\n const isHandRaised = useMemo(() => {\n try {\n const parsed = JSON.parse(room?.localParticipant?.metadata ?? '{}');\n return !!parsed.is_hand_raised;\n } catch {\n return false;\n }\n }, [room?.localParticipant?.metadata]);\n\n const toggleHand = useCallback(async () => {\n if (!room?.localParticipant || !callId) return;\n\n try {\n await updateParticipantMetadata({\n classroom_id: callId,\n is_hand_raised: !isHandRaised,\n });\n } catch (e) {\n console.error('raise hand error', e);\n }\n }, [room?.localParticipant, callId, updateParticipantMetadata, isHandRaised]);\n\n useEffect(() => {\n if (!room) return;\n\n const syncParticipant = (participant: Participant) => {\n try {\n const parsed = JSON.parse(participant.metadata || '{}');\n\n if (!('is_hand_raised' in parsed)) return;\n\n const prev = prevHands.current[participant.identity];\n\n if (parsed.is_hand_raised) {\n addRaisedHand({\n participantId: participant.identity,\n participantName: participant.name ?? participant.identity,\n timestamp: Date.now(),\n });\n\n if (!prev) {\n playSound('handRaise', handRaiseSoundVolume);\n }\n } else {\n removeRaisedHand(participant.identity);\n }\n } catch (e) {\n console.error('metadata parse error', e);\n }\n };\n\n const handler = (_metadata: string | undefined, participant: Participant) => {\n syncParticipant(participant);\n };\n\n room.on(RoomEvent.ParticipantMetadataChanged, handler);\n\n syncParticipant(room.localParticipant);\n room.remoteParticipants.forEach(syncParticipant);\n\n return () => {\n room.off(RoomEvent.ParticipantMetadataChanged, handler);\n };\n }, [room, addRaisedHand, removeRaisedHand, handRaiseSoundVolume]);\n\n return {\n toggleHand,\n isHandRaised,\n isPending: isPending,\n };\n};\n","import { useEffect } from 'react';\nimport { Track } from 'livekit-client';\nimport { useCallStore } from '@xipkg/calls-store';\nimport { useRoom } from '@xipkg/calls-providers';\nimport { useMaybeLayoutContext } from '@livekit/components-react';\n\nexport const useHandFocus = () => {\n const { raisedHands } = useCallStore();\n const { room } = useRoom();\n const layoutContext = useMaybeLayoutContext();\n\n useEffect(() => {\n if (!room || raisedHands.length === 0 || !layoutContext?.pin.dispatch) return;\n\n const earliestHand = raisedHands.reduce((earliest, current) =>\n current.timestamp < earliest.timestamp ? current : earliest,\n );\n\n const participant = room.getParticipantByIdentity(earliestHand.participantId);\n\n if (participant) {\n const cameraTrack = Array.from(participant.videoTrackPublications.values()).find(\n (track) => track.source === 'camera',\n );\n\n if (cameraTrack) {\n layoutContext.pin.dispatch({\n msg: 'set_pin',\n trackReference: {\n participant,\n source: Track.Source.Camera,\n publication: cameraTrack,\n },\n });\n }\n }\n }, [raisedHands, room, layoutContext]);\n};\n","import { Button } from '@xipkg/button';\nimport { Hand } from '@xipkg/icons';\nimport { cn } from '@xipkg/utils';\nimport { useRaisedHands } from '../hooks';\n\nexport const RaiseHandButton = ({ className }: { className?: string }) => {\n const { toggleHand, isHandRaised, isPending } = useRaisedHands();\n\n return (\n <Button\n size=\"icon\"\n variant=\"none\"\n onClick={toggleHand}\n className={cn(\n 'bg-background-surface hover:bg-background-page text-text-primary relative m-0 rounded-xl p-0',\n !className && 'h-10 w-10',\n isHandRaised && 'text-text-link',\n className,\n )}\n data-umami-event=\"call-raise-hand\"\n data-umami-event-state={isHandRaised ? 'lower' : 'raise'}\n disabled={isPending}\n >\n <Hand className={cn('h-6 w-6', isHandRaised ? 'fill-icon-brand' : 'fill-icon-primary')} />\n </Button>\n );\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xipkg/calls-risehand",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -14,11 +14,11 @@
14
14
  "dependencies": {
15
15
  "@xipkg/icons": "^3.0.21",
16
16
  "@xipkg/button": "4.1.0",
17
- "@xipkg/calls-ui": "^0.1.7",
18
- "@xipkg/calls-utils": "^0.1.7",
19
- "@xipkg/calls-config": "^0.1.7",
20
- "@xipkg/calls-providers": "^0.1.7",
21
- "@xipkg/calls-store": "^0.1.7",
17
+ "@xipkg/calls-ui": "^0.1.9",
18
+ "@xipkg/calls-utils": "^0.1.9",
19
+ "@xipkg/calls-config": "^0.1.9",
20
+ "@xipkg/calls-providers": "^0.1.9",
21
+ "@xipkg/calls-store": "^0.1.9",
22
22
  "@xipkg/utils": "^1.8.0"
23
23
  },
24
24
  "devDependencies": {