UI improvements

This commit is contained in:
Henry Jameson 2026-08-14 18:54:18 +03:00
commit bcfe1de1b2
16 changed files with 94 additions and 40 deletions

View file

@ -7,7 +7,7 @@
class="avatars-item" class="avatars-item"
> >
<UserAvatar <UserAvatar
:user="user" :user-id="user.id"
class="avatar-small" class="avatar-small"
/> />
</router-link> </router-link>

View file

@ -5,7 +5,7 @@
> >
<div class="chat-list-item-left"> <div class="chat-list-item-left">
<UserAvatar <UserAvatar
:user="chat.account" :user-id="chat.account.id"
height="48px" height="48px"
width="48px" width="48px"
/> />

View file

@ -83,7 +83,7 @@
<UserAvatar <UserAvatar
v-if="author" v-if="author"
:compact="true" :compact="true"
:user="author" :user-id="author.id"
/> />
</UserPopover> </UserPopover>
<div <div

View file

@ -10,7 +10,7 @@
> >
<UserAvatar <UserAvatar
class="titlebar-avatar" class="titlebar-avatar"
:user="user" :user-id="user.id"
/> />
</UserPopover> </UserPopover>
<RichContent <RichContent

View file

@ -6,6 +6,11 @@ import { useInstanceStore } from 'src/stores/instance.js'
import { useInterfaceStore } from 'src/stores/interface' import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useUsersStore } from 'src/stores/users.js' import { useUsersStore } from 'src/stores/users.js'
import { useStreamingStore } from 'src/stores/streaming.js'
import {
WSConnectionStatus,
} from 'src/api/websocket.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
@ -20,6 +25,8 @@ import {
faSignOutAlt, faSignOutAlt,
faTachometerAlt, faTachometerAlt,
faUserPlus, faUserPlus,
faPlug,
faPlugCircleXmark
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
library.add( library.add(
@ -34,6 +41,8 @@ library.add(
faTachometerAlt, faTachometerAlt,
faCog, faCog,
faInfoCircle, faInfoCircle,
faPlug,
faPlugCircleXmark
) )
export default { export default {
@ -93,9 +102,23 @@ export default {
hideSitename: (store) => store.instanceIdentity.hideSitename, hideSitename: (store) => store.instanceIdentity.hideSitename,
}), }),
...mapState(useUsersStore, ['currentUser']), ...mapState(useUsersStore, ['currentUser']),
...mapState(useStreamingStore, {
streamingConnected: (store) => store.state === WSConnectionStatus.JOINED
}),
...mapState(useMergedConfigStore, ['mergedConfig']),
shouldConfirmLogout() { shouldConfirmLogout() {
return useMergedConfigStore().mergedConfig.modalOnLogout return this.mergedConfig.modalOnLogout
}, },
streamingEnabled() {
return this.mergedConfig.useStreamingApi
},
streamingTooltip() {
if (this.streamingConnected) {
return this.$t('timeline.socket_reconnected')
} else {
return this.$t('timeline.socket_disconnected')
}
}
}, },
methods: { methods: {
scrollToTop() { scrollToTop() {

View file

@ -15,6 +15,25 @@
> >
{{ sitename }} {{ sitename }}
</router-link> </router-link>
<div
class="nav-icon"
v-if="streamingEnabled"
:title="streamingTooltip"
>
<FAIcon
v-if="streamingConnected"
fixed-width
class="fa-scale-110 fa-old-padding"
icon="plug"
/>
<FAIcon
v-else
fixed-width
class="fa-scale-110 fa-old-padding"
icon="plug-circle-xmark"
/>
</div>
</div> </div>
<router-link <router-link
class="logo" class="logo"

View file

@ -31,7 +31,7 @@
<UserAvatar <UserAvatar
v-if="shouldShowAvatar" v-if="shouldShowAvatar"
class="mention-avatar" class="mention-avatar"
:user="user" :user-id="user.id"
/><span /><span
class="shortName" class="shortName"
>@<span >@<span

View file

@ -57,7 +57,7 @@
<UserAvatar <UserAvatar
class="post-avatar" class="post-avatar"
:compact="true" :compact="true"
:user="notification.from_profile" :user-id="notification.from_profile.id"
/> />
</UserPopover> </UserPopover>
</a> </a>

View file

@ -223,9 +223,6 @@ const Status = {
botStatus() { botStatus() {
return this.status.user.actor_type === 'Service' return this.status.user.actor_type === 'Service'
}, },
showActorTypeIndicator() {
return !this.hideBotIndication
},
sensitiveStatus() { sensitiveStatus() {
return this.status.nsfw return this.status.nsfw
}, },

View file

@ -55,8 +55,7 @@
<UserAvatar <UserAvatar
v-if="retweet" v-if="retweet"
class="left-side repeater-avatar" class="left-side repeater-avatar"
:show-actor-type-indicator="showActorTypeIndicator" :user-id="statusoid.user.id"
:user="statusoid.user"
/> />
<div class="right-side faint"> <div class="right-side faint">
<bdi <bdi
@ -114,15 +113,14 @@
> >
<UserAvatar <UserAvatar
class="post-avatar" class="post-avatar"
:show-actor-type-indicator="showActorTypeIndicator"
:compact="compact" :compact="compact"
:user="status?.user" :user-id="status?.user.id"
/> />
</UserPopover> </UserPopover>
</a> </a>
<UserAvatar <UserAvatar
v-else v-else
:user="status?.user" :user-id="status?.user.id"
class="post-avatar" class="post-avatar"
:compact="compact" :compact="compact"
:title="$t('status.unknown_user_info')" :title="$t('status.unknown_user_info')"
@ -529,7 +527,6 @@
<UserAvatar <UserAvatar
class="post-avatar" class="post-avatar"
:compact="compact" :compact="compact"
:show-actor-type-indicator="showActorTypeIndicator"
/> />
</div> </div>
<div class="right-side"> <div class="right-side">

View file

@ -1,5 +1,7 @@
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useInterfaceStore } from 'src/stores/interface.js' import { useInterfaceStore } from 'src/stores/interface.js'
import { useUsersStore } from 'src/stores/users.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faPeopleGroup, faRobot } from '@fortawesome/free-solid-svg-icons' import { faPeopleGroup, faRobot } from '@fortawesome/free-solid-svg-icons'
@ -8,10 +10,10 @@ library.add(faRobot, faPeopleGroup)
const UserAvatar = { const UserAvatar = {
props: { props: {
// User object to show avatar of // UserID of a user to show avatar of
user: { userId: {
required: true, required: true,
type: Object, type: String,
}, },
// Use less space and use alternative roundness // Use less space and use alternative roundness
compact: { compact: {
@ -19,12 +21,6 @@ const UserAvatar = {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
// Show small icon indicating if account is a bot or group
showActorTypeIndicator: {
required: false,
type: Boolean,
default: false,
},
// Override avatar image URL, useful for profile editing // Override avatar image URL, useful for profile editing
url: { url: {
required: false, required: false,
@ -39,7 +35,14 @@ const UserAvatar = {
betterShadow: useInterfaceStore().browserSupport.cssFilter, betterShadow: useInterfaceStore().browserSupport.cssFilter,
} }
}, },
components: {}, computed: {
user() {
return useUsersStore().findUser(this.userId)
},
showActorTypeIndicator() {
return useMergedConfigStore().mergedConfig.hideBotIndication
},
},
methods: { methods: {
imgSrc(src) { imgSrc(src) {
return !src || this.showPlaceholder ? this.defaultAvatar : src return !src || this.showPlaceholder ? this.defaultAvatar : src

View file

@ -23,7 +23,7 @@
class="user-info-avatar -link" class="user-info-avatar -link"
@click="zoomAvatar" @click="zoomAvatar"
> >
<UserAvatar :user="user" /> <UserAvatar :user-id="user.id" />
<div class="user-info-avatar -link -overlay"> <div class="user-info-avatar -link -overlay">
<FAIcon <FAIcon
class="fa-scale-110 fa-old-padding" class="fa-scale-110 fa-old-padding"
@ -38,7 +38,7 @@
@click="changeAvatar" @click="changeAvatar"
> >
<UserAvatar <UserAvatar
:user="user" :user-id="user.id"
:url="avatarImgSrc" :url="avatarImgSrc"
/> />
<div class="user-info-avatar -link -overlay"> <div class="user-info-avatar -link -overlay">
@ -51,7 +51,7 @@
<UserAvatar <UserAvatar
v-else-if="typeof avatarAction === 'function'" v-else-if="typeof avatarAction === 'function'"
class="user-info-avatar" class="user-info-avatar"
:user="user" :user-id="user.id"
@click="avatarAction" @click="avatarAction"
/> />
<router-link <router-link
@ -59,7 +59,7 @@
class="user-info-avatar" class="user-info-avatar"
:to="userProfileLink(user)" :to="userProfileLink(user)"
> >
<UserAvatar :user="user" /> <UserAvatar :user-id="user.id" />
</router-link> </router-link>
<div class="user-summary"> <div class="user-summary">
<div class="top-line"> <div class="top-line">

View file

@ -17,7 +17,7 @@
class="user-list-row" class="user-list-row"
> >
<UserAvatar <UserAvatar
:user="user" :user-id="user.id"
class="avatar-small" class="avatar-small"
:compact="true" :compact="true"
/> />

View file

@ -1633,6 +1633,8 @@
"no_more_statuses": "No more statuses", "no_more_statuses": "No more statuses",
"no_statuses": "No statuses", "no_statuses": "No statuses",
"socket_reconnected": "Realtime connection established", "socket_reconnected": "Realtime connection established",
"socket_disconnected": "Realtime connection unavaialable",
"socket_closed": "Realtime connection closed",
"socket_broke": "Realtime connection lost: CloseEvent code {0}", "socket_broke": "Realtime connection lost: CloseEvent code {0}",
"quick_view_settings": "Quick view settings", "quick_view_settings": "Quick view settings",
"quick_filter_settings": "Quick filter settings", "quick_filter_settings": "Quick filter settings",
@ -1730,6 +1732,7 @@
"more_actions": "More actions on this status", "more_actions": "More actions on this status",
"loading": "Loading...", "loading": "Loading...",
"load_error": "Unable to load status: {error}", "load_error": "Unable to load status: {error}",
"interact_error": "Error interacting with status: {error}",
"admin_change_scope": "Change visibility", "admin_change_scope": "Change visibility",
"mark_as_sensitive": "Sensitive", "mark_as_sensitive": "Sensitive",
"mark_as_non-sensitive": "Non-sensitive" "mark_as_non-sensitive": "Non-sensitive"

View file

@ -95,14 +95,26 @@ export const useInterfaceStore = defineStore('interface', {
} }
}, },
onStreamDisconnect(closeEvent) { onStreamDisconnect(closeEvent) {
// TODO better explanation/localization const intendedCodes = new Set([
const { code } = closeEvent 1000, // Normal (intended) closure
1001, // Going away
])
const { code } = closeEvent.original
if (intendedCodes.has(code)) {
this.pushGlobalNotice({
level: 'success',
messageKey: 'timeline.socket_closed',
messageArgs: [code],
timeout: 5000,
})
} else {
this.pushGlobalNotice({ this.pushGlobalNotice({
level: 'error', level: 'error',
messageKey: 'timeline.socket_broke', messageKey: 'timeline.socket_broke',
messageArgs: [code], messageArgs: [code],
timeout: 5000, timeout: 5000,
}) })
}
}, },
setTemporaryChanges({ confirm, revert }) { setTemporaryChanges({ confirm, revert }) {
this.temporaryChangesCountdown = 10 this.temporaryChangesCountdown = 10

View file

@ -338,7 +338,7 @@ export const useStatusesStore = defineStore('statuses', {
useInterfaceStore().pushGlobalNotice({ useInterfaceStore().pushGlobalNotice({
level: 'error', level: 'error',
messageKey: 'status.react_error', messageKey: 'status.interact_error',
messageArgs: [error], messageArgs: [error],
timeout: 5000, timeout: 5000,
}) })