feat: sites and authors integration into existing screens (& formated all frontend files kinda accidentally)
This commit is contained in:
1 parent
3d81d2c717
commit
6b8990c55e
239 files changed
+9194
-1354
No files matched your search
@@ -0,0 +1,155 @@
|
||||
<script setup lang="ts">
|
||||
import { t } from '@/i18n'
|
||||
|
||||
import { computed, ref, useId } from 'vue'
|
||||
import AuthorList from '@/components/AuthorList.vue'
|
||||
import AuthorSelect from '@/components/AuthorSelect.vue'
|
||||
import CreateAuthorDialog from '@/components/CreateAuthorDialog.vue'
|
||||
import CreateAuthorHint from '@/components/CreateAuthorHint.vue'
|
||||
import SiteSelect from '@/components/SiteSelect.vue'
|
||||
import type { Byline } from '@/composables/useArticles'
|
||||
import type { Author } from '@/composables/useAuthors'
|
||||
import { useMyAuthorProfile } from '@/composables/useMyAuthorProfile'
|
||||
import type { Site } from '@/composables/useSites'
|
||||
import { metaSummary } from '@/lib/articleMeta'
|
||||
import { readHintDismissed, storeHintDismissed } from '@/lib/metaSectionMemory'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
sites: Site[]
|
||||
siteId: number | null
|
||||
authors: Byline[]
|
||||
remembered: boolean | null
|
||||
sheet?: boolean
|
||||
}>(),
|
||||
{ sheet: false },
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:siteId': [siteId: number]
|
||||
'update:authors': [authors: Byline[]]
|
||||
'update:remembered': [open: boolean]
|
||||
}>()
|
||||
|
||||
const session = useSessionStore()
|
||||
const profile = useMyAuthorProfile()
|
||||
const bodyId = useId()
|
||||
|
||||
const startedWithoutAuthors = props.authors.length === 0
|
||||
const autoOpen = computed(() => !props.sheet && (props.sites.length > 1 || startedWithoutAuthors))
|
||||
const isOpen = computed(() => props.remembered ?? autoOpen.value)
|
||||
const hintDismissed = ref(readHintDismissed(session.session?.id))
|
||||
const comboOpen = ref(false)
|
||||
const creating = ref<string | null>(null)
|
||||
|
||||
const severalSites = computed(() => props.sites.length > 1)
|
||||
const siteName = computed(() => props.sites.find((site) => site.id === props.siteId)?.name ?? null)
|
||||
const summary = computed(() =>
|
||||
metaSummary(props.authors, severalSites.value ? siteName.value : null),
|
||||
)
|
||||
const canCreate = computed(() => session.canManageContent)
|
||||
const showHint = computed(
|
||||
() =>
|
||||
session.session !== null &&
|
||||
session.session.author === undefined &&
|
||||
props.authors.length === 0 &&
|
||||
!hintDismissed.value,
|
||||
)
|
||||
|
||||
function toByline(author: Author): Byline {
|
||||
return { id: author.id, name: author.name, slug: author.slug }
|
||||
}
|
||||
|
||||
function dismissHint() {
|
||||
hintDismissed.value = true
|
||||
storeHintDismissed(session.session?.id)
|
||||
}
|
||||
|
||||
async function createMine() {
|
||||
const outcome = await profile.create()
|
||||
if (outcome.kind === 'created') {
|
||||
emit('update:authors', [toByline(outcome.author), ...props.authors])
|
||||
}
|
||||
if (outcome.kind === 'failed') {
|
||||
profile.announceFailed()
|
||||
}
|
||||
}
|
||||
|
||||
function append(author: Author) {
|
||||
if (props.authors.some((existing) => existing.id === author.id)) {
|
||||
return
|
||||
}
|
||||
emit('update:authors', [...props.authors, toByline(author)])
|
||||
}
|
||||
|
||||
function onCreated(author: Author) {
|
||||
creating.value = null
|
||||
append(author)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="flex flex-col gap-3 border-b border-default pb-4" data-testid="meta-section">
|
||||
<UCollapsible :open="isOpen" @update:open="emit('update:remembered', $event)">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="link"
|
||||
class="min-h-6 w-full justify-start gap-2 p-0 text-sm font-semibold text-highlighted"
|
||||
:aria-controls="bodyId"
|
||||
data-testid="meta-toggle"
|
||||
>
|
||||
<UIcon
|
||||
name="i-lucide-chevron-down"
|
||||
class="size-3.5 shrink-0 transition-transform"
|
||||
:class="{ '-rotate-90': !isOpen }"
|
||||
/>
|
||||
{{ t('Site and authors') }}
|
||||
</UButton>
|
||||
<template #content>
|
||||
<div :id="bodyId" class="mt-3 flex flex-col gap-4" data-testid="meta-body">
|
||||
<CreateAuthorHint
|
||||
v-if="showHint"
|
||||
:pending="profile.pending.value"
|
||||
@create="createMine"
|
||||
@dismiss="dismissHint"
|
||||
/>
|
||||
<SiteSelect
|
||||
v-if="severalSites"
|
||||
:sites="sites"
|
||||
:model-value="siteId"
|
||||
@update:model-value="(id) => id !== null && emit('update:siteId', id)"
|
||||
/>
|
||||
<AuthorList
|
||||
:authors="authors"
|
||||
:hint-hidden="comboOpen"
|
||||
@update:authors="emit('update:authors', $event)"
|
||||
>
|
||||
<template #add>
|
||||
<AuthorSelect
|
||||
v-model:open="comboOpen"
|
||||
:authors="authors"
|
||||
:can-create="canCreate"
|
||||
@pick="append"
|
||||
@create="creating = $event"
|
||||
/>
|
||||
</template>
|
||||
</AuthorList>
|
||||
</div>
|
||||
</template>
|
||||
</UCollapsible>
|
||||
<span
|
||||
v-if="!isOpen"
|
||||
class="truncate pl-5.5 text-muted"
|
||||
data-testid="meta-summary"
|
||||
:title="summary"
|
||||
>{{ summary }}</span
|
||||
>
|
||||
<CreateAuthorDialog
|
||||
v-if="creating !== null"
|
||||
:initial-name="creating"
|
||||
@close="creating = null"
|
||||
@created="onCreated"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
Reference in new issue
Block a user