✨ Gallery one item view
This commit is contained in:
		| @@ -7,32 +7,7 @@ | ||||
|     height="auto" | ||||
|   > | ||||
|     <v-carousel-item v-for="item in metadata" class="fill-height"> | ||||
|       <v-img v-if="item.mimetype.split('/')[0] == 'image'" :src="getAttachmentUrl(item.id)" class="fill-height" cover /> | ||||
|       <video v-else-if="item.mimetype.split('/')[0] == 'video'" :src="getAttachmentUrl(item.id)" class="fill-height" | ||||
|              controls /> | ||||
|       <v-sheet v-else color="rgba(0, 0, 0, .4)" height="calc(100% + 24px)" class="p-5"> | ||||
|         <v-row class="fill-height" align="center" justify="center"> | ||||
|           <v-col class="text-center"> | ||||
|             <h1 class="text-xl font-bold text-white"> | ||||
|               {{ item.alt }} | ||||
|             </h1> | ||||
|             <p class="text-md text-white">{{ item.mimetype }}</p> | ||||
|  | ||||
|             <p class="text-sm text-white mt-3">Unable to preview, you can open it via other ways.</p> | ||||
|  | ||||
|             <div class="flex justify-center mt-3"> | ||||
|               <v-btn | ||||
|                 variant="text" | ||||
|                 color="white" | ||||
|                 prepend-icon="mdi-launch" | ||||
|                 text="Open in browser" | ||||
|                 :href="getAttachmentUrl(item.id)" | ||||
|                 target="_blank" | ||||
|               /> | ||||
|             </div> | ||||
|           </v-col> | ||||
|         </v-row> | ||||
|       </v-sheet> | ||||
|       <attachment-renderer :item="item" /> | ||||
|     </v-carousel-item> | ||||
|   </v-carousel> | ||||
| </template> | ||||
|   | ||||
							
								
								
									
										61
									
								
								components/attachment/Renderer.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								components/attachment/Renderer.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| <template> | ||||
|   <v-sheet v-if="item.is_mature && !showMature" color="rgba(0, 0, 0, .4)" height="calc(100% + 24px)" class="p-5"> | ||||
|     <v-row class="fill-height" align="center" justify="center"> | ||||
|       <v-col class="text-center"> | ||||
|         <h1 class="text-xl font-bold text-white">Mature Content</h1> | ||||
|         <p class="text-md text-white">This content is rated and may not suitable for everyone to view.</p> | ||||
|  | ||||
|         <div class="flex justify-center mt-3"> | ||||
|           <v-btn | ||||
|             variant="text" | ||||
|             color="white" | ||||
|             prepend-icon="mdi-eye" | ||||
|             text="Reveal" | ||||
|             @click="showMature = true" | ||||
|           /> | ||||
|         </div> | ||||
|       </v-col> | ||||
|     </v-row> | ||||
|   </v-sheet> | ||||
|   <v-img v-else-if="item.mimetype.split('/')[0] == 'image'" :src="getAttachmentUrl(item.id)" class="w-full h-full" | ||||
|          cover /> | ||||
|   <video v-else-if="item.mimetype.split('/')[0] == 'video'" :src="getAttachmentUrl(item.id)" class="w-full h-full" | ||||
|          controls /> | ||||
|   <v-sheet v-else color="rgba(0, 0, 0, .4)" height="calc(100% + 24px)" class="p-5"> | ||||
|     <v-row class="fill-height" align="center" justify="center"> | ||||
|       <v-col class="text-center"> | ||||
|         <h1 class="text-xl font-bold text-white"> | ||||
|           {{ item.alt }} | ||||
|         </h1> | ||||
|         <p class="text-md text-white">{{ item.mimetype }}</p> | ||||
|  | ||||
|         <p class="text-sm text-white mt-3">Unable to preview, you can open it via other ways.</p> | ||||
|  | ||||
|         <div class="flex justify-center mt-3"> | ||||
|           <v-btn | ||||
|             variant="text" | ||||
|             color="white" | ||||
|             prepend-icon="mdi-launch" | ||||
|             text="Open in browser" | ||||
|             :href="getAttachmentUrl(item.id)" | ||||
|             target="_blank" | ||||
|           /> | ||||
|         </div> | ||||
|       </v-col> | ||||
|     </v-row> | ||||
|   </v-sheet> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const props = defineProps<{ item: any }>() | ||||
|  | ||||
| const item = computed(() => props.item) | ||||
|  | ||||
| const showMature = ref(false) | ||||
|  | ||||
| function getAttachmentUrl(id: number) { | ||||
|   return `${config.public.solarNetworkApi}/cgi/files/attachments/${id}` | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										131
									
								
								pages/gallery/[id].vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								pages/gallery/[id].vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | ||||
| <template> | ||||
|   <v-container class="content-container mx-auto"> | ||||
|     <div class="mt-3 mb-4.5 mx-[2.5ch] flex flex-row gap-4 items-center"> | ||||
|       <v-avatar :image="attachment.account?.avatar" /> | ||||
|       <div class="flex flex-col"> | ||||
|         <span class="text-xs">Uploaded by</span> | ||||
|         <span>{{ attachment.account?.nick }} <span class="text-xs">@{{ attachment.account?.name }}</span></span> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <h2 class="section-header">Preview</h2> | ||||
|     <v-card class="mb-5"> | ||||
|       <attachment-renderer :item="attachment" /> | ||||
|     </v-card> | ||||
|  | ||||
|     <h2 class="section-header">Metadata</h2> | ||||
|     <v-card class="mb-5"> | ||||
|       <v-card-text class="flex flex-col gap-4"> | ||||
|         <div class="flex flex-col" v-if="attachment?.alt"> | ||||
|           <span class="text-xs font-bold">Alternative</span> | ||||
|           <span class="text-truncate">{{ attachment?.alt }}</span> | ||||
|         </div> | ||||
|         <div class="flex flex-col"> | ||||
|           <span class="text-xs font-bold">Original File Name</span> | ||||
|           <span class="text-truncate">{{ attachment?.name }}</span> | ||||
|         </div> | ||||
|         <div class="flex flex-col"> | ||||
|           <span class="text-xs font-bold">Size</span> | ||||
|           <span>{{ formatBytes(attachment?.size) }}</span> | ||||
|         </div> | ||||
|         <div class="flex flex-col" v-if="attachment?.metadata?.ratio"> | ||||
|           <span class="text-xs font-bold">Aspect Ratio</span> | ||||
|           <span> | ||||
|             {{ attachment?.metadata?.width }}x{{ attachment?.metadata?.height }} | ||||
|             {{ attachment?.metadata?.ratio.toFixed(2) }} | ||||
|           </span> | ||||
|         </div> | ||||
|         <div class="flex flex-col" v-if="attachment?.mimetype"> | ||||
|           <span class="text-xs font-bold">Mimetype</span> | ||||
|           <span>{{ attachment?.mimetype }}</span> | ||||
|         </div> | ||||
|         <div class="flex flex-col"> | ||||
|           <span class="text-xs font-bold">Raw Data</span> | ||||
|           <v-code class="font-mono mt-1">{{ JSON.stringify(attachment.metadata, null, 4) }}</v-code> | ||||
|         </div> | ||||
|       </v-card-text> | ||||
|     </v-card> | ||||
|  | ||||
|     <div class="text-xs text-grey flex flex-col mx-[2.5ch]"> | ||||
|       <span>Solar Network Attachment Web Preview</span> | ||||
|       <span>Powered by <a class="underline" target="_blank" href="https://git.solsynth.dev/Hydrogen/Paperclip">Hydrogen.Paperclip</a></span> | ||||
|     </div> | ||||
|   </v-container> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| const route = useRoute() | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const firstImage = ref<string | null>() | ||||
| const firstVideo = ref<string | null>() | ||||
|  | ||||
| const { data: attachment } = await useFetch<any>(`${config.public.solarNetworkApi}/cgi/files/attachments/${route.params.id}/meta`) | ||||
|  | ||||
| if (!attachment.value) { | ||||
|   throw createError({ | ||||
|     statusCode: 404, | ||||
|     statusMessage: "Attachment Not Found", | ||||
|   }) | ||||
| } | ||||
|  | ||||
| const title = computed(() => `Attachment from ${attachment.value.account.nick}`) | ||||
|  | ||||
| watch(attachment, (value) => { | ||||
|   if (value.mimetype.split("/")[0] == "image") { | ||||
|     firstImage.value = `${config.public.solarNetworkApi}/cgi/files/attachments/${value.id}` | ||||
|   } | ||||
|  | ||||
|   if (value.mimetype.split("/")[0] == "video") { | ||||
|     firstVideo.value = `${config.public.solarNetworkApi}/cgi/files/attachments/${value.id}` | ||||
|   } | ||||
| }, { immediate: true, deep: true }) | ||||
|  | ||||
| useHead({ | ||||
|   title: title.value, | ||||
|   titleTemplate: "%s on Solar Network", | ||||
|   link: [ | ||||
|     { rel: "icon", type: "image/png", href: "/favicon-solian.png" }, | ||||
|     { rel: "apple-touch-icon", type: "image/png", href: "/favicon-solian.png" }, | ||||
|   ], | ||||
| }) | ||||
|  | ||||
| useSeoMeta({ | ||||
|   author: attachment.value?.account.nick, | ||||
|   title: title, | ||||
|   description: attachment.value?.alt, | ||||
|   ogTitle: title, | ||||
|   ogDescription: attachment.value?.alt, | ||||
|   ogUrl: `${useRuntimeConfig().public.siteUrl}${route.fullPath}`, | ||||
|   ogImage: firstImage, | ||||
|   ogVideo: firstVideo, | ||||
|   publisher: "Solar Network", | ||||
|   ogSiteName: "Solsynth Capital", | ||||
| }) | ||||
|  | ||||
| function formatBytes(bytes: number, decimals = 2) { | ||||
|   if (!+bytes) return "0 Bytes" | ||||
|  | ||||
|   const k = 1024 | ||||
|   const dm = decimals < 0 ? 0 : decimals | ||||
|   const sizes = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] | ||||
|  | ||||
|   const i = Math.floor(Math.log(bytes) / Math.log(k)) | ||||
|  | ||||
|   return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}` | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .content-container { | ||||
|   max-width: 70ch !important; | ||||
| } | ||||
|  | ||||
| .section-header { | ||||
|   margin-left: 2.5ch; | ||||
|   margin-right: 2.5ch; | ||||
|   margin-bottom: 8px; | ||||
|  | ||||
|   @apply text-lg; | ||||
| } | ||||
| </style> | ||||
| @@ -6,34 +6,8 @@ | ||||
|     </div> | ||||
|  | ||||
|     <div class="album"> | ||||
|       <v-card v-for="item in items" class="album-item mb-3"> | ||||
|         <v-img v-if="item.mimetype.split('/')[0] == 'image'" :src="getAttachmentUrl(item.id)" class="w-full h-full" | ||||
|                cover /> | ||||
|         <video v-else-if="item.mimetype.split('/')[0] == 'video'" :src="getAttachmentUrl(item.id)" class="w-full h-full" | ||||
|                controls /> | ||||
|         <v-sheet v-else color="rgba(0, 0, 0, .4)" height="calc(100% + 24px)" class="p-5"> | ||||
|           <v-row class="fill-height" align="center" justify="center"> | ||||
|             <v-col class="text-center"> | ||||
|               <h1 class="text-xl font-bold text-white"> | ||||
|                 {{ item.alt }} | ||||
|               </h1> | ||||
|               <p class="text-md text-white">{{ item.mimetype }}</p> | ||||
|  | ||||
|               <p class="text-sm text-white mt-3">Unable to preview, you can open it via other ways.</p> | ||||
|  | ||||
|               <div class="flex justify-center mt-3"> | ||||
|                 <v-btn | ||||
|                   variant="text" | ||||
|                   color="white" | ||||
|                   prepend-icon="mdi-launch" | ||||
|                   text="Open in browser" | ||||
|                   :href="getAttachmentUrl(item.id)" | ||||
|                   target="_blank" | ||||
|                 /> | ||||
|               </div> | ||||
|             </v-col> | ||||
|           </v-row> | ||||
|         </v-sheet> | ||||
|       <v-card v-for="item in items" class="album-item mb-3" :to="`/gallery/${item.id}`"> | ||||
|         <attachment-renderer :item="item" /> | ||||
|       </v-card> | ||||
|  | ||||
|       <div class="flex p-5 justify-center items-center"> | ||||
|   | ||||
| @@ -57,8 +57,8 @@ const route = useRoute() | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const attachments = ref<any[]>([]) | ||||
| const firstImage = ref<string|null>() | ||||
| const firstVideo = ref<string|null>() | ||||
| const firstImage = ref<string | null>() | ||||
| const firstVideo = ref<string | null>() | ||||
|  | ||||
| const { data: post } = await useFetch<any>(`${config.public.solarNetworkApi}/cgi/interactive/posts/${route.params.id}`) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user