import { Prompt } from "react-router"; import { useHistory } from "react-router-dom"; import type { Attachment } from "revolt-api/types/Autumn"; import { Bot } from "revolt-api/types/Bots"; import type { EmbedImage } from "revolt-api/types/January"; import { Channel } from "revolt.js/dist/maps/Channels"; import { Message } from "revolt.js/dist/maps/Messages"; import { Server } from "revolt.js/dist/maps/Servers"; import { User } from "revolt.js/dist/maps/Users"; import { createContext } from "preact"; import { useContext, useEffect, useMemo, useState } from "preact/hooks"; import { internalSubscribe } from "../../lib/eventEmitter"; import { determineLink } from "../../lib/links"; import { getState } from "../../redux"; import { Action } from "../../components/ui/Modal"; import { Children } from "../../types/Preact"; 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: "_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_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 } | { 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; } | { id: "onboarding"; callback: ( username: string, loginAfterSuccess?: true, ) => Promise; } // Pop-overs | { id: "profile"; user_id: string } | { id: "user_picker"; omit?: string[]; callback: (users: string[]) => Promise; } | { id: "image_viewer"; attachment?: Attachment; embed?: EmbedImage } | { id: "channel_info"; channel: Channel } | { id: "pending_requests"; users: User[] } | { id: "modify_account"; field: "username" | "email" | "password" } | { id: "create_bot"; onCreate: (bot: Bot) => void } | { id: "server_identity"; server: Server; }; export const IntermediateContext = createContext({ screen: { id: "none" }, focusTaken: false, }); export const IntermediateActionsContext = createContext<{ openLink: (href?: string) => boolean; openScreen: (screen: Screen) => void; writeClipboard: (text: string) => void; }>({ openLink: null!, openScreen: null!, writeClipboard: null!, }); interface Props { children: Children; } export default function Intermediate(props: Props) { const [screen, openScreen] = useState({ id: "none" }); const history = useHistory(); const value = { screen, focusTaken: screen.id !== "none", }; const actions = useMemo(() => { return { openLink: (href?: string) => { const link = determineLink(href); switch (link.type) { case "profile": { openScreen({ id: "profile", user_id: link.id }); return true; } case "navigate": { history.push(link.path); return true; } case "external": { const { trustedLinks } = getState(); if ( !trustedLinks.domains?.includes(link.url.hostname) ) { openScreen({ id: "external_link_prompt", link: link.href, }); } else { window.open(link.href, "_blank", "noreferrer"); } } } return true; }, openScreen: (screen: Screen) => openScreen(screen), writeClipboard: (text: string) => { if (navigator.clipboard) { navigator.clipboard.writeText(text); } else { actions.openScreen({ id: "clipboard", text }); } }, }; // eslint-disable-next-line }, []); useEffect(() => { const openProfile = (user_id: string) => openScreen({ id: "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 ( {screen.id !== "onboarding" && props.children} { if (action === "POP") { openScreen({ id: "none" }); setTimeout(() => history.push(history.location), 0); return false; } return true; }} /> ); } export const useIntermediate = () => useContext(IntermediateActionsContext);