]> git.ipfire.org Git - pbs.git/commitdiff
frontend: Add an Avatar component
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 18 Jul 2025 14:59:10 +0000 (14:59 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 18 Jul 2025 14:59:10 +0000 (14:59 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
frontend/src/api/users.ts
frontend/src/components/Avatar.vue [new file with mode: 0644]
frontend/src/components/BasicComponents.ts

index a1e7b8c7d0cf61dc3cef46dd5387fc8048fc10fe..337b80f167e4356a2a60f9f55c4364b089e3170a 100644 (file)
@@ -22,3 +22,35 @@ export async function fetchCurrentUser(): Promise<User> {
                throw new Error("Failed to load user")
        }
 }
+
+export async function fetchUser(username: string): Promise<User> {
+       const response = await api.get(`/v1/users/${username}`);
+       return response.data as User;
+}
+
+export function makeAvatarUrl(user: User, size: number): string | undefined {
+       const url = URL.parse(user.avatar_url);
+       if (!url)
+               return undefined;
+
+       // Add the size argument
+       url.searchParams.set("size", `${size}`);
+
+       // Return the URL as string
+       return url.toString();
+}
+
+export function makeAvatarUrls(user: User, size: number): string | undefined {
+       const multipliers = [2, 3];
+       const sources = [];
+
+       for (const multiplier of multipliers) {
+               let source = makeAvatarUrl(user, size * multiplier);
+               if (!source)
+                       continue;
+
+               sources.push(`${source} ${multiplier}x`);
+       }
+
+       return sources.join(", ");
+}
diff --git a/frontend/src/components/Avatar.vue b/frontend/src/components/Avatar.vue
new file mode 100644 (file)
index 0000000..20eea62
--- /dev/null
@@ -0,0 +1,27 @@
+<script setup lang="ts">
+       import { ref, onMounted } from "vue";
+
+       // Import types
+       import type { User } from "@/api/users";
+       import { makeAvatarUrl, makeAvatarUrls } from "@/api/users";
+
+       // Fetch the build UUID
+       const props = withDefaults(
+               defineProps<{
+                       user: User,
+                       size?: number,
+                       isRounded?: boolean,
+               }>(),
+               {
+                       size      : 256,
+                       isRounded : false,
+               },
+       );
+</script>
+
+<template>
+       <figure :class="`image is-${size}x${size}`">
+               <img :class="{ 'is-rounded' : isRounded }" :alt="user.name"
+                       :src="makeAvatarUrl(user, size)" :srcset="makeAvatarUrls(user, size)" />
+       </figure>
+</template>
index 004f659a7e0274fc0904360afffc6ec1480b5564..60bc3cff9e343b9b90ff4ca2bc1c5419d9791c72 100644 (file)
@@ -12,6 +12,7 @@ import Tags from "@/components/Tags.vue";
 import Tag from "@/components/Tag.vue";
 
 // Components
+import Avatar from "@/components/Avatar.vue";
 import Modal from "@/components/Modal.vue";
 
 // Icons
@@ -22,6 +23,7 @@ import Loader from "@/components/Loader.vue";
 
 export default {
        install(app: App) {
+               app.component("Avatar", Avatar)
                app.component("Container", Container)
                app.component("Block", Block)
                app.component("Box", Box)