Merge branch 'profile-overhaul' into shigusegubu-themes3
This commit is contained in:
commit
a1d3d04087
33 changed files with 683 additions and 556 deletions
|
|
@ -12,6 +12,8 @@
|
||||||
"custom-property-pattern": null,
|
"custom-property-pattern": null,
|
||||||
"keyframes-name-pattern": null,
|
"keyframes-name-pattern": null,
|
||||||
"scss/operator-no-newline-after": null,
|
"scss/operator-no-newline-after": null,
|
||||||
|
"declaration-property-value-no-unknown": true,
|
||||||
|
"scss/declaration-property-value-no-unknown": true,
|
||||||
"declaration-block-no-redundant-longhand-properties": [
|
"declaration-block-no-redundant-longhand-properties": [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
0
changelog.d/linter.skip
Normal file
0
changelog.d/linter.skip
Normal file
1
changelog.d/timed.add
Normal file
1
changelog.d/timed.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Support for expiring mutes and blocks (if available)
|
||||||
|
|
@ -525,6 +525,10 @@ textarea {
|
||||||
height: unset;
|
height: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--textFaint)
|
||||||
|
}
|
||||||
|
|
||||||
--_padding: 0.5em;
|
--_padding: 0.5em;
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -1075,7 +1079,7 @@ option {
|
||||||
scale: 1.0063 0.9938;
|
scale: 1.0063 0.9938;
|
||||||
translate: 0 -10%;
|
translate: 0 -10%;
|
||||||
transform: rotateZ(var(--defaultZ));
|
transform: rotateZ(var(--defaultZ));
|
||||||
animation-timing-function: ease-in-ou;
|
animation-timing-function: ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
90% {
|
90% {
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,6 @@ const getNodeInfo = async ({ store }) => {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
const metadata = data.metadata
|
const metadata = data.metadata
|
||||||
const features = metadata.features
|
const features = metadata.features
|
||||||
console.log(features)
|
|
||||||
store.dispatch('setInstanceOption', { name: 'name', value: metadata.nodeName })
|
store.dispatch('setInstanceOption', { name: 'name', value: metadata.nodeName })
|
||||||
store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations })
|
store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations })
|
||||||
store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })
|
store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
<Popover
|
<Popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
:bound-to="{ x: 'container' }"
|
|
||||||
remove-padding
|
remove-padding
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,10 @@ const BlockCard = {
|
||||||
blocked () {
|
blocked () {
|
||||||
return this.relationship.blocking
|
return this.relationship.blocking
|
||||||
},
|
},
|
||||||
|
blockExpiryAvailable () {
|
||||||
|
return this.user.block_expires_at !== undefined
|
||||||
|
},
|
||||||
blockExpiry () {
|
blockExpiry () {
|
||||||
console.log(this.user)
|
|
||||||
return this.user.block_expires_at == null
|
return this.user.block_expires_at == null
|
||||||
? this.$t('user_card.block_expires_forever')
|
? this.$t('user_card.block_expires_forever')
|
||||||
: this.$t('user_card.block_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()])
|
: this.$t('user_card.block_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()])
|
||||||
|
|
@ -29,7 +31,6 @@ const BlockCard = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
unblockUser () {
|
unblockUser () {
|
||||||
this.progress = true
|
|
||||||
this.$store.dispatch('unblockUser', this.user.id)
|
this.$store.dispatch('unblockUser', this.user.id)
|
||||||
},
|
},
|
||||||
blockUser () {
|
blockUser () {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<basic-user-card :user="user">
|
<basic-user-card :user="user">
|
||||||
<div class="block-card-content-container">
|
<div class="block-card-content-container">
|
||||||
<span v-if="blocked" class="alert neutral">
|
<span
|
||||||
|
v-if="blocked && blockExpiryAvailable"
|
||||||
|
class="alert neutral"
|
||||||
|
>
|
||||||
{{ blockExpiry }}
|
{{ blockExpiry }}
|
||||||
</span>
|
</span>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
|
|
@ -22,9 +25,9 @@
|
||||||
</div>
|
</div>
|
||||||
<teleport to="#modal">
|
<teleport to="#modal">
|
||||||
<UserTimedFilterModal
|
<UserTimedFilterModal
|
||||||
|
ref="timedBlockDialog"
|
||||||
:user="user"
|
:user="user"
|
||||||
:is-mute="false"
|
:is-mute="false"
|
||||||
ref="timedBlockDialog"
|
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
</basic-user-card>
|
</basic-user-card>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="input color-input-field"
|
class="input color-input-field"
|
||||||
:class="{ disabled: !present || disabled }"
|
:class="{ disabled: !present || disabled, unstyled }"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
:id="name + '-t'"
|
:id="name + '-t'"
|
||||||
|
|
@ -92,6 +92,11 @@ export default {
|
||||||
required: true,
|
required: true,
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
|
// use unstyled, uh, style
|
||||||
|
unstyled: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
// Color value, should be required but vue cannot tell the difference
|
// Color value, should be required but vue cannot tell the difference
|
||||||
// between "property missing" and "property set to undefined"
|
// between "property missing" and "property set to undefined"
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
<slot name="footerLeft" />
|
||||||
<button
|
<button
|
||||||
class="btn button-default"
|
class="btn button-default"
|
||||||
@click.prevent="onAccept"
|
@click.prevent="onAccept"
|
||||||
|
|
|
||||||
|
|
@ -154,10 +154,12 @@
|
||||||
// Autoprefixed seem to ignore this one, and also syntax is different
|
// Autoprefixed seem to ignore this one, and also syntax is different
|
||||||
/* stylelint-disable mask-composite */
|
/* stylelint-disable mask-composite */
|
||||||
/* stylelint-disable declaration-property-value-no-unknown */
|
/* stylelint-disable declaration-property-value-no-unknown */
|
||||||
|
/* stylelint-disable scss/declaration-property-value-no-unknown */
|
||||||
|
|
||||||
/* TODO check if this is still needed */
|
/* TODO check if this is still needed */
|
||||||
mask-composite: xor;
|
mask-composite: xor;
|
||||||
/* stylelint-enable declaration-property-value-no-unknown */
|
/* stylelint-enable declaration-property-value-no-unknown */
|
||||||
|
/* stylelint-enable scss/declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable mask-composite */
|
/* stylelint-enable mask-composite */
|
||||||
mask-composite: exclude;
|
mask-composite: exclude;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,9 +159,11 @@
|
||||||
/* Autoprefixed seem to ignore this one, and also syntax is different */
|
/* Autoprefixed seem to ignore this one, and also syntax is different */
|
||||||
/* stylelint-disable mask-composite */
|
/* stylelint-disable mask-composite */
|
||||||
/* stylelint-disable declaration-property-value-no-unknown */
|
/* stylelint-disable declaration-property-value-no-unknown */
|
||||||
|
/* stylelint-disable scss/declaration-property-value-no-unknown */
|
||||||
|
|
||||||
/* TODO check if this is still needed */
|
/* TODO check if this is still needed */
|
||||||
mask-composite: xor;
|
mask-composite: xor;
|
||||||
|
/* stylelint-enable scss/declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable declaration-property-value-no-unknown */
|
/* stylelint-enable declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable mask-composite */
|
/* stylelint-enable mask-composite */
|
||||||
mask-composite: exclude;
|
mask-composite: exclude;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ const MuteCard = {
|
||||||
muted () {
|
muted () {
|
||||||
return this.relationship.muting
|
return this.relationship.muting
|
||||||
},
|
},
|
||||||
|
muteExpiryAvailable () {
|
||||||
|
return this.user.mute_expires_at !== undefined
|
||||||
|
},
|
||||||
muteExpiry () {
|
muteExpiry () {
|
||||||
return this.user.mute_expires_at == null
|
return this.user.mute_expires_at == null
|
||||||
? this.$t('user_card.mute_expires_forever')
|
? this.$t('user_card.mute_expires_forever')
|
||||||
|
|
@ -25,7 +28,6 @@ const MuteCard = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
unmuteUser () {
|
unmuteUser () {
|
||||||
this.progress = true
|
|
||||||
this.$store.dispatch('unmuteUser', this.userId)
|
this.$store.dispatch('unmuteUser', this.userId)
|
||||||
},
|
},
|
||||||
muteUser () {
|
muteUser () {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<basic-user-card :user="user">
|
<basic-user-card :user="user">
|
||||||
<div class="mute-card-content-container">
|
<div class="mute-card-content-container">
|
||||||
<span v-if="muted" class="alert neutral">
|
<span
|
||||||
|
v-if="muted && muteExpiryAvailable"
|
||||||
|
class="alert neutral"
|
||||||
|
>
|
||||||
{{ muteExpiry }}
|
{{ muteExpiry }}
|
||||||
</span>
|
</span>
|
||||||
{{ ' ' }}
|
{{ ' ' }}
|
||||||
|
|
@ -22,9 +25,9 @@
|
||||||
</div>
|
</div>
|
||||||
<teleport to="#modal">
|
<teleport to="#modal">
|
||||||
<UserTimedFilterModal
|
<UserTimedFilterModal
|
||||||
|
ref="timedMuteDialog"
|
||||||
:user="user"
|
:user="user"
|
||||||
:is-mute="true"
|
:is-mute="true"
|
||||||
ref="timedMuteDialog"
|
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
</basic-user-card>
|
</basic-user-card>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
import { mapState, mapActions } from 'pinia'
|
import { mapState, mapActions } from 'pinia'
|
||||||
|
import { mapState as mapVuexState } from 'vuex'
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||||
|
|
@ -30,6 +31,11 @@ const FilteringTab = {
|
||||||
value: mode,
|
value: mode,
|
||||||
label: this.$t(`settings.reply_visibility_${mode}`)
|
label: this.$t(`settings.reply_visibility_${mode}`)
|
||||||
})),
|
})),
|
||||||
|
muteBlockLv1Options: ['ask', 'forever', 'temporarily'].map(mode => ({
|
||||||
|
key: mode,
|
||||||
|
value: mode,
|
||||||
|
label: this.$t(`user_card.mute_block_${mode}`)
|
||||||
|
})),
|
||||||
muteFiltersDraftObject: cloneDeep(useServerSideStorageStore().prefsStorage.simple.muteFilters),
|
muteFiltersDraftObject: cloneDeep(useServerSideStorageStore().prefsStorage.simple.muteFilters),
|
||||||
muteFiltersDraftDirty: Object.fromEntries(
|
muteFiltersDraftDirty: Object.fromEntries(
|
||||||
Object.entries(
|
Object.entries(
|
||||||
|
|
@ -93,6 +99,43 @@ const FilteringTab = {
|
||||||
muteFiltersObject: store => store.prefsStorage.simple.muteFilters
|
muteFiltersObject: store => store.prefsStorage.simple.muteFilters
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
...mapVuexState({
|
||||||
|
blockExpirationSupported: state => state.instance.blockExpiration
|
||||||
|
}),
|
||||||
|
onMuteDefaultActionLv1: {
|
||||||
|
get () {
|
||||||
|
const value = this.$store.state.config.onMuteDefaultAction
|
||||||
|
if (value === 'ask' || value === 'forever') {
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
return 'temporarily'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
let realValue = value
|
||||||
|
if (value !== 'ask' && value !== 'forever') {
|
||||||
|
realValue = '14d'
|
||||||
|
}
|
||||||
|
this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onBlockDefaultActionLv1: {
|
||||||
|
get () {
|
||||||
|
const value = this.$store.state.config.onBlockDefaultAction
|
||||||
|
if (value === 'ask' || value === 'forever') {
|
||||||
|
return value
|
||||||
|
} else {
|
||||||
|
return 'temporarily'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set (value) {
|
||||||
|
let realValue = value
|
||||||
|
if (value !== 'ask' && value !== 'forever') {
|
||||||
|
realValue = '14d'
|
||||||
|
}
|
||||||
|
this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue })
|
||||||
|
}
|
||||||
|
},
|
||||||
muteFiltersDraft () {
|
muteFiltersDraft () {
|
||||||
return Object.entries(this.muteFiltersDraftObject)
|
return Object.entries(this.muteFiltersDraftObject)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,66 @@
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<h2>{{ $t('settings.filter.mute_filter') }}</h2>
|
<h2>{{ $t('settings.filter.mute_filter') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
|
<li>
|
||||||
|
{{ $t('user_card.default_mute_expiration') }}
|
||||||
|
<Select
|
||||||
|
id="onMuteDefaultActionLv1"
|
||||||
|
v-model="onMuteDefaultActionLv1"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="option in muteBlockLv1Options"
|
||||||
|
:key="option.key"
|
||||||
|
:value="option.value"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</option>
|
||||||
|
</Select>
|
||||||
|
<ul
|
||||||
|
v-if="onMuteDefaultActionLv1 === 'temporarily'"
|
||||||
|
class="setting-list suboptions"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<UnitSetting
|
||||||
|
path="onMuteDefaultAction"
|
||||||
|
unit-set="time"
|
||||||
|
:units="['s', 'm', 'h', 'd']"
|
||||||
|
:min="0"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.default_expiration_time') }}
|
||||||
|
</UnitSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li v-if="blockExpirationSupported">
|
||||||
|
{{ $t('user_card.default_block_expiration') }}
|
||||||
|
<Select
|
||||||
|
id="onBlockDefaultActionLv1"
|
||||||
|
v-model="onBlockDefaultActionLv1"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="option in muteBlockLv1Options"
|
||||||
|
:key="option.key"
|
||||||
|
:value="option.value"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</option>
|
||||||
|
</Select>
|
||||||
|
<ul
|
||||||
|
v-if="onBlockDefaultActionLv1 === 'temporarily'"
|
||||||
|
class="setting-list suboptions"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<UnitSetting
|
||||||
|
path="onBlockDefaultAction"
|
||||||
|
unit-set="time"
|
||||||
|
:units="['s', 'm', 'h', 'd']"
|
||||||
|
:min="0"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.default_expiration_time') }}
|
||||||
|
</UnitSetting>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="hideFilteredStatuses">
|
<BooleanSetting path="hideFilteredStatuses">
|
||||||
{{ $t('settings.hide_muted_statuses') }}
|
{{ $t('settings.hide_muted_statuses') }}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import Select from 'src/components/select/select.vue'
|
||||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||||
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
||||||
|
|
||||||
import { mapState } from 'vuex'
|
|
||||||
import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js'
|
import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
|
|
@ -59,11 +58,6 @@ const GeneralTab = {
|
||||||
value: mode,
|
value: mode,
|
||||||
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
||||||
})),
|
})),
|
||||||
muteBlockLv1Options: ['ask', 'forever', 'temporarily'].map(mode => ({
|
|
||||||
key: mode,
|
|
||||||
value: mode,
|
|
||||||
label: this.$t(`user_card.mute_block_${mode}`)
|
|
||||||
})),
|
|
||||||
loopSilentAvailable:
|
loopSilentAvailable:
|
||||||
// Firefox
|
// Firefox
|
||||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||||
|
|
@ -88,40 +82,6 @@ const GeneralTab = {
|
||||||
postFormats () {
|
postFormats () {
|
||||||
return this.$store.state.instance.postFormats || []
|
return this.$store.state.instance.postFormats || []
|
||||||
},
|
},
|
||||||
onMuteDefaultActionLv1: {
|
|
||||||
get () {
|
|
||||||
const value = this.$store.state.config.onMuteDefaultAction
|
|
||||||
if (value === 'ask' || value === 'forever') {
|
|
||||||
return value
|
|
||||||
} else {
|
|
||||||
return 'temporarily'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set (value) {
|
|
||||||
let realValue = value
|
|
||||||
if (value !== 'ask' && value !== 'forever') {
|
|
||||||
realValue = '14d'
|
|
||||||
}
|
|
||||||
this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onBlockDefaultActionLv1: {
|
|
||||||
get () {
|
|
||||||
const value = this.$store.state.config.onBlockDefaultAction
|
|
||||||
if (value === 'ask' || value === 'forever') {
|
|
||||||
return value
|
|
||||||
} else {
|
|
||||||
return 'temporarily'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set (value) {
|
|
||||||
let realValue = value
|
|
||||||
if (value !== 'ask' && value !== 'forever') {
|
|
||||||
realValue = '14d'
|
|
||||||
}
|
|
||||||
this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
postContentOptions () {
|
postContentOptions () {
|
||||||
return this.postFormats.map(format => ({
|
return this.postFormats.map(format => ({
|
||||||
key: format,
|
key: format,
|
||||||
|
|
@ -137,10 +97,7 @@ const GeneralTab = {
|
||||||
},
|
},
|
||||||
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
|
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
|
||||||
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
|
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
|
||||||
...SharedComputedObject(),
|
...SharedComputedObject()
|
||||||
...mapState({
|
|
||||||
blockExpirationSupported: state => state.instance.blockExpiration
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changeDefaultScope (value) {
|
changeDefaultScope (value) {
|
||||||
|
|
|
||||||
|
|
@ -69,66 +69,6 @@
|
||||||
{{ $t('settings.user_popover_avatar_overlay') }}
|
{{ $t('settings.user_popover_avatar_overlay') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
{{ $t('user_card.default_mute_expiration') }}
|
|
||||||
<Select
|
|
||||||
id="onMuteDefaultActionLv1"
|
|
||||||
v-model="onMuteDefaultActionLv1"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="option in muteBlockLv1Options"
|
|
||||||
:key="option.key"
|
|
||||||
:value="option.value"
|
|
||||||
>
|
|
||||||
{{ option.label }}
|
|
||||||
</option>
|
|
||||||
</Select>
|
|
||||||
<ul
|
|
||||||
class="setting-list suboptions"
|
|
||||||
v-if="onMuteDefaultActionLv1 === 'temporarily'"
|
|
||||||
>
|
|
||||||
<li>
|
|
||||||
<UnitSetting
|
|
||||||
path="onMuteDefaultAction"
|
|
||||||
unit-set="time"
|
|
||||||
:units="['s', 'm', 'h', 'd']"
|
|
||||||
:min="0"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.default_expiration_time') }}
|
|
||||||
</UnitSetting>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li v-if="blockExpirationSupported">
|
|
||||||
{{ $t('user_card.default_block_expiration') }}
|
|
||||||
<Select
|
|
||||||
id="onBlockDefaultActionLv1"
|
|
||||||
v-model="onBlockDefaultActionLv1"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="option in muteBlockLv1Options"
|
|
||||||
:key="option.key"
|
|
||||||
:value="option.value"
|
|
||||||
>
|
|
||||||
{{ option.label }}
|
|
||||||
</option>
|
|
||||||
</Select>
|
|
||||||
<ul
|
|
||||||
class="setting-list suboptions"
|
|
||||||
v-if="onBlockDefaultActionLv1 === 'temporarily'"
|
|
||||||
>
|
|
||||||
<li>
|
|
||||||
<UnitSetting
|
|
||||||
path="onBlockDefaultAction"
|
|
||||||
unit-set="time"
|
|
||||||
:units="['s', 'm', 'h', 'd']"
|
|
||||||
:min="0"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.default_expiration_time') }}
|
|
||||||
</UnitSetting>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting
|
<BooleanSetting
|
||||||
path="alwaysShowNewPostButton"
|
path="alwaysShowNewPostButton"
|
||||||
|
|
@ -167,7 +107,10 @@
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting v-if="!blockExpirationSupported" path="modalOnBlock">
|
<BooleanSetting
|
||||||
|
v-if="!blockExpirationSupported"
|
||||||
|
path="modalOnBlock"
|
||||||
|
>
|
||||||
{{ $t('settings.confirm_dialogs_block') }}
|
{{ $t('settings.confirm_dialogs_block') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,9 @@
|
||||||
:user="user"
|
:user="user"
|
||||||
/>
|
/>
|
||||||
<UserTimedFilterModal
|
<UserTimedFilterModal
|
||||||
|
ref="confirmUser"
|
||||||
:is-mute="true"
|
:is-mute="true"
|
||||||
:user="user"
|
:user="user"
|
||||||
ref="confirmUser"
|
|
||||||
/>
|
/>
|
||||||
</teleport>
|
</teleport>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,11 @@
|
||||||
/* Autoprefixed seem to ignore this one, and also syntax is different */
|
/* Autoprefixed seem to ignore this one, and also syntax is different */
|
||||||
/* stylelint-disable mask-composite */
|
/* stylelint-disable mask-composite */
|
||||||
/* stylelint-disable declaration-property-value-no-unknown */
|
/* stylelint-disable declaration-property-value-no-unknown */
|
||||||
|
/* stylelint-disable scss/declaration-property-value-no-unknown */
|
||||||
|
|
||||||
/* TODO check if this is still needed */
|
/* TODO check if this is still needed */
|
||||||
mask-composite: xor;
|
mask-composite: xor;
|
||||||
|
/* stylelint-enable scss/declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable declaration-property-value-no-unknown */
|
/* stylelint-enable declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable mask-composite */
|
/* stylelint-enable mask-composite */
|
||||||
mask-composite: exclude;
|
mask-composite: exclude;
|
||||||
|
|
@ -156,9 +158,11 @@
|
||||||
/* Autoprefixed seem to ignore this one, and also syntax is different */
|
/* Autoprefixed seem to ignore this one, and also syntax is different */
|
||||||
/* stylelint-disable mask-composite */
|
/* stylelint-disable mask-composite */
|
||||||
/* stylelint-disable declaration-property-value-no-unknown */
|
/* stylelint-disable declaration-property-value-no-unknown */
|
||||||
|
/* stylelint-disable scss/declaration-property-value-no-unknown */
|
||||||
|
|
||||||
/* TODO check if this is still needed */
|
/* TODO check if this is still needed */
|
||||||
mask-composite: xor;
|
mask-composite: xor;
|
||||||
|
/* stylelint-enable scss/declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable declaration-property-value-no-unknown */
|
/* stylelint-enable declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable mask-composite */
|
/* stylelint-enable mask-composite */
|
||||||
mask-composite: exclude;
|
mask-composite: exclude;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
||||||
import ProgressButton from '../progress_button/progress_button.vue'
|
import ProgressButton from '../progress_button/progress_button.vue'
|
||||||
|
|
@ -9,6 +10,7 @@ import Select from '../select/select.vue'
|
||||||
import UserLink from '../user_link/user_link.vue'
|
import UserLink from '../user_link/user_link.vue'
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue'
|
||||||
|
import localeService from 'src/services/locale/locale.service.js'
|
||||||
|
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
|
@ -21,7 +23,8 @@ import {
|
||||||
faExternalLinkAlt,
|
faExternalLinkAlt,
|
||||||
faEdit,
|
faEdit,
|
||||||
faTimes,
|
faTimes,
|
||||||
faExpandAlt
|
faExpandAlt,
|
||||||
|
faBirthdayCake
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
import { useMediaViewerStore } from '../../stores/media_viewer'
|
import { useMediaViewerStore } from '../../stores/media_viewer'
|
||||||
|
|
@ -34,7 +37,8 @@ library.add(
|
||||||
faExternalLinkAlt,
|
faExternalLinkAlt,
|
||||||
faEdit,
|
faEdit,
|
||||||
faTimes,
|
faTimes,
|
||||||
faExpandAlt
|
faExpandAlt,
|
||||||
|
faBirthdayCake
|
||||||
)
|
)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -60,7 +64,8 @@ export default {
|
||||||
RichContent,
|
RichContent,
|
||||||
UserLink,
|
UserLink,
|
||||||
UserNote,
|
UserNote,
|
||||||
UserTimedFilterModal
|
UserTimedFilterModal,
|
||||||
|
ColorInput
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|
@ -159,16 +164,26 @@ export default {
|
||||||
supportsNote () {
|
supportsNote () {
|
||||||
return 'note' in this.relationship
|
return 'note' in this.relationship
|
||||||
},
|
},
|
||||||
|
muteExpiryAvailable () {
|
||||||
|
return this.user.mute_expires_at !== undefined
|
||||||
|
},
|
||||||
muteExpiry () {
|
muteExpiry () {
|
||||||
return this.user.mute_expires_at == null
|
return this.user.mute_expires_at == null
|
||||||
? this.$t('user_card.mute_expires_forever')
|
? this.$t('user_card.mute_expires_forever')
|
||||||
: this.$t('user_card.mute_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()])
|
: this.$t('user_card.mute_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()])
|
||||||
},
|
},
|
||||||
|
blockExpiryAvailable () {
|
||||||
|
return this.user.block_expires_at !== undefined
|
||||||
|
},
|
||||||
blockExpiry () {
|
blockExpiry () {
|
||||||
return this.user.block_expires_at == null
|
return this.user.block_expires_at == null
|
||||||
? this.$t('user_card.block_expires_forever')
|
? this.$t('user_card.block_expires_forever')
|
||||||
: this.$t('user_card.block_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()])
|
: this.$t('user_card.block_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()])
|
||||||
},
|
},
|
||||||
|
formattedBirthday () {
|
||||||
|
const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale)
|
||||||
|
return this.user.birthday && new Date(Date.parse(this.user.birthday)).toLocaleDateString(browserLocale, { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' })
|
||||||
|
},
|
||||||
...mapGetters(['mergedConfig'])
|
...mapGetters(['mergedConfig'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -2,45 +2,86 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
|
.user-card-inner {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
--_still-image-img-visibility: visible;
|
--_still-image-img-visibility: visible;
|
||||||
--_still-image-canvas-visibility: hidden;
|
--_still-image-canvas-visibility: hidden;
|
||||||
--_still-image-label-visibility: hidden;
|
--_still-image-label-visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-mute, .btn-mention {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-heading {
|
.panel-heading {
|
||||||
padding: 0.5em 0;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
backdrop-filter: none;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
// create new stacking context
|
// create new stacking context
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.personal-marks {
|
||||||
|
margin: 0.6em;
|
||||||
|
padding: 0.6em;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlighter {
|
||||||
|
h4 {
|
||||||
|
margin-top: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userHighlightSel {
|
||||||
|
vertical-align: bottom;
|
||||||
|
margin-left: -0.1em;
|
||||||
|
|
||||||
|
&.-none select {
|
||||||
|
color: var(--textFaint);
|
||||||
|
|
||||||
|
option {
|
||||||
|
color: var(--text);
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlighter-color {
|
||||||
|
vertical-align: bottom;
|
||||||
|
margin-left: 0.6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.background-image {
|
.background-image {
|
||||||
position: absolute;
|
width: calc(100% + 2.4em);
|
||||||
|
margin-left: -1.2em;
|
||||||
|
margin-right: -1.2em;
|
||||||
|
margin-top: -1.2em;
|
||||||
|
margin-bottom: -4.5em;
|
||||||
|
aspect-ratio: 3;
|
||||||
|
padding: 0;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
mask:
|
mask:
|
||||||
linear-gradient(to top, white, transparent) bottom no-repeat,
|
linear-gradient(to top, white 0%, transparent 20%) bottom no-repeat,
|
||||||
linear-gradient(to top, white, white);
|
linear-gradient(to top, white 0%, white 100%);
|
||||||
// Autoprefixer seem to ignore this one, and also syntax is different
|
// Autoprefixer seem to ignore this one, and also syntax is different
|
||||||
/* stylelint-disable mask-composite */
|
/* stylelint-disable mask-composite */
|
||||||
/* stylelint-disable declaration-property-value-no-unknown */
|
/* stylelint-disable declaration-property-value-no-unknown */
|
||||||
|
/* stylelint-disable scss/declaration-property-value-no-unknown */
|
||||||
|
|
||||||
/* TODO check if this is still needed */
|
/* TODO check if this is still needed */
|
||||||
mask-composite: xor;
|
mask-composite: xor;
|
||||||
|
/* stylelint-enable scss/declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable declaration-property-value-no-unknown */
|
/* stylelint-enable declaration-property-value-no-unknown */
|
||||||
/* stylelint-enable mask-composite */
|
/* stylelint-enable mask-composite */
|
||||||
mask-composite: exclude;
|
mask-composite: exclude;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
mask-size: 100% 60%;
|
mask-size: 100%;
|
||||||
border-top-left-radius: calc(var(--__roundnessTop, --panelRadius) - 1px);
|
border-top-left-radius: calc(var(--__roundnessTop, --panelRadius) - 1px);
|
||||||
border-top-right-radius: calc(var(--__roundnessTop, --panelRadius) - 1px);
|
border-top-right-radius: calc(var(--__roundnessTop, --panelRadius) - 1px);
|
||||||
border-bottom-left-radius: calc(var(--__roundnessBottom, --panelRadius) - 1px);
|
border-bottom-left-radius: calc(var(--__roundnessBottom, --panelRadius) - 1px);
|
||||||
|
|
@ -55,10 +96,11 @@
|
||||||
|
|
||||||
&-bio {
|
&-bio {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
color: var(--lightText);
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
padding: 1em;
|
padding: 0.6em;
|
||||||
margin: 0;
|
margin: 0 0.6em;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
|
@ -98,14 +140,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-info {
|
.user-info {
|
||||||
padding: 0 26px;
|
position: relative;
|
||||||
|
margin: 0.6em;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
.container {
|
.user-identity {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 16px 0 6px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-end;
|
||||||
max-height: 56px;
|
margin-bottom: 1em;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
@ -121,19 +164,42 @@
|
||||||
--_avatarShadowFilter: var(--avatarShadowFilter);
|
--_avatarShadowFilter: var(--avatarShadowFilter);
|
||||||
--_avatarShadowInset: var(--avatarShadowInset);
|
--_avatarShadowInset: var(--avatarShadowInset);
|
||||||
|
|
||||||
width: 56px;
|
width: 7em;
|
||||||
height: 56px;
|
height: 7em;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.other-actions {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-gap: 0.6em;
|
||||||
|
|
||||||
|
a, button {
|
||||||
|
text-align: center;
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
|
||||||
|
&:not(:hover) .icon {
|
||||||
|
color: var(--lightText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-avatar {
|
&-avatar {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
margin-right: 0.6em;
|
||||||
|
|
||||||
&.-overlay {
|
&.-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: -0.6em;
|
||||||
|
left: -0.6em;
|
||||||
|
right: -1.2em;
|
||||||
background-color: rgb(0 0 0 / 30%);
|
background-color: rgb(0 0 0 / 30%);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -152,22 +218,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.external-link-button,
|
|
||||||
.edit-profile-button {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 2.5em;
|
|
||||||
text-align: center;
|
|
||||||
margin: -0.5em 0;
|
|
||||||
padding: 0.5em 0;
|
|
||||||
|
|
||||||
&:not(:hover) .icon {
|
|
||||||
color: var(--lightText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-line {
|
.bottom-line {
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
font-size: 1.1em;
|
font-size: 0.9em;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
|
||||||
.lock-icon {
|
.lock-icon {
|
||||||
|
|
@ -182,16 +235,9 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dailyAvg {
|
|
||||||
min-width: 1px;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
margin-left: 1em;
|
|
||||||
font-size: 0.7em;
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-role {
|
.user-role {
|
||||||
flex: none;
|
flex: none;
|
||||||
|
align-self: baseline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -214,9 +260,16 @@
|
||||||
--link: var(--text) !important;
|
--link: var(--text) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-line,
|
.top-line, {
|
||||||
|
display: flex;
|
||||||
|
line-height: 2;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.bottom-line {
|
.bottom-line {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,9 +281,8 @@
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-meta {
|
.highlighter {
|
||||||
margin-bottom: 0.15em;
|
margin: 5em;
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
@ -277,23 +329,17 @@
|
||||||
|
|
||||||
.user-interactions {
|
.user-interactions {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-flow: row wrap;
|
grid-template-columns: repeat(auto-fill, minmax(17%, 7em));
|
||||||
margin-right: -0.75em;
|
grid-gap: 0.6em;
|
||||||
|
|
||||||
> * {
|
.popover-trigger-button, .moderation-tools-button {
|
||||||
margin: 0 0.75em 0.6em 0;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .btn-group, > button {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
min-width: 95px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-note {
|
|
||||||
margin: 0 0.75em 0.6em 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,28 +347,93 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-counts {
|
.user-extras {
|
||||||
display: flex;
|
padding: 1.2em 0.6em;
|
||||||
line-height: 16px;
|
line-height: 1.5;
|
||||||
padding: 0.5em 1.5em 0;
|
|
||||||
text-align: center;
|
.user-stats {
|
||||||
justify-content: space-between;
|
display: block;
|
||||||
flex-wrap: wrap;
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
dl {
|
||||||
|
display: inline;
|
||||||
|
margin-right: 1em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
font-weight: bolder;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd, dt {
|
||||||
|
display: inline
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line no-descending-specificity */
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.birthday {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
--icon: var(--text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-count {
|
.user-profile-fields {
|
||||||
flex: 1 0 auto;
|
|
||||||
padding: 0.5em 0;
|
|
||||||
margin: 0 0.5em;
|
margin: 0 0.5em;
|
||||||
|
|
||||||
h5 {
|
img {
|
||||||
font-size: 1em;
|
object-fit: contain;
|
||||||
font-weight: bolder;
|
vertical-align: middle;
|
||||||
margin: 0 0 0.25em;
|
max-width: 100%;
|
||||||
|
max-height: 400px;
|
||||||
|
|
||||||
|
&.emoji {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stylelint-disable-next-line no-descending-specificity */
|
.user-profile-field {
|
||||||
a {
|
display: flex;
|
||||||
text-decoration: none;
|
margin: 0.25em;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--roundness);
|
||||||
|
|
||||||
|
.user-profile-field-name,
|
||||||
|
.user-profile-field-value {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-profile-field-name {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: right;
|
||||||
|
color: var(--lightText);
|
||||||
|
min-width: 9em;
|
||||||
|
border-right: 1px solid var(--border);
|
||||||
|
padding-left: 1.2em;
|
||||||
|
padding-right: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-profile-field-value {
|
||||||
|
flex: 1 1 55%;
|
||||||
|
color: var(--text);
|
||||||
|
padding-left: 0.6em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,65 @@
|
||||||
class="user-card"
|
class="user-card"
|
||||||
:class="classes"
|
:class="classes"
|
||||||
>
|
>
|
||||||
<div
|
<div :class="onClose ? '' : 'panel-heading -flexible-height'" class="user-card-inner">
|
||||||
:class="{ 'hide-bio': hideBio }"
|
|
||||||
:style="style"
|
|
||||||
class="background-image"
|
|
||||||
/>
|
|
||||||
<div :class="onClose ? '' : 'panel-heading -flexible-height'">
|
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<div class="container">
|
<div
|
||||||
|
:class="{ 'hide-bio': hideBio }"
|
||||||
|
:style="style"
|
||||||
|
class="background-image"
|
||||||
|
/>
|
||||||
|
<div class="other-actions">
|
||||||
|
<button
|
||||||
|
v-if="!isOtherUser && user.is_local"
|
||||||
|
class="button-unstyled edit-profile-button"
|
||||||
|
@click.stop="openProfileTab"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
fixed-width
|
||||||
|
class="icon"
|
||||||
|
icon="edit"
|
||||||
|
:title="$t('user_card.edit_profile')"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
v-if="isOtherUser && !user.is_local"
|
||||||
|
:href="user.statusnet_profile_url"
|
||||||
|
target="_blank"
|
||||||
|
class="button-unstyled external-link-button"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
class="icon"
|
||||||
|
icon="external-link-alt"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<AccountActions
|
||||||
|
v-if="isOtherUser && loggedIn"
|
||||||
|
:user="user"
|
||||||
|
:relationship="relationship"
|
||||||
|
/>
|
||||||
|
<router-link
|
||||||
|
v-if="onClose"
|
||||||
|
:to="userProfileLink(user)"
|
||||||
|
class="button-unstyled external-link-button"
|
||||||
|
@click="onClose"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
class="icon"
|
||||||
|
icon="expand-alt"
|
||||||
|
/>
|
||||||
|
</router-link>
|
||||||
|
<button
|
||||||
|
v-if="onClose"
|
||||||
|
class="button-unstyled external-link-button"
|
||||||
|
@click="onClose"
|
||||||
|
>
|
||||||
|
<FAIcon
|
||||||
|
class="icon"
|
||||||
|
icon="times"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="user-identity">
|
||||||
<a
|
<a
|
||||||
v-if="avatarAction === 'zoom'"
|
v-if="avatarAction === 'zoom'"
|
||||||
class="user-info-avatar -link"
|
class="user-info-avatar -link"
|
||||||
|
|
@ -32,6 +83,7 @@
|
||||||
/>
|
/>
|
||||||
<router-link
|
<router-link
|
||||||
v-else
|
v-else
|
||||||
|
class="user-info-avatar"
|
||||||
:to="userProfileLink(user)"
|
:to="userProfileLink(user)"
|
||||||
>
|
>
|
||||||
<UserAvatar :user="user" />
|
<UserAvatar :user="user" />
|
||||||
|
|
@ -48,55 +100,6 @@
|
||||||
:emoji="user.emoji"
|
:emoji="user.emoji"
|
||||||
/>
|
/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<button
|
|
||||||
v-if="!isOtherUser && user.is_local"
|
|
||||||
class="button-unstyled edit-profile-button"
|
|
||||||
@click.stop="openProfileTab"
|
|
||||||
>
|
|
||||||
<FAIcon
|
|
||||||
fixed-width
|
|
||||||
class="icon"
|
|
||||||
icon="edit"
|
|
||||||
:title="$t('user_card.edit_profile')"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<a
|
|
||||||
v-if="isOtherUser && !user.is_local"
|
|
||||||
:href="user.statusnet_profile_url"
|
|
||||||
target="_blank"
|
|
||||||
class="button-unstyled external-link-button"
|
|
||||||
>
|
|
||||||
<FAIcon
|
|
||||||
class="icon"
|
|
||||||
icon="external-link-alt"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<AccountActions
|
|
||||||
v-if="isOtherUser && loggedIn"
|
|
||||||
:user="user"
|
|
||||||
:relationship="relationship"
|
|
||||||
/>
|
|
||||||
<router-link
|
|
||||||
v-if="onClose"
|
|
||||||
:to="userProfileLink(user)"
|
|
||||||
class="button-unstyled external-link-button"
|
|
||||||
@click="onClose"
|
|
||||||
>
|
|
||||||
<FAIcon
|
|
||||||
class="icon"
|
|
||||||
icon="expand-alt"
|
|
||||||
/>
|
|
||||||
</router-link>
|
|
||||||
<button
|
|
||||||
v-if="onClose"
|
|
||||||
class="button-unstyled external-link-button"
|
|
||||||
@click="onClose"
|
|
||||||
>
|
|
||||||
<FAIcon
|
|
||||||
class="icon"
|
|
||||||
icon="times"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-line">
|
<div class="bottom-line">
|
||||||
<user-link
|
<user-link
|
||||||
|
|
@ -137,71 +140,26 @@
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="relationship.muting"
|
v-if="relationship.muting && muteExpiryAvailable"
|
||||||
class="alert neutral user-role"
|
class="alert neutral user-role"
|
||||||
>
|
>
|
||||||
{{ muteExpiry }}
|
{{ muteExpiry }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="relationship.blocking"
|
v-if="relationship.blocking && blockExpiryAvailable"
|
||||||
class="alert neutral user-role"
|
class="alert neutral user-role"
|
||||||
>
|
>
|
||||||
{{ blockExpiry }}
|
{{ blockExpiry }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<div
|
||||||
v-if="!mergedConfig.hideUserStats && !hideBio"
|
v-if="relationship.followed_by && loggedIn && isOtherUser"
|
||||||
class="dailyAvg"
|
class="alert neutral user-role"
|
||||||
>{{ dailyAvg }} {{ $t('user_card.per_day') }}</span>
|
>
|
||||||
|
{{ $t('user_card.follows_you') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-meta">
|
|
||||||
<div
|
|
||||||
v-if="relationship.followed_by && loggedIn && isOtherUser"
|
|
||||||
class="following"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.follows_you') }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="isOtherUser && (loggedIn || !switcher)"
|
|
||||||
class="highlighter"
|
|
||||||
>
|
|
||||||
<!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to -->
|
|
||||||
<input
|
|
||||||
v-if="userHighlightType !== 'disabled'"
|
|
||||||
:id="'userHighlightColorTx'+user.id"
|
|
||||||
v-model="userHighlightColor"
|
|
||||||
class="input userHighlightText"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-if="userHighlightType !== 'disabled'"
|
|
||||||
:id="'userHighlightColor'+user.id"
|
|
||||||
v-model="userHighlightColor"
|
|
||||||
class="input userHighlightCl"
|
|
||||||
type="color"
|
|
||||||
>
|
|
||||||
{{ ' ' }}
|
|
||||||
<Select
|
|
||||||
:id="'userHighlightSel'+user.id"
|
|
||||||
v-model="userHighlightType"
|
|
||||||
class="userHighlightSel"
|
|
||||||
>
|
|
||||||
<option value="disabled">
|
|
||||||
{{ $t('user_card.highlight.disabled') }}
|
|
||||||
</option>
|
|
||||||
<option value="solid">
|
|
||||||
{{ $t('user_card.highlight.solid') }}
|
|
||||||
</option>
|
|
||||||
<option value="striped">
|
|
||||||
{{ $t('user_card.highlight.striped') }}
|
|
||||||
</option>
|
|
||||||
<option value="side">
|
|
||||||
{{ $t('user_card.highlight.side') }}
|
|
||||||
</option>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="loggedIn && isOtherUser"
|
v-if="loggedIn && isOtherUser"
|
||||||
class="user-interactions"
|
class="user-interactions"
|
||||||
|
|
@ -241,35 +199,32 @@
|
||||||
</ProgressButton>
|
</ProgressButton>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<button
|
||||||
<button
|
v-if="relationship.muting"
|
||||||
v-if="relationship.muting"
|
class="btn button-default btn-mute toggled"
|
||||||
class="btn button-default btn-mute toggled"
|
:disabled="user.deactivated"
|
||||||
:disabled="user.deactivated"
|
@click="unmuteUser"
|
||||||
@click="unmuteUser"
|
>
|
||||||
>
|
{{ $t('user_card.muted') }}
|
||||||
{{ $t('user_card.muted') }}
|
</button>
|
||||||
</button>
|
<button
|
||||||
<button
|
v-else
|
||||||
v-else
|
class="btn button-default btn-mute"
|
||||||
class="btn button-default btn-mute"
|
:disabled="user.deactivated"
|
||||||
:disabled="user.deactivated"
|
@click="muteUser"
|
||||||
@click="muteUser"
|
>
|
||||||
>
|
{{ $t('user_card.mute') }}
|
||||||
{{ $t('user_card.mute') }}
|
</button>
|
||||||
</button>
|
<button
|
||||||
</div>
|
class="btn button-default btn-mention"
|
||||||
<div>
|
:disabled="user.deactivated"
|
||||||
<button
|
@click="mentionUser"
|
||||||
class="btn button-default btn-mention"
|
>
|
||||||
:disabled="user.deactivated"
|
{{ $t('user_card.mention') }}
|
||||||
@click="mentionUser"
|
</button>
|
||||||
>
|
|
||||||
{{ $t('user_card.mention') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<ModerationTools
|
<ModerationTools
|
||||||
v-if="showModerationMenu"
|
v-if="showModerationMenu"
|
||||||
|
class="moderation-menu"
|
||||||
:user="user"
|
:user="user"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -279,51 +234,132 @@
|
||||||
>
|
>
|
||||||
<RemoteFollow :user="user" />
|
<RemoteFollow :user="user" />
|
||||||
</div>
|
</div>
|
||||||
<UserNote
|
</div>
|
||||||
v-if="loggedIn && isOtherUser && (hasNote || (hasNoteEditor && supportsNote))"
|
</div>
|
||||||
:user="user"
|
<div class="personal-marks" v-if="loggedIn && isOtherUser && (hasNote || !hideBio)">
|
||||||
:relationship="relationship"
|
<UserNote
|
||||||
:editable="hasNoteEditor"
|
v-if="hasNote || (hasNoteEditor && supportsNote)"
|
||||||
|
:user="user"
|
||||||
|
:relationship="relationship"
|
||||||
|
:editable="hasNoteEditor"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="!hideBio"
|
||||||
|
class="highlighter"
|
||||||
|
>
|
||||||
|
<h4>{{ $t('user_card.highlight_header') }}</h4>
|
||||||
|
<Select
|
||||||
|
:id="'userHighlightSel'+user.id"
|
||||||
|
v-model="userHighlightType"
|
||||||
|
class="userHighlightSel unstyled"
|
||||||
|
:class="{ '-none': userHighlightType === 'disabled' }"
|
||||||
|
>
|
||||||
|
<option value="disabled">
|
||||||
|
{{ $t('user_card.highlight_new.disabled') }}
|
||||||
|
</option>
|
||||||
|
<option value="solid">
|
||||||
|
{{ $t('user_card.highlight_new.solid') }}
|
||||||
|
</option>
|
||||||
|
<option value="striped">
|
||||||
|
{{ $t('user_card.highlight_new.striped') }}
|
||||||
|
</option>
|
||||||
|
<option value="side">
|
||||||
|
{{ $t('user_card.highlight_new.side') }}
|
||||||
|
</option>
|
||||||
|
</Select>
|
||||||
|
<!-- id's need to be unique, otherwise vue confuses which user-card checkbox belongs to -->
|
||||||
|
<ColorInput
|
||||||
|
v-if="userHighlightType !== 'disabled'"
|
||||||
|
class="highlighter-color"
|
||||||
|
v-model="userHighlightColor"
|
||||||
|
:show-optional-checkbox="false"
|
||||||
|
name="'userHighlightColorTx'+user.id"
|
||||||
|
:unstyled="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<RichContent
|
||||||
v-if="!hideBio"
|
v-if="!hideBio"
|
||||||
class="user-bio"
|
class="user-card-bio"
|
||||||
|
:html="user.description_html"
|
||||||
|
:emoji="user.emoji"
|
||||||
|
:handle-links="true"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="!hideBio && user.fields_html && user.fields_html.length > 0"
|
||||||
|
class="user-profile-fields"
|
||||||
>
|
>
|
||||||
<div
|
<dl
|
||||||
v-if="!mergedConfig.hideUserStats && switcher"
|
v-for="(field, index) in user.fields_html"
|
||||||
class="user-counts"
|
:key="index"
|
||||||
|
class="user-profile-field"
|
||||||
>
|
>
|
||||||
<div
|
<dt
|
||||||
|
:title="user.fields_text[index].name"
|
||||||
|
class="user-profile-field-name"
|
||||||
|
>
|
||||||
|
<RichContent
|
||||||
|
:html="field.name"
|
||||||
|
:emoji="user.emoji"
|
||||||
|
/>
|
||||||
|
</dt>
|
||||||
|
<dd
|
||||||
|
:title="user.fields_text[index].value"
|
||||||
|
class="user-profile-field-value"
|
||||||
|
>
|
||||||
|
<RichContent
|
||||||
|
:html="field.value"
|
||||||
|
:emoji="user.emoji"
|
||||||
|
/>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="user-extras" v-if="!hideBio">
|
||||||
|
<span
|
||||||
|
v-if="!mergedConfig.hideUserStats"
|
||||||
|
class="user-stats"
|
||||||
|
>
|
||||||
|
<dl
|
||||||
|
class="user-count"
|
||||||
|
@click.prevent="setProfileView('statuses')"
|
||||||
|
v-if="!mergedConfig.hideUserStats && !hideBio"
|
||||||
|
>
|
||||||
|
<dd>{{ user.statuses_count }}</dd>
|
||||||
|
{{ ' ' }}
|
||||||
|
<dt>{{ $t('user_card.statuses') }}</dt>
|
||||||
|
</dl>
|
||||||
|
<dl
|
||||||
class="user-count"
|
class="user-count"
|
||||||
@click.prevent="setProfileView('statuses')"
|
@click.prevent="setProfileView('statuses')"
|
||||||
>
|
>
|
||||||
<h5>{{ $t('user_card.statuses') }}</h5>
|
<dd>{{ dailyAvg }}</dd>
|
||||||
<span>{{ user.statuses_count }} <br></span>
|
{{ ' ' }}
|
||||||
</div>
|
<dt>{{ $t('user_card.statuses_per_day') }}</dt>
|
||||||
<div
|
</dl>
|
||||||
|
<dl
|
||||||
class="user-count"
|
class="user-count"
|
||||||
@click.prevent="setProfileView('friends')"
|
@click.prevent="setProfileView('friends')"
|
||||||
>
|
>
|
||||||
<h5>{{ $t('user_card.followees') }}</h5>
|
<dd>{{ hideFollowsCount ? $t('user_card.hidden') : user.friends_count }}</dd>
|
||||||
<span>{{ hideFollowsCount ? $t('user_card.hidden') : user.friends_count }}</span>
|
{{ ' ' }}
|
||||||
</div>
|
<dt>{{ $t('user_card.followees') }}</dt>
|
||||||
<div
|
</dl>
|
||||||
|
<dl
|
||||||
class="user-count"
|
class="user-count"
|
||||||
@click.prevent="setProfileView('followers')"
|
@click.prevent="setProfileView('followers')"
|
||||||
>
|
>
|
||||||
<h5>{{ $t('user_card.followers') }}</h5>
|
<dd>{{ hideFollowersCount ? $t('user_card.hidden') : user.followers_count }}</dd>
|
||||||
<span>{{ hideFollowersCount ? $t('user_card.hidden') : user.followers_count }}</span>
|
{{ ' ' }}
|
||||||
</div>
|
<dt>{{ $t('user_card.followers') }}</dt>
|
||||||
|
</dl>
|
||||||
|
</span>
|
||||||
|
<div class="birthday" v-if="!hideBio && !!user.birthday">
|
||||||
|
<FAIcon
|
||||||
|
class="fa-old-padding"
|
||||||
|
icon="birthday-cake"
|
||||||
|
/>
|
||||||
|
{{ $t('user_card.birthday', { birthday: formattedBirthday }) }}
|
||||||
</div>
|
</div>
|
||||||
<RichContent
|
|
||||||
v-if="!hideBio"
|
|
||||||
class="user-card-bio"
|
|
||||||
:html="user.description_html"
|
|
||||||
:emoji="user.emoji"
|
|
||||||
:handle-links="true"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<teleport to="#modal">
|
<teleport to="#modal">
|
||||||
<UserTimedFilterModal
|
<UserTimedFilterModal
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
|
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
|
||||||
|
|
||||||
const UserNote = {
|
const UserNote = {
|
||||||
props: {
|
props: {
|
||||||
user: Object,
|
user: Object,
|
||||||
relationship: Object,
|
relationship: Object,
|
||||||
editable: Boolean
|
editable: Boolean
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
PanelLoading
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
localNote: '',
|
localNote: this.relationship.note,
|
||||||
editing: false,
|
editing: false,
|
||||||
frozen: false
|
frozen: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
watch: {
|
||||||
shouldShow () {
|
relationship () {
|
||||||
return this.relationship.note || this.editing
|
this.localNote = this.relationship.note
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -21,9 +26,6 @@ const UserNote = {
|
||||||
this.localNote = this.relationship.note
|
this.localNote = this.relationship.note
|
||||||
this.editing = true
|
this.editing = true
|
||||||
},
|
},
|
||||||
cancelEditing () {
|
|
||||||
this.editing = false
|
|
||||||
},
|
|
||||||
finalizeEditing () {
|
finalizeEditing () {
|
||||||
this.frozen = true
|
this.frozen = true
|
||||||
|
|
||||||
|
|
@ -37,6 +39,7 @@ const UserNote = {
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.frozen = false
|
this.frozen = false
|
||||||
|
this.editing = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="user-note"
|
class="user-note"
|
||||||
|
:class="{ '-frozen': frozen }"
|
||||||
>
|
>
|
||||||
<div class="heading">
|
<h4>{{ $t('user_card.personal_note') }}</h4>
|
||||||
<span>{{ $t('user_card.note') }}</span>
|
|
||||||
<div class="buttons">
|
|
||||||
<button
|
|
||||||
v-show="!editing && editable"
|
|
||||||
class="button-default btn"
|
|
||||||
@click="startEditing"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.edit_note') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-show="editing"
|
|
||||||
class="button-default btn"
|
|
||||||
:disabled="frozen"
|
|
||||||
@click="finalizeEditing"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.edit_note_apply') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-show="editing"
|
|
||||||
class="button-default btn"
|
|
||||||
:disabled="frozen"
|
|
||||||
@click="cancelEditing"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.edit_note_cancel') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<textarea
|
<textarea
|
||||||
v-show="editing"
|
|
||||||
v-model="localNote"
|
v-model="localNote"
|
||||||
class="input note-text"
|
class="input note-text"
|
||||||
|
:class="{ unstyled: !editing }"
|
||||||
|
@focus="startEditing"
|
||||||
|
@blur="finalizeEditing"
|
||||||
|
rows="1"
|
||||||
|
:placeholder="$t('user_card.note_blank_click')"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-show="!editing"
|
class="overlay"
|
||||||
class="note-text"
|
v-if="frozen"
|
||||||
:class="{ '-blank': !relationship.note }"
|
|
||||||
>
|
>
|
||||||
{{ relationship.note || $t('user_card.note_blank') }}
|
<PanelLoading />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -49,38 +26,27 @@
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.user-note {
|
.user-note {
|
||||||
display: flex;
|
position: relative;
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.heading {
|
h4 {
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
margin-bottom: 0.5em;
|
||||||
margin-bottom: 0.75em;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
min-width: 95px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: right;
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-text {
|
.note-text {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
|
min-width: 100%;
|
||||||
|
margin: -0.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-text.-blank {
|
.overlay {
|
||||||
font-style: italic;
|
position: absolute;
|
||||||
color: var(--textFaint);
|
inset: 0;
|
||||||
|
background-color: rgb(0 0 0 / 30%);
|
||||||
|
|
||||||
|
.panel-loading {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,26 @@
|
||||||
backdrop-filter: var(--backdrop-filter);
|
backdrop-filter: var(--backdrop-filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.other-actions {
|
||||||
|
top: -0.6em;
|
||||||
|
right: -0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
margin-bottom: -1em;
|
||||||
|
|
||||||
|
.background-image {
|
||||||
|
max-height: 5.6em;
|
||||||
|
margin-bottom: -4.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Avatar {
|
||||||
|
width: 5em;
|
||||||
|
height: 5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.signed-in {
|
.signed-in {
|
||||||
overflow: visible;
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,24 @@
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/* popover styles load on-demand, so we need to override */
|
/* popover styles load on-demand, so we need to override */
|
||||||
/* stylelint-disable block-no-empty */
|
/* stylelint-disable block-no-empty */
|
||||||
.user-popover.popover {
|
.user-popover{
|
||||||
|
.user-card-inner {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.popover {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0.6em;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
width: 30em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-image {
|
||||||
|
max-height: 6em;
|
||||||
|
margin-bottom: -4.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* stylelint-enable block-no-empty */
|
/* stylelint-enable block-no-empty */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,13 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||||
import List from '../list/list.vue'
|
import List from '../list/list.vue'
|
||||||
import withLoadMore from '../../hocs/with_load_more/with_load_more'
|
import withLoadMore from '../../hocs/with_load_more/with_load_more'
|
||||||
import localeService from 'src/services/locale/locale.service.js'
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faCircleNotch,
|
faCircleNotch
|
||||||
faBirthdayCake
|
|
||||||
} from '@fortawesome/free-solid-svg-icons'
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faCircleNotch,
|
faCircleNotch
|
||||||
faBirthdayCake
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const FollowerList = withLoadMore({
|
const FollowerList = withLoadMore({
|
||||||
|
|
@ -82,10 +79,6 @@ const UserProfile = {
|
||||||
},
|
},
|
||||||
favoritesTabVisible () {
|
favoritesTabVisible () {
|
||||||
return this.isUs || (this.$store.state.instance.pleromaPublicFavouritesAvailable && !this.user.hide_favorites)
|
return this.isUs || (this.$store.state.instance.pleromaPublicFavouritesAvailable && !this.user.hide_favorites)
|
||||||
},
|
|
||||||
formattedBirthday () {
|
|
||||||
const browserLocale = localeService.internalToBrowserLocale(this.$i18n.locale)
|
|
||||||
return this.user.birthday && new Date(Date.parse(this.user.birthday)).toLocaleDateString(browserLocale, { timeZone: 'UTC', day: 'numeric', month: 'long', year: 'numeric' })
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -13,45 +13,6 @@
|
||||||
rounded="top"
|
rounded="top"
|
||||||
:has-note-editor="true"
|
:has-note-editor="true"
|
||||||
/>
|
/>
|
||||||
<span
|
|
||||||
v-if="!!user.birthday"
|
|
||||||
class="user-birthday"
|
|
||||||
>
|
|
||||||
<FAIcon
|
|
||||||
class="fa-old-padding"
|
|
||||||
icon="birthday-cake"
|
|
||||||
/>
|
|
||||||
{{ $t('user_card.birthday', { birthday: formattedBirthday }) }}
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
v-if="user.fields_html && user.fields_html.length > 0"
|
|
||||||
class="user-profile-fields"
|
|
||||||
>
|
|
||||||
<dl
|
|
||||||
v-for="(field, index) in user.fields_html"
|
|
||||||
:key="index"
|
|
||||||
class="user-profile-field"
|
|
||||||
>
|
|
||||||
<dt
|
|
||||||
:title="user.fields_text[index].name"
|
|
||||||
class="user-profile-field-name"
|
|
||||||
>
|
|
||||||
<RichContent
|
|
||||||
:html="field.name"
|
|
||||||
:emoji="user.emoji"
|
|
||||||
/>
|
|
||||||
</dt>
|
|
||||||
<dd
|
|
||||||
:title="user.fields_text[index].value"
|
|
||||||
class="user-profile-field-value"
|
|
||||||
>
|
|
||||||
<RichContent
|
|
||||||
:html="field.value"
|
|
||||||
:emoji="user.emoji"
|
|
||||||
/>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<tab-switcher
|
<tab-switcher
|
||||||
:active-tab="tab"
|
:active-tab="tab"
|
||||||
|
|
@ -173,58 +134,6 @@
|
||||||
// No sticky header on user profile
|
// No sticky header on user profile
|
||||||
--currentPanelStack: 0;
|
--currentPanelStack: 0;
|
||||||
|
|
||||||
.user-birthday {
|
|
||||||
margin: 0 0.75em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-fields {
|
|
||||||
margin: 0 0.5em;
|
|
||||||
|
|
||||||
img {
|
|
||||||
object-fit: contain;
|
|
||||||
vertical-align: middle;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 400px;
|
|
||||||
|
|
||||||
&.emoji {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-field {
|
|
||||||
display: flex;
|
|
||||||
margin: 0.25em;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--roundness);
|
|
||||||
|
|
||||||
.user-profile-field-name {
|
|
||||||
flex: 0 1 30%;
|
|
||||||
font-weight: 500;
|
|
||||||
text-align: right;
|
|
||||||
color: var(--lightText);
|
|
||||||
min-width: 120px;
|
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-field-value {
|
|
||||||
flex: 1 1 70%;
|
|
||||||
color: var(--text);
|
|
||||||
margin: 0 0 0 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-profile-field-name,
|
|
||||||
.user-profile-field-value {
|
|
||||||
line-height: 1.3;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 0.5em 1.5em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.userlist-placeholder {
|
.userlist-placeholder {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,8 @@
|
||||||
.input-expire-at {
|
.input-expire-at {
|
||||||
margin-left: 0.25em;
|
margin-left: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-left-checkbox {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<confirm-modal
|
<confirm-modal
|
||||||
v-if="showing"
|
v-if="showing"
|
||||||
|
class="UserTimedFilterModal"
|
||||||
:title="$t(isMute ? $t('user_card.mute') : $t('user_card.block'))"
|
:title="$t(isMute ? $t('user_card.mute') : $t('user_card.block'))"
|
||||||
:confirm-text="$t(isMute ? 'user_card.mute_confirm_accept_button' : 'user_card.block_confirm_accept_button')"
|
:confirm-text="$t(isMute ? 'user_card.mute_confirm_accept_button' : 'user_card.block_confirm_accept_button')"
|
||||||
:cancel-text="$t(isMute ? 'user_card.mute_confirm_cancel_button' : 'user_card.block_confirm_cancel_button')"
|
:cancel-text="$t(isMute ? 'user_card.mute_confirm_cancel_button' : 'user_card.block_confirm_cancel_button')"
|
||||||
@accepted="accept"
|
@accepted="accept"
|
||||||
@cancelled="cancel"
|
@cancelled="cancel"
|
||||||
>
|
>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{{ $t(isMute ? 'user_card.expire_mute_message' : 'user_card.expire_block_message', [user.screen_name]) }}
|
{{ $t(isMute ? 'user_card.expire_mute_message' : 'user_card.expire_block_message', [user.screen_name]) }}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('user_card.expire_in') }}
|
{{ $t('user_card.expire_in') }}
|
||||||
<input
|
<input
|
||||||
id="userFilterExpires"
|
id="userFilterExpires"
|
||||||
|
v-model="expiration"
|
||||||
class="input input-expire-in"
|
class="input input-expire-in"
|
||||||
:class="{ disabled: forever }"
|
:class="{ disabled: forever }"
|
||||||
v-model="expiration"
|
|
||||||
:disabled="forever"
|
:disabled="forever"
|
||||||
min="1"
|
min="1"
|
||||||
type="number"
|
type="number"
|
||||||
|
|
@ -28,31 +28,56 @@
|
||||||
class="input unit-input unstyled"
|
class="input unit-input unstyled"
|
||||||
:disabled="forever"
|
:disabled="forever"
|
||||||
>
|
>
|
||||||
<option key="s" value="s"> {{ $t('time.unit.seconds_suffix') }} </option>
|
<option
|
||||||
<option key="m" value="m"> {{ $t('time.unit.minutes_suffix') }} </option>
|
key="s"
|
||||||
<option key="h" value="h"> {{ $t('time.unit.hours_suffix') }} </option>
|
value="s"
|
||||||
<option key="d" value="d"> {{ $t('time.unit.days_suffix') }} </option>
|
>
|
||||||
|
{{ $t('time.unit.seconds_suffix') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="m"
|
||||||
|
value="m"
|
||||||
|
>
|
||||||
|
{{ $t('time.unit.minutes_suffix') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="h"
|
||||||
|
value="h"
|
||||||
|
>
|
||||||
|
{{ $t('time.unit.hours_suffix') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
key="d"
|
||||||
|
value="d"
|
||||||
|
>
|
||||||
|
{{ $t('time.unit.days_suffix') }}
|
||||||
|
</option>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<br />
|
{{ $t('user_card.mute_or') }}
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="forever"
|
id="forever"
|
||||||
v-model="forever"
|
v-model="forever"
|
||||||
name="forever"
|
name="forever"
|
||||||
class="input-forever"
|
class="input-forever"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.mute_block_forever') }}
|
{{ $t('user_card.mute_block_never') }}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Checkbox
|
<template #footerLeft>
|
||||||
id="dontAskAgain"
|
<div class="footer-left-checkbox">
|
||||||
v-model="dontAskAgain"
|
<Checkbox
|
||||||
name="dontAskAgain"
|
id="dontAskAgain"
|
||||||
class="input-dont-ask-again"
|
v-model="dontAskAgain"
|
||||||
>
|
name="dontAskAgain"
|
||||||
{{ $t(isMute ? 'user_card.dont_ask_again_mute' : 'user_card.dont_ask_again_block') }}
|
class="input-dont-ask-again"
|
||||||
</Checkbox>
|
>
|
||||||
|
{{ $t(isMute ? 'user_card.dont_ask_again_mute' : 'user_card.dont_ask_again_block') }}
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</confirm-modal>
|
</confirm-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1405,6 +1405,7 @@
|
||||||
"mute_confirm": "Do you really want to mute {user}?",
|
"mute_confirm": "Do you really want to mute {user}?",
|
||||||
"mute_confirm_accept_button": "Mute",
|
"mute_confirm_accept_button": "Mute",
|
||||||
"mute_confirm_cancel_button": "Do not mute",
|
"mute_confirm_cancel_button": "Do not mute",
|
||||||
|
"mute_or": "or",
|
||||||
"expire_in": "Expire in",
|
"expire_in": "Expire in",
|
||||||
"expire_mute_message": "Are you sure you want to mute {0}?",
|
"expire_mute_message": "Are you sure you want to mute {0}?",
|
||||||
"expire_block_message": "Are you sure you want to block {0}?",
|
"expire_block_message": "Are you sure you want to block {0}?",
|
||||||
|
|
@ -1412,6 +1413,7 @@
|
||||||
"dont_ask_again_block": "Always block users this way",
|
"dont_ask_again_block": "Always block users this way",
|
||||||
"mute_block_temporarily": "Temporarily",
|
"mute_block_temporarily": "Temporarily",
|
||||||
"mute_block_forever": "Forever",
|
"mute_block_forever": "Forever",
|
||||||
|
"mute_block_never": "Never",
|
||||||
"mute_block_ask": "Ask",
|
"mute_block_ask": "Ask",
|
||||||
"default_mute_expiration": "Always mute users",
|
"default_mute_expiration": "Always mute users",
|
||||||
"default_block_expiration": "Always block users",
|
"default_block_expiration": "Always block users",
|
||||||
|
|
@ -1421,7 +1423,7 @@
|
||||||
"block_expires_forever": "Blocked forever",
|
"block_expires_forever": "Blocked forever",
|
||||||
"block_expires_at": "Blocked until {0}",
|
"block_expires_at": "Blocked until {0}",
|
||||||
"mute_duration_prompt": "Mute this user for (0 for indefinite time):",
|
"mute_duration_prompt": "Mute this user for (0 for indefinite time):",
|
||||||
"per_day": "per day",
|
"statuses_per_day": "Statuses per day",
|
||||||
"remote_follow": "Remote follow",
|
"remote_follow": "Remote follow",
|
||||||
"remove_follower": "Remove follower",
|
"remove_follower": "Remove follower",
|
||||||
"remove_follower_confirm_title": "Remove follower confirmation",
|
"remove_follower_confirm_title": "Remove follower confirmation",
|
||||||
|
|
@ -1462,17 +1464,15 @@
|
||||||
"delete_user": "Delete user",
|
"delete_user": "Delete user",
|
||||||
"delete_user_data_and_deactivate_confirmation": "This will permanently delete the data from this account and deactivate it. Are you absolutely sure?"
|
"delete_user_data_and_deactivate_confirmation": "This will permanently delete the data from this account and deactivate it. Are you absolutely sure?"
|
||||||
},
|
},
|
||||||
"highlight": {
|
"highlight_new": {
|
||||||
"disabled": "No highlight",
|
"disabled": "Don't highlight",
|
||||||
"solid": "Solid bg",
|
"solid": "Solid background",
|
||||||
"striped": "Striped bg",
|
"striped": "Striped background",
|
||||||
"side": "Side stripe"
|
"side": "Side stripe"
|
||||||
},
|
},
|
||||||
"note": "Note",
|
"personal_note": "Personal note",
|
||||||
"note_blank": "(None)",
|
"note_blank_click": "Click to add note",
|
||||||
"edit_note": "Edit note",
|
"highlight_header": "Highlight user's posts and mentions"
|
||||||
"edit_note_apply": "Apply",
|
|
||||||
"edit_note_cancel": "Cancel"
|
|
||||||
},
|
},
|
||||||
"user_profile": {
|
"user_profile": {
|
||||||
"timeline_title": "User timeline",
|
"timeline_title": "User timeline",
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ const blockUser = (store, args) => {
|
||||||
|
|
||||||
return store.rootState.api.backendInteractor.blockUser({ id, expiresIn })
|
return store.rootState.api.backendInteractor.blockUser({ id, expiresIn })
|
||||||
.then((relationship) => {
|
.then((relationship) => {
|
||||||
console.log(relationship)
|
|
||||||
store.commit('updateUserRelationship', [relationship])
|
store.commit('updateUserRelationship', [relationship])
|
||||||
store.commit('addBlockId', id)
|
store.commit('addBlockId', id)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue