mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-24 22:52:09 -05:00
Add VoiceChannel support.
This commit is contained in:
parent
5df1c463a3
commit
babb53c794
12 changed files with 123 additions and 59 deletions
2
external/lang
vendored
2
external/lang
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 332cc2d7125b9cfb26ce211a9cb0fbf29301946c
|
Subproject commit f3d13c09b6fa2f28f027ce32643caffadbb63cf1
|
|
@ -73,7 +73,7 @@
|
||||||
"react-scroll": "^1.8.2",
|
"react-scroll": "^1.8.2",
|
||||||
"react-tippy": "^1.4.0",
|
"react-tippy": "^1.4.0",
|
||||||
"redux": "^4.1.0",
|
"redux": "^4.1.0",
|
||||||
"revolt.js": "4.3.1-alpha.0",
|
"revolt.js": "4.3.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"sass": "^1.35.1",
|
"sass": "^1.35.1",
|
||||||
"shade-blend-color": "^1.0.0",
|
"shade-blend-color": "^1.0.0",
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { useContext } from "preact/hooks";
|
import { useContext } from "preact/hooks";
|
||||||
import { Hash } from "@styled-icons/feather";
|
|
||||||
import { Channels } from "revolt.js/dist/api/objects";
|
import { Channels } from "revolt.js/dist/api/objects";
|
||||||
|
import { Hash, Volume2 } from "@styled-icons/feather";
|
||||||
import { ImageIconBase, IconBaseProps } from "./IconBase";
|
import { ImageIconBase, IconBaseProps } from "./IconBase";
|
||||||
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
import { AppContext } from "../../context/revoltjs/RevoltClient";
|
||||||
|
|
||||||
interface Props extends IconBaseProps<Channels.GroupChannel | Channels.TextChannel> {
|
interface Props extends IconBaseProps<Channels.GroupChannel | Channels.TextChannel | Channels.VoiceChannel> {
|
||||||
isServerChannel?: boolean;
|
isServerChannel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,19 @@ export default function ChannelIcon(props: Props & Omit<JSX.HTMLAttributes<HTMLI
|
||||||
|
|
||||||
const { size, target, attachment, isServerChannel: server, animate, children, as, ...imgProps } = props;
|
const { size, target, attachment, isServerChannel: server, animate, children, as, ...imgProps } = props;
|
||||||
const iconURL = client.generateFileURL(target?.icon ?? attachment, { max_side: 256 }, animate);
|
const iconURL = client.generateFileURL(target?.icon ?? attachment, { max_side: 256 }, animate);
|
||||||
const isServerChannel = server || target?.channel_type === 'TextChannel';
|
const isServerChannel = server || (target && (target.channel_type === 'TextChannel' || target.channel_type === 'VoiceChannel'));
|
||||||
|
|
||||||
if (typeof iconURL === 'undefined') {
|
if (typeof iconURL === 'undefined') {
|
||||||
if (isServerChannel) {
|
if (isServerChannel) {
|
||||||
return (
|
if (target?.channel_type === 'VoiceChannel') {
|
||||||
<Hash size={size} />
|
return (
|
||||||
)
|
<Volume2 size={size} />
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Hash size={size} />
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,14 @@ interface Props {
|
||||||
head?: boolean
|
head?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Message({ attachContext, message, contrast, content: replacement, head, queued }: Props) {
|
export default function Message({ attachContext, message, contrast, content: replacement, head: preferHead, queued }: Props) {
|
||||||
// TODO: Can improve re-renders here by providing a list
|
// TODO: Can improve re-renders here by providing a list
|
||||||
// TODO: of dependencies. We only need to update on u/avatar.
|
// TODO: of dependencies. We only need to update on u/avatar.
|
||||||
const user = useUser(message.author);
|
const user = useUser(message.author);
|
||||||
const client = useContext(AppContext);
|
const client = useContext(AppContext);
|
||||||
|
|
||||||
const content = message.content as string;
|
const content = message.content as string;
|
||||||
|
const head = (message.replies && message.replies.length > 0) || preferHead;
|
||||||
return (
|
return (
|
||||||
<MessageBase id={message._id}
|
<MessageBase id={message._id}
|
||||||
head={head}
|
head={head}
|
||||||
|
|
|
@ -16,7 +16,8 @@ export function useUnreads({ channel, unreads, dispatcher }: UnreadProps, contex
|
||||||
function checkUnread(target?: Channel) {
|
function checkUnread(target?: Channel) {
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
if (target._id !== channel._id) return;
|
if (target._id !== channel._id) return;
|
||||||
if (target?.channel_type === "SavedMessages") return;
|
if (target.channel_type === "SavedMessages" ||
|
||||||
|
target.channel_type === "VoiceChannel") return;
|
||||||
|
|
||||||
const unread = unreads[channel._id]?.last_id;
|
const unread = unreads[channel._id]?.last_id;
|
||||||
if (target.last_message) {
|
if (target.last_message) {
|
||||||
|
|
|
@ -26,11 +26,11 @@ export type Screen =
|
||||||
{ type: "delete_message", target: Channels.Message } |
|
{ type: "delete_message", target: Channels.Message } |
|
||||||
{ type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } |
|
{ type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } |
|
||||||
{ type: "kick_member", target: Servers.Server, user: string } |
|
{ type: "kick_member", target: Servers.Server, user: string } |
|
||||||
{ type: "ban_member", target: Servers.Server, user: string }
|
{ type: "ban_member", target: Servers.Server, user: string } |
|
||||||
|
{ type: "create_channel", target: Servers.Server }
|
||||||
)) |
|
)) |
|
||||||
({ id: "special_input" } & (
|
({ id: "special_input" } & (
|
||||||
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
|
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
|
||||||
{ type: "create_channel", server: string }
|
|
||||||
))
|
))
|
||||||
| {
|
| {
|
||||||
id: "_input";
|
id: "_input";
|
||||||
|
|
|
@ -65,8 +65,7 @@ export function InputModal({
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpecialProps = { onClose: () => void } & (
|
type SpecialProps = { onClose: () => void } & (
|
||||||
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
|
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
|
||||||
{ type: "create_channel", server: string }
|
|
||||||
)
|
)
|
||||||
|
|
||||||
export function SpecialInputModal(props: SpecialProps) {
|
export function SpecialInputModal(props: SpecialProps) {
|
||||||
|
@ -110,24 +109,6 @@ export function SpecialInputModal(props: SpecialProps) {
|
||||||
}}
|
}}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
case "create_channel": {
|
|
||||||
return <InputModal
|
|
||||||
onClose={onClose}
|
|
||||||
question={<Text id="app.context_menu.create_channel" />}
|
|
||||||
field={<Text id="app.main.servers.channel_name" />}
|
|
||||||
callback={async name => {
|
|
||||||
const channel = await client.servers.createChannel(
|
|
||||||
props.server,
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
nonce: ulid()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
history.push(`/server/${props.server}/channel/${channel._id}`);
|
|
||||||
}}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
case "set_custom_status": {
|
case "set_custom_status": {
|
||||||
return <InputModal
|
return <InputModal
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import { ulid } from "ulid";
|
||||||
import { Text } from "preact-i18n";
|
import { Text } from "preact-i18n";
|
||||||
import styles from './Prompt.module.scss';
|
import styles from './Prompt.module.scss';
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
import Radio from "../../../components/ui/Radio";
|
||||||
import { Children } from "../../../types/Preact";
|
import { Children } from "../../../types/Preact";
|
||||||
import { useIntermediate } from "../Intermediate";
|
import { useIntermediate } from "../Intermediate";
|
||||||
import InputBox from "../../../components/ui/InputBox";
|
import InputBox from "../../../components/ui/InputBox";
|
||||||
|
@ -44,7 +47,8 @@ type SpecialProps = { onClose: () => void } & (
|
||||||
{ type: "delete_message", target: Channels.Message } |
|
{ type: "delete_message", target: Channels.Message } |
|
||||||
{ type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } |
|
{ type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } |
|
||||||
{ type: "kick_member", target: Servers.Server, user: string } |
|
{ type: "kick_member", target: Servers.Server, user: string } |
|
||||||
{ type: "ban_member", target: Servers.Server, user: string }
|
{ type: "ban_member", target: Servers.Server, user: string } |
|
||||||
|
{ type: "create_channel", target: Servers.Server }
|
||||||
)
|
)
|
||||||
|
|
||||||
export function SpecialPromptModal(props: SpecialProps) {
|
export function SpecialPromptModal(props: SpecialProps) {
|
||||||
|
@ -263,6 +267,59 @@ export function SpecialPromptModal(props: SpecialProps) {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
case 'create_channel': {
|
||||||
|
const [ name, setName ] = useState('');
|
||||||
|
const [ type, setType ] = useState<'Text' | 'Voice'>('Text');
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PromptModal
|
||||||
|
onClose={onClose}
|
||||||
|
question={<Text id="app.context_menu.create_channel" />}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
confirmation: true,
|
||||||
|
contrast: true,
|
||||||
|
text: <Text id="app.special.modals.actions.create" />,
|
||||||
|
onClick: async () => {
|
||||||
|
setProcessing(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const channel = await client.servers.createChannel(
|
||||||
|
props.target._id,
|
||||||
|
{
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
nonce: ulid()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
history.push(`/server/${props.target._id}/channel/${channel._id}`);
|
||||||
|
onClose();
|
||||||
|
} catch (err) {
|
||||||
|
setError(takeError(err));
|
||||||
|
setProcessing(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ text: <Text id="app.special.modals.actions.cancel" />, onClick: onClose }
|
||||||
|
]}
|
||||||
|
content={<>
|
||||||
|
<Overline block type="subtle"><Text id="app.main.servers.channel_type" /></Overline>
|
||||||
|
<Radio checked={type === 'Text'} onSelect={() => setType('Text')}>
|
||||||
|
<Text id="app.main.servers.text_channel" /></Radio>
|
||||||
|
<Radio checked={type === 'Voice'} onSelect={() => setType('Voice')}>
|
||||||
|
<Text id="app.main.servers.voice_channel" /></Radio>
|
||||||
|
<Overline block type="subtle"><Text id="app.main.servers.channel_name" /></Overline>
|
||||||
|
<InputBox
|
||||||
|
value={name}
|
||||||
|
onChange={e => setName(e.currentTarget.value)} />
|
||||||
|
</>}
|
||||||
|
disabled={processing}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ type Action =
|
||||||
| { action: "set_presence"; presence: Users.Presence }
|
| { action: "set_presence"; presence: Users.Presence }
|
||||||
| { action: "set_status" }
|
| { action: "set_status" }
|
||||||
| { action: "clear_status" }
|
| { action: "clear_status" }
|
||||||
| { action: "create_channel"; server: string }
|
| { action: "create_channel"; target: Servers.Server }
|
||||||
| { action: "create_invite"; target: Channels.GroupChannel | Channels.TextChannel }
|
| { action: "create_invite"; target: Channels.GroupChannel | Channels.TextChannel }
|
||||||
| { action: "leave_group"; target: Channels.GroupChannel }
|
| { action: "leave_group"; target: Channels.GroupChannel }
|
||||||
| { action: "delete_channel"; target: Channels.TextChannel }
|
| { action: "delete_channel"; target: Channels.TextChannel }
|
||||||
|
@ -92,7 +92,8 @@ function ContextMenus(props: WithDispatcher) {
|
||||||
break;
|
break;
|
||||||
case "mark_as_read":
|
case "mark_as_read":
|
||||||
{
|
{
|
||||||
if (data.channel.channel_type === 'SavedMessages') return;
|
if (data.channel.channel_type === 'SavedMessages' ||
|
||||||
|
data.channel.channel_type === 'VoiceChannel') return;
|
||||||
|
|
||||||
let message = data.channel.channel_type === 'TextChannel' ? data.channel.last_message : data.channel.last_message._id;
|
let message = data.channel.channel_type === 'TextChannel' ? data.channel.last_message : data.channel.last_message._id;
|
||||||
props.dispatcher({
|
props.dispatcher({
|
||||||
|
@ -280,14 +281,13 @@ function ContextMenus(props: WithDispatcher) {
|
||||||
case "delete_channel":
|
case "delete_channel":
|
||||||
case "delete_server":
|
case "delete_server":
|
||||||
case "delete_message":
|
case "delete_message":
|
||||||
|
case "create_channel":
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
case "create_invite": openScreen({ id: "special_prompt", type: data.action, target: data.target }); break;
|
case "create_invite": openScreen({ id: "special_prompt", type: data.action, target: data.target }); break;
|
||||||
|
|
||||||
case "ban_member":
|
case "ban_member":
|
||||||
case "kick_member": openScreen({ id: "special_prompt", type: data.action, target: data.target, user: data.user }); break;
|
case "kick_member": openScreen({ id: "special_prompt", type: data.action, target: data.target, user: data.user }); break;
|
||||||
|
|
||||||
case "create_channel": openScreen({ id: "special_input", type: "create_channel", server: data.server }); break;
|
|
||||||
|
|
||||||
case "open_channel_settings": history.push(`/channel/${data.id}/settings`); break;
|
case "open_channel_settings": history.push(`/channel/${data.id}/settings`); break;
|
||||||
case "open_server_channel_settings": history.push(`/server/${data.server}/channel/${data.id}/settings`); break;
|
case "open_server_channel_settings": history.push(`/server/${data.server}/channel/${data.id}/settings`); break;
|
||||||
case "open_server_settings": history.push(`/server/${data.id}/settings`); break;
|
case "open_server_settings": history.push(`/server/${data.id}/settings`); break;
|
||||||
|
@ -341,9 +341,12 @@ function ContextMenus(props: WithDispatcher) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server_list) {
|
if (server_list) {
|
||||||
|
let server = useServer(server_list, forceUpdate);
|
||||||
let permissions = useServerPermission(server_list, forceUpdate);
|
let permissions = useServerPermission(server_list, forceUpdate);
|
||||||
if (permissions & ServerPermission.ManageChannels) generateAction({ action: 'create_channel', server: server_list });
|
if (server) {
|
||||||
if (permissions & ServerPermission.ManageServer) generateAction({ action: 'open_server_settings', id: server_list });
|
if (permissions & ServerPermission.ManageChannels) generateAction({ action: 'create_channel', target: server });
|
||||||
|
if (permissions & ServerPermission.ManageServer) generateAction({ action: 'open_server_settings', id: server_list });
|
||||||
|
}
|
||||||
|
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { useChannel, useForceUpdate } from "../../context/revoltjs/hooks";
|
||||||
import MemberSidebar from "../../components/navigation/right/MemberSidebar";
|
import MemberSidebar from "../../components/navigation/right/MemberSidebar";
|
||||||
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
||||||
import TypingIndicator from "../../components/common/messaging/bars/TypingIndicator";
|
import TypingIndicator from "../../components/common/messaging/bars/TypingIndicator";
|
||||||
|
import { Channel } from "revolt.js";
|
||||||
|
|
||||||
const ChannelMain = styled.div`
|
const ChannelMain = styled.div`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
@ -31,22 +32,36 @@ export function Channel({ id }: { id: string }) {
|
||||||
const channel = useChannel(id, ctx);
|
const channel = useChannel(id, ctx);
|
||||||
|
|
||||||
if (!channel) return null;
|
if (!channel) return null;
|
||||||
|
|
||||||
|
if (channel.channel_type === 'VoiceChannel') {
|
||||||
|
return <VoiceChannel channel={channel} />;
|
||||||
|
} else {
|
||||||
|
return <TextChannel channel={channel} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function TextChannel({ channel }: { channel: Channel }) {
|
||||||
const [ showMembers, setMembers ] = useState(true);
|
const [ showMembers, setMembers ] = useState(true);
|
||||||
|
|
||||||
return (
|
let id = channel._id;
|
||||||
<>
|
return <>
|
||||||
<ChannelHeader channel={channel} toggleSidebar={() => setMembers(!showMembers)} />
|
<ChannelHeader channel={channel} toggleSidebar={() => setMembers(!showMembers)} />
|
||||||
<ChannelMain>
|
<ChannelMain>
|
||||||
<ChannelContent>
|
<ChannelContent>
|
||||||
<MessageArea id={id} />
|
<MessageArea id={id} />
|
||||||
<TypingIndicator id={channel._id} />
|
<TypingIndicator id={id} />
|
||||||
<JumpToBottom id={id} />
|
<JumpToBottom id={id} />
|
||||||
<MessageBox channel={channel} />
|
<MessageBox channel={channel} />
|
||||||
</ChannelContent>
|
</ChannelContent>
|
||||||
{ !isTouchscreenDevice && showMembers && <MemberSidebar channel={channel} /> }
|
{ !isTouchscreenDevice && showMembers && <MemberSidebar channel={channel} /> }
|
||||||
</ChannelMain>
|
</ChannelMain>
|
||||||
</>
|
</>;
|
||||||
)
|
}
|
||||||
|
|
||||||
|
function VoiceChannel({ channel }: { channel: Channel }) {
|
||||||
|
return <>
|
||||||
|
<ChannelHeader channel={channel} />
|
||||||
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function() {
|
export default function() {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
||||||
import { FileUploader } from "../../../context/revoltjs/FileUploads";
|
import { FileUploader } from "../../../context/revoltjs/FileUploads";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
channel: Channels.GroupChannel | Channels.TextChannel;
|
channel: Channels.GroupChannel | Channels.TextChannel | Channels.VoiceChannel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Overview({ channel }: Props) {
|
export function Overview({ channel }: Props) {
|
||||||
|
|
|
@ -3344,10 +3344,10 @@ reusify@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||||
|
|
||||||
revolt.js@4.3.1-alpha.0:
|
revolt.js@4.3.2:
|
||||||
version "4.3.1-alpha.0"
|
version "4.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.1-alpha.0.tgz#21abb0706852468a0b7991a80d81093f547d25f3"
|
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.2.tgz#2e613ff1d918d77266e9c777e226bfbddd5a9b87"
|
||||||
integrity sha512-YwDdDgioVYeBYkgZtgtXM37//96WmT18XVPJ7cBJzDQ3GWUKKPrw4VFjmi9FSh0ksfgfkSIrA7/hqmztZWbnVw==
|
integrity sha512-JyD3fRaory3Rhy/sAWcvHjLb/CluJRZap2Di2ZFFf9uiRJBgLNlClS/3RkBLAcQqx4KVx7Ua3WbKq1/dU6x7dQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@insertish/mutable" "1.1.0"
|
"@insertish/mutable" "1.1.0"
|
||||||
axios "^0.19.2"
|
axios "^0.19.2"
|
||||||
|
|
Loading…
Reference in a new issue