From f0e247d5e3ba8996dd5e5bbe557b2ada526b6562 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sat, 18 Jan 2025 16:33:58 +0800 Subject: [PATCH 1/4] BLOG-8 feat: setup gitea workflow --- .gitea/workflows/frontend-ci.yaml | 27 +++++++++++++++++++++++++++ frontend/package.json | 1 + 2 files changed, 28 insertions(+) create mode 100644 .gitea/workflows/frontend-ci.yaml diff --git a/.gitea/workflows/frontend-ci.yaml b/.gitea/workflows/frontend-ci.yaml new file mode 100644 index 0000000..78e925c --- /dev/null +++ b/.gitea/workflows/frontend-ci.yaml @@ -0,0 +1,27 @@ +name: Frontend CI +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install + + - name: ESLint + run: pnpm run lint + + - name: Build + run: pnpm run build diff --git a/frontend/package.json b/frontend/package.json index 97d14dc..662908c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,7 @@ "start": "next start", "lint": "next lint" }, + "packageManager": "pnpm@10.0.0", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.2", "@fortawesome/free-brands-svg-icons": "^6.7.2", -- 2.47.1 From dc73ef180b1d7b1abb583f5a91fb1cfd56712ea2 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sat, 18 Jan 2025 17:21:02 +0800 Subject: [PATCH 2/4] BLOG-8 fix: wrong directory --- .gitea/workflows/frontend-ci.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/frontend-ci.yaml b/.gitea/workflows/frontend-ci.yaml index 78e925c..9021a48 100644 --- a/.gitea/workflows/frontend-ci.yaml +++ b/.gitea/workflows/frontend-ci.yaml @@ -1,25 +1,32 @@ name: Frontend CI on: [push] +defaults: + run: + working-directory: ./frontend + jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - + - name: Install pnpm uses: pnpm/action-setup@v4 + with: + version: 10 + run_install: false - - name: Install Node.js + - name: Install node.js uses: actions/setup-node@v4 with: node-version: 20 - cache: 'pnpm' - + cache: "pnpm" + - name: Install dependencies run: pnpm install - + - name: ESLint run: pnpm run lint -- 2.47.1 From c2fef757b34493688e9388b65ab096059730b0d4 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sat, 18 Jan 2025 17:30:11 +0800 Subject: [PATCH 3/4] BLOG-8 fix: node pnpm cache --- .gitea/workflows/frontend-ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/frontend-ci.yaml b/.gitea/workflows/frontend-ci.yaml index 9021a48..2c5bb83 100644 --- a/.gitea/workflows/frontend-ci.yaml +++ b/.gitea/workflows/frontend-ci.yaml @@ -23,6 +23,7 @@ jobs: with: node-version: 20 cache: "pnpm" + cache-dependency-path: frontend/pnpm-lock.yaml - name: Install dependencies run: pnpm install -- 2.47.1 From 018e037cf847f7ab9d5bc1d51cb80a7bb768e6bf Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sat, 18 Jan 2025 19:49:57 +0800 Subject: [PATCH 4/4] BLOG-8 fix: circular dependency --- .../src/lib/home/framework/ui/SelfTags.tsx | 34 ++++++--- .../presenter/{tagHooks.ts => tagHook.ts} | 0 frontend/src/lib/home/presenter/tagReducer.ts | 55 ++++++++++++++ frontend/src/lib/home/presenter/tagSlice.ts | 73 ------------------- frontend/src/lib/home/presenter/tagStore.ts | 10 +-- 5 files changed, 80 insertions(+), 92 deletions(-) rename frontend/src/lib/home/presenter/{tagHooks.ts => tagHook.ts} (100%) create mode 100644 frontend/src/lib/home/presenter/tagReducer.ts delete mode 100644 frontend/src/lib/home/presenter/tagSlice.ts diff --git a/frontend/src/lib/home/framework/ui/SelfTags.tsx b/frontend/src/lib/home/framework/ui/SelfTags.tsx index da6af05..5de24ea 100644 --- a/frontend/src/lib/home/framework/ui/SelfTags.tsx +++ b/frontend/src/lib/home/framework/ui/SelfTags.tsx @@ -1,10 +1,10 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Provider } from "react-redux"; -import { useTagDispatch, useTagSelector } from "@/lib/home/presenter/tagHooks"; -import { tagStartedAction, tagStoppedAction } from "@/lib/home/presenter/tagSlice"; +import { useTagDispatch, useTagSelector } from "@/lib/home/presenter/tagHook"; +import { tagShuffledAction } from "@/lib/home/presenter/tagReducer"; import tagStore from "@/lib/home/presenter/tagStore"; export default function SelfTags() { @@ -16,25 +16,35 @@ export default function SelfTags() { } function SelfTagsProvided() { - const tags = useTagSelector((state) => state.tag.tags); + const tags = useTagSelector((state) => state.tags); const dispatch = useTagDispatch(); + // Initialize with placeholder to prevent flickering + const [tagsToShow, setTagsToShow] = useState([""]); const [isTagsVisible, setIsTagsVisible] = useState(false); + const timer = useRef(undefined); + + // On mount useEffect(() => { - dispatch(tagStartedAction({ interval: 4000 })); + timer.current = setInterval(() => { + dispatch(tagShuffledAction()); + }, 4000); + return () => { - dispatch(tagStoppedAction()); - setIsTagsVisible(false); + clearInterval(timer.current); + timer.current = undefined; }; }, [dispatch]); + // On tags changed useEffect(() => { - if (tags.length === 0) return; - setIsTagsVisible(true); + setIsTagsVisible(false); + setTimeout(() => { - setIsTagsVisible(false); - }, 3500); + setTagsToShow(tags); + setIsTagsVisible(true); + }, 500); }, [tags]); return ( @@ -43,7 +53,7 @@ function SelfTagsProvided() { >
- {tags.map((tag) => ( + {tagsToShow.map((tag) => ( ))}
diff --git a/frontend/src/lib/home/presenter/tagHooks.ts b/frontend/src/lib/home/presenter/tagHook.ts similarity index 100% rename from frontend/src/lib/home/presenter/tagHooks.ts rename to frontend/src/lib/home/presenter/tagHook.ts diff --git a/frontend/src/lib/home/presenter/tagReducer.ts b/frontend/src/lib/home/presenter/tagReducer.ts new file mode 100644 index 0000000..5777d88 --- /dev/null +++ b/frontend/src/lib/home/presenter/tagReducer.ts @@ -0,0 +1,55 @@ +import { createAction, createReducer } from "@reduxjs/toolkit"; + +import shuffleArray from "@/lib/util/shuffleArray"; + +export const tagShuffledAction = createAction("tag/shuffled"); + +const tagReducer = createReducer( + () => ({ + tags: shuffleArray(tagsCollection), + }), + (builder) => { + builder.addCase(tagShuffledAction, (state) => { + state.tags = shuffleArray(tagsCollection); + }); + }, +); + +export default tagReducer; +export type TagAction = ReturnType; + +export interface TagState { + tags: string[]; + timer?: NodeJS.Timeout; +} + +const tagsCollection = [ + "APP", + "C++", + "Design Pattern", + "Docker", + "Flutter", + "Go", + "Java", + "LINER", + "Linux", + "Python", + "Squid", + "TypeScript", + "中央大學", + "全端", + "分享", + "前端", + "後端", + "教學", + "暴肝", + "知識", + "碼農", + "科技", + "科普", + "程式設計", + "資工系", + "軟體工程", + "遊戲", + "魷魚", +]; diff --git a/frontend/src/lib/home/presenter/tagSlice.ts b/frontend/src/lib/home/presenter/tagSlice.ts deleted file mode 100644 index 0f4c568..0000000 --- a/frontend/src/lib/home/presenter/tagSlice.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { PayloadAction, createSlice } from "@reduxjs/toolkit"; - -import tagStore from "@/lib/home/presenter/tagStore"; -import shuffleArray from "@/lib/util/shuffleArray"; - -export interface TagState { - tags: string[]; - timer?: NodeJS.Timeout; -} - -export interface TagStartedActionPayload { - interval: number; -} - -export const tagSlice = createSlice({ - name: "tag", - initialState: { - tags: [], - timer: undefined, - } as TagState, - reducers: { - started: (state, action: PayloadAction) => { - state.tags = shuffleArray(tagsCollection); - state.timer = setInterval(() => { - tagStore.dispatch(tagSlice.actions.shuffled()); - }, action.payload.interval); - }, - shuffled: (state) => { - state.tags = shuffleArray(tagsCollection); - }, - stopped: (state) => { - clearInterval(state.timer); - state.timer = undefined; - }, - }, -}); - -export const tagStartedAction = tagSlice.actions.started; -export const tagStoppedAction = tagSlice.actions.stopped; - -const tagReducer = tagSlice.reducer; -export default tagReducer; - -const tagsCollection = [ - "APP", - "C++", - "Design Pattern", - "Docker", - "Flutter", - "Go", - "Java", - "LINER", - "Linux", - "Python", - "Squid", - "TypeScript", - "中央大學", - "全端", - "分享", - "前端", - "後端", - "教學", - "暴肝", - "知識", - "碼農", - "科技", - "科普", - "程式設計", - "資工系", - "軟體工程", - "遊戲", - "魷魚", -]; diff --git a/frontend/src/lib/home/presenter/tagStore.ts b/frontend/src/lib/home/presenter/tagStore.ts index 082f139..91e1df6 100644 --- a/frontend/src/lib/home/presenter/tagStore.ts +++ b/frontend/src/lib/home/presenter/tagStore.ts @@ -1,11 +1,7 @@ import { configureStore } from "@reduxjs/toolkit"; -import tagReducer from "@/lib/home/presenter/tagSlice"; +import tagReducer, { TagAction, TagState } from "@/lib/home/presenter/tagReducer"; -const tagStore = configureStore({ - reducer: { - tag: tagReducer, - }, +export default configureStore({ + reducer: tagReducer, }); - -export default tagStore; -- 2.47.1