mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-22 10:22:30 +01:00
channel creation
This commit is contained in:
parent
5847ed2df0
commit
09a8401712
8
src-tauri/.idea/.gitignore
vendored
Normal file
8
src-tauri/.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
12
src-tauri/.idea/discord.xml
Normal file
12
src-tauri/.idea/discord.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="PROJECT_FILES" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
<option name="theme" value="material" />
|
||||||
|
<option name="button1Title" value="" />
|
||||||
|
<option name="button1Url" value="" />
|
||||||
|
<option name="button2Title" value="" />
|
||||||
|
<option name="button2Url" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
src-tauri/.idea/modules.xml
Normal file
8
src-tauri/.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/src-tauri.iml" filepath="$PROJECT_DIR$/.idea/src-tauri.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
11
src-tauri/.idea/src-tauri.iml
Normal file
11
src-tauri/.idea/src-tauri.iml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
src-tauri/.idea/vcs.xml
Normal file
6
src-tauri/.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -66,7 +66,7 @@ export const LabelWrapper = styled.div<{ error?: boolean }>`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: ${(props) => (props.error ? "var(--error)" : "var(--text)")};
|
color: ${(props) => (props.error ? "var(--error)" : "var(--text-header-secondary)")};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const InputErrorText = styled.label`
|
export const InputErrorText = styled.label`
|
||||||
@ -88,10 +88,9 @@ export const InputWrapper = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// TODO: Fix border hover causing small layout shift
|
export const Input = styled.input<{ error?: boolean; disableFocusRing?: boolean }>`
|
||||||
export const Input = styled.input<{ error?: boolean }>`
|
|
||||||
outline: none;
|
outline: none;
|
||||||
background: var(--background-secondary-alt);
|
background: var(--background-secondary);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -100,17 +99,23 @@ export const Input = styled.input<{ error?: boolean }>`
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: none;
|
||||||
aria-invalid: ${(props) => (props.error ? "true" : "false")};
|
aria-invalid: ${(props) => (props.error ? "true" : "false")};
|
||||||
border: ${(props) => (props.error ? "1px solid var(--error)" : "none")};
|
border: ${(props) => (props.error ? "1px solid var(--error)" : "1px solid var(--background-secondary)")};
|
||||||
|
|
||||||
&:focus {
|
${(props) =>
|
||||||
border: 1px solid var(--primary);
|
!props.disableFocusRing &&
|
||||||
}
|
`
|
||||||
|
&:focus {
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
|
||||||
// disabled styling
|
// disabled styling
|
||||||
&:disabled {
|
&:disabled {
|
||||||
background: var(--background-secondary-alt);
|
// TODO: this might need to be adjusted
|
||||||
|
background: var(--background-primary-alt);
|
||||||
color: var(--text-disabled);
|
color: var(--text-disabled);
|
||||||
border: 1px solid var(--background-secondary-alt);
|
border: 1px solid var(--background-secondary-alt);
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
|
@ -25,6 +25,7 @@ function ChannelList() {
|
|||||||
|
|
||||||
const active = app.activeChannelId === item.id;
|
const active = app.activeChannelId === item.id;
|
||||||
const isCategory = item.type === ChannelType.GuildCategory;
|
const isCategory = item.type === ChannelType.GuildCategory;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style}>
|
<div style={style}>
|
||||||
<ChannelListItem key={key} isCategory={isCategory} active={active} channel={item} />
|
<ChannelListItem key={key} isCategory={isCategory} active={active} channel={item} />
|
||||||
|
@ -2,6 +2,7 @@ import React, { useContext } from "react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
import { ContextMenuContext } from "../../contexts/ContextMenuContext";
|
||||||
|
import { modalController } from "../../controllers/modals";
|
||||||
import Channel from "../../stores/objects/Channel";
|
import Channel from "../../stores/objects/Channel";
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
import Floating from "../floating/Floating";
|
import Floating from "../floating/Floating";
|
||||||
@ -9,7 +10,7 @@ import FloatingTrigger from "../floating/FloatingTrigger";
|
|||||||
|
|
||||||
const ListItem = styled.div<{ isCategory?: boolean }>`
|
const ListItem = styled.div<{ isCategory?: boolean }>`
|
||||||
padding: ${(props) => (props.isCategory ? "16px 8px 0 0" : "1px 8px 0 0")};
|
padding: ${(props) => (props.isCategory ? "16px 8px 0 0" : "1px 8px 0 0")};
|
||||||
cursor: ${(props) => (props.isCategory ? "not-allowed" : "pointer")};
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Wrapper = styled.div<{ isCategory?: boolean; active?: boolean }>`
|
const Wrapper = styled.div<{ isCategory?: boolean; active?: boolean }>`
|
||||||
@ -45,7 +46,9 @@ function ChannelListItem({ channel, isCategory, active }: Props) {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const contextMenu = useContext(ContextMenuContext);
|
const contextMenu = useContext(ContextMenuContext);
|
||||||
|
|
||||||
const [hovered, setHovered] = React.useState(false);
|
const [wrapperHovered, setWrapperHovered] = React.useState(false);
|
||||||
|
const [createChannelHovered, setCreateChannelHovered] = React.useState(false);
|
||||||
|
const [createChannelDown, setChannelCreateDown] = React.useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
@ -63,8 +66,8 @@ function ChannelListItem({ channel, isCategory, active }: Props) {
|
|||||||
<Wrapper
|
<Wrapper
|
||||||
isCategory={isCategory}
|
isCategory={isCategory}
|
||||||
active={active}
|
active={active}
|
||||||
onMouseOver={() => setHovered(true)}
|
onMouseOver={() => setWrapperHovered(true)}
|
||||||
onMouseOut={() => setHovered(false)}
|
onMouseOut={() => setWrapperHovered(false)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -87,13 +90,13 @@ function ChannelListItem({ channel, isCategory, active }: Props) {
|
|||||||
<Icon
|
<Icon
|
||||||
icon="mdiChevronDown"
|
icon="mdiChevronDown"
|
||||||
size="12px"
|
size="12px"
|
||||||
color={hovered ? "var(--text)" : "var(--text-secondary)"}
|
color={wrapperHovered ? "var(--text)" : "var(--text-secondary)"}
|
||||||
style={{
|
style={{
|
||||||
marginRight: "8px",
|
marginRight: "8px",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Text isCategory={isCategory} hovered={hovered}>
|
<Text isCategory={isCategory} hovered={wrapperHovered}>
|
||||||
{channel.name}
|
{channel.name}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
@ -107,14 +110,37 @@ function ChannelListItem({ channel, isCategory, active }: Props) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FloatingTrigger>
|
<FloatingTrigger>
|
||||||
<span>
|
<span
|
||||||
|
onMouseOver={() => setCreateChannelHovered(true)}
|
||||||
|
onMouseOut={() => setCreateChannelHovered(false)}
|
||||||
|
onMouseDown={() => setChannelCreateDown(true)}
|
||||||
|
onMouseUp={() => setChannelCreateDown(false)}
|
||||||
|
onClick={() => {
|
||||||
|
if (!channel.guild) {
|
||||||
|
console.warn("No guild found for channel", channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modalController.push({
|
||||||
|
type: "create_channel",
|
||||||
|
guild: channel.guild,
|
||||||
|
category: channel,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="mdiPlus"
|
icon="mdiPlus"
|
||||||
size="18px"
|
size="18px"
|
||||||
style={{
|
style={{
|
||||||
marginLeft: "auto",
|
marginLeft: "auto",
|
||||||
}}
|
}}
|
||||||
color={hovered ? "var(--text)" : "var(--text-secondary)"}
|
color={
|
||||||
|
createChannelDown
|
||||||
|
? "var(--text-header)"
|
||||||
|
: createChannelHovered
|
||||||
|
? "var(--text)"
|
||||||
|
: "var(--text-secondary)"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</FloatingTrigger>
|
</FloatingTrigger>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: var(--background-secondary-alt);
|
background: var(--background-secondary);
|
||||||
border: none;
|
border: none;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
@ -3,7 +3,7 @@ import styled from "styled-components";
|
|||||||
// TODO: migrate some things from AuthComponents
|
// TODO: migrate some things from AuthComponents
|
||||||
|
|
||||||
export const InputSelect = styled.select`
|
export const InputSelect = styled.select`
|
||||||
background-color: var(--background-secondary-alt);
|
background-color: var(--background-secondary);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
outline: none;
|
outline: none;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
@ -24,16 +24,23 @@ function GuildMenuPopout() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onChannelCreateClick() {
|
||||||
|
modalController.push({
|
||||||
|
type: "create_channel",
|
||||||
|
guild: activeGuild!,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomContextMenu>
|
<CustomContextMenu>
|
||||||
<ContextMenuButton icon="mdiCog" disabled>
|
<ContextMenuButton icon="mdiCog" disabled>
|
||||||
Server Settings
|
Server Settings
|
||||||
</ContextMenuButton>
|
</ContextMenuButton>
|
||||||
<ContextMenuButton icon="mdiPlusCircle" disabled>
|
<ContextMenuButton icon="mdiPlusCircle" onClick={onChannelCreateClick}>
|
||||||
Create Channel
|
Create Channel
|
||||||
</ContextMenuButton>
|
</ContextMenuButton>
|
||||||
<ContextMenuButton icon="mdiFolderPlus" disabled>
|
<ContextMenuButton icon="mdiFolderPlus" disabled>
|
||||||
Create Channel
|
Create Category
|
||||||
</ContextMenuButton>
|
</ContextMenuButton>
|
||||||
<ContextMenuDivider />
|
<ContextMenuDivider />
|
||||||
<ContextMenuButton icon="mdiBell" disabled>
|
<ContextMenuButton icon="mdiBell" disabled>
|
||||||
|
322
src/components/modals/CreateChannelModel.tsx
Normal file
322
src/components/modals/CreateChannelModel.tsx
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import {
|
||||||
|
ChannelType,
|
||||||
|
RESTPostAPIGuildChannelJSONBody,
|
||||||
|
RESTPostAPIGuildChannelResult,
|
||||||
|
Routes,
|
||||||
|
} from "@spacebarchat/spacebar-api-types/v9";
|
||||||
|
import React from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import * as yup from "yup";
|
||||||
|
import { ModalProps, modalController } from "../../controllers/modals";
|
||||||
|
import { useAppStore } from "../../stores/AppStore";
|
||||||
|
import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
||||||
|
import { Input, InputErrorText } from "../AuthComponents";
|
||||||
|
import { TextDivider } from "../Divider";
|
||||||
|
import Icon, { IconType } from "../Icon";
|
||||||
|
import { Modal } from "./ModalComponents";
|
||||||
|
|
||||||
|
const CHANNEL_OPTIONS: {
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
icon: IconType;
|
||||||
|
type: ChannelType;
|
||||||
|
note?: string;
|
||||||
|
canBePrivate?: boolean;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
label: "Text",
|
||||||
|
description: "Send messages, images, and GIFs",
|
||||||
|
icon: "mdiPound",
|
||||||
|
type: ChannelType.GuildText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Voice",
|
||||||
|
description: "Hang out and talk with friends",
|
||||||
|
icon: "mdiVolumeHigh",
|
||||||
|
type: ChannelType.GuildVoice,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Announcement",
|
||||||
|
description: "Important updates for people in and out of the server",
|
||||||
|
icon: "mdiBullhorn",
|
||||||
|
type: ChannelType.GuildAnnouncement,
|
||||||
|
note: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||||
|
canBePrivate: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const TitleText = styled.h1`
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DescriptionText = styled.p`
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: var(--font-weight-regular);
|
||||||
|
color: var(--text-header-secondary);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Form = styled.form`
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const List = styled.ul`
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
gap: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ListItem = styled.li<{ active: boolean }>`
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: ${(props) =>
|
||||||
|
props.active ? "var(--background-secondary-highlight)" : "var(--background-secondary)"};
|
||||||
|
filter: brightness(${(props) => (props.active ? 1.3 : 1.1)});
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--background-secondary-highlight);
|
||||||
|
filter: ${(props) => (props.active ? "brightness(1.3)" : "brightness(1.1)")};
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
color: var(--text-header-secondary);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
& span:nth-child(2) {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: var(--font-weight-regular);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Label = styled.label`
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-header-secondary);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Section = styled.div<{ row?: boolean }>`
|
||||||
|
margin: 10px 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: ${(props) => (props.row ? "row" : "column")};
|
||||||
|
align-items: ${(props) => (props.row ? "center" : "stretch")};
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const LabelWrapper = styled.div<{ error?: boolean }>`
|
||||||
|
& label,
|
||||||
|
span {
|
||||||
|
color: ${(props) => (props.error ? "var(--error)" : "var(--text-header-secondary)")};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const schema = yup
|
||||||
|
.object({
|
||||||
|
type: yup
|
||||||
|
.number()
|
||||||
|
.oneOf(Object.values(ChannelType).filter((x) => typeof x === "number") as number[])
|
||||||
|
.required(),
|
||||||
|
name: yup.string().required(),
|
||||||
|
private: yup.boolean(),
|
||||||
|
})
|
||||||
|
.required();
|
||||||
|
|
||||||
|
export function CreateChannelModel({ guild, category, ...props }: ModalProps<"create_channel">) {
|
||||||
|
const app = useAppStore();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [isLoading, setLoading] = React.useState(false);
|
||||||
|
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
||||||
|
const {
|
||||||
|
formState: { disabled, isSubmitting, isValid, errors },
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
setError,
|
||||||
|
watch,
|
||||||
|
} = useForm({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
type: CHANNEL_OPTIONS[0].type,
|
||||||
|
private: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const isDisabled = disabled || isLoading || isSubmitting || !isValid;
|
||||||
|
|
||||||
|
const onSubmit = handleSubmit((data) => {
|
||||||
|
// set form loading
|
||||||
|
setLoading(true);
|
||||||
|
app.rest
|
||||||
|
.post<RESTPostAPIGuildChannelJSONBody, RESTPostAPIGuildChannelResult>(Routes.guildChannels(guild.id), {
|
||||||
|
name: data.name,
|
||||||
|
type: data.type,
|
||||||
|
parent_id: category?.id,
|
||||||
|
// permission_overwrites: []
|
||||||
|
})
|
||||||
|
.then((channel) => {
|
||||||
|
// add the new channel to the guild
|
||||||
|
app.channels.add(channel);
|
||||||
|
guild.channels_.add(channel.id);
|
||||||
|
|
||||||
|
// navigate to the new channel
|
||||||
|
navigate(`/channels/${guild.id}/${channel.id}`);
|
||||||
|
modalController.pop("close");
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
// error
|
||||||
|
if (e.errors) {
|
||||||
|
const t = messageFromFieldError(e.errors);
|
||||||
|
if (t) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
setError(t.field as any, {
|
||||||
|
type: "manual",
|
||||||
|
message: t.error,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError("type", {
|
||||||
|
type: "manual",
|
||||||
|
message: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("type", {
|
||||||
|
type: "manual",
|
||||||
|
message: e.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// only do this here because if successful there is no reason to re-enable the button
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const nameProps = register("name", {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
{...props}
|
||||||
|
title={<TitleText>Create Channel</TitleText>}
|
||||||
|
description={<DescriptionText>in {category ? category.name : guild.name}</DescriptionText>}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
onClick: onSubmit,
|
||||||
|
children: <span>Create Channel</span>,
|
||||||
|
palette: "primary",
|
||||||
|
size: "small",
|
||||||
|
disabled: isDisabled,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: () => {
|
||||||
|
modalController.pop("close");
|
||||||
|
},
|
||||||
|
children: <span>Cancel</span>,
|
||||||
|
palette: "link",
|
||||||
|
size: "small",
|
||||||
|
confirmation: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<List>
|
||||||
|
<Section>
|
||||||
|
<LabelWrapper error={!!errors.type}>
|
||||||
|
<Label>Channel Type</Label>
|
||||||
|
{errors.type && (
|
||||||
|
<>
|
||||||
|
<TextDivider>-</TextDivider>
|
||||||
|
<InputErrorText>{errors.type.message}</InputErrorText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</LabelWrapper>
|
||||||
|
{CHANNEL_OPTIONS.map((option, index) => (
|
||||||
|
<ListItem
|
||||||
|
active={index === selectedIndex}
|
||||||
|
key={index}
|
||||||
|
onClick={() => {
|
||||||
|
setValue("type", option.type);
|
||||||
|
setSelectedIndex(index);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "row",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.icon && <Icon icon={option.icon} color="var(--text-disabled)" size="24px" />}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "3px",
|
||||||
|
marginLeft: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{option.label}</span>
|
||||||
|
<span>{option.description}</span>
|
||||||
|
</div>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</Section>
|
||||||
|
<Section>
|
||||||
|
<LabelWrapper error={!!errors.name}>
|
||||||
|
<Label>Channel Name</Label>
|
||||||
|
{errors.name && (
|
||||||
|
<>
|
||||||
|
<TextDivider>-</TextDivider>
|
||||||
|
<InputErrorText>{errors.name.message}</InputErrorText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</LabelWrapper>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: "var(--background-secondary)",
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: "0 10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon={CHANNEL_OPTIONS[selectedIndex].icon} size="16px" color="var(--text)" />
|
||||||
|
<Input
|
||||||
|
{...nameProps}
|
||||||
|
onChange={(e) => {
|
||||||
|
e.target.value = e.target.value.replace(" ", "-");
|
||||||
|
nameProps.onChange(e);
|
||||||
|
}}
|
||||||
|
disableFocusRing
|
||||||
|
style={{
|
||||||
|
borderRadius: 8,
|
||||||
|
}}
|
||||||
|
placeholder="new-channel"
|
||||||
|
error={!!errors.name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
{/* // TODO: Add private channel support */}
|
||||||
|
{/* {(CHANNEL_OPTIONS[selectedIndex].canBePrivate ?? true) && (
|
||||||
|
<Section row>
|
||||||
|
<Label>Private Channel</Label>
|
||||||
|
<Input {...register("private", { required: false })} type="checkbox" />
|
||||||
|
</Section>
|
||||||
|
)} */}
|
||||||
|
</List>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -246,7 +246,7 @@ export function CreateInviteModal({ target, ...props }: ModalProps<"create_invit
|
|||||||
|
|
||||||
<InputWrapper
|
<InputWrapper
|
||||||
style={{
|
style={{
|
||||||
background: "var(--background-secondary-alt)",
|
background: "var(--background-secondary)",
|
||||||
borderRadius: "12px",
|
borderRadius: "12px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -16,7 +16,7 @@ interface ModalProps {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
onClose?: (force: boolean) => void;
|
onClose?: (force: boolean) => void;
|
||||||
signal?: "close" | "confirm" | "cancel";
|
signal?: "close" | "confirm" | "cancel";
|
||||||
title?: string;
|
title?: React.ReactNode;
|
||||||
description?: React.ReactNode;
|
description?: React.ReactNode;
|
||||||
transparent?: boolean;
|
transparent?: boolean;
|
||||||
nonDismissable?: boolean;
|
nonDismissable?: boolean;
|
||||||
@ -62,10 +62,10 @@ export const ModalBase = styled.div<{ closing?: boolean }>`
|
|||||||
> div {
|
> div {
|
||||||
animation-name: ${animationZoomOut};
|
animation-name: ${animationZoomOut};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
: css`
|
: css`
|
||||||
animation-name: ${animationFadeIn};
|
animation-name: ${animationFadeIn};
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,7 +96,7 @@ export const ModalWrapper = styled.div<
|
|||||||
!props.transparent &&
|
!props.transparent &&
|
||||||
css`
|
css`
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--background-secondary);
|
background: var(--background-primary);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
@ -126,7 +126,7 @@ export const ModalContentContainer = styled.div<Pick<ModalProps, "transparent" |
|
|||||||
${(props) =>
|
${(props) =>
|
||||||
!props.transparent &&
|
!props.transparent &&
|
||||||
css`
|
css`
|
||||||
background: var(--background-secondary);
|
background: var(--background-primary);
|
||||||
`}
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ const Actions = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
background: var(--background-primary);
|
background: var(--background-secondary);
|
||||||
border-radius: 0 0 4px 4px;
|
border-radius: 0 0 4px 4px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export * from "./AddServerModal";
|
export * from "./AddServerModal";
|
||||||
export * from "./BanMemberModal";
|
export * from "./BanMemberModal";
|
||||||
|
export * from "./CreateChannelModel";
|
||||||
export * from "./CreateInviteModal";
|
export * from "./CreateInviteModal";
|
||||||
export * from "./CreateServerModal";
|
export * from "./CreateServerModal";
|
||||||
export * from "./DeleteMessageModal";
|
export * from "./DeleteMessageModal";
|
||||||
|
@ -4,6 +4,7 @@ import { action, computed, makeObservable, observable } from "mobx";
|
|||||||
import {
|
import {
|
||||||
AddServerModal,
|
AddServerModal,
|
||||||
BanMemberModal,
|
BanMemberModal,
|
||||||
|
CreateChannelModel,
|
||||||
CreateInviteModal,
|
CreateInviteModal,
|
||||||
CreateServerModal,
|
CreateServerModal,
|
||||||
DeleteMessageModal,
|
DeleteMessageModal,
|
||||||
@ -154,7 +155,7 @@ export const modalController = new ModalControllerExtended({
|
|||||||
// block_user: Confirmation,
|
// block_user: Confirmation,
|
||||||
// unfriend_user: Confirmation,
|
// unfriend_user: Confirmation,
|
||||||
// create_category: CreateCategory,
|
// create_category: CreateCategory,
|
||||||
// create_channel: CreateChannel,
|
create_channel: CreateChannelModel,
|
||||||
// create_group: CreateGroup,
|
// create_group: CreateGroup,
|
||||||
create_invite: CreateInviteModal,
|
create_invite: CreateInviteModal,
|
||||||
// create_role: CreateRole,
|
// create_role: CreateRole,
|
||||||
|
@ -50,6 +50,11 @@ export type Modal = {
|
|||||||
height?: number;
|
height?: number;
|
||||||
isVideo?: boolean;
|
isVideo?: boolean;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: "create_channel";
|
||||||
|
guild: Guild;
|
||||||
|
category?: Channel;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {
|
export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {
|
||||||
|
@ -198,7 +198,7 @@ export default class Guild {
|
|||||||
reason
|
reason
|
||||||
? {
|
? {
|
||||||
"X-Audit-Log-Reason": reason,
|
"X-Audit-Log-Reason": reason,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -214,7 +214,7 @@ export default class Guild {
|
|||||||
reason
|
reason
|
||||||
? {
|
? {
|
||||||
"X-Audit-Log-Reason": reason,
|
"X-Audit-Log-Reason": reason,
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user