Coves frontend - a photon fork
at main 160 lines 4.1 kB view raw
1<script lang="ts"> 2 import type { PostView } from '$lib/api/coves/types' 3 import { type View, settings } from '$lib/app/settings.svelte' 4 import { publishedToDate } from '$lib/ui/util/date' 5 import type { ClassValue } from 'svelte/elements' 6 import { 7 PostActions, 8 PostBody, 9 PostMedia, 10 PostMediaCompact, 11 PostMeta, 12 } from '.' 13 import { extractEmbedTitle, extractEmbedUrl, mediaType } from './helpers' 14 import { type MetaTag, parseTags } from './PostMeta.svelte' 15 16 interface Props { 17 post: PostView 18 actions?: boolean 19 hideCommunity?: boolean 20 pinned?: boolean 21 view?: View 22 style?: string 23 class?: ClassValue 24 extraBadges?: import('svelte').Snippet 25 } 26 27 let { 28 post = $bindable(), 29 actions = true, 30 hideCommunity = false, 31 pinned = false, 32 view = settings.view, 33 style = '', 34 class: clazz = '', 35 extraBadges, 36 }: Props = $props() 37 38 let tags = $derived.by<{ title?: string; tags: MetaTag[] }>(() => { 39 const parsed = parseTags(post.record?.title) 40 41 return { 42 title: parsed.title, 43 tags: parsed.tags, 44 } 45 }) 46 let type = $derived(mediaType(post.embed)) 47 let embedUrl = $derived(extractEmbedUrl(post.embed)) 48 let embedTitle = $derived(extractEmbedTitle(post.embed)) 49 let hideTitle = $derived( 50 settings.posts.deduplicateEmbed && 51 embedTitle === post.record?.title && 52 view !== 'compact' && 53 type !== 'iframe', 54 ) 55 56 let badges = $derived({ 57 featured: pinned, 58 saved: post.viewer?.saved ?? false, 59 }) 60</script> 61 62<!-- 63 @component 64 This is the sole component for displaying posts. 65 It adapts to all kinds of form factors for different contexts, such as feeds, full post view, and crosspost list. 66--> 67<article 68 class={[ 69 'relative group/post', 70 settings.leftAlign && 'left-align', 71 view == 'compact' && 'py-3 list-type compact', 72 view == 'cozy' && 'py-5 flex flex-col gap-2', 73 clazz, 74 ]} 75 id={post.uri} 76 {style} 77> 78 <PostMeta 79 community={post.community} 80 showCommunity={!hideCommunity} 81 user={post.author} 82 published={publishedToDate(post.createdAt)} 83 {badges} 84 uri={post.uri} 85 title={hideTitle 86 ? undefined 87 : tags?.title 88 ? tags.title 89 : post.record?.title} 90 style="grid-area: meta;" 91 edited={post.editedAt} 92 tags={tags?.tags} 93 postUrl={embedUrl} 94 {view} 95 {extraBadges} 96 /> 97 {#key embedUrl} 98 <div style="grid-area:embed;" class={{ contents: view == 'cozy' }}> 99 <PostMedia embed={post.embed} {view} {type} /> 100 </div> 101 {#if view == 'compact'} 102 <PostMediaCompact 103 embed={post.embed} 104 {type} 105 class="{settings.leftAlign ? 'mr-3' : 'ml-3'} shrink no-list-margin" 106 style="grid-area: media;" 107 {view} 108 /> 109 {/if} 110 {/key} 111 {#if post.record?.content && view != 'compact'} 112 <PostBody 113 element="section" 114 body={post.record.content} 115 style="grid-area: body" 116 class="relative" 117 /> 118 {/if} 119 {#if actions} 120 <PostActions bind:post style="grid-area: actions;" {view} /> 121 {/if} 122</article> 123 124<style> 125 .list-type { 126 display: grid; 127 grid-template-areas: 'meta media' 'title media' 'body media' 'embed embed' 'actions actions'; 128 grid-template-columns: minmax(0, 1fr) auto; 129 width: 100%; 130 height: 100%; 131 } 132 133 /* Swap media/item positions */ 134 .list-type.left-align { 135 grid-template-areas: 'media meta' 'media title' 'media body' 'embed embed' 'actions actions'; 136 grid-template-columns: auto minmax(0, 1fr); 137 } 138 139 /* Has media on the right for all of them */ 140 @media (min-width: 480px) { 141 .list-type.compact { 142 grid-template-areas: 'meta media' 'title media' 'body media' 'embed media' 'actions media'; 143 } 144 } 145 146 /* Swap above again */ 147 @media (min-width: 480px) { 148 .list-type.compact.left-align { 149 grid-template-areas: 'media meta' 'media title' 'media body' 'media embed' 'media actions'; 150 } 151 } 152 153 :global(.compact > *:not(.no-list-margin):not(:first-child)) { 154 margin-top: 0.3rem; 155 } 156 157 :global(.list-type:not(.compact) > *:not(.no-list-margin):not(:first-child)) { 158 margin-top: 0.5rem; 159 } 160</style>