Merge branch 'chat-refactor' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2026-07-23 14:22:48 +03:00
commit 08f5d1d584
9 changed files with 110 additions and 20 deletions

View file

@ -14,6 +14,7 @@ import StatusPopover from 'src/components/status_popover/status_popover.vue'
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
import UserPopover from 'src/components/user_popover/user_popover.vue'
import Timeago from 'src/components/timeago/timeago.vue'
import EmojiReactions from 'src/components/emoji_reactions/emoji_reactions.vue'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInterfaceStore } from 'src/stores/interface'
@ -25,9 +26,11 @@ import {
faEllipsisH,
faTimes,
faReply,
faRetweet,
faStar,
} from '@fortawesome/free-solid-svg-icons'
library.add(faTimes, faEllipsisH, faCircleNotch, faReply)
library.add(faTimes, faEllipsisH, faCircleNotch, faReply, faStar, faRetweet)
const ChatMessage = {
name: 'ChatMessage',
@ -52,6 +55,7 @@ const ChatMessage = {
Gallery,
LinkPreview,
ChatMessageDate,
EmojiReactions,
UserPopover,
StatusPopover,
MentionLink,
@ -153,6 +157,11 @@ const ChatMessage = {
menuOpened: false,
}
},
watch: {
focused: function (value) {
this.scrollIfFocused(value)
},
},
methods: {
onHover(bool) {
this.$emit('hover', {
@ -188,6 +197,22 @@ const ChatMessage = {
this.hovered = false
this.menuOpened = false
},
scrollIfFocused(focused) {
if (this.$el.getBoundingClientRect == null) return
if (focused) {
const rect = this.$el.getBoundingClientRect()
if (rect.top < 100) {
// Post is above screen, match its top to screen top
window.scrollBy(0, rect.top - 100)
} else if (rect.height >= window.innerHeight - 50) {
// Post we want to see is taller than screen so match its top to screen top
window.scrollBy(0, rect.top - 100)
} else if (rect.bottom > window.innerHeight - 50) {
// Post is below screen, match its bottom to screen bottom
window.scrollBy(0, rect.bottom - window.innerHeight + 50)
}
}
},
},
}

View file

@ -11,6 +11,10 @@
}
}
.attachments {
min-width: 30em;
}
.chat-message-toolbar {
transition: opacity 0.1s;
opacity: 0;
@ -126,20 +130,26 @@
align-items: flex-start;
}
.end-spacer {
flex: 1 1 0;
min-width: calc(2.2rem + 0.5rem + 2rem);
}
.reply-indicator {
display: flex;
place-items: center;
place-content: center;
padding: 0.25em;
width: 1em;
margin: var(--roundness) 0;
background: var(--border);
border-radius: var(--roundness);
border: 1px solid var(--border);
}
.end-spacer {
flex: 1 1 0;
min-width: calc(2.2em + 0.5em + 2em);
+ .end-spacer {
// Compensate for reply indicator
min-width: calc(2.2rem + 0.5rem + 2rem - (1rem + (1px + 0.25rem) * 2));
}
}
&.-incoming {

View file

@ -44,22 +44,24 @@
</span>
</template>
<template #user>
<!-- v-if is there because status might not be loaded yet -->
<StatusBody
v-if="customReplyTo && customReplyTo.text.trim().length > 0"
class="reply-body faint"
:status="customReplyTo"
collapse
single-line
/>
<MentionLink
v-else-if="customReplyTo"
class="reply-body"
:content="replyToName"
:url="replyProfileLink"
:user-id="message.in_reply_to_user_id"
:user-screen-name="message.in_reply_to_screen_name"
/>
<!-- v-if is there because status might not be loaded yet -->
<template v-if="customReplyTo && customReplyTo.text.trim().length > 0">
:
<StatusBody
class="reply-body faint"
:status="customReplyTo"
collapse
single-line
ignore-subject
/>
</template>
</template>
</i18n-t>
<div
@ -143,9 +145,28 @@
:full-content="true"
>
<template #footer>
<EmojiReactions :status="message" />
<span
class="created-at"
>
<span
v-if="message.favorited"
>
<FAIcon
class="fa-scale-110"
icon="star"
fixed-width
/>
</span>
<span
v-if="message.repeated"
>
<FAIcon
class="fa-scale-110"
icon="retweet"
fixed-width
/>
</span>
<span
v-if="message.visibility"
class="visibility-icon"

View file

@ -7,6 +7,7 @@ import PostStatusForm from 'src/components/post_status_form/post_status_form.vue
import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filter_settings.vue'
import QuickViewSettings from 'src/components/quick_view_settings/quick_view_settings.vue'
import ThreadTree from 'src/components/thread_tree/thread_tree.vue'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
@ -426,6 +427,7 @@ const conversation = {
QuickViewSettings,
ChatMessageList,
PostStatusForm,
RichContent,
},
watch: {
statusId(newVal, oldVal) {

View file

@ -10,7 +10,13 @@
class="panel-heading conversation-heading -sticky"
>
<h1 class="title">
<RichContent
v-if="conversation[0]?.summary"
:html="conversation[0].summary"
/>
<template v-else>
{{ $t('timeline.conversation') }}
</template>
</h1>
<button
v-if="collapsable"

View file

@ -540,10 +540,9 @@ const Status = {
toggleThreadDisplay() {
this.controlledToggleThreadDisplay()
},
scrollIfFocused(focusedId) {
scrollIfFocused(focused) {
if (this.$el.getBoundingClientRect == null) return
const id = focusedId
if (this.status.id === id) {
if (focused) {
const rect = this.$el.getBoundingClientRect()
if (rect.top < 100) {
// Post is above screen, match its top to screen top

View file

@ -1,6 +1,7 @@
import { mapState } from 'pinia'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@ -15,6 +16,9 @@ library.add(faFile, faMusic, faImage, faLink, faPollH)
const StatusBody = {
name: 'StatusBody',
components: {
RichContent,
},
props: {
status: {
// Main thing
@ -44,6 +48,12 @@ const StatusBody = {
type: Boolean,
default: false,
},
ignoreSubject: {
// Pretend subject line doesn't exist. Useful for chat messages
// to indicate what post reply belongs to
type: Boolean,
default: false,
}
},
data() {
return {
@ -76,7 +86,7 @@ const StatusBody = {
return this.status.summary.length > 240
},
hasSubject() {
return !!this.status.summary
return !!this.status.summary && !this.ignoreSubject
},
// When a status has a subject and is also tall, we should only have one show more/less
// button. If the default is to collapse statuses with subjects, we just treat it like

View file

@ -92,6 +92,23 @@
}
}
&.-single-line {
.summary-wrapper {
display: inline-block;
margin: 0;
padding: 0;
border: none;
.summary {
padding: 0;
}
}
.text-wrapper {
display: inline-flex;
}
}
& .tall-status-hider,
& .tall-subject-hider,
& .status-unhider,

View file

@ -1,7 +1,7 @@
<template>
<div
class="StatusBody"
:class="{ '-compact': compact }"
:class="{ '-compact': compact, '-single-line': singleLine }"
>
<div class="body">
<div
@ -36,7 +36,7 @@
</div>
<div
class="text-wrapper"
:class="{'-tall-status': hideTallStatus, '-hidden': shouldHide, '-expanded': showingMore}"
:class="{'-tall-status': hideTallStatus, '-hidden': shouldHide, '-expanded': showingMore }"
>
<RichContent
v-if="!(singleLine && hasSubject) && !shouldHide"