mirror of
https://github.com/revoltchat/revite.git
synced 2024-12-24 06:32:08 -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-tippy": "^1.4.0",
|
||||
"redux": "^4.1.0",
|
||||
"revolt.js": "4.3.1-alpha.0",
|
||||
"revolt.js": "4.3.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.35.1",
|
||||
"shade-blend-color": "^1.0.0",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useContext } from "preact/hooks";
|
||||
import { Hash } from "@styled-icons/feather";
|
||||
import { Channels } from "revolt.js/dist/api/objects";
|
||||
import { Hash, Volume2 } from "@styled-icons/feather";
|
||||
import { ImageIconBase, IconBaseProps } from "./IconBase";
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -15,15 +15,21 @@ export default function ChannelIcon(props: Props & Omit<JSX.HTMLAttributes<HTMLI
|
|||
|
||||
const { size, target, attachment, isServerChannel: server, animate, children, as, ...imgProps } = props;
|
||||
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 (isServerChannel) {
|
||||
if (target?.channel_type === 'VoiceChannel') {
|
||||
return (
|
||||
<Volume2 size={size} />
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Hash size={size} />
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
// ! fixme: replace fallback with <picture /> + <source />
|
||||
|
|
|
@ -22,13 +22,14 @@ interface Props {
|
|||
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: of dependencies. We only need to update on u/avatar.
|
||||
const user = useUser(message.author);
|
||||
const client = useContext(AppContext);
|
||||
|
||||
const content = message.content as string;
|
||||
const head = (message.replies && message.replies.length > 0) || preferHead;
|
||||
return (
|
||||
<MessageBase id={message._id}
|
||||
head={head}
|
||||
|
|
|
@ -16,7 +16,8 @@ export function useUnreads({ channel, unreads, dispatcher }: UnreadProps, contex
|
|||
function checkUnread(target?: Channel) {
|
||||
if (!target) 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;
|
||||
if (target.last_message) {
|
||||
|
|
|
@ -26,11 +26,11 @@ export type Screen =
|
|||
{ type: "delete_message", target: Channels.Message } |
|
||||
{ type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } |
|
||||
{ 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" } & (
|
||||
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
|
||||
{ type: "create_channel", server: string }
|
||||
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
|
||||
))
|
||||
| {
|
||||
id: "_input";
|
||||
|
|
|
@ -65,8 +65,7 @@ export function InputModal({
|
|||
}
|
||||
|
||||
type SpecialProps = { onClose: () => void } & (
|
||||
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" } |
|
||||
{ type: "create_channel", server: string }
|
||||
{ type: "create_group" | "create_server" | "set_custom_status" | "add_friend" }
|
||||
)
|
||||
|
||||
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": {
|
||||
return <InputModal
|
||||
onClose={onClose}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { ulid } from "ulid";
|
||||
import { Text } from "preact-i18n";
|
||||
import styles from './Prompt.module.scss';
|
||||
import { useHistory } from "react-router-dom";
|
||||
import Radio from "../../../components/ui/Radio";
|
||||
import { Children } from "../../../types/Preact";
|
||||
import { useIntermediate } from "../Intermediate";
|
||||
import InputBox from "../../../components/ui/InputBox";
|
||||
|
@ -44,7 +47,8 @@ type SpecialProps = { onClose: () => void } & (
|
|||
{ type: "delete_message", target: Channels.Message } |
|
||||
{ type: "create_invite", target: Channels.TextChannel | Channels.GroupChannel } |
|
||||
{ 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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ type Action =
|
|||
| { action: "set_presence"; presence: Users.Presence }
|
||||
| { action: "set_status" }
|
||||
| { action: "clear_status" }
|
||||
| { action: "create_channel"; server: string }
|
||||
| { action: "create_channel"; target: Servers.Server }
|
||||
| { action: "create_invite"; target: Channels.GroupChannel | Channels.TextChannel }
|
||||
| { action: "leave_group"; target: Channels.GroupChannel }
|
||||
| { action: "delete_channel"; target: Channels.TextChannel }
|
||||
|
@ -92,7 +92,8 @@ function ContextMenus(props: WithDispatcher) {
|
|||
break;
|
||||
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;
|
||||
props.dispatcher({
|
||||
|
@ -280,14 +281,13 @@ function ContextMenus(props: WithDispatcher) {
|
|||
case "delete_channel":
|
||||
case "delete_server":
|
||||
case "delete_message":
|
||||
case "create_channel":
|
||||
// @ts-expect-error
|
||||
case "create_invite": openScreen({ id: "special_prompt", type: data.action, target: data.target }); break;
|
||||
|
||||
case "ban_member":
|
||||
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_server_channel_settings": history.push(`/server/${data.server}/channel/${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) {
|
||||
let server = useServer(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.ManageChannels) generateAction({ action: 'create_channel', target: server });
|
||||
if (permissions & ServerPermission.ManageServer) generateAction({ action: 'open_server_settings', id: server_list });
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { useChannel, useForceUpdate } from "../../context/revoltjs/hooks";
|
|||
import MemberSidebar from "../../components/navigation/right/MemberSidebar";
|
||||
import JumpToBottom from "../../components/common/messaging/bars/JumpToBottom";
|
||||
import TypingIndicator from "../../components/common/messaging/bars/TypingIndicator";
|
||||
import { Channel } from "revolt.js";
|
||||
|
||||
const ChannelMain = styled.div`
|
||||
flex-grow: 1;
|
||||
|
@ -31,22 +32,36 @@ export function Channel({ id }: { id: string }) {
|
|||
const channel = useChannel(id, ctx);
|
||||
|
||||
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);
|
||||
|
||||
return (
|
||||
<>
|
||||
let id = channel._id;
|
||||
return <>
|
||||
<ChannelHeader channel={channel} toggleSidebar={() => setMembers(!showMembers)} />
|
||||
<ChannelMain>
|
||||
<ChannelContent>
|
||||
<MessageArea id={id} />
|
||||
<TypingIndicator id={channel._id} />
|
||||
<TypingIndicator id={id} />
|
||||
<JumpToBottom id={id} />
|
||||
<MessageBox channel={channel} />
|
||||
</ChannelContent>
|
||||
{ !isTouchscreenDevice && showMembers && <MemberSidebar channel={channel} /> }
|
||||
</ChannelMain>
|
||||
</>
|
||||
)
|
||||
</>;
|
||||
}
|
||||
|
||||
function VoiceChannel({ channel }: { channel: Channel }) {
|
||||
return <>
|
||||
<ChannelHeader channel={channel} />
|
||||
</>;
|
||||
}
|
||||
|
||||
export default function() {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
|||
import { FileUploader } from "../../../context/revoltjs/FileUploads";
|
||||
|
||||
interface Props {
|
||||
channel: Channels.GroupChannel | Channels.TextChannel;
|
||||
channel: Channels.GroupChannel | Channels.TextChannel | Channels.VoiceChannel;
|
||||
}
|
||||
|
||||
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"
|
||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||
|
||||
revolt.js@4.3.1-alpha.0:
|
||||
version "4.3.1-alpha.0"
|
||||
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.1-alpha.0.tgz#21abb0706852468a0b7991a80d81093f547d25f3"
|
||||
integrity sha512-YwDdDgioVYeBYkgZtgtXM37//96WmT18XVPJ7cBJzDQ3GWUKKPrw4VFjmi9FSh0ksfgfkSIrA7/hqmztZWbnVw==
|
||||
revolt.js@4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/revolt.js/-/revolt.js-4.3.2.tgz#2e613ff1d918d77266e9c777e226bfbddd5a9b87"
|
||||
integrity sha512-JyD3fRaory3Rhy/sAWcvHjLb/CluJRZap2Di2ZFFf9uiRJBgLNlClS/3RkBLAcQqx4KVx7Ua3WbKq1/dU6x7dQ==
|
||||
dependencies:
|
||||
"@insertish/mutable" "1.1.0"
|
||||
axios "^0.19.2"
|
||||
|
|
Loading…
Reference in a new issue