2022-02-19 18:11:55 +01:00
|
|
|
import { useEffect } from "react";
|
|
|
|
|
import type { ReactNode } from "react";
|
2022-02-14 17:05:17 +01:00
|
|
|
import {
|
|
|
|
|
LinksFunction,
|
|
|
|
|
LoaderFunction,
|
|
|
|
|
MetaFunction,
|
|
|
|
|
useLoaderData,
|
|
|
|
|
} from "remix";
|
|
|
|
|
import type { User, Team } from "@prisma/client";
|
2022-02-19 18:11:55 +01:00
|
|
|
import {
|
|
|
|
|
Links,
|
|
|
|
|
LiveReload,
|
|
|
|
|
Outlet,
|
|
|
|
|
useCatch,
|
|
|
|
|
Meta,
|
|
|
|
|
Scripts,
|
|
|
|
|
ScrollRestoration,
|
|
|
|
|
useLocation,
|
|
|
|
|
useMatches,
|
|
|
|
|
} from "remix";
|
2022-02-14 17:05:17 +01:00
|
|
|
import { getUser } from "./utils/session.server";
|
2022-02-10 10:44:44 +01:00
|
|
|
|
|
|
|
|
import styles from "./tailwind.css";
|
2022-02-14 13:23:06 +01:00
|
|
|
import headerStyles from "./styles/header.css";
|
2022-02-10 10:44:44 +01:00
|
|
|
|
|
|
|
|
export const links: LinksFunction = () => {
|
2022-02-14 13:23:06 +01:00
|
|
|
return [
|
|
|
|
|
{ rel: "stylesheet", href: styles },
|
|
|
|
|
{ rel: "stylesheet", href: headerStyles },
|
|
|
|
|
];
|
2022-02-10 10:44:44 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const meta: MetaFunction = () => {
|
|
|
|
|
const description = `Track and split shared expenses with friends and family.`;
|
|
|
|
|
return {
|
|
|
|
|
description,
|
|
|
|
|
keywords:
|
|
|
|
|
"Explit,expenses,split,flatmate,friends,family,payments,debts,money",
|
|
|
|
|
"twitter:creator": "@rawmaterial_it",
|
|
|
|
|
"twitter:site": "@rawmaterial_it",
|
|
|
|
|
"twitter:title": "Explit",
|
|
|
|
|
"twitter:description": description,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2022-02-14 17:05:17 +01:00
|
|
|
type LoaderData = {
|
|
|
|
|
user: (User & { team: Team & { members: User[] } }) | null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const loader: LoaderFunction = async ({ request }) => {
|
|
|
|
|
const user = await getUser(request);
|
|
|
|
|
|
|
|
|
|
const data: LoaderData = {
|
|
|
|
|
user,
|
|
|
|
|
};
|
|
|
|
|
return data;
|
|
|
|
|
};
|
|
|
|
|
|
2022-02-19 18:11:55 +01:00
|
|
|
let isMount = true;
|
|
|
|
|
|
2022-02-10 10:44:44 +01:00
|
|
|
function Document({
|
|
|
|
|
children,
|
|
|
|
|
title = `Explit`,
|
|
|
|
|
}: {
|
2022-02-19 18:11:55 +01:00
|
|
|
children: ReactNode;
|
2022-02-10 10:44:44 +01:00
|
|
|
title?: string;
|
|
|
|
|
}) {
|
2022-02-14 17:05:17 +01:00
|
|
|
const data = useLoaderData<LoaderData>();
|
|
|
|
|
|
2022-02-19 18:11:55 +01:00
|
|
|
let location = useLocation();
|
|
|
|
|
let matches = useMatches();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
let mounted = isMount;
|
|
|
|
|
isMount = false;
|
|
|
|
|
if ("serviceWorker" in navigator) {
|
|
|
|
|
if (navigator.serviceWorker.controller) {
|
|
|
|
|
navigator.serviceWorker.controller?.postMessage({
|
|
|
|
|
type: "REMIX_NAVIGATION",
|
|
|
|
|
isMount: mounted,
|
|
|
|
|
location,
|
|
|
|
|
matches,
|
|
|
|
|
manifest: window.__remixManifest,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
let listener = async () => {
|
|
|
|
|
await navigator.serviceWorker.ready;
|
|
|
|
|
navigator.serviceWorker.controller?.postMessage({
|
|
|
|
|
type: "REMIX_NAVIGATION",
|
|
|
|
|
isMount: mounted,
|
|
|
|
|
location,
|
|
|
|
|
matches,
|
|
|
|
|
manifest: window.__remixManifest,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
navigator.serviceWorker.addEventListener("controllerchange", listener);
|
|
|
|
|
return () => {
|
|
|
|
|
navigator.serviceWorker.removeEventListener(
|
|
|
|
|
"controllerchange",
|
|
|
|
|
listener
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [location]);
|
|
|
|
|
|
2022-02-10 10:44:44 +01:00
|
|
|
return (
|
2022-02-14 17:05:17 +01:00
|
|
|
<html lang="en" data-theme={data?.user?.theme ?? "dark"}>
|
2022-02-10 10:44:44 +01:00
|
|
|
<head>
|
|
|
|
|
<meta charSet="utf-8" />
|
2022-02-19 18:11:55 +01:00
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
2022-02-19 18:33:21 +01:00
|
|
|
<meta name="theme-color" content="#2a2e37" />
|
2022-02-10 10:44:44 +01:00
|
|
|
<Meta />
|
|
|
|
|
<title>{title}</title>
|
2022-02-19 18:11:55 +01:00
|
|
|
|
|
|
|
|
<link rel="manifest" href="/resources/manifest.json" />
|
|
|
|
|
|
|
|
|
|
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
2022-02-19 18:30:09 +01:00
|
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon.png" />
|
2022-02-19 18:11:55 +01:00
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
sizes="192x192"
|
|
|
|
|
href="/icons/android-chrome-192x192.png"
|
|
|
|
|
/>
|
|
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
sizes="32x32"
|
|
|
|
|
href="/icons/favicon-32x32.png"
|
|
|
|
|
/>
|
|
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
sizes="16x16"
|
|
|
|
|
href="/icons/favicon-16x16.png"
|
|
|
|
|
/>
|
2022-02-10 10:44:44 +01:00
|
|
|
<Links />
|
|
|
|
|
</head>
|
2022-02-14 13:23:06 +01:00
|
|
|
<body className="bg-base-300 m-0 min-h-screen p-3">
|
2022-02-10 10:44:44 +01:00
|
|
|
{children}
|
2022-02-19 18:11:55 +01:00
|
|
|
<ScrollRestoration />
|
2022-02-10 10:44:44 +01:00
|
|
|
<Scripts />
|
|
|
|
|
{process.env.NODE_ENV === "development" ? <LiveReload /> : null}
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function App() {
|
|
|
|
|
return (
|
|
|
|
|
<Document>
|
|
|
|
|
<Outlet />
|
|
|
|
|
</Document>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function CatchBoundary() {
|
|
|
|
|
const caught = useCatch();
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Document title={`${caught.status} ${caught.statusText}`}>
|
|
|
|
|
<div className="error-container">
|
|
|
|
|
<h1>
|
|
|
|
|
{caught.status} {caught.statusText}
|
|
|
|
|
</h1>
|
|
|
|
|
</div>
|
|
|
|
|
</Document>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ErrorBoundary({ error }: { error: Error }) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Document title="Uh-oh!">
|
|
|
|
|
<div className="error-container">
|
|
|
|
|
<h1>App Error</h1>
|
|
|
|
|
<pre>{error.message}</pre>
|
|
|
|
|
</div>
|
|
|
|
|
</Document>
|
|
|
|
|
);
|
|
|
|
|
}
|