Skip to content

Commit

Permalink
feat(_official-blog-tutorial): use enabled future flags (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelDeBoey committed Apr 18, 2023
1 parent c6fe178 commit 52d59bb
Show file tree
Hide file tree
Showing 26 changed files with 332 additions and 275 deletions.
9 changes: 8 additions & 1 deletion _official-jokes/.dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
node_modules
fly.toml
/node_modules
*.log
.DS_Store
.env
/.cache
/public/build
/build
2 changes: 1 addition & 1 deletion _official-jokes/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
DATABASE_URL="file:./dev.db"
SESSION_SECRET="remixrulz"
DATABASE_URL="file:./dev.db?connection_limit=1"
2 changes: 1 addition & 1 deletion _official-jokes/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ node_modules
/public/build
.env

prisma/dev.db
/prisma/dev.db
1 change: 0 additions & 1 deletion _official-jokes/.prettierrc

This file was deleted.

8 changes: 4 additions & 4 deletions _official-jokes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ FROM node:16-bullseye-slim as base
# Install openssl for Prisma
RUN apt-get update && apt-get install -y openssl

# set for base and all that inherit from it
ENV NODE_ENV=production
ENV NODE_ENV production

# Install all node_modules, including dev dependencies
FROM base as deps
Expand Down Expand Up @@ -43,7 +42,7 @@ RUN npm run build
# Finally, build the production image with minimal footprint
FROM base

ENV NODE_ENV=production
ENV NODE_ENV production

RUN mkdir /app
WORKDIR /app
Expand All @@ -54,4 +53,5 @@ COPY --from=build /app/build /app/build
COPY --from=build /app/public /app/public
ADD . .

CMD ["node", "node_modules/.bin/remix-serve", "build"]
ENV PORT 8080
CMD ["npm", "run", "start"]
2 changes: 1 addition & 1 deletion _official-jokes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Production deploy here: https://remix-jokes.lol

Tutorial here: https://rmx.as/jokes

This example demonstrates some of the basic features of Remix, including:
This example demonstrates some basic features of Remix, including:

- Generating a new Remix project
- Conventional files
Expand Down
10 changes: 5 additions & 5 deletions _official-jokes/app/components/joke.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ import type { Joke } from "@prisma/client";
import { Form, Link } from "@remix-run/react";

export function JokeDisplay({
joke,
isOwner,
canDelete = true,
isOwner,
joke,
}: {
joke: Pick<Joke, "content" | "name">;
isOwner: boolean;
canDelete?: boolean;
isOwner: boolean;
joke: Pick<Joke, "content" | "name">;
}) {
return (
<div>
<p>Here's your hilarious joke:</p>
<p>{joke.content}</p>
<Link to=".">{joke.name} Permalink</Link>
<Link to=".">"{joke.name}" Permalink</Link>
{isOwner ? (
<Form method="post">
<button
Expand Down
85 changes: 43 additions & 42 deletions _official-jokes/app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { LinksFunction, MetaFunction } from "@remix-run/node";
import type { LinksFunction, V2_MetaFunction } from "@remix-run/node";
import {
isRouteErrorResponse,
Links,
LiveReload,
Meta,
Outlet,
Scripts,
useCatch,
useRouteError,
} from "@remix-run/react";
import type { PropsWithChildren } from "react";

import globalLargeStylesUrl from "./styles/global-large.css";
import globalMediumStylesUrl from "./styles/global-medium.css";
import globalStylesUrl from "./styles/global.css";
import globalLargeStylesUrl from "~/styles/global-large.css";
import globalMediumStylesUrl from "~/styles/global-medium.css";
import globalStylesUrl from "~/styles/global.css";

export const links: LinksFunction = () => [
{ rel: "stylesheet", href: globalStylesUrl },
Expand All @@ -26,31 +28,31 @@ export const links: LinksFunction = () => [
},
];

export const meta: MetaFunction = () => {
const description = `Learn Remix and laugh at the same time!`;
return {
charset: "utf-8",
description,
keywords: "Remix,jokes",
"twitter:image": "https://remix-jokes.lol/social.png",
"twitter:card": "summary_large_image",
"twitter:creator": "@remix_run",
"twitter:site": "@remix_run",
"twitter:title": "Remix Jokes",
"twitter:description": description,
};
export const meta: V2_MetaFunction = () => {
const description = "Learn Remix and laugh at the same time!";

return [
{ name: "description", content: description },
{ name: "twitter:description", content: description },
{ title: "Remix: So great, it's funny!" },
];
};

function Document({
children,
title,
}: {
children: React.ReactNode;
title?: string;
}) {
function Document({ children, title }: PropsWithChildren<{ title?: string }>) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="keywords" content="Remix,jokes" />
<meta
name="twitter:image"
content="https://remix-jokes.lol/social.png"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content="@remix_run" />
<meta name="twitter:site" content="@remix_run" />
<meta name="twitter:title" content="Remix Jokes" />
<Meta />
{title ? <title>{title}</title> : null}
<Links />
Expand All @@ -65,36 +67,35 @@ function Document({
}

export default function App() {
// throw new Error("Not implemented");
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 }) {
export function ErrorBoundary() {
const error = useRouteError();
console.error(error);

if (isRouteErrorResponse(error)) {
return (
<Document title={`${error.status} ${error.statusText}`}>
<div className="error-container">
<h1>
{error.status} {error.statusText}
</h1>
</div>
</Document>
);
}

const errorMessage = error instanceof Error ? error.message : "Unknown error";
return (
<Document title="Uh-oh!">
<div className="error-container">
<h1>App Error</h1>
<pre>{error.message}</pre>
<pre>{errorMessage}</pre>
</div>
</Document>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import type { LinksFunction, MetaFunction } from "@remix-run/node";
import type { LinksFunction } from "@remix-run/node";
import { Link } from "@remix-run/react";

import stylesUrl from "../styles/index.css";

export const meta: MetaFunction = () => {
return {
title: "Remix: It's funny!",
description: "Remix jokes app. Learn Remix and laugh at the same time!",
};
};
import stylesUrl from "~/styles/index.css";

export const links: LinksFunction = () => [
{ rel: "stylesheet", href: stylesUrl },
];

export default function Index() {
export default function IndexRoute() {
return (
<div className="container">
<div className="content">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import type { ActionArgs, LoaderArgs, MetaFunction } from "@remix-run/node";
import type { ActionArgs, LoaderArgs, V2_MetaFunction } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { useCatch, useLoaderData, useParams } from "@remix-run/react";
import {
isRouteErrorResponse,
useLoaderData,
useParams,
useRouteError,
} from "@remix-run/react";

import { JokeDisplay } from "~/components/joke";
import { db } from "~/utils/db.server";
import { getUserId, requireUserId } from "~/utils/session.server";

export const meta: MetaFunction<typeof loader> = ({ data }) => {
if (!data) {
return {
title: "No joke",
description: "No joke found",
};
}
return {
title: `"${data.joke.name}" joke`,
description: `Enjoy the "${data.joke.name}" joke and much more`,
};
export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
const { description, title } = data
? {
description: `Enjoy the "${data.joke.name}" joke and much more`,
title: `"${data.joke.name}" joke`,
}
: { description: "No joke found", title: "No joke" };

return [
{ name: "description", content: description },
{ name: "twitter:description", content: description },
{ title },
];
};

export const loader = async ({ params, request }: LoaderArgs) => {
const userId = await getUserId(request);
const joke = await db.joke.findUnique({ where: { id: params.jokeId } });
const joke = await db.joke.findUnique({
where: { id: params.jokeId },
});
if (!joke) {
throw new Response("What a joke! Not found.", { status: 404 });
}
return json({ joke, isOwner: userId === joke.jokesterId });
return json({
isOwner: userId === joke.jokesterId,
joke,
});
};

export const action = async ({ params, request }: ActionArgs) => {
Expand All @@ -43,9 +55,7 @@ export const action = async ({ params, request }: ActionArgs) => {
throw new Response("Can't delete what does not exist", { status: 404 });
}
if (joke.jokesterId !== userId) {
throw new Response("Pssh, nice try. That's not your joke", {
status: 403,
});
throw new Response("Pssh, nice try. That's not your joke", { status: 403 });
}
await db.joke.delete({ where: { id: params.jokeId } });
return redirect("/jokes");
Expand All @@ -54,47 +64,39 @@ export const action = async ({ params, request }: ActionArgs) => {
export default function JokeRoute() {
const data = useLoaderData<typeof loader>();

return <JokeDisplay joke={data.joke} isOwner={data.isOwner} />;
return <JokeDisplay isOwner={data.isOwner} joke={data.joke} />;
}

export function CatchBoundary() {
const caught = useCatch();
const params = useParams();
switch (caught.status) {
case 400: {
export function ErrorBoundary() {
const { jokeId } = useParams();
const error = useRouteError();
console.error(error);

if (isRouteErrorResponse(error)) {
if (error.status === 400) {
return (
<div className="error-container">
What you're trying to do is not allowed.
</div>
);
}
case 404: {
if (error.status === 403) {
return (
<div className="error-container">
Huh? What the heck is {params.jokeId}?
Sorry, but "{jokeId}" is not your joke.
</div>
);
}
case 403: {
if (error.status === 404) {
return (
<div className="error-container">
Sorry, but {params.jokeId} is not your joke.
</div>
<div className="error-container">Huh? What the heck is "{jokeId}"?</div>
);
}
default: {
throw new Error(`Unhandled error: ${caught.status}`);
}
}
}

export function ErrorBoundary({ error }: { error: Error }) {
console.error(error);

const { jokeId } = useParams();
return (
<div className="error-container">
There was an error loading joke by the id {jokeId}. Sorry.
There was an error loading joke by the id ${jokeId}. Sorry.
</div>
);
}
Loading

0 comments on commit 52d59bb

Please sign in to comment.