feat(modal): port Error and ShowToken

This commit is contained in:
Paul Makles 2022-06-18 12:25:56 +01:00
parent 374be319c4
commit d10bd96900
13 changed files with 92 additions and 61 deletions

View file

@ -24,7 +24,7 @@ import {
import { useApplicationState } from "../../../mobx/State";
import { Reply } from "../../../mobx/stores/MessageQueue";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { modalController } from "../../../context/modals";
import {
FileUploader,
grabFiles,
@ -148,7 +148,6 @@ export default observer(({ channel }: Props) => {
});
const [typing, setTyping] = useState<boolean | number>(false);
const [replies, setReplies] = useState<Reply[]>([]);
const { openScreen } = useIntermediate();
const client = useContext(AppContext);
const translate = useTranslation();
@ -473,7 +472,10 @@ export default observer(({ channel }: Props) => {
files: [...uploadState.files, ...files],
}),
() =>
openScreen({ id: "error", error: "FileTooLarge" }),
modalController.push({
type: "error",
error: "FileTooLarge",
}),
true,
)
}

View file

@ -1,14 +1,10 @@
import { internalEmit } from "../../lib/eventEmitter";
//import { isModalClosing } from "../../components/ui/Modal";
import { Screen } from "./Intermediate";
import { ClipboardModal } from "./modals/Clipboard";
import { ErrorModal } from "./modals/Error";
import { ExternalLinkModal } from "./modals/ExternalLinkPrompt";
import { InputModal } from "./modals/Input";
import { OnboardingModal } from "./modals/Onboarding";
import { PromptModal } from "./modals/Prompt";
import { TokenRevealModal } from "./modals/TokenReveal";
export interface Props {
screen: Screen;
@ -26,12 +22,8 @@ export default function Modals({ screen, openScreen }: Props) {
return <PromptModal onClose={onClose} {...screen} />;
case "_input":
return <InputModal onClose={onClose} {...screen} />;
case "error":
return <ErrorModal onClose={onClose} {...screen} />;
case "clipboard":
return <ClipboardModal onClose={onClose} {...screen} />;
case "token_reveal":
return <TokenRevealModal onClose={onClose} {...screen} />;
case "onboarding":
return <OnboardingModal onClose={onClose} {...screen} />;
case "external_link_prompt":

View file

@ -2,23 +2,23 @@ import { Text } from "preact-i18n";
import { Modal } from "@revoltchat/ui";
interface Props {
onClose: () => void;
error: string;
}
import { noopTrue } from "../../../lib/js";
export function ErrorModal({ onClose, error }: Props) {
import { ModalProps } from "../types";
export function Error({ error, ...props }: ModalProps<"error">) {
return (
<Modal
onClose={onClose}
{...props}
title={<Text id="app.special.modals.error" />}
actions={[
{
onClick: onClose,
onClick: noopTrue,
confirmation: true,
children: <Text id="app.special.modals.actions.ok" />,
},
{
palette: "plain-secondary",
onClick: () => location.reload(),
children: <Text id="app.special.modals.actions.reload" />,
},

View file

@ -2,25 +2,23 @@ import { Text } from "preact-i18n";
import { Modal } from "@revoltchat/ui";
interface Props {
onClose: () => void;
token: string;
username: string;
}
import { noopTrue } from "../../../lib/js";
export function TokenRevealModal({ onClose, token, username }: Props) {
import { ModalProps } from "../types";
export function ShowToken({ name, token, ...props }: ModalProps<"show_token">) {
return (
<Modal
onClose={onClose}
{...props}
title={
<Text
id={"app.special.modals.token_reveal"}
fields={{ name: username }}
fields={{ name }}
/>
}
actions={[
{
onClick: onClose,
onClick: noopTrue,
confirmation: true,
children: <Text id="app.special.modals.actions.close" />,
},

View file

@ -1,7 +0,0 @@
import { Modal } from "@revoltchat/ui";
import { ModalProps } from "../types";
export default function Test({ onClose }: ModalProps<"test">) {
return <Modal title="I am a sub modal!" onClose={onClose} />;
}

View file

@ -9,13 +9,14 @@ import type { Client, API } from "revolt.js";
import { ulid } from "ulid";
import Changelog from "./components/Changelog";
import { Error } from "./components/Error";
import MFAEnableTOTP from "./components/MFAEnableTOTP";
import MFAFlow from "./components/MFAFlow";
import MFARecovery from "./components/MFARecovery";
import OutOfDate from "./components/OutOfDate";
import { ShowToken } from "./components/ShowToken";
import { SignOutSessions } from "./components/SignOutSessions";
import { SignedOut } from "./components/SignedOut";
import Test from "./components/Test";
import { Modal } from "./types";
type Components = Record<string, React.FC<any>>;
@ -92,6 +93,9 @@ class ModalController<T extends Modal> {
);
}
/**
* Whether a modal is currently visible
*/
get isVisible() {
return this.stack.length > 0;
}
@ -140,11 +144,12 @@ class ModalControllerExtended extends ModalController<Modal> {
export const modalController = new ModalControllerExtended({
changelog: Changelog,
error: Error,
mfa_flow: MFAFlow,
mfa_recovery: MFARecovery,
mfa_enable_totp: MFAEnableTOTP,
out_of_date: OutOfDate,
show_token: ShowToken,
signed_out: SignedOut,
sign_out_sessions: SignOutSessions,
test: Test,
});

View file

@ -33,13 +33,22 @@ export type Modal = {
initial?: number;
}
| {
type: "sign_out_sessions";
client: Client;
onDelete: () => void;
onDeleting: () => void;
type: "sign_out_sessions";
}
| {
type: "test" | "signed_out";
type: "show_token";
name: string;
token: string;
}
| {
type: "error";
error: string;
}
| {
type: "signed_out";
}
);

View file

@ -12,6 +12,7 @@ import { IconButton, Preloader } from "@revoltchat/ui";
import { determineFileSize } from "../../lib/fileSize";
import { useIntermediate } from "../intermediate/Intermediate";
import { modalController } from "../modals";
import { AppContext } from "./RevoltClient";
import { takeError } from "./util";
@ -139,12 +140,19 @@ export function FileUploader(props: Props) {
);
}
} catch (err) {
return openScreen({ id: "error", error: takeError(err) });
return modalController.push({
type: "error",
error: takeError(err),
});
} finally {
setUploading(false);
}
},
() => openScreen({ id: "error", error: "FileTooLarge" }),
() =>
modalController.push({
type: "error",
error: "FileTooLarge",
}),
props.behaviour === "multi",
);
}
@ -180,8 +188,8 @@ export function FileUploader(props: Props) {
const blob = item.getAsFile();
if (blob) {
if (blob.size > props.maxFileSize) {
openScreen({
id: "error",
modalController.push({
type: "error",
error: "FileTooLarge",
});
continue;
@ -212,7 +220,10 @@ export function FileUploader(props: Props) {
const files = [];
for (const item of dropped) {
if (item.size > props.maxFileSize) {
openScreen({ id: "error", error: "FileTooLarge" });
modalController.push({
type: "error",
error: "FileTooLarge",
});
continue;
}

View file

@ -9,7 +9,6 @@ import { Preloader } from "@revoltchat/ui";
import { useApplicationState } from "../../mobx/State";
import { useIntermediate } from "../intermediate/Intermediate";
import { modalController } from "../modals";
import { registerEvents } from "./events";
import { takeError } from "./util";
@ -30,7 +29,7 @@ export interface ClientOperations {
export const AppContext = createContext<Client>(null!);
export const StatusContext = createContext<ClientStatus>(null!);
export const LogOutContext = createContext((avoidReq?: boolean) => {});
export const LogOutContext = createContext<(avoidReq?: boolean) => void>(null!);
type Props = {
children: Children;
@ -38,7 +37,6 @@ type Props = {
export default observer(({ children }: Props) => {
const state = useApplicationState();
const { openScreen } = useIntermediate();
const [client, setClient] = useState<Client>(null!);
const [status, setStatus] = useState(ClientStatus.LOADING);
const [loaded, setLoaded] = useState(false);
@ -72,7 +70,10 @@ export default observer(({ children }: Props) => {
modalController.push({ type: "signed_out" });
} else {
setStatus(ClientStatus.DISCONNECTED);
openScreen({ id: "error", error });
modalController.push({
type: "error",
error,
});
}
})
.finally(() => setLoaded(true));

View file

@ -2,8 +2,15 @@ import { ChevronRight, Trash } from "@styled-icons/boxicons-regular";
import { Cog, UserVoice } from "@styled-icons/boxicons-solid";
import { isFirefox } from "react-device-detect";
import { useHistory } from "react-router-dom";
import { Channel, Message, Server, User, API } from "revolt.js";
import { Permission, UserPermission } from "revolt.js";
import {
Channel,
Message,
Server,
User,
API,
Permission,
UserPermission,
} from "revolt.js";
import {
ContextMenuWithData,
@ -20,6 +27,7 @@ import { QueuedMessage } from "../mobx/stores/MessageQueue";
import { NotificationState } from "../mobx/stores/NotificationOptions";
import { Screen, useIntermediate } from "../context/intermediate/Intermediate";
import { modalController } from "../context/modals";
import {
AppContext,
ClientStatus,
@ -431,7 +439,10 @@ export default function ContextMenus() {
break;
}
})().catch((err) => {
openScreen({ id: "error", error: takeError(err) });
modalController.push({
type: "error",
error: takeError(err),
});
});
}

View file

@ -6,7 +6,7 @@ import { useContext, useEffect } from "preact/hooks";
import { Header } from "@revoltchat/ui";
import { useIntermediate } from "../context/intermediate/Intermediate";
import { modalController } from "../context/modals";
import {
AppContext,
ClientStatus,
@ -18,7 +18,6 @@ export default function Open() {
const client = useContext(AppContext);
const status = useContext(StatusContext);
const { id } = useParams<{ id: string }>();
const { openScreen } = useIntermediate();
if (status !== ClientStatus.ONLINE) {
return (
@ -40,7 +39,12 @@ export default function Open() {
client
.user!.openDM()
.then((channel) => history.push(`/channel/${channel?._id}`))
.catch((error) => openScreen({ id: "error", error }));
.catch((error) =>
modalController.push({
type: "error",
error,
}),
);
return;
}
@ -62,7 +66,12 @@ export default function Open() {
.get(id)
?.openDM()
.then((channel) => history.push(`/channel/${channel?._id}`))
.catch((error) => openScreen({ id: "error", error }));
.catch((error) =>
modalController.push({
type: "error",
error,
}),
);
}
return;

View file

@ -25,6 +25,7 @@ import { useTranslation } from "../../../lib/i18n";
import { stopPropagation } from "../../../lib/stopPropagation";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { modalController } from "../../../context/modals";
import { FileUploader } from "../../../context/revoltjs/FileUploads";
import { useClient } from "../../../context/revoltjs/RevoltClient";
@ -366,10 +367,10 @@ function BotCard({ bot, onDelete, onUpdate }: Props) {
onClick={(ev) =>
stopPropagation(
ev,
openScreen({
id: "token_reveal",
modalController.push({
type: "show_token",
token: bot.token,
username: user!.username,
name: user!.username,
}),
)
}>

View file

@ -10,12 +10,11 @@ import { urlBase64ToUint8Array } from "../../../lib/conversion";
import { useApplicationState } from "../../../mobx/State";
import { useIntermediate } from "../../../context/intermediate/Intermediate";
import { modalController } from "../../../context/modals";
import { AppContext } from "../../../context/revoltjs/RevoltClient";
export const Notifications = observer(() => {
const client = useContext(AppContext);
const { openScreen } = useIntermediate();
const settings = useApplicationState().settings;
const [pushEnabled, setPushEnabled] = useState<undefined | boolean>(
undefined,
@ -52,8 +51,8 @@ export const Notifications = observer(() => {
await Notification.requestPermission();
if (permission !== "granted") {
return openScreen({
id: "error",
return modalController.push({
type: "error",
error: "DeniedNotification",
});
}