mirror of
https://github.com/revoltchat/revite.git
synced 2024-11-29 10:20:59 -05:00
Re-write voice context. Working towards #21
This commit is contained in:
parent
0fe154c651
commit
4ec1ff5c59
7 changed files with 195 additions and 325 deletions
|
@ -1,213 +0,0 @@
|
||||||
import { Channel } from "revolt.js/dist/maps/Channels";
|
|
||||||
|
|
||||||
import { createContext } from "preact";
|
|
||||||
import {
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "preact/hooks";
|
|
||||||
|
|
||||||
import type { ProduceType, VoiceUser } from "../lib/vortex/Types";
|
|
||||||
import type VoiceClient from "../lib/vortex/VoiceClient";
|
|
||||||
|
|
||||||
import { Children } from "../types/Preact";
|
|
||||||
import { SoundContext } from "./Settings";
|
|
||||||
|
|
||||||
export enum VoiceStatus {
|
|
||||||
LOADING = 0,
|
|
||||||
UNAVAILABLE,
|
|
||||||
ERRORED,
|
|
||||||
READY = 3,
|
|
||||||
CONNECTING = 4,
|
|
||||||
AUTHENTICATING,
|
|
||||||
RTC_CONNECTING,
|
|
||||||
CONNECTED,
|
|
||||||
// RECONNECTING
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VoiceOperations {
|
|
||||||
connect: (channel: Channel) => Promise<Channel>;
|
|
||||||
disconnect: () => void;
|
|
||||||
isProducing: (type: ProduceType) => boolean;
|
|
||||||
startProducing: (type: ProduceType) => Promise<void>;
|
|
||||||
stopProducing: (type: ProduceType) => Promise<void> | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VoiceState {
|
|
||||||
roomId?: string;
|
|
||||||
status: VoiceStatus;
|
|
||||||
participants?: Readonly<Map<string, VoiceUser>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// They should be present from first render. - insert's words
|
|
||||||
export const VoiceContext = createContext<VoiceState>(null!);
|
|
||||||
export const VoiceOperationsContext = createContext<VoiceOperations>(null!);
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
children: Children;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function Voice({ children }: Props) {
|
|
||||||
const [client, setClient] = useState<VoiceClient | undefined>(undefined);
|
|
||||||
const [state, setState] = useState<VoiceState>({
|
|
||||||
status: VoiceStatus.LOADING,
|
|
||||||
participants: new Map(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const setStatus = useCallback(
|
|
||||||
(status: VoiceStatus, roomId?: string) => {
|
|
||||||
setState({
|
|
||||||
status,
|
|
||||||
roomId: roomId ?? client?.roomId,
|
|
||||||
participants: client?.participants ?? new Map(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// eslint-disable-next-line
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
import("../lib/vortex/VoiceClient")
|
|
||||||
.then(({ default: VoiceClient }) => {
|
|
||||||
const client = new VoiceClient();
|
|
||||||
setClient(client);
|
|
||||||
|
|
||||||
if (!client?.supported()) {
|
|
||||||
setStatus(VoiceStatus.UNAVAILABLE);
|
|
||||||
} else {
|
|
||||||
setStatus(VoiceStatus.READY);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Failed to load voice library!", err);
|
|
||||||
setStatus(VoiceStatus.UNAVAILABLE);
|
|
||||||
});
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const isConnecting = useRef(false);
|
|
||||||
const operations: VoiceOperations = useMemo(() => {
|
|
||||||
return {
|
|
||||||
connect: async (channel) => {
|
|
||||||
if (!client?.supported()) throw new Error("RTC is unavailable");
|
|
||||||
|
|
||||||
isConnecting.current = true;
|
|
||||||
setStatus(VoiceStatus.CONNECTING, channel._id);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const call = await channel.joinCall();
|
|
||||||
|
|
||||||
if (!isConnecting.current) {
|
|
||||||
setStatus(VoiceStatus.READY);
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ! TODO: use configuration to check if voso is enabled
|
|
||||||
// await client.connect("wss://voso.revolt.chat/ws");
|
|
||||||
await client.connect(
|
|
||||||
"wss://voso.revolt.chat/ws",
|
|
||||||
channel._id,
|
|
||||||
);
|
|
||||||
|
|
||||||
setStatus(VoiceStatus.AUTHENTICATING);
|
|
||||||
|
|
||||||
await client.authenticate(call.token);
|
|
||||||
setStatus(VoiceStatus.RTC_CONNECTING);
|
|
||||||
|
|
||||||
await client.initializeTransports();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
setStatus(VoiceStatus.READY);
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
setStatus(VoiceStatus.CONNECTED);
|
|
||||||
isConnecting.current = false;
|
|
||||||
return channel;
|
|
||||||
},
|
|
||||||
disconnect: () => {
|
|
||||||
if (!client?.supported()) throw new Error("RTC is unavailable");
|
|
||||||
|
|
||||||
// if (status <= VoiceStatus.READY) return;
|
|
||||||
// this will not update in this context
|
|
||||||
|
|
||||||
isConnecting.current = false;
|
|
||||||
client.disconnect();
|
|
||||||
setStatus(VoiceStatus.READY);
|
|
||||||
},
|
|
||||||
isProducing: (type: ProduceType) => {
|
|
||||||
switch (type) {
|
|
||||||
case "audio":
|
|
||||||
return client?.audioProducer !== undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
startProducing: async (type: ProduceType) => {
|
|
||||||
switch (type) {
|
|
||||||
case "audio": {
|
|
||||||
if (client?.audioProducer !== undefined)
|
|
||||||
return console.log("No audio producer."); // ! TODO: let the user know
|
|
||||||
if (navigator.mediaDevices === undefined)
|
|
||||||
return console.log("No media devices."); // ! TODO: let the user know
|
|
||||||
const mediaStream =
|
|
||||||
await navigator.mediaDevices.getUserMedia({
|
|
||||||
audio: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await client?.startProduce(
|
|
||||||
mediaStream.getAudioTracks()[0],
|
|
||||||
"audio",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stopProducing: (type: ProduceType) => {
|
|
||||||
return client?.stopProduce(type);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [client]);
|
|
||||||
|
|
||||||
const playSound = useContext(SoundContext);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!client?.supported()) return;
|
|
||||||
|
|
||||||
// ! TODO: message for fatal:
|
|
||||||
// ! get rid of these force updates
|
|
||||||
// ! handle it through state or smth
|
|
||||||
|
|
||||||
function stateUpdate() {
|
|
||||||
setStatus(state.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
client.on("startProduce", stateUpdate);
|
|
||||||
client.on("stopProduce", stateUpdate);
|
|
||||||
client.on("userJoined", stateUpdate);
|
|
||||||
client.on("userLeft", stateUpdate);
|
|
||||||
client.on("userStartProduce", stateUpdate);
|
|
||||||
client.on("userStopProduce", stateUpdate);
|
|
||||||
client.on("close", stateUpdate);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
client.removeListener("startProduce", stateUpdate);
|
|
||||||
client.removeListener("stopProduce", stateUpdate);
|
|
||||||
client.removeListener("userJoined", stateUpdate);
|
|
||||||
client.removeListener("userLeft", stateUpdate);
|
|
||||||
client.removeListener("userStartProduce", stateUpdate);
|
|
||||||
client.removeListener("userStopProduce", stateUpdate);
|
|
||||||
client.removeListener("close", stateUpdate);
|
|
||||||
};
|
|
||||||
}, [client, state, playSound, setStatus]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<VoiceContext.Provider value={state}>
|
|
||||||
<VoiceOperationsContext.Provider value={operations}>
|
|
||||||
{children}
|
|
||||||
</VoiceOperationsContext.Provider>
|
|
||||||
</VoiceContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ import { Children } from "../types/Preact";
|
||||||
import Locale from "./Locale";
|
import Locale from "./Locale";
|
||||||
import Settings from "./Settings";
|
import Settings from "./Settings";
|
||||||
import Theme from "./Theme";
|
import Theme from "./Theme";
|
||||||
import Voice from "./Voice";
|
|
||||||
import Intermediate from "./intermediate/Intermediate";
|
import Intermediate from "./intermediate/Intermediate";
|
||||||
import Client from "./revoltjs/RevoltClient";
|
import Client from "./revoltjs/RevoltClient";
|
||||||
|
|
||||||
|
@ -18,9 +17,7 @@ export default function Context({ children }: { children: Children }) {
|
||||||
<Settings>
|
<Settings>
|
||||||
<Locale>
|
<Locale>
|
||||||
<Intermediate>
|
<Intermediate>
|
||||||
<Client>
|
<Client>{children}</Client>
|
||||||
<Voice>{children}</Voice>
|
|
||||||
</Client>
|
|
||||||
</Intermediate>
|
</Intermediate>
|
||||||
</Locale>
|
</Locale>
|
||||||
</Settings>
|
</Settings>
|
||||||
|
|
161
src/lib/vortex/VoiceState.ts
Normal file
161
src/lib/vortex/VoiceState.ts
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
import { action, makeAutoObservable, runInAction } from "mobx";
|
||||||
|
import { Channel } from "revolt.js/dist/maps/Channels";
|
||||||
|
import { Nullable, toNullable } from "revolt.js/dist/util/null";
|
||||||
|
|
||||||
|
import type { ProduceType, VoiceUser } from "./Types";
|
||||||
|
import type VoiceClient from "./VoiceClient";
|
||||||
|
|
||||||
|
export enum VoiceStatus {
|
||||||
|
LOADING = 0,
|
||||||
|
UNAVAILABLE,
|
||||||
|
ERRORED,
|
||||||
|
READY = 3,
|
||||||
|
CONNECTING = 4,
|
||||||
|
UNLOADED = 5,
|
||||||
|
AUTHENTICATING,
|
||||||
|
RTC_CONNECTING,
|
||||||
|
CONNECTED,
|
||||||
|
// RECONNECTING
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an example of how to implement MobX state.
|
||||||
|
// * Note for better implementation:
|
||||||
|
// * MobX state should be implemented on the VoiceClient itself.
|
||||||
|
class VoiceStateReference {
|
||||||
|
client?: VoiceClient;
|
||||||
|
connecting?: boolean;
|
||||||
|
|
||||||
|
status: VoiceStatus;
|
||||||
|
roomId: Nullable<string>;
|
||||||
|
participants: Map<string, VoiceUser>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.roomId = null;
|
||||||
|
this.status = VoiceStatus.UNLOADED;
|
||||||
|
this.participants = new Map();
|
||||||
|
|
||||||
|
makeAutoObservable(this, {
|
||||||
|
client: false,
|
||||||
|
connecting: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This takes information from the voice
|
||||||
|
// client and applies it to the state here.
|
||||||
|
@action syncState() {
|
||||||
|
if (!this.client) return;
|
||||||
|
this.roomId = toNullable(this.client.roomId);
|
||||||
|
this.participants.clear();
|
||||||
|
this.client.participants.forEach((v, k) => this.participants.set(k, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This imports and constructs the voice client.
|
||||||
|
@action async loadVoice() {
|
||||||
|
if (this.status !== VoiceStatus.UNLOADED) return;
|
||||||
|
this.status = VoiceStatus.LOADING;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { default: VoiceClient } = await import("./VoiceClient");
|
||||||
|
const client = new VoiceClient();
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
if (!client.supported()) {
|
||||||
|
this.status = VoiceStatus.UNAVAILABLE;
|
||||||
|
} else {
|
||||||
|
this.status = VoiceStatus.READY;
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to load voice library!", err);
|
||||||
|
runInAction(() => {
|
||||||
|
this.status = VoiceStatus.UNAVAILABLE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to a voice channel.
|
||||||
|
@action async connect(channel: Channel) {
|
||||||
|
if (!this.client?.supported()) throw new Error("RTC is unavailable");
|
||||||
|
|
||||||
|
this.connecting = true;
|
||||||
|
this.status = VoiceStatus.CONNECTING;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const call = await channel.joinCall();
|
||||||
|
|
||||||
|
await this.client.connect("wss://voso.revolt.chat/ws", channel._id);
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this.status = VoiceStatus.AUTHENTICATING;
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.client.authenticate(call.token);
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this.status = VoiceStatus.RTC_CONNECTING;
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.client.initializeTransports();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this.status = VoiceStatus.READY;
|
||||||
|
});
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this.status = VoiceStatus.CONNECTED;
|
||||||
|
this.connecting = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect from current channel.
|
||||||
|
@action disconnect() {
|
||||||
|
if (!this.client?.supported()) throw new Error("RTC is unavailable");
|
||||||
|
|
||||||
|
this.connecting = false;
|
||||||
|
this.status = VoiceStatus.READY;
|
||||||
|
|
||||||
|
this.client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
isProducing(type: ProduceType) {
|
||||||
|
switch (type) {
|
||||||
|
case "audio":
|
||||||
|
return this.client?.audioProducer !== undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async startProducing(type: ProduceType) {
|
||||||
|
switch (type) {
|
||||||
|
case "audio": {
|
||||||
|
if (this.client?.audioProducer !== undefined)
|
||||||
|
return console.log("No audio producer."); // ! TODO: let the user know
|
||||||
|
|
||||||
|
if (navigator.mediaDevices === undefined)
|
||||||
|
return console.log("No media devices."); // ! TODO: let the user know
|
||||||
|
|
||||||
|
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.client?.startProduce(
|
||||||
|
mediaStream.getAudioTracks()[0],
|
||||||
|
"audio",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopProducing(type: ProduceType) {
|
||||||
|
this.client?.stopProduce(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const voiceState = new VoiceStateReference();
|
|
@ -8,13 +8,8 @@ import {
|
||||||
} from "@styled-icons/boxicons-solid";
|
} from "@styled-icons/boxicons-solid";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import { useContext } from "preact/hooks";
|
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
||||||
|
|
||||||
import {
|
|
||||||
VoiceContext,
|
|
||||||
VoiceOperationsContext,
|
|
||||||
VoiceStatus,
|
|
||||||
} from "../../../context/Voice";
|
|
||||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||||
|
|
||||||
import UpdateIndicator from "../../../components/common/UpdateIndicator";
|
import UpdateIndicator from "../../../components/common/UpdateIndicator";
|
||||||
|
@ -74,27 +69,27 @@ function VoiceActions({ channel }: Pick<ChannelHeaderProps, "channel">) {
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const voice = useContext(VoiceContext);
|
if (voiceState.status >= VoiceStatus.READY) {
|
||||||
const { connect, disconnect } = useContext(VoiceOperationsContext);
|
if (voiceState.roomId === channel._id) {
|
||||||
|
|
||||||
if (voice.status >= VoiceStatus.READY) {
|
|
||||||
if (voice.roomId === channel._id) {
|
|
||||||
return (
|
return (
|
||||||
<IconButton onClick={disconnect}>
|
<IconButton onClick={voiceState.disconnect}>
|
||||||
<PhoneOff size={22} />
|
<PhoneOff size={22} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
disconnect();
|
await voiceState.loadVoice();
|
||||||
connect(channel);
|
voiceState.disconnect();
|
||||||
|
voiceState.connect(channel);
|
||||||
}}>
|
}}>
|
||||||
<PhoneCall size={24} />
|
<PhoneCall size={24} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<PhoneCall size={24} /** ! FIXME: TEMP */ color="red" />
|
<PhoneCall size={24} /** ! FIXME: TEMP */ color="red" />
|
||||||
|
|
|
@ -72,7 +72,11 @@ function MessageRenderer({ id, state, queue, highlight }: Props) {
|
||||||
|
|
||||||
const subs = [
|
const subs = [
|
||||||
internalSubscribe("MessageRenderer", "edit_last", editLast),
|
internalSubscribe("MessageRenderer", "edit_last", editLast),
|
||||||
internalSubscribe("MessageRenderer", "edit_message", setEditing),
|
internalSubscribe(
|
||||||
|
"MessageRenderer",
|
||||||
|
"edit_message",
|
||||||
|
setEditing as (...args: unknown[]) => void,
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
return () => subs.forEach((unsub) => unsub());
|
return () => subs.forEach((unsub) => unsub());
|
||||||
|
|
|
@ -3,13 +3,10 @@ import { observer } from "mobx-react-lite";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import { useContext } from "preact/hooks";
|
import { useMemo } from "preact/hooks";
|
||||||
|
|
||||||
|
import { voiceState, VoiceStatus } from "../../../lib/vortex/VoiceState";
|
||||||
|
|
||||||
import {
|
|
||||||
VoiceContext,
|
|
||||||
VoiceOperationsContext,
|
|
||||||
VoiceStatus,
|
|
||||||
} from "../../../context/Voice";
|
|
||||||
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
import { useClient } from "../../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
import UserIcon from "../../../components/common/user/UserIcon";
|
import UserIcon from "../../../components/common/user/UserIcon";
|
||||||
|
@ -68,19 +65,16 @@ const VoiceBase = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default observer(({ id }: Props) => {
|
export default observer(({ id }: Props) => {
|
||||||
const { status, participants, roomId } = useContext(VoiceContext);
|
if (voiceState.roomId !== id) return null;
|
||||||
if (roomId !== id) return null;
|
|
||||||
|
|
||||||
const { isProducing, startProducing, stopProducing, disconnect } =
|
|
||||||
useContext(VoiceOperationsContext);
|
|
||||||
|
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const self = client.users.get(client.user!._id);
|
const self = client.users.get(client.user!._id);
|
||||||
|
|
||||||
//const ctx = useForceUpdate();
|
const keys = Array.from(voiceState.participants.keys());
|
||||||
//const self = useSelf(ctx);
|
const users = useMemo(() => {
|
||||||
const keys = participants ? Array.from(participants.keys()) : undefined;
|
return keys.map((key) => client.users.get(key));
|
||||||
const users = keys?.map((key) => client.users.get(key));
|
// eslint-disable-next-line
|
||||||
|
}, [keys]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VoiceBase>
|
<VoiceBase>
|
||||||
|
@ -95,7 +89,8 @@ export default observer(({ id }: Props) => {
|
||||||
target={user}
|
target={user}
|
||||||
status={false}
|
status={false}
|
||||||
voice={
|
voice={
|
||||||
participants!.get(id)?.audio
|
voiceState.participants!.get(id)
|
||||||
|
?.audio
|
||||||
? undefined
|
? undefined
|
||||||
: "muted"
|
: "muted"
|
||||||
}
|
}
|
||||||
|
@ -115,20 +110,20 @@ export default observer(({ id }: Props) => {
|
||||||
</div>
|
</div>
|
||||||
<div className="status">
|
<div className="status">
|
||||||
<BarChart size={20} />
|
<BarChart size={20} />
|
||||||
{status === VoiceStatus.CONNECTED && (
|
{voiceState.status === VoiceStatus.CONNECTED && (
|
||||||
<Text id="app.main.channel.voice.connected" />
|
<Text id="app.main.channel.voice.connected" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<Button error onClick={disconnect}>
|
<Button error onClick={voiceState.disconnect}>
|
||||||
<Text id="app.main.channel.voice.leave" />
|
<Text id="app.main.channel.voice.leave" />
|
||||||
</Button>
|
</Button>
|
||||||
{isProducing("audio") ? (
|
{voiceState.isProducing("audio") ? (
|
||||||
<Button onClick={() => stopProducing("audio")}>
|
<Button onClick={() => voiceState.stopProducing("audio")}>
|
||||||
<Text id="app.main.channel.voice.mute" />
|
<Text id="app.main.channel.voice.mute" />
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button onClick={() => startProducing("audio")}>
|
<Button onClick={() => voiceState.startProducing("audio")}>
|
||||||
<Text id="app.main.channel.voice.unmute" />
|
<Text id="app.main.channel.voice.unmute" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -136,71 +131,3 @@ export default observer(({ id }: Props) => {
|
||||||
</VoiceBase>
|
</VoiceBase>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**{voice.roomId === id && (
|
|
||||||
<div className={styles.rtc}>
|
|
||||||
<div className={styles.participants}>
|
|
||||||
{participants.length !== 0 ? participants.map((user, index) => {
|
|
||||||
const id = participantIds[index];
|
|
||||||
return (
|
|
||||||
<div key={id}>
|
|
||||||
<UserIcon
|
|
||||||
size={80}
|
|
||||||
user={user}
|
|
||||||
status={false}
|
|
||||||
voice={
|
|
||||||
voice.participants.get(id)
|
|
||||||
?.audio
|
|
||||||
? undefined
|
|
||||||
: "muted"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}) : self !== undefined && (
|
|
||||||
<div key={self._id} className={styles.disconnected}>
|
|
||||||
<UserIcon
|
|
||||||
size={80}
|
|
||||||
user={self}
|
|
||||||
status={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={styles.status}>
|
|
||||||
<BarChart size={20} />
|
|
||||||
{ voice.status === VoiceStatus.CONNECTED && <Text id="app.main.channel.voice.connected" /> }
|
|
||||||
</div>
|
|
||||||
<div className={styles.actions}>
|
|
||||||
<Button
|
|
||||||
style="error"
|
|
||||||
onClick={() =>
|
|
||||||
voice.operations.disconnect()
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text id="app.main.channel.voice.leave" />
|
|
||||||
</Button>
|
|
||||||
{voice.operations.isProducing("audio") ? (
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
voice.operations.stopProducing(
|
|
||||||
"audio"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text id="app.main.channel.voice.mute" />
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
voice.operations.startProducing(
|
|
||||||
"audio"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text id="app.main.channel.voice.unmute" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)} */
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ import { Text } from "preact-i18n";
|
||||||
import { useContext } from "preact/hooks";
|
import { useContext } from "preact/hooks";
|
||||||
|
|
||||||
import { stopPropagation } from "../../lib/stopPropagation";
|
import { stopPropagation } from "../../lib/stopPropagation";
|
||||||
|
import { voiceState } from "../../lib/vortex/VoiceState";
|
||||||
|
|
||||||
import { VoiceOperationsContext } from "../../context/Voice";
|
|
||||||
import { useIntermediate } from "../../context/intermediate/Intermediate";
|
import { useIntermediate } from "../../context/intermediate/Intermediate";
|
||||||
|
|
||||||
import UserIcon from "../../components/common/user/UserIcon";
|
import UserIcon from "../../components/common/user/UserIcon";
|
||||||
|
@ -29,7 +29,6 @@ interface Props {
|
||||||
export const Friend = observer(({ user }: Props) => {
|
export const Friend = observer(({ user }: Props) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { openScreen } = useIntermediate();
|
const { openScreen } = useIntermediate();
|
||||||
const { connect } = useContext(VoiceOperationsContext);
|
|
||||||
|
|
||||||
const actions: Children[] = [];
|
const actions: Children[] = [];
|
||||||
let subtext: Children = null;
|
let subtext: Children = null;
|
||||||
|
@ -46,7 +45,7 @@ export const Friend = observer(({ user }: Props) => {
|
||||||
ev,
|
ev,
|
||||||
user
|
user
|
||||||
.openDM()
|
.openDM()
|
||||||
.then(connect)
|
.then(voiceState.connect)
|
||||||
.then((x) => history.push(`/channel/${x._id}`)),
|
.then((x) => history.push(`/channel/${x._id}`)),
|
||||||
)
|
)
|
||||||
}>
|
}>
|
||||||
|
|
Loading…
Reference in a new issue