revite/src/context/intermediate/Intermediate.tsx

182 lines
5.5 KiB
TypeScript
Raw Normal View History

2021-07-05 06:23:23 -04:00
import { Prompt } from "react-router";
import { useHistory } from "react-router-dom";
import {
2021-07-05 06:25:20 -04:00
Attachment,
Channels,
EmbedImage,
Servers,
Users,
2021-07-05 06:23:23 -04:00
} from "revolt.js/dist/api/objects";
import { createContext } from "preact";
2021-06-19 13:46:05 -04:00
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
2021-07-05 06:23:23 -04:00
import { internalSubscribe } from "../../lib/eventEmitter";
2021-07-05 06:23:23 -04:00
2021-07-29 13:41:01 -04:00
import { Channel, User } from "../../mobx";
2021-07-29 10:11:21 -04:00
2021-06-19 13:46:05 -04:00
import { Action } from "../../components/ui/Modal";
2021-07-05 06:23:23 -04:00
2021-06-19 13:46:05 -04:00
import { Children } from "../../types/Preact";
2021-07-05 06:23:23 -04:00
import Modals from "./Modals";
2021-06-19 13:46:05 -04:00
export type Screen =
2021-07-05 06:25:20 -04:00
| { id: "none" }
// Modals
| { id: "signed_out" }
| { id: "error"; error: string }
| { id: "clipboard"; text: string }
| {
id: "_prompt";
question: Children;
content?: Children;
actions: Action[];
}
| ({ id: "special_prompt" } & (
2021-07-29 13:41:01 -04:00
| { type: "leave_group"; target: Channel }
| { type: "close_dm"; target: Channel }
2021-07-05 06:25:20 -04:00
| { type: "leave_server"; target: Servers.Server }
| { type: "delete_server"; target: Servers.Server }
2021-07-29 13:41:01 -04:00
| { type: "delete_channel"; target: Channel }
2021-07-05 06:25:20 -04:00
| { type: "delete_message"; target: Channels.Message }
| {
type: "create_invite";
2021-07-29 13:41:01 -04:00
target: Channel;
2021-07-05 06:25:20 -04:00
}
2021-07-29 10:11:21 -04:00
| { type: "kick_member"; target: Servers.Server; user: User }
| { type: "ban_member"; target: Servers.Server; user: User }
| { type: "unfriend_user"; target: User }
| { type: "block_user"; target: User }
2021-07-05 06:25:20 -04:00
| { type: "create_channel"; target: Servers.Server }
))
| ({ id: "special_input" } & (
| {
type:
| "create_group"
| "create_server"
| "set_custom_status"
| "add_friend";
}
| {
type: "create_role";
server: string;
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: "image_viewer"; attachment?: Attachment; embed?: EmbedImage }
| { id: "modify_account"; field: "username" | "email" | "password" }
| { id: "profile"; user_id: string }
2021-07-29 13:41:01 -04:00
| { id: "channel_info"; channel: Channel }
2021-07-29 10:11:21 -04:00
| { id: "pending_requests"; users: User[] }
2021-07-05 06:25:20 -04:00
| {
id: "user_picker";
omit?: string[];
callback: (users: string[]) => Promise<void>;
};
2021-06-19 13:46:05 -04:00
export const IntermediateContext = createContext({
2021-07-05 06:25:20 -04:00
screen: { id: "none" } as Screen,
focusTaken: false,
2021-06-19 13:46:05 -04:00
});
export const IntermediateActionsContext = createContext({
2021-07-05 06:25:20 -04:00
openScreen: (screen: Screen) => {},
writeClipboard: (text: string) => {},
2021-06-19 13:46:05 -04:00
});
interface Props {
2021-07-05 06:25:20 -04:00
children: Children;
2021-06-19 13:46:05 -04:00
}
export default function Intermediate(props: Props) {
2021-07-05 06:25:20 -04:00
const [screen, openScreen] = useState<Screen>({ id: "none" });
const history = useHistory();
const value = {
screen,
focusTaken: screen.id !== "none",
};
const actions = useMemo(() => {
return {
openScreen: (screen: Screen) => openScreen(screen),
writeClipboard: (text: string) => {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
} else {
actions.openScreen({ id: "clipboard", text });
}
},
};
}, []);
useEffect(() => {
const openProfile = (user_id: string) =>
openScreen({ id: "profile", user_id });
const navigate = (path: string) => history.push(path);
const subs = [
internalSubscribe("Intermediate", "openProfile", openProfile),
internalSubscribe("Intermediate", "navigate", navigate),
];
return () => subs.map((unsub) => unsub());
}, []);
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>
);
2021-06-19 13:46:05 -04:00
}
export const useIntermediate = () => useContext(IntermediateActionsContext);