Coves frontend - a photon fork
1<script lang="ts">
2 import type { CommunityViewDetailed } from '$lib/api/coves/types'
3 import { coves } from '$lib/api/client.svelte'
4 import { profile } from '$lib/app/auth.svelte'
5 import { t } from '$lib/app/i18n'
6 import EntityHeader from '$lib/ui/generic/EntityHeader.svelte'
7 import { action, Button, Menu, MenuButton, modal, toast } from 'mono-svelte'
8 import { formatRelativeDate } from 'mono-svelte/util/RelativeDate.svelte'
9 import {
10 Check,
11 Cog6Tooth,
12 EllipsisHorizontal,
13 Fire,
14 Icon,
15 Newspaper,
16 Plus,
17 ShieldCheck,
18 } from 'svelte-hero-icons/dist'
19 import { purgeCommunity } from './CommunityCard.svelte'
20 import { communityDisplayName, communityIdentifier } from './helpers'
21
22 interface Props {
23 community: CommunityViewDetailed
24 banner?: boolean
25 class?: string
26 compact?: 'always' | 'lg'
27 avatarCircle?: boolean
28 }
29
30 let {
31 community = $bindable(),
32 banner = true,
33 class: clazz = '',
34 compact,
35 ...rest
36 }: Props = $props()
37
38 let subscribing = $state(false)
39
40 async function handleSubscribe(): Promise<void> {
41 if (!profile.current?.jwt) return
42 subscribing = true
43
44 const wasSubscribed = community.viewer?.subscribed === true
45
46 try {
47 if (wasSubscribed) {
48 await coves().unsubscribe({ community: community.did })
49 } else {
50 await coves().subscribe({ community: community.did })
51 }
52
53 // Toggle state only on success
54 if (community.viewer) {
55 community.viewer.subscribed = !wasSubscribed
56 } else {
57 community.viewer = { subscribed: !wasSubscribed }
58 }
59 } catch (err) {
60 const errorMsg = err instanceof Error ? err.message : String(err)
61 toast({ content: errorMsg, type: 'error' })
62 }
63
64 subscribing = false
65 }
66</script>
67
68<EntityHeader
69 {...rest}
70 {compact}
71 banner={banner ? community.banner : undefined}
72 avatar={community.avatar}
73 name={communityDisplayName(community)}
74 url="/c/{communityIdentifier(community)}"
75 stats={[
76 {
77 name: $t('cards.community.members'),
78 value: community.subscriberCount.toString(),
79 },
80 {
81 name: $t('content.posts'),
82 value: community.postCount.toString(),
83 },
84 {
85 name: $t('stats.created'),
86 format: false,
87 value: formatRelativeDate(new Date(community.createdAt), {
88 style: 'short',
89 }).toString(),
90 },
91 ]}
92 bio={community.description}
93 class={['tracking-normal', clazz]}
94>
95 {#snippet nameDetail()}
96 <button
97 onclick={() => {
98 navigator?.clipboard?.writeText?.(`!${communityIdentifier(community)}`)
99 toast({ content: $t('toast.copied') })
100 }}
101 class="text-sm flex gap-0 items-center"
102 >
103 !{communityIdentifier(community)}
104 </button>
105 {/snippet}
106 <div
107 class={[
108 'flex items-center gap-2 h-max w-max',
109 compact == 'lg' && 'lg:hidden',
110 ]}
111 >
112 {#if profile.current?.jwt}
113 {@const subscribed = community.viewer?.subscribed === true}
114 <Button
115 disabled={subscribing}
116 loading={subscribing}
117 color={!subscribed ? 'primary' : 'secondary'}
118 onclick={handleSubscribe}
119 class="relative z-[inherit]"
120 size="lg"
121 icon={subscribed ? Check : Plus}
122 >
123 {subscribed
124 ? $t('cards.community.subscribed')
125 : $t('cards.community.subscribe')}
126 </Button>
127 {/if}
128
129 {#if profile.isMod(community)}
130 <Button
131 color="secondary"
132 size="square-lg"
133 href="/c/{communityIdentifier(community)}/settings"
134 >
135 <Icon src={Cog6Tooth} size="16" mini />
136 </Button>
137 {/if}
138 <Menu placement="top-end">
139 {#snippet target(attachment)}
140 <Button {@attach attachment} size="square-lg" icon={EllipsisHorizontal}
141 ></Button>
142 {/snippet}
143 <MenuButton href="/modlog?community={community.did}">
144 <Icon src={Newspaper} size="16" mini />
145 {$t('cards.community.modlog')}
146 </MenuButton>
147 {#if profile.isMod(community)}
148 <MenuButton
149 color="success-subtle"
150 href="/moderation?community={community.did}"
151 >
152 <Icon src={ShieldCheck} size="16" micro />
153 {$t('routes.moderation.feed')}
154 </MenuButton>
155 {/if}
156 {#if profile.current?.jwt}
157 {#if profile.isAdmin}
158 <MenuButton
159 color="danger-subtle"
160 onclick={() =>
161 modal({
162 title: $t('admin.purgeCommunity.title'),
163 body: `${communityDisplayName(community)}: ${$t('admin.purgeCommunity.warning')}`,
164 actions: [
165 action({
166 close: true,
167 content: $t('common.cancel'),
168 }),
169 action({
170 action: () => purgeCommunity(community.did),
171 close: true,
172 content: $t('admin.purge'),
173 type: 'danger',
174 icon: Fire,
175 }),
176 ],
177 dismissable: true,
178 type: 'error',
179 })}
180 icon={Fire}
181 >
182 {$t('admin.purge')}
183 </MenuButton>
184 {/if}
185 {/if}
186 </Menu>
187 </div>
188</EntityHeader>