Skip to content

Commit 9dd1828

Browse files
authored
feat(blog): move blog into app (tone-row#417)
* feat(blog): move blog into app * chore(actions): update to latest actions * chore(blog): remove old blog * chore(research): link research to notion * chore(i18n): add only in english warning * chore(repo): remove shared package
1 parent 1dc991a commit 9dd1828

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1363
-3230
lines changed

.github/workflows/benchmark.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ jobs:
55
name: Benchmark
66
runs-on: ubuntu-latest
77
steps:
8-
- uses: actions/checkout@v2
9-
- uses: actions/setup-node@v1
10-
with:
11-
node-version: 16.x
12-
- uses: pnpm/[email protected]
8+
- uses: actions/checkout@v3
9+
- uses: actions/setup-node@v3
10+
- uses: pnpm/[email protected]
1311
with:
1412
version: 7
1513
- name: Install Deps

.github/workflows/e2e.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
URL=https://$(echo $CLEAN | jq --arg h "$CURRENT_HASH" '.deployments[] | select(.meta.githubCommitSha==$h) | .url')
2626
echo ::set-output name=preview_url::$(echo $URL)
2727
- name: Checkout Code
28-
uses: actions/checkout@v2
28+
uses: actions/checkout@v3
2929
- name: Add env
3030
run: |
3131
touch app/.env
@@ -34,10 +34,8 @@ jobs:
3434
echo SUPABASE_TEST_URL=${{ secrets.SUPABASE_TEST_URL }} >> app/.env
3535
echo SUPABASE_TEST_ANON_KEY=${{ secrets.SUPABASE_TEST_ANON_KEY }} >> app/.env
3636
- name: Setup Node
37-
uses: actions/setup-node@v1
38-
with:
39-
node-version: 16.x
40-
- uses: pnpm/[email protected]
37+
uses: actions/setup-node@v3
38+
- uses: pnpm/[email protected]
4139
with:
4240
version: 7
4341
- name: Install Playwright

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
steps:
1111
- name: Checkout repository
12-
uses: actions/checkout@v2
12+
uses: actions/checkout@v3
1313
with:
1414
fetch-depth: 0
1515
- name: Automatic GitHub Release

.github/workflows/test.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ jobs:
55
name: Test
66
runs-on: ubuntu-latest
77
steps:
8-
- uses: actions/checkout@v2
9-
- uses: actions/setup-node@v1
10-
with:
11-
node-version: 16.x
12-
- uses: pnpm/[email protected]
8+
- uses: actions/checkout@v3
9+
- uses: actions/setup-node@v3
10+
- uses: pnpm/[email protected]
1311
name: Install pnpm
1412
id: pnpm-install
1513
with:

api/.eslintrc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"plugins": ["@typescript-eslint"],
5+
"extends": [
6+
"eslint:recommended",
7+
"plugin:@typescript-eslint/eslint-recommended",
8+
"plugin:@typescript-eslint/recommended"
9+
],
10+
/* ignore javascript files */
11+
"ignorePatterns": ["*.js"],
12+
"rules": {
13+
"@typescript-eslint/no-explicity-any": ["off"],
14+
"@typescript-eslint/no-var-requires": ["off"]
15+
}
16+
}
File renamed without changes.
File renamed without changes.

blog/src/components/niceData.ts renamed to api/_lib/_dates.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ export function niceDate(d: string) {
77
export function dateString(d: string) {
88
return format(parse(d, "yyyy-MM-dd", new Date()), "yyyy-MM-dd");
99
}
10+
export function dateAsNumber(d: string) {
11+
return parseInt(format(parse(d, "yyyy-MM-dd", new Date()), "yyyyMMdd"), 10);
12+
}
1013

1114
export function niceDateIso(d: string) {
1215
return format(parseISO(d), "LLLL d, yyyy");
1316
}
14-
15-
export function dateAsNumber(d: string) {
16-
return parseInt(format(parse(d, "yyyy-MM-dd", new Date()), "yyyyMMdd"), 10);
17-
}

api/_lib/_linear.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { marked } from "marked";
2+
3+
import { IssueConnection, LinearClient } from "@linear/sdk";
4+
5+
const linear = new LinearClient({
6+
apiKey: process.env.LINEAR_API_KEY,
7+
});
8+
9+
export async function getIssues() {
10+
let initialIssues: IssueConnection["nodes"];
11+
if (process.env.NODE_ENV === "development") {
12+
initialIssues = (require("../_fixtures/issues.json") as IssueConnection)
13+
.nodes;
14+
} else {
15+
initialIssues = await getLinearIssues();
16+
}
17+
18+
const issues = initialIssues.map((issue) => {
19+
return {
20+
title: issue.title,
21+
description: marked.parse(issue.description ?? ""),
22+
};
23+
});
24+
25+
return issues;
26+
}
27+
28+
async function getLinearIssues() {
29+
const ff = await linear.team("FF");
30+
const issues = await ff.issues({
31+
first: 10,
32+
filter: {
33+
// priority: { lte: 2, neq: 0 }
34+
state: { name: { eq: "Todo" } },
35+
},
36+
});
37+
return issues.nodes;
38+
}

api/_lib/_notion.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Client } from "@notionhq/client";
2+
import { NotionToMarkdown } from "notion-to-md";
3+
import { QueryDatabaseResponse } from "@notionhq/client/build/src/api-endpoints";
4+
import hljs from "highlight.js";
5+
import { marked } from "marked";
6+
7+
const auth = process.env.NOTION_ACCESS_TOKEN;
8+
if (!auth) throw new Error("NOTION_ACCESS_TOKEN is not defined");
9+
10+
// Initializing a client
11+
export const notion = new Client({
12+
auth,
13+
});
14+
15+
export const n2m = new NotionToMarkdown({ notionClient: notion });
16+
17+
type Properties = Extract<
18+
QueryDatabaseResponse["results"][number],
19+
{ properties: unknown }
20+
>["properties"];
21+
22+
/**
23+
* Returns the nested properties of a Notion page on an object using the same keys
24+
*/
25+
export function getNestedProperties(properties: Properties) {
26+
const props: Record<string, string> = {};
27+
for (const key in properties) {
28+
const property = properties[key];
29+
if ("title" in property) {
30+
props[key] = property.title[0].plain_text;
31+
} else if ("rich_text" in property) {
32+
props[key] = property.rich_text[0].plain_text;
33+
} else if ("date" in property) {
34+
const pDate = property.date;
35+
if (pDate) {
36+
props[key] = pDate.start;
37+
}
38+
} else if ("status" in property) {
39+
const status = property["status"] as { name: string };
40+
if (status) {
41+
props[key] = status.name;
42+
}
43+
} else {
44+
console.log("Unknown property", key, property);
45+
}
46+
}
47+
return props;
48+
}
49+
50+
marked.setOptions({
51+
highlight: function (code, lang) {
52+
const language = hljs.getLanguage(lang) ? lang : "plaintext";
53+
return hljs.highlight(code, { language }).value;
54+
},
55+
langPrefix: "hljs language-",
56+
});
57+
58+
export async function getPostHtmlFromId(id: string) {
59+
const mdblocks = await n2m.pageToMarkdown(id);
60+
const mdString = n2m.toMarkdownString(mdblocks);
61+
const html = marked(mdString);
62+
return html;
63+
}
64+
65+
// tonerow/Flowchart-Fun-Areas-of-Research-6797865c1d744aa0babbea7d1558e8b3
66+
const AREAS_OF_RESEARCH_PAGE_ID = "6797865c1d744aa0babbea7d1558e8b3";
67+
export async function getAreasOfResearchHtml() {
68+
return await getPostHtmlFromId(AREAS_OF_RESEARCH_PAGE_ID);
69+
}

api/_lib/_releases.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Octokit } from "@octokit/core";
2+
import { marked } from "marked";
3+
import { niceDateIso } from "./_dates";
4+
5+
export async function getReleases() {
6+
let initialReleases = [];
7+
8+
if (process.env.NODE_ENV === "development") {
9+
initialReleases = require("../_fixtures/releases.json") as Awaited<
10+
ReturnType<typeof githubReleases>
11+
>;
12+
} else {
13+
initialReleases = await githubReleases();
14+
}
15+
16+
if (!initialReleases) return [];
17+
18+
const releases = initialReleases.map((release) => ({
19+
id: release.id,
20+
name: release.name,
21+
date: release.published_at,
22+
niceDate: niceDateIso(release.published_at ?? ""),
23+
body: marked.parse(release.body ?? ""),
24+
url: release.html_url,
25+
}));
26+
27+
return releases;
28+
}
29+
30+
async function githubReleases() {
31+
const octokit = new Octokit({
32+
auth: process.env.GITHUB_PERSONAL_ACCESS_TOKEN,
33+
});
34+
35+
const response = await octokit.request("GET /repos/{owner}/{repo}/releases", {
36+
owner: "tone-row",
37+
repo: "flowchart-fun",
38+
});
39+
40+
return response.data;
41+
}

api/blog/post.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { VercelRequest, VercelResponse } from "@vercel/node";
2+
import { niceDate } from "../_lib/_dates";
3+
import {
4+
notion,
5+
getNestedProperties,
6+
getPostHtmlFromId,
7+
} from "../_lib/_notion";
8+
9+
export default async function handler(req: VercelRequest, res: VercelResponse) {
10+
const slug = req.query.slug;
11+
if (!slug || typeof slug != "string") throw new Error("No slug");
12+
13+
// get notion post form column slug
14+
const response = await notion.databases.query({
15+
database_id: "b7a09b10aa83485b94092269239a8b38",
16+
filter: {
17+
property: "slug",
18+
text: {
19+
equals: slug,
20+
},
21+
},
22+
});
23+
24+
if (!response.results.length) throw new Error("No results");
25+
26+
const post = response.results[0];
27+
28+
if (!("properties" in post)) throw new Error("No properties");
29+
const { properties = {}, id } = post;
30+
const { date, ...props } = getNestedProperties(properties);
31+
let publishDate = niceDate(date);
32+
33+
const htmlContent = await getPostHtmlFromId(id);
34+
35+
res.json({ id, htmlContent, publishDate, ...props });
36+
}

api/blog/posts.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { VercelRequest, VercelResponse } from "@vercel/node";
2+
import { dateAsNumber, dateString, niceDate } from "../_lib/_dates";
3+
import { notion, getNestedProperties } from "../_lib/_notion";
4+
5+
export default async function handler(req: VercelRequest, res: VercelResponse) {
6+
const response = await notion.databases.query({
7+
database_id: "b7a09b10aa83485b94092269239a8b38",
8+
});
9+
const posts = response.results.map((page) => {
10+
if (!("properties" in page)) throw new Error("No properties");
11+
const { properties = {}, id } = page;
12+
const { date, ...props } = getNestedProperties(properties);
13+
if (!("title" in props)) throw new Error("No title");
14+
if (!("status" in props)) throw new Error("No status");
15+
if (!("slug" in props)) throw new Error("No slug");
16+
if (!("description" in props)) throw new Error("No description");
17+
18+
if (!date) throw new Error("No date");
19+
20+
let sanitizedDate = dateString(date);
21+
let publishDate = niceDate(date);
22+
let rawDate = dateAsNumber(date);
23+
24+
return { id, rawDate, date: sanitizedDate, publishDate, ...props };
25+
});
26+
27+
res.json(posts);
28+
}

api/changelog.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { VercelRequest, VercelResponse } from "@vercel/node";
2+
import { getReleases } from "./_lib/_releases";
3+
4+
export default async function handler(req: VercelRequest, res: VercelResponse) {
5+
const releases = await getReleases();
6+
res.json(releases);
7+
}

api/package.json

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,32 @@
44
"description": "",
55
"main": "index.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"check": "tsc --noEmit"
89
},
910
"keywords": [],
1011
"author": "",
1112
"license": "ISC",
1213
"dependencies": {
13-
"@notionhq/client": "^0.4.11",
14+
"@linear/sdk": "^2.0.0",
15+
"@notionhq/client": "^0.4.13",
16+
"@octokit/core": "^4.2.0",
1417
"@sendgrid/mail": "^7.4.6",
1518
"@supabase/supabase-js": "^2",
19+
"@vercel/node": "^2.8.15",
20+
"date-fns": "^2.29.3",
21+
"highlight.js": "^11.7.0",
22+
"marked": "^4.1.1",
1623
"moniker": "^0.1.2",
24+
"notion-to-md": "^2.5.5",
1725
"stripe": "^8.222.0"
26+
},
27+
"devDependencies": {
28+
"@types/marked": "^4.0.7",
29+
"@types/node": "^12.20.55",
30+
"@typescript-eslint/eslint-plugin": "^5",
31+
"@typescript-eslint/parser": "^5",
32+
"eslint": "^8.3.0",
33+
"typescript": "^4.8.4"
1834
}
1935
}

api/roadmap.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { VercelRequest, VercelResponse } from "@vercel/node";
2+
import { getIssues } from "./_lib/_linear";
3+
import { getAreasOfResearchHtml } from "./_lib/_notion";
4+
5+
export default async function handler(req: VercelRequest, res: VercelResponse) {
6+
const [issues, areasOfResearch] = await Promise.all([
7+
getIssues(),
8+
getAreasOfResearchHtml(),
9+
]);
10+
res.status(200).json({ issues, areasOfResearch });
11+
}

0 commit comments

Comments
 (0)