From 7e3836ad34a7d81de875b627a51e2f2cd51599cd Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Sat, 11 May 2024 00:58:30 +0200
Subject: [PATCH 1/8] Add blog section initial commit
---
packages/backend/index.js | 2 +
packages/backend/routes/blog.js | 36 +++++++++++++
packages/react-app/components/BlogSection.jsx | 51 +++++++++++++++++++
packages/react-app/data/api/blog.js | 12 +++++
packages/react-app/pages/index.jsx | 7 ++-
5 files changed, 107 insertions(+), 1 deletion(-)
create mode 100644 packages/backend/routes/blog.js
create mode 100644 packages/react-app/components/BlogSection.jsx
create mode 100644 packages/react-app/data/api/blog.js
diff --git a/packages/backend/index.js b/packages/backend/index.js
index 3ac69c90..bd627ebb 100644
--- a/packages/backend/index.js
+++ b/packages/backend/index.js
@@ -14,6 +14,7 @@ const ensRoutes = require("./routes/ens");
const apiRoutes = require("./routes/api");
const cohortRoutes = require("./routes/cohorts");
const notificationsRoutes = require("./routes/notifications");
+const blogRoutes = require("./routes/blog");
const app = express();
@@ -31,6 +32,7 @@ app.use("/ens", ensRoutes);
app.use("/api", apiRoutes);
app.use("/cohorts", cohortRoutes);
app.use("/notifications", notificationsRoutes);
+app.use("/blog", blogRoutes);
app.get("/sign-message", async (req, res) => {
const messageId = req.query.messageId;
diff --git a/packages/backend/routes/blog.js b/packages/backend/routes/blog.js
new file mode 100644
index 00000000..ed15aa61
--- /dev/null
+++ b/packages/backend/routes/blog.js
@@ -0,0 +1,36 @@
+const express = require("express");
+const axios = require("axios");
+const xml2js = require("xml2js");
+const cors = require('cors');
+
+const router = express.Router();
+
+router.get("/posts", cors(), async (req, res) => {
+ try {
+ const response = await axios.get("https://buidlguidl.substack.com/feed");
+ const xml = response.data;
+
+ const parser = new xml2js.Parser();
+ parser.parseString(xml, (err, result) => {
+ if (err) {
+ console.error("Failed to parse blog feed:", err);
+ res.status(500).json({ error: "Failed to parse blog feed" });
+ } else {
+ const posts = result.rss.channel[0].item.map(item => ({
+ title: item.title[0],
+ description: item.description[0],
+ link: item.link[0],
+ pubDate: item.pubDate[0],
+ imageUrl: item.enclosure ? item.enclosure[0].$.url : null
+ }));
+
+ res.status(200).json(posts.slice(0, 3)); // Return the last 3 posts
+ }
+ });
+ } catch (error) {
+ console.error("Error fetching blog posts:", error);
+ res.status(500).json({ error: "Internal Server Error" });
+ }
+});
+
+module.exports = router;
diff --git a/packages/react-app/components/BlogSection.jsx b/packages/react-app/components/BlogSection.jsx
new file mode 100644
index 00000000..2200b69e
--- /dev/null
+++ b/packages/react-app/components/BlogSection.jsx
@@ -0,0 +1,51 @@
+import React, { useState, useEffect } from 'react';
+import { Container, VStack, Box, Heading, Text, Image, Link, Flex } from '@chakra-ui/react';
+import { fetchRecentPosts } from "../data/api/blog";
+
+const BlogSection = ( posts ) => {
+ const [posts, setPosts] = useState([]);
+
+ useEffect(() => {
+ const fetchPosts = async () => {
+ try {
+ const recentPosts = await fetchRecentPosts();
+ setPosts(recentPosts);
+ } catch (error) {
+ console.error('Error fetching posts:', error);
+ }
+ };
+
+ fetchPosts();
+ }, []);
+
+ return (
+
+
+ Shipping Log
+
+ {posts.map((post, index) => {
+ const date = new Date(post.pubDate);
+ const formattedDate = `${date.toLocaleString('default', { month: 'short' }).toUpperCase()} ${date.getDate()}`;
+
+ return (
+
+
+
+
+
+ {post.title}
+ {post.description}
+ {formattedDate}
+
+
+
+
+
+
+ );
+ })}
+
+ );
+};
+
+export default BlogSection;
diff --git a/packages/react-app/data/api/blog.js b/packages/react-app/data/api/blog.js
new file mode 100644
index 00000000..d72f32a9
--- /dev/null
+++ b/packages/react-app/data/api/blog.js
@@ -0,0 +1,12 @@
+import axios from "axios";
+import { SERVER_URL as serverUrl } from "../../constants";
+
+export const fetchRecentPosts = async () => {
+ try {
+ const response = await axios.get(`${serverUrl}/blog/posts`);
+ return response.data;
+ } catch (error) {
+ console.error("Error fetching recent posts:", error);
+ throw new Error("Couldn't fetch recent posts");
+ }
+};
diff --git a/packages/react-app/pages/index.jsx b/packages/react-app/pages/index.jsx
index c3c2f12e..c185a64f 100644
--- a/packages/react-app/pages/index.jsx
+++ b/packages/react-app/pages/index.jsx
@@ -7,10 +7,11 @@ import { getStats } from "../data/api/builder";
import HeroSection from "../components/home/HeroSection";
import ActivitySection from "../components/home/ActivitySection";
import { getAllEvents } from "../data/api";
+import BlogSection from "../components/BlogSection";
const buildersToShow = ["fullstack", "frontend", "damageDealer", "advisor", "artist", "support"];
/* eslint-disable jsx-a11y/accessible-emoji */
-export default function Index({ bgStats, events }) {
+export default function Index({ bgStats, events, posts }) {
const [builders, setBuilders] = useState([]);
const [isLoadingBuilders, setIsLoadingBuilders] = useState(false);
@@ -48,6 +49,8 @@ export default function Index({ bgStats, events }) {
+
+
{/* Footer */}
@@ -64,6 +67,7 @@ export default function Index({ bgStats, events }) {
export async function getStaticProps() {
const stats = await getStats();
const events = await getAllEvents(null, 10);
+ const posts = await fetchRecentPosts();
return {
props: {
@@ -76,6 +80,7 @@ export async function getStaticProps() {
streamedEthIncrementMonth: stats?.streamedEthIncrementMonth,
},
events,
+ posts,
},
// ToDo. Maybe a 15 min refresh? or load events in the frontend?
// 6 hours refresh.
From ee5861a894dc9027e492862922caea853c38c1b8 Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Sun, 12 May 2024 00:22:42 +0200
Subject: [PATCH 2/8] Remove fetch logic from component to send posts with prop
---
packages/react-app/components/BlogSection.jsx | 39 ++++++-------------
packages/react-app/pages/index.jsx | 1 +
2 files changed, 12 insertions(+), 28 deletions(-)
diff --git a/packages/react-app/components/BlogSection.jsx b/packages/react-app/components/BlogSection.jsx
index 2200b69e..c30fa90d 100644
--- a/packages/react-app/components/BlogSection.jsx
+++ b/packages/react-app/components/BlogSection.jsx
@@ -1,33 +1,16 @@
-import React, { useState, useEffect } from 'react';
+import React from 'react';
import { Container, VStack, Box, Heading, Text, Image, Link, Flex } from '@chakra-ui/react';
-import { fetchRecentPosts } from "../data/api/blog";
-
-const BlogSection = ( posts ) => {
- const [posts, setPosts] = useState([]);
-
- useEffect(() => {
- const fetchPosts = async () => {
- try {
- const recentPosts = await fetchRecentPosts();
- setPosts(recentPosts);
- } catch (error) {
- console.error('Error fetching posts:', error);
- }
- };
-
- fetchPosts();
- }, []);
+const BlogSection = ({ posts }) => {
return (
-
-
- Shipping Log
-
- {posts.map((post, index) => {
- const date = new Date(post.pubDate);
- const formattedDate = `${date.toLocaleString('default', { month: 'short' }).toUpperCase()} ${date.getDate()}`;
-
- return (
+
+
+ Shipping Log
+
+ {posts.map((post, index) => {
+ const date = new Date(post.pubDate);
+ const formattedDate = `${date.toLocaleString('default', { month: 'short' }).toUpperCase()} ${date.getDate()}`;
+ return (
@@ -37,7 +20,7 @@ const BlogSection = ( posts ) => {
{post.description}
{formattedDate}
-
+
diff --git a/packages/react-app/pages/index.jsx b/packages/react-app/pages/index.jsx
index c185a64f..57ed5a8d 100644
--- a/packages/react-app/pages/index.jsx
+++ b/packages/react-app/pages/index.jsx
@@ -8,6 +8,7 @@ import HeroSection from "../components/home/HeroSection";
import ActivitySection from "../components/home/ActivitySection";
import { getAllEvents } from "../data/api";
import BlogSection from "../components/BlogSection";
+import { fetchRecentPosts } from "../data/api/blog";
const buildersToShow = ["fullstack", "frontend", "damageDealer", "advisor", "artist", "support"];
/* eslint-disable jsx-a11y/accessible-emoji */
From 0d0336d7d3ce693673df59370552d1c6bea395b6 Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Sun, 12 May 2024 00:22:49 +0200
Subject: [PATCH 3/8] Remove cors
---
packages/backend/routes/blog.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/packages/backend/routes/blog.js b/packages/backend/routes/blog.js
index ed15aa61..b2a44670 100644
--- a/packages/backend/routes/blog.js
+++ b/packages/backend/routes/blog.js
@@ -1,16 +1,15 @@
const express = require("express");
const axios = require("axios");
const xml2js = require("xml2js");
-const cors = require('cors');
const router = express.Router();
-router.get("/posts", cors(), async (req, res) => {
+router.get("/posts", async (req, res) => {
try {
const response = await axios.get("https://buidlguidl.substack.com/feed");
const xml = response.data;
-
const parser = new xml2js.Parser();
+
parser.parseString(xml, (err, result) => {
if (err) {
console.error("Failed to parse blog feed:", err);
From 0b56183a7aceb9a4efa1afc443ec690f0b93a46b Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Sun, 12 May 2024 00:32:44 +0200
Subject: [PATCH 4/8] Tweak color handling and move BlogSection component to
the right folder
---
packages/react-app/components/{ => home}/BlogSection.jsx | 8 +++++---
packages/react-app/pages/index.jsx | 2 +-
2 files changed, 6 insertions(+), 4 deletions(-)
rename packages/react-app/components/{ => home}/BlogSection.jsx (80%)
diff --git a/packages/react-app/components/BlogSection.jsx b/packages/react-app/components/home/BlogSection.jsx
similarity index 80%
rename from packages/react-app/components/BlogSection.jsx
rename to packages/react-app/components/home/BlogSection.jsx
index c30fa90d..76014696 100644
--- a/packages/react-app/components/BlogSection.jsx
+++ b/packages/react-app/components/home/BlogSection.jsx
@@ -1,8 +1,10 @@
import React from 'react';
import { Container, VStack, Box, Heading, Text, Image, Link, Flex } from '@chakra-ui/react';
+import useCustomColorModes from "../../hooks/useCustomColorModes";
const BlogSection = ({ posts }) => {
- return (
+ const { baseColor, secondaryFontColor } = useCustomColorModes();
+ return (
Shipping Log
@@ -13,12 +15,12 @@ const BlogSection = ({ posts }) => {
return (
-
+
{post.title}
{post.description}
- {formattedDate}
+ {formattedDate}
diff --git a/packages/react-app/pages/index.jsx b/packages/react-app/pages/index.jsx
index 57ed5a8d..8cd6ba0c 100644
--- a/packages/react-app/pages/index.jsx
+++ b/packages/react-app/pages/index.jsx
@@ -7,7 +7,7 @@ import { getStats } from "../data/api/builder";
import HeroSection from "../components/home/HeroSection";
import ActivitySection from "../components/home/ActivitySection";
import { getAllEvents } from "../data/api";
-import BlogSection from "../components/BlogSection";
+import BlogSection from "../components/home/BlogSection";
import { fetchRecentPosts } from "../data/api/blog";
const buildersToShow = ["fullstack", "frontend", "damageDealer", "advisor", "artist", "support"];
From c25e71536c9b642215ac9ee08d07ab0c5a34bf69 Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Sun, 12 May 2024 00:56:10 +0200
Subject: [PATCH 5/8] Move date formatting logic out from frontend component
---
packages/backend/routes/blog.js | 19 +++++----
.../react-app/components/home/BlogSection.jsx | 41 +++++++++----------
2 files changed, 31 insertions(+), 29 deletions(-)
diff --git a/packages/backend/routes/blog.js b/packages/backend/routes/blog.js
index b2a44670..4fec59e1 100644
--- a/packages/backend/routes/blog.js
+++ b/packages/backend/routes/blog.js
@@ -15,13 +15,18 @@ router.get("/posts", async (req, res) => {
console.error("Failed to parse blog feed:", err);
res.status(500).json({ error: "Failed to parse blog feed" });
} else {
- const posts = result.rss.channel[0].item.map(item => ({
- title: item.title[0],
- description: item.description[0],
- link: item.link[0],
- pubDate: item.pubDate[0],
- imageUrl: item.enclosure ? item.enclosure[0].$.url : null
- }));
+ const posts = result.rss.channel[0].item.map(item => {
+ const date = new Date(item.pubDate[0]);
+ const formattedDate = `${date.toLocaleString('default', { month: 'short' }).toUpperCase()} ${date.getDate()}`;
+
+ return {
+ title: item.title[0],
+ description: item.description[0],
+ link: item.link[0],
+ pubDate: formattedDate,
+ imageUrl: item.enclosure ? item.enclosure[0].$.url : null
+ };
+ });
res.status(200).json(posts.slice(0, 3)); // Return the last 3 posts
}
diff --git a/packages/react-app/components/home/BlogSection.jsx b/packages/react-app/components/home/BlogSection.jsx
index 76014696..49b69cdf 100644
--- a/packages/react-app/components/home/BlogSection.jsx
+++ b/packages/react-app/components/home/BlogSection.jsx
@@ -9,28 +9,25 @@ const BlogSection = ({ posts }) => {
Shipping Log
- {posts.map((post, index) => {
- const date = new Date(post.pubDate);
- const formattedDate = `${date.toLocaleString('default', { month: 'short' }).toUpperCase()} ${date.getDate()}`;
- return (
-
-
-
-
-
- {post.title}
- {post.description}
- {formattedDate}
-
-
-
-
-
-
- );
- })}
-
- );
+
+ {posts.map((post, index) => (
+
+
+
+
+
+ {post.title}
+ {post.description}
+ {post.pubDate}
+
+
+
+
+
+
+ ))}
+
+ );
};
export default BlogSection;
From 15604f0bca7c4d3ccfaeb0f8bfacd7c4cbde14b8 Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Mon, 13 May 2024 09:48:38 +0200
Subject: [PATCH 6/8] Manage error on fetching posts
---
packages/react-app/components/home/BlogSection.jsx | 5 +++++
packages/react-app/data/api/blog.js | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/packages/react-app/components/home/BlogSection.jsx b/packages/react-app/components/home/BlogSection.jsx
index 49b69cdf..d714e7fa 100644
--- a/packages/react-app/components/home/BlogSection.jsx
+++ b/packages/react-app/components/home/BlogSection.jsx
@@ -4,6 +4,11 @@ import useCustomColorModes from "../../hooks/useCustomColorModes";
const BlogSection = ({ posts }) => {
const { baseColor, secondaryFontColor } = useCustomColorModes();
+
+ if (!posts || posts.length === 0) {
+ return null;
+ }
+
return (
diff --git a/packages/react-app/data/api/blog.js b/packages/react-app/data/api/blog.js
index d72f32a9..fd443157 100644
--- a/packages/react-app/data/api/blog.js
+++ b/packages/react-app/data/api/blog.js
@@ -7,6 +7,6 @@ export const fetchRecentPosts = async () => {
return response.data;
} catch (error) {
console.error("Error fetching recent posts:", error);
- throw new Error("Couldn't fetch recent posts");
+ return [];
}
};
From 4755bdda83afeb53d390ea2aced551adbb5c1fb6 Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Tue, 21 May 2024 12:57:27 +0200
Subject: [PATCH 7/8] Rename fetch to get for consistency
---
packages/react-app/data/api/blog.js | 2 +-
packages/react-app/pages/index.jsx | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/react-app/data/api/blog.js b/packages/react-app/data/api/blog.js
index fd443157..a4f2d0b9 100644
--- a/packages/react-app/data/api/blog.js
+++ b/packages/react-app/data/api/blog.js
@@ -1,7 +1,7 @@
import axios from "axios";
import { SERVER_URL as serverUrl } from "../../constants";
-export const fetchRecentPosts = async () => {
+export const getRecentPosts = async () => {
try {
const response = await axios.get(`${serverUrl}/blog/posts`);
return response.data;
diff --git a/packages/react-app/pages/index.jsx b/packages/react-app/pages/index.jsx
index 645001d7..260ac882 100644
--- a/packages/react-app/pages/index.jsx
+++ b/packages/react-app/pages/index.jsx
@@ -6,7 +6,7 @@ import ActivitySection from "../components/home/ActivitySection";
import { getAllBuilds, getAllEvents } from "../data/api";
import RecentBuildsSection from "../components/home/RecentBuildsSection";
import BlogSection from "../components/home/BlogSection";
-import { fetchRecentPosts } from "../data/api/blog";
+import { getRecentPosts } from "../data/api/blog";
const buildersToShow = ["fullstack", "frontend", "damageDealer", "advisor", "artist", "support"];
/* eslint-disable jsx-a11y/accessible-emoji */
@@ -56,7 +56,7 @@ export async function getStaticProps() {
const stats = await getStats();
const events = await getAllEvents(null, 10);
const builds = (await getAllBuilds()).sort((a, b) => b.submittedTimestamp - a.submittedTimestamp).slice(0, 4);
- const posts = await fetchRecentPosts();
+ const posts = await getRecentPosts();
return {
props: {
From 69525d86ca8ee26316ec60205168220fada7c068 Mon Sep 17 00:00:00 2001
From: Pablo Alayeto <55535804+Pabl0cks@users.noreply.github.com>
Date: Tue, 21 May 2024 13:10:38 +0200
Subject: [PATCH 8/8] Fix merge problems
---
packages/react-app/pages/index.jsx | 32 ++----------------------------
1 file changed, 2 insertions(+), 30 deletions(-)
diff --git a/packages/react-app/pages/index.jsx b/packages/react-app/pages/index.jsx
index 260ac882..eb657a76 100644
--- a/packages/react-app/pages/index.jsx
+++ b/packages/react-app/pages/index.jsx
@@ -5,38 +5,10 @@ import HeroSection from "../components/home/HeroSection";
import ActivitySection from "../components/home/ActivitySection";
import { getAllBuilds, getAllEvents } from "../data/api";
import RecentBuildsSection from "../components/home/RecentBuildsSection";
-import BlogSection from "../components/home/BlogSection";
import { getRecentPosts } from "../data/api/blog";
-const buildersToShow = ["fullstack", "frontend", "damageDealer", "advisor", "artist", "support"];
-
-/* eslint-disable jsx-a11y/accessible-emoji */
-export default function Index({ bgStats, events, posts }) {
- const [builders, setBuilders] = useState([]);
- const [isLoadingBuilders, setIsLoadingBuilders] = useState(false);
-
- const streamSection = useRef(null);
-
- const { colorMode } = useColorMode();
- const isDarkMode = colorMode === "dark";
- const scaffoldEthBg = useColorModeValue("#fbf7f6", "whiteAlpha.300");
-
- useEffect(() => {
- async function fetchBuilders() {
- setIsLoadingBuilders(true);
- const fetchedBuilders = await axios.get(`${SERVER_URL}/builders`);
-
- setBuilders(fetchedBuilders.data);
- setIsLoadingBuilders(false);
- }
-
- fetchBuilders();
- }, []);
-
- const smoothScroll = ref => {
- ref.current.scrollIntoView({ behavior: "smooth" });
- };
+import BlogSection from "../components/home/BlogSection";
-export default function Index({ bgStats, events, builds }) {
+export default function Index({ bgStats, events, builds, posts }) {
return (
<>