handmade-revolt/src/pages/discover/Discover.tsx

180 lines
5.0 KiB
TypeScript

import { Compass } from "@styled-icons/boxicons-solid";
import { reaction } from "mobx";
import { useHistory, useLocation } from "react-router-dom";
import styled, { css } from "styled-components/macro";
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
import { isTouchscreenDevice } from "../../lib/isTouchscreenDevice";
import { useApplicationState } from "../../mobx/State";
import { Overrides } from "../../context/Theme";
import { useIntermediate } from "../../context/intermediate/Intermediate";
import Header from "../../components/ui/Header";
import Preloader from "../../components/ui/Preloader";
const Container = styled.div`
flex-grow: 1;
display: flex;
flex-direction: column;
${() =>
isTouchscreenDevice
? css`
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed;
padding-bottom: 50px;
background: var(--background);
`
: css`
background: var(--background);
`}
`;
const Frame = styled.iframe<{ loaded: boolean }>`
border: none;
${() =>
!isTouchscreenDevice &&
css`
background: var(--secondary-background);
border-start-start-radius: 8px;
border-end-start-radius: 8px;
`}
${(props) =>
props.loaded
? css`
height: 100%;
`
: css`
display: none;
`}
`;
const Loader = styled.div`
flex-grow: 1;
${() =>
!isTouchscreenDevice &&
css`
background: var(--secondary-background);
border-start-start-radius: 8px;
border-end-start-radius: 8px;
`}
`;
const TRUSTED_HOSTS = [
"local.revolt.chat:3000",
"local.revolt.chat:3001",
"rvlt.gg",
];
const REMOTE = "https://rvlt.gg";
export default function Discover() {
const state = useApplicationState();
const { openLink } = useIntermediate();
const history = useHistory();
const { pathname, search } = useLocation();
const path = useMemo(() => {
const query = new URLSearchParams(search);
query.set("embedded", "true");
return `${pathname}?${query.toString()}`;
}, []);
const [loaded, setLoaded] = useState(false);
const ref = useRef<HTMLIFrameElement>(null!);
function sendTheme(theme?: Overrides) {
ref.current?.contentWindow?.postMessage(
JSON.stringify({
source: "revolt",
type: "theme",
theme: theme ?? state.settings.theme.computeVariables(),
}),
"*",
);
}
useEffect(
() =>
reaction(() => state.settings.theme.computeVariables(), sendTheme),
[],
);
useEffect(() => state.layout.setLastDiscoverPath(path), []);
useEffect(() => {
function onMessage(message: MessageEvent) {
let url = new URL(message.origin);
if (!TRUSTED_HOSTS.includes(url.host)) return;
try {
let data = JSON.parse(message.data);
if (data.source === "discover") {
switch (data.type) {
case "init": {
sendTheme();
break;
}
case "path": {
history.replace(data.path);
state.layout.setLastDiscoverPath(data.path);
break;
}
case "navigate": {
openLink(data.url);
break;
}
case "applyTheme": {
state.settings.theme.hydrate({
...data.theme.variables,
css: data.theme.css,
});
break;
}
}
}
} catch (err) {
if (import.meta.env.DEV) {
console.error(err);
}
}
}
window.addEventListener("message", onMessage);
return () => window.removeEventListener("message", onMessage);
}, [ref]);
return (
<Container>
{isTouchscreenDevice && (
<Header placement="primary">
<Compass size={27} />
Discover
</Header>
)}
{!loaded && (
<Loader>
<Preloader type="ring" />
</Loader>
)}
<Frame
ref={ref}
loaded={loaded}
crossOrigin="anonymous"
onLoad={() => setLoaded(true)}
src={REMOTE + path}
/>
</Container>
);
}