Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(website): automatically publish latest blog posts to website #13175

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ jobs:
uses: ./.github/actions/install-pnpm-dependencies

- name: Build Website
run: pnpm -F website build
run: pnpm -F website build
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ library LibBlockHeaderDecoder {
/// @return _timestamp The timestamp
/// @return _transactionsRoot The transactionsRoot
/// @return _receiptsRoot The receiptsRoot
function decodeBlockHeader(
function decodeBlockHeader(
bytes calldata blockHeader,
bytes32 blockHash,
bool postEIP1559
Expand Down Expand Up @@ -230,5 +230,4 @@ library LibBlockHeaderDecoder {
_receiptsRoot, len := loadValue(add(memStart, mul(32, 5)))
// sstore(originReceiptsRoot.slot, value)
}
}
}
2 changes: 1 addition & 1 deletion packages/website/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
.next
node_modules
node_modules
188 changes: 116 additions & 72 deletions packages/website/components/BlogSection.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,95 @@
const posts = [
{
title: "Taiko Ambassador Program",
href: "https://mirror.xyz/labs.taiko.eth/BvcEyYeVIiHnjc-i5qf3zR4s67Jc6nz_R6OSGj5rzOE",
description:
"Ethereum has come a long way in its seven-year life — changing the world, in our opinion — but it is only just getting started.",
date: "Jan 04, 2023",
datetime: "2023-01-04",
imageUrl:
"https://mirror-media.imgix.net/publication-images/5Ed-TXJIB3LTC2HJdPuEN.png?height=512&width=1024&h=512&w=1024&auto=compress",
readingTime: "2 min",
author: {
name: "finestone",
imageUrl: "https://avatars.githubusercontent.com/u/36642873?v=4",
},
},
{
title: "Taiko Alpha-1 Testnet is Live",
href: "https://mirror.xyz/labs.taiko.eth/-lahy4KbGkeAcqhs0ETG3Up3oTVzZ0wLoE1eK_ao5h4",
description:
"Today, the Taiko Alpha-1 testnet (a1) is live - our first public testnet! We’ve codenamed this testnet, Snæfellsjökull.",
date: "Dec 27, 2022",
datetime: "2022-12-27",
imageUrl:
"https://mirror-media.imgix.net/publication-images/4qVW-dWhNmMQr61g91hGt.png?height=512&width=1024&h=512&w=1024&auto=compress",
readingTime: "4 min",
author: {
name: "finestone",
imageUrl: "https://avatars.githubusercontent.com/u/36642873?v=4",
},
},
{
title: "Rollup Decentralization",
href: "https://mirror.xyz/labs.taiko.eth/sxR3iKyD-GvTuyI9moCg4_ggDI4E4CqnvhdwRq5yL0A",
description:
"This post explores definitions and high-level ideas of rollup decentralization. It does not cover deep technical detail about decentralizing rollup implementations.",
date: "Dec 20, 2022",
datetime: "2022-12-20",
imageUrl:
"https://mirror-media.imgix.net/publication-images/NTeYUqYqHo4NqrRGJHvfO.png?height=512&width=1024&h=512&w=1024&auto=compress",
readingTime: "9 min",
author: {
name: "finestone",
imageUrl: "https://avatars.githubusercontent.com/u/36642873?v=4",
},
},
];
import React, { useEffect, useState } from "react";

interface Content {
body: string;
timestamp: number;
title: string;
}

interface Authorship {
contributor: string;
signingKey: {
crv: string;
ext: boolean;
key_ops: string[];
kty: string;
x: string;
y: string;
};
signature: string;
signingKeySignature: string;
signingKeyMessage: string;
algorithm: {
name: string;
hash: string;
};
}

interface Wnft {
chainId: number;
description: string;
fee: number;
fundingRecipient: string;
imageURI: string;
mediaAssetId: number;
name: string;
nonce: number;
owner: string;
price: number;
proxyAddress: string;
renderer: string;
supply: number;
symbol: string;
}

interface Post {
OriginalDigest: string;
content: Content;
authorship: Authorship;
digest: string;
version: string;
wnft: Wnft;
}

function getReadingTime(text) {
const wordsPerMinute = 200;
const wordCount = text.split(" ").length;
const readingTime = Math.round(wordCount / wordsPerMinute);
return readingTime;
}

function getDate(timestamp: string): string {
let date = new Date(Number(timestamp) * 1000);
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
}

function getDateTime(timestamp: string): string {
let date = new Date(parseInt(timestamp) * 1000);
return `${date.getFullYear()}-${(date.getMonth() + 1)
.toString()
.padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
}

export default function BlogSection(): JSX.Element {
const [posts, setPosts] = useState<Post[]>([]);
if (posts.length < 3) {
fetch("/api/getPosts")
.then((response) => response.json())
.then((json) => {
json = json.sort((a, b) => b.content.timestamp - a.content.timestamp);
json = json.slice(0, 3);
setPosts(json);
});
}

export default function BlogSection() {
return (
<div className="relative bg-neutral-50 px-4 pt-16 pb-20 sm:px-6 lg:px-8 lg:pt-24 lg:pb-28 dark:bg-neutral-900">
<div className="relative bg-neutral-50 px-4 pt-16 pb-20 sm:px-6 lg:px-8 lg:pt-24 lg:pb-28 dark:bg-neutral-800">
<div className="absolute inset-0">
<div className="h-1/3 bg-white sm:h-2/3 dark:bg-neutral-900" />
<div className="h-1/3 bg-white sm:h-2/3 dark:bg-[#1B1B1D]" />
</div>
<div className="relative mx-auto max-w-7xl">
<div className="text-center">
Expand All @@ -64,49 +103,54 @@ export default function BlogSection() {
</a>
</div>
</div>

<div className="mx-auto mt-12 grid max-w-lg gap-5 lg:max-w-none lg:grid-cols-3">
{posts.map((post) => (
{posts.map((post: Post) => (
<div
key={post.title}
key={post.content.title}
className="flex flex-col overflow-hidden rounded-lg shadow-lg"
>
<div className="flex-shrink-0">
<a href={post.href} target="_blank">
<a
href={
"https://mirror.xyz/labs.taiko.eth/" + post.OriginalDigest
}
target="_blank"
>
<img
className="h-54 w-full object-cover"
src={post.imageUrl}
className="w-full h-40 sm:h-64 lg:h-36 xl:h-48 object-cover object-top"
src={`https://ipfs.io/ipfs/${post.wnft.imageURI}`}
alt=""
/>
</a>
</div>
<div className="flex flex-1 flex-col justify-between bg-white p-6 dark:bg-neutral-800">
<div className="flex-1">
<a href={post.href} target="_blank" className="mt-2 block">
<a
href={
"https://mirror.xyz/labs.taiko.eth/" + post.OriginalDigest
}
target="_blank"
className="mt-2 block"
>
<div className="text-xl font-semibold text-neutral-900 dark:text-neutral-200">
{post.title}
{post.content.title}
</div>
<div className="mt-3 text-base text-neutral-500 dark:text-neutral-300">
{post.description}
{post.wnft.description}
</div>
</a>
</div>
<div className="mt-6 flex items-center">
<div className="flex-shrink-0">
<span className="sr-only">{post.author.name}</span>
<img
className="h-10 w-10 rounded-full"
src={post.author.imageUrl}
alt=""
/>
</div>
<div className="ml-3">
<div className="text-sm font-medium text-[#fc0fc0]">
{post.author.name}
</div>
<div className="flex space-x-1 text-sm text-neutral-500 dark:text-neutral-400">
<time dateTime={post.datetime}>{post.date}</time>
<time dateTime={getDateTime(`${post.content.timestamp}`)}>
{getDate(`${post.content.timestamp}`)}
</time>
<span aria-hidden="true">&middot;</span>
<span>{post.readingTime} read</span>
<span>
{getReadingTime(post.content.body) + " min read"}
</span>
</div>
</div>
</div>
Expand All @@ -117,4 +161,4 @@ export default function BlogSection() {
</div>
</div>
);
}
}
1 change: 1 addition & 0 deletions packages/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"start": "pnpm next start"
},
"dependencies": {
"arweave": "^1.12.6",
"next": "^13.1.6",
"next-themes": "^0.2.1",
"nextra": "^2.2.14",
Expand Down
81 changes: 81 additions & 0 deletions packages/website/pages/api/getPosts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const Arweave = require("arweave");

const arweave = Arweave.init({
host: "arweave.net",
port: 443,
protocol: "https",
});

let posts = []
const hoursBetweenBlogPostFetches = 1;

const getTransactionIds = async () => {
const response = await fetch("https://arweave.net/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
query {
transactions(
first: 100
sort: HEIGHT_DESC
tags: [
{
name: "Contributor"
values: ["0x5b796c4B197B6DfD413f177059C27963EB80af0F","0x2b1F13149C7F89622BBfB46Ae1e3ECc573Bb9331","0x381636D0E4eD0fa6aCF07D8fd821909Fb63c0d10"]
},
{
name: "App-Name"
values: "MirrorXYZ"
}
]
) {
edges {
node {
id
tags {
name
value
}
}
}
}
}
`,
}),
});

const responseJSON = await response.json();
posts = await getPosts(responseJSON);
};

async function getPosts(response) {
const newPosts = [];
const promises = response.data.transactions.edges.map(async (edge) => {
const transactionId = edge.node.id;

const response = await arweave.transactions.getData(`${transactionId}`, {
decode: true,
string: true,
});
const data = JSON.parse(response);
// Check if the posts have the required keys
if (data.hasOwnProperty("wnft")) {
// add the original digest
data["OriginalDigest"] = edge.node.tags[4].value;
newPosts.push(data);
}
});

await Promise.all(promises);
return newPosts;
}

getTransactionIds();
setInterval(getTransactionIds, hoursBetweenBlogPostFetches * 3600000);

export default (req, res) => {
res.json(posts);
};
2 changes: 1 addition & 1 deletion packages/website/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/website/styles.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind utilities;
Loading