The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

add views to ai gallery

Luna 1d797d0c b9b1ef72

+75 -5
+21 -4
app/ai-gallery/[uploadId]/layout.tsx
··· 1 - import { Chip } from "@nextui-org/react"; 1 + import { Button, Chip } from "@nextui-org/react"; 2 2 import { Metadata } from "next"; 3 3 import NextImage from "next/image"; 4 4 import Link from "next/link"; 5 - import { HiHome, HiPlus } from "react-icons/hi"; 5 + import { HiArrowLeft, HiHome, HiPlus } from "react-icons/hi"; 6 6 7 7 import Footer from "@/components/footer"; 8 8 import Notice from "@/components/notice"; 9 9 import { ScreenMessage, SupportButton } from "@/components/screen-message"; 10 10 import { ServerButton } from "@/components/server-button"; 11 + import { getPageAnalytics } from "@/lib/analytics"; 11 12 import { getGuild } from "@/lib/api"; 12 13 import SadWumpusPic from "@/public/sad-wumpus.gif"; 13 14 import { getBaseUrl, getCanonicalUrl } from "@/utils/urls"; ··· 60 61 children 61 62 }: Props) { 62 63 const upload = await getUpload(params.uploadId); 63 - const guild = "guildId" in upload ? await getGuild(upload.guildId) : undefined; 64 - const uploads = "model" in upload ? await getUploads({ model: upload.model }) : undefined; 64 + 65 + const [guild, uploads, analytics] = await Promise.all([ 66 + "guildId" in upload ? getGuild(upload.guildId) : undefined, 67 + "model" in upload ? getUploads({ model: upload.model }) : undefined, 68 + getPageAnalytics("/ai-gallery/" + params.uploadId) 69 + ]); 65 70 66 71 return ( 67 72 <div className="w-full space-y-12"> ··· 96 101 <Side 97 102 upload={upload} 98 103 guild={guild} 104 + analytics={analytics} 99 105 /> 100 106 </div> 101 107 ··· 106 112 <span className="relative bottom-4 mb-4"> 107 113 Images that were also generated with the <Chip>{"model" in upload ? upload.model : "..."}</Chip> model. 108 114 </span> 115 + 109 116 {Array.isArray(uploads) ? 110 117 <div className="flex flex-wrap gap-4"> 111 118 {uploads ··· 135 142 : 136 143 <Notice message={uploads?.message || "Something went wrong..."} /> 137 144 } 145 + 138 146 </div> 147 + 148 + <Button 149 + className="!mt-5" 150 + as={Link} 151 + href="/ai" 152 + startContent={<HiArrowLeft />} 153 + > 154 + View all Uploads 155 + </Button> 139 156 140 157 <Footer className="mt-10" /> 141 158
+22 -1
app/ai-gallery/[uploadId]/side.component.tsx
··· 10 10 import { CopyToClipboardButton } from "@/components/copy-to-clipboard"; 11 11 import ImageReduceMotion from "@/components/image-reduce-motion"; 12 12 import { formatDate } from "@/components/time"; 13 + import { AnalyticsError, AnalyticsResponse } from "@/lib/analytics"; 13 14 import { ApiError, ApiV1GuildsGetResponse } from "@/typings"; 14 15 import { getCanonicalUrl } from "@/utils/urls"; 15 16 ··· 17 18 18 19 export default function Side({ 19 20 upload, 20 - guild 21 + guild, 22 + analytics 21 23 }: { 22 24 upload: ExtendedUpload | ApiError; 23 25 guild: ApiV1GuildsGetResponse | ApiError | undefined; 26 + analytics: { results: AnalyticsResponse[] } | AnalyticsError | undefined; 24 27 }) { 25 28 const web = webStore((w) => w); 26 29 ··· 90 93 } 91 94 </Chip> 92 95 </div> 96 + 97 + {analytics && "results" in analytics && 98 + <div className="flex items-center justify-between"> 99 + <span>Views</span> 100 + <Chip 101 + className="select-none" 102 + radius="sm" 103 + > 104 + {Array.isArray(analytics?.results) ? 105 + (analytics.results[0].visits || analytics.results[0].visitors) 106 + : 107 + "unknown" 108 + } 109 + {" "} 110 + views 111 + </Chip> 112 + </div> 113 + } 93 114 94 115 {"author" in upload && 95 116 <div className="flex items-center justify-between">
+32
lib/analytics.ts
··· 1 + export interface AnalyticsResponse { 2 + page: string; 3 + visitors: number; 4 + visits: number; 5 + } 6 + 7 + export interface AnalyticsError { 8 + error: string; 9 + } 10 + 11 + export async function getPageAnalytics(page: string): Promise<{ results: AnalyticsResponse[] } | AnalyticsError | undefined> { 12 + 13 + const params = { 14 + site_id: process.env.PLAUSIBLE_DOMAIN as string, 15 + period: "custom", 16 + date: "2021-01-01,2026-03-02", 17 + property: "event:page", 18 + filters: "event:page==" + page, 19 + metrics: "visits,visitors" 20 + }; 21 + 22 + const res = await fetch(`${process.env.PLAUSIBLE_API}/v1/stats/breakdown?${objectToQueryString(params)}`, { 23 + headers: { Authorization: "Bearer " + process.env.PLAUSIBLE_API_KEY as string }, 24 + next: { revalidate: 60 } 25 + }); 26 + 27 + return res.json(); 28 + } 29 + 30 + function objectToQueryString(obj: Record<string, string>): string { 31 + return Object.entries(obj).map(([key, value]) => `${key}=${value}`).join("&"); 32 + }