From b08b87decf7837313957d598cff559b8ea3965a5 Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Tue, 3 Jun 2025 17:44:43 -0700 Subject: [PATCH] components: add components library (#233) * components: init @streamplace/components * fix build * components: implemented chat! * components: local chat works now! * dump tsup, remove old code * remove example-app for now * build: add knip for checking missing dependencies * fix build * update README * maybe lexicons postinstall? * app: remove old chat stuff from redux * components: migrate replyTo * components: migrate viewers * components: migrate livestream * components: migrate segment * components: migrate renditions * components: tweak handleWebsocketMessages * libraries: pnpm run build as postinstall * build: perhaps make js-lexicons like this? * app: mobile tweaks * websocket: can't send a profile we don't have * websocket --> websocket-consumer --- .vscode/settings.json | 6 +- js/app/components/chat/chat-box.tsx | 65 +-- js/app/components/chat/chat.tsx | 54 +- js/app/components/edit-livestream.tsx | 4 +- js/app/components/livestream/livestream.tsx | 38 +- js/app/components/player/controls.tsx | 11 +- js/app/components/player/player.tsx | 11 +- js/app/components/player/provider.tsx | 111 +--- .../components/provider/provider.shared.tsx | 55 +- js/app/features/bluesky/blueskySlice.tsx | 179 +------ js/app/features/bluesky/blueskyTypes.tsx | 7 +- js/app/features/player/playerSlice.tsx | 485 +----------------- .../features/streamplace/streamplaceSlice.tsx | 4 +- js/app/package.json | 1 + js/app/src/router.tsx | 11 + js/app/src/screens/live-dashboard.tsx | 49 +- js/app/store/store.tsx | 2 +- .../package.json | 3 +- js/components/README.md | 35 ++ js/components/package.json | 38 ++ js/components/src/index.tsx | 4 + .../src/livestream-provider/index.tsx | 37 ++ .../src/livestream-provider/websocket.tsx | 47 ++ js/components/src/livestream-store/chat.tsx | 212 ++++++++ .../src/livestream-store/context.tsx | 10 + js/components/src/livestream-store/index.tsx | 3 + .../src/livestream-store/livestream-state.tsx | 18 + .../src/livestream-store/livestream-store.tsx | 56 ++ .../livestream-store/websocket-consumer.tsx | 62 +++ .../src/streamplace-provider/context.tsx | 10 + .../src/streamplace-provider/index.tsx | 32 ++ .../src/streamplace-provider/poller.tsx | 38 ++ .../src/streamplace-provider/xrpc.tsx | 0 js/components/src/streamplace-store/index.tsx | 2 + .../streamplace-store/streamplace-store.tsx | 71 +++ js/components/src/streamplace-store/user.tsx | 57 ++ js/components/src/streamplace-store/xrpc.tsx | 15 + js/components/tsconfig.json | 9 + js/streamplace/package.json | 8 +- .../agent.tsx => streamplace/src/agent.ts} | 6 +- js/streamplace/src/index.ts | 2 + js/streamplace/src/useful-types.ts | 15 + js/streamplace/tsconfig.json | 12 +- js/streamplace/tsup.config.ts | 18 - knip.json | 8 + package.json | 10 +- pkg/api/websocket.go | 19 + pnpm-lock.yaml | 442 +++++++++++++--- 48 files changed, 1386 insertions(+), 1006 deletions(-) create mode 100644 js/components/README.md create mode 100644 js/components/package.json create mode 100644 js/components/src/index.tsx create mode 100644 js/components/src/livestream-provider/index.tsx create mode 100644 js/components/src/livestream-provider/websocket.tsx create mode 100644 js/components/src/livestream-store/chat.tsx create mode 100644 js/components/src/livestream-store/context.tsx create mode 100644 js/components/src/livestream-store/index.tsx create mode 100644 js/components/src/livestream-store/livestream-state.tsx create mode 100644 js/components/src/livestream-store/livestream-store.tsx create mode 100644 js/components/src/livestream-store/websocket-consumer.tsx create mode 100644 js/components/src/streamplace-provider/context.tsx create mode 100644 js/components/src/streamplace-provider/index.tsx create mode 100644 js/components/src/streamplace-provider/poller.tsx create mode 100644 js/components/src/streamplace-provider/xrpc.tsx create mode 100644 js/components/src/streamplace-store/index.tsx create mode 100644 js/components/src/streamplace-store/streamplace-store.tsx create mode 100644 js/components/src/streamplace-store/user.tsx create mode 100644 js/components/src/streamplace-store/xrpc.tsx create mode 100644 js/components/tsconfig.json rename js/{app/features/bluesky/agent.tsx => streamplace/src/agent.ts} (81%) create mode 100644 js/streamplace/src/useful-types.ts delete mode 100644 js/streamplace/tsup.config.ts create mode 100644 knip.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 8fc95471..d459664c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,5 +16,9 @@ "go.alternateTools": { "customFormatter": "golangci-lint" }, - "go.formatFlags": ["fmt", "--stdin"] + "go.formatFlags": ["fmt", "--stdin"], + "editor.codeActionsOnSave": { + "source.addMissingImports": "explicit", + "source.organizeImports": "explicit" + } } diff --git a/js/app/components/chat/chat-box.tsx b/js/app/components/chat/chat-box.tsx index 822f00d2..7b4c026f 100644 --- a/js/app/components/chat/chat-box.tsx +++ b/js/app/components/chat/chat-box.tsx @@ -1,4 +1,11 @@ import { useNavigation } from "@react-navigation/native"; +import { + useChat, + useCreateChatMessage, + useLivestream, + useReplyToMessage, + useSetReplyToMessage, +} from "@streamplace/components"; import { Palette, SquareArrowOutUpRight, @@ -9,20 +16,13 @@ import { emojiEmitter } from "components/emoji-picker/emoji-emitter"; import { EmojiPicker } from "components/emoji-picker/emoji-picker"; import NameColorPicker from "components/name-color-picker/name-color-picker"; import { - chatMessage, selectChatProfile, selectIsReady, selectUserProfile, } from "features/bluesky/blueskySlice"; import { - addLocalChatMessage, LivestreamViewHydrated, - MessageViewHydrated, - useChat, - usePlayerActions, usePlayerId, - usePlayerLivestream, - useReplyToMessage, } from "features/player/playerSlice"; import { chatWarn, @@ -32,12 +32,13 @@ import { usePreloadEmoji } from "hooks/usePreloadEmoji"; import { useEffect, useRef, useState } from "react"; import { Keyboard } from "react-native"; import { useAppDispatch, useAppSelector } from "store/hooks"; +import { ChatMessageViewHydrated } from "streamplace"; import { Button, Form, Input, isWeb, Text, TextArea, View } from "tamagui"; import EmojiSuggestions, { EmojiSuggestion } from "./emoji-suggestions"; import MentionSuggestions, { MentionSuggestion } from "./mention-suggestions"; const getParticipantSuggestions = ( - chat: MessageViewHydrated[], + chat: ChatMessageViewHydrated[], currentUserDid?: string, ) => { const participants = new Set(); @@ -76,14 +77,14 @@ export default function ChatBox({ const chatProfile = useAppSelector(selectChatProfile); const chatWarned = useAppSelector(selectChatWarned); const loggedOut = isReady && !userProfile; - const livestream = useAppSelector(usePlayerLivestream()); - const chat = useAppSelector(useChat()); + const livestream = useLivestream(); + const chat = useChat(); const textAreaRef = useRef(null); const dispatch = useAppDispatch(); const navigate = useNavigation(); const playerId = usePlayerId(); - const playerActions = usePlayerActions(); - const replyTo = useAppSelector(useReplyToMessage()); + const replyTo = useReplyToMessage(); + const setReplyToMessage = useSetReplyToMessage(); if (isWeb) usePreloadEmoji({ immediate: true }); const [isTextAreaFocused, setIsTextAreaFocused] = useState(false); const [pickerState, setPickerState] = useState({ @@ -309,6 +310,8 @@ export default function ChatBox({ } }, [showSuggestions]); + const createChatMessage = useCreateChatMessage(); + const updateSuggestions = (text: string, cursorPosition: number) => { const atIndex = text.lastIndexOf("@", cursorPosition); @@ -385,39 +388,13 @@ export default function ChatBox({ if (!isWeb) Keyboard.dismiss(); if (!message.length || !livestream || !userProfile) return; - // Add local message - dispatch( - addLocalChatMessage({ - playerId, - message, - ...(replyTo ? { replyTo } : {}), - author: { - did: userProfile.did, - handle: userProfile.handle, - }, - chatProfile: chatProfile?.profile?.color - ? { - color: { - red: chatProfile.profile.color.red, - green: chatProfile.profile.color.green, - blue: chatProfile.profile.color.blue, - }, - } - : undefined, - }), - ); - - // Send to server - dispatch( - chatMessage({ - text: message, - livestream, - ...(replyTo ? { replyTo } : {}), - }), - ); + createChatMessage({ + text: message, + reply: replyTo ? replyTo : undefined, + }); setMessage(""); - playerActions.setReplyToMessage(null); + setReplyToMessage(null); setShowSuggestions(false); if (isWeb && textAreaRef.current) { const textarea = textAreaRef.current as unknown as HTMLTextAreaElement; @@ -493,7 +470,7 @@ export default function ChatBox({