feat: re-work modal behaviour to be more natural

This commit is contained in:
Paul Makles 2022-06-18 11:22:37 +01:00
parent 63d5f6bb7d
commit 0ee7b73d61
11 changed files with 72 additions and 12 deletions

View file

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

View file

@ -1,5 +1,22 @@
import { observer } from "mobx-react-lite";
import { useEffect } from "preact/hooks";
import { modalController } from ".";
export default observer(() => modalController.rendered);
export default observer(() => {
useEffect(() => {
function keyUp(event: KeyboardEvent) {
if (event.key === "Escape") {
modalController.pop("close");
} else if (event.key === "Enter") {
modalController.pop("confirm");
}
}
document.addEventListener("keyup", keyUp);
return () => document.removeEventListener("keyup", keyUp);
}, []);
return modalController.rendered;
});

View file

@ -40,6 +40,7 @@ function RenderLog({ post }: { post: ChangelogPost }) {
export default function Changelog({
initial,
onClose,
signal,
}: ModalProps<"changelog">) {
const [log, setLog] = useState(initial);
@ -86,7 +87,8 @@ export default function Changelog({
)
}
actions={actions}
onClose={onClose}>
onClose={onClose}
signal={signal}>
{entry ? (
<RenderLog post={entry} />
) : (

View file

@ -31,6 +31,7 @@ export default function MFAEnableTOTP({
secret,
callback,
onClose,
signal,
}: ModalProps<"mfa_enable_totp">) {
const uri = `otpauth://totp/Revolt:${identifier}?secret=${secret}&issuer=Revolt`;
const [value, setValue] = useState("");
@ -61,7 +62,9 @@ export default function MFAEnableTOTP({
onClose={() => {
callback();
onClose();
}}>
}}
signal={signal}
nonDismissable>
<Column>
<Centred>
<Qr>

View file

@ -81,7 +81,11 @@ function ResponseEntry({
/**
* MFA ticket creation flow
*/
export default function MFAFlow({ onClose, ...props }: ModalProps<"mfa_flow">) {
export default function MFAFlow({
onClose,
signal,
...props
}: ModalProps<"mfa_flow">) {
const [methods, setMethods] = useState<API.MFAMethod[] | undefined>(
props.state === "unknown" ? props.available_methods : undefined,
);
@ -178,6 +182,16 @@ export default function MFAFlow({ onClose, ...props }: ModalProps<"mfa_flow">) {
},
]
}
// If we are logging in or have selected a method,
// don't allow the user to dismiss the modal by clicking off.
// This is to just generally prevent annoying situations
// where you accidentally close the modal while logging in
// or when switching to your password manager.
nonDismissable={
props.state === "unknown" ||
typeof selectedMethod !== "undefined"
}
signal={signal}
onClose={() => {
props.callback();
onClose();

View file

@ -32,6 +32,7 @@ export default function MFARecovery({
codes,
client,
onClose,
signal,
}: ModalProps<"mfa_recovery">) {
// Keep track of changes to recovery codes
const [known, setCodes] = useState(codes);
@ -69,7 +70,8 @@ export default function MFARecovery({
onClick: reset,
},
]}
onClose={onClose}>
onClose={onClose}
signal={signal}>
<List>
{known.map((code) => (
<span key={code}>{code}</span>

View file

@ -31,8 +31,10 @@ class ModalController<T extends Modal> {
makeObservable(this, {
stack: observable,
push: action,
pop: action,
remove: action,
rendered: computed,
isVisible: computed,
});
}
@ -50,6 +52,16 @@ class ModalController<T extends Modal> {
];
}
/**
* Remove the top modal from the screen
* @param signal What action to trigger
*/
pop(signal: "close" | "confirm" | "force") {
this.stack = this.stack.map((entry, index) =>
index === this.stack.length - 1 ? { ...entry, signal } : entry,
);
}
/**
* Remove the keyed modal from the stack
*/
@ -66,6 +78,8 @@ class ModalController<T extends Modal> {
{this.stack.map((modal) => {
const Component = this.components[modal.type];
return (
// ESLint does not understand spread operator
// eslint-disable-next-line
<Component
{...modal}
onClose={() => this.remove(modal.key!)}
@ -75,6 +89,10 @@ class ModalController<T extends Modal> {
</>
);
}
get isVisible() {
return this.stack.length > 0;
}
}
/**

View file

@ -39,4 +39,5 @@ export type Modal = {
export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {
onClose: () => void;
signal?: "close" | "confirm";
};

View file

@ -150,7 +150,6 @@ export default class State {
() => stringify(store.toJSON()),
async (value) => {
try {
console.log(id, "updated!");
// Save updated store to local storage.
await localforage.setItem(id, JSON.parse(value));

View file

@ -13,6 +13,8 @@ import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../mobx/State";
import { modalController } from "../../context/modals";
import ButtonItem from "../../components/navigation/items/ButtonItem";
interface Props {
@ -61,6 +63,8 @@ export function GenericSettings({
useEffect(() => {
function keyDown(e: KeyboardEvent) {
if (e.key === "Escape") {
if (modalController.isVisible) return;
exitSettings();
}
}

View file

@ -2231,9 +2231,9 @@ __metadata:
languageName: node
linkType: hard
"@revoltchat/ui@npm:1.0.40":
version: 1.0.40
resolution: "@revoltchat/ui@npm:1.0.40"
"@revoltchat/ui@npm:1.0.43":
version: 1.0.43
resolution: "@revoltchat/ui@npm:1.0.43"
dependencies:
"@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0
@ -2246,7 +2246,7 @@ __metadata:
react-device-detect: "*"
react-virtuoso: "*"
revolt.js: "*"
checksum: bc0bc906cdb22e8a31c862d1e87f8bd5c46cb463aa23ad773e9c683514fbe0e52ac44e9eab41dd6aa6e8e207050f9ab0590d6e51b2a4d8af6c0fb2ea899d789f
checksum: d6a6d0cb4a2f08fea45a4d61e5599894012fbb591472ef95d34ee8ddc9e66cfdc7626e94360b7c104e59d3c64a7d0bd674d6a42f5c3cefc723574db8c1aee64e
languageName: node
linkType: hard
@ -3539,7 +3539,7 @@ __metadata:
"@hcaptcha/react-hcaptcha": ^0.3.6
"@insertish/vite-plugin-babel-macros": ^1.0.5
"@preact/preset-vite": ^2.0.0
"@revoltchat/ui": 1.0.40
"@revoltchat/ui": 1.0.43
"@rollup/plugin-replace": ^2.4.2
"@styled-icons/boxicons-logos": ^10.38.0
"@styled-icons/boxicons-regular": ^10.38.0