feat: list logos from the api instead of hardcoding it
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const baseApiUrl = useBaseApiUrl();
|
const baseApiUrl = useBaseApiUrl();
|
||||||
const { copy: copyBaseApiUrl, icon: baseApiUrlIcon } = useCopyable(baseApiUrl);
|
const { copy: copyBaseApiUrl, icon: baseApiUrlIcon } = useCopyable(baseApiUrl);
|
||||||
|
const { data: logos } = await useFetch<string[]>("/api/logos");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -10,8 +11,8 @@ const { copy: copyBaseApiUrl, icon: baseApiUrlIcon } = useCopyable(baseApiUrl);
|
|||||||
<p>
|
<p>
|
||||||
You can easily generate QRCodes by using the API, with no rate
|
You can easily generate QRCodes by using the API, with no rate
|
||||||
limitation.
|
limitation.
|
||||||
<br />
|
<br >
|
||||||
<br />
|
<br >
|
||||||
If you are not sure how to use the API, you can fill the QRCode form
|
If you are not sure how to use the API, you can fill the QRCode form
|
||||||
and copy the generated API URL.
|
and copy the generated API URL.
|
||||||
</p>
|
</p>
|
||||||
@@ -58,7 +59,7 @@ const { copy: copyBaseApiUrl, icon: baseApiUrlIcon } = useCopyable(baseApiUrl);
|
|||||||
<div class="flex pb-4 justify-between gap-x-5">
|
<div class="flex pb-4 justify-between gap-x-5">
|
||||||
<span class="text-primary font-semibold">logo</span>
|
<span class="text-primary font-semibold">logo</span>
|
||||||
<span class="text-slate-700 dark:text-slate-200 text-right">{{
|
<span class="text-slate-700 dark:text-slate-200 text-right">{{
|
||||||
arrayToUnion(LOGOS)
|
arrayToUnion(logos ?? [])
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ const apiModal = overlay.create(LazyApiModal);
|
|||||||
|
|
||||||
const isQRCodeEmpty = computed(() => qrCode.value === "/default.webp");
|
const isQRCodeEmpty = computed(() => qrCode.value === "/default.webp");
|
||||||
|
|
||||||
|
const { data: logos } = await useFetch<string[]>("/api/logos");
|
||||||
|
|
||||||
const formSchema = z
|
const formSchema = z
|
||||||
.object({
|
.object({
|
||||||
hasLogo: z.boolean(),
|
hasLogo: z.boolean(),
|
||||||
logo: z.enum(LOGOS).optional(),
|
logo: z.string().optional(),
|
||||||
format: z.enum(IMAGE_FORMATS).default("png"),
|
format: z.enum(IMAGE_FORMATS).default("png"),
|
||||||
content: z
|
content: z
|
||||||
.string()
|
.string()
|
||||||
@@ -103,7 +105,7 @@ const {
|
|||||||
<img
|
<img
|
||||||
:src="qrCode"
|
:src="qrCode"
|
||||||
class="w-full max-w-[375px] max-h-[375px] sm:max-w-[315px] sm:max-h-[315px] md:max-w-[375px] md:max-h-[375px] m-auto aspect-square border border-gray-100 dark:border-gray-800"
|
class="w-full max-w-[375px] max-h-[375px] sm:max-w-[315px] sm:max-h-[315px] md:max-w-[375px] md:max-h-[375px] m-auto aspect-square border border-gray-100 dark:border-gray-800"
|
||||||
/>
|
>
|
||||||
|
|
||||||
<div class="flex-1 flex flex-col justify-center">
|
<div class="flex-1 flex flex-col justify-center">
|
||||||
<UForm
|
<UForm
|
||||||
@@ -138,7 +140,7 @@ const {
|
|||||||
<USelectMenu
|
<USelectMenu
|
||||||
v-model="formState.logo"
|
v-model="formState.logo"
|
||||||
icon="i-heroicons-photo"
|
icon="i-heroicons-photo"
|
||||||
:items="unreadonly(LOGOS)"
|
:items="logos ?? []"
|
||||||
:disabled="!formState.hasLogo"
|
:disabled="!formState.hasLogo"
|
||||||
placeholder="Select logo"
|
placeholder="Select logo"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
|
|||||||
22
server/api/logos.ts
Normal file
22
server/api/logos.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { readdir } from "fs/promises";
|
||||||
|
import { join } from "path";
|
||||||
|
import { createHash } from "crypto";
|
||||||
|
|
||||||
|
const logosDir = import.meta.dev
|
||||||
|
? join(process.cwd(), "public/logos")
|
||||||
|
: join(process.cwd(), ".output/public/logos");
|
||||||
|
|
||||||
|
export default defineCachedEventHandler(async () => {
|
||||||
|
const files = await readdir(logosDir);
|
||||||
|
return files
|
||||||
|
.filter((f) => f.endsWith(".png"))
|
||||||
|
.map((f) => f.replace(/\.png$/, ""))
|
||||||
|
.sort();
|
||||||
|
}, {
|
||||||
|
maxAge: 60 * 60 * 24,
|
||||||
|
getKey: async () => {
|
||||||
|
const files = await readdir(logosDir);
|
||||||
|
const key = files.filter((f) => f.endsWith(".png")).sort().join(",");
|
||||||
|
return createHash("sha256").update(key).digest("hex");
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,60 +1,12 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
// TODO: might be better to load these dynamically lol
|
|
||||||
export const LOGOS = [
|
|
||||||
"bereal",
|
|
||||||
"bitcoin",
|
|
||||||
"buymeacoffee",
|
|
||||||
"diaspora",
|
|
||||||
"discord",
|
|
||||||
"dropbox",
|
|
||||||
"ello",
|
|
||||||
"facebook",
|
|
||||||
"flickr",
|
|
||||||
"github",
|
|
||||||
"googlemaps",
|
|
||||||
"googlemeet",
|
|
||||||
"googlemessages",
|
|
||||||
"imessage",
|
|
||||||
"instagram",
|
|
||||||
"kik",
|
|
||||||
"line",
|
|
||||||
"linkedin",
|
|
||||||
"litecoin",
|
|
||||||
"mastodon",
|
|
||||||
"medium",
|
|
||||||
"messenger",
|
|
||||||
"monero",
|
|
||||||
"onlyfans",
|
|
||||||
"patreon",
|
|
||||||
"paypal",
|
|
||||||
"peertube",
|
|
||||||
"pinterest",
|
|
||||||
"reddit",
|
|
||||||
"session",
|
|
||||||
"signal",
|
|
||||||
"snapchat",
|
|
||||||
"spotify",
|
|
||||||
"substack",
|
|
||||||
"telegram",
|
|
||||||
"threema",
|
|
||||||
"twitch",
|
|
||||||
"venmo",
|
|
||||||
"viber",
|
|
||||||
"wechat",
|
|
||||||
"whatsapp",
|
|
||||||
"x",
|
|
||||||
"youtube",
|
|
||||||
"zoom",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const IMAGE_FORMATS = ["png", "jpeg", "webp"] as const;
|
export const IMAGE_FORMATS = ["png", "jpeg", "webp"] as const;
|
||||||
|
|
||||||
export type ImageFormat = (typeof IMAGE_FORMATS)[number];
|
export type ImageFormat = (typeof IMAGE_FORMATS)[number];
|
||||||
|
|
||||||
export const settingsSchema = z.object({
|
export const settingsSchema = z.object({
|
||||||
format: z.enum(IMAGE_FORMATS).default("png"),
|
format: z.enum(IMAGE_FORMATS).default("png"),
|
||||||
logo: z.enum(LOGOS).optional(),
|
logo: z.string().optional(),
|
||||||
content: z.string().min(1, "Required"),
|
content: z.string().min(1, "Required"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user