diff --git a/package.json b/package.json index 733f29e9..8bd66244 100644 --- a/package.json +++ b/package.json @@ -162,5 +162,8 @@ "repository": "https://github.com/revoltchat/revite.git", "author": "Paul ", "license": "MIT", - "packageManager": "yarn@3.2.0" + "packageManager": "yarn@3.2.0", + "resolutions": { + "@revoltchat/ui": "portal:../components" + } } diff --git a/src/context/index.tsx b/src/context/index.tsx index 89026b31..f30f885c 100644 --- a/src/context/index.tsx +++ b/src/context/index.tsx @@ -4,12 +4,7 @@ import { ContextMenuTrigger } from "preact-context-menu"; import { Text } from "preact-i18n"; import { useEffect, useState } from "preact/hooks"; -import { - LinkProvider, - Preloader, - TextProvider, - TrigProvider, -} from "@revoltchat/ui"; +import { Preloader, UIProvider } from "@revoltchat/ui"; import { hydrateState } from "../mobx/State"; @@ -20,6 +15,13 @@ import ModalRenderer from "./modals/ModalRenderer"; import Client from "./revoltjs/RevoltClient"; import SyncManager from "./revoltjs/SyncManager"; +const uiContext = { + Link, + Text: Text as any, + Trigger: ContextMenuTrigger, + emitAction: () => {}, +}; + /** * This component provides all of the application's context layers. * @param param0 Provided children @@ -35,21 +37,17 @@ export default function Context({ children }: { children: Children }) { return ( - - - - - - - {children} - - - - - - - - + + + + + {children} + + + + + + ); diff --git a/src/context/modals/components/MFAFlow.tsx b/src/context/modals/components/MFAFlow.tsx index 565c4be6..73c4d0c8 100644 --- a/src/context/modals/components/MFAFlow.tsx +++ b/src/context/modals/components/MFAFlow.tsx @@ -3,7 +3,12 @@ import { Key, Keyboard } from "@styled-icons/boxicons-solid"; import { API } from "revolt.js"; import { Text } from "preact-i18n"; -import { useCallback, useEffect, useState } from "preact/hooks"; +import { + useCallback, + useEffect, + useLayoutEffect, + useState, +} from "preact/hooks"; import { Category, @@ -15,8 +20,6 @@ import { import { noopTrue } from "../../../lib/js"; -import { useApplicationState } from "../../../mobx/State"; - import { ModalProps } from "../types"; const ICONS: Record> = { @@ -34,12 +37,13 @@ function ResponseEntry({ value?: API.MFAResponse; onChange: (v: API.MFAResponse) => void; }) { - if (type === "Password") { - return ( - <> - - - + return ( + <> + + + + + {type === "Password" && ( - - ); - } else { - return null; - } + )} + + ); } /** * MFA ticket creation flow */ -export default function MFAFlow({ - callback, - onClose, - ...props -}: ModalProps<"mfa_flow">) { - const state = useApplicationState(); - +export default function MFAFlow({ onClose, ...props }: ModalProps<"mfa_flow">) { const [methods, setMethods] = useState( props.state === "unknown" ? props.available_methods : undefined, ); + // Current state of the modal const [selectedMethod, setSelected] = useState(); const [response, setResponse] = useState(); + // Fetch available methods if they have not been provided. useEffect(() => { if (!methods && props.state === "known") { props.client.api.get("/auth/mfa/methods").then(setMethods); } }, []); + // Always select first available method if only one available. + useLayoutEffect(() => { + if (methods && methods.length === 1) { + setSelected(methods[0]); + } + }, [methods]); + + // Callback to generate a new ticket or send response back up the chain. const generateTicket = useCallback(async () => { if (response) { - let ticket; - if (props.state === "known") { - ticket = await props.client.api.put( + const ticket = await props.client.api.put( "/auth/mfa/ticket", response, ); + + props.callback(ticket); } else { - ticket = await state.config - .createClient() - .api.put("/auth/mfa/ticket", response, { - headers: { - "X-MFA-Ticket": props.ticket.token, - }, - }); + props.callback(response); } - callback(ticket); return true; } @@ -122,8 +121,12 @@ export default function MFAFlow({ }, { palette: "plain", - children: "Back", - onClick: () => setSelected(undefined), + children: + methods!.length === 1 ? "Cancel" : "Back", + onClick: () => + methods!.length === 1 + ? true + : void setSelected(undefined), }, ] : [ diff --git a/src/context/modals/types.ts b/src/context/modals/types.ts index e15a552c..33b5e3ef 100644 --- a/src/context/modals/types.ts +++ b/src/context/modals/types.ts @@ -5,16 +5,16 @@ export type Modal = { } & ( | ({ type: "mfa_flow"; - callback: (ticket: API.MFATicket) => void; } & ( | { state: "known"; client: Client; + callback: (ticket: API.MFATicket) => void; } | { state: "unknown"; available_methods: API.MFAMethod[]; - ticket: API.MFATicket & { validated: false }; + callback: (response: API.MFAResponse) => void; } )) | { diff --git a/src/pages/login/forms/FormLogin.tsx b/src/pages/login/forms/FormLogin.tsx index e827b197..bde6da9d 100644 --- a/src/pages/login/forms/FormLogin.tsx +++ b/src/pages/login/forms/FormLogin.tsx @@ -4,6 +4,7 @@ import { API } from "revolt.js"; import { useApplicationState } from "../../../mobx/State"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; +import { modalController } from "../../../context/modals"; import { Form } from "./Form"; @@ -43,14 +44,34 @@ export function FormLogin() { // This should be replaced in the future. const client = state.config.createClient(); await client.fetchConfiguration(); - const session = await client.api.post("/auth/session/login", { + + let session = await client.api.post("/auth/session/login", { ...data, friendly_name, }); - if (session.result !== "Success") { - alert("unsupported!"); - return; + if (session.result === "MFA") { + const { allowed_methods } = session; + let mfa_response: API.MFAResponse = await new Promise( + (callback) => + modalController.push({ + type: "mfa_flow", + state: "unknown", + available_methods: allowed_methods, + callback, + }), + ); + + session = await client.api.post("/auth/session/login", { + mfa_response, + mfa_ticket: session.ticket, + friendly_name, + }); + + if (session.result === "MFA") { + // unreachable code + return; + } } const s = session; diff --git a/src/pages/settings/panes/Account.tsx b/src/pages/settings/panes/Account.tsx index bc3849a8..82e58a04 100644 --- a/src/pages/settings/panes/Account.tsx +++ b/src/pages/settings/panes/Account.tsx @@ -1,22 +1,19 @@ import { At, Key, Block } from "@styled-icons/boxicons-regular"; -import { - Envelope, - HelpCircle, - Lock, - Trash, - Pencil, -} from "@styled-icons/boxicons-solid"; +import { Envelope, Lock, Trash, Pencil } from "@styled-icons/boxicons-solid"; import { observer } from "mobx-react-lite"; -import { useHistory } from "react-router-dom"; -import { API } from "revolt.js"; +import { Link } from "react-router-dom"; import styles from "./Panes.module.scss"; import { Text } from "preact-i18n"; import { useContext, useEffect, useState } from "preact/hooks"; -import { Button, CategoryButton, LineDivider, Tip } from "@revoltchat/ui"; - -import { stopPropagation } from "../../../lib/stopPropagation"; +import { + AccountDetail, + CategoryButton, + Column, + HiddenValue, + Tip, +} from "@revoltchat/ui"; import { useIntermediate } from "../../../context/intermediate/Intermediate"; import { modalController } from "../../../context/modals"; @@ -27,26 +24,14 @@ import { useClient, } from "../../../context/revoltjs/RevoltClient"; -import Tooltip from "../../../components/common/Tooltip"; -import UserIcon from "../../../components/common/user/UserIcon"; - export const Account = observer(() => { - const { openScreen, writeClipboard } = useIntermediate(); + const { openScreen } = useIntermediate(); const logOut = useContext(LogOutContext); const status = useContext(StatusContext); const client = useClient(); const [email, setEmail] = useState("..."); - const [revealEmail, setRevealEmail] = useState(false); - const [profile, setProfile] = useState( - undefined, - ); - const history = useHistory(); - - function switchPage(to: string) { - history.replace(`/settings/${to}`); - } useEffect(() => { if (email === "..." && status === ClientStatus.ONLINE) { @@ -54,124 +39,48 @@ export const Account = observer(() => { .get("/auth/account/") .then((account) => setEmail(account.email)); } - - if (profile === undefined && status === ClientStatus.ONLINE) { - client - .user!.fetchProfile() - .then((profile) => setProfile(profile ?? {})); - } - }, [client, email, profile, status]); + }, [client, email, status]); return (
-
-
- switchPage("profile")} - /> -
-
- switchPage("profile")} - /> -
- @{client.user!.username} -
-
- -
-
+ + + + + {( + [ + ["username", client.user!.username, At], + ["email", email, Envelope], + ["password", "•••••••••", Key], + ] as const + ).map(([field, value, Icon]) => ( + } + description={ + field === "email" ? ( + + ) : ( + value + ) + } + account + action={} + onClick={() => + openScreen({ + id: "modify_account", + field, + }) + }> + + + ))} - -
-
- {( - [ - [ - "username", - client.user!.username, - , - ], - ["email", email, ], - ["password", "•••••••••", ], - ] as const - ).map(([field, value, icon]) => ( - - {value}{" "} - - stopPropagation( - ev, - setRevealEmail(false), - ) - }> - - - - ) : ( - <> - •••••••••••@••••••.•••{" "} - - stopPropagation( - ev, - setRevealEmail(true), - ) - }> - - - - ) - ) : ( - value - ) - } - account - action={} - onClick={() => - openScreen({ - id: "modify_account", - field, - }) - }> - - - ))} -

+

@@ -197,10 +106,13 @@ export const Account = observer(() => { action="chevron"> View my backup codes */} +
+

+
@@ -227,6 +139,7 @@ export const Account = observer(() => { }> + } description={ @@ -250,13 +163,14 @@ export const Account = observer(() => { }> + {" "} - switchPage("profile")}> + - +
); diff --git a/yarn.lock b/yarn.lock index 5e07617b..95441f09 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2220,9 +2220,9 @@ __metadata: languageName: node linkType: hard -"@revoltchat/ui@npm:1.0.39": - version: 1.0.39 - resolution: "@revoltchat/ui@npm:1.0.39" +"@revoltchat/ui@portal:../components::locator=client%40workspace%3A.": + version: 0.0.0-use.local + resolution: "@revoltchat/ui@portal:../components::locator=client%40workspace%3A." dependencies: "@styled-icons/boxicons-logos": ^10.38.0 "@styled-icons/boxicons-regular": ^10.38.0 @@ -2235,9 +2235,8 @@ __metadata: react-device-detect: "*" react-virtuoso: "*" revolt.js: "*" - checksum: 0376ef1e6c90a139da613a0b76d498327c7bad63941d02eb27b9d5b8208f09c01fb45330fc4e0643554a298beee416814dd41fd9992750378491450c6f773ee0 languageName: node - linkType: hard + linkType: soft "@rollup/plugin-babel@npm:^5.2.0": version: 5.3.0