Live video on the AT Protocol

chore: add and run pretter-plugin-organize-imports (#244)

authored by

Eli Mallon and committed by
GitHub
c4b634aa 7961e88f

+301 -295
+2 -1
.prettierrc
··· 7 7 "proseWrap": "preserve" 8 8 } 9 9 } 10 - ] 10 + ], 11 + "plugins": ["prettier-plugin-organize-imports"] 11 12 }
+7 -3
js/app/components/aqlink.tsx
··· 1 - import { Link, useNavigation } from "@react-navigation/native"; 2 - import { NavigationProp, ParamListBase } from "@react-navigation/native"; 1 + import { 2 + Link, 3 + NavigationProp, 4 + ParamListBase, 5 + useNavigation, 6 + } from "@react-navigation/native"; 3 7 import usePlatform from "hooks/usePlatform"; 8 + import { useEffect } from "react"; 4 9 import { Pressable, StyleProp, ViewStyle } from "react-native"; 5 10 import Loading from "./loading/loading"; 6 - import { useEffect } from "react"; 7 11 8 12 export type LinkParams = { screen: string; params?: Record<string, string> }; 9 13
+18 -18
js/app/components/chat/chat-box.tsx
··· 1 1 import { useNavigation } from "@react-navigation/native"; 2 + import { 3 + Palette, 4 + SquareArrowOutUpRight, 5 + X as XIcon, 6 + } from "@tamagui/lucide-icons"; 2 7 import { useToastController } from "@tamagui/toast"; 8 + import { emojiEmitter } from "components/emoji-picker/emoji-emitter"; 9 + import { EmojiPicker } from "components/emoji-picker/emoji-picker"; 10 + import NameColorPicker from "components/name-color-picker/name-color-picker"; 3 11 import { 4 12 chatMessage, 13 + selectChatProfile, 5 14 selectIsReady, 6 15 selectUserProfile, 7 - selectChatProfile, 8 16 } from "features/bluesky/blueskySlice"; 9 17 import { 18 + addLocalChatMessage, 10 19 LivestreamViewHydrated, 11 - usePlayerLivestream, 12 - addLocalChatMessage, 20 + MessageViewHydrated, 21 + useChat, 22 + usePlayerActions, 13 23 usePlayerId, 24 + usePlayerLivestream, 14 25 useReplyToMessage, 15 - usePlayerActions, 16 - useChat, 17 - MessageViewHydrated, 18 26 } from "features/player/playerSlice"; 19 27 import { 20 28 chatWarn, 21 29 selectChatWarned, 22 30 } from "features/streamplace/streamplaceSlice"; 23 - import { useRef, useState, useEffect } from "react"; 31 + import { usePreloadEmoji } from "hooks/usePreloadEmoji"; 32 + import { useEffect, useRef, useState } from "react"; 24 33 import { Keyboard } from "react-native"; 25 34 import { useAppDispatch, useAppSelector } from "store/hooks"; 26 - import { Button, Form, Input, isWeb, TextArea, View, Text } from "tamagui"; 27 - import { 28 - Palette, 29 - SquareArrowOutUpRight, 30 - X as XIcon, 31 - } from "@tamagui/lucide-icons"; 32 - import NameColorPicker from "components/name-color-picker/name-color-picker"; 35 + import { Button, Form, Input, isWeb, Text, TextArea, View } from "tamagui"; 36 + import EmojiSuggestions, { EmojiSuggestion } from "./emoji-suggestions"; 33 37 import MentionSuggestions, { MentionSuggestion } from "./mention-suggestions"; 34 - import { emojiEmitter } from "components/emoji-picker/emoji-emitter"; 35 - import { EmojiPicker } from "components/emoji-picker/emoji-picker"; 36 - import EmojiSuggestions, { EmojiSuggestion } from "./emoji-suggestions"; 37 - import { usePreloadEmoji } from "hooks/usePreloadEmoji"; 38 38 39 39 const getParticipantSuggestions = ( 40 40 chat: MessageViewHydrated[],
+6 -6
js/app/components/chat/chat.tsx
··· 1 - import { Settings, X, Reply } from "@tamagui/lucide-icons"; 1 + import { Reply, Settings, X } from "@tamagui/lucide-icons"; 2 2 import { 3 3 createBlockRecord, 4 4 selectUserProfile, ··· 6 6 import { 7 7 MessageViewHydrated, 8 8 useChat, 9 - usePlayerLivestream, 10 9 usePlayerActions, 10 + usePlayerLivestream, 11 11 } from "features/player/playerSlice"; 12 + import usePlatform from "hooks/usePlatform"; 12 13 import { useEffect, useRef, useState } from "react"; 13 - import { TouchableOpacity, Linking } from "react-native"; 14 + import { Linking, TouchableOpacity } from "react-native"; 14 15 import { useAppDispatch, useAppSelector } from "store/hooks"; 15 - import usePlatform from "hooks/usePlatform"; 16 16 17 - import { Button, ScrollView, Sheet, Text, useMedia, View } from "tamagui"; 18 17 import { RichText } from "@atproto/api"; 19 18 import { 20 19 isMention, 21 20 Link, 22 21 Mention, 23 22 } from "@atproto/api/dist/client/types/app/bsky/richtext/facet"; 23 + import { $Typed } from "@atproto/api/src/client/util"; 24 + import { Button, ScrollView, Sheet, Text, useMedia, View } from "tamagui"; 24 25 import { RichtextSegment, segmentize } from "../../utils/facet"; 25 - import { $Typed } from "@atproto/api/src/client/util"; 26 26 27 27 export default function Chat({ 28 28 isChatVisible,
+1 -1
js/app/components/chat/emoji-suggestions.tsx
··· 1 - import { ScrollView, Text, View } from "tamagui"; 2 1 import { TouchableOpacity } from "react-native"; 2 + import { ScrollView, Text, View } from "tamagui"; 3 3 4 4 export interface EmojiSuggestion { 5 5 emoji: string;
+1 -1
js/app/components/chat/mention-suggestions.tsx
··· 1 - import { ScrollView, Text, View } from "tamagui"; 2 1 import { TouchableOpacity } from "react-native"; 2 + import { ScrollView, Text, View } from "tamagui"; 3 3 4 4 export interface MentionSuggestion { 5 5 did: string;
+1 -1
js/app/components/countdown.tsx
··· 1 + import * as chrono from "chrono-node"; 1 2 import { useEffect, useState } from "react"; 2 3 import { 3 4 Text, ··· 7 8 useMedia, 8 9 useWindowDimensions, 9 10 } from "tamagui"; 10 - import * as chrono from "chrono-node"; 11 11 12 12 const CountdownBox = styled(View, { 13 13 alignSelf: "center",
+6 -6
js/app/components/create-livestream.tsx
··· 1 - import { useEffect, useState } from "react"; 2 - import { Button, Label, Paragraph, TextArea, View, isWeb } from "tamagui"; 3 1 import { useToastController } from "@tamagui/toast"; 2 + import ThumbnailSelector from "components/thumbnail-selector"; 4 3 import { 5 4 createLivestreamRecord, 6 5 selectNewLivestream, 7 6 selectUserProfile, 8 7 } from "features/bluesky/blueskySlice"; 9 - import { useAppDispatch, useAppSelector } from "store/hooks"; 10 - import { useLiveUser } from "hooks/useLiveUser"; 11 - import ThumbnailSelector from "components/thumbnail-selector"; 12 8 import { useCaptureVideoFrame } from "hooks/useCaptureVideoFrame"; 13 - import { useWindowDimensions, ScrollView } from "react-native"; 9 + import { useLiveUser } from "hooks/useLiveUser"; 10 + import { useEffect, useState } from "react"; 11 + import { ScrollView, useWindowDimensions } from "react-native"; 12 + import { useAppDispatch, useAppSelector } from "store/hooks"; 13 + import { Button, Label, Paragraph, TextArea, View, isWeb } from "tamagui"; 14 14 15 15 export default function CreateLivestream() { 16 16 const dispatch = useAppDispatch();
+1 -2
js/app/components/debug/breakpoint-indicator.tsx
··· 1 - import { Text, View } from "tamagui"; 2 - import { useMedia } from "tamagui"; 1 + import { Text, useMedia, View } from "tamagui"; 3 2 4 3 export default function BreakpointIndicator() { 5 4 const media = useMedia();
+3 -3
js/app/components/edit-livestream.tsx
··· 1 - import { useEffect, useState } from "react"; 2 - import { Button, H3, Label, Paragraph, Text, TextArea, View } from "tamagui"; 3 1 import { useToastController } from "@tamagui/toast"; 4 2 import { 5 3 selectNewLivestream, 6 4 selectUserProfile, 7 5 updateLivestreamRecord, 8 6 } from "features/bluesky/blueskySlice"; 9 - import { useAppDispatch, useAppSelector } from "store/hooks"; 10 7 import { useLiveUser } from "hooks/useLiveUser"; 8 + import { useEffect, useState } from "react"; 11 9 import { ScrollView } from "react-native"; 10 + import { useAppDispatch, useAppSelector } from "store/hooks"; 11 + import { Button, H3, Label, Paragraph, Text, TextArea, View } from "tamagui"; 12 12 13 13 export default function UpdateLivestream({ 14 14 playerId,
+1 -2
js/app/components/emoji-picker/emoji-picker.tsx
··· 1 1 import Picker from "@emoji-mart/react"; 2 + import { isWeb, View } from "tamagui"; 2 3 import { emojiEmitter } from "./emoji-emitter"; 3 - import { View } from "tamagui"; 4 - import { isWeb } from "tamagui"; 5 4 6 5 export type Emoji = { 7 6 native: string;
+1 -1
js/app/components/error/error.tsx
··· 1 - import { View, Text, Button } from "tamagui"; 1 + import { Button, Text, View } from "tamagui"; 2 2 3 3 export default function (props: { onRetry: () => void }) { 4 4 return (
+4 -4
js/app/components/follow-button.tsx
··· 1 + import { Check, Plus } from "@tamagui/lucide-icons"; 1 2 import React, { useEffect, useState } from "react"; 2 - import { useAppDispatch, useAppSelector } from "../store/hooks"; 3 - import { selectStreamplace } from "../features/streamplace/streamplaceSlice"; 3 + import { Button, Text, View } from "tamagui"; 4 4 import { followUser, unfollowUser } from "../features/bluesky/blueskySlice"; 5 - import { Button, View, Text } from "tamagui"; 6 - import { Plus, Check } from "@tamagui/lucide-icons"; 5 + import { selectStreamplace } from "../features/streamplace/streamplaceSlice"; 6 + import { useAppDispatch, useAppSelector } from "../store/hooks"; 7 7 8 8 /** 9 9 * FollowButton component for following/unfollowing a streamer.
+1 -1
js/app/components/get-apps.tsx
··· 1 - import { XStack, Image, Anchor } from "tamagui"; 1 + import { Anchor, Image, XStack } from "tamagui"; 2 2 3 3 const RATIO = 3.39741547176; 4 4 const WIDTH = 200;
+2 -2
js/app/components/home/cards.tsx
··· 1 - import { Image } from "react-native"; 2 - import { Stack, Text, XStack, YStack, isWeb, useMedia, View } from "tamagui"; 3 1 import Viewers from "components/viewers"; 4 2 import useStreamplaceNode from "hooks/useStreamplaceNode"; 3 + import { Image } from "react-native"; 4 + import { isWeb, Stack, Text, useMedia, View, XStack, YStack } from "tamagui"; 5 5 6 6 export type StreamCardSize = "xs" | "sm" | "md" | "lg" | "xl"; 7 7
+3 -3
js/app/components/home/live-dot.tsx
··· 1 - import React, { useEffect } from "react"; 1 + import { useEffect } from "react"; 2 2 import { StyleSheet, View } from "react-native"; 3 3 import Animated, { 4 - useSharedValue, 5 4 useAnimatedStyle, 5 + useSharedValue, 6 + withRepeat, 6 7 withTiming, 7 - withRepeat, 8 8 } from "react-native-reanimated"; 9 9 10 10 export default function LiveDot() {
+1 -1
js/app/components/index.tsx
··· 1 1 export { Countdown } from "./countdown"; 2 2 export { Player } from "./player/player"; 3 - export { Settings } from "./settings/settings"; 4 3 export { default as Provider } from "./provider/provider"; 4 + export { Settings } from "./settings/settings";
+3 -3
js/app/components/live-dashboard/stream-key.tsx
··· 1 1 import { useToastController } from "@tamagui/toast"; 2 + import { Redirect } from "components/aqlink"; 2 3 import Loading from "components/loading/loading"; 3 4 import { 4 5 clearStreamKeyRecord, 5 6 createStreamKeyRecord, 6 - selectUserProfile, 7 7 selectIsReady, 8 + selectUserProfile, 8 9 } from "features/bluesky/blueskySlice"; 9 10 import { useEffect, useState } from "react"; 10 11 import { useAppDispatch, useAppSelector } from "store/hooks"; 11 - import { View, Paragraph, Button, Text } from "tamagui"; 12 - import { Redirect } from "components/aqlink"; 12 + import { Button, Paragraph, Text, View } from "tamagui"; 13 13 const Row = ({ children }: { children: React.ReactNode }) => { 14 14 return ( 15 15 <View w="100%" f={1} fd="row" padding="$4">
+1 -1
js/app/components/live-dashboard/waiting.tsx
··· 1 1 import Loading from "components/loading/loading"; 2 - import { View, Text } from "tamagui"; 2 + import { Text, View } from "tamagui"; 3 3 4 4 export default function Waiting() { 5 5 return (
+8 -8
js/app/components/livestream/livestream.tsx
··· 1 + import { MessageCircleMore, MessageCircleOff } from "@tamagui/lucide-icons"; 2 + import { useToastController } from "@tamagui/toast"; 1 3 import Chat from "components/chat/chat"; 2 4 import ChatBox from "components/chat/chat-box"; 5 + import FollowButton from "components/follow-button"; 3 6 import Loading from "components/loading/loading"; 4 7 import { Player } from "components/player/player"; 5 8 import { PlayerProps } from "components/player/props"; 6 9 import PlayerProvider from "components/player/provider"; 7 10 import Popup from "components/popup"; 8 - import Viewers from "components/viewers"; 9 11 import Timer from "components/timer"; 12 + import Viewers from "components/viewers"; 13 + import { useFullscreen } from "contexts/FullscreenContext"; 14 + import { getProfile, selectProfiles } from "features/bluesky/blueskySlice"; 10 15 import { usePlayer } from "features/player/playerSlice"; 11 16 import { 12 17 selectTelemetry, ··· 17 22 import { useCallback, useEffect, useState } from "react"; 18 23 import { 19 24 LayoutChangeEvent, 25 + Linking, 20 26 View as RNView, 21 27 SafeAreaView, 22 - Linking, 23 28 } from "react-native"; 29 + import storage from "storage"; 24 30 import { useAppDispatch, useAppSelector } from "store/hooks"; 25 31 import { 26 32 Button, ··· 31 37 useWindowDimensions, 32 38 View, 33 39 } from "tamagui"; 34 - import { MessageCircleOff, MessageCircleMore } from "@tamagui/lucide-icons"; 35 - import FollowButton from "components/follow-button"; 36 - import { useToastController } from "@tamagui/toast"; 37 - import { selectProfiles, getProfile } from "features/bluesky/blueskySlice"; 38 - import storage from "storage"; 39 - import { useFullscreen } from "contexts/FullscreenContext"; 40 40 41 41 export default function Livestream(props: Partial<PlayerProps>) { 42 42 return (
+1 -1
js/app/components/loading/loading.tsx
··· 1 - import { View, Spinner as TamaguiSpinner } from "tamagui"; 1 + import { Spinner as TamaguiSpinner, View } from "tamagui"; 2 2 3 3 export default function () { 4 4 return (
+4 -5
js/app/components/login/login.tsx
··· 1 - import { AtpBaseClient } from "streamplace"; 1 + import { useNavigation } from "@react-navigation/native"; 2 + import { CircleHelp } from "@tamagui/lucide-icons"; 3 + import { useToastController } from "@tamagui/toast"; 4 + import Loading from "components/loading/loading"; 2 5 import NameColorPicker from "components/name-color-picker/name-color-picker"; 3 6 import { 4 7 login, ··· 21 24 XStack, 22 25 YStack, 23 26 } from "tamagui"; 24 - import Loading from "components/loading/loading"; 25 - import { useToastController } from "@tamagui/toast"; 26 - import { useNavigation } from "@react-navigation/native"; 27 - import { CircleHelp } from "@tamagui/lucide-icons"; 28 27 29 28 export default function Login() { 30 29 const dispatch = useAppDispatch();
+4 -4
js/app/components/login/signup.tsx
··· 1 + import { useNavigation } from "@react-navigation/native"; 2 + import { CircleHelp } from "@tamagui/lucide-icons"; 3 + import { useToastController } from "@tamagui/toast"; 4 + import Loading from "components/loading/loading"; 1 5 import { 2 6 login, 3 7 selectIsReady, ··· 17 21 XStack, 18 22 YStack, 19 23 } from "tamagui"; 20 - import Loading from "components/loading/loading"; 21 - import { useToastController } from "@tamagui/toast"; 22 - import { useNavigation } from "@react-navigation/native"; 23 - import { CircleHelp } from "@tamagui/lucide-icons"; 24 24 25 25 export default function SignUp() { 26 26 const dispatch = useAppDispatch();
+1 -1
js/app/components/name-color-picker/name-color-picker.tsx
··· 5 5 selectChatProfile, 6 6 selectUserProfile, 7 7 } from "features/bluesky/blueskySlice"; 8 - import { PlaceStreamChatProfile } from "streamplace"; 9 8 import { useEffect, useState } from "react"; 10 9 import { Keyboard } from "react-native"; 11 10 import ColorPicker, { ··· 15 14 Swatches, 16 15 } from "reanimated-color-picker"; 17 16 import { useAppDispatch, useAppSelector } from "store/hooks"; 17 + import { PlaceStreamChatProfile } from "streamplace"; 18 18 import { Button, H3, isWeb, Sheet, useTheme, View } from "tamagui"; 19 19 20 20 /**
+23 -23
js/app/components/player/controls.tsx
··· 13 13 Volume2, 14 14 VolumeX, 15 15 } from "@tamagui/lucide-icons"; 16 + import { Countdown } from "components/countdown"; 17 + import Loading from "components/loading/loading"; 18 + import Viewers from "components/viewers"; 19 + import { 20 + usePlayer, 21 + usePlayerActions, 22 + usePlayerProtocol, 23 + usePlayerRenditions, 24 + usePlayerSegment, 25 + usePlayerSelectedRendition, 26 + } from "features/player/playerSlice"; 27 + import { userMute } from "features/streamplace/streamplaceSlice"; 28 + import usePlatform from "hooks/usePlatform"; 16 29 import { 17 30 Dispatch, 18 31 Fragment, 32 + useCallback, 19 33 useEffect, 20 34 useRef, 21 35 useState, 22 - useCallback, 23 36 } from "react"; 24 - import { Animated, Pressable, Easing } from "react-native"; 37 + import { Animated, Easing, Pressable } from "react-native"; 38 + import { useAppDispatch, useAppSelector } from "store/hooks"; 39 + import { PlaceStreamDefs } from "streamplace"; 25 40 import { 41 + Adapt, 26 42 Button, 43 + H1, 27 44 H3, 45 + H5, 46 + Image, 28 47 ListItem, 48 + Paragraph, 29 49 Popover, 30 50 Separator, 51 + Slider, 31 52 Text, 32 53 useMedia, 33 54 View, 34 55 XStack, 35 56 YGroup, 36 - H1, 37 - H5, 38 - Paragraph, 39 - Slider, 40 - Adapt, 41 57 } from "tamagui"; 42 58 import { PlayerProps, PROTOCOL_HLS, PROTOCOL_WEBRTC } from "./props"; 43 - import { 44 - usePlayer, 45 - usePlayerActions, 46 - usePlayerProtocol, 47 - usePlayerRenditions, 48 - usePlayerSegment, 49 - usePlayerSelectedRendition, 50 - } from "features/player/playerSlice"; 51 - import { useAppDispatch, useAppSelector } from "store/hooks"; 52 - import Loading from "components/loading/loading"; 53 - import Viewers from "components/viewers"; 54 - import { userMute } from "features/streamplace/streamplaceSlice"; 55 - import { Countdown } from "components/countdown"; 56 - import { PlaceStreamDefs } from "streamplace"; 57 - import usePlatform from "hooks/usePlatform"; 58 - import { Image } from "tamagui"; 59 59 60 60 const Bar = (props) => ( 61 61 <XStack
+6 -11
js/app/components/player/fullscreen.native.tsx
··· 1 + import { useNavigation } from "@react-navigation/native"; 1 2 import { VideoView } from "expo-video"; 2 - import { useRef, useEffect, useState } from "react"; 3 - import { StatusBar, Dimensions, StyleSheet, BackHandler } from "react-native"; 3 + import { usePlayerProtocol } from "features/player/playerSlice"; 4 + import { useEffect, useRef, useState } from "react"; 5 + import { BackHandler, Dimensions, StatusBar, StyleSheet } from "react-native"; 4 6 import { useSafeAreaInsets } from "react-native-safe-area-context"; 5 - import { useNavigation } from "@react-navigation/native"; 7 + import { useAppSelector } from "store/hooks"; 6 8 import { View } from "tamagui"; 7 9 import Controls from "./controls"; 8 10 import PlayerLoading from "./player-loading"; 9 11 import { PlayerProps, PROTOCOL_WEBRTC } from "./props"; 12 + import VideoRetry from "./video-retry"; 10 13 import Video from "./video.native"; 11 - import VideoRetry from "./video-retry"; 12 - import { usePlayerProtocol } from "features/player/playerSlice"; 13 - import { useAppSelector } from "store/hooks"; 14 - import { useDispatch } from "react-redux"; 15 - import { 16 - setSidebarHidden, 17 - setSidebarUnhidden, 18 - } from "features/base/sidebarSlice"; 19 14 20 15 // Standard 16:9 video aspect ratio 21 16 const VIDEO_ASPECT_RATIO = 16 / 9;
+1 -1
js/app/components/player/player-loading.tsx
··· 1 1 import { Play } from "@tamagui/lucide-icons"; 2 + import KeepAwake from "components/keep-awake"; 2 3 import { Spinner } from "components/loading/loading"; 3 4 import { useTheme, View } from "tamagui"; 4 5 import { PlayerProps, PlayerStatus } from "./props"; 5 - import KeepAwake from "components/keep-awake"; 6 6 7 7 export default function PlayerLoading(props: PlayerProps) { 8 8 const theme = useTheme();
+9 -9
js/app/components/player/player.tsx
··· 1 - import useStreamplaceNode from "hooks/useStreamplaceNode"; 1 + import { 2 + usePlayerRenditions, 3 + usePlayerSegment, 4 + usePlayerSelectedRendition, 5 + } from "features/player/playerSlice"; 6 + import { selectUserMuted } from "features/streamplace/streamplaceSlice"; 2 7 import usePlatform from "hooks/usePlatform"; 8 + import useStreamplaceNode from "hooks/useStreamplaceNode"; 3 9 import { uuidv7 } from "hooks/uuid"; 4 10 import { useEffect, useMemo, useState } from "react"; 5 - import { Text, useMedia, View } from "tamagui"; 11 + import { useAppSelector } from "store/hooks"; 12 + import { Text, View } from "tamagui"; 6 13 import Fullscreen from "./fullscreen"; 7 14 import { 8 15 IngestMediaSource, ··· 12 19 PlayerStatusTracker, 13 20 } from "./props"; 14 21 import PlayerProvider from "./provider"; 15 - import { selectUserMuted } from "features/streamplace/streamplaceSlice"; 16 - import { useAppSelector } from "store/hooks"; 17 - import { 18 - usePlayerRenditions, 19 - usePlayerSegment, 20 - usePlayerSelectedRendition, 21 - } from "features/player/playerSlice"; 22 22 23 23 const HIDE_CONTROLS_AFTER = 2000; 24 24 const OFFLINE_THRESHOLD = 10000;
-1
js/app/components/player/props.tsx
··· 1 - import { VideoView } from "expo-video"; 2 1 import { PlaceStreamDefs } from "streamplace"; 3 2 4 3 export enum IngestMediaSource {
+1 -1
js/app/components/player/shared.tsx
··· 1 1 import useStreamplaceNode from "hooks/useStreamplaceNode"; 2 + import { useMemo } from "react"; 2 3 import { 3 4 PlayerProps, 4 5 PROTOCOL_HLS, ··· 6 7 PROTOCOL_PROGRESSIVE_WEBM, 7 8 PROTOCOL_WEBRTC, 8 9 } from "./props"; 9 - import { useMemo } from "react"; 10 10 11 11 const protocolSuffixes = { 12 12 m3u8: PROTOCOL_HLS,
+2 -2
js/app/components/player/video-retry.tsx
··· 1 - import React, { useEffect, useState } from "react"; 2 - import { PlayerProps, PlayerStatus } from "./props"; 3 1 import { usePlayerSelectedRendition } from "features/player/playerSlice"; 2 + import React, { useEffect, useState } from "react"; 4 3 import { useAppSelector } from "store/hooks"; 4 + import { PlayerProps, PlayerStatus } from "./props"; 5 5 6 6 export default function VideoRetry( 7 7 props: PlayerProps & { children: React.ReactNode },
+3 -4
js/app/components/player/video.native.tsx
··· 1 1 import { useVideoPlayer, VideoPlayerEvents, VideoView } from "expo-video"; 2 + import { usePlayerProtocol } from "features/player/playerSlice"; 2 3 import React, { useEffect } from "react"; 3 - import { RTCView } from "react-native-webrtc"; 4 + import { MediaStream, RTCView } from "react-native-webrtc"; 5 + import { useAppSelector } from "store/hooks"; 4 6 import { View } from "tamagui"; 5 7 import { PlayerProps, PlayerStatus, PROTOCOL_WEBRTC } from "./props"; 6 8 import { srcToUrl } from "./shared"; 7 9 import useWebRTC from "./use-webrtc"; 8 - import { MediaStream } from "react-native-webrtc"; 9 - import { usePlayerProtocol } from "features/player/playerSlice"; 10 - import { useAppSelector } from "store/hooks"; 11 10 12 11 // export function Player() { 13 12 // return <View f={1}></View>;
-1
js/app/components/player/video.tsx
··· 6 6 import { 7 7 ForwardedRef, 8 8 forwardRef, 9 - RefObject, 10 9 useCallback, 11 10 useEffect, 12 11 useRef,
+1 -1
js/app/components/popup.tsx
··· 1 1 import { X } from "@tamagui/lucide-icons"; 2 - import { View, Button, ViewProps } from "tamagui"; 2 + import { Button, View, ViewProps } from "tamagui"; 3 3 4 4 export default function Popup({ 5 5 onClose,
+3 -3
js/app/components/provider/provider.shared.tsx
··· 5 5 } from "@react-navigation/native"; 6 6 import { ToastProvider, ToastViewport } from "@tamagui/toast"; 7 7 import { useFonts } from "expo-font"; 8 + import BlueskyProvider from "features/bluesky/blueskyProvider"; 8 9 import StreamplaceProvider from "features/streamplace/streamplaceProvider"; 9 10 import React from "react"; 11 + import { Provider as ReduxProvider } from "react-redux"; 12 + import { store } from "store/store"; 10 13 import { PortalProvider, TamaguiProvider } from "tamagui"; 11 14 import config from "tamagui.config"; 12 15 import { CurrentToast } from "./CurrentToast"; 13 - import { Provider as ReduxProvider } from "react-redux"; 14 - import BlueskyProvider from "features/bluesky/blueskyProvider"; 15 - import { store } from "store/store"; 16 16 export default function Provider({ 17 17 children, 18 18 linking,
+2 -2
js/app/components/provider/provider.tsx
··· 2 2 import "@rainbow-me/rainbowkit/styles.css"; 3 3 4 4 import { LinkingOptions } from "@react-navigation/native"; 5 - import SharedProvider from "./provider.shared"; 6 - import React, { useEffect } from "react"; 7 5 import { WalletProvider } from "hooks/useWallet"; 6 + import React, { useEffect } from "react"; 7 + import SharedProvider from "./provider.shared"; 8 8 9 9 export default function Provider({ 10 10 children,
+4 -4
js/app/components/settings/settings.tsx
··· 1 1 import { 2 + DEFAULT_URL, 2 3 selectTelemetry, 3 4 setURL, 4 5 telemetryOpt, 5 - DEFAULT_URL, 6 6 } from "features/streamplace/streamplaceSlice"; 7 7 import useStreamplaceNode from "hooks/useStreamplaceNode"; 8 - import { useState, useEffect } from "react"; 8 + import { useEffect, useState } from "react"; 9 + import { Switch } from "react-native"; 9 10 import { useAppDispatch, useAppSelector } from "store/hooks"; 10 - import { Button, Form, H3, Input, View, XStack, Text, isWeb } from "tamagui"; 11 + import { Button, Form, H3, Input, Text, View, XStack, isWeb } from "tamagui"; 11 12 import { Updates } from "./updates"; 12 - import { Switch } from "react-native"; 13 13 14 14 export function Settings() { 15 15 const dispatch = useAppDispatch();
+2 -2
js/app/components/settings/updates.native.tsx
··· 1 + import { ToastViewport, useToastController } from "@tamagui/toast"; 1 2 import * as ExpoUpdates from "expo-updates"; 2 3 import { useEffect, useState } from "react"; 4 + import { Platform } from "react-native"; 3 5 import { Button, H2, H5, Text, View } from "tamagui"; 4 - import { ToastViewport, useToastController } from "@tamagui/toast"; 5 6 import pkg from "../../package.json"; 6 - import { Platform } from "react-native"; 7 7 8 8 export function Updates() { 9 9 const version = pkg.version;
+1 -1
js/app/components/settings/updates.tsx
··· 1 + import { H2, View } from "tamagui"; 1 2 import pkg from "../../package.json"; 2 - import { View, H2 } from "tamagui"; 3 3 4 4 // maybe someday some PWA update stuff will live here 5 5 export function Updates() {
+2 -2
js/app/components/sidebar/sidebar-item.tsx
··· 1 1 import { FileQuestion } from "@tamagui/lucide-icons"; 2 - import { Text, View, AnimatePresence } from "tamagui"; 3 - import { Pressable } from "react-native-gesture-handler"; 4 2 import { ReactNode, useState } from "react"; 5 3 import { PressableStateCallbackType, StyleProp, ViewStyle } from "react-native"; 4 + import { Pressable } from "react-native-gesture-handler"; 5 + import { AnimatePresence, Text, View } from "tamagui"; 6 6 7 7 export default function SidebarItem({ 8 8 icon,
+6 -6
js/app/components/sidebar/sidebar.tsx
··· 1 - import { YStack, styled, Text, View, Image } from "tamagui"; 2 - import { SharedValue, useAnimatedStyle } from "react-native-reanimated"; 3 - import SidebarItem from "./sidebar-item"; 1 + import { DrawerNavigationOptions } from "@react-navigation/drawer"; 2 + import { DrawerDescriptorMap } from "@react-navigation/drawer/lib/typescript/src/types"; 4 3 import { 5 4 CommonActions, 6 5 DrawerNavigationState, 7 6 ParamListBase, 8 7 } from "@react-navigation/native"; 9 - import { DrawerNavigationOptions } from "@react-navigation/drawer"; 10 - import { DrawerDescriptorMap } from "@react-navigation/drawer/lib/typescript/src/types"; 8 + import { FileQuestion } from "@tamagui/lucide-icons"; 11 9 import { Platform } from "react-native"; 12 - import { FileQuestion } from "@tamagui/lucide-icons"; 10 + import { SharedValue, useAnimatedStyle } from "react-native-reanimated"; 11 + import { Image, styled, Text, View, YStack } from "tamagui"; 12 + import SidebarItem from "./sidebar-item"; 13 13 14 14 const AnimatedYStack = styled(YStack, { 15 15 name: "AnimatedYStack",
+1 -1
js/app/components/thumbnail-selector/thumbnail-selector.tsx
··· 1 + import { Camera, Image as ImageIcon, X } from "@tamagui/lucide-icons"; 1 2 import { useCallback, useEffect, useRef, useState } from "react"; 2 3 import { Button, Image, Text, View, isWeb } from "tamagui"; 3 - import { Camera, Image as ImageIcon, X } from "@tamagui/lucide-icons"; 4 4 import { ThumbnailSelectorProps } from "./shared"; 5 5 6 6 export default function ThumbnailSelector({
+2 -2
js/app/components/timer.tsx
··· 1 - import { View, Text } from "tamagui"; 2 - import { useState, useEffect } from "react"; 1 + import { useEffect, useState } from "react"; 2 + import { Text, View } from "tamagui"; 3 3 4 4 export default function Timer({ start }: { start: string | Date }) { 5 5 const [elapsedTime, setElapsedTime] = useState(0);
+1 -1
js/app/components/ui/button-selector.tsx
··· 1 - import { YStack, XStack, Text, Button, View, YStackProps } from "tamagui"; 1 + import { Button, Text, XStack, YStack, YStackProps } from "tamagui"; 2 2 3 3 export default function ButtonSelector({ 4 4 text,
+1 -1
js/app/components/viewers.tsx
··· 1 1 import { User } from "@tamagui/lucide-icons"; 2 - import { View, Text } from "tamagui"; 2 + import { Text, View } from "tamagui"; 3 3 4 4 export default function Viewers({ viewers }: { viewers: number }) { 5 5 return (
+2 -2
js/app/contexts/FullscreenContext.tsx
··· 4 4 } from "features/base/sidebarSlice"; 5 5 import { 6 6 createContext, 7 - useContext, 8 - useState, 9 7 ReactNode, 8 + useContext, 10 9 useEffect, 10 + useState, 11 11 } from "react"; 12 12 import { useDispatch } from "react-redux"; 13 13
+1 -1
js/app/contexts/VideoElementContext.tsx
··· 1 - import React, { createContext, useContext, ReactNode } from "react"; 1 + import { createContext, ReactNode, useContext } from "react"; 2 2 3 3 interface VideoElementContextType { 4 4 videoElement: HTMLVideoElement | null;
+2 -3
js/app/features/bluesky/agent.tsx
··· 1 1 import { Agent } from "@atproto/api"; 2 + import { schemas as parentSchemas } from "@atproto/api/dist/client/lexicons"; 2 3 import { SessionManager } from "@atproto/api/dist/session-manager"; 3 - import { PlaceNS } from "streamplace"; 4 - import { schemas as parentSchemas } from "@atproto/api/dist/client/lexicons"; 5 - import { schemas as appSchemas } from "streamplace"; 6 4 import { Lexicons } from "@atproto/lexicon"; 5 + import { schemas as appSchemas, PlaceNS } from "streamplace"; 7 6 export class StreamplaceAgent extends Agent { 8 7 place = new PlaceNS(this); 9 8 lex: Lexicons;
-1
js/app/features/bluesky/blueskyProvider.tsx
··· 6 6 getProfile, 7 7 loadOAuthClient, 8 8 oauthCallback, 9 - oauthError, 10 9 selectIsReady, 11 10 selectOAuthSession, 12 11 selectUserProfile,
+2 -2
js/app/features/bluesky/blueskyTypes.tsx
··· 1 - import { OAuthSession } from "@streamplace/atproto-oauth-client-react-native"; 2 1 import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 2 + import { OAuthSession } from "@streamplace/atproto-oauth-client-react-native"; 3 3 import { StreamKey } from "features/base/baseSlice"; 4 4 import { PlaceStreamChatProfile, PlaceStreamLivestream } from "streamplace"; 5 - import { StreamplaceOAuthClient } from "./oauthClient"; 6 5 import { StreamplaceAgent } from "./agent"; 6 + import { StreamplaceOAuthClient } from "./oauthClient"; 7 7 8 8 type NewLivestream = { 9 9 loading: boolean;
+5 -5
js/app/features/platform/platformSlice.native.tsx
··· 1 + import messaging from "@react-native-firebase/messaging"; 1 2 import { openAuthSessionAsync } from "expo-web-browser"; 3 + import { oauthCallback } from "features/bluesky/blueskySlice"; 4 + import { BlueskyState } from "features/bluesky/blueskyTypes"; 5 + import { PermissionsAndroid, Platform } from "react-native"; 2 6 import { createAppSlice } from "../../hooks/createSlice"; 3 - import messaging from "@react-native-firebase/messaging"; 4 - import { Platform, PermissionsAndroid } from "react-native"; 5 7 import { 6 8 initialState, 9 + PlatformState, 7 10 RegisterNotificationTokenBody, 8 - PlatformState, 9 11 } from "./shared"; 10 - import { BlueskyState } from "features/bluesky/blueskyTypes"; 11 - import { oauthCallback } from "features/bluesky/blueskySlice"; 12 12 13 13 const checkApplicationPermission = async () => { 14 14 if (Platform.OS === "android") {
+5 -5
js/app/features/player/playerSlice.tsx
··· 4 4 import { StreamplaceState } from "features/streamplace/streamplaceSlice"; 5 5 import { uuidv7 } from "hooks/uuid"; 6 6 import { createContext, useContext } from "react"; 7 - import { createAppSlice } from "../../hooks/createSlice"; 7 + import { useAppDispatch } from "store/hooks"; 8 8 import { 9 - PlaceStreamSegment, 10 - PlaceStreamLivestream, 11 - PlaceStreamDefs, 12 9 PlaceStreamChatDefs, 13 10 PlaceStreamChatMessage, 11 + PlaceStreamDefs, 12 + PlaceStreamLivestream, 13 + PlaceStreamSegment, 14 14 } from "streamplace"; 15 - import { useAppDispatch } from "store/hooks"; 15 + import { createAppSlice } from "../../hooks/createSlice"; 16 16 17 17 export interface PlayerContextType { 18 18 playerId: string | null;
+3 -3
js/app/features/streamplace/streamplaceProvider.tsx
··· 1 + import Loading from "components/loading/loading"; 1 2 import { createContext, useEffect } from "react"; 2 - import { DEFAULT_URL, initialize, selectStreamplace } from "./streamplaceSlice"; 3 3 import { useAppDispatch, useAppSelector } from "store/hooks"; 4 - import Loading from "components/loading/loading"; 5 - import { View, Text } from "tamagui"; 4 + import { Text, View } from "tamagui"; 5 + import { DEFAULT_URL, initialize, selectStreamplace } from "./streamplaceSlice"; 6 6 7 7 export const StreamplaceContext = createContext({ 8 8 url: DEFAULT_URL,
+2 -3
js/app/features/streamplace/streamplaceSlice.tsx
··· 1 + import { BlueskyState } from "features/bluesky/blueskyTypes"; 2 + import { PlaceStreamLivestream, PlaceStreamSegment } from "streamplace"; 1 3 import { isWeb } from "tamagui"; 2 4 import { createAppSlice } from "../../hooks/createSlice"; 3 5 import Storage from "../../storage"; 4 - import { BlueskyState } from "features/bluesky/blueskyTypes"; 5 - import { PlaceStreamSegment, PlaceStreamLivestream } from "streamplace"; 6 - import { StreamplaceAgent } from "features/bluesky/agent"; 7 6 8 7 let DEFAULT_URL = process.env.EXPO_PUBLIC_STREAMPLACE_URL as string; 9 8 if (isWeb && process.env.EXPO_PUBLIC_WEB_TRY_LOCAL === "true") {
+3 -3
js/app/hooks/useAvatars.tsx
··· 1 - import { useEffect, useMemo } from "react"; 2 1 import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 3 - import { useAppSelector, useAppDispatch } from "store/hooks"; 4 2 import { 5 - selectCachedProfiles, 6 3 getProfiles, 4 + selectCachedProfiles, 7 5 } from "features/bluesky/blueskySlice"; 6 + import { useEffect, useMemo } from "react"; 7 + import { useAppDispatch, useAppSelector } from "store/hooks"; 8 8 9 9 // Hack: Easy way to cache and get avatars 10 10 export default function useAvatars(dids: string[]) {
+1 -1
js/app/hooks/useCaptureVideoFrame.ts
··· 1 + import { useVideoElement } from "contexts/VideoElementContext"; 1 2 import { useCallback } from "react"; 2 3 import { isWeb } from "tamagui"; 3 - import { useVideoElement } from "contexts/VideoElementContext"; 4 4 import { captureVideoFrame } from "utils/videoCapture"; 5 5 6 6 /**
+1 -1
js/app/hooks/usePreloadEmoji.ts
··· 1 - import React from "react"; 2 1 import { init } from "emoji-mart"; 2 + import React from "react"; 3 3 import { isWeb } from "tamagui"; 4 4 5 5 let loadRequested = false;
+3 -3
js/app/hooks/useSidebarControl.tsx
··· 4 4 useSharedValue, 5 5 withTiming, 6 6 } from "react-native-reanimated"; 7 - import { useWindowDimensions } from "tamagui"; 8 7 import { useDispatch, useSelector } from "react-redux"; 8 + import { useWindowDimensions } from "tamagui"; 9 9 10 10 import { 11 - toggleSidebar, 12 11 selectIsSidebarCollapsed, 12 + selectIsSidebarHidden, 13 13 selectSidebarTargetWidth, 14 - selectIsSidebarHidden, 14 + toggleSidebar, 15 15 } from "../features/base/sidebarSlice"; 16 16 import { RootState } from "../store/store"; 17 17
+1 -1
js/app/hooks/useWallet.shared.tsx
··· 1 - import { TypedData, SignTypedDataParameters } from "viem"; 1 + import { SignTypedDataParameters, TypedData } from "viem"; 2 2 3 3 export type SignTypedDataFn = < 4 4 const typedData extends TypedData | { [key: string]: unknown },
+6 -7
js/app/hooks/useWallet.tsx
··· 1 - import { createContext, useContext, useEffect, useState } from "react"; 2 - import { createWalletClient, http } from "viem"; 3 - import usePlatform from "./usePlatform"; 4 1 import { 5 2 ConnectButton, 6 3 getDefaultConfig, 7 4 RainbowKitProvider, 8 5 } from "@rainbow-me/rainbowkit"; 9 - import React from "react"; 6 + import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 7 + import React, { createContext, useContext, useEffect, useState } from "react"; 10 8 import { View as RNView, View } from "react-native"; 9 + import { Paragraph } from "tamagui"; 10 + import { createWalletClient, http } from "viem"; 11 11 import { privateKeyToAccount } from "viem/accounts"; 12 12 import { arbitrum, base, mainnet, optimism, polygon } from "viem/chains"; 13 - import { Paragraph } from "tamagui"; 14 - import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 13 + import usePlatform from "./usePlatform"; 15 14 import { 16 15 notActiveWallet, 16 + SignTypedDataFn, 17 17 WalletStuff, 18 - SignTypedDataFn, 19 18 } from "./useWallet.shared"; 20 19 21 20 const WalletContext = createContext(notActiveWallet);
+1 -1
js/app/src/entrypoint.tsx
··· 2 2 // hot module reloading works properly on web. 3 3 4 4 import "@expo/metro-runtime"; 5 + import { registerRootComponent } from "expo"; 5 6 import "./polyfills"; 6 7 import Router from "./router"; 7 - import { registerRootComponent } from "expo"; 8 8 9 9 registerRootComponent(Router);
+33 -33
js/app/src/router.tsx
··· 16 16 import { createNativeStackNavigator } from "@react-navigation/native-stack"; 17 17 import { 18 18 ArrowLeft, 19 + Book, 20 + Download, 21 + ExternalLink, 19 22 Home, 20 23 LogIn, 21 24 Menu, 25 + Notebook, 26 + PanelLeftClose, 27 + PanelLeftOpen, 22 28 Settings as SettingsIcon, 23 - User, 24 29 ShieldQuestion, 25 - Download, 30 + User, 26 31 Video, 27 - PanelLeftOpen, 28 - PanelLeftClose, 29 - Book, 30 - ExternalLink, 31 - Notebook, 32 32 } from "@tamagui/lucide-icons"; 33 + import { useToastController } from "@tamagui/toast"; 33 34 import { Provider, Settings } from "components"; 34 35 import AQLink from "components/aqlink"; 35 36 import Login from "components/login/login"; 37 + import Popup from "components/popup"; 38 + import Sidebar, { ExternalDrawerItem } from "components/sidebar/sidebar"; 39 + import { hydrate, selectHydrated } from "features/base/baseSlice"; 36 40 import { selectUserProfile } from "features/bluesky/blueskySlice"; 41 + import { 42 + clearNotification, 43 + initPushNotifications, 44 + registerNotificationToken, 45 + selectNotificationDestination, 46 + selectNotificationToken, 47 + } from "features/platform/platformSlice.native"; 48 + import { 49 + pollMySegments, 50 + pollSegments, 51 + } from "features/streamplace/streamplaceSlice"; 52 + import { useLiveUser } from "hooks/useLiveUser"; 37 53 import usePlatform from "hooks/usePlatform"; 54 + import { useSidebarControl } from "hooks/useSidebarControl"; 38 55 import { ReactElement, useEffect, useState } from "react"; 39 56 import { 40 57 ImageBackground, ··· 45 62 StatusBar, 46 63 } from "react-native"; 47 64 import { useAppDispatch, useAppSelector } from "store/hooks"; 48 - import { useTheme, Text, View, H3 } from "tamagui"; 49 - import AppReturnScreen from "./screens/app-return"; 50 - import MultiScreen from "./screens/multi"; 51 - import StreamScreen from "./screens/stream"; 52 - import SupportScreen from "./screens/support"; 65 + import { H3, Text, useTheme, View } from "tamagui"; 53 66 import AboutScreen from "./screens/about"; 54 - import DownloadScreen from "./screens/download"; 55 - import { hydrate, selectHydrated } from "features/base/baseSlice"; 67 + import AppReturnScreen from "./screens/app-return"; 56 68 import AVSyncScreen from "./screens/av-sync"; 57 - import { 58 - clearNotification, 59 - initPushNotifications, 60 - registerNotificationToken, 61 - selectNotificationDestination, 62 - selectNotificationToken, 63 - } from "features/platform/platformSlice.native"; 64 - import { 65 - pollMySegments, 66 - pollSegments, 67 - } from "features/streamplace/streamplaceSlice"; 68 - import { useLiveUser } from "hooks/useLiveUser"; 69 - import { useToastController } from "@tamagui/toast"; 70 - import LiveDashboard from "./screens/live-dashboard"; 71 - import Popup from "components/popup"; 72 69 import PopoutChat from "./screens/chat-popout"; 70 + import DownloadScreen from "./screens/download"; 73 71 import EmbedScreen from "./screens/embed"; 74 - import { useSidebarControl } from "hooks/useSidebarControl"; 75 - import Sidebar, { ExternalDrawerItem } from "components/sidebar/sidebar"; 72 + import LiveDashboard from "./screens/live-dashboard"; 73 + import MultiScreen from "./screens/multi"; 74 + import StreamScreen from "./screens/stream"; 75 + import SupportScreen from "./screens/support"; 76 76 77 77 // probabl should move this 78 - import { store } from "store/store"; 78 + import SignUp from "components/login/signup"; 79 79 import { loadStateFromStorage } from "features/base/sidebarSlice"; 80 + import { store } from "store/store"; 80 81 import HomeScreen from "./screens/home"; 81 - import SignUp from "components/login/signup"; 82 82 83 83 store.dispatch(loadStateFromStorage()); 84 84
+1 -1
js/app/src/screens/about.tsx
··· 1 - import { H4 as TamaguiH4, Paragraph, View, Anchor } from "tamagui"; 1 + import { Anchor, Paragraph, H4 as TamaguiH4, View } from "tamagui"; 2 2 3 3 const H4 = (props: any) => <TamaguiH4 fontWeight="100" {...props} />; 4 4 const P = (props: any) => (
+4 -4
js/app/src/screens/av-sync.tsx
··· 1 - import { useEffect, useRef } from "react"; 2 - import { View } from "tamagui"; 3 - import QRCode from "qrcode"; 4 1 import { Countdown } from "components"; 5 - import { str2ab } from "quietjs-bundle"; 6 2 import { QUIET_PROFILE } from "components/player/av-sync"; 3 + import QRCode from "qrcode"; 4 + import { str2ab } from "quietjs-bundle"; 5 + import { useEffect, useRef } from "react"; 6 + import { View } from "tamagui"; 7 7 8 8 // screen that displays timestamp as a QR code and encodes timestamp in audio 9 9 // so we can measure sync between them
+1 -1
js/app/src/screens/download.tsx
··· 1 - import { H4 as TamaguiH4, Paragraph, View, Anchor } from "tamagui"; 1 + import { Anchor, Paragraph, H4 as TamaguiH4, View } from "tamagui"; 2 2 import GetApps from "../../components/get-apps"; 3 3 4 4 const H4 = (props: any) => <TamaguiH4 fontWeight="100" {...props} />;
+1 -1
js/app/src/screens/embed.tsx
··· 1 + import { Player } from "components"; 1 2 import { PlayerProps } from "components/player/props"; 2 3 import { isWeb } from "tamagui"; 3 4 import { queryToProps } from "./util"; 4 - import { Player } from "components"; 5 5 6 6 export default function EmbedScreen({ route }) { 7 7 const { user, protocol, url } = route.params;
+8 -9
js/app/src/screens/home.tsx
··· 1 + import { AlertCircle } from "@tamagui/lucide-icons"; 1 2 import { UseMediaState } from "@tamagui/web"; 2 3 import AQLink from "components/aqlink"; 4 + import Container from "components/container"; 3 5 import ErrorBox from "components/error/error"; 4 - import Loading from "components/loading/loading"; 5 6 import StreamCardHorizontal, { StreamCardSize } from "components/home/cards"; 6 - import Container from "components/container"; 7 7 import LiveDot from "components/home/live-dot"; 8 + import Loading from "components/loading/loading"; 8 9 import Title from "components/title"; 9 10 import { 10 11 pollSegments, 11 - Repo, 12 12 selectRecentSegments, 13 13 } from "features/streamplace/streamplaceSlice"; 14 14 import useAvatars from "hooks/useAvatars"; ··· 16 16 import { useEffect, useState } from "react"; 17 17 import { RefreshControl } from "react-native"; 18 18 import { useAppDispatch, useAppSelector } from "store/hooks"; 19 + import { PlaceStreamLivestream } from "streamplace"; 19 20 import { 21 + H3, 22 + Image, 23 + Paragraph, 20 24 ScrollView, 21 25 ScrollViewProps, 26 + Text, 22 27 useMedia, 23 28 View, 24 - Image, 25 - Paragraph, 26 - H3, 27 - Text, 28 29 } from "tamagui"; 29 - import { LivestreamRecord, PlaceStreamLivestream } from "streamplace"; 30 - import { AlertCircle, AlertTriangle } from "@tamagui/lucide-icons"; 31 30 32 31 // as we're not using a specific grid library these are necessary 33 32 // to constrain the cards
+12 -13
js/app/src/screens/live-dashboard.tsx
··· 1 + import { Camera, FerrisWheel, X } from "@tamagui/lucide-icons"; 2 + import { Redirect } from "components/aqlink"; 1 3 import CreateLivestream from "components/create-livestream"; 2 - import { Button, isWeb, View } from "tamagui"; 4 + import UpdateLivestream from "components/edit-livestream"; 5 + import StreamKeyScreen from "components/live-dashboard/stream-key"; 6 + import Waiting from "components/live-dashboard/waiting"; 7 + import Loading from "components/loading/loading"; 3 8 import { Player } from "components/player/player"; 4 - import Loading from "components/loading/loading"; 9 + import ButtonSelector from "components/ui/button-selector"; 10 + import { VideoElementProvider } from "contexts/VideoElementContext"; 5 11 import { 6 12 selectIsReady, 7 13 selectUserProfile, 8 14 } from "features/bluesky/blueskySlice"; 15 + import { selectTelemetry } from "features/streamplace/streamplaceSlice"; 16 + import { useLiveUser } from "hooks/useLiveUser"; 17 + import React, { useCallback, useState } from "react"; 9 18 import { useAppSelector } from "store/hooks"; 10 - import { Redirect } from "components/aqlink"; 11 - import React, { useCallback, useState } from "react"; 12 - import { useLiveUser } from "hooks/useLiveUser"; 13 - import StreamKeyScreen from "components/live-dashboard/stream-key"; 14 - import { VideoElementProvider } from "contexts/VideoElementContext"; 15 - import { Camera, FerrisWheel, X } from "@tamagui/lucide-icons"; 16 - import { H6, Text } from "tamagui"; 17 - import Waiting from "components/live-dashboard/waiting"; 18 - import { selectTelemetry } from "features/streamplace/streamplaceSlice"; 19 - import UpdateLivestream from "components/edit-livestream"; 20 - import ButtonSelector from "components/ui/button-selector"; 19 + import { Button, H6, isWeb, Text, View } from "tamagui"; 21 20 22 21 enum StreamSource { 23 22 Start,
+2 -2
js/app/src/screens/stream.tsx
··· 1 + import Livestream from "components/livestream/livestream"; 1 2 import { PlayerProps } from "components/player/props"; 3 + import { FullscreenProvider } from "contexts/FullscreenContext"; 2 4 import { isWeb } from "tamagui"; 3 5 import { queryToProps } from "./util"; 4 - import Livestream from "components/livestream/livestream"; 5 - import { FullscreenProvider } from "contexts/FullscreenContext"; 6 6 7 7 export default function StreamScreen({ route }) { 8 8 const { user, protocol, url } = route.params;
+1 -1
js/app/storage/storage.native.tsx
··· 1 1 import Storage from "expo-sqlite/kv-store"; 2 - import { AQStorage } from "./storage.shared"; 3 2 import { Lock } from "./lock"; 3 + import { AQStorage } from "./storage.shared"; 4 4 5 5 // Needed because concurrent calls seem to return with a locked database 6 6 const lock = new Lock();
+1 -1
js/app/store/listener.ts
··· 1 1 import { createListenerMiddleware, isAnyOf } from "@reduxjs/toolkit"; 2 2 import { SIDEBAR_STORAGE_KEY, sidebarSlice } from "features/base/sidebarSlice"; 3 - import { RootState } from "./store"; 4 3 import storage from "storage"; 4 + import { RootState } from "./store"; 5 5 6 6 export const listenerMiddleware = createListenerMiddleware(); 7 7
+2 -2
js/app/store/store.tsx
··· 1 1 import type { Action, ThunkAction } from "@reduxjs/toolkit"; 2 2 import { combineSlices, configureStore } from "@reduxjs/toolkit"; 3 3 import { setupListeners } from "@reduxjs/toolkit/query"; 4 - import { streamplaceSlice } from "features/streamplace/streamplaceSlice"; 5 4 import { baseSlice } from "features/base/baseSlice"; 5 + import { sidebarSlice } from "features/base/sidebarSlice"; 6 6 import { blueskySlice } from "features/bluesky/blueskySlice"; 7 7 import { platformSlice } from "features/platform/platformSlice"; 8 8 import { playerSlice } from "features/player/playerSlice"; 9 - import { sidebarSlice } from "features/base/sidebarSlice"; 9 + import { streamplaceSlice } from "features/streamplace/streamplaceSlice"; 10 10 11 11 import { listenerMiddleware } from "./listener"; 12 12
+1 -1
js/app/tamagui.config.ts
··· 1 1 import { config as configBase } from "@tamagui/config/v3"; 2 - import { createTamagui, createFont } from "tamagui"; 2 + import { createFont, createTamagui } from "tamagui"; 3 3 4 4 const sizes = { 5 5 "1": 11,
+1 -1
js/app/utils/videoCapture.ts
··· 1 1 /** 2 2 * Utility functions for capturing video frames 3 3 */ 4 - import { isWeb } from "tamagui"; 5 4 import React from "react"; 5 + import { isWeb } from "tamagui"; 6 6 7 7 /** 8 8 * Captures a frame from a video ref or element and returns it as a compressed JPEG blob
+1 -1
js/desktop/src/env.ts
··· 27 27 declare const MAIN_WINDOW_WEBPACK_ENTRY: string; 28 28 declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; 29 29 30 - export { MAIN_WINDOW_WEBPACK_ENTRY, MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY }; 30 + export { MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, MAIN_WINDOW_WEBPACK_ENTRY };
+2 -3
js/desktop/src/index.ts
··· 1 1 import { app, dialog, ipcMain } from "electron"; 2 2 import { parseArgs } from "node:util"; 3 3 import "source-map-support/register"; 4 + import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; 4 5 import getEnv from "./env"; 5 6 import makeNode from "./node"; 7 + import runTests, { allTestNames } from "./tests/test-runner"; 6 8 import initUpdater from "./updater"; 7 - import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; 8 9 import { makeWindow } from "./window"; 9 - import runTests from "./tests/test-runner"; 10 - import { allTestNames } from "./tests/test-runner"; 11 10 12 11 // Handle creating/removing shortcuts on Windows when installing/uninstalling. 13 12 if (require("electron-squirrel-startup")) {
+3 -3
js/desktop/src/node.ts
··· 1 + import { spawn } from "child_process"; 2 + import { app } from "electron"; 3 + import { access, constants } from "fs/promises"; 1 4 import os from "os"; 2 5 import { resolve } from "path"; 3 - import { access, constants } from "fs/promises"; 4 - import { spawn } from "child_process"; 5 6 import getEnv from "./env"; 6 - import { app } from "electron"; 7 7 8 8 const findExe = async (): Promise<string> => { 9 9 const { isDev } = getEnv();
+1 -1
js/desktop/src/tests/playback-test.ts
··· 1 - import { delay, PlayerReport } from "./util"; 2 1 import { v7 as uuidv7 } from "uuid"; 3 2 import { makeWindow } from "../window"; 4 3 import { E2ETest, TestEnv } from "./test-env"; 4 + import { delay, PlayerReport } from "./util"; 5 5 6 6 const PLAYING_SUCCESS = 0.5; 7 7
+3 -3
js/desktop/src/tests/sync-test.ts
··· 1 - import { BrowserWindow, WebFrameMain, webFrameMain, session } from "electron"; 1 + import { BrowserWindow, session, WebFrameMain, webFrameMain } from "electron"; 2 + import { v7 as uuidv7 } from "uuid"; 2 3 import { MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY } from "../env"; 3 - import { delay, PlayerReport } from "./util"; 4 4 import { E2ETest, TestEnv } from "./test-env"; 5 - import { v7 as uuidv7 } from "uuid"; 5 + import { delay, PlayerReport } from "./util"; 6 6 7 7 const SYNC_TOO_FAR = 20000; 8 8
+5 -5
js/desktop/src/tests/test-runner.ts
··· 1 1 import { bytesToMultibase } from "@atproto/crypto"; 2 + import { ChildProcess } from "child_process"; 3 + import fs from "fs/promises"; 4 + import os from "os"; 5 + import path from "path"; 6 + import { privateKeyToAccount } from "viem/accounts"; 2 7 import getEnv from "../env"; 3 8 import makeNode from "../node"; 4 9 import { playbackTest } from "./playback-test"; 5 10 import { syncTest } from "./sync-test"; 6 11 import { E2ETest, TestEnv } from "./test-env"; 7 - import { ChildProcess } from "child_process"; 8 - import { privateKeyToAccount } from "viem/accounts"; 9 - import fs from "fs/promises"; 10 - import path from "path"; 11 - import os from "os"; 12 12 13 13 const allTests: Record<string, E2ETest> = { 14 14 playback: playbackTest,
+3 -3
js/desktop/src/updater.ts
··· 1 + import electron from "electron"; 2 + import ms from "ms"; 1 3 import { 2 4 IUpdateElectronAppOptions, 3 5 UpdateSourceType, 4 6 } from "update-electron-app"; 5 - import electron from "electron"; 6 - import ms from "ms"; 7 - import * as build from "./version"; 8 7 import env from "./env"; 8 + import * as build from "./version"; 9 9 10 10 const supportedPlatforms = ["darwin", "win32"]; 11 11
+1 -1
js/desktop/src/window.ts
··· 1 1 import { BrowserWindow, globalShortcut } from "electron"; 2 - import getEnv, { MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY } from "./env"; 3 2 import { resolve } from "path"; 3 + import getEnv, { MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY } from "./env"; 4 4 5 5 export const makeWindow = async (): Promise<BrowserWindow> => { 6 6 const { isDev } = getEnv();
+1 -1
js/desktop/webpack.main.config.ts
··· 1 1 import type { Configuration } from "webpack"; 2 2 3 - import { rules } from "./webpack.rules"; 4 3 import { plugins } from "./webpack.plugins"; 4 + import { rules } from "./webpack.rules"; 5 5 6 6 export const mainConfig: Configuration = { 7 7 /**
+1 -1
js/desktop/webpack.renderer.config.ts
··· 1 1 import type { Configuration } from "webpack"; 2 2 3 - import { rules } from "./webpack.rules"; 4 3 import { plugins } from "./webpack.plugins"; 4 + import { rules } from "./webpack.rules"; 5 5 6 6 rules.push({ 7 7 test: /\.css$/,
+1 -1
js/docs/astro.config.mjs
··· 1 1 // @ts-check 2 - import { defineConfig } from "astro/config"; 3 2 import starlight from "@astrojs/starlight"; 3 + import { defineConfig } from "astro/config"; 4 4 import starlightOpenAPI, { openAPISidebarGroups } from "starlight-openapi"; 5 5 6 6 // https://astro.build/config
+1 -1
js/docs/src/content.config.ts
··· 1 - import { defineCollection } from "astro:content"; 2 1 import { docsLoader } from "@astrojs/starlight/loaders"; 3 2 import { docsSchema } from "@astrojs/starlight/schema"; 3 + import { defineCollection } from "astro:content"; 4 4 5 5 export const collections = { 6 6 docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
+2 -1
package.json
··· 37 37 ], 38 38 "dependencies": { 39 39 "firebase-admin": "^12.7.0", 40 - "lex-md": "^0.1.0" 40 + "lex-md": "^0.1.0", 41 + "prettier-plugin-organize-imports": "^4.1.0" 41 42 }, 42 43 "packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977" 43 44 }
+18
pnpm-lock.yaml
··· 14 14 lex-md: 15 15 specifier: ^0.1.0 16 16 version: 0.1.0 17 + prettier-plugin-organize-imports: 18 + specifier: ^4.1.0 19 + version: 4.1.0(prettier@3.4.2)(typescript@5.6.3) 17 20 devDependencies: 18 21 '@atproto/lex-cli': 19 22 specifier: ^0.5.6 ··· 10546 10549 prelude-ls@1.2.1: 10547 10550 resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 10548 10551 engines: {node: '>= 0.8.0'} 10552 + 10553 + prettier-plugin-organize-imports@4.1.0: 10554 + resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==} 10555 + peerDependencies: 10556 + prettier: '>=2.0' 10557 + typescript: '>=2.9' 10558 + vue-tsc: ^2.1.0 10559 + peerDependenciesMeta: 10560 + vue-tsc: 10561 + optional: true 10549 10562 10550 10563 prettier@3.4.2: 10551 10564 resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} ··· 27447 27460 tunnel-agent: 0.6.0 27448 27461 27449 27462 prelude-ls@1.2.1: {} 27463 + 27464 + prettier-plugin-organize-imports@4.1.0(prettier@3.4.2)(typescript@5.6.3): 27465 + dependencies: 27466 + prettier: 3.4.2 27467 + typescript: 5.6.3 27450 27468 27451 27469 prettier@3.4.2: {} 27452 27470