feat: port input modals to new system

This commit is contained in:
Paul Makles 2022-07-05 17:53:41 +01:00
parent 23dec32476
commit 79c90c1b00
19 changed files with 275 additions and 127 deletions

View file

@ -73,7 +73,7 @@
"@hcaptcha/react-hcaptcha": "^0.3.6", "@hcaptcha/react-hcaptcha": "^0.3.6",
"@insertish/vite-plugin-babel-macros": "^1.0.5", "@insertish/vite-plugin-babel-macros": "^1.0.5",
"@preact/preset-vite": "^2.0.0", "@preact/preset-vite": "^2.0.0",
"@revoltchat/ui": "1.0.58", "@revoltchat/ui": "1.0.61",
"@rollup/plugin-replace": "^2.4.2", "@rollup/plugin-replace": "^2.4.2",
"@styled-icons/boxicons-logos": "^10.38.0", "@styled-icons/boxicons-logos": "^10.38.0",
"@styled-icons/boxicons-regular": "^10.38.0", "@styled-icons/boxicons-regular": "^10.38.0",

View file

@ -25,6 +25,7 @@ import { useIntermediate } from "../../../context/intermediate/Intermediate";
import placeholderSVG from "../items/placeholder.svg"; import placeholderSVG from "../items/placeholder.svg";
import { useClient } from "../../../controllers/client/ClientController"; import { useClient } from "../../../controllers/client/ClientController";
import { modalController } from "../../../controllers/modals/ModalController";
import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase"; import { GenericSidebarBase, GenericSidebarList } from "../SidebarBase";
import ButtonItem, { ChannelButton } from "../items/ButtonItem"; import ButtonItem, { ChannelButton } from "../items/ButtonItem";
import ConnectionStatus from "../items/ConnectionStatus"; import ConnectionStatus from "../items/ConnectionStatus";
@ -131,8 +132,7 @@ export default observer(() => {
<Text id="app.main.categories.conversations" /> <Text id="app.main.categories.conversations" />
<IconButton <IconButton
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_input",
type: "create_group", type: "create_group",
}) })
}> }>

View file

@ -7,9 +7,8 @@ import { ServerList } from "@revoltchat/ui";
import { useApplicationState } from "../../../mobx/State"; import { useApplicationState } from "../../../mobx/State";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { useClient } from "../../../controllers/client/ClientController"; import { useClient } from "../../../controllers/client/ClientController";
import { modalController } from "../../../controllers/modals/ModalController";
/** /**
* Server list sidebar shim component * Server list sidebar shim component
@ -17,13 +16,11 @@ import { useClient } from "../../../controllers/client/ClientController";
export default observer(() => { export default observer(() => {
const client = useClient(); const client = useClient();
const state = useApplicationState(); const state = useApplicationState();
const { openScreen } = useIntermediate();
const { server: server_id } = useParams<{ server?: string }>(); const { server: server_id } = useParams<{ server?: string }>();
const createServer = useCallback( const createServer = useCallback(
() => () =>
openScreen({ modalController.push({
id: "special_input",
type: "create_server", type: "create_server",
}), }),
[], [],

View file

@ -8,7 +8,21 @@ import { useApplicationState } from "../../../mobx/State";
import { FONTS, Fonts, FONT_KEYS } from "../../../context/Theme"; import { FONTS, Fonts, FONT_KEYS } from "../../../context/Theme";
import { ShimDisplayEmoji } from "../appearance_legacy/Shims"; import { EmojiSelector } from "./legacy/EmojiSelector";
/**
* ! LEGACY
* Component providing a way to change emoji pack.
*/
export const ShimDisplayEmoji = observer(() => {
const settings = useApplicationState().settings;
return (
<EmojiSelector
value={settings.get("appearance:emoji")}
setValue={(v) => settings.set("appearance:emoji", v)}
/>
);
});
export default observer(() => { export default observer(() => {
const settings = useApplicationState().settings; const settings = useApplicationState().settings;

View file

@ -93,96 +93,6 @@ export function SpecialInputModal(props: SpecialProps) {
const { onClose } = props; const { onClose } = props;
switch (props.type) { switch (props.type) {
case "create_group": {
return (
<InputModal
onClose={onClose}
question={<Text id="app.main.groups.create" />}
field={<Text id="app.main.groups.name" />}
callback={async (name) => {
const group = await client.channels.createGroup({
name,
users: [],
});
history.push(`/channel/${group._id}`);
}}
/>
);
}
case "create_server": {
return (
<InputModal
onClose={onClose}
question={<Text id="app.main.servers.create" />}
field={<Text id="app.main.servers.name" />}
description={
<div>
By creating this server, you agree to the{" "}
<a
href="https://revolt.chat/aup"
target="_blank"
rel="noreferrer">
Acceptable Use Policy.
</a>
</div>
}
callback={async (name) => {
const server = await client.servers.createServer({
name,
});
history.push(`/server/${server._id}`);
}}
/>
);
}
case "create_role": {
return (
<InputModal
onClose={onClose}
question={
<Text id="app.settings.permissions.create_role" />
}
field={<Text id="app.settings.permissions.role_name" />}
callback={async (name) => {
const role = await props.server.createRole(name);
props.callback(role.id);
}}
/>
);
}
case "set_custom_status": {
return (
<InputModal
onClose={onClose}
question={<Text id="app.context_menu.set_custom_status" />}
field={<Text id="app.context_menu.custom_status" />}
defaultValue={client.user?.status?.text ?? undefined}
callback={(text) =>
client.users.edit({
status: {
...client.user?.status,
text: text.trim().length > 0 ? text : undefined,
},
})
}
/>
);
}
case "add_friend": {
return (
<InputModal
onClose={onClose}
question={"Add Friend"}
callback={(username) =>
client.api
.post(`/users/friend`, { username })
.then(undefined)
}
/>
);
}
default: default:
return null; return null;
} }

View file

@ -27,6 +27,11 @@ export function takeError(error: any): string {
return "UnknownError"; return "UnknownError";
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function mapError(error: any): never {
throw takeError(error);
}
export function getChannelName( export function getChannelName(
channel: Channel, channel: Channel,
prefixType?: boolean, prefixType?: boolean,

View file

@ -16,9 +16,14 @@ import { getApplicationState } from "../../mobx/State";
import { history } from "../../context/history"; import { history } from "../../context/history";
import { __thisIsAHack } from "../../context/intermediate/Intermediate"; import { __thisIsAHack } from "../../context/intermediate/Intermediate";
import AddFriend from "./components/AddFriend";
import Changelog from "./components/Changelog"; import Changelog from "./components/Changelog";
import ChannelInfo from "./components/ChannelInfo"; import ChannelInfo from "./components/ChannelInfo";
import Clipboard from "./components/Clipboard"; import Clipboard from "./components/Clipboard";
import CreateGroup from "./components/CreateGroup";
import CreateRole from "./components/CreateRole";
import CreateServer from "./components/CreateServer";
import CustomStatus from "./components/CustomStatus";
import Error from "./components/Error"; import Error from "./components/Error";
import ImageViewer from "./components/ImageViewer"; import ImageViewer from "./components/ImageViewer";
import LinkWarning from "./components/LinkWarning"; import LinkWarning from "./components/LinkWarning";
@ -222,10 +227,15 @@ class ModalControllerExtended extends ModalController<Modal> {
} }
export const modalController = new ModalControllerExtended({ export const modalController = new ModalControllerExtended({
add_friend: AddFriend,
changelog: Changelog, changelog: Changelog,
channel_info: ChannelInfo, channel_info: ChannelInfo,
clipboard: Clipboard, clipboard: Clipboard,
create_group: CreateGroup,
create_role: CreateRole,
create_server: CreateServer,
create_bot: CreateBotModal, create_bot: CreateBotModal,
custom_status: CustomStatus,
error: Error, error: Error,
image_viewer: ImageViewer, image_viewer: ImageViewer,
link_warning: LinkWarning, link_warning: LinkWarning,

View file

@ -0,0 +1,31 @@
import { ModalForm } from "@revoltchat/ui";
import { noop } from "../../../lib/js";
import { useClient } from "../../client/ClientController";
import { ModalProps } from "../types";
/**
* Add friend modal
*/
export default function AddFriend({ ...props }: ModalProps<"add_friend">) {
const client = useClient();
return (
<ModalForm
{...props}
title="Add Friend"
schema={{
username: "text",
}}
data={{
username: {
field: "Username",
},
}}
callback={({ username }) =>
client.api.post(`/users/friend`, { username }).then(noop)
}
/>
);
}

View file

@ -0,0 +1,45 @@
import { useHistory } from "react-router-dom";
import { Text } from "preact-i18n";
import { ModalForm } from "@revoltchat/ui";
import { mapError } from "../../../context/revoltjs/util";
import { useClient } from "../../client/ClientController";
import { ModalProps } from "../types";
/**
* Group creation modal
*/
export default function CreateGroup({ ...props }: ModalProps<"create_group">) {
const history = useHistory();
const client = useClient();
return (
<ModalForm
{...props}
title={<Text id="app.main.groups.create" />}
schema={{
name: "text",
}}
data={{
name: {
field: (
<Text id="app.main.groups.name" />
) as React.ReactChild,
},
}}
callback={async ({ name }) => {
const group = await client.channels
.createGroup({
name,
users: [],
})
.catch(mapError);
history.push(`/channel/${group._id}`);
}}
/>
);
}

View file

@ -0,0 +1,35 @@
import { Text } from "preact-i18n";
import { ModalForm } from "@revoltchat/ui";
import { ModalProps } from "../types";
/**
* Role creation modal
*/
export default function CreateRole({
server,
callback,
...props
}: ModalProps<"create_role">) {
return (
<ModalForm
{...props}
title={<Text id="app.settings.permissions.create_role" />}
schema={{
name: "text",
}}
data={{
name: {
field: (
<Text id="app.settings.permissions.role_name" />
) as React.ReactChild,
},
}}
callback={async ({ name }) => {
const role = await server.createRole(name);
callback(role.id);
}}
/>
);
}

View file

@ -0,0 +1,57 @@
import { useHistory } from "react-router-dom";
import { Text } from "preact-i18n";
import { ModalForm } from "@revoltchat/ui";
import { mapError } from "../../../context/revoltjs/util";
import { useClient } from "../../client/ClientController";
import { ModalProps } from "../types";
/**
* Server creation modal
*/
export default function CreateServer({
...props
}: ModalProps<"create_server">) {
const history = useHistory();
const client = useClient();
return (
<ModalForm
{...props}
title={<Text id="app.main.servers.create" />}
description={
<div>
By creating this server, you agree to the{" "}
<a
href="https://revolt.chat/aup"
target="_blank"
rel="noreferrer">
Acceptable Use Policy.
</a>
</div>
}
schema={{
name: "text",
}}
data={{
name: {
field: (
<Text id="app.main.servers.name" />
) as React.ReactChild,
},
}}
callback={async ({ name }) => {
const server = await client.servers
.createServer({
name,
})
.catch(mapError);
history.push(`/server/${server._id}`);
}}
/>
);
}

View file

@ -0,0 +1,43 @@
import { Text } from "preact-i18n";
import { ModalForm } from "@revoltchat/ui";
import { useClient } from "../../client/ClientController";
import { ModalProps } from "../types";
/**
* Custom status modal
*/
export default function CustomStatus({
...props
}: ModalProps<"custom_status">) {
const client = useClient();
return (
<ModalForm
{...props}
title={<Text id="app.context_menu.set_custom_status" />}
schema={{
text: "text",
}}
defaults={{
text: client.user?.status?.text as string,
}}
data={{
text: {
field: (
<Text id="app.context_menu.custom_status" />
) as React.ReactChild,
},
}}
callback={({ text }) =>
client.users.edit({
status: {
...client.user?.status,
text: text.trim().length > 0 ? text : undefined,
},
})
}
/>
);
}

View file

@ -3,6 +3,14 @@ import { API, Client, User, Member, Channel, Server } from "revolt.js";
export type Modal = { export type Modal = {
key?: string; key?: string;
} & ( } & (
| {
type:
| "signed_out"
| "create_group"
| "create_server"
| "custom_status"
| "add_friend";
}
| ({ | ({
type: "mfa_flow"; type: "mfa_flow";
} & ( } & (
@ -69,9 +77,6 @@ export type Modal = {
type: "server_identity"; type: "server_identity";
member: Member; member: Member;
} }
| {
type: "signed_out";
}
| { | {
type: "channel_info"; type: "channel_info";
channel: Channel; channel: Channel;
@ -107,6 +112,11 @@ export type Modal = {
loginAfterSuccess?: true, loginAfterSuccess?: true,
) => Promise<void>; ) => Promise<void>;
} }
| {
type: "create_role";
server: Server;
callback: (id: string) => void;
}
); );
export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & { export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {

View file

@ -368,9 +368,8 @@ export default function ContextMenus() {
break; break;
case "set_status": case "set_status":
openScreen({ modalController.push({
id: "special_input", type: "custom_status",
type: "set_custom_status",
}); });
break; break;

View file

@ -85,8 +85,7 @@ export default observer(() => {
<Tooltip content={"Create Group"} placement="bottom"> <Tooltip content={"Create Group"} placement="bottom">
<IconButton <IconButton
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_input",
type: "create_group", type: "create_group",
}) })
}> }>
@ -96,8 +95,7 @@ export default observer(() => {
<Tooltip content={"Add Friend"} placement="bottom"> <Tooltip content={"Add Friend"} placement="bottom">
<IconButton <IconButton
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_input",
type: "add_friend", type: "add_friend",
}) })
}> }>

View file

@ -29,6 +29,7 @@ import wideSVG from "/assets/wide.svg";
import { PageHeader } from "../../components/ui/Header"; import { PageHeader } from "../../components/ui/Header";
import { useClient } from "../../controllers/client/ClientController"; import { useClient } from "../../controllers/client/ClientController";
import { modalController } from "../../controllers/modals/ModalController";
const Overlay = styled.div` const Overlay = styled.div`
display: grid; display: grid;
@ -98,8 +99,7 @@ export default observer(() => {
<div className={styles.actions}> <div className={styles.actions}>
<a <a
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_input",
type: "create_group", type: "create_group",
}) })
}> }>

View file

@ -347,9 +347,8 @@ export default observer(() => {
<a <a
className="status" className="status"
onClick={() => onClick={() =>
openScreen({ modalController.push({
id: "special_input", type: "custom_status",
type: "set_custom_status",
}) })
}> }>
Change your status... Change your status...

View file

@ -16,10 +16,9 @@ import {
Category, Category,
} from "@revoltchat/ui"; } from "@revoltchat/ui";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { PermissionList } from "../../../components/settings/roles/PermissionList"; import { PermissionList } from "../../../components/settings/roles/PermissionList";
import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection"; import { RoleOrDefault } from "../../../components/settings/roles/RoleSelection";
import { modalController } from "../../../controllers/modals/ModalController";
interface Props { interface Props {
server: Server; server: Server;
@ -54,18 +53,14 @@ export const Roles = observer(({ server }: Props) => {
// Consolidate all permissions that we can change right now. // Consolidate all permissions that we can change right now.
const currentRoles = useRoles(server); const currentRoles = useRoles(server);
// Pull in modal context.
const { openScreen } = useIntermediate();
return ( return (
<PermissionsLayout <PermissionsLayout
server={server} server={server}
rank={server.member?.ranking ?? Infinity} rank={server.member?.ranking ?? Infinity}
onCreateRole={(callback) => onCreateRole={(callback) =>
openScreen({ modalController.push({
id: "special_input",
type: "create_role", type: "create_role",
server: server as any, server,
callback, callback,
}) })
} }

View file

@ -2231,9 +2231,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@revoltchat/ui@npm:1.0.58": "@revoltchat/ui@npm:1.0.61":
version: 1.0.58 version: 1.0.61
resolution: "@revoltchat/ui@npm:1.0.58" resolution: "@revoltchat/ui@npm:1.0.61"
dependencies: dependencies:
"@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0
@ -2247,7 +2247,7 @@ __metadata:
react-virtuoso: ^2.12.0 react-virtuoso: ^2.12.0
peerDependencies: peerDependencies:
revolt.js: "*" revolt.js: "*"
checksum: 1ebdb3963c77fbad11427963e27f1be1beb480e80360a59f06a3a2fd2d3f5e335ff33fbb8bf99f533549696848ae1d1db5072ff4d45e98d25c9b1b372f25d795 checksum: 678584f84cb3d43307507058eb434507cb1d201455f9da6618eb1731c6439240ddc16601dc636b5790a83393884770bf4e8fee75d235871f65ba5abcd8f685ac
languageName: node languageName: node
linkType: hard linkType: hard
@ -3554,7 +3554,7 @@ __metadata:
"@hcaptcha/react-hcaptcha": ^0.3.6 "@hcaptcha/react-hcaptcha": ^0.3.6
"@insertish/vite-plugin-babel-macros": ^1.0.5 "@insertish/vite-plugin-babel-macros": ^1.0.5
"@preact/preset-vite": ^2.0.0 "@preact/preset-vite": ^2.0.0
"@revoltchat/ui": 1.0.58 "@revoltchat/ui": 1.0.61
"@rollup/plugin-replace": ^2.4.2 "@rollup/plugin-replace": ^2.4.2
"@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0