Skip to content

Commit

Permalink
refactor: use async route components (denoland#424)
Browse files Browse the repository at this point in the history
  • Loading branch information
iuioiua authored Aug 15, 2023
1 parent f62654b commit aca43dd
Show file tree
Hide file tree
Showing 28 changed files with 387 additions and 539 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"imports": {
"@/": "./",
"$fresh/": "https://deno.land/x/fresh@1.3.0/",
"$fresh/": "https://raw.githubusercontent.com/denoland/fresh/726740ed39aa0c5a9c484f4eba4f6e329cf730d1/",
"$gfm": "https://deno.land/x/[email protected]/mod.ts",
"preact": "https://esm.sh/[email protected]",
"preact/": "https://esm.sh/[email protected]/",
Expand Down
108 changes: 54 additions & 54 deletions e2e_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,147 +14,147 @@ Deno.test("[http]", async (test) => {
const handler = await createHandler(manifest);

await test.step("GET /", async () => {
const response = await handler(new Request("http://localhost"));
const resp = await handler(new Request("http://localhost"));

assert(response.ok);
assertInstanceOf(response.body, ReadableStream);
assert(resp.ok);
assertInstanceOf(resp.body, ReadableStream);
assertEquals(
response.headers.get("content-type"),
resp.headers.get("content-type"),
"text/html; charset=utf-8",
);
assertEquals(response.status, 200);
assertEquals(resp.status, 200);
});

await test.step("GET /account", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/account"),
);

assertFalse(response.ok);
assertFalse(response.body);
assertFalse(resp.ok);
assertFalse(resp.body);
assertEquals(
response.headers.get("location"),
resp.headers.get("location"),
"/signin?from=http://localhost/account",
);
assertEquals(response.status, 303);
assertEquals(resp.status, 303);
});

await test.step("GET /callback", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/callback"),
);

assertFalse(response.ok);
assertInstanceOf(response.body, ReadableStream);
assertFalse(resp.ok);
assertInstanceOf(resp.body, ReadableStream);
assertEquals(
response.headers.get("content-type"),
resp.headers.get("content-type"),
"text/html; charset=utf-8",
);
assertEquals(response.status, 500);
assertEquals(resp.status, 500);
});

await test.step("GET /blog", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/blog"),
);

assert(response.ok);
assertInstanceOf(response.body, ReadableStream);
assert(resp.ok);
assertInstanceOf(resp.body, ReadableStream);
assertEquals(
response.headers.get("content-type"),
resp.headers.get("content-type"),
"text/html; charset=utf-8",
);
assertEquals(response.status, 200);
assertEquals(resp.status, 200);
});

await test.step("GET /pricing", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/pricing"),
);

assertFalse(response.ok);
assertInstanceOf(response.body, ReadableStream);
assertFalse(resp.ok);
assertInstanceOf(resp.body, ReadableStream);
assertEquals(
response.headers.get("content-type"),
resp.headers.get("content-type"),
"text/html; charset=utf-8",
);
assertEquals(response.status, 404);
assertEquals(resp.status, 404);
});

await test.step("GET /signin", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/signin"),
);

assertFalse(response.ok);
assertFalse(response.body);
assertFalse(resp.ok);
assertFalse(resp.body);
assertStringIncludes(
response.headers.get("location")!,
resp.headers.get("location")!,
"https://github.com/login/oauth/authorize",
);
assertEquals(response.status, 302);
assertEquals(resp.status, 302);
});

await test.step("GET /signout", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/signout"),
);

assertFalse(response.ok);
assertFalse(response.body);
assertEquals(response.headers.get("location"), "/");
assertEquals(response.status, 302);
assertFalse(resp.ok);
assertFalse(resp.body);
assertEquals(resp.headers.get("location"), "/");
assertEquals(resp.status, 302);
});

await test.step("GET /dashboard", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/dashboard"),
);

assertFalse(response.ok);
assertFalse(response.body);
assertFalse(resp.ok);
assertFalse(resp.body);
assertEquals(
response.headers.get("location"),
resp.headers.get("location"),
"/signin?from=http://localhost/dashboard",
);
assertEquals(response.status, 303);
assertEquals(resp.status, 303);
});

await test.step("GET /submit", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/submit"),
);

assertFalse(response.ok);
assertFalse(response.body);
assertFalse(resp.ok);
assertFalse(resp.body);
assertEquals(
response.headers.get("location"),
resp.headers.get("location"),
"/signin?from=http://localhost/submit",
);
assertEquals(response.status, 303);
assertEquals(resp.status, 303);
});

await test.step("POST /submit", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/submit", { method: "POST" }),
);

assertFalse(response.ok);
assertFalse(response.body);
assertEquals(response.status, 401);
assertFalse(resp.ok);
assertFalse(resp.body);
assertEquals(resp.status, 303);
});

await test.step("GET /feed", async () => {
const response = await handler(
const resp = await handler(
new Request("http://localhost/feed"),
);

assert(response.ok);
assertInstanceOf(response.body, ReadableStream);
assert(resp.ok);
assertInstanceOf(resp.body, ReadableStream);
assertEquals(
response.headers.get("content-type"),
resp.headers.get("content-type"),
"application/atom+xml; charset=utf-8",
);
assertEquals(response.status, 200);
assertEquals(resp.status, 200);
});
});
12 changes: 7 additions & 5 deletions fresh.gen.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// DO NOT EDIT. This file is generated by fresh.
// DO NOT EDIT. This file is generated by Fresh.
// This file SHOULD be checked into source version control.
// This file is automatically updated during development when running `dev.ts`.

Expand Down Expand Up @@ -28,8 +28,9 @@ import * as $22 from "./routes/notifications/index.tsx";
import * as $23 from "./routes/pricing.tsx";
import * as $24 from "./routes/signin.ts";
import * as $25 from "./routes/signout.ts";
import * as $26 from "./routes/submit.tsx";
import * as $27 from "./routes/users/[login].tsx";
import * as $26 from "./routes/submit/_middleware.tsx";
import * as $27 from "./routes/submit/index.tsx";
import * as $28 from "./routes/users/[login].tsx";
import * as $$0 from "./islands/Chart.tsx";
import * as $$1 from "./islands/PageInput.tsx";
import * as $$2 from "./islands/VoteButton.tsx";
Expand Down Expand Up @@ -62,8 +63,9 @@ const manifest = {
"./routes/pricing.tsx": $23,
"./routes/signin.ts": $24,
"./routes/signout.ts": $25,
"./routes/submit.tsx": $26,
"./routes/users/[login].tsx": $27,
"./routes/submit/_middleware.tsx": $26,
"./routes/submit/index.tsx": $27,
"./routes/users/[login].tsx": $28,
},
islands: {
"./islands/Chart.tsx": $$0,
Expand Down
4 changes: 2 additions & 2 deletions islands/VoteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export default function VoteButton(props: VoteButtonProps) {
if (event.detail === 1) {
const url = `/api/vote?item_id=${props.item.id}`;
const method = isVoted.value ? "DELETE" : "POST";
const response = await fetch(url, { method, credentials: "same-origin" });
const resp = await fetch(url, { method, credentials: "same-origin" });

if (response.status === 401) {
if (resp.status === 401) {
window.location.href = "/signin";
return;
}
Expand Down
7 changes: 4 additions & 3 deletions routes/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
import { AppProps } from "$fresh/server.ts";
import Header from "@/components/Header.tsx";
import Footer from "@/components/Footer.tsx";
import type { State } from "./_middleware.ts";

export default function App(props: AppProps) {
export default function App(props: AppProps<undefined, State>) {
return (
<div class="dark:bg-gray-900">
<div class="flex flex-col min-h-screen mx-auto max-w-7xl w-full dark:text-white">
<Header
url={props.url}
sessionId={props.data?.sessionId}
hasNotifications={props.data?.hasNotifications}
sessionId={props.state?.sessionId}
hasNotifications={props.state?.hasNotifications}
/>
<props.Component />
<Footer url={props.url} />
Expand Down
4 changes: 2 additions & 2 deletions routes/_middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { MetaProps } from "@/components/Meta.tsx";

export interface State extends MetaProps {
sessionId?: string;
hasNotifications?: boolean;
hasNotifications: boolean;
}

async function redirectToNewOrigin(
Expand All @@ -27,7 +27,7 @@ async function setState(req: Request, ctx: MiddlewareHandlerContext<State>) {

const sessionId = await getSessionId(req);
ctx.state.sessionId = sessionId;

ctx.state.hasNotifications = false;
if (sessionId) {
const user = await getUserBySession(sessionId);
ctx.state.hasNotifications = await ifUserHasNotifications(user!.login);
Expand Down
35 changes: 15 additions & 20 deletions routes/account/index.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import type { Handlers, PageProps } from "$fresh/server.ts";
import type { RouteContext } from "$fresh/server.ts";
import type { SignedInState } from "@/utils/middleware.ts";
import { BUTTON_STYLES } from "@/utils/constants.ts";
import { ComponentChild } from "preact";
import { stripe } from "@/utils/payments.ts";
import Head from "@/components/Head.tsx";
import GitHubAvatarImg from "@/components/GitHubAvatarImg.tsx";

export const handler: Handlers<SignedInState, SignedInState> = {
GET(_request, ctx) {
return ctx.render(ctx.state);
},
};

interface RowProps {
title: string;
children?: ComponentChild;
text: string;
}

function Row(props: RowProps) {
function Row(
props: { title: string; children?: ComponentChild; text: string },
) {
return (
<li class="py-4">
<div class="flex flex-wrap justify-between">
Expand All @@ -35,26 +25,31 @@ function Row(props: RowProps) {
);
}

export default function AccountPage(props: PageProps<SignedInState>) {
const action = props.data.user.isSubscribed ? "Manage" : "Upgrade";
// deno-lint-ignore require-await
export default async function AccountPage(
_req: Request,
ctx: RouteContext<undefined, SignedInState>,
) {
const { user } = ctx.state;
const action = user.isSubscribed ? "Manage" : "Upgrade";

return (
<>
<Head title="Account" href={props.url.href} />
<Head title="Account" href={ctx.url.href} />
<main class="max-w-lg m-auto w-full flex-1 p-4 flex flex-col justify-center">
<GitHubAvatarImg
login={props.data.user.login}
login={user.login}
size={240}
class="m-auto"
/>
<ul>
<Row
title="Username"
text={props.data.user.login}
text={user.login}
/>
<Row
title="Subscription"
text={props.data.user.isSubscribed ? "Premium 🦕" : "Free"}
text={user.isSubscribed ? "Premium 🦕" : "Free"}
>
{stripe && (
<a
Expand Down
28 changes: 14 additions & 14 deletions routes/account/manage.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import type { Handlers } from "$fresh/server.ts";
import type { RouteContext } from "$fresh/server.ts";
import { stripe } from "@/utils/payments.ts";
import type { SignedInState } from "@/utils/middleware.ts";
import { redirect } from "@/utils/redirect.ts";

// deno-lint-ignore no-explicit-any
export const handler: Handlers<any, SignedInState> = {
async GET(req, ctx) {
if (stripe === undefined || ctx.state.user.stripeCustomerId === undefined) {
return ctx.renderNotFound();
}
export default async function AccountManagePage(
_req: Request,
ctx: RouteContext<undefined, SignedInState>,
) {
if (stripe === undefined || ctx.state.user.stripeCustomerId === undefined) {
return await ctx.renderNotFound();
}

const { url } = await stripe.billingPortal.sessions.create({
customer: ctx.state.user.stripeCustomerId,
return_url: new URL(req.url).origin + "/account",
});
const { url } = await stripe.billingPortal.sessions.create({
customer: ctx.state.user.stripeCustomerId,
return_url: ctx.url.origin + "/account",
});

return redirect(url);
},
};
return redirect(url);
}
Loading

0 comments on commit aca43dd

Please sign in to comment.