chore: delete intermediate

This commit is contained in:
Paul Makles
2022-07-05 21:13:42 +01:00
parent f7ff7d0dfe
commit f9c6f5cd9d
35 changed files with 129 additions and 1104 deletions

View File

@@ -13,7 +13,6 @@ import ModalRenderer from "../controllers/modals/ModalRenderer";
import Locale from "./Locale";
import Theme from "./Theme";
import { history } from "./history";
import Intermediate from "./intermediate/Intermediate";
const uiContext = {
Link,
@@ -39,10 +38,8 @@ export default function Context({ children }: { children: Children }) {
<Router history={history}>
<UIProvider value={uiContext}>
<Locale>
<Intermediate>
{children}
<Binder />
</Intermediate>
<>{children}</>
<Binder />
<ModalRenderer />
</Locale>
</UIProvider>

View File

@@ -1,219 +0,0 @@
import { Prompt } from "react-router";
import { useHistory } from "react-router-dom";
import { API, Channel, Message, Server, User } from "revolt.js";
import { createContext } from "preact";
import {
StateUpdater,
useContext,
useEffect,
useMemo,
useState,
} from "preact/hooks";
import type { Action } from "@revoltchat/ui/esm/components/design/atoms/display/Modal";
import { internalSubscribe } from "../../lib/eventEmitter";
import { determineLink } from "../../lib/links";
import { useApplicationState } from "../../mobx/State";
import { modalController } from "../../controllers/modals/ModalController";
import Modals from "./Modals";
export type Screen =
| { id: "none" }
// Modals
| { id: "signed_out" }
| { id: "error"; error: string }
| { id: "clipboard"; text: string }
| { id: "token_reveal"; token: string; username: string }
| { id: "external_link_prompt"; link: string }
| { id: "sessions"; confirm: () => void }
| {
id: "_prompt";
question: Children;
content?: Children;
actions: Action[];
}
| ({ id: "special_prompt" } & (
| { type: "leave_group"; target: Channel }
| { type: "close_dm"; target: Channel }
| { type: "leave_server"; target: Server }
| { type: "delete_server"; target: Server }
| { type: "delete_channel"; target: Channel }
| {
type: "delete_bot";
target: string;
name: string;
cb?: () => void;
}
| { type: "delete_message"; target: Message }
| {
type: "create_invite";
target: Channel;
}
| { type: "kick_member"; target: Server; user: User }
| { type: "ban_member"; target: Server; user: User }
| { type: "unfriend_user"; target: User }
| { type: "block_user"; target: User }
| {
type: "create_channel";
target: Server;
cb?: (
channel: Channel & {
channel_type: "TextChannel" | "VoiceChannel";
},
) => void;
}
| { type: "create_category"; target: Server }
))
| ({ id: "special_input" } & (
| {
type:
| "create_group"
| "create_server"
| "set_custom_status"
| "add_friend";
}
| {
type: "create_role";
server: Server;
callback: (id: string) => void;
}
))
| {
id: "_input";
question: Children;
field: Children;
defaultValue?: string;
callback: (value: string) => Promise<void>;
}
| {
id: "onboarding";
callback: (
username: string,
loginAfterSuccess?: true,
) => Promise<void>;
}
// Pop-overs
| { id: "profile"; user_id: string }
| {
id: "user_picker";
omit?: string[];
callback: (users: string[]) => Promise<void>;
}
| { id: "image_viewer"; attachment?: API.File; embed?: API.Image }
| { id: "channel_info"; channel: Channel }
| { id: "pending_requests"; users: User[] }
| { id: "modify_account"; field: "username" | "email" | "password" }
| { id: "create_bot"; onCreate: (bot: API.Bot) => void }
| {
id: "server_identity";
server: Server;
};
export const IntermediateContext = createContext({
screen: { id: "none" },
focusTaken: false,
});
export const IntermediateActionsContext = createContext<{
openLink: (href?: string, trusted?: boolean) => boolean;
openScreen: (screen: Screen) => void;
writeClipboard: (text: string) => void;
}>({
openLink: null!,
openScreen: null!,
writeClipboard: null!,
});
interface Props {
children: Children;
}
export let __thisIsAHack: StateUpdater<Screen>;
export default function Intermediate(props: Props) {
const [screen, openScreen] = useState<Screen>({ id: "none" });
__thisIsAHack = openScreen;
const history = useHistory();
const value = {
screen,
focusTaken: screen.id !== "none",
};
const actions = useMemo(() => {
return {
openLink: (href?: string, trusted?: boolean) => {
return modalController.openLink(href, trusted);
},
openScreen: (screen: Screen) => openScreen(screen),
writeClipboard: (a: string) => modalController.writeText(a),
};
// eslint-disable-next-line
}, []);
useEffect(() => {
const openProfile = (user_id: string) =>
modalController.push({ type: "user_profile", user_id });
const navigate = (path: string) => history.push(path);
const subs = [
internalSubscribe(
"Intermediate",
"openProfile",
openProfile as (...args: unknown[]) => void,
),
internalSubscribe(
"Intermediate",
"navigate",
navigate as (...args: unknown[]) => void,
),
];
return () => subs.map((unsub) => unsub());
}, [history]);
return (
<IntermediateContext.Provider value={value}>
<IntermediateActionsContext.Provider value={actions}>
{screen.id !== "onboarding" && props.children}
<Modals
{...value}
{...actions}
key={
screen.id
} /** By specifying a key, we reset state whenever switching screen. */
/>
<Prompt
when={[
"modify_account",
"special_prompt",
"special_input",
"image_viewer",
"profile",
"channel_info",
"pending_requests",
"user_picker",
].includes(screen.id)}
message={(_, action) => {
if (action === "POP") {
openScreen({ id: "none" });
setTimeout(() => history.push(history.location), 0);
return false;
}
return true;
}}
/>
</IntermediateActionsContext.Provider>
</IntermediateContext.Provider>
);
}
export const useIntermediate = () => useContext(IntermediateActionsContext);

View File

@@ -1,25 +0,0 @@
//import { isModalClosing } from "../../components/ui/Modal";
import { Screen } from "./Intermediate";
import { InputModal } from "./modals/Input";
import { PromptModal } from "./modals/Prompt";
export interface Props {
screen: Screen;
openScreen: (screen: Screen) => void;
}
export default function Modals({ screen, openScreen }: Props) {
const onClose = () =>
//isModalClosing || screen.id === "onboarding"
openScreen({ id: "none" });
// : internalEmit("Modal", "close");
switch (screen.id) {
case "_prompt":
return <PromptModal onClose={onClose} {...screen} />;
case "_input":
return <InputModal onClose={onClose} {...screen} />;
}
return null;
}

View File

@@ -1,26 +0,0 @@
import { useContext } from "preact/hooks";
import { IntermediateContext, useIntermediate } from "./Intermediate";
import { SpecialInputModal } from "./modals/Input";
import { SpecialPromptModal } from "./modals/Prompt";
export default function Popovers() {
const { screen } = useContext(IntermediateContext);
const { openScreen } = useIntermediate();
const onClose = () =>
//isModalClosing
openScreen({ id: "none" });
//: internalEmit("Modal", "close");
switch (screen.id) {
case "special_prompt":
// @ts-expect-error someone figure this out :)
return <SpecialPromptModal onClose={onClose} {...screen} />;
case "special_input":
// @ts-expect-error someone figure this out :)
return <SpecialInputModal onClose={onClose} {...screen} />;
}
return null;
}

View File

@@ -1,99 +0,0 @@
import { useHistory } from "react-router";
import { Server } from "revolt.js";
import { Text } from "preact-i18n";
import { useContext, useState } from "preact/hooks";
import { Category, InputBox, Modal } from "@revoltchat/ui";
import { useClient } from "../../../controllers/client/ClientController";
import { I18nError } from "../../Locale";
import { takeError } from "../../revoltjs/util";
interface Props {
onClose: () => void;
question: Children;
field?: Children;
description?: Children;
defaultValue?: string;
callback: (value: string) => Promise<void>;
}
export function InputModal({
onClose,
question,
field,
description,
defaultValue,
callback,
}: Props) {
const [processing, setProcessing] = useState(false);
const [value, setValue] = useState(defaultValue ?? "");
const [error, setError] = useState<undefined | string>(undefined);
return (
<Modal
title={question}
description={description}
disabled={processing}
actions={[
{
confirmation: true,
children: <Text id="app.special.modals.actions.ok" />,
onClick: () => {
setProcessing(true);
callback(value)
.then(onClose)
.catch((err) => {
setError(takeError(err));
setProcessing(false);
});
},
},
{
children: <Text id="app.special.modals.actions.cancel" />,
onClick: onClose,
},
]}
onClose={onClose}>
{field ? (
<Category>
<I18nError error={error}>{field}</I18nError>
</Category>
) : (
error && (
<Category>
<I18nError error={error} />
</Category>
)
)}
<InputBox
value={value}
style={{ width: "100%" }}
onChange={(e) => setValue(e.currentTarget.value)}
/>
</Modal>
);
}
type SpecialProps = { onClose: () => void } & (
| {
type:
| "create_group"
| "create_server"
| "set_custom_status"
| "add_friend";
}
| { type: "create_role"; server: Server; callback: (id: string) => void }
);
export function SpecialInputModal(props: SpecialProps) {
const history = useHistory();
const client = useClient();
const { onClose } = props;
switch (props.type) {
default:
return null;
}
}

View File

@@ -1,18 +0,0 @@
.invite {
display: flex;
flex-direction: column;
code {
padding: 1em;
user-select: all;
font-size: 1.4em;
text-align: center;
font-family: var(--monospace-font);
}
}
.column {
display: flex;
align-items: center;
flex-direction: column;
}

View File

@@ -1,561 +0,0 @@
import { observer } from "mobx-react-lite";
import { useHistory } from "react-router-dom";
import { Channel, Message as MessageI, Server, User } from "revolt.js";
import { ulid } from "ulid";
import styles from "./Prompt.module.scss";
import { Text } from "preact-i18n";
import { useEffect, useState } from "preact/hooks";
import { Category, Modal, InputBox, Radio } from "@revoltchat/ui";
import type { Action } from "@revoltchat/ui/esm/components/design/atoms/display/Modal";
import { TextReact } from "../../../lib/i18n";
import Message from "../../../components/common/messaging/Message";
import UserIcon from "../../../components/common/user/UserIcon";
import { useClient } from "../../../controllers/client/ClientController";
import { I18nError } from "../../Locale";
import { takeError } from "../../revoltjs/util";
import { useIntermediate } from "../Intermediate";
interface Props {
onClose: () => void;
question: Children;
description?: Children;
content?: Children;
disabled?: boolean;
actions: Action[];
error?: string;
}
export function PromptModal({
onClose,
question,
description,
content,
actions,
disabled,
error,
}: Props) {
return (
<Modal
title={question}
description={description}
actions={actions}
onClose={onClose}
disabled={disabled}>
{error && (
<Category>
<I18nError error={error} />
</Category>
)}
{content}
</Modal>
);
}
type SpecialProps = { onClose: () => void } & (
| { type: "leave_group"; target: Channel }
| { type: "close_dm"; target: Channel }
| { type: "delete_channel"; target: Channel }
| {
type: "create_invite";
target: Channel;
}
| { type: "leave_server"; target: Server }
| { type: "delete_server"; target: Server }
| { type: "delete_bot"; target: string; name: string; cb?: () => void }
| { type: "delete_message"; target: MessageI }
| { type: "kick_member"; target: Server; user: User }
| { type: "ban_member"; target: Server; user: User }
| { type: "unfriend_user"; target: User }
| { type: "block_user"; target: User }
| {
type: "create_channel";
target: Server;
cb?: (
channel: Channel & {
channel_type: "TextChannel" | "VoiceChannel";
},
) => void;
}
| { type: "create_category"; target: Server }
);
export const SpecialPromptModal = observer((props: SpecialProps) => {
const client = useClient();
const history = useHistory();
const [processing, setProcessing] = useState(false);
const [error, setError] = useState<undefined | string>(undefined);
const { onClose } = props;
switch (props.type) {
case "leave_group":
case "close_dm":
case "leave_server":
case "delete_server":
case "delete_channel":
case "delete_bot":
case "unfriend_user":
case "block_user": {
const EVENTS = {
close_dm: ["confirm_close_dm", "close"],
delete_server: ["confirm_delete", "delete"],
delete_channel: ["confirm_delete", "delete"],
delete_bot: ["confirm_delete", "delete"],
leave_group: ["confirm_leave", "leave"],
leave_server: ["confirm_leave", "leave"],
unfriend_user: ["unfriend_user", "remove"],
block_user: ["block_user", "block"],
};
const event = EVENTS[props.type];
let name;
switch (props.type) {
case "unfriend_user":
case "block_user":
name = props.target.username;
break;
case "close_dm":
name = props.target.recipient?.username;
break;
case "delete_bot":
name = props.name;
break;
default:
name = props.target.name;
}
return (
<PromptModal
onClose={onClose}
question={
<Text
id={`app.special.modals.prompt.${event[0]}`}
fields={{ name }}
/>
}
description={
<TextReact
id={`app.special.modals.prompt.${event[0]}_long`}
fields={{ name: <b>{name}</b> }}
/>
}
actions={[
{
confirmation: true,
palette: "error",
children: (
<Text
id={`app.special.modals.actions.${event[1]}`}
/>
),
onClick: async () => {
setProcessing(true);
try {
switch (props.type) {
case "unfriend_user":
await props.target.removeFriend();
break;
case "block_user":
await props.target.blockUser();
break;
case "leave_group":
case "close_dm":
case "delete_channel":
case "leave_server":
case "delete_server":
if (props.type != "delete_channel")
history.push("/");
props.target.delete();
break;
case "delete_bot":
client.bots.delete(props.target);
props.cb?.();
break;
}
return true;
} catch (err) {
setError(takeError(err));
setProcessing(false);
return false;
}
},
},
{
children: (
<Text id="app.special.modals.actions.cancel" />
),
onClick: onClose,
},
]}
disabled={processing}
error={error}
/>
);
}
case "delete_message": {
return (
<PromptModal
onClose={onClose}
question={<Text id={"app.context_menu.delete_message"} />}
description={
<Text
id={`app.special.modals.prompt.confirm_delete_message_long`}
/>
}
actions={[
{
confirmation: true,
palette: "error",
children: (
<Text id="app.special.modals.actions.delete" />
),
onClick: async () => {
setProcessing(true);
try {
props.target.delete();
return true;
} catch (err) {
setError(takeError(err));
setProcessing(false);
return false;
}
},
},
{
children: (
<Text id="app.special.modals.actions.cancel" />
),
onClick: onClose,
palette: "plain",
},
]}
content={
<Message message={props.target} head={true} contrast />
}
disabled={processing}
error={error}
/>
);
}
case "create_invite": {
const [code, setCode] = useState("abcdef");
const { writeClipboard } = useIntermediate();
useEffect(() => {
setProcessing(true);
props.target
.createInvite()
.then(({ _id }) => setCode(_id))
.catch((err) => setError(takeError(err)))
.finally(() => setProcessing(false));
}, [props.target]);
return (
<PromptModal
onClose={onClose}
question={<Text id={`app.context_menu.create_invite`} />}
actions={[
{
children: (
<Text id="app.special.modals.actions.ok" />
),
confirmation: true,
onClick: onClose,
},
{
children: <Text id="app.context_menu.copy_link" />,
onClick: () =>
writeClipboard(
`${window.location.protocol}//${window.location.host}/invite/${code}`,
),
},
]}
content={
processing ? (
<Text id="app.special.modals.prompt.create_invite_generate" />
) : (
<div className={styles.invite}>
<Text id="app.special.modals.prompt.create_invite_created" />
<code>{code}</code>
</div>
)
}
disabled={processing}
error={error}
/>
);
}
case "kick_member": {
return (
<PromptModal
onClose={onClose}
question={<Text id={`app.context_menu.kick_member`} />}
actions={[
{
children: (
<Text id="app.special.modals.actions.kick" />
),
palette: "error",
confirmation: true,
onClick: async () => {
setProcessing(true);
try {
client.members
.getKey({
server: props.target._id,
user: props.user._id,
})
?.kick();
return true;
} catch (err) {
setError(takeError(err));
setProcessing(false);
return false;
}
},
},
{
children: (
<Text id="app.special.modals.actions.cancel" />
),
onClick: onClose,
},
]}
content={
<div className={styles.column}>
<UserIcon target={props.user} size={64} />
<Text
id="app.special.modals.prompt.confirm_kick"
fields={{ name: props.user?.username }}
/>
</div>
}
disabled={processing}
error={error}
/>
);
}
case "ban_member": {
const [reason, setReason] = useState<string | undefined>(undefined);
return (
<PromptModal
onClose={onClose}
question={<Text id={`app.context_menu.ban_member`} />}
actions={[
{
children: (
<Text id="app.special.modals.actions.ban" />
),
palette: "error",
confirmation: true,
onClick: async () => {
setProcessing(true);
try {
await props.target.banUser(props.user._id, {
reason,
});
return true;
} catch (err) {
setError(takeError(err));
setProcessing(false);
return false;
}
},
},
{
children: (
<Text id="app.special.modals.actions.cancel" />
),
onClick: onClose,
},
]}
content={
<div className={styles.column}>
<UserIcon target={props.user} size={64} />
<Text
id="app.special.modals.prompt.confirm_ban"
fields={{ name: props.user?.username }}
/>
<Category>
<Text id="app.special.modals.prompt.confirm_ban_reason" />
</Category>
<InputBox
value={reason ?? ""}
onChange={(e) =>
setReason(e.currentTarget.value)
}
/>
</div>
}
disabled={processing}
error={error}
/>
);
}
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,
palette: "secondary",
children: (
<Text id="app.special.modals.actions.create" />
),
onClick: async () => {
setProcessing(true);
try {
const channel =
await props.target.createChannel({
type,
name,
});
if (props.cb) {
props.cb(channel as any);
} else {
history.push(
`/server/${props.target._id}/channel/${channel._id}`,
);
}
return true;
} catch (err) {
setError(takeError(err));
setProcessing(false);
return false;
}
},
},
{
children: (
<Text id="app.special.modals.actions.cancel" />
),
onClick: onClose,
},
]}
content={
<>
<Category>
<Text id="app.main.servers.channel_type" />
</Category>
<Radio
title={
<Text id="app.main.servers.text_channel" />
}
value={type === "Text"}
onSelect={() => setType("Text")}
/>
<Radio
title={
<Text id="app.main.servers.voice_channel" />
}
value={type === "Voice"}
onSelect={() => setType("Voice")}
/>
<Category>
<Text id="app.main.servers.channel_name" />
</Category>
<InputBox
value={name}
onChange={(e) => setName(e.currentTarget.value)}
/>
</>
}
disabled={processing}
error={error}
/>
);
}
case "create_category": {
const [name, setName] = useState("");
return (
<PromptModal
onClose={onClose}
question={<Text id="app.context_menu.create_category" />}
actions={[
{
confirmation: true,
palette: "secondary",
children: (
<Text id="app.special.modals.actions.create" />
),
onClick: async () => {
setProcessing(true);
try {
props.target.edit({
categories: [
...(props.target.categories ?? []),
{
id: ulid(),
title: name,
channels: [],
},
],
});
setProcessing(false);
return true;
} catch (err) {
setError(takeError(err));
setProcessing(false);
return false;
}
},
},
{
children: (
<Text id="app.special.modals.actions.cancel" />
),
onClick: onClose,
},
]}
content={
<>
<Category>
<Text id="app.main.servers.category_name" />
</Category>
<InputBox
value={name}
onChange={(e) => setName(e.currentTarget.value)}
/>
</>
}
disabled={processing}
error={error}
/>
);
}
default:
return null;
}
});

View File

@@ -1,5 +0,0 @@
.list {
max-width: 100%;
max-height: 360px;
overflow-y: scroll;
}

View File

@@ -13,7 +13,6 @@ import { determineFileSize } from "../../lib/fileSize";
import { useClient } from "../../controllers/client/ClientController";
import { modalController } from "../../controllers/modals/ModalController";
import { useIntermediate } from "../intermediate/Intermediate";
import { takeError } from "./util";
type BehaviourType =
@@ -112,7 +111,6 @@ export function grabFiles(
export function FileUploader(props: Props) {
const { fileType, maxFileSize, remove } = props;
const { openScreen } = useIntermediate();
const client = useClient();
const [uploading, setUploading] = useState(false);
@@ -243,7 +241,7 @@ export function FileUploader(props: Props) {
document.removeEventListener("dragover", dragover);
document.removeEventListener("drop", drop);
};
}, [openScreen, props, props.append]);
}, [props, props.append]);
}
if (props.style === "icon" || props.style === "banner") {