Merge branch 'profile-bg' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2026-06-01 22:24:23 +03:00
commit 2bfc423108
24 changed files with 107 additions and 53 deletions

View file

@ -149,13 +149,16 @@ export default {
userBackground() {
return this.currentUser.background_image
},
foreignProfileBackground() {
return useMergedConfigStore().mergedConfig.allowForeignUserBackground && useInterfaceStore().foreignProfileBackground
},
instanceBackground() {
return useMergedConfigStore().mergedConfig.hideInstanceWallpaper
? null
: this.instanceBackgroundUrl
},
background() {
return this.userBackground || this.instanceBackground
return this.foreignProfileBackground || this.userBackground || this.instanceBackground
},
bgStyle() {
if (this.background) {

View file

@ -50,7 +50,7 @@ body {
// have a cursor/pointer to operate them
@media (any-pointer: fine) {
* {
scrollbar-color: var(--text) transparent;
scrollbar-color: var(--icon) transparent;
&::-webkit-scrollbar {
background: transparent;
@ -130,7 +130,7 @@ body {
}
// Body should have background to scrollbar otherwise it will use white (body color?)
html {
scrollbar-color: var(--text) var(--wallpaper);
scrollbar-color: var(--icon) var(--wallpaper);
background: var(--wallpaper);
}
}
@ -200,6 +200,7 @@ nav {
background-color: var(--wallpaper);
background-image: var(--body-background-image);
background-position: 50%;
transition: background-image 1s;
}
.underlay {

View file

@ -129,6 +129,7 @@ const EmojiPicker = {
hideCustomEmojiInPicker: false,
// Lazy-load only after the first time `showing` becomes true.
contentLoaded: false,
popoverShown: false,
groupRefs: {},
emojiRefs: {},
filteredEmojiGroups: [],
@ -176,6 +177,13 @@ const EmojiPicker = {
const fullEmojiSize = emojiSizeReal + 2 * 0.2 * fontSizeMultiplier * 14
this.emojiSize = fullEmojiSize
},
togglePicker() {
if (this.popoverShown) {
this.hidePicker()
} else {
this.showPicker()
}
},
showPicker() {
this.$refs.popover.showPopover()
this.$nextTick(() => {
@ -194,10 +202,10 @@ const EmojiPicker = {
}
},
onPopoverShown() {
this.$emit('show')
this.popoverShown = true
},
onPopoverClosed() {
this.$emit('close')
this.popoverShown = false
},
onStickerUploaded(e) {
this.$emit('sticker-uploaded', e)

View file

@ -11,8 +11,8 @@ import ModifiedIndicator from '../helpers/modified_indicator.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import StringSetting from '../helpers/string_setting.vue'
import { useInstanceStore } from 'src/stores/instance.js'
import { useEmojiStore } from 'src/stores/emoji.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { library } from '@fortawesome/fontawesome-svg-core'
@ -176,21 +176,24 @@ const EmojiTab = {
},
refreshPackList() {
useEmojiStore().getAdminPacks(
this.remotePackInstance,
this.$store.state.api.backendInteractor.listEmojiPacks,
).then((allPacks) => {
this.knownLocalPacks = allPacks
for (const name of Object.keys(this.knownLocalPacks)) {
this.sortPackFiles(name)
}
})
useEmojiStore()
.getAdminPacks(
this.remotePackInstance,
this.$store.state.api.backendInteractor.listEmojiPacks,
)
.then((allPacks) => {
this.knownLocalPacks = allPacks
for (const name of Object.keys(this.knownLocalPacks)) {
this.sortPackFiles(name)
}
})
},
listRemotePacks() {
useEmojiStore().getAdminPacks(
this.remotePackInstance,
this.$store.state.api.backendInteractor.listRemoteEmojiPacks,
)
useEmojiStore()
.getAdminPacks(
this.remotePackInstance,
this.$store.state.api.backendInteractor.listRemoteEmojiPacks,
)
.then((allPacks) => {
let inst = this.remotePackInstance
if (!inst.startsWith('http')) {

View file

@ -41,6 +41,7 @@
.tab-slot-wrapper {
flex: 1 1 auto;
height: 100%;
padding: 0 1em;
overflow-y: auto;
display: grid;
grid-template-columns: minmax(1em, 1fr) minmax(min-content, 45em) minmax(1em, 1fr);

View file

@ -38,6 +38,7 @@
p {
line-height: 1.5;
margin-left: 2em;
}
.sidenote {
@ -49,6 +50,7 @@
.setting-description {
margin-top: 0.2em;
margin-bottom: 0;
margin-left: 0;
font-size: 80%;
}

View file

@ -263,6 +263,11 @@
{{ $t('settings.hide_wallpaper') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="allowForeignUserBackground">
{{ $t('settings.foreign_user_background') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="compactProfiles">
{{ $t('settings.compact_profiles') }}

View file

@ -169,7 +169,10 @@
</div>
<span class="heading-right">
<span class="pin" v-if="showPinned">
<span
v-if="showPinned"
class="pin"
>
<FAIcon
icon="thumbtack"
class="faint"

View file

@ -14,8 +14,8 @@ import {
import {
faBookmark,
faCheck,
faChevronRight,
faChevronDown,
faChevronRight,
faExternalLinkAlt,
faEyeSlash,
faHistory,
@ -131,6 +131,12 @@ export default {
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
}
},
onShowEmojiPicker() {
this.$emit('emojiPickerShown', true)
},
onHideEmojiPicker() {
this.$emit('emojiPickerShown', false)
},
doActionWrap(
button,
close = () => {
@ -142,7 +148,7 @@ export default {
)
return
if (button.name === 'emoji') {
this.$refs.picker.showPicker()
this.$refs.picker.togglePicker()
} else {
this.animationState = true
this.getComponent(button) === 'button' && this.doAction(button)

View file

@ -72,9 +72,8 @@
<Popover
v-if="button.name === 'bookmark'"
class="chevron-popover"
placement="bottom"
:trigger="extra ? 'hover' : 'click'"
:placement="extra ? 'right' : 'top'"
:placement="extra ? 'right' : 'bottom'"
:offset="extra ? { x: 10 } : { y: 10 }"
:trigger-attrs="{ class: 'extra-button' }"
>
@ -101,6 +100,8 @@
:hide-custom-emoji="hideCustomEmoji"
class="emoji-picker-panel"
@emoji="addReaction"
@show="onShowEmojiPicker"
@close="onHideEmojiPicker"
/>
</div>
</template>

View file

@ -20,6 +20,7 @@ export default {
UserTimedFilterModal,
},
props: ['button', 'status'],
emits: ['emojiPickerShown'],
mounted() {
if (this.button.name === 'mute') {
this.$store.dispatch('fetchDomainMutes')

View file

@ -79,6 +79,7 @@
:button="button"
:status="status"
v-bind="$attrs"
@emojiPickerShown="e => $emit('emojiPickerShown', e)"
/>
<teleport to="#modal">
<MuteConfirm

View file

@ -98,7 +98,7 @@ export const BUTTONS = [
label: 'tool_tip.add_reaction',
icon: ['far', 'smile-beam'],
interactive: () => true,
active: () => false,
active: ({ emojiPickerShown }) => emojiPickerShown,
toggleable: true,
anonLink: true,
},

View file

@ -28,6 +28,7 @@ const StatusActionButtons = {
/* no-op */
},
randomSeed: genRandomSeed(),
emojiPickerShown: false,
}
},
components: {
@ -56,6 +57,7 @@ const StatusActionButtons = {
return {
status: this.status,
replying: this.replying,
emojiPickerShown: this.emojiPickerShown,
emit: this.$emit,
dispatch: this.$store.dispatch,
state: this.$store.state,
@ -107,6 +109,9 @@ const StatusActionButtons = {
onExtraClose() {
this.showPin = false
},
onEmojiPickerShown(state) {
this.emojiPickerShown = state
},
isPinned(button) {
return this.pinnedItems.has(button.name)
},

View file

@ -20,6 +20,7 @@
:get-component="getComponent"
:close="() => { /* no-op */ }"
:do-action="doAction"
@emojiPickerShown="onEmojiPickerShown"
/>
<button
v-if="showPin && currentUser"

View file

@ -138,8 +138,6 @@
align-items: start;
flex-direction: row;
--emoji-size: calc(var(--emojiSize, 32px) / 2);
& .body,
& .attachments {
max-height: 3.25em;

View file

@ -1,11 +1,11 @@
import Popover from 'components/popover/popover.vue'
import SelectComponent from 'components/select/select.vue'
import StillImage from './still-image.vue'
import { mapState } from 'pinia'
import { useInterfaceStore } from 'src/stores/interface'
import StillImage from './still-image.vue'
import { useEmojiStore } from 'src/stores/emoji'
import { useInterfaceStore } from 'src/stores/interface'
export default {
components: { StillImage, Popover, SelectComponent },
@ -28,7 +28,7 @@ export default {
isUserAdmin() {
return this.$store.state.users.currentUser?.rights.admin
},
...mapState(useEmojiStore, ['adminPacksLocal', 'adminPacksLocalLoading'])
...mapState(useEmojiStore, ['adminPacksLocal', 'adminPacksLocalLoading']),
},
methods: {
displayError(msg) {
@ -64,9 +64,11 @@ export default {
},
fetchEmojiPacksIfAdmin() {
useEmojiStore().getAdminPacksLocal().then(() => {
this.$refs.emojiPopover.updateStyles()
})
useEmojiStore()
.getAdminPacksLocal()
.then(() => {
this.$refs.emojiPopover.updateStyles()
})
},
},
}

View file

@ -3,8 +3,8 @@ import { defineAsyncComponent } from 'vue'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@ -40,7 +40,7 @@ const UserListPopover = {
useInstanceStore().restrictedNicknames,
)
},
}
},
}
export default UserListPopover

View file

@ -12,8 +12,8 @@
<template v-if="users.length">
<router-link
v-for="(user) in usersCapped"
:to="generateProfileLink(user)"
:key="user.id"
:to="generateProfileLink(user)"
class="user-list-row"
>
<UserAvatar

View file

@ -11,6 +11,7 @@ import Timeline from '../timeline/timeline.vue'
import UserCard from '../user_card/user_card.vue'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
@ -56,9 +57,14 @@ const UserProfile = {
const routeParams = this.$route.params
this.load({ name: routeParams.name, id: routeParams.id })
this.tab = get(this.$route, 'query.tab', defaultTabKey)
useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
},
updated() {
useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
},
unmounted() {
this.stopFetching()
useInterfaceStore().setForeignProfileBackground(null)
},
computed: {
timeline() {

View file

@ -636,6 +636,7 @@
"navbar_column_stretch": "Stretch navbar to columns width",
"always_show_post_button": "Always show floating New Post button",
"hide_wallpaper": "Hide instance wallpaper",
"foreign_user_background": "Allow other user's profiles to override wallpaper",
"preload_images": "Preload images",
"use_one_click_nsfw": "Open NSFW attachments with just one click",
"hide_post_stats": "Hide post statistics (e.g. the number of favorites)",

View file

@ -140,6 +140,10 @@ export const INSTANCE_DEFAULT_CONFIG_DEFINITIONS = {
description: 'Hide Instance-specific panel',
default: false,
},
allowForeignUserBackground: {
description: 'Allow other user\'s profiles to override wallpaper',
default: true,
},
hideInstanceWallpaper: {
description: 'Hide Instance default background',
default: false,
@ -631,7 +635,7 @@ export const LOCAL_DEFAULT_CONFIG_DEFINITIONS = {
},
imageCompression: {
description: 'Image compression (WebP/JPEG)',
default: true,
default: false,
},
alwaysUseJpeg: {
description: 'Compress images using JPEG only',

View file

@ -1,6 +1,6 @@
import { merge } from 'lodash'
import { defineStore } from 'pinia'
import { merge } from 'lodash'
import { useInstanceStore } from 'src/stores/instance.js'
import { ensureFinalFallback } from 'src/i18n/languages.js'
@ -187,7 +187,10 @@ export const useEmojiStore = defineStore('emoji', {
const listFunction = backendInteractor.listEmojiPacks
this.adminPacksLocalLoading = true
this.adminPacksLocal = await this.getAdminPacks(useInstanceStore().server, listFunction)
this.adminPacksLocal = await this.getAdminPacks(
useInstanceStore().server,
listFunction,
)
this.adminPacksLocalLoading = false
},
@ -197,8 +200,6 @@ export const useEmojiStore = defineStore('emoji', {
if (!currentUser.rights.admin) return
const pageSize = 25
const allPacks = {}
return await listFunction({
instance,
@ -222,25 +223,22 @@ export const useEmojiStore = defineStore('emoji', {
})
.then((data) => data.json())
.then((pageData) => {
if (pageData.error !== undefined) {
return Promise.reject(pageData.error)
}
if (pageData.error !== undefined) {
return Promise.reject(pageData.error)
}
return pageData.packs
})
}),
)
}
return Promise
.all(promises)
.then((results) => {
return merge({}, ...results)
})
return Promise.all(promises).then((results) => {
return merge({}, ...results)
})
})
.then((allPacks) => {
// Sort by key
return Object
.keys(allPacks)
return Object.keys(allPacks)
.sort()
.reduce((acc, key) => {
if (key.length === 0) return acc

View file

@ -60,6 +60,7 @@ export const useInterfaceStore = defineStore('interface', {
globalNotices: [],
layoutHeight: 0,
lastTimeline: null,
foreignProfileBackground: null,
}),
actions: {
setTemporaryChanges({ confirm, revert }) {
@ -96,6 +97,9 @@ export const useInterfaceStore = defineStore('interface', {
console.error(`${error}`)
}
},
setForeignProfileBackground(url) {
this.foreignProfileBackground = url
},
settingsSaved({ success, error }) {
if (success) {
if (this.noticeClearTimeout) {