@streamplace/components 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/chat/chat-box.js +46 -43
- package/dist/components/chat/chat-message.js +36 -33
- package/dist/components/chat/chat.js +31 -27
- package/dist/components/chat/mention-suggestions.js +16 -13
- package/dist/components/chat/mod-view.js +20 -17
- package/dist/components/mobile-player/fullscreen.js +21 -17
- package/dist/components/mobile-player/fullscreen.native.js +39 -35
- package/dist/components/mobile-player/player.js +38 -32
- package/dist/components/mobile-player/props.js +2 -1
- package/dist/components/mobile-player/shared.js +16 -13
- package/dist/components/mobile-player/ui/countdown.js +23 -19
- package/dist/components/mobile-player/ui/index.js +9 -5
- package/dist/components/mobile-player/ui/input.js +16 -12
- package/dist/components/mobile-player/ui/metrics.js +20 -16
- package/dist/components/mobile-player/ui/streamer-context-menu.js +6 -3
- package/dist/components/mobile-player/ui/viewer-context-menu.js +19 -16
- package/dist/components/mobile-player/ui/viewers.js +23 -0
- package/dist/components/mobile-player/use-webrtc.js +29 -24
- package/dist/components/mobile-player/video.js +109 -99
- package/dist/components/mobile-player/video.native.js +108 -84
- package/dist/components/mobile-player/webrtc-diagnostics.js +9 -5
- package/dist/components/mobile-player/webrtc-primitives.js +8 -6
- package/dist/components/mobile-player/webrtc-primitives.native.js +8 -1
- package/dist/components/ui/button.js +26 -23
- package/dist/components/ui/dialog.js +43 -40
- package/dist/components/ui/dropdown.js +121 -116
- package/dist/components/ui/icons.js +8 -5
- package/dist/components/ui/index.js +27 -19
- package/dist/components/ui/input.js +31 -28
- package/dist/components/ui/loader.js +9 -6
- package/dist/components/ui/primitives/button.js +33 -29
- package/dist/components/ui/primitives/input.js +44 -40
- package/dist/components/ui/primitives/modal.js +45 -41
- package/dist/components/ui/primitives/text.js +35 -29
- package/dist/components/ui/resizeable.js +54 -43
- package/dist/components/ui/text.js +50 -48
- package/dist/components/ui/textarea.js +13 -11
- package/dist/components/ui/toast.js +26 -23
- package/dist/components/ui/view.js +41 -39
- package/dist/hooks/index.js +12 -8
- package/dist/hooks/useAvatars.js +11 -8
- package/dist/hooks/useCameraToggle.js +7 -4
- package/dist/hooks/useKeyboard.js +13 -10
- package/dist/hooks/useKeyboardSlide.js +8 -5
- package/dist/hooks/useLivestreamInfo.js +17 -14
- package/dist/hooks/useOuterAndInnerDimensions.js +9 -6
- package/dist/hooks/usePlayerDimensions.js +9 -6
- package/dist/hooks/useSegmentDimensions.js +17 -0
- package/dist/hooks/useSegmentTiming.js +13 -10
- package/dist/index.js +24 -15
- package/dist/lib/facet.js +5 -1
- package/dist/lib/theme/atoms.js +153 -148
- package/dist/lib/theme/atoms.types.js +2 -1
- package/dist/lib/theme/index.js +31 -5
- package/dist/lib/theme/theme.js +91 -83
- package/dist/lib/theme/tokens.js +15 -12
- package/dist/lib/utils.js +22 -11
- package/dist/livestream-provider/index.js +19 -14
- package/dist/livestream-provider/websocket.js +14 -10
- package/dist/livestream-store/chat.js +26 -19
- package/dist/livestream-store/context.js +5 -2
- package/dist/livestream-store/index.js +7 -4
- package/dist/livestream-store/livestream-state.js +2 -1
- package/dist/livestream-store/livestream-store.js +31 -18
- package/dist/livestream-store/stream-key.js +22 -18
- package/dist/livestream-store/websocket-consumer.js +18 -14
- package/dist/player-store/context.js +5 -2
- package/dist/player-store/index.js +8 -5
- package/dist/player-store/player-provider.js +20 -15
- package/dist/player-store/player-state.js +9 -6
- package/dist/player-store/player-store.js +32 -21
- package/dist/player-store/single-player-provider.js +35 -23
- package/dist/streamplace-provider/context.js +5 -2
- package/dist/streamplace-provider/index.js +14 -10
- package/dist/streamplace-provider/poller.js +20 -17
- package/dist/streamplace-store/block.js +6 -3
- package/dist/streamplace-store/index.js +6 -3
- package/dist/streamplace-store/stream.js +14 -10
- package/dist/streamplace-store/streamplace-store.js +23 -13
- package/dist/streamplace-store/user.js +19 -14
- package/dist/streamplace-store/xrpc.js +10 -7
- package/node-compile-cache/v22.15.0-x64-92db9086-0/37be0eec +0 -0
- package/node-compile-cache/v22.15.0-x64-92db9086-0/56540125 +0 -0
- package/node-compile-cache/{v22.15.0-x64-efe9a9df-0 → v22.15.0-x64-92db9086-0}/67b1eb60 +0 -0
- package/node-compile-cache/{v22.15.0-x64-efe9a9df-0 → v22.15.0-x64-92db9086-0}/7c275f90 +0 -0
- package/package.json +6 -7
- package/src/components/mobile-player/ui/index.ts +1 -0
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +5 -4
- package/src/components/mobile-player/ui/viewers.tsx +32 -0
- package/src/components/mobile-player/video.native.tsx +50 -5
- package/src/components/ui/resizeable.tsx +15 -3
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useSegmentDimensions.tsx +18 -0
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/56540125 +0 -0
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = Poller;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const streamplace_1 = require("streamplace");
|
|
7
|
+
const streamplace_store_1 = require("../streamplace-store");
|
|
8
|
+
const xrpc_1 = require("../streamplace-store/xrpc");
|
|
9
|
+
function Poller({ children }) {
|
|
10
|
+
const url = (0, streamplace_store_1.useStreamplaceStore)((state) => state.url);
|
|
11
|
+
const setLiveUsers = (0, streamplace_store_1.useStreamplaceStore)((state) => state.setLiveUsers);
|
|
12
|
+
const did = (0, streamplace_store_1.useDID)();
|
|
13
|
+
const pdsAgent = (0, xrpc_1.usePDSAgent)();
|
|
14
|
+
const getChatProfile = (0, streamplace_store_1.useGetChatProfile)();
|
|
15
|
+
const getBskyProfile = (0, streamplace_store_1.useGetBskyProfile)();
|
|
16
|
+
const liveUserRefresh = (0, streamplace_store_1.useStreamplaceStore)((state) => state.liveUsersRefresh);
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
15
18
|
if (pdsAgent && did) {
|
|
16
19
|
getChatProfile();
|
|
17
20
|
getBskyProfile();
|
|
18
21
|
}
|
|
19
22
|
}, [pdsAgent, did]);
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
const agent = new StreamplaceAgent(url);
|
|
23
|
+
(0, react_1.useEffect)(() => {
|
|
24
|
+
const agent = new streamplace_1.StreamplaceAgent(url);
|
|
22
25
|
const go = async () => {
|
|
23
26
|
setLiveUsers({
|
|
24
27
|
liveUsersLoading: true,
|
|
@@ -42,5 +45,5 @@ export default function Poller({ children }) {
|
|
|
42
45
|
const handle = setInterval(go, 3000);
|
|
43
46
|
return () => clearInterval(handle);
|
|
44
47
|
}, [url, liveUserRefresh]);
|
|
45
|
-
return
|
|
48
|
+
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
|
|
46
49
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useCreateBlockRecord = useCreateBlockRecord;
|
|
4
|
+
const xrpc_1 = require("./xrpc");
|
|
5
|
+
function useCreateBlockRecord() {
|
|
6
|
+
let agent = (0, xrpc_1.usePDSAgent)();
|
|
4
7
|
return async (subjectDID) => {
|
|
5
8
|
if (!agent) {
|
|
6
9
|
throw new Error("No PDS agent found");
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./stream"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./streamplace-store"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./user"), exports);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useCreateStreamRecord = useCreateStreamRecord;
|
|
4
|
+
exports.useUpdateStreamRecord = useUpdateStreamRecord;
|
|
5
|
+
const api_1 = require("@atproto/api");
|
|
6
|
+
const streamplace_store_1 = require("./streamplace-store");
|
|
7
|
+
const xrpc_1 = require("./xrpc");
|
|
4
8
|
const uploadThumbnail = async (pdsAgent, customThumbnail) => {
|
|
5
9
|
if (customThumbnail) {
|
|
6
10
|
let tries = 0;
|
|
@@ -42,7 +46,7 @@ function buildGoLivePost(text, url, profile, params, thumbnail) {
|
|
|
42
46
|
const textUrl = `${url.protocol}//${url.host}/${profile.handle}`;
|
|
43
47
|
const suffix = ` ${text}`;
|
|
44
48
|
const content = prefix + textUrl + suffix;
|
|
45
|
-
const rt = new RichText({ text: content });
|
|
49
|
+
const rt = new api_1.RichText({ text: content });
|
|
46
50
|
rt.detectFacetsWithoutResolution();
|
|
47
51
|
const record = {
|
|
48
52
|
$type: "app.bsky.feed.post",
|
|
@@ -65,9 +69,9 @@ function buildGoLivePost(text, url, profile, params, thumbnail) {
|
|
|
65
69
|
};
|
|
66
70
|
return record;
|
|
67
71
|
}
|
|
68
|
-
|
|
69
|
-
let agent = usePDSAgent();
|
|
70
|
-
let url = useUrl();
|
|
72
|
+
function useCreateStreamRecord() {
|
|
73
|
+
let agent = (0, xrpc_1.usePDSAgent)();
|
|
74
|
+
let url = (0, streamplace_store_1.useUrl)();
|
|
71
75
|
return async (title, customThumbnail, submitPost = true) => {
|
|
72
76
|
if (!agent) {
|
|
73
77
|
throw new Error("No PDS agent found");
|
|
@@ -147,9 +151,9 @@ export function useCreateStreamRecord() {
|
|
|
147
151
|
return record;
|
|
148
152
|
};
|
|
149
153
|
}
|
|
150
|
-
|
|
151
|
-
let agent = usePDSAgent();
|
|
152
|
-
let url = useUrl();
|
|
154
|
+
function useUpdateStreamRecord() {
|
|
155
|
+
let agent = (0, xrpc_1.usePDSAgent)();
|
|
156
|
+
let url = (0, streamplace_store_1.useUrl)();
|
|
153
157
|
return async (title, livestream, customThumbnail) => {
|
|
154
158
|
if (!agent) {
|
|
155
159
|
throw new Error("No PDS agent found");
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useSetHandle = exports.useHandle = exports.useDID = exports.useUrl = exports.makeStreamplaceStore = void 0;
|
|
4
|
+
exports.getStreamplaceStoreFromContext = getStreamplaceStoreFromContext;
|
|
5
|
+
exports.useStreamplaceStore = useStreamplaceStore;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const zustand_1 = require("zustand");
|
|
8
|
+
const context_1 = require("../streamplace-provider/context");
|
|
9
|
+
const makeStreamplaceStore = ({ url, }) => {
|
|
10
|
+
return (0, zustand_1.createStore)()((set) => ({
|
|
6
11
|
url,
|
|
7
12
|
liveUsers: null,
|
|
8
13
|
setLiveUsers: (opts) => {
|
|
@@ -18,20 +23,25 @@ export const makeStreamplaceStore = ({ url, }) => {
|
|
|
18
23
|
chatProfile: null,
|
|
19
24
|
}));
|
|
20
25
|
};
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
exports.makeStreamplaceStore = makeStreamplaceStore;
|
|
27
|
+
function getStreamplaceStoreFromContext() {
|
|
28
|
+
const context = (0, react_1.useContext)(context_1.StreamplaceContext);
|
|
23
29
|
if (!context) {
|
|
24
30
|
throw new Error("useStreamplaceStore must be used within a StreamplaceProvider");
|
|
25
31
|
}
|
|
26
32
|
return context.store;
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
return useStore(getStreamplaceStoreFromContext(), selector);
|
|
34
|
+
function useStreamplaceStore(selector) {
|
|
35
|
+
return (0, zustand_1.useStore)(getStreamplaceStoreFromContext(), selector);
|
|
30
36
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
const useUrl = () => useStreamplaceStore((x) => x.url);
|
|
38
|
+
exports.useUrl = useUrl;
|
|
39
|
+
const useDID = () => useStreamplaceStore((x) => x.oauthSession?.did);
|
|
40
|
+
exports.useDID = useDID;
|
|
41
|
+
const useHandle = () => useStreamplaceStore((x) => x.handle);
|
|
42
|
+
exports.useHandle = useHandle;
|
|
43
|
+
const useSetHandle = () => {
|
|
35
44
|
const store = getStreamplaceStoreFromContext();
|
|
36
45
|
return (handle) => store.setState({ handle });
|
|
37
46
|
};
|
|
47
|
+
exports.useSetHandle = useSetHandle;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useGetChatProfile = useGetChatProfile;
|
|
4
|
+
exports.useGetBskyProfile = useGetBskyProfile;
|
|
5
|
+
exports.useChatProfile = useChatProfile;
|
|
6
|
+
const streamplace_1 = require("streamplace");
|
|
7
|
+
const streamplace_store_1 = require("./streamplace-store");
|
|
8
|
+
const xrpc_1 = require("./xrpc");
|
|
9
|
+
function useGetChatProfile() {
|
|
10
|
+
const did = (0, streamplace_store_1.useDID)();
|
|
11
|
+
const pdsAgent = (0, xrpc_1.usePDSAgent)();
|
|
12
|
+
const store = (0, streamplace_store_1.getStreamplaceStoreFromContext)();
|
|
8
13
|
return async () => {
|
|
9
14
|
if (!did || !pdsAgent) {
|
|
10
15
|
throw new Error("No DID or PDS agent");
|
|
@@ -17,7 +22,7 @@ export function useGetChatProfile() {
|
|
|
17
22
|
if (!res.success) {
|
|
18
23
|
throw new Error("Failed to get chat profile record");
|
|
19
24
|
}
|
|
20
|
-
if (PlaceStreamChatProfile.isRecord(res.data.value)) {
|
|
25
|
+
if (streamplace_1.PlaceStreamChatProfile.isRecord(res.data.value)) {
|
|
21
26
|
store.setState({ chatProfile: res.data.value });
|
|
22
27
|
}
|
|
23
28
|
else {
|
|
@@ -25,10 +30,10 @@ export function useGetChatProfile() {
|
|
|
25
30
|
}
|
|
26
31
|
};
|
|
27
32
|
}
|
|
28
|
-
|
|
29
|
-
const did = useDID();
|
|
30
|
-
const pdsAgent = usePDSAgent();
|
|
31
|
-
const store = getStreamplaceStoreFromContext();
|
|
33
|
+
function useGetBskyProfile() {
|
|
34
|
+
const did = (0, streamplace_store_1.useDID)();
|
|
35
|
+
const pdsAgent = (0, xrpc_1.usePDSAgent)();
|
|
36
|
+
const store = (0, streamplace_store_1.getStreamplaceStoreFromContext)();
|
|
32
37
|
return async () => {
|
|
33
38
|
if (!did || !pdsAgent) {
|
|
34
39
|
throw new Error("No DID or PDS agent");
|
|
@@ -42,6 +47,6 @@ export function useGetBskyProfile() {
|
|
|
42
47
|
store.setState({ handle: res.data.handle });
|
|
43
48
|
};
|
|
44
49
|
}
|
|
45
|
-
|
|
46
|
-
return useStreamplaceStore((x) => x.chatProfile);
|
|
50
|
+
function useChatProfile() {
|
|
51
|
+
return (0, streamplace_store_1.useStreamplaceStore)((x) => x.chatProfile);
|
|
47
52
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePDSAgent = usePDSAgent;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const streamplace_1 = require("streamplace");
|
|
6
|
+
const _1 = require(".");
|
|
7
|
+
function usePDSAgent() {
|
|
8
|
+
const oauthSession = (0, _1.useStreamplaceStore)((state) => state.oauthSession);
|
|
9
|
+
return (0, react_1.useMemo)(() => {
|
|
7
10
|
if (!oauthSession) {
|
|
8
11
|
return null;
|
|
9
12
|
}
|
|
10
|
-
return new StreamplaceAgent(oauthSession);
|
|
13
|
+
return new streamplace_1.StreamplaceAgent(oauthSession);
|
|
11
14
|
}, [oauthSession]);
|
|
12
15
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamplace/components",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Streamplace React (Native) Components",
|
|
5
|
-
"type": "module",
|
|
6
5
|
"main": "dist/index.js",
|
|
7
6
|
"types": "src/index.tsx",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
9
|
"@streamplace/dev": "./src/index.tsx",
|
|
11
|
-
"types": "./
|
|
12
|
-
"default": "./dist/index.
|
|
10
|
+
"types": "./src/index.tsx",
|
|
11
|
+
"default": "./dist/index.js"
|
|
13
12
|
}
|
|
14
13
|
},
|
|
15
14
|
"scripts": {
|
|
16
15
|
"build": "tsc",
|
|
17
|
-
"
|
|
16
|
+
"prepare": "pnpm run build",
|
|
18
17
|
"start": "tsc --watch --preserveWatchOutput"
|
|
19
18
|
},
|
|
20
19
|
"keywords": [
|
|
@@ -44,12 +43,12 @@
|
|
|
44
43
|
"react-native-safe-area-context": "5.4.1",
|
|
45
44
|
"react-native-webrtc": "git+https://github.com/streamplace/react-native-webrtc.git#6b8472a771ac47f89217d327058a8a4124a6ae56",
|
|
46
45
|
"react-use-websocket": "^4.13.0",
|
|
47
|
-
"streamplace": "0.7.
|
|
46
|
+
"streamplace": "0.7.2",
|
|
48
47
|
"viem": "^2.21.44",
|
|
49
48
|
"zustand": "^5.0.5"
|
|
50
49
|
},
|
|
51
50
|
"peerDependencies": {
|
|
52
51
|
"react": "*"
|
|
53
52
|
},
|
|
54
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "b07a68160ae6da34cf47aa397b4ef452248d79e5"
|
|
55
54
|
}
|
|
@@ -39,7 +39,7 @@ export function ContextMenu() {
|
|
|
39
39
|
<DropdownMenuGroup title="Resolution">
|
|
40
40
|
<DropdownMenuRadioGroup value={quality} onValueChange={setQuality}>
|
|
41
41
|
<DropdownMenuRadioItem value="source">
|
|
42
|
-
<Text>Source</Text>
|
|
42
|
+
<Text>Source (Original Quality)</Text>
|
|
43
43
|
</DropdownMenuRadioItem>
|
|
44
44
|
{qualities.map((r) => (
|
|
45
45
|
<DropdownMenuRadioItem value={r.name}>
|
|
@@ -55,15 +55,16 @@ export function ContextMenu() {
|
|
|
55
55
|
>
|
|
56
56
|
<Text>Low Latency</Text>
|
|
57
57
|
</DropdownMenuCheckboxItem>
|
|
58
|
-
|
|
58
|
+
</DropdownMenuGroup>
|
|
59
|
+
<DropdownMenuInfo description="Reduces the delay between video and chat for a more real-time experience." />
|
|
60
|
+
<DropdownMenuGroup>
|
|
59
61
|
<DropdownMenuCheckboxItem
|
|
60
62
|
checked={debugInfo}
|
|
61
63
|
onCheckedChange={() => setShowDebugInfo(!debugInfo)}
|
|
62
64
|
>
|
|
63
|
-
<Text>
|
|
65
|
+
<Text>Show Debug Info</Text>
|
|
64
66
|
</DropdownMenuCheckboxItem>
|
|
65
67
|
</DropdownMenuGroup>
|
|
66
|
-
<DropdownMenuInfo description="Lowers the delay between video and chat messages." />
|
|
67
68
|
</ResponsiveDropdownMenuContent>
|
|
68
69
|
</DropdownMenu>
|
|
69
70
|
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Eye } from "lucide-react-native";
|
|
2
|
+
import * as atoms from "../../../lib/theme/atoms";
|
|
3
|
+
import { useViewers } from "../../../livestream-store";
|
|
4
|
+
import { Text, View } from "../../ui";
|
|
5
|
+
|
|
6
|
+
export function Viewers() {
|
|
7
|
+
const viewers = useViewers();
|
|
8
|
+
return (
|
|
9
|
+
<View
|
|
10
|
+
style={[
|
|
11
|
+
atoms.layout.flex.center,
|
|
12
|
+
atoms.layout.flex.row,
|
|
13
|
+
atoms.gap.all[2],
|
|
14
|
+
]}
|
|
15
|
+
>
|
|
16
|
+
<Eye color="#fd5050" />
|
|
17
|
+
<Text
|
|
18
|
+
style={{
|
|
19
|
+
color: "#fd5050",
|
|
20
|
+
textShadowColor: "black",
|
|
21
|
+
textShadowOffset: { width: -1, height: 1 },
|
|
22
|
+
textShadowRadius: 3,
|
|
23
|
+
fontSize: 16,
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
{new Intl.NumberFormat(undefined, { notation: "compact" }).format(
|
|
27
|
+
viewers || 0,
|
|
28
|
+
)}
|
|
29
|
+
</Text>
|
|
30
|
+
</View>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { useVideoPlayer, VideoPlayerEvents, VideoView } from "expo-video";
|
|
2
|
+
import { ArrowRight } from "lucide-react-native";
|
|
2
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
-
import { LayoutChangeEvent } from "react-native";
|
|
4
|
+
import { LayoutChangeEvent, Linking } from "react-native";
|
|
4
5
|
import {
|
|
5
6
|
MediaStream,
|
|
6
7
|
RTCView,
|
|
7
8
|
RTCView as RTCViewIngest,
|
|
8
9
|
} from "react-native-webrtc";
|
|
9
10
|
import {
|
|
11
|
+
Button,
|
|
10
12
|
IngestMediaSource,
|
|
11
13
|
PlayerStatus as IngestPlayerStatus,
|
|
12
14
|
PlayerProtocol,
|
|
@@ -17,7 +19,16 @@ import {
|
|
|
17
19
|
useStreamplaceStore,
|
|
18
20
|
View,
|
|
19
21
|
} from "../..";
|
|
20
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
borderRadius,
|
|
24
|
+
colors,
|
|
25
|
+
fontWeight,
|
|
26
|
+
gap,
|
|
27
|
+
h,
|
|
28
|
+
layout,
|
|
29
|
+
m,
|
|
30
|
+
p,
|
|
31
|
+
} from "../../lib/theme/atoms";
|
|
21
32
|
import { srcToUrl } from "./shared";
|
|
22
33
|
import useWebRTC, { useWebRTCIngest } from "./use-webrtc";
|
|
23
34
|
import { mediaDevices, WebRTCMediaStream } from "./webrtc-primitives.native";
|
|
@@ -301,12 +312,31 @@ export function NativeIngestPlayer() {
|
|
|
301
312
|
})
|
|
302
313
|
.then((stream: WebRTCMediaStream) => {
|
|
303
314
|
setLocalMediaStream(stream);
|
|
315
|
+
|
|
316
|
+
let errs: string[] = [];
|
|
317
|
+
if (stream.getAudioTracks().length === 0) {
|
|
318
|
+
console.warn("No audio tracks found in user media stream");
|
|
319
|
+
errs.push("microphone");
|
|
320
|
+
}
|
|
321
|
+
if (stream.getVideoTracks().length === 0) {
|
|
322
|
+
console.warn("No video tracks found in user media stream");
|
|
323
|
+
errs.push("camera");
|
|
324
|
+
}
|
|
325
|
+
if (errs.length > 0) {
|
|
326
|
+
setError(
|
|
327
|
+
new Error(
|
|
328
|
+
`We could not access your ${errs.join(" and ")}. To stream, you need to give us permission to access these.`,
|
|
329
|
+
),
|
|
330
|
+
);
|
|
331
|
+
} else {
|
|
332
|
+
setError(null);
|
|
333
|
+
}
|
|
304
334
|
})
|
|
305
335
|
.catch((e: any) => {
|
|
306
336
|
console.error("error getting user media", e);
|
|
307
337
|
setError(
|
|
308
338
|
new Error(
|
|
309
|
-
"We could not access your camera or microphone.
|
|
339
|
+
"We could not access your camera or microphone. To stream, you need to give us permission to access these.",
|
|
310
340
|
),
|
|
311
341
|
);
|
|
312
342
|
});
|
|
@@ -334,12 +364,27 @@ export function NativeIngestPlayer() {
|
|
|
334
364
|
return (
|
|
335
365
|
<View
|
|
336
366
|
backgroundColor={colors.destructive[900]}
|
|
337
|
-
style={[p[4], { borderRadius: borderRadius.md }]}
|
|
367
|
+
style={[p[4], m[4], gap.all[2], { borderRadius: borderRadius.md }]}
|
|
338
368
|
>
|
|
339
369
|
<View>
|
|
340
|
-
<Text
|
|
370
|
+
<Text style={[fontWeight.semibold]} size="2xl">
|
|
371
|
+
Error encountered!
|
|
372
|
+
</Text>
|
|
341
373
|
</View>
|
|
342
374
|
<Text>{error.message}</Text>
|
|
375
|
+
{error.message.includes(
|
|
376
|
+
"To stream, you need to give us permission to access these.",
|
|
377
|
+
) && (
|
|
378
|
+
<Button
|
|
379
|
+
onPress={Linking.openSettings}
|
|
380
|
+
style={[h[10]]}
|
|
381
|
+
variant="secondary"
|
|
382
|
+
>
|
|
383
|
+
<View style={[layout.flex.row, gap.all[1]]}>
|
|
384
|
+
<Text>Open Settings</Text> <ArrowRight color="white" size="18" />
|
|
385
|
+
</View>
|
|
386
|
+
</Button>
|
|
387
|
+
)}
|
|
343
388
|
</View>
|
|
344
389
|
);
|
|
345
390
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChevronUp } from "lucide-react-native";
|
|
2
|
-
import { ComponentProps } from "react";
|
|
2
|
+
import { ComponentProps, useEffect } from "react";
|
|
3
3
|
import { Dimensions } from "react-native";
|
|
4
4
|
import {
|
|
5
5
|
Gesture,
|
|
@@ -23,6 +23,7 @@ const AnimatedView = Animated.createAnimatedComponent(View);
|
|
|
23
23
|
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
24
24
|
|
|
25
25
|
type ResizableChatSheetProps = {
|
|
26
|
+
startingPercentage?: number;
|
|
26
27
|
isPlayerRatioGreater: boolean;
|
|
27
28
|
style?: ComponentProps<typeof AnimatedView>["style"];
|
|
28
29
|
children?: React.ReactNode;
|
|
@@ -31,6 +32,7 @@ type ResizableChatSheetProps = {
|
|
|
31
32
|
const SPRING_CONFIG = { damping: 20, stiffness: 100 };
|
|
32
33
|
|
|
33
34
|
export function Resizable({
|
|
35
|
+
startingPercentage,
|
|
34
36
|
isPlayerRatioGreater,
|
|
35
37
|
style = {},
|
|
36
38
|
children,
|
|
@@ -44,6 +46,15 @@ export function Resizable({
|
|
|
44
46
|
const sheetHeight = useSharedValue(MIN_HEIGHT);
|
|
45
47
|
const startHeight = useSharedValue(MIN_HEIGHT);
|
|
46
48
|
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
sheetHeight.value = withSpring(
|
|
52
|
+
startingPercentage ? startingPercentage * SCREEN_HEIGHT : MIN_HEIGHT,
|
|
53
|
+
SPRING_CONFIG,
|
|
54
|
+
);
|
|
55
|
+
}, 1000);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
47
58
|
const panGesture = Gesture.Pan()
|
|
48
59
|
.onStart(() => {
|
|
49
60
|
startHeight.value = sheetHeight.value;
|
|
@@ -70,7 +81,9 @@ export function Resizable({
|
|
|
70
81
|
transform: [
|
|
71
82
|
{
|
|
72
83
|
translateY:
|
|
73
|
-
slideKeyboard
|
|
84
|
+
slideKeyboard +
|
|
85
|
+
Math.max(0, -sheetHeight.value) +
|
|
86
|
+
(slideKeyboard < 0 ? 0 : -safeBottom),
|
|
74
87
|
},
|
|
75
88
|
],
|
|
76
89
|
}));
|
|
@@ -94,7 +107,6 @@ export function Resizable({
|
|
|
94
107
|
w.percent[100],
|
|
95
108
|
layout.flex.center,
|
|
96
109
|
zIndex[1],
|
|
97
|
-
{ marginBottom: safeBottom },
|
|
98
110
|
]}
|
|
99
111
|
>
|
|
100
112
|
<Pressable
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useLivestreamStore } from "../livestream-store";
|
|
2
|
+
|
|
3
|
+
export function useSegmentDimensions() {
|
|
4
|
+
const latestSegment = useLivestreamStore((x) => x.segment);
|
|
5
|
+
|
|
6
|
+
let seg = latestSegment?.video && latestSegment.video[0];
|
|
7
|
+
|
|
8
|
+
let ratio = {
|
|
9
|
+
height: seg?.height || 0,
|
|
10
|
+
width: seg?.width || 0,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
isPlayerRatioGreater: ratio.width > ratio.height,
|
|
15
|
+
height: ratio.height,
|
|
16
|
+
width: ratio.width,
|
|
17
|
+
};
|
|
18
|
+
}
|