diff --git a/js/app/components/mobile/desktop-ui/kebab.tsx b/js/app/components/mobile/desktop-ui/kebab.tsx index 5573defc..4b741455 100644 --- a/js/app/components/mobile/desktop-ui/kebab.tsx +++ b/js/app/components/mobile/desktop-ui/kebab.tsx @@ -10,10 +10,14 @@ import { DropdownMenuTrigger, ResponsiveDropdownMenuContent, Text, + UpdateStreamTitleDialog, + useCanModerate, + useLivestream, useLivestreamInfo, useLivestreamStore, usePlayerStore, useTheme, + useUpdateLivestreamRecord, } from "@streamplace/components"; import { EllipsisVertical } from "lucide-react-native"; import { useState } from "react"; @@ -35,12 +39,21 @@ interface KebabMenuProps { export function KebabMenu({ dropdownPortalContainer }: KebabMenuProps) { const th = useTheme(); const [isOpen, setIsOpen] = useState(false); + const [showUpdateTitleDialog, setShowUpdateTitleDialog] = useState(false); - const livestream = useLivestreamStore((x) => x.livestream); + const livestreamFromStore = useLivestreamStore((x) => x.livestream); + const livestream = useLivestream(); const setReportModalOpen = usePlayerStore((x) => x.setReportModalOpen); const setReportSubject = usePlayerStore((x) => x.setReportSubject); const { profile } = useLivestreamInfo(); + // Get the streamer's DID from the profile + const streamerDID = profile?.did; + // Check moderation permissions for the current user on this streamer's channel + const modPermissions = useCanModerate(streamerDID); + const { updateLivestream, isLoading: isUpdateTitleLoading } = + useUpdateLivestreamRecord(); + const iconRotate = useAnimatedStyle(() => { return { transform: [ @@ -55,31 +68,52 @@ export function KebabMenu({ dropdownPortalContainer }: KebabMenuProps) { }); return ( - - - - - - - - - - - - - + <> + + + + + + + + {modPermissions.canManageLivestream && ( + + + + )} + + + + + + + + {showUpdateTitleDialog && ( + setShowUpdateTitleDialog(false)} + /> + )} + ); } @@ -141,3 +175,29 @@ function ReportUserItem({ ); } + +function UpdateStreamTitleItem({ + setShowUpdateTitleDialog, + isUpdateTitleLoading, + livestream, +}: { + setShowUpdateTitleDialog: (show: boolean) => void; + isUpdateTitleLoading: boolean; + livestream: any; +}) { + const { onOpenChange } = useRootContext(); + + return ( + { + onOpenChange?.(false); + setShowUpdateTitleDialog(true); + }} + disabled={isUpdateTitleLoading || !livestream} + > + + {isUpdateTitleLoading ? "Updating..." : "Update stream title"} + + + ); +} diff --git a/js/components/src/components/chat/mod-view.tsx b/js/components/src/components/chat/mod-view.tsx index e77b0f72..cdcaffae 100644 --- a/js/components/src/components/chat/mod-view.tsx +++ b/js/components/src/components/chat/mod-view.tsx @@ -5,7 +5,6 @@ import { usePlayerStore } from "../../player-store"; import { useCreateBlockRecord, useCreateHideChatRecord, - useUpdateLivestreamRecord, } from "../../streamplace-store/block"; import { ModerationPermissions, @@ -17,24 +16,19 @@ import { Linking } from "react-native"; import { ChatMessageViewHydrated } from "streamplace"; import { useDeleteChatMessage, - useLivestream, useLivestreamStore, } from "../../livestream-store"; import { useStreamplaceStore } from "../../streamplace-store"; import { formatHandle, formatHandleWithAt } from "../../utils/format-handle"; import { atoms, - Button, - DialogFooter, DropdownMenu, DropdownMenuGroup, DropdownMenuItem, DropdownMenuTrigger, layout, - ResponsiveDialog, ResponsiveDropdownMenuContent, Text, - Textarea, useToast, View, } from "../ui"; @@ -61,10 +55,6 @@ export const ModView = forwardRef(() => { let [messageRemoved, setMessageRemoved] = useState(false); let { createBlock, isLoading: isBlockLoading } = useCreateBlockRecord(); let { createHideChat, isLoading: isHideLoading } = useCreateHideChatRecord(); - let { updateLivestream, isLoading: isUpdateTitleLoading } = - useUpdateLivestreamRecord(); - const livestream = useLivestream(); - const [showUpdateTitleDialog, setShowUpdateTitleDialog] = useState(false); const setReportModalOpen = usePlayerStore((x) => x.setReportModalOpen); const setReportSubject = usePlayerStore((x) => x.setReportSubject); @@ -135,9 +125,6 @@ export const ModView = forwardRef(() => { createHideChat={createHideChat} createBlock={createBlock} toast={toast} - setShowUpdateTitleDialog={setShowUpdateTitleDialog} - isUpdateTitleLoading={isUpdateTitleLoading} - livestream={livestream} setReportModalOpen={setReportModalOpen} setReportSubject={setReportSubject} deleteChatMessage={deleteChatMessage} @@ -145,17 +132,6 @@ export const ModView = forwardRef(() => { )} - - {/* Update Stream Title Dialog - rendered outside dropdown */} - {showUpdateTitleDialog && ( - setShowUpdateTitleDialog(false)} - /> - )} ); }); @@ -173,9 +149,6 @@ interface ModViewContentProps { createHideChat: (uri: string, streamerDID?: string) => Promise; createBlock: (did: string, streamerDID?: string) => Promise; toast: ReturnType; - setShowUpdateTitleDialog: (show: boolean) => void; - isUpdateTitleLoading: boolean; - livestream: any; setReportModalOpen: (open: boolean) => void; setReportSubject: (subject: any) => void; deleteChatMessage: (uri: string) => Promise; @@ -194,9 +167,6 @@ function ModViewContent({ createHideChat, createBlock, toast, - setShowUpdateTitleDialog, - isUpdateTitleLoading, - livestream, setReportModalOpen, setReportSubject, deleteChatMessage, @@ -290,23 +260,6 @@ function ModViewContent({ )} - {modPermissions.canManageLivestream && ( - - { - setShowUpdateTitleDialog(true); - }} - disabled={isUpdateTitleLoading || !livestream} - > - - {isUpdateTitleLoading ? "Updating..." : "Update stream title"} - - - - )} - { @@ -418,161 +371,3 @@ export function ReportButton({ ); } - -interface UpdateStreamTitleDialogProps { - livestream: any; - streamerDID?: string; - updateLivestream: ( - livestreamUri: string, - title: string, - streamerDID?: string, - ) => Promise; - isLoading: boolean; - onClose: () => void; -} - -function UpdateStreamTitleDialog({ - livestream, - streamerDID, - updateLivestream, - isLoading, - onClose, -}: UpdateStreamTitleDialogProps) { - const [title, setTitle] = useState(livestream?.record?.title || ""); - const [error, setError] = useState(null); - const toast = useToast(); - - useEffect(() => { - if (livestream?.record?.title) { - setTitle(livestream.record.title); - } - }, [livestream?.record?.title]); - - const handleUpdate = async () => { - setError(null); - - if (!title.trim()) { - setError("Please enter a stream title"); - return; - } - - if (!livestream?.uri) { - setError("No livestream found"); - return; - } - - try { - await updateLivestream(livestream.uri, title.trim(), streamerDID); - toast.show( - "Stream title updated", - "The stream title has been successfully updated.", - { duration: 3 }, - ); - onClose(); - } catch (err) { - setError( - err instanceof Error ? err.message : "Failed to update stream title", - ); - } - }; - - return ( - { - if (!open) { - onClose(); - setError(null); - setTitle(livestream?.record?.title || ""); - } - }} - title="Update Stream Title" - description="Update the title of the livestream." - size="md" - dismissible={false} - > - - - - Stream Title - -