(fix): joining a call while already being in a call wouldn't reset the call state
(qol): format
This commit is contained in:
parent
be26e486d4
commit
4dbf48b3f9
8 changed files with 85 additions and 50 deletions
|
|
@ -1,10 +1,5 @@
|
||||||
import { useTTP } from "@tensamin/ttp";
|
import { useTTP } from "@tensamin/ttp";
|
||||||
import {
|
import { Button, Popover, PopoverContent, PopoverTrigger } from "@tensamin/ui";
|
||||||
Button,
|
|
||||||
Popover,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverTrigger,
|
|
||||||
} from "@tensamin/ui";
|
|
||||||
import Wrapper from "@tensamin/user/wrapper";
|
import Wrapper from "@tensamin/user/wrapper";
|
||||||
import { Mail } from "lucide-react";
|
import { Mail } from "lucide-react";
|
||||||
import { sendCallInvite } from "../../store";
|
import { sendCallInvite } from "../../store";
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,8 @@ export default function Base({
|
||||||
{/* Detect video / user and place here */}
|
{/* Detect video / user and place here */}
|
||||||
|
|
||||||
{type === "stream" &&
|
{type === "stream" &&
|
||||||
(screenSharePublication?.isSubscribed && screenSharePublication.track ? (
|
(screenSharePublication?.isSubscribed &&
|
||||||
|
screenSharePublication.track ? (
|
||||||
<VideoViewer
|
<VideoViewer
|
||||||
flush={flush}
|
flush={flush}
|
||||||
participantId={participant.identity}
|
participantId={participant.identity}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ export default function TopBar() {
|
||||||
const room = useCall((state) => state.room);
|
const room = useCall((state) => state.room);
|
||||||
const view = useCall((state) => state.view);
|
const view = useCall((state) => state.view);
|
||||||
|
|
||||||
const userIds = Array.from(room.remoteParticipants.values(), (participant) => {
|
const userIds = Array.from(
|
||||||
const participantId = Number(participant.identity);
|
room.remoteParticipants.values(),
|
||||||
return Number.isInteger(participantId) && participantId > 0
|
(participant) => {
|
||||||
? participantId
|
const participantId = Number(participant.identity);
|
||||||
: null;
|
return Number.isInteger(participantId) && participantId > 0
|
||||||
}).filter((participantId): participantId is number => participantId != null);
|
? participantId
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
).filter((participantId): participantId is number => participantId != null);
|
||||||
const userIdsKey = userIds.join(",");
|
const userIdsKey = userIds.join(",");
|
||||||
|
|
||||||
const [users, setUsers] = useState<User[]>([]);
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,13 @@ export function createScreenShareController({
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
screenShareSession.tracks.map((track) =>
|
screenShareSession.tracks.map((track) =>
|
||||||
room.localParticipant.unpublishTrack(track, true).catch((error) => {
|
room.localParticipant.unpublishTrack(track, true).catch((error) => {
|
||||||
log(1, "call", "red", "Failed to unpublish screen share track", error);
|
log(
|
||||||
|
1,
|
||||||
|
"call",
|
||||||
|
"red",
|
||||||
|
"Failed to unpublish screen share track",
|
||||||
|
error,
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -194,15 +200,12 @@ export function createScreenShareController({
|
||||||
});
|
});
|
||||||
}, 125);
|
}, 125);
|
||||||
|
|
||||||
await publishScreenShareTracks(
|
await publishScreenShareTracks([videoTrack], () => {
|
||||||
[videoTrack],
|
stopped = true;
|
||||||
() => {
|
window.clearInterval(interval);
|
||||||
stopped = true;
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
window.clearInterval(interval);
|
canvas.remove();
|
||||||
stream.getTracks().forEach((track) => track.stop());
|
});
|
||||||
canvas.remove();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stopScreenShare() {
|
async function stopScreenShare() {
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,8 @@ function getActiveScreenShareParticipantIds(): number[] {
|
||||||
.map((participant) => ({
|
.map((participant) => ({
|
||||||
participantId: getParticipantId(participant.identity),
|
participantId: getParticipantId(participant.identity),
|
||||||
hasScreenShare:
|
hasScreenShare:
|
||||||
participant.getTrackPublication(Track.Source.ScreenShare)?.track != null,
|
participant.getTrackPublication(Track.Source.ScreenShare)?.track !=
|
||||||
|
null,
|
||||||
}))
|
}))
|
||||||
.filter(
|
.filter(
|
||||||
(entry): entry is { participantId: number; hasScreenShare: true } =>
|
(entry): entry is { participantId: number; hasScreenShare: true } =>
|
||||||
|
|
@ -290,18 +291,21 @@ export async function sendCallInvite(userId: number) {
|
||||||
|
|
||||||
const ownUserId = (await runtime.load("user_id")) as number;
|
const ownUserId = (await runtime.load("user_id")) as number;
|
||||||
const privateKey = await runtime.load("private_key");
|
const privateKey = await runtime.load("private_key");
|
||||||
const ownPublicKey = await runtime.getUser(ownUserId).then(
|
const ownPublicKey = await runtime
|
||||||
(data) => data.public_key,
|
.getUser(ownUserId)
|
||||||
);
|
.then((data) => data.public_key);
|
||||||
const remotePublicKey = await runtime.getUser(userId).then(
|
const remotePublicKey = await runtime
|
||||||
(data) => data.public_key,
|
.getUser(userId)
|
||||||
);
|
.then((data) => data.public_key);
|
||||||
const sharedSecret = await runtime.getSharedSecret(
|
const sharedSecret = await runtime.getSharedSecret(
|
||||||
privateKey,
|
privateKey,
|
||||||
ownPublicKey,
|
ownPublicKey,
|
||||||
remotePublicKey,
|
remotePublicKey,
|
||||||
);
|
);
|
||||||
const encryptedCallSecret = await runtime.encryptText(sharedSecret, callSecret);
|
const encryptedCallSecret = await runtime.encryptText(
|
||||||
|
sharedSecret,
|
||||||
|
callSecret,
|
||||||
|
);
|
||||||
|
|
||||||
await runtime.send("call_invite", {
|
await runtime.send("call_invite", {
|
||||||
receiver_id: userId,
|
receiver_id: userId,
|
||||||
|
|
@ -373,8 +377,9 @@ export function stopWatchingFocusedStream() {
|
||||||
stopWatchingStream(focusedParticipantId);
|
stopWatchingStream(focusedParticipantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let screenShareController: ReturnType<typeof createScreenShareController> | null =
|
let screenShareController: ReturnType<
|
||||||
null;
|
typeof createScreenShareController
|
||||||
|
> | null = null;
|
||||||
|
|
||||||
function getScreenShareController() {
|
function getScreenShareController() {
|
||||||
if (!screenShareController) {
|
if (!screenShareController) {
|
||||||
|
|
@ -390,7 +395,8 @@ function getScreenShareController() {
|
||||||
: updater,
|
: updater,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
getLocalParticipantId: () => getParticipantId(room.localParticipant.identity),
|
getLocalParticipantId: () =>
|
||||||
|
getParticipantId(room.localParticipant.identity),
|
||||||
startWatching: startWatchingStream,
|
startWatching: startWatchingStream,
|
||||||
stopWatching: stopWatchingStream,
|
stopWatching: stopWatchingStream,
|
||||||
syncParticipantState,
|
syncParticipantState,
|
||||||
|
|
@ -439,7 +445,13 @@ export async function disconnect() {
|
||||||
try {
|
try {
|
||||||
await getScreenShareController().clearPublishedScreenShare();
|
await getScreenShareController().clearPublishedScreenShare();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(1, "call", "red", "Failed to clear screen share during disconnect", error);
|
log(
|
||||||
|
1,
|
||||||
|
"call",
|
||||||
|
"red",
|
||||||
|
"Failed to clear screen share during disconnect",
|
||||||
|
error,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
useCall.setState({
|
useCall.setState({
|
||||||
|
|
@ -463,7 +475,7 @@ export async function disconnect() {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
room.disconnect();
|
await room.disconnect();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(1, "call", "red", "Failed to disconnect from room", error);
|
log(1, "call", "red", "Failed to disconnect from room", error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -480,6 +492,11 @@ export async function joinCall(
|
||||||
sendInvite = true,
|
sendInvite = true,
|
||||||
) {
|
) {
|
||||||
const runtime = requireRuntime(useCall.getState().runtime);
|
const runtime = requireRuntime(useCall.getState().runtime);
|
||||||
|
const state = useCall.getState().state;
|
||||||
|
|
||||||
|
if (state !== "closed") {
|
||||||
|
await disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
log(2, "call", "purple", "Call creation initialised");
|
log(2, "call", "purple", "Call creation initialised");
|
||||||
useCall.setState({
|
useCall.setState({
|
||||||
|
|
@ -754,11 +771,10 @@ export function useInitializeCall() {
|
||||||
|
|
||||||
if (invitedUserId != null) {
|
if (invitedUserId != null) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
void sendCallInvite(invitedUserId)
|
void sendCallInvite(invitedUserId).catch((error) => {
|
||||||
.catch((error) => {
|
toast("error", "Failed to send call invite.");
|
||||||
toast("error", "Failed to send call invite.");
|
log(1, "call", "red", "Failed to send call invite", error);
|
||||||
log(1, "call", "red", "Failed to send call invite", error);
|
});
|
||||||
});
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,10 @@ export default function View() {
|
||||||
(state) => state.activeScreenShareParticipantIds,
|
(state) => state.activeScreenShareParticipantIds,
|
||||||
);
|
);
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [focusedTileSize, setFocusedTileSize] = useState({ width: 0, height: 0 });
|
const [focusedTileSize, setFocusedTileSize] = useState({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
const [isFocusedTileFlush, setIsFocusedTileFlush] = useState(false);
|
const [isFocusedTileFlush, setIsFocusedTileFlush] = useState(false);
|
||||||
const [participantVersion, setParticipantVersion] = useState(0);
|
const [participantVersion, setParticipantVersion] = useState(0);
|
||||||
|
|
||||||
|
|
@ -42,7 +45,7 @@ export default function View() {
|
||||||
const participantId = Number(participant.identity);
|
const participantId = Number(participant.identity);
|
||||||
return Number.isInteger(participantId) && participantId > 0;
|
return Number.isInteger(participantId) && participantId > 0;
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [participantVersion, room]);
|
}, [participantVersion, room]);
|
||||||
|
|
||||||
const userIds = useMemo(
|
const userIds = useMemo(
|
||||||
|
|
@ -141,7 +144,10 @@ export default function View() {
|
||||||
activeScreenShareParticipantIdSet.has(focusedParticipantId);
|
activeScreenShareParticipantIdSet.has(focusedParticipantId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className="flex h-full w-full items-center justify-center overflow-hidden">
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className="flex h-full w-full items-center justify-center overflow-hidden"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className="flex max-h-full w-full flex-col items-center overflow-hidden"
|
className="flex max-h-full w-full flex-col items-center overflow-hidden"
|
||||||
style={{ gap: tiles.length > 0 ? STACK_GAP_PX : 0 }}
|
style={{ gap: tiles.length > 0 ? STACK_GAP_PX : 0 }}
|
||||||
|
|
|
||||||
|
|
@ -129,13 +129,16 @@ export default function View() {
|
||||||
}, [room]);
|
}, [room]);
|
||||||
|
|
||||||
const users = useMemo(() => {
|
const users = useMemo(() => {
|
||||||
const participants = [...room.remoteParticipants.values(), room.localParticipant];
|
const participants = [
|
||||||
|
...room.remoteParticipants.values(),
|
||||||
|
room.localParticipant,
|
||||||
|
];
|
||||||
|
|
||||||
return participants.filter((participant) => {
|
return participants.filter((participant) => {
|
||||||
const participantId = Number(participant.identity);
|
const participantId = Number(participant.identity);
|
||||||
return Number.isInteger(participantId) && participantId > 0;
|
return Number.isInteger(participantId) && participantId > 0;
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [participantVersion, room]);
|
}, [participantVersion, room]);
|
||||||
|
|
||||||
const userIds = useMemo(
|
const userIds = useMemo(
|
||||||
|
|
@ -195,9 +198,13 @@ export default function View() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} style={{
|
<div
|
||||||
height: "calc(100% - 2rem)"
|
ref={containerRef}
|
||||||
}} className="w-full overflow-hidden p-3">
|
style={{
|
||||||
|
height: "calc(100% - 2rem)",
|
||||||
|
}}
|
||||||
|
className="w-full overflow-hidden p-3"
|
||||||
|
>
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center gap-3">
|
<div className="flex h-full w-full flex-col items-center justify-center gap-3">
|
||||||
{rows.map((row) => (
|
{rows.map((row) => (
|
||||||
<div key={row.rowIndex} className="flex justify-center gap-3">
|
<div key={row.rowIndex} className="flex justify-center gap-3">
|
||||||
|
|
@ -217,7 +224,10 @@ export default function View() {
|
||||||
height: layout.tileHeight,
|
height: layout.tileHeight,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Base type={tile.kind} participant={getParticipantById(tile.participantId)} />
|
<Base
|
||||||
|
type={tile.kind}
|
||||||
|
participant={getParticipantById(tile.participantId)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@
|
||||||
- Buttons
|
- Buttons
|
||||||
- Preview image
|
- Preview image
|
||||||
- Mobile
|
- Mobile
|
||||||
|
- Deaf status is not synced
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue