···44import { colors } from "../../lib/theme";
55import { useLivestreamStore } from "../../livestream-store";
66import { useUrl } from "../../streamplace-store";
77+import { formatHandle } from "../../utils/format-handle";
78import { BlueskyIcon } from "../icons/bluesky-icon";
89import {
910 DropdownMenu,
···26272728 // Get the current stream URL
2829 const getStreamUrl = useCallback(() => {
2929- return url + (profile ? `/@${profile.handle}` : "");
3030+ return url + (profile ? `/${formatHandle(profile)}` : "");
3031 }, [profile]);
31323233 // Get the embed URL
3334 const getEmbedUrl = useCallback(() => {
3434- return url + (profile ? `/embed/${profile.handle}` : "");
3535+ return url + (profile ? `/embed/${formatHandle(profile)}` : "");
3536 }, [profile]);
36373738 // Get embed code
···6364 // Share to Bluesky
6465 const shareToBluesky = useCallback(() => {
6566 const streamUrl = getStreamUrl();
6666- const text = profile
6767- ? `Check out @${profile.handle} live on Streamplace! ${streamUrl}`
6868- : `Check out this stream on Streamplace! ${streamUrl}`;
6767+ const text =
6868+ profile && profile.handle
6969+ ? `Check out @${profile.handle} live on Streamplace! ${streamUrl}`
7070+ : `Check out this stream on Streamplace! ${streamUrl}`;
6971 const blueskyUrl = `https://bsky.app/intent/compose?text=${encodeURIComponent(text)}`;
7072 Linking.openURL(blueskyUrl);
7173 onShare?.("share_bluesky", true);
7274 }, [profile, getStreamUrl, onShare]);
73757474- // Share to Twitter/X
7575- const shareToTwitter = useCallback(() => {
7676- const streamUrl = getStreamUrl();
7777- const text = profile
7878- ? `Check out @${profile.handle} live on Streamplace!`
7979- : `Check out this stream on Streamplace!`;
8080- const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(streamUrl)}`;
8181- Linking.openURL(twitterUrl);
8282- onShare?.("share_twitter", true);
8383- }, [profile, getStreamUrl, onShare]);
8484-8576 // Native share (mobile)
8677 const nativeShare = useCallback(async () => {
8778 const streamUrl = getStreamUrl();
8888- const text = profile
8989- ? `Check out @${profile.handle} live on Streamplace!`
9090- : `Check out this stream on Streamplace!`;
7979+ const text =
8080+ profile && profile.handle
8181+ ? `Check out @${profile.handle} live on Streamplace!`
8282+ : `Check out this stream on Streamplace!`;
91839284 if (Platform.OS === "web" && navigator.share) {
9385 try {
···119111 <Text>Share to Bluesky</Text>
120112 </View>
121113 </DropdownMenuItem>
122122- {/* <DropdownMenuItem onPress={shareToTwitter}>
123123- <View
124124- style={{ flexDirection: "row", alignItems: "center", gap: 12 }}
125125- >
126126- <MessageCircle size={20} color={colors.gray[400]} />
127127- <Text>Share to X</Text>
128128- </View>
129129- </DropdownMenuItem> */}
130114 {/* navigator isn't on non-web */}
131115 {Platform.OS !== "web" || (navigator && (navigator as any).share) ? (
132116 <DropdownMenuItem onPress={nativeShare}>
+2
js/components/src/index.tsx
···3737export { default as VideoRetry } from "./components/mobile-player/video-retry";
3838export * from "./lib/system-messages";
39394040+export * from "./utils/format-handle";
4141+4042export { DanmuOverlay } from "./components/danmu/danmu-overlay";
4143export { DanmuOverlayOBS } from "./components/danmu/danmu-overlay-obs";
4244
+5-3
js/components/src/utils/format-handle.ts
···55 */
66export function formatHandle(
77 profile: Pick<AppBskyActorDefs.ProfileViewBasic, "handle" | "did">,
88+ prefix: string = "",
89): string {
910 if (profile.handle === "handle.invalid") {
1011 return profile.did;
1112 }
1212- return profile.handle;
1313+ return prefix + profile.handle;
1314}
14151516/**
1616- * formats a user's handle with @ prefix for display, falling back to DID if handle is invalid
1717+ * convenience function for formatting a user's handle with @ prefix for display,
1818+ * falling back to DID if handle is invalid
1719 */
1820export function formatHandleWithAt(
1921 profile: Pick<AppBskyActorDefs.ProfileViewBasic, "handle" | "did">,
2022): string {
2121- return `@${formatHandle(profile)}`;
2323+ return formatHandle(profile, "@");
2224}