Skip to content

Commit

Permalink
feat: ✨ implement custom Head component with defaults and OG/Twitter …
Browse files Browse the repository at this point in the history
…support
  • Loading branch information
Dan6erbond committed May 5, 2023
1 parent c5e8682 commit a073a26
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 184 deletions.
50 changes: 50 additions & 0 deletions src/components/head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import NextHead from "next/head";

interface HeadProps {
children?: React.ReactNode;
pageTitle: string;
description?: string;
url?: string;
image?: string | null;
video?: string | null;
ogType?: "article" | "website" | "post";
twitterCard?: "summary_large_image";
}

function Head({
children,
pageTitle,
description = "Easily share images and videos with others",
url,
image,
video,
ogType,
twitterCard,
}: HeadProps) {
const siteTitle = pageTitle ? `${pageTitle} | Share Me` : "Share Me";

return (
<NextHead>
<title>{siteTitle}</title>
<meta property="og:title" content={siteTitle} />
<meta name="description" content={description} />
<meta property="og:url" content={url} />
<meta property="og:type" content={ogType} />
{image && (
<>
<meta property="og:image" content={image} />
<meta property="twitter:image" content={image} />
</>
)}
{video && <meta property="og:video" content={video} />}
{twitterCard && <meta property="twitter:card" content={twitterCard} />}
<meta property="twitter:title" content={siteTitle} />
<meta property="twitter:description" content={description} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
{children}
</NextHead>
);
}

export default Head;
12 changes: 2 additions & 10 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { FileWithPath } from "@mantine/dropzone";
import { notifications } from "@mantine/notifications";
import { IconAlertCircle } from "@tabler/icons-react";
import { GetServerSideProps } from "next";
import Head from "next/head";
import Head from "@/components/head";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";

Expand Down Expand Up @@ -65,15 +65,7 @@ export default function Home() {

return (
<>
<Head>
<title>Share Me</title>
<meta
name="description"
content="Easily share images and videos with others"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Head pageTitle="Upload" />
<Box component="main" p="lg">
<Nav />
<Group sx={{ justifyContent: "center" }} align="start">
Expand Down
124 changes: 64 additions & 60 deletions src/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
SiTwitch,
SiTwitter,
} from "react-icons/si";
import Head from "@/components/head";

const authProviderIcons: Record<string, JSX.Element> = {
apple: <SiApple />,
Expand Down Expand Up @@ -90,67 +91,70 @@ function Login() {
};

return (
<Box>
<Group position="center" h="100vh">
<form onSubmit={form.onSubmit(login)}>
<Stack>
{usernamePasswordEnabled && (
<>
<Title>Share Me</Title>
<Title order={3} color="gray.5">
Login
</Title>
<TextInput
label="Username"
miw="300px"
{...form.getInputProps("username")}
/>
<PasswordInput
label="Password"
miw="300px"
{...form.getInputProps("password")}
/>
{loginError && <Text color="red">{loginError}</Text>}
<Divider />
</>
)}
{authProviders?.map((provider) => (
<Button
leftIcon={authProviderIcons[provider.name]}
key={provider.name}
onClick={() =>
pb
.collection("users")
.authWithOAuth2({ provider: provider.name })
.then(() => router.push("/"))
.catch((ex) => {
console.error(ex);
notifications.show({
color: "red",
title: "An error occured",
message: "Please contact the developers",
icon: <IconAlertCircle />,
});
})
}
>
Sign in with {provider.name}
</Button>
))}
{usernamePasswordEnabled && (
<Group sx={{ justifyContent: "space-between" }}>
<Anchor component={Link} href="/sign-up">
Sign Up
</Anchor>
<Button variant="gradient" type="submit">
Log In
<>
<Head pageTitle="Login" />
<Box>
<Group position="center" h="100vh">
<form onSubmit={form.onSubmit(login)}>
<Stack>
{usernamePasswordEnabled && (
<>
<Title>Share Me</Title>
<Title order={3} color="gray.5">
Login
</Title>
<TextInput
label="Username"
miw="300px"
{...form.getInputProps("username")}
/>
<PasswordInput
label="Password"
miw="300px"
{...form.getInputProps("password")}
/>
{loginError && <Text color="red">{loginError}</Text>}
<Divider />
</>
)}
{authProviders?.map((provider) => (
<Button
leftIcon={authProviderIcons[provider.name]}
key={provider.name}
onClick={() =>
pb
.collection("users")
.authWithOAuth2({ provider: provider.name })
.then(() => router.push("/"))
.catch((ex) => {
console.error(ex);
notifications.show({
color: "red",
title: "An error occured",
message: "Please contact the developers",
icon: <IconAlertCircle />,
});
})
}
>
Sign in with {provider.name}
</Button>
</Group>
)}
</Stack>
</form>
</Group>
</Box>
))}
{usernamePasswordEnabled && (
<Group sx={{ justifyContent: "space-between" }}>
<Anchor component={Link} href="/sign-up">
Sign Up
</Anchor>
<Button variant="gradient" type="submit">
Log In
</Button>
</Group>
)}
</Stack>
</form>
</Group>
</Box>
</>
);
}

Expand Down
137 changes: 63 additions & 74 deletions src/pages/posts/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Dropzone from "@/components/dropzone";
import Head from "@/components/head";
import Nav from "@/components/nav";
import {
initPocketBaseServer,
Expand Down Expand Up @@ -36,7 +37,6 @@ import {
} from "@tabler/icons-react";
import { FileWithPath } from "file-selector";
import { GetServerSideProps } from "next";
import Head from "next/head";
import { useRouter } from "next/router";
import { Record } from "pocketbase";
import { useCallback, useEffect, useState } from "react";
Expand Down Expand Up @@ -185,31 +185,20 @@ export default function Post(props: PostProps) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [post, debouncedTitle, isPublic, nsfw]);

const siteTitle = post?.title || `Post by ${props.postAuthorUsername}`;
const postTitle = post?.title || `Post by ${props.postAuthorUsername}`;
const description = `Shared by ${props.postAuthorUsername}`;

return (
<>
<Head>
<title>{siteTitle} | Share Me</title>
<meta property="og:title" content={`${siteTitle} | Share Me`} />
<meta
name="description"
content={`Shared by ${props.postAuthorUsername}`}
/>
<meta property="og:url" content="" />
<meta property="og:type" content="article" />
{props.image && <meta property="og:image" content={props.image} />}
{props.video && <meta property="og:video" content={props.video} />}
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content={`${siteTitle} | Share Me`} />
<meta
property="twitter:description"
content={`Shared by ${props.postAuthorUsername}`}
/>
{props.image && <meta property="twitter:image" content={props.image} />}
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Head
pageTitle={postTitle}
description={description}
image={props.image}
video={props.video}
ogType="post"
twitterCard="summary_large_image"
/>

<Box component="main" p="lg">
<Nav />
<Group sx={{ justifyContent: "center" }} align="start">
Expand Down Expand Up @@ -248,16 +237,16 @@ export default function Post(props: PostProps) {
</ActionIcon>
)}
</Group>
<Box
pos="relative"
sx={{
":hover": {
"> button": {
opacity: 1,
},
<Box
pos="relative"
sx={{
":hover": {
"> button": {
opacity: 1,
},
}}
>
},
}}
>
{IMAGE_MIME_TYPE.includes(f.type as any) ? (
<Image
src={pb.files.getUrl(f, f.file)}
Expand All @@ -274,7 +263,7 @@ export default function Post(props: PostProps) {
)
}
/>
) : (
) : (
<video
src={pb.files.getUrl(f, f.file)}
controls
Expand All @@ -291,49 +280,49 @@ export default function Post(props: PostProps) {
}
/>
)}
<CopyButton value={pb.files.getUrl(f, f.file)}>
{({ copy, copied }) => (
<>
<MediaQuery
smallerThan="sm"
styles={{ display: "none" }}
<CopyButton value={pb.files.getUrl(f, f.file)}>
{({ copy, copied }) => (
<>
<MediaQuery
smallerThan="sm"
styles={{ display: "none" }}
>
<Button
pos="absolute"
top="8px"
right="8px"
size="sm"
radius="md"
variant="gradient"
onClick={copy}
opacity={0}
>
<Button
pos="absolute"
top="8px"
right="8px"
size="sm"
radius="md"
variant="gradient"
onClick={copy}
opacity={0}
>
{copied ? "Copied" : "Copy Link"}
</Button>
</MediaQuery>
<MediaQuery
largerThan="sm"
styles={{ display: "none" }}
{copied ? "Copied" : "Copy Link"}
</Button>
</MediaQuery>
<MediaQuery
largerThan="sm"
styles={{ display: "none" }}
>
<ActionIcon
pos="absolute"
top="8px"
right="8px"
size="lg"
variant="gradient"
onClick={copy}
>
<ActionIcon
pos="absolute"
top="8px"
right="8px"
size="lg"
variant="gradient"
onClick={copy}
>
{copied ? (
<IconClipboardCheck />
) : (
<IconClipboardCopy />
)}
</ActionIcon>
</MediaQuery>
</>
)}
</CopyButton>
</Box>
{copied ? (
<IconClipboardCheck />
) : (
<IconClipboardCopy />
)}
</ActionIcon>
</MediaQuery>
</>
)}
</CopyButton>
</Box>
{userIsAuthor ? (
<TextInput
placeholder="Description"
Expand Down
Loading

0 comments on commit a073a26

Please sign in to comment.