diff --git a/app/root.tsx b/app/root.tsx index 0100364..1cb842d 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,9 +1,13 @@ import "@mantine/core/styles.css"; import { + Button as MantineButton, + Code, ColorSchemeScript, CSSVariablesResolver, MantineProvider, + Stack, + Text, Title, } from "@mantine/core"; import { useLocalStorage, useToggle } from "@mantine/hooks"; @@ -14,6 +18,7 @@ import type { MetaFunction, } from "@remix-run/node"; import { + isRouteErrorResponse, json, Link, Links, @@ -24,9 +29,9 @@ import { useRouteError, } from "@remix-run/react"; -import { Button, Layout, Notification } from "~/components"; +import { Layout, Notification } from "~/components"; import styles from "~/styles/root.css?url"; -import { Status, status as httpStatus } from "~/utils"; +import { baseURL, Status, status as httpStatus } from "~/utils"; import ColorSchemeContext from "./styles/colorSchemeContext"; @@ -51,9 +56,9 @@ export const links: LinksFunction = () => [ rel: "license", href: "https://github.com/blakenetz/portfolio/blob/master/LICENSE", }, - { rel: "me", href: "https://blakenetzeband.com", type: "text/html" }, + { rel: "me", href: baseURL, type: "text/html" }, { rel: "me", href: "mailto:blake.netzeband@gmail.com" }, - { rel: "index", href: "https://blakenetzeband.com" }, + { rel: "index", href: baseURL }, ]; export const headers: HeadersFunction = () => ({ @@ -94,6 +99,11 @@ const resolver: CSSVariablesResolver = (theme) => { export function ErrorBoundary() { const error = useRouteError(); + const status = isRouteErrorResponse(error) ? error.status : 520; + const statusText = isRouteErrorResponse(error) + ? error.statusText + : (error as Error)?.message ?? "Unknown"; + if (process.env.NODE_ENV === "development") { console.error("aw shit!", error); } @@ -103,6 +113,7 @@ export function ErrorBoundary() { + @@ -110,12 +121,25 @@ export function ErrorBoundary() { - - Crap. We hit an issue. - - + + + <Text fw="900" component="span"> + {status} + </Text> + ... Crap. We hit an issue + + This is all we know: + {statusText} + + + Send me home! + + diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 17f64f3..3bee393 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,10 +1,15 @@ import { Anchor, Flex, Text, Title } from "@mantine/core"; +import { MetaFunction } from "@remix-run/node"; import { Link } from "@remix-run/react"; import { Button } from "~/components"; import commonStyles from "~/styles/common.module.css"; import styles from "~/styles/index.module.css"; -import { cls } from "~/utils"; +import { cls, getCanonicalLink } from "~/utils"; + +export const meta: MetaFunction = ({ location }) => { + return [getCanonicalLink(location)]; +}; export default function Index() { return ( diff --git a/app/routes/blog.$post/route.tsx b/app/routes/blog.$post/route.tsx index ed02dbc..403f15d 100644 --- a/app/routes/blog.$post/route.tsx +++ b/app/routes/blog.$post/route.tsx @@ -17,7 +17,7 @@ import { authenticator } from "~/server/authenticator.server"; import { getPost, postComment } from "~/server/blog.server"; import { PostModel } from "~/server/db.singleton.server"; import commonStyles from "~/styles/common.module.css"; -import { cls } from "~/utils"; +import { baseURL, cls, getCanonicalLink } from "~/utils"; import Comments from "./comments"; import components from "./components"; @@ -27,17 +27,18 @@ import Source from "./source"; export const meta: MetaFunction = ({ data, location }) => { const { meta } = data as PostModel; + const url = new URL(location.pathname, baseURL); + + const canonicalLink = getCanonicalLink(location); const tags = [ { title: ["BN", "Blog", meta.title].join(" | ") }, { name: "description", content: meta.description }, + canonicalLink, /** @see https://www.linkedin.com/help/linkedin/answer/a521928/making-your-website-shareable-on-linkedin?lang=en */ { property: "og:title", content: meta.title }, { property: "og:description", content: meta.description }, - { - property: "og:url", - content: "https://blakenetzeband.com" + location.pathname, - }, + { property: "og:url", content: url.toString() }, { property: "og:type", content: "article" }, ]; diff --git a/app/routes/blog._index/route.tsx b/app/routes/blog._index/route.tsx index f5de5ac..fa5049d 100644 --- a/app/routes/blog._index/route.tsx +++ b/app/routes/blog._index/route.tsx @@ -13,13 +13,14 @@ import { useState } from "react"; import { Card, SortControl } from "~/components"; import { inputName, sorts } from "~/server/blog"; import { getPosts } from "~/server/blog.server"; -import { getSearchString, validate } from "~/utils"; +import { getCanonicalLink, getSearchString, validate } from "~/utils"; import styles from "./blog.module.css"; -export const meta: MetaFunction = () => [ +export const meta: MetaFunction = ({ location }) => [ { title: "BN | Blog" }, { description: "My thoughts. some complete... others not... 😜" }, + getCanonicalLink(location), ]; export async function loader({ request }: LoaderFunctionArgs) { diff --git a/app/routes/projects/route.tsx b/app/routes/projects/route.tsx index e808162..50cbfee 100644 --- a/app/routes/projects/route.tsx +++ b/app/routes/projects/route.tsx @@ -3,15 +3,18 @@ import { Form, Link, useLoaderData, useSubmit } from "@remix-run/react"; import { Button } from "~/components"; import { getRepos } from "~/server/projects.server"; -import { status } from "~/utils"; +import { getCanonicalLink, status } from "~/utils"; import styles from "./projects.module.css"; import Repos from "./repos"; -export const meta: MetaFunction = () => [ - { title: "BN | Projects" }, - { description: "My personal and work Github repositories" }, -]; +export const meta: MetaFunction = ({ location }) => { + return [ + getCanonicalLink(location), + { title: "BN | Projects" }, + { description: "My personal and work Github repositories" }, + ]; +}; export async function loader({ request }: LoaderFunctionArgs) { const repos = await getRepos(request); diff --git a/app/utils/frontend.tsx b/app/utils/frontend.tsx index 1114b5b..881089e 100644 --- a/app/utils/frontend.tsx +++ b/app/utils/frontend.tsx @@ -1,3 +1,18 @@ +import { MetaDescriptor } from "@remix-run/node"; +import { Location } from "@remix-run/react"; + export function cls(...args: (string | boolean | undefined | null)[]) { return args.filter(Boolean).join(" ").trim(); } + +export const baseURL = "https://blakenetzeband.com"; + +export function getCanonicalLink(location: Location): MetaDescriptor { + const url = new URL(location.pathname, baseURL); + + return { + tagName: "link", + rel: "canonical", + href: url.toString(), + }; +} diff --git a/scripts/sitemap.ts b/scripts/sitemap.ts index ad673dd..824d096 100644 --- a/scripts/sitemap.ts +++ b/scripts/sitemap.ts @@ -4,6 +4,8 @@ import { DOMParser, XMLSerializer } from "@xmldom/xmldom"; import { format } from "date-fns"; import path from "path"; +import { baseURL } from "~/utils"; + import { extractVFile, formatMeta } from "./util"; type NodeData = { [tagName: string]: string }; @@ -22,8 +24,8 @@ export default async function createSiteMap() { const siteMapPath = path.resolve(".", "public/sitemap.xml"); const blogDir = path.resolve(".", "app/blog"); // trailing slash is important - const urlBase = "https://blakenetzeband.com/"; - const blogBase = new URL("./blog", urlBase) + "/"; + + const blogBase = new URL("./blog", baseURL) + "/"; const siteMap = await fs.readFile(siteMapPath, "utf-8"); @@ -60,7 +62,7 @@ export default async function createSiteMap() { // iterate over routes ["", "projects/", "blog/"].forEach((route) => { - const loc = new URL(`./${route}`, urlBase).toString(); + const loc = new URL(`./${route}`, baseURL).toString(); const today = formatDate(); const node = getByLocation(loc);