diff --git a/js/app/components/mobile/desktop-ui/index.ts b/js/app/components/mobile/desktop-ui/index.ts index c156af42..91c372da 100644 --- a/js/app/components/mobile/desktop-ui/index.ts +++ b/js/app/components/mobile/desktop-ui/index.ts @@ -1,4 +1,5 @@ export { BottomControlBar } from "./bottom-controls"; +export { KebabMenu } from "./kebab"; export { LiveBubble } from "./live-bubble"; export { MuteOverlay } from "./mute-overlay"; export { TopControlBar } from "./top-controls"; diff --git a/js/app/components/mobile/desktop-ui/kebab.tsx b/js/app/components/mobile/desktop-ui/kebab.tsx new file mode 100644 index 00000000..8cd66f99 --- /dev/null +++ b/js/app/components/mobile/desktop-ui/kebab.tsx @@ -0,0 +1,143 @@ +import { + AppBskyActorDefs, + ComAtprotoModerationCreateReport, +} from "@atproto/api"; +import { useRootContext } from "@rn-primitives/dropdown-menu"; +import { + DropdownMenu, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuTrigger, + ResponsiveDropdownMenuContent, + Text, + useLivestreamInfo, + useLivestreamStore, + usePlayerStore, + useTheme, +} from "@streamplace/components"; +import { EllipsisVertical } from "lucide-react-native"; +import { useState } from "react"; +import Animated, { + Easing, + useAnimatedStyle, + withTiming, +} from "react-native-reanimated"; +import { LivestreamViewHydrated } from "streamplace"; + +type ReportSubject = + | ComAtprotoModerationCreateReport.InputSchema["subject"] + | null; + +interface KebabMenuProps { + dropdownPortalContainer?: string; +} + +export function KebabMenu({ dropdownPortalContainer }: KebabMenuProps) { + const th = useTheme(); + const [isOpen, setIsOpen] = useState(false); + + const livestream = useLivestreamStore((x) => x.livestream); + const setReportModalOpen = usePlayerStore((x) => x.setReportModalOpen); + const setReportSubject = usePlayerStore((x) => x.setReportSubject); + const { profile } = useLivestreamInfo(); + + const iconRotate = useAnimatedStyle(() => { + return { + transform: [ + { + rotateZ: withTiming(isOpen ? "5deg" : "0deg", { + duration: 200, + easing: Easing.out(Easing.ease), + }), + }, + ], + }; + }); + + return ( + + + + + + + + + + + + + + ); +} + +function ReportStreamItem({ + livestream, + setReportModalOpen, + setReportSubject, +}: { + livestream: LivestreamViewHydrated | null; + setReportModalOpen: (open: boolean) => void; + setReportSubject: (subject: ReportSubject) => void; +}) { + const { onOpenChange } = useRootContext(); + + return ( + { + if (!livestream) return; + onOpenChange?.(false); + setReportModalOpen(true); + setReportSubject({ + $type: "com.atproto.repo.strongRef", + uri: livestream.uri, + cid: livestream.cid, + }); + }} + disabled={!livestream} + > + Report Stream... + + ); +} + +function ReportUserItem({ + profile, + setReportModalOpen, + setReportSubject, +}: { + profile: AppBskyActorDefs.ProfileViewBasic | null; + setReportModalOpen: (open: boolean) => void; + setReportSubject: (subject: ReportSubject) => void; +}) { + const { onOpenChange } = useRootContext(); + + return ( + { + if (!profile?.did) return; + onOpenChange?.(false); + setReportModalOpen(true); + setReportSubject({ + $type: "com.atproto.admin.defs#repoRef", + did: profile.did, + }); + }} + disabled={!profile?.did} + > + Report User... + + ); +}