Commit cccd33f

HPCesia <me@hpcesia.com>
2025-04-18 15:11:41
feat(user components): repo card support gitea
1 parent 1a02a03
Changed files (3)
src
components
content
src/components/user/RepoCard.astro
@@ -1,26 +1,69 @@
 ---
 import { Icon } from 'astro-icon/components';
 
-interface Props {
+type Platform = {
+  /**
+   * The platform type of the repository
+   *
+   * 存储库的平台类型
+   */
+  platform: string;
+};
+
+interface GithubPlatform extends Platform {
+  platform: 'github';
+}
+
+interface GiteaPlatform extends Platform {
+  platform: 'gitea' | 'forgejo';
+  /**
+   * The host of the Gitea/Forgejo instance
+   *
+   * Gitea/Forgejo 实例的主机地址
+   *
+   * @example 'https://gitea.example.com'
+   */
+  host: string;
+  /**
+   * The Iconify icon name of the Gitea/Forgejo instance
+   *
+   * Gitea/Forgejo 实例的 Iconify 图标名称
+   */
+  icon?: string;
+}
+
+type AllPlatform = GithubPlatform | GiteaPlatform;
+
+type Props = {
   repo:
     | {
         owner: string;
         name: string;
       }
     | `${string}/${string}`;
-  platform: 'github';
-}
+} & AllPlatform;
 
 const { repo, platform } = Astro.props;
 const repoName = typeof repo === 'string' ? repo : `${repo.owner}/${repo.name}`;
 
 let url: string;
-let platformIconName: string;
+let icon: string;
 
 switch (platform) {
   case 'github': {
     url = `https://github.com/${repoName}/`;
-    platformIconName = 'mdi:github';
+    icon = 'mdi:github';
+    break;
+  }
+  case 'gitea':
+  case 'forgejo': {
+    const { host, icon: hostIcon } = Astro.props;
+    url = `${host}/${repoName}/`;
+    icon = hostIcon || `simple-icons:${platform}`;
+    break;
+  }
+  default: {
+    throw new Error(`Unsupported platform: ${platform} for RepoCard`);
   }
 }
 ---
@@ -34,7 +77,7 @@ switch (platform) {
   <div class="card-body p-4">
     <div class="card-title mb-4 justify-between">
       <span class="text-xl">{repoName}</span>
-      <Icon name={platformIconName} height="3rem" width="3rem" />
+      <Icon name={icon} height="3rem" width="3rem" />
     </div>
     <div class="repo-card-desc flex flex-col gap-2">
       <div class="skeleton h-4 w-full"></div>
@@ -62,7 +105,7 @@ switch (platform) {
 </a>
 
 <script>
-  const allPlatform = ['github'] as const;
+  const allPlatform = ['github', 'gitea', 'forgejo'] as const;
   type Platform = (typeof allPlatform)[number];
 
   interface RepoMeta {
@@ -132,7 +175,7 @@ switch (platform) {
             const repoMeta: RepoMeta = {
               description: meta.data.description,
               language: meta.data.language,
-              license: meta.data.license?.name || null,
+              license: meta.data.license?.spdx_id || null,
               forks: meta.data.forks_count,
               stars: meta.data.stargazers_count,
             };
@@ -141,6 +184,33 @@ switch (platform) {
           });
           break;
         }
+        case 'gitea':
+        case 'forgejo': {
+          cards.forEach(async (card) => {
+            const url = card.getAttribute('href');
+            const repoName = card.getAttribute('data-repo')!;
+            const host = url?.substring(0, url.indexOf(repoName) - 1);
+            if (!host) return;
+
+            try {
+              const response = await fetch(`${host}/api/v1/repos/${repoName}`);
+              const data = await response.json();
+              const repoMeta: RepoMeta = {
+                description: data.description,
+                language: data.language,
+                // Gitea and Forgejo do not provide license information in the API response.
+                // And infer license type on client side is expensive.
+                license: null,
+                forks: data.forks_count,
+                stars: data.stars_count,
+              };
+
+              updateCardUI(card, repoMeta);
+            } catch (error) {
+              console.error(`Failed to fetch repo data for ${repoName}:`, error);
+            }
+          });
+        }
       }
     });
   }
src/content/posts/components.mdx
@@ -142,6 +142,44 @@ Components let you easily reuse a piece of UI or styling consistently. You can u
   </Fragment>
 </Repl>
 
+### LinkCard
+<Repl>
+  <LinkCard
+    title="Astral Halo"
+    description="A static blog template developed with Astro"
+    url="https://github.com/HPCesia/astral-halo"
+  />
+  <LinkCard
+    title="Astro"
+    description="The all-in-one web framework designed for speed."
+    url="https://astro.build/"
+  />
+  <Fragment slot="desc">
+    <Tabs>
+      <TabItem label="mdx" active>
+        ```jsx
+        <LinkCard
+          title="Astral Halo"
+          description="A static blog template developed with Astro"
+          url="https://github.com/HPCesia/astral-halo"
+        />
+        <LinkCard
+          title="Astro"
+          description="The all-in-one web framework designed for speed."
+          url="https://astro.build/"
+        />
+        ```
+      </TabItem>
+      <TabItem label="md">
+        ```md
+        ::linkcard{title="Astral Halo" description="A static blog template developed with Astro" url="https://github.com/HPCesia/astral-halo"}
+        ::linkcard{title="Astro" description="The all-in-one web framework designed for speed." url="https://astro.build/"}
+        ```
+      </TabItem>
+    </Tabs>
+  </Fragment>
+</Repl>
+
 ## Inline Containers
 
 ### Tooltip
@@ -225,49 +263,13 @@ Components let you easily reuse a piece of UI or styling consistently. You can u
 
 <Repl>
   <RepoCard repo="HPCesia/astral-halo" platform='github' />
-  <RepoCard repo="withastro/astro" platform='github' />
+  <RepoCard repo="HPCesia/AstralHalo" platform='forgejo' host='https://codeberg.org' icon='simple-icons:codeberg' />
+  <RepoCard repo="gitea/awesome-gitea" platform='gitea' host='https://gitea.com' icon='simple-icons:gitea' />
   <Fragment slot="desc">
     ```jsx
     <RepoCard repo="HPCesia/astral-halo" platform='github' />
-    <RepoCard repo="withastro/astro" platform='github' />
+    <RepoCard repo="HPCesia/AstralHalo" platform='forgejo' host='https://codeberg.org' icon='simple-icons:codeberg' />
+    <RepoCard repo="gitea/awesome-gitea" platform='gitea' host='https://gitea.com' icon='simple-icons:gitea' />
     ```
   </Fragment>
 </Repl>
-
-### LinkCard
-<Repl>
-  <LinkCard
-    title="Astral Halo"
-    description="A static blog template developed with Astro"
-    url="https://github.com/HPCesia/astral-halo"
-  />
-  <LinkCard
-    title="Astro"
-    description="The all-in-one web framework designed for speed."
-    url="https://astro.build/"
-  />
-  <Fragment slot="desc">
-    <Tabs>
-      <TabItem label="mdx" active>
-        ```jsx
-        <LinkCard
-          title="Astral Halo"
-          description="A static blog template developed with Astro"
-          url="https://github.com/HPCesia/astral-halo"
-        />
-        <LinkCard
-          title="Astro"
-          description="The all-in-one web framework designed for speed."
-          url="https://astro.build/"
-        />
-        ```
-      </TabItem>
-      <TabItem label="md">
-        ```md
-        ::linkcard{title="Astral Halo" description="A static blog template developed with Astro" url="https://github.com/HPCesia/astral-halo"}
-        ::linkcard{title="Astro" description="The all-in-one web framework designed for speed." url="https://astro.build/"}
-        ```
-      </TabItem>
-    </Tabs>
-  </Fragment>
-</Repl>
\ No newline at end of file
package.json
@@ -20,6 +20,7 @@
     "@astrojs/vue": "^5.0.10",
     "@iconify-json/material-symbols": "^1.2.19",
     "@iconify-json/mdi": "^1.2.3",
+    "@iconify-json/simple-icons": "^1.2.3",
     "@iconify-json/mingcute": "^1.2.3",
     "@iconify/utils": "^2.3.0",
     "@octokit/request": "^9.2.3",