Skip to content

Commit 52d59bb

Browse files
feat(_official-blog-tutorial): use enabled future flags (#216)
1 parent c6fe178 commit 52d59bb

26 files changed

+332
-275
lines changed

_official-jokes/.dockerignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
node_modules
1+
fly.toml
2+
/node_modules
3+
*.log
4+
.DS_Store
5+
.env
6+
/.cache
7+
/public/build
8+
/build

_official-jokes/.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1+
DATABASE_URL="file:./dev.db"
12
SESSION_SECRET="remixrulz"
2-
DATABASE_URL="file:./dev.db?connection_limit=1"

_official-jokes/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ node_modules
55
/public/build
66
.env
77

8-
prisma/dev.db
8+
/prisma/dev.db

_official-jokes/.prettierrc

Lines changed: 0 additions & 1 deletion
This file was deleted.

_official-jokes/Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ FROM node:16-bullseye-slim as base
44
# Install openssl for Prisma
55
RUN apt-get update && apt-get install -y openssl
66

7-
# set for base and all that inherit from it
8-
ENV NODE_ENV=production
7+
ENV NODE_ENV production
98

109
# Install all node_modules, including dev dependencies
1110
FROM base as deps
@@ -43,7 +42,7 @@ RUN npm run build
4342
# Finally, build the production image with minimal footprint
4443
FROM base
4544

46-
ENV NODE_ENV=production
45+
ENV NODE_ENV production
4746

4847
RUN mkdir /app
4948
WORKDIR /app
@@ -54,4 +53,5 @@ COPY --from=build /app/build /app/build
5453
COPY --from=build /app/public /app/public
5554
ADD . .
5655

57-
CMD ["node", "node_modules/.bin/remix-serve", "build"]
56+
ENV PORT 8080
57+
CMD ["npm", "run", "start"]

_official-jokes/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Production deploy here: https://remix-jokes.lol
66

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

9-
This example demonstrates some of the basic features of Remix, including:
9+
This example demonstrates some basic features of Remix, including:
1010

1111
- Generating a new Remix project
1212
- Conventional files

_official-jokes/app/components/joke.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import type { Joke } from "@prisma/client";
22
import { Form, Link } from "@remix-run/react";
33

44
export function JokeDisplay({
5-
joke,
6-
isOwner,
75
canDelete = true,
6+
isOwner,
7+
joke,
88
}: {
9-
joke: Pick<Joke, "content" | "name">;
10-
isOwner: boolean;
119
canDelete?: boolean;
10+
isOwner: boolean;
11+
joke: Pick<Joke, "content" | "name">;
1212
}) {
1313
return (
1414
<div>
1515
<p>Here's your hilarious joke:</p>
1616
<p>{joke.content}</p>
17-
<Link to=".">{joke.name} Permalink</Link>
17+
<Link to=".">"{joke.name}" Permalink</Link>
1818
{isOwner ? (
1919
<Form method="post">
2020
<button

_official-jokes/app/root.tsx

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import type { LinksFunction, MetaFunction } from "@remix-run/node";
1+
import type { LinksFunction, V2_MetaFunction } from "@remix-run/node";
22
import {
3+
isRouteErrorResponse,
34
Links,
45
LiveReload,
56
Meta,
67
Outlet,
78
Scripts,
8-
useCatch,
9+
useRouteError,
910
} from "@remix-run/react";
11+
import type { PropsWithChildren } from "react";
1012

11-
import globalLargeStylesUrl from "./styles/global-large.css";
12-
import globalMediumStylesUrl from "./styles/global-medium.css";
13-
import globalStylesUrl from "./styles/global.css";
13+
import globalLargeStylesUrl from "~/styles/global-large.css";
14+
import globalMediumStylesUrl from "~/styles/global-medium.css";
15+
import globalStylesUrl from "~/styles/global.css";
1416

1517
export const links: LinksFunction = () => [
1618
{ rel: "stylesheet", href: globalStylesUrl },
@@ -26,31 +28,31 @@ export const links: LinksFunction = () => [
2628
},
2729
];
2830

29-
export const meta: MetaFunction = () => {
30-
const description = `Learn Remix and laugh at the same time!`;
31-
return {
32-
charset: "utf-8",
33-
description,
34-
keywords: "Remix,jokes",
35-
"twitter:image": "https://remix-jokes.lol/social.png",
36-
"twitter:card": "summary_large_image",
37-
"twitter:creator": "@remix_run",
38-
"twitter:site": "@remix_run",
39-
"twitter:title": "Remix Jokes",
40-
"twitter:description": description,
41-
};
31+
export const meta: V2_MetaFunction = () => {
32+
const description = "Learn Remix and laugh at the same time!";
33+
34+
return [
35+
{ name: "description", content: description },
36+
{ name: "twitter:description", content: description },
37+
{ title: "Remix: So great, it's funny!" },
38+
];
4239
};
4340

44-
function Document({
45-
children,
46-
title,
47-
}: {
48-
children: React.ReactNode;
49-
title?: string;
50-
}) {
41+
function Document({ children, title }: PropsWithChildren<{ title?: string }>) {
5142
return (
5243
<html lang="en">
5344
<head>
45+
<meta charSet="utf-8" />
46+
<meta name="viewport" content="width=device-width,initial-scale=1" />
47+
<meta name="keywords" content="Remix,jokes" />
48+
<meta
49+
name="twitter:image"
50+
content="https://remix-jokes.lol/social.png"
51+
/>
52+
<meta name="twitter:card" content="summary_large_image" />
53+
<meta name="twitter:creator" content="@remix_run" />
54+
<meta name="twitter:site" content="@remix_run" />
55+
<meta name="twitter:title" content="Remix Jokes" />
5456
<Meta />
5557
{title ? <title>{title}</title> : null}
5658
<Links />
@@ -65,36 +67,35 @@ function Document({
6567
}
6668

6769
export default function App() {
68-
// throw new Error("Not implemented");
6970
return (
7071
<Document>
7172
<Outlet />
7273
</Document>
7374
);
7475
}
7576

76-
export function CatchBoundary() {
77-
const caught = useCatch();
78-
79-
return (
80-
<Document title={`${caught.status} ${caught.statusText}`}>
81-
<div className="error-container">
82-
<h1>
83-
{caught.status} {caught.statusText}
84-
</h1>
85-
</div>
86-
</Document>
87-
);
88-
}
89-
90-
export function ErrorBoundary({ error }: { error: Error }) {
77+
export function ErrorBoundary() {
78+
const error = useRouteError();
9179
console.error(error);
9280

81+
if (isRouteErrorResponse(error)) {
82+
return (
83+
<Document title={`${error.status} ${error.statusText}`}>
84+
<div className="error-container">
85+
<h1>
86+
{error.status} {error.statusText}
87+
</h1>
88+
</div>
89+
</Document>
90+
);
91+
}
92+
93+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
9394
return (
9495
<Document title="Uh-oh!">
9596
<div className="error-container">
9697
<h1>App Error</h1>
97-
<pre>{error.message}</pre>
98+
<pre>{errorMessage}</pre>
9899
</div>
99100
</Document>
100101
);

_official-jokes/app/routes/index.tsx renamed to _official-jokes/app/routes/_index.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
import type { LinksFunction, MetaFunction } from "@remix-run/node";
1+
import type { LinksFunction } from "@remix-run/node";
22
import { Link } from "@remix-run/react";
33

4-
import stylesUrl from "../styles/index.css";
5-
6-
export const meta: MetaFunction = () => {
7-
return {
8-
title: "Remix: It's funny!",
9-
description: "Remix jokes app. Learn Remix and laugh at the same time!",
10-
};
11-
};
4+
import stylesUrl from "~/styles/index.css";
125

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

17-
export default function Index() {
10+
export default function IndexRoute() {
1811
return (
1912
<div className="container">
2013
<div className="content">
Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,43 @@
1-
import type { ActionArgs, LoaderArgs, MetaFunction } from "@remix-run/node";
1+
import type { ActionArgs, LoaderArgs, V2_MetaFunction } from "@remix-run/node";
22
import { json, redirect } from "@remix-run/node";
3-
import { useCatch, useLoaderData, useParams } from "@remix-run/react";
3+
import {
4+
isRouteErrorResponse,
5+
useLoaderData,
6+
useParams,
7+
useRouteError,
8+
} from "@remix-run/react";
49

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

9-
export const meta: MetaFunction<typeof loader> = ({ data }) => {
10-
if (!data) {
11-
return {
12-
title: "No joke",
13-
description: "No joke found",
14-
};
15-
}
16-
return {
17-
title: `"${data.joke.name}" joke`,
18-
description: `Enjoy the "${data.joke.name}" joke and much more`,
19-
};
14+
export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
15+
const { description, title } = data
16+
? {
17+
description: `Enjoy the "${data.joke.name}" joke and much more`,
18+
title: `"${data.joke.name}" joke`,
19+
}
20+
: { description: "No joke found", title: "No joke" };
21+
22+
return [
23+
{ name: "description", content: description },
24+
{ name: "twitter:description", content: description },
25+
{ title },
26+
];
2027
};
2128

2229
export const loader = async ({ params, request }: LoaderArgs) => {
2330
const userId = await getUserId(request);
24-
const joke = await db.joke.findUnique({ where: { id: params.jokeId } });
31+
const joke = await db.joke.findUnique({
32+
where: { id: params.jokeId },
33+
});
2534
if (!joke) {
2635
throw new Response("What a joke! Not found.", { status: 404 });
2736
}
28-
return json({ joke, isOwner: userId === joke.jokesterId });
37+
return json({
38+
isOwner: userId === joke.jokesterId,
39+
joke,
40+
});
2941
};
3042

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

57-
return <JokeDisplay joke={data.joke} isOwner={data.isOwner} />;
67+
return <JokeDisplay isOwner={data.isOwner} joke={data.joke} />;
5868
}
5969

60-
export function CatchBoundary() {
61-
const caught = useCatch();
62-
const params = useParams();
63-
switch (caught.status) {
64-
case 400: {
70+
export function ErrorBoundary() {
71+
const { jokeId } = useParams();
72+
const error = useRouteError();
73+
console.error(error);
74+
75+
if (isRouteErrorResponse(error)) {
76+
if (error.status === 400) {
6577
return (
6678
<div className="error-container">
6779
What you're trying to do is not allowed.
6880
</div>
6981
);
7082
}
71-
case 404: {
83+
if (error.status === 403) {
7284
return (
7385
<div className="error-container">
74-
Huh? What the heck is {params.jokeId}?
86+
Sorry, but "{jokeId}" is not your joke.
7587
</div>
7688
);
7789
}
78-
case 403: {
90+
if (error.status === 404) {
7991
return (
80-
<div className="error-container">
81-
Sorry, but {params.jokeId} is not your joke.
82-
</div>
92+
<div className="error-container">Huh? What the heck is "{jokeId}"?</div>
8393
);
8494
}
85-
default: {
86-
throw new Error(`Unhandled error: ${caught.status}`);
87-
}
8895
}
89-
}
90-
91-
export function ErrorBoundary({ error }: { error: Error }) {
92-
console.error(error);
9396

94-
const { jokeId } = useParams();
9597
return (
9698
<div className="error-container">
97-
There was an error loading joke by the id {jokeId}. Sorry.
99+
There was an error loading joke by the id ${jokeId}. Sorry.
98100
</div>
99101
);
100102
}

0 commit comments

Comments
 (0)