feat: threads improvements (#562)
parent
baa2696d31
commit
d9e8703882
|
@ -9,6 +9,7 @@ const props = withDefaults(
|
||||||
hover?: boolean
|
hover?: boolean
|
||||||
faded?: boolean
|
faded?: boolean
|
||||||
showReplyTo?: boolean
|
showReplyTo?: boolean
|
||||||
|
connectReply?: boolean
|
||||||
}>(),
|
}>(),
|
||||||
{ actions: true, showReplyTo: true },
|
{ actions: true, showReplyTo: true },
|
||||||
)
|
)
|
||||||
|
@ -60,6 +61,7 @@ const avatarOnAvatar = $(computedEager(() => useFeatureFlags().experimentalAvata
|
||||||
const showRebloggedByAvatarOnAvatar = $computed(() => rebloggedBy && avatarOnAvatar && rebloggedBy.id !== status.account.id)
|
const showRebloggedByAvatarOnAvatar = $computed(() => rebloggedBy && avatarOnAvatar && rebloggedBy.id !== status.account.id)
|
||||||
|
|
||||||
const isDM = $computed(() => status.visibility === 'direct')
|
const isDM = $computed(() => status.visibility === 'direct')
|
||||||
|
const isSelf = $computed(() => status.account.id === currentUser.value?.account.id)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -84,6 +86,9 @@ const isDM = $computed(() => status.visibility === 'direct')
|
||||||
<div v-if="showRebloggedByAvatarOnAvatar" absolute class="-top-1 -left-2" w-9 h-9 border-bg-base border-3 rounded-full>
|
<div v-if="showRebloggedByAvatarOnAvatar" absolute class="-top-1 -left-2" w-9 h-9 border-bg-base border-3 rounded-full>
|
||||||
<AccountAvatar :account="rebloggedBy" />
|
<AccountAvatar :account="rebloggedBy" />
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="connectReply" w-full h-full flex justify-center>
|
||||||
|
<div h-full class="w-2.5px" bg-border />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div flex="~ col 1" min-w-0>
|
<div flex="~ col 1" min-w-0>
|
||||||
<div flex items-center space-x-1>
|
<div flex items-center space-x-1>
|
||||||
|
@ -104,39 +109,10 @@ const isDM = $computed(() => status.visibility === 'direct')
|
||||||
</div>
|
</div>
|
||||||
<StatusActionsMore :status="status" mr--2 />
|
<StatusActionsMore :status="status" mr--2 />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<StatusContent :status="status" :context="context" mb2 :class="{ mt2: isDM }" />
|
||||||
space-y-3
|
<div>
|
||||||
:class="{
|
<StatusActions v-if="(actions !== false && !isZenMode)" :status="status" />
|
||||||
'mt2 pt1 pb0.5 px3.5 br2 bg-fade border-primary-light border-1 rounded-3 rounded-tl-none': isDM,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<StatusSpoiler :enabled="status.sensitive || isFiltered" :filter="isFiltered">
|
|
||||||
<template v-if="status.spoilerText || filterPhrase" #spoiler>
|
|
||||||
<p>{{ status.spoilerText || `${$t('status.filter_hidden_phrase')}: ${filterPhrase}` }}</p>
|
|
||||||
</template>
|
|
||||||
<StatusBody :status="status" />
|
|
||||||
<StatusPoll
|
|
||||||
v-if="status.poll"
|
|
||||||
:poll="status.poll"
|
|
||||||
/>
|
|
||||||
<StatusMedia
|
|
||||||
v-if="status.mediaAttachments?.length"
|
|
||||||
:status="status"
|
|
||||||
/>
|
|
||||||
<StatusPreviewCard
|
|
||||||
v-if="status.card"
|
|
||||||
:card="status.card"
|
|
||||||
:small-picture-only="status.mediaAttachments?.length > 0"
|
|
||||||
/>
|
|
||||||
<StatusCard
|
|
||||||
v-if="status.reblog"
|
|
||||||
:status="status.reblog" border="~ rounded"
|
|
||||||
:actions="false"
|
|
||||||
/>
|
|
||||||
<div v-if="isDM" />
|
|
||||||
</StatusSpoiler>
|
|
||||||
</div>
|
</div>
|
||||||
<StatusActions v-if="(actions !== false && !isZenMode)" :status="status" :class="isDM ? 'mt1' : 'mt2'" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { FilterContext, Status } from 'masto'
|
||||||
|
|
||||||
|
const { status, context } = defineProps<{
|
||||||
|
status: Status
|
||||||
|
context?: FilterContext | 'details'
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isDM = $computed(() => status.visibility === 'direct')
|
||||||
|
const isSelf = $computed(() => status.account.id === currentUser.value?.account.id)
|
||||||
|
const isDetails = $computed(() => context === 'details')
|
||||||
|
|
||||||
|
// Content Filter logic
|
||||||
|
const filterResult = $computed(() => status.filtered?.length ? status.filtered[0] : null)
|
||||||
|
const filter = $computed(() => filterResult?.filter)
|
||||||
|
|
||||||
|
// a bit of a hack due to Filter being different in v1 and v2
|
||||||
|
// clean up when masto.js supports explicit versions: https://github.com/neet/masto.js/issues/722
|
||||||
|
const filterPhrase = $computed(() => filter?.phrase || (filter as any)?.title)
|
||||||
|
const isFiltered = $computed(() => filterPhrase && (context && context !== 'details' ? filter?.context.includes(context) : false))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
space-y-3
|
||||||
|
:class="{
|
||||||
|
'pt2 pb0.5 px3.5 br2 border-1 rounded-3 rounded-tl-none': isDM,
|
||||||
|
'bg-fade border-primary-light': isDM && !isSelf,
|
||||||
|
'bg-code border-base': isDM && isSelf,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<StatusSpoiler :enabled="status.sensitive || isFiltered" :filter="isFiltered">
|
||||||
|
<template v-if="status.spoilerText || filterPhrase" #spoiler>
|
||||||
|
<p>{{ status.spoilerText || `${$t('status.filter_hidden_phrase')}: ${filterPhrase}` }}</p>
|
||||||
|
</template>
|
||||||
|
<StatusBody :status="status" :with-action="!isDetails" :class="isDetails ? 'text-xl' : ''" />
|
||||||
|
<StatusPoll
|
||||||
|
v-if="status.poll"
|
||||||
|
:poll="status.poll"
|
||||||
|
/>
|
||||||
|
<StatusMedia
|
||||||
|
v-if="status.mediaAttachments?.length"
|
||||||
|
:status="status"
|
||||||
|
/>
|
||||||
|
<StatusPreviewCard
|
||||||
|
v-if="status.card"
|
||||||
|
:card="status.card"
|
||||||
|
:small-picture-only="status.mediaAttachments?.length > 0"
|
||||||
|
/>
|
||||||
|
<StatusCard
|
||||||
|
v-if="status.reblog"
|
||||||
|
:status="status.reblog" border="~ rounded"
|
||||||
|
:actions="false"
|
||||||
|
/>
|
||||||
|
<div v-if="isDM" />
|
||||||
|
</StatusSpoiler>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,10 +1,13 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Status } from 'masto'
|
import type { Status } from 'masto'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
status: Status
|
status: Status
|
||||||
command?: boolean
|
command?: boolean
|
||||||
}>()
|
actions?: boolean
|
||||||
|
}>(), {
|
||||||
|
actions: true,
|
||||||
|
})
|
||||||
|
|
||||||
const status = $computed(() => {
|
const status = $computed(() => {
|
||||||
if (props.status.reblog && props.status.reblog)
|
if (props.status.reblog && props.status.reblog)
|
||||||
|
@ -33,37 +36,7 @@ const isDM = $computed(() => status.visibility === 'direct')
|
||||||
<AccountInfo :account="status.account" />
|
<AccountInfo :account="status.account" />
|
||||||
</AccountHoverWrapper>
|
</AccountHoverWrapper>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<div
|
<StatusContent :status="status" context="details" />
|
||||||
space-y-3
|
|
||||||
:class="{
|
|
||||||
'pt2 pb0.5 px3.5 br2 bg-fade border-primary-light border-1 rounded-3': isDM,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<StatusSpoiler :enabled="status.sensitive">
|
|
||||||
<template #spoiler>
|
|
||||||
<p text-2xl>
|
|
||||||
{{ status.spoilerText }}
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<StatusBody :status="status" :with-action="false" text-xl />
|
|
||||||
<StatusPoll
|
|
||||||
v-if="status.poll"
|
|
||||||
:poll="status.poll"
|
|
||||||
/>
|
|
||||||
<StatusMedia
|
|
||||||
v-if="status.mediaAttachments?.length"
|
|
||||||
:status="status"
|
|
||||||
full-size
|
|
||||||
/>
|
|
||||||
<StatusPreviewCard
|
|
||||||
v-if="status.card"
|
|
||||||
:card="status.card"
|
|
||||||
:small-picture-only="status.mediaAttachments?.length > 0"
|
|
||||||
mt-2
|
|
||||||
/>
|
|
||||||
<div v-if="isDM" />
|
|
||||||
</StatusSpoiler>
|
|
||||||
</div>
|
|
||||||
<div flex="~ gap-1" items-center text-secondary text-sm>
|
<div flex="~ gap-1" items-center text-secondary text-sm>
|
||||||
<div flex>
|
<div flex>
|
||||||
<div>{{ createdAt }}</div>
|
<div>{{ createdAt }}</div>
|
||||||
|
@ -85,6 +58,8 @@ const isDM = $computed(() => status.visibility === 'direct')
|
||||||
{{ status.application?.name }}
|
{{ status.application?.name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<StatusActions :status="status" details :command="command" border="t base" pt-2 />
|
<div border="t base" pt-2>
|
||||||
|
<StatusActions v-if="actions" :status="status" details :command="command" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -59,35 +59,38 @@ onReactivated(() => {
|
||||||
<MainContent back>
|
<MainContent back>
|
||||||
<template v-if="!pending">
|
<template v-if="!pending">
|
||||||
<div v-if="status" min-h-100vh mt--1px>
|
<div v-if="status" min-h-100vh mt--1px>
|
||||||
<template v-if="context">
|
<template v-for="comment of context?.ancestors" :key="comment.id">
|
||||||
<template v-for="comment of context?.ancestors" :key="comment.id">
|
<StatusCard
|
||||||
<StatusCard :status="comment" context="account" border="t base" :show-reply-to="false" />
|
:status="comment" :actions="comment.visibility !== 'direct'" context="account"
|
||||||
</template>
|
:show-reply-to="false" :connect-reply="true"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<StatusDetails
|
<StatusDetails
|
||||||
ref="main"
|
ref="main"
|
||||||
:status="status"
|
:status="status"
|
||||||
command
|
command
|
||||||
border="t base"
|
|
||||||
style="scroll-margin-top: 60px"
|
style="scroll-margin-top: 60px"
|
||||||
|
:actions="status.visibility !== 'direct'"
|
||||||
/>
|
/>
|
||||||
<PublishWidget
|
<PublishWidget
|
||||||
v-if="currentUser"
|
v-if="currentUser"
|
||||||
ref="publishWidget"
|
ref="publishWidget"
|
||||||
:draft-key="replyDraft!.key"
|
:draft-key="replyDraft!.key"
|
||||||
:initial="replyDraft!.draft"
|
:initial="replyDraft!.draft"
|
||||||
border="t base"
|
|
||||||
@published="refreshContext()"
|
@published="refreshContext()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<template v-if="context">
|
<template v-for="comment, di of context?.descendants" :key="comment.id">
|
||||||
<template v-for="comment of context?.descendants" :key="comment.id">
|
<StatusCard
|
||||||
<StatusCard :status="comment" context="account" border="t base" />
|
:status="comment" :actions="comment.visibility !== 'direct'" context="account"
|
||||||
</template>
|
:connect-reply="comment.id === context?.descendants[di + 1]?.inReplyToId"
|
||||||
|
:show-reply-to="di !== 0 && comment.inReplyToId !== context?.descendants[di - 1]?.id"
|
||||||
|
:class="{ 'border-t border-base': di !== 0 && comment.inReplyToId !== context?.descendants[di - 1]?.id }"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div border="t base" :style="{ height: `${bottomSpace}px` }" />
|
<div :style="{ height: `${bottomSpace}px` }" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<StatusNotFound v-else :account="$route.params.account" :status="id" />
|
<StatusNotFound v-else :account="$route.params.account" :status="id" />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
:root {
|
:root {
|
||||||
--c-primary: #EA9E44;
|
--c-primary: #EA9E44;
|
||||||
--c-primary-active: #C16929;
|
--c-primary-active: #C16929;
|
||||||
--c-primary-light: #EA9E4466;
|
--c-primary-light: #EA9E441A;
|
||||||
--c-border: #eee;
|
--c-border: #eee;
|
||||||
|
|
||||||
--c-bg-base: #fff;
|
--c-bg-base: #fff;
|
||||||
|
|
|
@ -19,6 +19,7 @@ export default defineConfig({
|
||||||
|
|
||||||
// background
|
// background
|
||||||
'bg-base': 'bg-$c-bg-base',
|
'bg-base': 'bg-$c-bg-base',
|
||||||
|
'bg-border': 'bg-$c-border',
|
||||||
'bg-active': 'bg-$c-bg-active',
|
'bg-active': 'bg-$c-bg-active',
|
||||||
'bg-code': 'bg-$c-bg-code',
|
'bg-code': 'bg-$c-bg-code',
|
||||||
'bg-fade': 'bg-$c-bg-fade',
|
'bg-fade': 'bg-$c-bg-fade',
|
||||||
|
|
Loading…
Reference in New Issue