forked from AkkomaGang/akkoma-fe
Merge remote-tracking branch 'upstream/develop' into emoji-optimizations
* upstream/develop: (95 commits) Lightbox/modal multi image improvements - #381 '/api/pleroma/profile/mfa' -> '/api/pleroma/accounts/mfa' Add ability to change user's email translations-de-batch-1 eu-translate update profile-banner rounding css, fixes #690 fix indentation remove needless ref show preview popover when hover numbered replies refactor conditions do not make too many nested div add fetchStatus action refactor status loading logic split status preview popover into a separate component uninstall mobile-detect library listen both events minor css fix restrict distance at top side only set different trigger event in desktop and mobile by default fix eslint warnings ...
This commit is contained in:
commit
0dcb696e26
77 changed files with 1318 additions and 1127 deletions
|
@ -1,12 +1,15 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- Ability to hide/show repeats from user
|
||||||
|
- User profile button clutter organized into a menu
|
||||||
- Emoji picker
|
- Emoji picker
|
||||||
- Started changelog anew
|
- Started changelog anew
|
||||||
|
- Ability to change user's email
|
||||||
### Changed
|
### Changed
|
||||||
- changed the way fading effects for user profile/long statuses works, now uses css-mask instead of gradient background hacks which weren't exactly compatible with semi-transparent themes
|
- changed the way fading effects for user profile/long statuses works, now uses css-mask instead of gradient background hacks which weren't exactly compatible with semi-transparent themes
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
created () {
|
created () {
|
||||||
// Load the locale from the storage
|
// Load the locale from the storage
|
||||||
this.$i18n.locale = this.$store.state.config.interfaceLanguage
|
this.$i18n.locale = this.$store.getters.mergedConfig.interfaceLanguage
|
||||||
window.addEventListener('resize', this.updateMobileState)
|
window.addEventListener('resize', this.updateMobileState)
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
|
@ -93,7 +93,7 @@ export default {
|
||||||
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
|
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
|
||||||
showInstanceSpecificPanel () {
|
showInstanceSpecificPanel () {
|
||||||
return this.$store.state.instance.showInstanceSpecificPanel &&
|
return this.$store.state.instance.showInstanceSpecificPanel &&
|
||||||
!this.$store.state.config.hideISP &&
|
!this.$store.getters.mergedConfig.hideISP &&
|
||||||
this.$store.state.instance.instanceSpecificPanelContent
|
this.$store.state.instance.instanceSpecificPanelContent
|
||||||
},
|
},
|
||||||
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
|
||||||
|
|
30
src/App.scss
30
src/App.scss
|
@ -39,10 +39,13 @@ h4 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-family: var(--interfaceFont, sans-serif);
|
font-family: var(--interfaceFont, sans-serif);
|
||||||
font-size: 14px;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: $fallback--text;
|
color: $fallback--text;
|
||||||
color: var(--text, $fallback--text);
|
color: var(--text, $fallback--text);
|
||||||
|
@ -717,31 +720,6 @@ nav {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes modal-background-fadein {
|
|
||||||
from {
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-view {
|
|
||||||
z-index: 1000;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
overflow: auto;
|
|
||||||
animation-duration: 0.2s;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
animation-name: modal-background-fadein;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-icon {
|
.button-icon {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default (store) => {
|
||||||
{ name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },
|
{ name: 'dms', path: '/users/:username/dms', component: DMs, beforeEnter: validateAuthenticatedRoute },
|
||||||
{ name: 'settings', path: '/settings', component: Settings },
|
{ name: 'settings', path: '/settings', component: Settings },
|
||||||
{ name: 'registration', path: '/registration', component: Registration },
|
{ name: 'registration', path: '/registration', component: Registration },
|
||||||
{ name: 'password-reset', path: '/password-reset', component: PasswordReset },
|
{ name: 'password-reset', path: '/password-reset', component: PasswordReset, props: true },
|
||||||
{ name: 'registration-token', path: '/registration/:token', component: Registration },
|
{ name: 'registration-token', path: '/registration/:token', component: Registration },
|
||||||
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
|
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
|
||||||
{ name: 'user-settings', path: '/user-settings', component: UserSettings, beforeEnter: validateAuthenticatedRoute },
|
{ name: 'user-settings', path: '/user-settings', component: UserSettings, beforeEnter: validateAuthenticatedRoute },
|
||||||
|
|
35
src/components/account_actions/account_actions.js
Normal file
35
src/components/account_actions/account_actions.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import ProgressButton from '../progress_button/progress_button.vue'
|
||||||
|
|
||||||
|
const AccountActions = {
|
||||||
|
props: [
|
||||||
|
'user'
|
||||||
|
],
|
||||||
|
data () {
|
||||||
|
return { }
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ProgressButton
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showRepeats () {
|
||||||
|
this.$store.dispatch('showReblogs', this.user.id)
|
||||||
|
},
|
||||||
|
hideRepeats () {
|
||||||
|
this.$store.dispatch('hideReblogs', this.user.id)
|
||||||
|
},
|
||||||
|
blockUser () {
|
||||||
|
this.$store.dispatch('blockUser', this.user.id)
|
||||||
|
},
|
||||||
|
unblockUser () {
|
||||||
|
this.$store.dispatch('unblockUser', this.user.id)
|
||||||
|
},
|
||||||
|
reportUser () {
|
||||||
|
this.$store.dispatch('openUserReportingModal', this.user.id)
|
||||||
|
},
|
||||||
|
mentionUser () {
|
||||||
|
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AccountActions
|
93
src/components/account_actions/account_actions.vue
Normal file
93
src/components/account_actions/account_actions.vue
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<div class="account-actions">
|
||||||
|
<v-popover
|
||||||
|
trigger="click"
|
||||||
|
class="account-tools-popover"
|
||||||
|
:container="false"
|
||||||
|
placement="bottom-end"
|
||||||
|
:offset="5"
|
||||||
|
>
|
||||||
|
<div slot="popover">
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<button
|
||||||
|
class="btn btn-default btn-block dropdown-item"
|
||||||
|
@click="mentionUser"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.mention') }}
|
||||||
|
</button>
|
||||||
|
<template v-if="user.following">
|
||||||
|
<div
|
||||||
|
role="separator"
|
||||||
|
class="dropdown-divider"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
v-if="user.showing_reblogs"
|
||||||
|
class="btn btn-default dropdown-item"
|
||||||
|
@click="hideRepeats"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.hide_repeats') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="!user.showing_reblogs"
|
||||||
|
class="btn btn-default dropdown-item"
|
||||||
|
@click="showRepeats"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.show_repeats') }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
role="separator"
|
||||||
|
class="dropdown-divider"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
v-if="user.statusnet_blocking"
|
||||||
|
class="btn btn-default btn-block dropdown-item"
|
||||||
|
@click="unblockUser"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.unblock') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
class="btn btn-default btn-block dropdown-item"
|
||||||
|
@click="blockUser"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.block') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-default btn-block dropdown-item"
|
||||||
|
@click="reportUser"
|
||||||
|
>
|
||||||
|
{{ $t('user_card.report') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn btn-default ellipsis-button">
|
||||||
|
<i class="icon-ellipsis trigger-button" />
|
||||||
|
</div>
|
||||||
|
</v-popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./account_actions.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
@import '../popper/popper.scss';
|
||||||
|
.account-actions {
|
||||||
|
margin: 0 .8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-actions button.dropdown-item {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.account-actions .trigger-button {
|
||||||
|
color: $fallback--lightText;
|
||||||
|
color: var(--lightText, $fallback--lightText);
|
||||||
|
opacity: .8;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: $fallback--text;
|
||||||
|
color: var(--text, $fallback--text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -10,13 +10,14 @@ const Attachment = {
|
||||||
'statusId',
|
'statusId',
|
||||||
'size',
|
'size',
|
||||||
'allowPlay',
|
'allowPlay',
|
||||||
'setMedia'
|
'setMedia',
|
||||||
|
'naturalSizeLoad'
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage,
|
nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage,
|
||||||
hideNsfwLocal: this.$store.state.config.hideNsfw,
|
hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw,
|
||||||
preloadImage: this.$store.state.config.preloadImage,
|
preloadImage: this.$store.getters.mergedConfig.preloadImage,
|
||||||
loading: false,
|
loading: false,
|
||||||
img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'),
|
img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'),
|
||||||
modalOpen: false,
|
modalOpen: false,
|
||||||
|
@ -57,7 +58,7 @@ const Attachment = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openModal (event) {
|
openModal (event) {
|
||||||
const modalTypes = this.$store.state.config.playVideosInModal
|
const modalTypes = this.$store.getters.mergedConfig.playVideosInModal
|
||||||
? ['image', 'video']
|
? ['image', 'video']
|
||||||
: ['image']
|
: ['image']
|
||||||
if (fileTypeService.fileMatchesSomeType(modalTypes, this.attachment) ||
|
if (fileTypeService.fileMatchesSomeType(modalTypes, this.attachment) ||
|
||||||
|
@ -70,7 +71,7 @@ const Attachment = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleHidden (event) {
|
toggleHidden (event) {
|
||||||
if (this.$store.state.config.useOneClickNsfw && !this.showHidden) {
|
if (this.$store.getters.mergedConfig.useOneClickNsfw && !this.showHidden) {
|
||||||
this.openModal(event)
|
this.openModal(event)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -88,6 +89,11 @@ const Attachment = {
|
||||||
} else {
|
} else {
|
||||||
this.showHidden = !this.showHidden
|
this.showHidden = !this.showHidden
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onImageLoad (image) {
|
||||||
|
const width = image.naturalWidth
|
||||||
|
const height = image.naturalHeight
|
||||||
|
this.naturalSizeLoad && this.naturalSizeLoad({ width, height })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
:referrerpolicy="referrerpolicy"
|
:referrerpolicy="referrerpolicy"
|
||||||
:mimetype="attachment.mimetype"
|
:mimetype="attachment.mimetype"
|
||||||
:src="attachment.large_thumb_url || attachment.url"
|
:src="attachment.large_thumb_url || attachment.url"
|
||||||
|
:image-load-handler="onImageLoad"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<label class="checkbox">
|
<label
|
||||||
|
class="checkbox"
|
||||||
|
:class="{ disabled, indeterminate }"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
:disabled="disabled"
|
||||||
:checked="checked"
|
:checked="checked"
|
||||||
:indeterminate.prop="indeterminate"
|
:indeterminate.prop="indeterminate"
|
||||||
@change="$emit('change', $event.target.checked)"
|
@change="$emit('change', $event.target.checked)"
|
||||||
>
|
>
|
||||||
<i class="checkbox-indicator" />
|
<i class="checkbox-indicator" />
|
||||||
<span v-if="!!$slots.default"><slot /></span>
|
<span
|
||||||
|
class="label"
|
||||||
|
v-if="!!$slots.default"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -17,7 +26,11 @@ export default {
|
||||||
prop: 'checked',
|
prop: 'checked',
|
||||||
event: 'change'
|
event: 'change'
|
||||||
},
|
},
|
||||||
props: ['checked', 'indeterminate']
|
props: [
|
||||||
|
'checked',
|
||||||
|
'indeterminate',
|
||||||
|
'disabled'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -27,12 +40,16 @@ export default {
|
||||||
.checkbox {
|
.checkbox {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-left: 1.2em;
|
|
||||||
min-height: 1.2em;
|
min-height: 1.2em;
|
||||||
|
|
||||||
|
&-indicator {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
&-indicator::before {
|
&-indicator::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
display: block;
|
display: block;
|
||||||
content: '✔';
|
content: '✔';
|
||||||
|
@ -54,6 +71,17 @@ export default {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
.checkbox-indicator::before,
|
||||||
|
.label {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
color: $fallback--faint;
|
||||||
|
color: var(--faint, $fallback--faint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input[type=checkbox] {
|
input[type=checkbox] {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
|
@ -68,9 +96,6 @@ export default {
|
||||||
color: var(--text, $fallback--text);
|
color: var(--text, $fallback--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled + .checkbox-indicator::before {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& > span {
|
& > span {
|
||||||
|
|
|
@ -99,7 +99,7 @@ const EmojiInput = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
padEmoji () {
|
padEmoji () {
|
||||||
return this.$store.state.config.padEmoji
|
return this.$store.getters.mergedConfig.padEmoji
|
||||||
},
|
},
|
||||||
suggestions () {
|
suggestions () {
|
||||||
const firstchar = this.textAtCaret.charAt(0)
|
const firstchar = this.textAtCaret.charAt(0)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { set } from 'vue'
|
import { set } from 'vue'
|
||||||
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
|
||||||
const LOAD_EMOJI_BY = 50
|
const LOAD_EMOJI_BY = 50
|
||||||
const LOAD_EMOJI_INTERVAL = 100
|
const LOAD_EMOJI_INTERVAL = 100
|
||||||
|
@ -18,7 +19,6 @@ const EmojiPicker = {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
labelKey: String(Math.random() * 100000),
|
|
||||||
keyword: '',
|
keyword: '',
|
||||||
activeGroup: 'custom',
|
activeGroup: 'custom',
|
||||||
showingStickers: false,
|
showingStickers: false,
|
||||||
|
@ -32,7 +32,8 @@ const EmojiPicker = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
StickerPicker: () => import('../sticker_picker/sticker_picker.vue')
|
StickerPicker: () => import('../sticker_picker/sticker_picker.vue'),
|
||||||
|
Checkbox
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onEmoji (emoji) {
|
onEmoji (emoji) {
|
||||||
|
|
|
@ -75,22 +75,10 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="keep-open">
|
||||||
class="keep-open"
|
<Checkbox v-model="keepOpen">
|
||||||
>
|
{{ $t('emoji.keep_open') }}
|
||||||
<input
|
</Checkbox>
|
||||||
:id="labelKey + 'keep-open'"
|
|
||||||
v-model="keepOpen"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
class="keep-open-label"
|
|
||||||
:for="labelKey + 'keep-open'"
|
|
||||||
>
|
|
||||||
<div class="keep-open-label-text">
|
|
||||||
{{ $t('emoji.keep_open') }}
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="askForSanity"
|
v-if="askForSanity"
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
trigger="click"
|
trigger="click"
|
||||||
placement="top"
|
placement="top"
|
||||||
class="extra-button-popover"
|
class="extra-button-popover"
|
||||||
:offset="5"
|
|
||||||
:container="false"
|
|
||||||
>
|
>
|
||||||
<div slot="popover">
|
<div slot="popover">
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
const FavoriteButton = {
|
const FavoriteButton = {
|
||||||
props: ['status', 'loggedIn'],
|
props: ['status', 'loggedIn'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
hidePostStatsLocal: typeof this.$store.state.config.hidePostStats === 'undefined'
|
|
||||||
? this.$store.state.instance.hidePostStats
|
|
||||||
: this.$store.state.config.hidePostStats,
|
|
||||||
animated: false
|
animated: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -28,7 +27,8 @@ const FavoriteButton = {
|
||||||
'icon-star': this.status.favorited,
|
'icon-star': this.status.favorited,
|
||||||
'animate-spin': this.animated
|
'animate-spin': this.animated
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
...mapGetters(['mergedConfig'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
:title="$t('tool_tip.favorite')"
|
:title="$t('tool_tip.favorite')"
|
||||||
@click.prevent="favorite()"
|
@click.prevent="favorite()"
|
||||||
/>
|
/>
|
||||||
<span v-if="!hidePostStatsLocal && status.fave_num > 0">{{ status.fave_num }}</span>
|
<span v-if="!mergedConfig.hidePostStats && status.fave_num > 0">{{ status.fave_num }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<i
|
<i
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
class="button-icon favorite-button"
|
class="button-icon favorite-button"
|
||||||
:title="$t('tool_tip.favorite')"
|
:title="$t('tool_tip.favorite')"
|
||||||
/>
|
/>
|
||||||
<span v-if="!hidePostStatsLocal && status.fave_num > 0">{{ status.fave_num }}</span>
|
<span v-if="!mergedConfig.hidePostStats && status.fave_num > 0">{{ status.fave_num }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
53
src/components/follow_button/follow_button.js
Normal file
53
src/components/follow_button/follow_button.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
||||||
|
export default {
|
||||||
|
props: ['user', 'labelFollowing', 'buttonClass'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
inProgress: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isPressed () {
|
||||||
|
return this.inProgress || this.user.following
|
||||||
|
},
|
||||||
|
title () {
|
||||||
|
if (this.inProgress || this.user.following) {
|
||||||
|
return this.$t('user_card.follow_unfollow')
|
||||||
|
} else if (this.user.requested) {
|
||||||
|
return this.$t('user_card.follow_again')
|
||||||
|
} else {
|
||||||
|
return this.$t('user_card.follow')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label () {
|
||||||
|
if (this.inProgress) {
|
||||||
|
return this.$t('user_card.follow_progress')
|
||||||
|
} else if (this.user.following) {
|
||||||
|
return this.labelFollowing || this.$t('user_card.following')
|
||||||
|
} else if (this.user.requested) {
|
||||||
|
return this.$t('user_card.follow_sent')
|
||||||
|
} else {
|
||||||
|
return this.$t('user_card.follow')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick () {
|
||||||
|
this.user.following ? this.unfollow() : this.follow()
|
||||||
|
},
|
||||||
|
follow () {
|
||||||
|
this.inProgress = true
|
||||||
|
requestFollow(this.user, this.$store).then(() => {
|
||||||
|
this.inProgress = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
unfollow () {
|
||||||
|
const store = this.$store
|
||||||
|
this.inProgress = true
|
||||||
|
requestUnfollow(this.user, store).then(() => {
|
||||||
|
this.inProgress = false
|
||||||
|
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/components/follow_button/follow_button.vue
Normal file
13
src/components/follow_button/follow_button.vue
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
class="btn btn-default follow-button"
|
||||||
|
:class="{ pressed: isPressed }"
|
||||||
|
:disabled="inProgress"
|
||||||
|
:title="title"
|
||||||
|
@click="onClick"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./follow_button.js"></script>
|
|
@ -1,21 +1,16 @@
|
||||||
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
|
||||||
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
||||||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
import FollowButton from '../follow_button/follow_button.vue'
|
||||||
|
|
||||||
const FollowCard = {
|
const FollowCard = {
|
||||||
props: [
|
props: [
|
||||||
'user',
|
'user',
|
||||||
'noFollowsYou'
|
'noFollowsYou'
|
||||||
],
|
],
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
inProgress: false,
|
|
||||||
requestSent: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
BasicUserCard,
|
BasicUserCard,
|
||||||
RemoteFollow
|
RemoteFollow,
|
||||||
|
FollowButton
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isMe () {
|
isMe () {
|
||||||
|
@ -24,21 +19,6 @@ const FollowCard = {
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
return this.$store.state.users.currentUser
|
return this.$store.state.users.currentUser
|
||||||
}
|
}
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
followUser () {
|
|
||||||
this.inProgress = true
|
|
||||||
requestFollow(this.user, this.$store).then(({ sent }) => {
|
|
||||||
this.inProgress = false
|
|
||||||
this.requestSent = sent
|
|
||||||
})
|
|
||||||
},
|
|
||||||
unfollowUser () {
|
|
||||||
this.inProgress = true
|
|
||||||
requestUnfollow(this.user, this.$store).then(() => {
|
|
||||||
this.inProgress = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,36 +16,11 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<button
|
<FollowButton
|
||||||
v-if="!user.following"
|
:user="user"
|
||||||
class="btn btn-default follow-card-follow-button"
|
class="follow-card-follow-button"
|
||||||
:disabled="inProgress"
|
:label-following="$t('user_card.follow_unfollow')"
|
||||||
:title="requestSent ? $t('user_card.follow_again') : ''"
|
/>
|
||||||
@click="followUser"
|
|
||||||
>
|
|
||||||
<template v-if="inProgress">
|
|
||||||
{{ $t('user_card.follow_progress') }}
|
|
||||||
</template>
|
|
||||||
<template v-else-if="requestSent">
|
|
||||||
{{ $t('user_card.follow_sent') }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ $t('user_card.follow') }}
|
|
||||||
</template>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
class="btn btn-default follow-card-follow-button pressed"
|
|
||||||
:disabled="inProgress"
|
|
||||||
@click="unfollowUser"
|
|
||||||
>
|
|
||||||
<template v-if="inProgress">
|
|
||||||
{{ $t('user_card.follow_progress') }}
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
{{ $t('user_card.follow_unfollow') }}
|
|
||||||
</template>
|
|
||||||
</button>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</basic-user-card>
|
</basic-user-card>
|
||||||
|
|
|
@ -1,23 +1,18 @@
|
||||||
import Attachment from '../attachment/attachment.vue'
|
import Attachment from '../attachment/attachment.vue'
|
||||||
import { chunk, last, dropRight } from 'lodash'
|
import { chunk, last, dropRight, sumBy } from 'lodash'
|
||||||
|
|
||||||
const Gallery = {
|
const Gallery = {
|
||||||
data: () => ({
|
|
||||||
width: 500
|
|
||||||
}),
|
|
||||||
props: [
|
props: [
|
||||||
'attachments',
|
'attachments',
|
||||||
'nsfw',
|
'nsfw',
|
||||||
'setMedia'
|
'setMedia'
|
||||||
],
|
],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
sizes: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
components: { Attachment },
|
components: { Attachment },
|
||||||
mounted () {
|
|
||||||
this.resize()
|
|
||||||
window.addEventListener('resize', this.resize)
|
|
||||||
},
|
|
||||||
destroyed () {
|
|
||||||
window.removeEventListener('resize', this.resize)
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
rows () {
|
rows () {
|
||||||
if (!this.attachments) {
|
if (!this.attachments) {
|
||||||
|
@ -33,21 +28,24 @@ const Gallery = {
|
||||||
}
|
}
|
||||||
return rows
|
return rows
|
||||||
},
|
},
|
||||||
rowHeight () {
|
|
||||||
return itemsPerRow => ({ 'height': `${(this.width / (itemsPerRow + 0.6))}px` })
|
|
||||||
},
|
|
||||||
useContainFit () {
|
useContainFit () {
|
||||||
return this.$store.state.config.useContainFit
|
return this.$store.getters.mergedConfig.useContainFit
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resize () {
|
onNaturalSizeLoad (id, size) {
|
||||||
// Quick optimization to make resizing not always trigger state change,
|
this.$set(this.sizes, id, size)
|
||||||
// only update attachment size in 10px steps
|
},
|
||||||
const width = Math.floor(this.$el.getBoundingClientRect().width / 10) * 10
|
rowStyle (itemsPerRow) {
|
||||||
if (this.width !== width) {
|
return { 'padding-bottom': `${(100 / (itemsPerRow + 0.6))}%` }
|
||||||
this.width = width
|
},
|
||||||
}
|
itemStyle (id, row) {
|
||||||
|
const total = sumBy(row, item => this.getAspectRatio(item.id))
|
||||||
|
return { flex: `${this.getAspectRatio(id) / total} 1 0%` }
|
||||||
|
},
|
||||||
|
getAspectRatio (id) {
|
||||||
|
const size = this.sizes[id]
|
||||||
|
return size ? size.width / size.height : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,21 @@
|
||||||
v-for="(row, index) in rows"
|
v-for="(row, index) in rows"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="gallery-row"
|
class="gallery-row"
|
||||||
:style="rowHeight(row.length)"
|
:style="rowStyle(row.length)"
|
||||||
:class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"
|
:class="{ 'contain-fit': useContainFit, 'cover-fit': !useContainFit }"
|
||||||
>
|
>
|
||||||
<attachment
|
<div class="gallery-row-inner">
|
||||||
v-for="attachment in row"
|
<attachment
|
||||||
:key="attachment.id"
|
v-for="attachment in row"
|
||||||
:set-media="setMedia"
|
:key="attachment.id"
|
||||||
:nsfw="nsfw"
|
:set-media="setMedia"
|
||||||
:attachment="attachment"
|
:nsfw="nsfw"
|
||||||
:allow-play="false"
|
:attachment="attachment"
|
||||||
/>
|
:allow-play="false"
|
||||||
|
:natural-size-load="onNaturalSizeLoad.bind(null, attachment.id)"
|
||||||
|
:style="itemStyle(attachment.id, row)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -28,15 +32,24 @@
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.gallery-row {
|
.gallery-row {
|
||||||
height: 200px;
|
position: relative;
|
||||||
|
height: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-content: stretch;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
|
|
||||||
|
.gallery-row-inner {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-content: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: specificity problem with this and .attachments.attachment
|
// FIXME: specificity problem with this and .attachments.attachment
|
||||||
// we shouldn't have the need for .image here
|
// we shouldn't have the need for .image here
|
||||||
.attachment.image {
|
.attachment.image {
|
||||||
|
|
|
@ -40,7 +40,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
language: {
|
language: {
|
||||||
get: function () { return this.$store.state.config.interfaceLanguage },
|
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
||||||
set: function (val) {
|
set: function (val) {
|
||||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||||
this.$i18n.locale = val
|
this.$i18n.locale = val
|
||||||
|
|
|
@ -59,6 +59,8 @@ const LoginForm = {
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
if (result.error === 'mfa_required') {
|
if (result.error === 'mfa_required') {
|
||||||
this.requireMFA({ app: app, settings: result })
|
this.requireMFA({ app: app, settings: result })
|
||||||
|
} else if (result.identifier === 'password_reset_required') {
|
||||||
|
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
|
||||||
} else {
|
} else {
|
||||||
this.error = result.error
|
this.error = result.error
|
||||||
this.focusOnPasswordInput()
|
this.focusOnPasswordInput()
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import StillImage from '../still-image/still-image.vue'
|
import StillImage from '../still-image/still-image.vue'
|
||||||
import VideoAttachment from '../video_attachment/video_attachment.vue'
|
import VideoAttachment from '../video_attachment/video_attachment.vue'
|
||||||
|
import Modal from '../modal/modal.vue'
|
||||||
import fileTypeService from '../../services/file_type/file_type.service.js'
|
import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||||
|
import GestureService from '../../services/gesture_service/gesture_service'
|
||||||
|
|
||||||
const MediaModal = {
|
const MediaModal = {
|
||||||
components: {
|
components: {
|
||||||
StillImage,
|
StillImage,
|
||||||
VideoAttachment
|
VideoAttachment,
|
||||||
|
Modal
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showing () {
|
showing () {
|
||||||
|
@ -27,7 +30,27 @@ const MediaModal = {
|
||||||
return this.currentMedia ? fileTypeService.fileType(this.currentMedia.mimetype) : null
|
return this.currentMedia ? fileTypeService.fileType(this.currentMedia.mimetype) : null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
this.mediaSwipeGestureRight = GestureService.swipeGesture(
|
||||||
|
GestureService.DIRECTION_RIGHT,
|
||||||
|
this.goPrev,
|
||||||
|
50
|
||||||
|
)
|
||||||
|
this.mediaSwipeGestureLeft = GestureService.swipeGesture(
|
||||||
|
GestureService.DIRECTION_LEFT,
|
||||||
|
this.goNext,
|
||||||
|
50
|
||||||
|
)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
mediaTouchStart (e) {
|
||||||
|
GestureService.beginSwipe(e, this.mediaSwipeGestureRight)
|
||||||
|
GestureService.beginSwipe(e, this.mediaSwipeGestureLeft)
|
||||||
|
},
|
||||||
|
mediaTouchMove (e) {
|
||||||
|
GestureService.updateSwipe(e, this.mediaSwipeGestureRight)
|
||||||
|
GestureService.updateSwipe(e, this.mediaSwipeGestureLeft)
|
||||||
|
},
|
||||||
hide () {
|
hide () {
|
||||||
this.$store.dispatch('closeMediaViewer')
|
this.$store.dispatch('closeMediaViewer')
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Modal
|
||||||
v-if="showing"
|
v-if="showing"
|
||||||
v-body-scroll-lock="showing"
|
class="media-modal-view"
|
||||||
class="modal-view media-modal-view"
|
@backdropClicked="hide"
|
||||||
@click.prevent="hide"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="type === 'image'"
|
v-if="type === 'image'"
|
||||||
class="modal-image"
|
class="modal-image"
|
||||||
:src="currentMedia.url"
|
:src="currentMedia.url"
|
||||||
|
@touchstart.stop="mediaTouchStart"
|
||||||
|
@touchmove.stop="mediaTouchMove"
|
||||||
>
|
>
|
||||||
<VideoAttachment
|
<VideoAttachment
|
||||||
v-if="type === 'video'"
|
v-if="type === 'video'"
|
||||||
|
@ -33,33 +34,25 @@
|
||||||
>
|
>
|
||||||
<i class="icon-right-open arrow-icon" />
|
<i class="icon-right-open arrow-icon" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./media_modal.js"></script>
|
<script src="./media_modal.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
.modal-view.media-modal-view {
|
||||||
|
|
||||||
.media-modal-view {
|
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
|
|
||||||
body:not(.scroll-locked) & {
|
.modal-view-button-arrow {
|
||||||
display: none;
|
opacity: 0.75;
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&:focus,
|
||||||
.modal-view-button-arrow {
|
&:hover {
|
||||||
opacity: 0.75;
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
&:focus,
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: none;
|
opacity: 1;
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,5 +107,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -63,7 +63,7 @@ const MobileNav = {
|
||||||
this.$refs.notifications.markAsSeen()
|
this.$refs.notifications.markAsSeen()
|
||||||
},
|
},
|
||||||
onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) {
|
onScroll ({ target: { scrollTop, clientHeight, scrollHeight } }) {
|
||||||
if (this.$store.state.config.autoLoad && scrollTop + clientHeight >= scrollHeight) {
|
if (this.$store.getters.mergedConfig.autoLoad && scrollTop + clientHeight >= scrollHeight) {
|
||||||
this.$refs.notifications.fetchOlderNotifications()
|
this.$refs.notifications.fetchOlderNotifications()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ const MobilePostStatusButton = {
|
||||||
return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
|
return this.autohideFloatingPostButton && (this.hidden || this.inputActive)
|
||||||
},
|
},
|
||||||
autohideFloatingPostButton () {
|
autohideFloatingPostButton () {
|
||||||
return !!this.$store.state.config.autohideFloatingPostButton
|
return !!this.$store.getters.mergedConfig.autohideFloatingPostButton
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
52
src/components/modal/modal.vue
Normal file
52
src/components/modal/modal.vue
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-show="isOpen"
|
||||||
|
v-body-scroll-lock="isOpen"
|
||||||
|
class="modal-view"
|
||||||
|
@click.self="$emit('backdropClicked')"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
isOpen: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.modal-view {
|
||||||
|
z-index: 1000;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: auto;
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
animation-name: modal-background-fadein;
|
||||||
|
|
||||||
|
body:not(.scroll-locked) & {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modal-background-fadein {
|
||||||
|
from {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,9 +3,7 @@
|
||||||
<v-popover
|
<v-popover
|
||||||
trigger="click"
|
trigger="click"
|
||||||
class="moderation-tools-popover"
|
class="moderation-tools-popover"
|
||||||
:container="false"
|
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
:offset="5"
|
|
||||||
@show="showDropDown = true"
|
@show="showDropDown = true"
|
||||||
@hide="showDropDown = false"
|
@hide="showDropDown = false"
|
||||||
>
|
>
|
||||||
|
|
|
@ -39,7 +39,7 @@ const Notification = {
|
||||||
return highlightClass(this.notification.from_profile)
|
return highlightClass(this.notification.from_profile)
|
||||||
},
|
},
|
||||||
userStyle () {
|
userStyle () {
|
||||||
const highlight = this.$store.state.config.highlight
|
const highlight = this.$store.getters.mergedConfig.highlight
|
||||||
const user = this.notification.from_profile
|
const user = this.notification.from_profile
|
||||||
return highlightStyle(highlight[user.screen_name])
|
return highlightStyle(highlight[user.screen_name])
|
||||||
},
|
},
|
||||||
|
|
|
@ -25,6 +25,12 @@ const passwordReset = {
|
||||||
this.$router.push({ name: 'root' })
|
this.$router.push({ name: 'root' })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
passwordResetRequested: {
|
||||||
|
default: false,
|
||||||
|
type: Boolean
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
dismissError () {
|
dismissError () {
|
||||||
this.error = null
|
this.error = null
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
>
|
>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div v-if="!mailerEnabled">
|
<div v-if="!mailerEnabled">
|
||||||
<p>
|
<p v-if="passwordResetRequested">
|
||||||
|
{{ $t('password_reset.password_reset_required_but_mailer_is_disabled') }}
|
||||||
|
</p>
|
||||||
|
<p v-else>
|
||||||
{{ $t('password_reset.password_reset_disabled') }}
|
{{ $t('password_reset.password_reset_disabled') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,6 +28,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
<p
|
||||||
|
v-if="passwordResetRequested"
|
||||||
|
class="password-reset-required error"
|
||||||
|
>
|
||||||
|
{{ $t('password_reset.password_reset_required') }}
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('password_reset.instruction') }}
|
{{ $t('password_reset.instruction') }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -104,6 +113,11 @@
|
||||||
margin: 0.3em 0.0em 1em;
|
margin: 0.3em 0.0em 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.password-reset-required {
|
||||||
|
background-color: var(--alertError, $fallback--alertError);
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.notice-dismissible {
|
.notice-dismissible {
|
||||||
padding-right: 2rem;
|
padding-right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
border-color: $fallback--bg;
|
border-color: $fallback--bg;
|
||||||
border-color: var(--bg, $fallback--bg);
|
border-color: var(--bg, $fallback--bg);
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&[x-placement^="top"] {
|
&[x-placement^="top"] {
|
||||||
|
@ -31,7 +30,7 @@
|
||||||
border-left-color: transparent !important;
|
border-left-color: transparent !important;
|
||||||
border-right-color: transparent !important;
|
border-right-color: transparent !important;
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
bottom: -5px;
|
bottom: -4px;
|
||||||
left: calc(50% - 5px);
|
left: calc(50% - 5px);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -46,7 +45,7 @@
|
||||||
border-left-color: transparent !important;
|
border-left-color: transparent !important;
|
||||||
border-right-color: transparent !important;
|
border-right-color: transparent !important;
|
||||||
border-top-color: transparent !important;
|
border-top-color: transparent !important;
|
||||||
top: -5px;
|
top: -4px;
|
||||||
left: calc(50% - 5px);
|
left: calc(50% - 5px);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -61,7 +60,7 @@
|
||||||
border-left-color: transparent !important;
|
border-left-color: transparent !important;
|
||||||
border-top-color: transparent !important;
|
border-top-color: transparent !important;
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
left: -5px;
|
left: -4px;
|
||||||
top: calc(50% - 5px);
|
top: calc(50% - 5px);
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
@ -76,7 +75,7 @@
|
||||||
border-top-color: transparent !important;
|
border-top-color: transparent !important;
|
||||||
border-right-color: transparent !important;
|
border-right-color: transparent !important;
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
right: -5px;
|
right: -4px;
|
||||||
top: calc(50% - 5px);
|
top: calc(50% - 5px);
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
|
@ -7,6 +7,8 @@ import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||||
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
||||||
import { reject, map, uniqBy } from 'lodash'
|
import { reject, map, uniqBy } from 'lodash'
|
||||||
import suggestor from '../emoji_input/suggestor.js'
|
import suggestor from '../emoji_input/suggestor.js'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
|
||||||
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
|
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
|
||||||
let allAttentions = [...attentions]
|
let allAttentions = [...attentions]
|
||||||
|
@ -35,7 +37,8 @@ const PostStatusForm = {
|
||||||
MediaUpload,
|
MediaUpload,
|
||||||
EmojiInput,
|
EmojiInput,
|
||||||
PollForm,
|
PollForm,
|
||||||
ScopeSelector
|
ScopeSelector,
|
||||||
|
Checkbox
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.resize(this.$refs.textarea)
|
this.resize(this.$refs.textarea)
|
||||||
|
@ -50,9 +53,7 @@ const PostStatusForm = {
|
||||||
const preset = this.$route.query.message
|
const preset = this.$route.query.message
|
||||||
let statusText = preset || ''
|
let statusText = preset || ''
|
||||||
|
|
||||||
const scopeCopy = typeof this.$store.state.config.scopeCopy === 'undefined'
|
const { scopeCopy } = this.$store.getters.mergedConfig
|
||||||
? this.$store.state.instance.scopeCopy
|
|
||||||
: this.$store.state.config.scopeCopy
|
|
||||||
|
|
||||||
if (this.replyTo) {
|
if (this.replyTo) {
|
||||||
const currentUser = this.$store.state.users.currentUser
|
const currentUser = this.$store.state.users.currentUser
|
||||||
|
@ -63,9 +64,7 @@ const PostStatusForm = {
|
||||||
? this.copyMessageScope
|
? this.copyMessageScope
|
||||||
: this.$store.state.users.currentUser.default_scope
|
: this.$store.state.users.currentUser.default_scope
|
||||||
|
|
||||||
const contentType = typeof this.$store.state.config.postContentType === 'undefined'
|
const { postContentType: contentType } = this.$store.getters.mergedConfig
|
||||||
? this.$store.state.instance.postContentType
|
|
||||||
: this.$store.state.config.postContentType
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dropFiles: [],
|
dropFiles: [],
|
||||||
|
@ -94,10 +93,7 @@ const PostStatusForm = {
|
||||||
return this.$store.state.users.currentUser.default_scope
|
return this.$store.state.users.currentUser.default_scope
|
||||||
},
|
},
|
||||||
showAllScopes () {
|
showAllScopes () {
|
||||||
const minimalScopesMode = typeof this.$store.state.config.minimalScopesMode === 'undefined'
|
return !this.mergedConfig.minimalScopesMode
|
||||||
? this.$store.state.instance.minimalScopesMode
|
|
||||||
: this.$store.state.config.minimalScopesMode
|
|
||||||
return !minimalScopesMode
|
|
||||||
},
|
},
|
||||||
emojiUserSuggestor () {
|
emojiUserSuggestor () {
|
||||||
return suggestor({
|
return suggestor({
|
||||||
|
@ -145,13 +141,7 @@ const PostStatusForm = {
|
||||||
return this.$store.state.instance.minimalScopesMode
|
return this.$store.state.instance.minimalScopesMode
|
||||||
},
|
},
|
||||||
alwaysShowSubject () {
|
alwaysShowSubject () {
|
||||||
if (typeof this.$store.state.config.alwaysShowSubjectInput !== 'undefined') {
|
return this.mergedConfig.alwaysShowSubjectInput
|
||||||
return this.$store.state.config.alwaysShowSubjectInput
|
|
||||||
} else if (typeof this.$store.state.instance.alwaysShowSubjectInput !== 'undefined') {
|
|
||||||
return this.$store.state.instance.alwaysShowSubjectInput
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
postFormats () {
|
postFormats () {
|
||||||
return this.$store.state.instance.postFormats || []
|
return this.$store.state.instance.postFormats || []
|
||||||
|
@ -164,13 +154,14 @@ const PostStatusForm = {
|
||||||
this.$store.state.instance.pollLimits.max_options >= 2
|
this.$store.state.instance.pollLimits.max_options >= 2
|
||||||
},
|
},
|
||||||
hideScopeNotice () {
|
hideScopeNotice () {
|
||||||
return this.$store.state.config.hideScopeNotice
|
return this.$store.getters.mergedConfig.hideScopeNotice
|
||||||
},
|
},
|
||||||
pollContentError () {
|
pollContentError () {
|
||||||
return this.pollFormVisible &&
|
return this.pollFormVisible &&
|
||||||
this.newStatus.poll &&
|
this.newStatus.poll &&
|
||||||
this.newStatus.poll.error
|
this.newStatus.poll.error
|
||||||
}
|
},
|
||||||
|
...mapGetters(['mergedConfig'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
postStatus (newStatus) {
|
postStatus (newStatus) {
|
||||||
|
|
|
@ -264,12 +264,9 @@
|
||||||
v-if="newStatus.files.length > 0"
|
v-if="newStatus.files.length > 0"
|
||||||
class="upload_settings"
|
class="upload_settings"
|
||||||
>
|
>
|
||||||
<input
|
<Checkbox v-model="newStatus.nsfw">
|
||||||
id="filesSensitive"
|
{{ $t('post_status.attachments_sensitive') }}
|
||||||
v-model="newStatus.nsfw"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="filesSensitive">{{ $t('post_status.attachments_sensitive') }}</label>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||||
|
import Modal from '../modal/modal.vue'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
|
|
||||||
const PostStatusModal = {
|
const PostStatusModal = {
|
||||||
components: {
|
components: {
|
||||||
PostStatusForm
|
PostStatusForm,
|
||||||
|
Modal
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Modal
|
||||||
v-if="isLoggedIn && !resettingForm"
|
v-if="isLoggedIn && !resettingForm"
|
||||||
v-show="modalActivated"
|
:is-open="modalActivated"
|
||||||
class="post-form-modal-view modal-view"
|
class="post-form-modal-view"
|
||||||
@click="closeModal"
|
@backdropClicked="closeModal"
|
||||||
>
|
>
|
||||||
<div
|
<div class="post-form-modal-panel panel">
|
||||||
class="post-form-modal-panel panel"
|
|
||||||
@click.stop=""
|
|
||||||
>
|
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
{{ $t('post_status.new_status') }}
|
{{ $t('post_status.new_status') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,15 +15,13 @@
|
||||||
@posted="closeModal"
|
@posted="closeModal"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./post_status_modal.js"></script>
|
<script src="./post_status_modal.js"></script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
.modal-view.post-form-modal-view {
|
||||||
|
|
||||||
.post-form-modal-view {
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
const RetweetButton = {
|
const RetweetButton = {
|
||||||
props: ['status', 'loggedIn', 'visibility'],
|
props: ['status', 'loggedIn', 'visibility'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
hidePostStatsLocal: typeof this.$store.state.config.hidePostStats === 'undefined'
|
|
||||||
? this.$store.state.instance.hidePostStats
|
|
||||||
: this.$store.state.config.hidePostStats,
|
|
||||||
animated: false
|
animated: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -28,7 +27,8 @@ const RetweetButton = {
|
||||||
'retweeted-empty': !this.status.repeated,
|
'retweeted-empty': !this.status.repeated,
|
||||||
'animate-spin': this.animated
|
'animate-spin': this.animated
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
...mapGetters(['mergedConfig'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
:title="$t('tool_tip.repeat')"
|
:title="$t('tool_tip.repeat')"
|
||||||
@click.prevent="retweet()"
|
@click.prevent="retweet()"
|
||||||
/>
|
/>
|
||||||
<span v-if="!hidePostStatsLocal && status.repeat_num > 0">{{ status.repeat_num }}</span>
|
<span v-if="!mergedConfig.hidePostStats && status.repeat_num > 0">{{ status.repeat_num }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<i
|
<i
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
class="button-icon icon-retweet"
|
class="button-icon icon-retweet"
|
||||||
:title="$t('tool_tip.repeat')"
|
:title="$t('tool_tip.repeat')"
|
||||||
/>
|
/>
|
||||||
<span v-if="!hidePostStatsLocal && status.repeat_num > 0">{{ status.repeat_num }}</span>
|
<span v-if="!mergedConfig.hidePostStats && status.repeat_num > 0">{{ status.repeat_num }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -5,88 +5,22 @@ import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
||||||
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
||||||
import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'
|
import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'
|
||||||
import { extractCommit } from '../../services/version/version.service'
|
import { extractCommit } from '../../services/version/version.service'
|
||||||
|
import { instanceDefaultProperties, defaultState as configDefaultState } from '../../modules/config.js'
|
||||||
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
|
||||||
const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
||||||
const pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/'
|
const pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/'
|
||||||
|
|
||||||
|
const multiChoiceProperties = [
|
||||||
|
'postContentType',
|
||||||
|
'subjectLineBehavior'
|
||||||
|
]
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
data () {
|
data () {
|
||||||
const user = this.$store.state.config
|
|
||||||
const instance = this.$store.state.instance
|
const instance = this.$store.state.instance
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hideAttachmentsLocal: user.hideAttachments,
|
|
||||||
padEmojiLocal: user.padEmoji,
|
|
||||||
hideAttachmentsInConvLocal: user.hideAttachmentsInConv,
|
|
||||||
maxThumbnails: user.maxThumbnails,
|
|
||||||
hideNsfwLocal: user.hideNsfw,
|
|
||||||
useOneClickNsfw: user.useOneClickNsfw,
|
|
||||||
hideISPLocal: user.hideISP,
|
|
||||||
preloadImage: user.preloadImage,
|
|
||||||
|
|
||||||
hidePostStatsLocal: typeof user.hidePostStats === 'undefined'
|
|
||||||
? instance.hidePostStats
|
|
||||||
: user.hidePostStats,
|
|
||||||
hidePostStatsDefault: this.$t('settings.values.' + instance.hidePostStats),
|
|
||||||
|
|
||||||
hideUserStatsLocal: typeof user.hideUserStats === 'undefined'
|
|
||||||
? instance.hideUserStats
|
|
||||||
: user.hideUserStats,
|
|
||||||
hideUserStatsDefault: this.$t('settings.values.' + instance.hideUserStats),
|
|
||||||
|
|
||||||
hideFilteredStatusesLocal: typeof user.hideFilteredStatuses === 'undefined'
|
|
||||||
? instance.hideFilteredStatuses
|
|
||||||
: user.hideFilteredStatuses,
|
|
||||||
hideFilteredStatusesDefault: this.$t('settings.values.' + instance.hideFilteredStatuses),
|
|
||||||
|
|
||||||
notificationVisibilityLocal: user.notificationVisibility,
|
|
||||||
replyVisibilityLocal: user.replyVisibility,
|
|
||||||
loopVideoLocal: user.loopVideo,
|
|
||||||
muteWordsString: user.muteWords.join('\n'),
|
|
||||||
autoLoadLocal: user.autoLoad,
|
|
||||||
streamingLocal: user.streaming,
|
|
||||||
pauseOnUnfocusedLocal: user.pauseOnUnfocused,
|
|
||||||
hoverPreviewLocal: user.hoverPreview,
|
|
||||||
autohideFloatingPostButtonLocal: user.autohideFloatingPostButton,
|
|
||||||
|
|
||||||
hideMutedPostsLocal: typeof user.hideMutedPosts === 'undefined'
|
|
||||||
? instance.hideMutedPosts
|
|
||||||
: user.hideMutedPosts,
|
|
||||||
hideMutedPostsDefault: this.$t('settings.values.' + instance.hideMutedPosts),
|
|
||||||
|
|
||||||
collapseMessageWithSubjectLocal: typeof user.collapseMessageWithSubject === 'undefined'
|
|
||||||
? instance.collapseMessageWithSubject
|
|
||||||
: user.collapseMessageWithSubject,
|
|
||||||
collapseMessageWithSubjectDefault: this.$t('settings.values.' + instance.collapseMessageWithSubject),
|
|
||||||
|
|
||||||
subjectLineBehaviorLocal: typeof user.subjectLineBehavior === 'undefined'
|
|
||||||
? instance.subjectLineBehavior
|
|
||||||
: user.subjectLineBehavior,
|
|
||||||
subjectLineBehaviorDefault: instance.subjectLineBehavior,
|
|
||||||
|
|
||||||
postContentTypeLocal: typeof user.postContentType === 'undefined'
|
|
||||||
? instance.postContentType
|
|
||||||
: user.postContentType,
|
|
||||||
postContentTypeDefault: instance.postContentType,
|
|
||||||
|
|
||||||
alwaysShowSubjectInputLocal: typeof user.alwaysShowSubjectInput === 'undefined'
|
|
||||||
? instance.alwaysShowSubjectInput
|
|
||||||
: user.alwaysShowSubjectInput,
|
|
||||||
alwaysShowSubjectInputDefault: this.$t('settings.values.' + instance.alwaysShowSubjectInput),
|
|
||||||
|
|
||||||
scopeCopyLocal: typeof user.scopeCopy === 'undefined'
|
|
||||||
? instance.scopeCopy
|
|
||||||
: user.scopeCopy,
|
|
||||||
scopeCopyDefault: this.$t('settings.values.' + instance.scopeCopy),
|
|
||||||
|
|
||||||
minimalScopesModeLocal: typeof user.minimalScopesMode === 'undefined'
|
|
||||||
? instance.minimalScopesMode
|
|
||||||
: user.minimalScopesMode,
|
|
||||||
minimalScopesModeDefault: this.$t('settings.values.' + instance.minimalScopesMode),
|
|
||||||
|
|
||||||
stopGifs: user.stopGifs,
|
|
||||||
webPushNotificationsLocal: user.webPushNotifications,
|
|
||||||
loopVideoSilentOnlyLocal: user.loopVideosSilentOnly,
|
|
||||||
loopSilentAvailable:
|
loopSilentAvailable:
|
||||||
// Firefox
|
// Firefox
|
||||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||||
|
@ -94,8 +28,6 @@ const settings = {
|
||||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
|
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
|
||||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),
|
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),
|
||||||
playVideosInModal: user.playVideosInModal,
|
|
||||||
useContainFit: user.useContainFit,
|
|
||||||
|
|
||||||
backendVersion: instance.backendVersion,
|
backendVersion: instance.backendVersion,
|
||||||
frontendVersion: instance.frontendVersion
|
frontendVersion: instance.frontendVersion
|
||||||
|
@ -104,7 +36,8 @@ const settings = {
|
||||||
components: {
|
components: {
|
||||||
TabSwitcher,
|
TabSwitcher,
|
||||||
StyleSwitcher,
|
StyleSwitcher,
|
||||||
InterfaceLanguageSwitcher
|
InterfaceLanguageSwitcher,
|
||||||
|
Checkbox
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
|
@ -122,116 +55,56 @@ const settings = {
|
||||||
},
|
},
|
||||||
backendVersionLink () {
|
backendVersionLink () {
|
||||||
return pleromaBeCommitUrl + extractCommit(this.backendVersion)
|
return pleromaBeCommitUrl + extractCommit(this.backendVersion)
|
||||||
|
},
|
||||||
|
// Getting localized values for instance-default properties
|
||||||
|
...instanceDefaultProperties
|
||||||
|
.filter(key => multiChoiceProperties.includes(key))
|
||||||
|
.map(key => [
|
||||||
|
key + 'DefaultValue',
|
||||||
|
function () {
|
||||||
|
return this.$store.getters.instanceDefaultConfig[key]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
|
||||||
|
...instanceDefaultProperties
|
||||||
|
.filter(key => !multiChoiceProperties.includes(key))
|
||||||
|
.map(key => [
|
||||||
|
key + 'LocalizedValue',
|
||||||
|
function () {
|
||||||
|
return this.$t('settings.values.' + this.$store.getters.instanceDefaultConfig[key])
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
|
||||||
|
// Generating computed values for vuex properties
|
||||||
|
...Object.keys(configDefaultState)
|
||||||
|
.map(key => [key, {
|
||||||
|
get () { return this.$store.getters.mergedConfig[key] },
|
||||||
|
set (value) {
|
||||||
|
this.$store.dispatch('setOption', { name: key, value })
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
|
||||||
|
// Special cases (need to transform values)
|
||||||
|
muteWordsString: {
|
||||||
|
get () { return this.$store.getters.mergedConfig.muteWords.join('\n') },
|
||||||
|
set (value) {
|
||||||
|
this.$store.dispatch('setOption', {
|
||||||
|
name: 'muteWords',
|
||||||
|
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Updating nested properties
|
||||||
watch: {
|
watch: {
|
||||||
hideAttachmentsLocal (value) {
|
notificationVisibility: {
|
||||||
this.$store.dispatch('setOption', { name: 'hideAttachments', value })
|
handler (value) {
|
||||||
},
|
this.$store.dispatch('setOption', {
|
||||||
padEmojiLocal (value) {
|
name: 'notificationVisibility',
|
||||||
this.$store.dispatch('setOption', { name: 'padEmoji', value })
|
value: this.$store.getters.mergedConfig.notificationVisibility
|
||||||
},
|
})
|
||||||
hideAttachmentsInConvLocal (value) {
|
},
|
||||||
this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })
|
deep: true
|
||||||
},
|
|
||||||
hidePostStatsLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'hidePostStats', value })
|
|
||||||
},
|
|
||||||
hideUserStatsLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'hideUserStats', value })
|
|
||||||
},
|
|
||||||
hideFilteredStatusesLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })
|
|
||||||
},
|
|
||||||
hideNsfwLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'hideNsfw', value })
|
|
||||||
},
|
|
||||||
useOneClickNsfw (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'useOneClickNsfw', value })
|
|
||||||
},
|
|
||||||
preloadImage (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'preloadImage', value })
|
|
||||||
},
|
|
||||||
hideISPLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'hideISP', value })
|
|
||||||
},
|
|
||||||
'notificationVisibilityLocal.likes' (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
|
|
||||||
},
|
|
||||||
'notificationVisibilityLocal.follows' (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
|
|
||||||
},
|
|
||||||
'notificationVisibilityLocal.repeats' (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
|
|
||||||
},
|
|
||||||
'notificationVisibilityLocal.mentions' (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'notificationVisibility', value: this.$store.state.config.notificationVisibility })
|
|
||||||
},
|
|
||||||
replyVisibilityLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'replyVisibility', value })
|
|
||||||
},
|
|
||||||
loopVideoLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'loopVideo', value })
|
|
||||||
},
|
|
||||||
loopVideoSilentOnlyLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'loopVideoSilentOnly', value })
|
|
||||||
},
|
|
||||||
autoLoadLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'autoLoad', value })
|
|
||||||
},
|
|
||||||
streamingLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'streaming', value })
|
|
||||||
},
|
|
||||||
pauseOnUnfocusedLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'pauseOnUnfocused', value })
|
|
||||||
},
|
|
||||||
hoverPreviewLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'hoverPreview', value })
|
|
||||||
},
|
|
||||||
autohideFloatingPostButtonLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'autohideFloatingPostButton', value })
|
|
||||||
},
|
|
||||||
muteWordsString (value) {
|
|
||||||
value = filter(value.split('\n'), (word) => trim(word).length > 0)
|
|
||||||
this.$store.dispatch('setOption', { name: 'muteWords', value })
|
|
||||||
},
|
|
||||||
hideMutedPostsLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'hideMutedPosts', value })
|
|
||||||
},
|
|
||||||
collapseMessageWithSubjectLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'collapseMessageWithSubject', value })
|
|
||||||
},
|
|
||||||
scopeCopyLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'scopeCopy', value })
|
|
||||||
},
|
|
||||||
alwaysShowSubjectInputLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'alwaysShowSubjectInput', value })
|
|
||||||
},
|
|
||||||
subjectLineBehaviorLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'subjectLineBehavior', value })
|
|
||||||
},
|
|
||||||
postContentTypeLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'postContentType', value })
|
|
||||||
},
|
|
||||||
minimalScopesModeLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'minimalScopesMode', value })
|
|
||||||
},
|
|
||||||
stopGifs (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'stopGifs', value })
|
|
||||||
},
|
|
||||||
webPushNotificationsLocal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'webPushNotifications', value })
|
|
||||||
if (value) this.$store.dispatch('registerPushNotifications')
|
|
||||||
},
|
|
||||||
playVideosInModal (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'playVideosInModal', value })
|
|
||||||
},
|
|
||||||
useContainFit (value) {
|
|
||||||
this.$store.dispatch('setOption', { name: 'useContainFit', value })
|
|
||||||
},
|
|
||||||
maxThumbnails (value) {
|
|
||||||
value = this.maxThumbnails = Math.floor(Math.max(value, 0))
|
|
||||||
this.$store.dispatch('setOption', { name: 'maxThumbnails', value })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,12 +36,9 @@
|
||||||
<interface-language-switcher />
|
<interface-language-switcher />
|
||||||
</li>
|
</li>
|
||||||
<li v-if="instanceSpecificPanelPresent">
|
<li v-if="instanceSpecificPanelPresent">
|
||||||
<input
|
<Checkbox v-model="hideISP">
|
||||||
id="hideISP"
|
{{ $t('settings.hide_isp') }}
|
||||||
v-model="hideISPLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hideISP">{{ $t('settings.hide_isp') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,58 +46,42 @@
|
||||||
<h2>{{ $t('nav.timeline') }}</h2>
|
<h2>{{ $t('nav.timeline') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="hideMutedPosts">
|
||||||
id="hideMutedPosts"
|
{{ $t('settings.hide_muted_posts') }} {{ $t('settings.instance_default', { value: hideMutedPostsLocalizedValue }) }}
|
||||||
v-model="hideMutedPostsLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hideMutedPosts">{{ $t('settings.hide_muted_posts') }} {{ $t('settings.instance_default', { value: hideMutedPostsDefault }) }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="collapseMessageWithSubject">
|
||||||
id="collapseMessageWithSubject"
|
{{ $t('settings.collapse_subject') }} {{ $t('settings.instance_default', { value: collapseMessageWithSubjectLocalizedValue }) }}
|
||||||
v-model="collapseMessageWithSubjectLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="collapseMessageWithSubject">{{ $t('settings.collapse_subject') }} {{ $t('settings.instance_default', { value: collapseMessageWithSubjectDefault }) }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="streaming">
|
||||||
id="streaming"
|
{{ $t('settings.streaming') }}
|
||||||
v-model="streamingLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="streaming">{{ $t('settings.streaming') }}</label>
|
|
||||||
<ul
|
<ul
|
||||||
class="setting-list suboptions"
|
class="setting-list suboptions"
|
||||||
:class="[{disabled: !streamingLocal}]"
|
:class="[{disabled: !streaming}]"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox
|
||||||
id="pauseOnUnfocused"
|
v-model="pauseOnUnfocused"
|
||||||
v-model="pauseOnUnfocusedLocal"
|
:disabled="!streaming"
|
||||||
:disabled="!streamingLocal"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
>
|
||||||
<label for="pauseOnUnfocused">{{ $t('settings.pause_on_unfocused') }}</label>
|
{{ $t('settings.pause_on_unfocused') }}
|
||||||
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="autoLoad">
|
||||||
id="autoload"
|
{{ $t('settings.autoload') }}
|
||||||
v-model="autoLoadLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="autoload">{{ $t('settings.autoload') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="hoverPreview">
|
||||||
id="hoverPreview"
|
{{ $t('settings.reply_link_preview') }}
|
||||||
v-model="hoverPreviewLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hoverPreview">{{ $t('settings.reply_link_preview') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -109,24 +90,14 @@
|
||||||
<h2>{{ $t('settings.composing') }}</h2>
|
<h2>{{ $t('settings.composing') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="scopeCopy">
|
||||||
id="scopeCopy"
|
{{ $t('settings.scope_copy') }} {{ $t('settings.instance_default', { value: scopeCopyLocalizedValue }) }}
|
||||||
v-model="scopeCopyLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="scopeCopy">
|
|
||||||
{{ $t('settings.scope_copy') }} {{ $t('settings.instance_default', { value: scopeCopyDefault }) }}
|
|
||||||
</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="alwaysShowSubjectInput">
|
||||||
id="subjectHide"
|
{{ $t('settings.subject_input_always_show') }} {{ $t('settings.instance_default', { value: alwaysShowSubjectInputLocalizedValue }) }}
|
||||||
v-model="alwaysShowSubjectInputLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="subjectHide">
|
|
||||||
{{ $t('settings.subject_input_always_show') }} {{ $t('settings.instance_default', { value: alwaysShowSubjectInputDefault }) }}
|
|
||||||
</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div>
|
<div>
|
||||||
|
@ -137,19 +108,19 @@
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
id="subjectLineBehavior"
|
id="subjectLineBehavior"
|
||||||
v-model="subjectLineBehaviorLocal"
|
v-model="subjectLineBehavior"
|
||||||
>
|
>
|
||||||
<option value="email">
|
<option value="email">
|
||||||
{{ $t('settings.subject_line_email') }}
|
{{ $t('settings.subject_line_email') }}
|
||||||
{{ subjectLineBehaviorDefault == 'email' ? $t('settings.instance_default_simple') : '' }}
|
{{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }}
|
||||||
</option>
|
</option>
|
||||||
<option value="masto">
|
<option value="masto">
|
||||||
{{ $t('settings.subject_line_mastodon') }}
|
{{ $t('settings.subject_line_mastodon') }}
|
||||||
{{ subjectLineBehaviorDefault == 'mastodon' ? $t('settings.instance_default_simple') : '' }}
|
{{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }}
|
||||||
</option>
|
</option>
|
||||||
<option value="noop">
|
<option value="noop">
|
||||||
{{ $t('settings.subject_line_noop') }}
|
{{ $t('settings.subject_line_noop') }}
|
||||||
{{ subjectLineBehaviorDefault == 'noop' ? $t('settings.instance_default_simple') : '' }}
|
{{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<i class="icon-down-open" />
|
<i class="icon-down-open" />
|
||||||
|
@ -165,7 +136,7 @@
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
id="postContentType"
|
id="postContentType"
|
||||||
v-model="postContentTypeLocal"
|
v-model="postContentType"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="postFormat in postFormats"
|
v-for="postFormat in postFormats"
|
||||||
|
@ -173,7 +144,7 @@
|
||||||
:value="postFormat"
|
:value="postFormat"
|
||||||
>
|
>
|
||||||
{{ $t(`post_status.content_type["${postFormat}"]`) }}
|
{{ $t(`post_status.content_type["${postFormat}"]`) }}
|
||||||
{{ postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : '' }}
|
{{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<i class="icon-down-open" />
|
<i class="icon-down-open" />
|
||||||
|
@ -181,30 +152,19 @@
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="minimalScopesMode">
|
||||||
id="minimalScopesMode"
|
{{ $t('settings.minimal_scopes_mode') }} {{ $t('settings.instance_default', { value: minimalScopesModeLocalizedValue }) }}
|
||||||
v-model="minimalScopesModeLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="minimalScopesMode">
|
|
||||||
{{ $t('settings.minimal_scopes_mode') }} {{ $t('settings.instance_default', { value: minimalScopesModeDefault }) }}
|
|
||||||
</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="autohideFloatingPostButton">
|
||||||
id="autohideFloatingPostButton"
|
{{ $t('settings.autohide_floating_post_button') }}
|
||||||
v-model="autohideFloatingPostButtonLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="autohideFloatingPostButton">{{ $t('settings.autohide_floating_post_button') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="padEmoji">
|
||||||
id="padEmoji"
|
{{ $t('settings.pad_emoji') }}
|
||||||
v-model="padEmojiLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="padEmoji">{{ $t('settings.pad_emoji') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -213,23 +173,19 @@
|
||||||
<h2>{{ $t('settings.attachments') }}</h2>
|
<h2>{{ $t('settings.attachments') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="hideAttachments">
|
||||||
id="hideAttachments"
|
{{ $t('settings.hide_attachments_in_tl') }}
|
||||||
v-model="hideAttachmentsLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hideAttachments">{{ $t('settings.hide_attachments_in_tl') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="hideAttachmentsInConv">
|
||||||
id="hideAttachmentsInConv"
|
{{ $t('settings.hide_attachments_in_convo') }}
|
||||||
v-model="hideAttachmentsInConvLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hideAttachmentsInConv">{{ $t('settings.hide_attachments_in_convo') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="maxThumbnails">{{ $t('settings.max_thumbnails') }}</label>
|
<label for="maxThumbnails">
|
||||||
|
{{ $t('settings.max_thumbnails') }}
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
id="maxThumbnails"
|
id="maxThumbnails"
|
||||||
v-model.number="maxThumbnails"
|
v-model.number="maxThumbnails"
|
||||||
|
@ -240,60 +196,48 @@
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="hideNsfw">
|
||||||
id="hideNsfw"
|
{{ $t('settings.nsfw_clickthrough') }}
|
||||||
v-model="hideNsfwLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hideNsfw">{{ $t('settings.nsfw_clickthrough') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<ul class="setting-list suboptions">
|
<ul class="setting-list suboptions">
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox
|
||||||
id="preloadImage"
|
|
||||||
v-model="preloadImage"
|
v-model="preloadImage"
|
||||||
:disabled="!hideNsfwLocal"
|
:disabled="!hideNsfw"
|
||||||
type="checkbox"
|
|
||||||
>
|
>
|
||||||
<label for="preloadImage">{{ $t('settings.preload_images') }}</label>
|
{{ $t('settings.preload_images') }}
|
||||||
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox
|
||||||
id="useOneClickNsfw"
|
|
||||||
v-model="useOneClickNsfw"
|
v-model="useOneClickNsfw"
|
||||||
:disabled="!hideNsfwLocal"
|
:disabled="!hideNsfw"
|
||||||
type="checkbox"
|
|
||||||
>
|
>
|
||||||
<label for="useOneClickNsfw">{{ $t('settings.use_one_click_nsfw') }}</label>
|
{{ $t('settings.use_one_click_nsfw') }}
|
||||||
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="stopGifs">
|
||||||
id="stopGifs"
|
{{ $t('settings.stop_gifs') }}
|
||||||
v-model="stopGifs"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="stopGifs">{{ $t('settings.stop_gifs') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="loopVideo">
|
||||||
id="loopVideo"
|
{{ $t('settings.loop_video') }}
|
||||||
v-model="loopVideoLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="loopVideo">{{ $t('settings.loop_video') }}</label>
|
|
||||||
<ul
|
<ul
|
||||||
class="setting-list suboptions"
|
class="setting-list suboptions"
|
||||||
:class="[{disabled: !streamingLocal}]"
|
:class="[{disabled: !streaming}]"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox
|
||||||
id="loopVideoSilentOnly"
|
v-model="loopVideoSilentOnly"
|
||||||
v-model="loopVideoSilentOnlyLocal"
|
:disabled="!loopVideo || !loopSilentAvailable"
|
||||||
:disabled="!loopVideoLocal || !loopSilentAvailable"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
>
|
||||||
<label for="loopVideoSilentOnly">{{ $t('settings.loop_video_silent_only') }}</label>
|
{{ $t('settings.loop_video_silent_only') }}
|
||||||
|
</Checkbox>
|
||||||
<div
|
<div
|
||||||
v-if="!loopSilentAvailable"
|
v-if="!loopSilentAvailable"
|
||||||
class="unavailable"
|
class="unavailable"
|
||||||
|
@ -304,20 +248,14 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="playVideosInModal">
|
||||||
id="playVideosInModal"
|
{{ $t('settings.play_videos_in_modal') }}
|
||||||
v-model="playVideosInModal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="playVideosInModal">{{ $t('settings.play_videos_in_modal') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="useContainFit">
|
||||||
id="useContainFit"
|
{{ $t('settings.use_contain_fit') }}
|
||||||
v-model="useContainFit"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="useContainFit">{{ $t('settings.use_contain_fit') }}</label>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -326,14 +264,9 @@
|
||||||
<h2>{{ $t('settings.notifications') }}</h2>
|
<h2>{{ $t('settings.notifications') }}</h2>
|
||||||
<ul class="setting-list">
|
<ul class="setting-list">
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="webPushNotifications">
|
||||||
id="webPushNotifications"
|
|
||||||
v-model="webPushNotificationsLocal"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="webPushNotifications">
|
|
||||||
{{ $t('settings.enable_web_push_notifications') }}
|
{{ $t('settings.enable_web_push_notifications') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -351,44 +284,24 @@
|
||||||
<span class="label">{{ $t('settings.notification_visibility') }}</span>
|
<span class="label">{{ $t('settings.notification_visibility') }}</span>
|
||||||
<ul class="option-list">
|
<ul class="option-list">
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationVisibility.likes">
|
||||||
id="notification-visibility-likes"
|
|
||||||
v-model="notificationVisibilityLocal.likes"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-visibility-likes">
|
|
||||||
{{ $t('settings.notification_visibility_likes') }}
|
{{ $t('settings.notification_visibility_likes') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationVisibility.repeats">
|
||||||
id="notification-visibility-repeats"
|
|
||||||
v-model="notificationVisibilityLocal.repeats"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-visibility-repeats">
|
|
||||||
{{ $t('settings.notification_visibility_repeats') }}
|
{{ $t('settings.notification_visibility_repeats') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationVisibility.follows">
|
||||||
id="notification-visibility-follows"
|
|
||||||
v-model="notificationVisibilityLocal.follows"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-visibility-follows">
|
|
||||||
{{ $t('settings.notification_visibility_follows') }}
|
{{ $t('settings.notification_visibility_follows') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationVisibility.mentions">
|
||||||
id="notification-visibility-mentions"
|
|
||||||
v-model="notificationVisibilityLocal.mentions"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-visibility-mentions">
|
|
||||||
{{ $t('settings.notification_visibility_mentions') }}
|
{{ $t('settings.notification_visibility_mentions') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -400,7 +313,7 @@
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
id="replyVisibility"
|
id="replyVisibility"
|
||||||
v-model="replyVisibilityLocal"
|
v-model="replyVisibility"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
value="all"
|
value="all"
|
||||||
|
@ -413,24 +326,14 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<Checkbox v-model="hidePostStats">
|
||||||
id="hidePostStats"
|
{{ $t('settings.hide_post_stats') }} {{ $t('settings.instance_default', { value: hidePostStatsLocalizedValue }) }}
|
||||||
v-model="hidePostStatsLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hidePostStats">
|
|
||||||
{{ $t('settings.hide_post_stats') }} {{ $t('settings.instance_default', { value: hidePostStatsDefault }) }}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<Checkbox v-model="hideUserStats">
|
||||||
id="hideUserStats"
|
{{ $t('settings.hide_user_stats') }} {{ $t('settings.instance_default', { value: hideUserStatsLocalizedValue }) }}
|
||||||
v-model="hideUserStatsLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hideUserStats">
|
|
||||||
{{ $t('settings.hide_user_stats') }} {{ $t('settings.instance_default', { value: hideUserStatsDefault }) }}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
|
@ -442,14 +345,9 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<Checkbox v-model="hideFilteredStatuses">
|
||||||
id="hideFilteredStatuses"
|
{{ $t('settings.hide_filtered_statuses') }} {{ $t('settings.instance_default', { value: hideFilteredStatusesLocalizedValue }) }}
|
||||||
v-model="hideFilteredStatusesLocal"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="hideFilteredStatuses">
|
|
||||||
{{ $t('settings.hide_filtered_statuses') }} {{ $t('settings.instance_default', { value: hideFilteredStatusesDefault }) }}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,11 +10,13 @@ import Gallery from '../gallery/gallery.vue'
|
||||||
import LinkPreview from '../link-preview/link-preview.vue'
|
import LinkPreview from '../link-preview/link-preview.vue'
|
||||||
import AvatarList from '../avatar_list/avatar_list.vue'
|
import AvatarList from '../avatar_list/avatar_list.vue'
|
||||||
import Timeago from '../timeago/timeago.vue'
|
import Timeago from '../timeago/timeago.vue'
|
||||||
|
import StatusPopover from '../status_popover/status_popover.vue'
|
||||||
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 fileType from 'src/services/file_type/file_type.service'
|
import fileType from 'src/services/file_type/file_type.service'
|
||||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||||
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
|
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
|
||||||
import { filter, find, unescape, uniqBy } from 'lodash'
|
import { filter, unescape, uniqBy } from 'lodash'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
|
||||||
const Status = {
|
const Status = {
|
||||||
name: 'Status',
|
name: 'Status',
|
||||||
|
@ -37,25 +39,19 @@ const Status = {
|
||||||
replying: false,
|
replying: false,
|
||||||
unmuted: false,
|
unmuted: false,
|
||||||
userExpanded: false,
|
userExpanded: false,
|
||||||
preview: null,
|
|
||||||
showPreview: false,
|
|
||||||
showingTall: this.inConversation && this.focused,
|
showingTall: this.inConversation && this.focused,
|
||||||
showingLongSubject: false,
|
showingLongSubject: false,
|
||||||
error: null,
|
error: null,
|
||||||
expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined'
|
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject,
|
||||||
? !this.$store.state.instance.collapseMessageWithSubject
|
|
||||||
: !this.$store.state.config.collapseMessageWithSubject,
|
|
||||||
betterShadow: this.$store.state.interface.browserSupport.cssFilter
|
betterShadow: this.$store.state.interface.browserSupport.cssFilter
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
localCollapseSubjectDefault () {
|
localCollapseSubjectDefault () {
|
||||||
return typeof this.$store.state.config.collapseMessageWithSubject === 'undefined'
|
return this.mergedConfig.collapseMessageWithSubject
|
||||||
? this.$store.state.instance.collapseMessageWithSubject
|
|
||||||
: this.$store.state.config.collapseMessageWithSubject
|
|
||||||
},
|
},
|
||||||
muteWords () {
|
muteWords () {
|
||||||
return this.$store.state.config.muteWords
|
return this.mergedConfig.muteWords
|
||||||
},
|
},
|
||||||
repeaterClass () {
|
repeaterClass () {
|
||||||
const user = this.statusoid.user
|
const user = this.statusoid.user
|
||||||
|
@ -70,18 +66,18 @@ const Status = {
|
||||||
},
|
},
|
||||||
repeaterStyle () {
|
repeaterStyle () {
|
||||||
const user = this.statusoid.user
|
const user = this.statusoid.user
|
||||||
const highlight = this.$store.state.config.highlight
|
const highlight = this.mergedConfig.highlight
|
||||||
return highlightStyle(highlight[user.screen_name])
|
return highlightStyle(highlight[user.screen_name])
|
||||||
},
|
},
|
||||||
userStyle () {
|
userStyle () {
|
||||||
if (this.noHeading) return
|
if (this.noHeading) return
|
||||||
const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user
|
const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user
|
||||||
const highlight = this.$store.state.config.highlight
|
const highlight = this.mergedConfig.highlight
|
||||||
return highlightStyle(highlight[user.screen_name])
|
return highlightStyle(highlight[user.screen_name])
|
||||||
},
|
},
|
||||||
hideAttachments () {
|
hideAttachments () {
|
||||||
return (this.$store.state.config.hideAttachments && !this.inConversation) ||
|
return (this.mergedConfig.hideAttachments && !this.inConversation) ||
|
||||||
(this.$store.state.config.hideAttachmentsInConv && this.inConversation)
|
(this.mergedConfig.hideAttachmentsInConv && this.inConversation)
|
||||||
},
|
},
|
||||||
userProfileLink () {
|
userProfileLink () {
|
||||||
return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name)
|
return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name)
|
||||||
|
@ -120,9 +116,7 @@ const Status = {
|
||||||
},
|
},
|
||||||
muted () { return !this.unmuted && ((!this.inProfile && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) },
|
muted () { return !this.unmuted && ((!this.inProfile && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) },
|
||||||
hideFilteredStatuses () {
|
hideFilteredStatuses () {
|
||||||
return typeof this.$store.state.config.hideFilteredStatuses === 'undefined'
|
return this.mergedConfig.hideFilteredStatuses
|
||||||
? this.$store.state.instance.hideFilteredStatuses
|
|
||||||
: this.$store.state.config.hideFilteredStatuses
|
|
||||||
},
|
},
|
||||||
hideStatus () {
|
hideStatus () {
|
||||||
return (this.hideReply || this.deleted) || (this.muted && this.hideFilteredStatuses)
|
return (this.hideReply || this.deleted) || (this.muted && this.hideFilteredStatuses)
|
||||||
|
@ -163,7 +157,7 @@ const Status = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideReply () {
|
hideReply () {
|
||||||
if (this.$store.state.config.replyVisibility === 'all') {
|
if (this.mergedConfig.replyVisibility === 'all') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (this.inConversation || !this.isReply) {
|
if (this.inConversation || !this.isReply) {
|
||||||
|
@ -175,7 +169,7 @@ const Status = {
|
||||||
if (this.status.type === 'retweet') {
|
if (this.status.type === 'retweet') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const checkFollowing = this.$store.state.config.replyVisibility === 'following'
|
const checkFollowing = this.mergedConfig.replyVisibility === 'following'
|
||||||
for (var i = 0; i < this.status.attentions.length; ++i) {
|
for (var i = 0; i < this.status.attentions.length; ++i) {
|
||||||
if (this.status.user.id === this.status.attentions[i].id) {
|
if (this.status.user.id === this.status.attentions[i].id) {
|
||||||
continue
|
continue
|
||||||
|
@ -220,9 +214,7 @@ const Status = {
|
||||||
replySubject () {
|
replySubject () {
|
||||||
if (!this.status.summary) return ''
|
if (!this.status.summary) return ''
|
||||||
const decodedSummary = unescape(this.status.summary)
|
const decodedSummary = unescape(this.status.summary)
|
||||||
const behavior = typeof this.$store.state.config.subjectLineBehavior === 'undefined'
|
const behavior = this.mergedConfig.subjectLineBehavior
|
||||||
? this.$store.state.instance.subjectLineBehavior
|
|
||||||
: this.$store.state.config.subjectLineBehavior
|
|
||||||
const startsWithRe = decodedSummary.match(/^re[: ]/i)
|
const startsWithRe = decodedSummary.match(/^re[: ]/i)
|
||||||
if ((behavior !== 'noop' && startsWithRe) || behavior === 'masto') {
|
if ((behavior !== 'noop' && startsWithRe) || behavior === 'masto') {
|
||||||
return decodedSummary
|
return decodedSummary
|
||||||
|
@ -233,8 +225,8 @@ const Status = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
attachmentSize () {
|
attachmentSize () {
|
||||||
if ((this.$store.state.config.hideAttachments && !this.inConversation) ||
|
if ((this.mergedConfig.hideAttachments && !this.inConversation) ||
|
||||||
(this.$store.state.config.hideAttachmentsInConv && this.inConversation) ||
|
(this.mergedConfig.hideAttachmentsInConv && this.inConversation) ||
|
||||||
(this.status.attachments.length > this.maxThumbnails)) {
|
(this.status.attachments.length > this.maxThumbnails)) {
|
||||||
return 'hide'
|
return 'hide'
|
||||||
} else if (this.compact) {
|
} else if (this.compact) {
|
||||||
|
@ -246,7 +238,7 @@ const Status = {
|
||||||
if (this.attachmentSize === 'hide') {
|
if (this.attachmentSize === 'hide') {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
return this.$store.state.config.playVideosInModal
|
return this.mergedConfig.playVideosInModal
|
||||||
? ['image', 'video']
|
? ['image', 'video']
|
||||||
: ['image']
|
: ['image']
|
||||||
},
|
},
|
||||||
|
@ -261,7 +253,7 @@ const Status = {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
maxThumbnails () {
|
maxThumbnails () {
|
||||||
return this.$store.state.config.maxThumbnails
|
return this.mergedConfig.maxThumbnails
|
||||||
},
|
},
|
||||||
contentHtml () {
|
contentHtml () {
|
||||||
if (!this.status.summary_html) {
|
if (!this.status.summary_html) {
|
||||||
|
@ -284,10 +276,9 @@ const Status = {
|
||||||
return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ')
|
return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ')
|
||||||
},
|
},
|
||||||
hidePostStats () {
|
hidePostStats () {
|
||||||
return typeof this.$store.state.config.hidePostStats === 'undefined'
|
return this.mergedConfig.hidePostStats
|
||||||
? this.$store.state.instance.hidePostStats
|
},
|
||||||
: this.$store.state.config.hidePostStats
|
...mapGetters(['mergedConfig'])
|
||||||
}
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Attachment,
|
Attachment,
|
||||||
|
@ -301,7 +292,8 @@ const Status = {
|
||||||
Gallery,
|
Gallery,
|
||||||
LinkPreview,
|
LinkPreview,
|
||||||
AvatarList,
|
AvatarList,
|
||||||
Timeago
|
Timeago,
|
||||||
|
StatusPopover
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
visibilityIcon (visibility) {
|
visibilityIcon (visibility) {
|
||||||
|
@ -376,27 +368,6 @@ const Status = {
|
||||||
this.expandingSubject = true
|
this.expandingSubject = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replyEnter (id, event) {
|
|
||||||
this.showPreview = true
|
|
||||||
const targetId = id
|
|
||||||
const statuses = this.$store.state.statuses.allStatuses
|
|
||||||
|
|
||||||
if (!this.preview) {
|
|
||||||
// if we have the status somewhere already
|
|
||||||
this.preview = find(statuses, { 'id': targetId })
|
|
||||||
// or if we have to fetch it
|
|
||||||
if (!this.preview) {
|
|
||||||
this.$store.state.api.backendInteractor.fetchStatus({ id }).then((status) => {
|
|
||||||
this.preview = status
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if (this.preview.id !== targetId) {
|
|
||||||
this.preview = find(statuses, { 'id': targetId })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
replyLeave () {
|
|
||||||
this.showPreview = false
|
|
||||||
},
|
|
||||||
generateUserProfileLink (id, name) {
|
generateUserProfileLink (id, name) {
|
||||||
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
|
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
|
||||||
},
|
},
|
||||||
|
|
|
@ -174,20 +174,26 @@
|
||||||
v-if="isReply"
|
v-if="isReply"
|
||||||
class="reply-to-and-accountname"
|
class="reply-to-and-accountname"
|
||||||
>
|
>
|
||||||
<a
|
<StatusPopover
|
||||||
class="reply-to"
|
v-if="!isPreview"
|
||||||
href="#"
|
:status-id="status.in_reply_to_status_id"
|
||||||
:aria-label="$t('tool_tip.reply')"
|
|
||||||
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
|
|
||||||
@mouseenter.prevent.stop="replyEnter(status.in_reply_to_status_id, $event)"
|
|
||||||
@mouseleave.prevent.stop="replyLeave()"
|
|
||||||
>
|
>
|
||||||
<i
|
<a
|
||||||
v-if="!isPreview"
|
class="reply-to"
|
||||||
class="button-icon icon-reply"
|
href="#"
|
||||||
/>
|
:aria-label="$t('tool_tip.reply')"
|
||||||
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
|
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
|
||||||
</a>
|
>
|
||||||
|
<i class="button-icon icon-reply" />
|
||||||
|
<span class="faint-link reply-to-text">{{ $t('status.reply_to') }}</span>
|
||||||
|
</a>
|
||||||
|
</StatusPopover>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="reply-to"
|
||||||
|
>
|
||||||
|
<span class="reply-to-text">{{ $t('status.reply_to') }}</span>
|
||||||
|
</span>
|
||||||
<router-link :to="replyProfileLink">
|
<router-link :to="replyProfileLink">
|
||||||
{{ replyToName }}
|
{{ replyToName }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -199,50 +205,25 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="inConversation && !isPreview"
|
v-if="inConversation && !isPreview && replies && replies.length"
|
||||||
class="replies"
|
class="replies"
|
||||||
>
|
>
|
||||||
<span
|
<span class="faint">{{ $t('status.replies_list') }}</span>
|
||||||
v-if="replies && replies.length"
|
<StatusPopover
|
||||||
class="faint"
|
v-for="reply in replies"
|
||||||
>{{ $t('status.replies_list') }}</span>
|
:key="reply.id"
|
||||||
<template v-if="replies">
|
:status-id="reply.id"
|
||||||
<span
|
>
|
||||||
v-for="reply in replies"
|
<a
|
||||||
:key="reply.id"
|
href="#"
|
||||||
class="reply-link faint"
|
class="reply-link"
|
||||||
>
|
@click.prevent="gotoOriginal(reply.id)"
|
||||||
<a
|
>{{ reply.name }}</a>
|
||||||
href="#"
|
</StatusPopover>
|
||||||
@click.prevent="gotoOriginal(reply.id)"
|
|
||||||
@mouseenter="replyEnter(reply.id, $event)"
|
|
||||||
@mouseout="replyLeave()"
|
|
||||||
>{{ reply.name }}</a>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="showPreview"
|
|
||||||
class="status-preview-container"
|
|
||||||
>
|
|
||||||
<status
|
|
||||||
v-if="preview"
|
|
||||||
class="status-preview"
|
|
||||||
:is-preview="true"
|
|
||||||
:statusoid="preview"
|
|
||||||
:compact="true"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="status-preview status-preview-loading"
|
|
||||||
>
|
|
||||||
<i class="icon-spin4 animate-spin" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="longSubject"
|
v-if="longSubject"
|
||||||
class="status-content-wrapper"
|
class="status-content-wrapper"
|
||||||
|
@ -439,18 +420,6 @@ $status-margin: 0.75em;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-preview.status-el {
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px;
|
|
||||||
border-color: $fallback--border;
|
|
||||||
border-color: var(--border, $fallback--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-preview-container {
|
|
||||||
position: relative;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-pin {
|
.status-pin {
|
||||||
padding: $status-margin $status-margin 0;
|
padding: $status-margin $status-margin 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -458,44 +427,6 @@ $status-margin: 0.75em;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-preview {
|
|
||||||
position: absolute;
|
|
||||||
max-width: 95%;
|
|
||||||
display: flex;
|
|
||||||
background-color: $fallback--bg;
|
|
||||||
background-color: var(--bg, $fallback--bg);
|
|
||||||
border-color: $fallback--border;
|
|
||||||
border-color: var(--border, $fallback--border);
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px;
|
|
||||||
border-radius: $fallback--tooltipRadius;
|
|
||||||
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
|
|
||||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
|
||||||
box-shadow: var(--popupShadow);
|
|
||||||
margin-top: 0.25em;
|
|
||||||
margin-left: 0.5em;
|
|
||||||
z-index: 50;
|
|
||||||
|
|
||||||
.status {
|
|
||||||
flex: 1;
|
|
||||||
border: 0;
|
|
||||||
min-width: 15em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-preview-loading {
|
|
||||||
display: block;
|
|
||||||
min-width: 15em;
|
|
||||||
padding: 1em;
|
|
||||||
text-align: center;
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-left {
|
.media-left {
|
||||||
margin-right: $status-margin;
|
margin-right: $status-margin;
|
||||||
}
|
}
|
||||||
|
@ -553,11 +484,6 @@ $status-margin: 0.75em;
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
small {
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
@ -568,6 +494,11 @@ $status-margin: 0.75em;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
.name-and-account-name {
|
.name-and-account-name {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
@ -600,6 +531,7 @@ $status-margin: 0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading-reply-row {
|
.heading-reply-row {
|
||||||
|
position: relative;
|
||||||
align-content: baseline;
|
align-content: baseline;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
@ -608,11 +540,13 @@ $status-margin: 0.75em;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
||||||
a {
|
> .reply-to-and-accountname > a {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,6 +573,8 @@ $status-margin: 0.75em;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
margin: 0 0.4em 0 0.2em;
|
margin: 0 0.4em 0 0.2em;
|
||||||
|
color: $fallback--faint;
|
||||||
|
color: var(--faint, $fallback--faint);
|
||||||
}
|
}
|
||||||
|
|
||||||
.replies-separator {
|
.replies-separator {
|
||||||
|
@ -840,6 +776,11 @@ $status-margin: 0.75em;
|
||||||
&.button-icon-active {
|
&.button-icon-active {
|
||||||
color: $fallback--cBlue;
|
color: $fallback--cBlue;
|
||||||
color: var(--cBlue, $fallback--cBlue);
|
color: var(--cBlue, $fallback--cBlue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-icon.icon-reply {
|
||||||
|
&:not(.button-icon-disabled) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/components/status_popover/status_popover.js
Normal file
34
src/components/status_popover/status_popover.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { find } from 'lodash'
|
||||||
|
|
||||||
|
const StatusPopover = {
|
||||||
|
name: 'StatusPopover',
|
||||||
|
props: [
|
||||||
|
'statusId'
|
||||||
|
],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
popperOptions: {
|
||||||
|
modifiers: {
|
||||||
|
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
status () {
|
||||||
|
return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Status: () => import('../status/status.vue')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
enter () {
|
||||||
|
if (!this.status) {
|
||||||
|
this.$store.dispatch('fetchStatus', this.statusId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StatusPopover
|
85
src/components/status_popover/status_popover.vue
Normal file
85
src/components/status_popover/status_popover.vue
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<v-popover
|
||||||
|
popover-class="status-popover"
|
||||||
|
placement="top-start"
|
||||||
|
:popper-options="popperOptions"
|
||||||
|
@show="enter()"
|
||||||
|
>
|
||||||
|
<template slot="popover">
|
||||||
|
<Status
|
||||||
|
v-if="status"
|
||||||
|
:is-preview="true"
|
||||||
|
:statusoid="status"
|
||||||
|
:compact="true"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="status-preview-loading"
|
||||||
|
>
|
||||||
|
<i class="icon-spin4 animate-spin" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
</v-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./status_popover.js" ></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.tooltip.popover.status-popover {
|
||||||
|
font-size: 1rem;
|
||||||
|
min-width: 15em;
|
||||||
|
max-width: 95%;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
|
||||||
|
.popover-inner {
|
||||||
|
border-color: $fallback--border;
|
||||||
|
border-color: var(--border, $fallback--border);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: $fallback--tooltipRadius;
|
||||||
|
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
|
||||||
|
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
box-shadow: var(--popupShadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-arrow::before {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
left: -7px;
|
||||||
|
border: solid 7px transparent;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[x-placement^="bottom-start"] .popover-arrow::before {
|
||||||
|
top: -2px;
|
||||||
|
border-top-width: 0;
|
||||||
|
border-bottom-color: $fallback--border;
|
||||||
|
border-bottom-color: var(--border, $fallback--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[x-placement^="top-start"] .popover-arrow::before {
|
||||||
|
bottom: -2px;
|
||||||
|
border-bottom-width: 0;
|
||||||
|
border-top-color: $fallback--border;
|
||||||
|
border-top-color: var(--border, $fallback--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-el.status-el {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-preview-loading {
|
||||||
|
padding: 1em;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -3,11 +3,12 @@ const StillImage = {
|
||||||
'src',
|
'src',
|
||||||
'referrerpolicy',
|
'referrerpolicy',
|
||||||
'mimetype',
|
'mimetype',
|
||||||
'imageLoadError'
|
'imageLoadError',
|
||||||
|
'imageLoadHandler'
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
stopGifs: this.$store.state.config.stopGifs
|
stopGifs: this.$store.getters.mergedConfig.stopGifs
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -17,6 +18,7 @@ const StillImage = {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onLoad () {
|
onLoad () {
|
||||||
|
this.imageLoadHandler && this.imageLoadHandler(this.$refs.src)
|
||||||
const canvas = this.$refs.canvas
|
const canvas = this.$refs.canvas
|
||||||
if (!canvas) return
|
if (!canvas) return
|
||||||
const width = this.$refs.src.naturalWidth
|
const width = this.$refs.src.naturalWidth
|
||||||
|
|
|
@ -10,6 +10,7 @@ import ContrastRatio from '../contrast_ratio/contrast_ratio.vue'
|
||||||
import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
||||||
import Preview from './preview.vue'
|
import Preview from './preview.vue'
|
||||||
import ExportImport from '../export_import/export_import.vue'
|
import ExportImport from '../export_import/export_import.vue'
|
||||||
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
|
||||||
// List of color values used in v1
|
// List of color values used in v1
|
||||||
const v1OnlyNames = [
|
const v1OnlyNames = [
|
||||||
|
@ -27,7 +28,7 @@ export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
availableStyles: [],
|
availableStyles: [],
|
||||||
selected: this.$store.state.config.theme,
|
selected: this.$store.getters.mergedConfig.theme,
|
||||||
|
|
||||||
previewShadows: {},
|
previewShadows: {},
|
||||||
previewColors: {},
|
previewColors: {},
|
||||||
|
@ -112,7 +113,7 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.normalizeLocalState(this.$store.state.config.customTheme)
|
this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme)
|
||||||
if (typeof this.shadowSelected === 'undefined') {
|
if (typeof this.shadowSelected === 'undefined') {
|
||||||
this.shadowSelected = this.shadowsAvailable[0]
|
this.shadowSelected = this.shadowsAvailable[0]
|
||||||
}
|
}
|
||||||
|
@ -341,7 +342,8 @@ export default {
|
||||||
FontControl,
|
FontControl,
|
||||||
TabSwitcher,
|
TabSwitcher,
|
||||||
Preview,
|
Preview,
|
||||||
ExportImport
|
ExportImport,
|
||||||
|
Checkbox
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setCustomTheme () {
|
setCustomTheme () {
|
||||||
|
@ -368,9 +370,9 @@ export default {
|
||||||
return version >= 1 || version <= 2
|
return version >= 1 || version <= 2
|
||||||
},
|
},
|
||||||
clearAll () {
|
clearAll () {
|
||||||
const state = this.$store.state.config.customTheme
|
const state = this.$store.getters.mergedConfig.customTheme
|
||||||
const version = state.colors ? 2 : 'l1'
|
const version = state.colors ? 2 : 'l1'
|
||||||
this.normalizeLocalState(this.$store.state.config.customTheme, version)
|
this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme, version)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Clears all the extra stuff when loading V1 theme
|
// Clears all the extra stuff when loading V1 theme
|
||||||
|
|
|
@ -42,44 +42,29 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="save-load-options">
|
<div class="save-load-options">
|
||||||
<span class="keep-option">
|
<span class="keep-option">
|
||||||
<input
|
<Checkbox v-model="keepColor">
|
||||||
id="keep-color"
|
{{ $t('settings.style.switcher.keep_color') }}
|
||||||
v-model="keepColor"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="keep-color">{{ $t('settings.style.switcher.keep_color') }}</label>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="keep-option">
|
<span class="keep-option">
|
||||||
<input
|
<Checkbox v-model="keepShadows">
|
||||||
id="keep-shadows"
|
{{ $t('settings.style.switcher.keep_shadows') }}
|
||||||
v-model="keepShadows"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="keep-shadows">{{ $t('settings.style.switcher.keep_shadows') }}</label>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="keep-option">
|
<span class="keep-option">
|
||||||
<input
|
<Checkbox v-model="keepOpacity">
|
||||||
id="keep-opacity"
|
{{ $t('settings.style.switcher.keep_opacity') }}
|
||||||
v-model="keepOpacity"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="keep-opacity">{{ $t('settings.style.switcher.keep_opacity') }}</label>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="keep-option">
|
<span class="keep-option">
|
||||||
<input
|
<Checkbox v-model="keepRoundness">
|
||||||
id="keep-roundness"
|
{{ $t('settings.style.switcher.keep_roundness') }}
|
||||||
v-model="keepRoundness"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="keep-roundness">{{ $t('settings.style.switcher.keep_roundness') }}</label>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="keep-option">
|
<span class="keep-option">
|
||||||
<input
|
<Checkbox v-model="keepFonts">
|
||||||
id="keep-fonts"
|
{{ $t('settings.style.switcher.keep_fonts') }}
|
||||||
v-model="keepFonts"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="keep-fonts">{{ $t('settings.style.switcher.keep_fonts') }}</label>
|
|
||||||
</span>
|
</span>
|
||||||
<p>{{ $t('settings.style.switcher.save_load_hint') }}</p>
|
<p>{{ $t('settings.style.switcher.save_load_hint') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -141,7 +141,7 @@ const Timeline = {
|
||||||
const bodyBRect = document.body.getBoundingClientRect()
|
const bodyBRect = document.body.getBoundingClientRect()
|
||||||
const height = Math.max(bodyBRect.height, -(bodyBRect.y))
|
const height = Math.max(bodyBRect.height, -(bodyBRect.y))
|
||||||
if (this.timeline.loading === false &&
|
if (this.timeline.loading === false &&
|
||||||
this.$store.state.config.autoLoad &&
|
this.$store.getters.mergedConfig.autoLoad &&
|
||||||
this.$el.offsetHeight > 0 &&
|
this.$el.offsetHeight > 0 &&
|
||||||
(window.innerHeight + window.pageYOffset) >= (height - 750)) {
|
(window.innerHeight + window.pageYOffset) >= (height - 750)) {
|
||||||
this.fetchOlderStatuses()
|
this.fetchOlderStatuses()
|
||||||
|
@ -153,7 +153,7 @@ const Timeline = {
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
newStatusCount (count) {
|
newStatusCount (count) {
|
||||||
if (!this.$store.state.config.streaming) {
|
if (!this.$store.getters.mergedConfig.streaming) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
@ -162,7 +162,7 @@ const Timeline = {
|
||||||
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)
|
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)
|
||||||
if (top < 15 &&
|
if (top < 15 &&
|
||||||
!this.paused &&
|
!this.paused &&
|
||||||
!(this.unfocused && this.$store.state.config.pauseOnUnfocused)
|
!(this.unfocused && this.$store.getters.mergedConfig.pauseOnUnfocused)
|
||||||
) {
|
) {
|
||||||
this.showNewStatuses()
|
this.showNewStatuses()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
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'
|
||||||
|
import FollowButton from '../follow_button/follow_button.vue'
|
||||||
import ModerationTools from '../moderation_tools/moderation_tools.vue'
|
import ModerationTools from '../moderation_tools/moderation_tools.vue'
|
||||||
|
import AccountActions from '../account_actions/account_actions.vue'
|
||||||
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
|
||||||
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'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [ 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar' ],
|
props: [
|
||||||
|
'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar'
|
||||||
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
followRequestInProgress: false,
|
followRequestInProgress: false,
|
||||||
hideUserStatsLocal: typeof this.$store.state.config.hideUserStats === 'undefined'
|
|
||||||
? this.$store.state.instance.hideUserStats
|
|
||||||
: this.$store.state.config.hideUserStats,
|
|
||||||
betterShadow: this.$store.state.interface.browserSupport.cssFilter
|
betterShadow: this.$store.state.interface.browserSupport.cssFilter
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -29,9 +30,9 @@ export default {
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
style () {
|
style () {
|
||||||
const color = this.$store.state.config.customTheme.colors
|
const color = this.$store.getters.mergedConfig.customTheme.colors
|
||||||
? this.$store.state.config.customTheme.colors.bg // v2
|
? this.$store.getters.mergedConfig.customTheme.colors.bg // v2
|
||||||
: this.$store.state.config.colors.bg // v1
|
: this.$store.getters.mergedConfig.colors.bg // v1
|
||||||
|
|
||||||
if (color) {
|
if (color) {
|
||||||
const rgb = (typeof color === 'string') ? hex2rgb(color) : color
|
const rgb = (typeof color === 'string') ? hex2rgb(color) : color
|
||||||
|
@ -63,21 +64,22 @@ export default {
|
||||||
},
|
},
|
||||||
userHighlightType: {
|
userHighlightType: {
|
||||||
get () {
|
get () {
|
||||||
const data = this.$store.state.config.highlight[this.user.screen_name]
|
const data = this.$store.getters.mergedConfig.highlight[this.user.screen_name]
|
||||||
return (data && data.type) || 'disabled'
|
return (data && data.type) || 'disabled'
|
||||||
},
|
},
|
||||||
set (type) {
|
set (type) {
|
||||||
const data = this.$store.state.config.highlight[this.user.screen_name]
|
const data = this.$store.getters.mergedConfig.highlight[this.user.screen_name]
|
||||||
if (type !== 'disabled') {
|
if (type !== 'disabled') {
|
||||||
this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: (data && data.color) || '#FFFFFF', type })
|
this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: (data && data.color) || '#FFFFFF', type })
|
||||||
} else {
|
} else {
|
||||||
this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: undefined })
|
this.$store.dispatch('setHighlight', { user: this.user.screen_name, color: undefined })
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
...mapGetters(['mergedConfig'])
|
||||||
},
|
},
|
||||||
userHighlightColor: {
|
userHighlightColor: {
|
||||||
get () {
|
get () {
|
||||||
const data = this.$store.state.config.highlight[this.user.screen_name]
|
const data = this.$store.getters.mergedConfig.highlight[this.user.screen_name]
|
||||||
return data && data.color
|
return data && data.color
|
||||||
},
|
},
|
||||||
set (color) {
|
set (color) {
|
||||||
|
@ -90,36 +92,18 @@ export default {
|
||||||
const validRole = rights.admin || rights.moderator
|
const validRole = rights.admin || rights.moderator
|
||||||
const roleTitle = rights.admin ? 'admin' : 'moderator'
|
const roleTitle = rights.admin ? 'admin' : 'moderator'
|
||||||
return validRole && roleTitle
|
return validRole && roleTitle
|
||||||
}
|
},
|
||||||
|
...mapGetters(['mergedConfig'])
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
RemoteFollow,
|
RemoteFollow,
|
||||||
ModerationTools,
|
ModerationTools,
|
||||||
ProgressButton
|
AccountActions,
|
||||||
|
ProgressButton,
|
||||||
|
FollowButton
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
followUser () {
|
|
||||||
const store = this.$store
|
|
||||||
this.followRequestInProgress = true
|
|
||||||
requestFollow(this.user, store).then(() => {
|
|
||||||
this.followRequestInProgress = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
unfollowUser () {
|
|
||||||
const store = this.$store
|
|
||||||
this.followRequestInProgress = true
|
|
||||||
requestUnfollow(this.user, store).then(() => {
|
|
||||||
this.followRequestInProgress = false
|
|
||||||
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
|
|
||||||
})
|
|
||||||
},
|
|
||||||
blockUser () {
|
|
||||||
this.$store.dispatch('blockUser', this.user.id)
|
|
||||||
},
|
|
||||||
unblockUser () {
|
|
||||||
this.$store.dispatch('unblockUser', this.user.id)
|
|
||||||
},
|
|
||||||
muteUser () {
|
muteUser () {
|
||||||
this.$store.dispatch('muteUser', this.user.id)
|
this.$store.dispatch('muteUser', this.user.id)
|
||||||
},
|
},
|
||||||
|
@ -147,10 +131,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
userProfileLink (user) {
|
userProfileLink (user) {
|
||||||
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames)
|
return generateProfileLink(
|
||||||
},
|
user.id, user.screen_name,
|
||||||
reportUser () {
|
this.$store.state.instance.restrictedNicknames
|
||||||
this.$store.dispatch('openUserReportingModal', this.user.id)
|
)
|
||||||
},
|
},
|
||||||
zoomAvatar () {
|
zoomAvatar () {
|
||||||
const attachment = {
|
const attachment = {
|
||||||
|
@ -159,9 +143,6 @@ export default {
|
||||||
}
|
}
|
||||||
this.$store.dispatch('setMedia', [attachment])
|
this.$store.dispatch('setMedia', [attachment])
|
||||||
this.$store.dispatch('setCurrent', attachment)
|
this.$store.dispatch('setCurrent', attachment)
|
||||||
},
|
|
||||||
mentionUser () {
|
|
||||||
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,11 @@
|
||||||
>
|
>
|
||||||
<i class="icon-link-ext usersettings" />
|
<i class="icon-link-ext usersettings" />
|
||||||
</a>
|
</a>
|
||||||
|
<AccountActions
|
||||||
|
v-if="isOtherUser && loggedIn"
|
||||||
|
:user="user"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-line">
|
<div class="bottom-line">
|
||||||
<router-link
|
<router-link
|
||||||
class="user-screen-name"
|
class="user-screen-name"
|
||||||
|
@ -81,7 +84,7 @@
|
||||||
>{{ visibleRole }}</span>
|
>{{ visibleRole }}</span>
|
||||||
<span v-if="user.locked"><i class="icon icon-lock" /></span>
|
<span v-if="user.locked"><i class="icon icon-lock" /></span>
|
||||||
<span
|
<span
|
||||||
v-if="!hideUserStatsLocal && !hideBio"
|
v-if="!mergedConfig.hideUserStats && !hideBio"
|
||||||
class="dailyAvg"
|
class="dailyAvg"
|
||||||
>{{ dailyAvg }} {{ $t('user_card.per_day') }}</span>
|
>{{ dailyAvg }} {{ $t('user_card.per_day') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -135,72 +138,27 @@
|
||||||
v-if="loggedIn && isOtherUser"
|
v-if="loggedIn && isOtherUser"
|
||||||
class="user-interactions"
|
class="user-interactions"
|
||||||
>
|
>
|
||||||
<div v-if="!user.following">
|
<div class="btn-group">
|
||||||
<button
|
<FollowButton :user="user" />
|
||||||
class="btn btn-default btn-block"
|
<template v-if="user.following">
|
||||||
:disabled="followRequestInProgress"
|
<ProgressButton
|
||||||
:title="user.requested ? $t('user_card.follow_again') : ''"
|
v-if="!user.subscribed"
|
||||||
@click="followUser"
|
class="btn btn-default"
|
||||||
>
|
:click="subscribeUser"
|
||||||
<template v-if="followRequestInProgress">
|
:title="$t('user_card.subscribe')"
|
||||||
{{ $t('user_card.follow_progress') }}
|
>
|
||||||
</template>
|
<i class="icon-bell-alt" />
|
||||||
<template v-else-if="user.requested">
|
</ProgressButton>
|
||||||
{{ $t('user_card.follow_sent') }}
|
<ProgressButton
|
||||||
</template>
|
v-else
|
||||||
<template v-else>
|
class="btn btn-default pressed"
|
||||||
{{ $t('user_card.follow') }}
|
:click="unsubscribeUser"
|
||||||
</template>
|
:title="$t('user_card.unsubscribe')"
|
||||||
</button>
|
>
|
||||||
|
<i class="icon-bell-ringing-o" />
|
||||||
|
</ProgressButton>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="followRequestInProgress">
|
|
||||||
<button
|
|
||||||
class="btn btn-default btn-block pressed"
|
|
||||||
disabled
|
|
||||||
:title="$t('user_card.follow_unfollow')"
|
|
||||||
@click="unfollowUser"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.follow_progress') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="btn-group"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="btn btn-default pressed"
|
|
||||||
:title="$t('user_card.follow_unfollow')"
|
|
||||||
@click="unfollowUser"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.following') }}
|
|
||||||
</button>
|
|
||||||
<ProgressButton
|
|
||||||
v-if="!user.subscribed"
|
|
||||||
class="btn btn-default"
|
|
||||||
:click="subscribeUser"
|
|
||||||
:title="$t('user_card.subscribe')"
|
|
||||||
>
|
|
||||||
<i class="icon-bell-alt" />
|
|
||||||
</ProgressButton>
|
|
||||||
<ProgressButton
|
|
||||||
v-else
|
|
||||||
class="btn btn-default pressed"
|
|
||||||
:click="unsubscribeUser"
|
|
||||||
:title="$t('user_card.unsubscribe')"
|
|
||||||
>
|
|
||||||
<i class="icon-bell-ringing-o" />
|
|
||||||
</ProgressButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-default btn-block"
|
|
||||||
@click="mentionUser"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.mention') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
v-if="user.muted"
|
v-if="user.muted"
|
||||||
|
@ -217,33 +175,6 @@
|
||||||
{{ $t('user_card.mute') }}
|
{{ $t('user_card.mute') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
v-if="user.statusnet_blocking"
|
|
||||||
class="btn btn-default btn-block pressed"
|
|
||||||
@click="unblockUser"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.blocked') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
class="btn btn-default btn-block"
|
|
||||||
@click="blockUser"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.block') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-default btn-block"
|
|
||||||
@click="reportUser"
|
|
||||||
>
|
|
||||||
{{ $t('user_card.report') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ModerationTools
|
<ModerationTools
|
||||||
v-if="loggedIn.role === "admin""
|
v-if="loggedIn.role === "admin""
|
||||||
:user="user"
|
:user="user"
|
||||||
|
@ -262,7 +193,7 @@
|
||||||
class="panel-body"
|
class="panel-body"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="!hideUserStatsLocal && switcher"
|
v-if="!mergedConfig.hideUserStats && switcher"
|
||||||
class="user-counts"
|
class="user-counts"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -345,6 +276,8 @@
|
||||||
mask-composite: exclude;
|
mask-composite: exclude;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
mask-size: 100% 60%;
|
mask-size: 100% 60%;
|
||||||
|
border-top-left-radius: calc(var(--panelRadius) - 1px);
|
||||||
|
border-top-right-radius: calc(var(--panelRadius) - 1px);
|
||||||
|
|
||||||
&.hide-bio {
|
&.hide-bio {
|
||||||
mask-size: 100% 40px;
|
mask-size: 100% 40px;
|
||||||
|
@ -587,13 +520,12 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
justify-content: space-between;
|
|
||||||
margin-right: -.75em;
|
margin-right: -.75em;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
flex: 1 0 0;
|
|
||||||
margin: 0 .75em .6em 0;
|
margin: 0 .75em .6em 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
min-width: 95px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
import Status from '../status/status.vue'
|
import Status from '../status/status.vue'
|
||||||
import List from '../list/list.vue'
|
import List from '../list/list.vue'
|
||||||
import Checkbox from '../checkbox/checkbox.vue'
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
|
import Modal from '../modal/modal.vue'
|
||||||
|
|
||||||
const UserReportingModal = {
|
const UserReportingModal = {
|
||||||
components: {
|
components: {
|
||||||
Status,
|
Status,
|
||||||
List,
|
List,
|
||||||
Checkbox
|
Checkbox,
|
||||||
|
Modal
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<Modal
|
||||||
v-if="isOpen"
|
v-if="isOpen"
|
||||||
class="modal-view"
|
@backdropClicked="closeModal"
|
||||||
@click="closeModal"
|
|
||||||
>
|
>
|
||||||
<div
|
<div class="user-reporting-panel panel">
|
||||||
class="user-reporting-panel panel"
|
|
||||||
@click.stop=""
|
|
||||||
>
|
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
{{ $t('user_reporting.title', [user.screen_name]) }}
|
{{ $t('user_reporting.title', [user.screen_name]) }}
|
||||||
|
@ -69,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./user_reporting_modal.js"></script>
|
<script src="./user_reporting_modal.js"></script>
|
||||||
|
|
|
@ -17,6 +17,7 @@ import Autosuggest from '../autosuggest/autosuggest.vue'
|
||||||
import Importer from '../importer/importer.vue'
|
import Importer from '../importer/importer.vue'
|
||||||
import Exporter from '../exporter/exporter.vue'
|
import Exporter from '../exporter/exporter.vue'
|
||||||
import withSubscription from '../../hocs/with_subscription/with_subscription'
|
import withSubscription from '../../hocs/with_subscription/with_subscription'
|
||||||
|
import Checkbox from '../checkbox/checkbox.vue'
|
||||||
import Mfa from './mfa.vue'
|
import Mfa from './mfa.vue'
|
||||||
|
|
||||||
const BlockList = withSubscription({
|
const BlockList = withSubscription({
|
||||||
|
@ -34,6 +35,7 @@ const MuteList = withSubscription({
|
||||||
const UserSettings = {
|
const UserSettings = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
newEmail: '',
|
||||||
newName: this.$store.state.users.currentUser.name,
|
newName: this.$store.state.users.currentUser.name,
|
||||||
newBio: unescape(this.$store.state.users.currentUser.description),
|
newBio: unescape(this.$store.state.users.currentUser.description),
|
||||||
newLocked: this.$store.state.users.currentUser.locked,
|
newLocked: this.$store.state.users.currentUser.locked,
|
||||||
|
@ -55,6 +57,9 @@ const UserSettings = {
|
||||||
backgroundPreview: null,
|
backgroundPreview: null,
|
||||||
bannerUploadError: null,
|
bannerUploadError: null,
|
||||||
backgroundUploadError: null,
|
backgroundUploadError: null,
|
||||||
|
changeEmailError: false,
|
||||||
|
changeEmailPassword: '',
|
||||||
|
changedEmail: false,
|
||||||
deletingAccount: false,
|
deletingAccount: false,
|
||||||
deleteAccountConfirmPasswordInput: '',
|
deleteAccountConfirmPasswordInput: '',
|
||||||
deleteAccountError: false,
|
deleteAccountError: false,
|
||||||
|
@ -82,7 +87,8 @@ const UserSettings = {
|
||||||
ProgressButton,
|
ProgressButton,
|
||||||
Importer,
|
Importer,
|
||||||
Exporter,
|
Exporter,
|
||||||
Mfa
|
Mfa,
|
||||||
|
Checkbox
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
|
@ -303,6 +309,22 @@ const UserSettings = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
changeEmail () {
|
||||||
|
const params = {
|
||||||
|
email: this.newEmail,
|
||||||
|
password: this.changeEmailPassword
|
||||||
|
}
|
||||||
|
this.$store.state.api.backendInteractor.changeEmail(params)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === 'success') {
|
||||||
|
this.changedEmail = true
|
||||||
|
this.changeEmailError = false
|
||||||
|
} else {
|
||||||
|
this.changedEmail = false
|
||||||
|
this.changeEmailError = res.error
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
activateTab (tabName) {
|
activateTab (tabName) {
|
||||||
this.activeTab = tabName
|
this.activeTab = tabName
|
||||||
},
|
},
|
||||||
|
|
|
@ -53,12 +53,9 @@
|
||||||
/>
|
/>
|
||||||
</EmojiInput>
|
</EmojiInput>
|
||||||
<p>
|
<p>
|
||||||
<input
|
<Checkbox v-model="newLocked">
|
||||||
id="account-locked"
|
{{ $t('settings.lock_account_description') }}
|
||||||
v-model="newLocked"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="account-locked">{{ $t('settings.lock_account_description') }}</label>
|
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<label for="default-vis">{{ $t('settings.default_vis') }}</label>
|
<label for="default-vis">{{ $t('settings.default_vis') }}</label>
|
||||||
|
@ -75,69 +72,52 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<input
|
<Checkbox v-model="newNoRichText">
|
||||||
id="account-no-rich-text"
|
{{ $t('settings.no_rich_text_description') }}
|
||||||
v-model="newNoRichText"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="account-no-rich-text">{{ $t('settings.no_rich_text_description') }}</label>
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input
|
<Checkbox v-model="hideFollows">
|
||||||
id="account-hide-follows"
|
{{ $t('settings.hide_follows_description') }}
|
||||||
v-model="hideFollows"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="account-hide-follows">{{ $t('settings.hide_follows_description') }}</label>
|
|
||||||
</p>
|
</p>
|
||||||
<p class="setting-subitem">
|
<p class="setting-subitem">
|
||||||
<input
|
<Checkbox
|
||||||
id="account-hide-follows-count"
|
|
||||||
v-model="hideFollowsCount"
|
v-model="hideFollowsCount"
|
||||||
type="checkbox"
|
|
||||||
:disabled="!hideFollows"
|
:disabled="!hideFollows"
|
||||||
>
|
>
|
||||||
<label for="account-hide-follows-count">{{ $t('settings.hide_follows_count_description') }}</label>
|
{{ $t('settings.hide_follows_count_description') }}
|
||||||
|
</Checkbox>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input
|
<Checkbox
|
||||||
id="account-hide-followers"
|
|
||||||
v-model="hideFollowers"
|
v-model="hideFollowers"
|
||||||
type="checkbox"
|
|
||||||
>
|
>
|
||||||
<label for="account-hide-followers">{{ $t('settings.hide_followers_description') }}</label>
|
{{ $t('settings.hide_followers_description') }}
|
||||||
|
</Checkbox>
|
||||||
</p>
|
</p>
|
||||||
<p class="setting-subitem">
|
<p class="setting-subitem">
|
||||||
<input
|
<Checkbox
|
||||||
id="account-hide-followers-count"
|
|
||||||
v-model="hideFollowersCount"
|
v-model="hideFollowersCount"
|
||||||
type="checkbox"
|
|
||||||
:disabled="!hideFollowers"
|
:disabled="!hideFollowers"
|
||||||
>
|
>
|
||||||
<label for="account-hide-followers-count">{{ $t('settings.hide_followers_count_description') }}</label>
|
{{ $t('settings.hide_followers_count_description') }}
|
||||||
|
</Checkbox>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input
|
<Checkbox v-model="showRole">
|
||||||
id="account-show-role"
|
<template v-if="role === 'admin'">
|
||||||
v-model="showRole"
|
{{ $t('settings.show_admin_badge') }}
|
||||||
type="checkbox"
|
</template>
|
||||||
>
|
<template v-if="role === 'moderator'">
|
||||||
<label
|
{{ $t('settings.show_moderator_badge') }}
|
||||||
v-if="role === 'admin'"
|
</template>
|
||||||
for="account-show-role"
|
</Checkbox>
|
||||||
>{{ $t('settings.show_admin_badge') }}</label>
|
|
||||||
<label
|
|
||||||
v-if="role === 'moderator'"
|
|
||||||
for="account-show-role"
|
|
||||||
>{{ $t('settings.show_moderator_badge') }}</label>
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input
|
<Checkbox v-model="discoverable">
|
||||||
id="discoverable"
|
{{ $t('settings.discoverable') }}
|
||||||
v-model="discoverable"
|
</Checkbox>
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="discoverable">{{ $t('settings.discoverable') }}</label>
|
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
:disabled="newName && newName.length === 0"
|
:disabled="newName && newName.length === 0"
|
||||||
|
@ -253,6 +233,39 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :label="$t('settings.security_tab')">
|
<div :label="$t('settings.security_tab')">
|
||||||
|
<div class="setting-item">
|
||||||
|
<h2>{{ $t('settings.change_email') }}</h2>
|
||||||
|
<div>
|
||||||
|
<p>{{ $t('settings.new_email') }}</p>
|
||||||
|
<input
|
||||||
|
v-model="newEmail"
|
||||||
|
type="email"
|
||||||
|
autocomplete="email"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>{{ $t('settings.current_password') }}</p>
|
||||||
|
<input
|
||||||
|
v-model="changeEmailPassword"
|
||||||
|
type="password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-default"
|
||||||
|
@click="changeEmail"
|
||||||
|
>
|
||||||
|
{{ $t('general.submit') }}
|
||||||
|
</button>
|
||||||
|
<p v-if="changedEmail">
|
||||||
|
{{ $t('settings.changed_email') }}
|
||||||
|
</p>
|
||||||
|
<template v-if="changeEmailError !== false">
|
||||||
|
<p>{{ $t('settings.change_email_error') }}</p>
|
||||||
|
<p>{{ changeEmailError }}</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<h2>{{ $t('settings.change_password') }}</h2>
|
<h2>{{ $t('settings.change_password') }}</h2>
|
||||||
<div>
|
<div>
|
||||||
|
@ -367,44 +380,24 @@
|
||||||
<span class="label">{{ $t('settings.notification_setting') }}</span>
|
<span class="label">{{ $t('settings.notification_setting') }}</span>
|
||||||
<ul class="option-list">
|
<ul class="option-list">
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationSettings.follows">
|
||||||
id="notification-setting-follows"
|
|
||||||
v-model="notificationSettings.follows"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-setting-follows">
|
|
||||||
{{ $t('settings.notification_setting_follows') }}
|
{{ $t('settings.notification_setting_follows') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationSettings.followers">
|
||||||
id="notification-setting-followers"
|
|
||||||
v-model="notificationSettings.followers"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-setting-followers">
|
|
||||||
{{ $t('settings.notification_setting_followers') }}
|
{{ $t('settings.notification_setting_followers') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationSettings.non_follows">
|
||||||
id="notification-setting-non-follows"
|
|
||||||
v-model="notificationSettings.non_follows"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-setting-non-follows">
|
|
||||||
{{ $t('settings.notification_setting_non_follows') }}
|
{{ $t('settings.notification_setting_non_follows') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input
|
<Checkbox v-model="notificationSettings.non_followers">
|
||||||
id="notification-setting-non-followers"
|
|
||||||
v-model="notificationSettings.non_followers"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label for="notification-setting-non-followers">
|
|
||||||
{{ $t('settings.notification_setting_non_followers') }}
|
{{ $t('settings.notification_setting_non_followers') }}
|
||||||
</label>
|
</Checkbox>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,7 @@ const VideoAttachment = {
|
||||||
props: ['attachment', 'controls'],
|
props: ['attachment', 'controls'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loopVideo: this.$store.state.config.loopVideo
|
loopVideo: this.$store.getters.mergedConfig.loopVideo
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -12,16 +12,16 @@ const VideoAttachment = {
|
||||||
if (typeof target.webkitAudioDecodedByteCount !== 'undefined') {
|
if (typeof target.webkitAudioDecodedByteCount !== 'undefined') {
|
||||||
// non-zero if video has audio track
|
// non-zero if video has audio track
|
||||||
if (target.webkitAudioDecodedByteCount > 0) {
|
if (target.webkitAudioDecodedByteCount > 0) {
|
||||||
this.loopVideo = this.loopVideo && !this.$store.state.config.loopVideoSilentOnly
|
this.loopVideo = this.loopVideo && !this.$store.getters.mergedConfig.loopVideoSilentOnly
|
||||||
}
|
}
|
||||||
} else if (typeof target.mozHasAudio !== 'undefined') {
|
} else if (typeof target.mozHasAudio !== 'undefined') {
|
||||||
// true if video has audio track
|
// true if video has audio track
|
||||||
if (target.mozHasAudio) {
|
if (target.mozHasAudio) {
|
||||||
this.loopVideo = this.loopVideo && !this.$store.state.config.loopVideoSilentOnly
|
this.loopVideo = this.loopVideo && !this.$store.getters.mergedConfig.loopVideoSilentOnly
|
||||||
}
|
}
|
||||||
} else if (typeof target.audioTracks !== 'undefined') {
|
} else if (typeof target.audioTracks !== 'undefined') {
|
||||||
if (target.audioTracks.length > 0) {
|
if (target.audioTracks.length > 0) {
|
||||||
this.loopVideo = this.loopVideo && !this.$store.state.config.loopVideoSilentOnly
|
this.loopVideo = this.loopVideo && !this.$store.getters.mergedConfig.loopVideoSilentOnly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,42 +2,49 @@ import * as bodyScrollLock from 'body-scroll-lock'
|
||||||
|
|
||||||
let previousNavPaddingRight
|
let previousNavPaddingRight
|
||||||
let previousAppBgWrapperRight
|
let previousAppBgWrapperRight
|
||||||
|
const lockerEls = new Set([])
|
||||||
|
|
||||||
const disableBodyScroll = (el) => {
|
const disableBodyScroll = (el) => {
|
||||||
const scrollBarGap = window.innerWidth - document.documentElement.clientWidth
|
const scrollBarGap = window.innerWidth - document.documentElement.clientWidth
|
||||||
bodyScrollLock.disableBodyScroll(el, {
|
bodyScrollLock.disableBodyScroll(el, {
|
||||||
reserveScrollBarGap: true
|
reserveScrollBarGap: true
|
||||||
})
|
})
|
||||||
|
lockerEls.add(el)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// If previousNavPaddingRight is already set, don't set it again.
|
if (lockerEls.size <= 1) {
|
||||||
if (previousNavPaddingRight === undefined) {
|
// If previousNavPaddingRight is already set, don't set it again.
|
||||||
const navEl = document.getElementById('nav')
|
if (previousNavPaddingRight === undefined) {
|
||||||
previousNavPaddingRight = window.getComputedStyle(navEl).getPropertyValue('padding-right')
|
const navEl = document.getElementById('nav')
|
||||||
navEl.style.paddingRight = previousNavPaddingRight ? `calc(${previousNavPaddingRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
previousNavPaddingRight = window.getComputedStyle(navEl).getPropertyValue('padding-right')
|
||||||
|
navEl.style.paddingRight = previousNavPaddingRight ? `calc(${previousNavPaddingRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
||||||
|
}
|
||||||
|
// If previousAppBgWrapeprRight is already set, don't set it again.
|
||||||
|
if (previousAppBgWrapperRight === undefined) {
|
||||||
|
const appBgWrapperEl = document.getElementById('app_bg_wrapper')
|
||||||
|
previousAppBgWrapperRight = window.getComputedStyle(appBgWrapperEl).getPropertyValue('right')
|
||||||
|
appBgWrapperEl.style.right = previousAppBgWrapperRight ? `calc(${previousAppBgWrapperRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
||||||
|
}
|
||||||
|
document.body.classList.add('scroll-locked')
|
||||||
}
|
}
|
||||||
// If previousAppBgWrapeprRight is already set, don't set it again.
|
|
||||||
if (previousAppBgWrapperRight === undefined) {
|
|
||||||
const appBgWrapperEl = document.getElementById('app_bg_wrapper')
|
|
||||||
previousAppBgWrapperRight = window.getComputedStyle(appBgWrapperEl).getPropertyValue('right')
|
|
||||||
appBgWrapperEl.style.right = previousAppBgWrapperRight ? `calc(${previousAppBgWrapperRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
|
||||||
}
|
|
||||||
document.body.classList.add('scroll-locked')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const enableBodyScroll = (el) => {
|
const enableBodyScroll = (el) => {
|
||||||
|
lockerEls.delete(el)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (previousNavPaddingRight !== undefined) {
|
if (lockerEls.size === 0) {
|
||||||
document.getElementById('nav').style.paddingRight = previousNavPaddingRight
|
if (previousNavPaddingRight !== undefined) {
|
||||||
// Restore previousNavPaddingRight to undefined so disableBodyScroll knows it can be set again.
|
document.getElementById('nav').style.paddingRight = previousNavPaddingRight
|
||||||
previousNavPaddingRight = undefined
|
// Restore previousNavPaddingRight to undefined so disableBodyScroll knows it can be set again.
|
||||||
|
previousNavPaddingRight = undefined
|
||||||
|
}
|
||||||
|
if (previousAppBgWrapperRight !== undefined) {
|
||||||
|
document.getElementById('app_bg_wrapper').style.right = previousAppBgWrapperRight
|
||||||
|
// Restore previousAppBgWrapperRight to undefined so disableBodyScroll knows it can be set again.
|
||||||
|
previousAppBgWrapperRight = undefined
|
||||||
|
}
|
||||||
|
document.body.classList.remove('scroll-locked')
|
||||||
}
|
}
|
||||||
if (previousAppBgWrapperRight !== undefined) {
|
|
||||||
document.getElementById('app_bg_wrapper').style.right = previousAppBgWrapperRight
|
|
||||||
// Restore previousAppBgWrapperRight to undefined so disableBodyScroll knows it can be set again.
|
|
||||||
previousAppBgWrapperRight = undefined
|
|
||||||
}
|
|
||||||
document.body.classList.remove('scroll-locked')
|
|
||||||
})
|
})
|
||||||
bodyScrollLock.enableBodyScroll(el)
|
bodyScrollLock.enableBodyScroll(el)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
"features_panel": {
|
"features_panel": {
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
"gopher": "Gopher",
|
"gopher": "Gopher",
|
||||||
"media_proxy": "Media Proxy",
|
"media_proxy": "Medienproxy",
|
||||||
"scope_options": "Reichweitenoptionen",
|
"scope_options": "Reichweitenoptionen",
|
||||||
"text_limit": "Textlimit",
|
"text_limit": "Textlimit",
|
||||||
"title": "Features",
|
"title": "Features",
|
||||||
"who_to_follow": "Who to follow"
|
"who_to_follow": "Wem folgen?"
|
||||||
},
|
},
|
||||||
"finder": {
|
"finder": {
|
||||||
"error_fetching_user": "Fehler beim Suchen des Benutzers",
|
"error_fetching_user": "Fehler beim Suchen des Benutzers",
|
||||||
|
@ -29,15 +29,18 @@
|
||||||
"username": "Benutzername"
|
"username": "Benutzername"
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
|
"about": "Über",
|
||||||
"back": "Zurück",
|
"back": "Zurück",
|
||||||
"chat": "Lokaler Chat",
|
"chat": "Lokaler Chat",
|
||||||
"friend_requests": "Followanfragen",
|
"friend_requests": "Followanfragen",
|
||||||
"mentions": "Erwähnungen",
|
"mentions": "Erwähnungen",
|
||||||
|
"interactions": "Interaktionen",
|
||||||
"dms": "Direktnachrichten",
|
"dms": "Direktnachrichten",
|
||||||
"public_tl": "Öffentliche Zeitleiste",
|
"public_tl": "Öffentliche Zeitleiste",
|
||||||
"timeline": "Zeitleiste",
|
"timeline": "Zeitleiste",
|
||||||
"twkn": "Das gesamte bekannte Netzwerk",
|
"twkn": "Das gesamte bekannte Netzwerk",
|
||||||
"user_search": "Benutzersuche",
|
"user_search": "Benutzersuche",
|
||||||
|
"search": "Suche",
|
||||||
"preferences": "Voreinstellungen"
|
"preferences": "Voreinstellungen"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
|
@ -115,6 +118,9 @@
|
||||||
"delete_account_description": "Lösche deinen Account und alle deine Nachrichten unwiderruflich.",
|
"delete_account_description": "Lösche deinen Account und alle deine Nachrichten unwiderruflich.",
|
||||||
"delete_account_error": "Es ist ein Fehler beim Löschen deines Accounts aufgetreten. Tritt dies weiterhin auf, wende dich an den Administrator der Instanz.",
|
"delete_account_error": "Es ist ein Fehler beim Löschen deines Accounts aufgetreten. Tritt dies weiterhin auf, wende dich an den Administrator der Instanz.",
|
||||||
"delete_account_instructions": "Tippe dein Passwort unten in das Feld ein, um die Löschung deines Accounts zu bestätigen.",
|
"delete_account_instructions": "Tippe dein Passwort unten in das Feld ein, um die Löschung deines Accounts zu bestätigen.",
|
||||||
|
"discoverable": "Erlaubnis für automatisches Suchen nach diesem Account",
|
||||||
|
"avatar_size_instruction": "Die empfohlene minimale Größe für Avatare ist 150x150 Pixel.",
|
||||||
|
"pad_emoji": "Emojis mit Leerzeichen umrahmen",
|
||||||
"export_theme": "Farbschema speichern",
|
"export_theme": "Farbschema speichern",
|
||||||
"filtering": "Filtern",
|
"filtering": "Filtern",
|
||||||
"filtering_explanation": "Alle Beiträge die diese Wörter enthalten werden ausgeblendet. Ein Wort pro Zeile.",
|
"filtering_explanation": "Alle Beiträge die diese Wörter enthalten werden ausgeblendet. Ein Wort pro Zeile.",
|
||||||
|
@ -128,8 +134,11 @@
|
||||||
"general": "Allgemein",
|
"general": "Allgemein",
|
||||||
"hide_attachments_in_convo": "Anhänge in Unterhaltungen ausblenden",
|
"hide_attachments_in_convo": "Anhänge in Unterhaltungen ausblenden",
|
||||||
"hide_attachments_in_tl": "Anhänge in der Zeitleiste ausblenden",
|
"hide_attachments_in_tl": "Anhänge in der Zeitleiste ausblenden",
|
||||||
|
"hide_muted_posts": "Verberge Beiträge stummgeschalteter Nutzer",
|
||||||
|
"max_thumbnails": "Maximale Anzahl von Vorschaubildern pro Beitrag",
|
||||||
"hide_isp": "Instanz-spezifisches Panel ausblenden",
|
"hide_isp": "Instanz-spezifisches Panel ausblenden",
|
||||||
"preload_images": "Bilder vorausladen",
|
"preload_images": "Bilder vorausladen",
|
||||||
|
"use_one_click_nsfw": "Heikle Anhänge mit nur einem Klick öffnen",
|
||||||
"hide_post_stats": "Beitragsstatistiken verbergen (z.B. die Anzahl der Favoriten)",
|
"hide_post_stats": "Beitragsstatistiken verbergen (z.B. die Anzahl der Favoriten)",
|
||||||
"hide_user_stats": "Benutzerstatistiken verbergen (z.B. die Anzahl der Follower)",
|
"hide_user_stats": "Benutzerstatistiken verbergen (z.B. die Anzahl der Follower)",
|
||||||
"hide_filtered_statuses": "Gefilterte Beiträge verbergen",
|
"hide_filtered_statuses": "Gefilterte Beiträge verbergen",
|
||||||
|
@ -147,6 +156,9 @@
|
||||||
"lock_account_description": "Sperre deinen Account, um neue Follower zu genehmigen oder abzulehnen",
|
"lock_account_description": "Sperre deinen Account, um neue Follower zu genehmigen oder abzulehnen",
|
||||||
"loop_video": "Videos wiederholen",
|
"loop_video": "Videos wiederholen",
|
||||||
"loop_video_silent_only": "Nur Videos ohne Ton wiederholen (z.B. Mastodons \"gifs\")",
|
"loop_video_silent_only": "Nur Videos ohne Ton wiederholen (z.B. Mastodons \"gifs\")",
|
||||||
|
"mutes_tab": "Mutes",
|
||||||
|
"play_videos_in_modal": "Videos in größerem Medienfenster abspielen",
|
||||||
|
"use_contain_fit": "Vorschaubilder nicht zuschneiden",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name_bio": "Name & Bio",
|
"name_bio": "Name & Bio",
|
||||||
"new_password": "Neues Passwort",
|
"new_password": "Neues Passwort",
|
||||||
|
@ -158,6 +170,8 @@
|
||||||
"no_rich_text_description": "Rich-Text Formatierungen von allen Beiträgen entfernen",
|
"no_rich_text_description": "Rich-Text Formatierungen von allen Beiträgen entfernen",
|
||||||
"hide_follows_description": "Zeige nicht, wem ich folge",
|
"hide_follows_description": "Zeige nicht, wem ich folge",
|
||||||
"hide_followers_description": "Zeige nicht, wer mir folgt",
|
"hide_followers_description": "Zeige nicht, wer mir folgt",
|
||||||
|
"hide_follows_count_description": "Verberge die Anzahl deiner Gefolgten",
|
||||||
|
"hide_followers_count_description": "Verberge die Anzahl deiner Folgenden",
|
||||||
"nsfw_clickthrough": "Aktiviere ausblendbares Overlay für Anhänge, die als NSFW markiert sind",
|
"nsfw_clickthrough": "Aktiviere ausblendbares Overlay für Anhänge, die als NSFW markiert sind",
|
||||||
"oauth_tokens": "OAuth-Token",
|
"oauth_tokens": "OAuth-Token",
|
||||||
"token": "Zeichen",
|
"token": "Zeichen",
|
||||||
|
@ -176,10 +190,12 @@
|
||||||
"reply_visibility_all": "Alle Antworten zeigen",
|
"reply_visibility_all": "Alle Antworten zeigen",
|
||||||
"reply_visibility_following": "Zeige nur Antworten an mich oder an Benutzer, denen ich folge",
|
"reply_visibility_following": "Zeige nur Antworten an mich oder an Benutzer, denen ich folge",
|
||||||
"reply_visibility_self": "Nur Antworten an mich anzeigen",
|
"reply_visibility_self": "Nur Antworten an mich anzeigen",
|
||||||
|
"autohide_floating_post_button": "Automatisches Verbergen des Knopfs für neue Beiträge (mobil)",
|
||||||
"saving_err": "Fehler beim Speichern der Einstellungen",
|
"saving_err": "Fehler beim Speichern der Einstellungen",
|
||||||
"saving_ok": "Einstellungen gespeichert",
|
"saving_ok": "Einstellungen gespeichert",
|
||||||
"security_tab": "Sicherheit",
|
"security_tab": "Sicherheit",
|
||||||
"scope_copy": "Reichweite beim Antworten übernehmen (Direktnachrichten werden immer kopiert)",
|
"scope_copy": "Reichweite beim Antworten übernehmen (Direktnachrichten werden immer kopiert)",
|
||||||
|
"minimal_scopes_mode": "Minimiere Reichweitenoptionen",
|
||||||
"set_new_avatar": "Setze einen neuen Avatar",
|
"set_new_avatar": "Setze einen neuen Avatar",
|
||||||
"set_new_profile_background": "Setze einen neuen Hintergrund für dein Profil",
|
"set_new_profile_background": "Setze einen neuen Hintergrund für dein Profil",
|
||||||
"set_new_profile_banner": "Setze einen neuen Banner für dein Profil",
|
"set_new_profile_banner": "Setze einen neuen Banner für dein Profil",
|
||||||
|
@ -189,7 +205,8 @@
|
||||||
"subject_line_email": "Wie Email: \"re: Betreff\"",
|
"subject_line_email": "Wie Email: \"re: Betreff\"",
|
||||||
"subject_line_mastodon": "Wie Mastodon: unverändert kopieren",
|
"subject_line_mastodon": "Wie Mastodon: unverändert kopieren",
|
||||||
"subject_line_noop": "Nicht kopieren",
|
"subject_line_noop": "Nicht kopieren",
|
||||||
"stop_gifs": "Play-on-hover GIFs",
|
"post_status_content_type": "Beitragsart",
|
||||||
|
"stop_gifs": "Animationen nur beim Darüberfahren abspielen",
|
||||||
"streaming": "Aktiviere automatisches Laden (Streaming) von neuen Beiträgen",
|
"streaming": "Aktiviere automatisches Laden (Streaming) von neuen Beiträgen",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"theme": "Farbschema",
|
"theme": "Farbschema",
|
||||||
|
@ -372,5 +389,25 @@
|
||||||
"GiB": "GiB",
|
"GiB": "GiB",
|
||||||
"TiB": "TiB"
|
"TiB": "TiB"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"people": "Leute",
|
||||||
|
"hashtags": "Hashtags",
|
||||||
|
"person_talking": "{count} Person spricht darüber",
|
||||||
|
"people_talking": "{count} Leute sprechen darüber",
|
||||||
|
"no_results": "Keine Ergebnisse"
|
||||||
|
},
|
||||||
|
"password_reset": {
|
||||||
|
"forgot_password": "Passwort vergessen?",
|
||||||
|
"password_reset": "Password zurücksetzen",
|
||||||
|
"instruction": "Wenn du hier deinen Benutznamen oder die zugehörige E-Mail-Adresse eingibst, kann dir der Server einen Link zum Passwortzurücksetzen zuschicken.",
|
||||||
|
"placeholder": "Dein Benutzername oder die zugehörige E-Mail-Adresse",
|
||||||
|
"check_email": "Im E-Mail-Posteingang des angebenen Kontos müsste sich jetzt (oder zumindest in Kürze) die E-Mail mit dem Link zum Passwortzurücksetzen befinden.",
|
||||||
|
"return_home": "Zurück zur Heimseite",
|
||||||
|
"not_found": "Benutzername/E-Mail-Adresse nicht gefunden. Vertippt?",
|
||||||
|
"too_many_requests": "Kurze Pause. Zu viele Versuche. Bitte, später nochmal probieren.",
|
||||||
|
"password_reset_disabled": "Passwortzurücksetzen deaktiviert. Bitte Administrator kontaktieren.",
|
||||||
|
"password_reset_required": "Passwortzurücksetzen erforderlich",
|
||||||
|
"password_reset_required_but_mailer_is_disabled": "Passwortzurücksetzen wäre erforderlich, ist aber deaktiviert. Bitte Administrator kontaktieren."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,6 +219,9 @@
|
||||||
"cGreen": "Green (Retweet)",
|
"cGreen": "Green (Retweet)",
|
||||||
"cOrange": "Orange (Favorite)",
|
"cOrange": "Orange (Favorite)",
|
||||||
"cRed": "Red (Cancel)",
|
"cRed": "Red (Cancel)",
|
||||||
|
"change_email": "Change Email",
|
||||||
|
"change_email_error": "There was an issue changing your email.",
|
||||||
|
"changed_email": "Email changed successfully!",
|
||||||
"change_password": "Change Password",
|
"change_password": "Change Password",
|
||||||
"change_password_error": "There was an issue changing your password.",
|
"change_password_error": "There was an issue changing your password.",
|
||||||
"changed_password": "Password changed successfully!",
|
"changed_password": "Password changed successfully!",
|
||||||
|
@ -277,6 +280,7 @@
|
||||||
"use_contain_fit": "Don't crop the attachment in thumbnails",
|
"use_contain_fit": "Don't crop the attachment in thumbnails",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name_bio": "Name & Bio",
|
"name_bio": "Name & Bio",
|
||||||
|
"new_email": "New Email",
|
||||||
"new_password": "New password",
|
"new_password": "New password",
|
||||||
"notification_visibility": "Types of notifications to show",
|
"notification_visibility": "Types of notifications to show",
|
||||||
"notification_visibility_follows": "Follows",
|
"notification_visibility_follows": "Follows",
|
||||||
|
@ -558,6 +562,8 @@
|
||||||
"unmute": "Unmute",
|
"unmute": "Unmute",
|
||||||
"unmute_progress": "Unmuting...",
|
"unmute_progress": "Unmuting...",
|
||||||
"mute_progress": "Muting...",
|
"mute_progress": "Muting...",
|
||||||
|
"hide_repeats": "Hide repeats",
|
||||||
|
"show_repeats": "Show repeats",
|
||||||
"admin_menu": {
|
"admin_menu": {
|
||||||
"moderation": "Moderation",
|
"moderation": "Moderation",
|
||||||
"grant_admin": "Grant Admin",
|
"grant_admin": "Grant Admin",
|
||||||
|
@ -633,6 +639,8 @@
|
||||||
"return_home": "Return to the home page",
|
"return_home": "Return to the home page",
|
||||||
"not_found": "We couldn't find that email or username.",
|
"not_found": "We couldn't find that email or username.",
|
||||||
"too_many_requests": "You have reached the limit of attempts, try again later.",
|
"too_many_requests": "You have reached the limit of attempts, try again later.",
|
||||||
"password_reset_disabled": "Password reset is disabled. Please contact your instance administrator."
|
"password_reset_disabled": "Password reset is disabled. Please contact your instance administrator.",
|
||||||
|
"password_reset_required": "You must reset your password to log in.",
|
||||||
|
"password_reset_required_but_mailer_is_disabled": "You must reset your password, but password reset is disabled. Please contact your instance administrator."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,8 @@
|
||||||
"error": "Se ha producido un error al importar el archivo."
|
"error": "Se ha producido un error al importar el archivo."
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "Identificación",
|
"login": "Identificarse",
|
||||||
"description": "Identificación con OAuth",
|
"description": "Identificarse con OAuth",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"password": "Contraseña",
|
"password": "Contraseña",
|
||||||
"placeholder": "p.ej. lain",
|
"placeholder": "p.ej. lain",
|
||||||
|
@ -68,6 +68,7 @@
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"about": "Acerca de",
|
"about": "Acerca de",
|
||||||
|
"administration": "Administración",
|
||||||
"back": "Volver",
|
"back": "Volver",
|
||||||
"chat": "Chat Local",
|
"chat": "Chat Local",
|
||||||
"friend_requests": "Solicitudes de seguimiento",
|
"friend_requests": "Solicitudes de seguimiento",
|
||||||
|
@ -106,6 +107,15 @@
|
||||||
"expired": "La encuesta terminó hace {0}",
|
"expired": "La encuesta terminó hace {0}",
|
||||||
"not_enough_options": "Muy pocas opciones únicas en la encuesta"
|
"not_enough_options": "Muy pocas opciones únicas en la encuesta"
|
||||||
},
|
},
|
||||||
|
"emoji": {
|
||||||
|
"stickers": "Pegatinas",
|
||||||
|
"emoji": "Emoji",
|
||||||
|
"keep_open": "Mantener el selector abierto",
|
||||||
|
"search_emoji": "Buscar un emoji",
|
||||||
|
"add_emoji": "Insertar un emoji",
|
||||||
|
"custom": "Emojis personalizados",
|
||||||
|
"unicode": "Emojis unicode"
|
||||||
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "Añadir Pegatina"
|
"add_sticker": "Añadir Pegatina"
|
||||||
},
|
},
|
||||||
|
@ -222,7 +232,9 @@
|
||||||
"data_import_export_tab": "Importar / Exportar Datos",
|
"data_import_export_tab": "Importar / Exportar Datos",
|
||||||
"default_vis": "Alcance de visibilidad por defecto",
|
"default_vis": "Alcance de visibilidad por defecto",
|
||||||
"delete_account": "Eliminar la cuenta",
|
"delete_account": "Eliminar la cuenta",
|
||||||
|
"discoverable": "Permitir la aparición de esta cuenta en los resultados de búsqueda y otros servicios",
|
||||||
"delete_account_description": "Eliminar para siempre la cuenta y todos los mensajes.",
|
"delete_account_description": "Eliminar para siempre la cuenta y todos los mensajes.",
|
||||||
|
"pad_emoji": "Rellenar con espacios al agregar emojis desde el selector",
|
||||||
"delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el administrador de tu instancia.",
|
"delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el administrador de tu instancia.",
|
||||||
"delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.",
|
"delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.",
|
||||||
"avatar_size_instruction": "El tamaño mínimo recomendado para el avatar es de 150X150 píxeles.",
|
"avatar_size_instruction": "El tamaño mínimo recomendado para el avatar es de 150X150 píxeles.",
|
||||||
|
@ -277,6 +289,8 @@
|
||||||
"no_mutes": "No hay usuarios sinlenciados",
|
"no_mutes": "No hay usuarios sinlenciados",
|
||||||
"hide_follows_description": "No mostrar a quién sigo",
|
"hide_follows_description": "No mostrar a quién sigo",
|
||||||
"hide_followers_description": "No mostrar quién me sigue",
|
"hide_followers_description": "No mostrar quién me sigue",
|
||||||
|
"hide_follows_count_description": "No mostrar el número de cuentas que sigo",
|
||||||
|
"hide_followers_count_description": "No mostrar el número de cuentas que me siguen",
|
||||||
"show_admin_badge": "Mostrar la insignia de Administrador en mi perfil",
|
"show_admin_badge": "Mostrar la insignia de Administrador en mi perfil",
|
||||||
"show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil",
|
"show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil",
|
||||||
"nsfw_clickthrough": "Activar el clic para ocultar los adjuntos NSFW",
|
"nsfw_clickthrough": "Activar el clic para ocultar los adjuntos NSFW",
|
||||||
|
@ -529,6 +543,7 @@
|
||||||
"follows_you": "¡Te sigue!",
|
"follows_you": "¡Te sigue!",
|
||||||
"its_you": "¡Eres tú!",
|
"its_you": "¡Eres tú!",
|
||||||
"media": "Media",
|
"media": "Media",
|
||||||
|
"mention": "Mencionar",
|
||||||
"mute": "Silenciar",
|
"mute": "Silenciar",
|
||||||
"muted": "Silenciado",
|
"muted": "Silenciado",
|
||||||
"per_day": "por día",
|
"per_day": "por día",
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
"about": "Honi buruz",
|
"about": "Honi buruz",
|
||||||
|
"administration": "Administrazioa",
|
||||||
"back": "Atzera",
|
"back": "Atzera",
|
||||||
"chat": "Txat lokala",
|
"chat": "Txat lokala",
|
||||||
"friend_requests": "Jarraitzeko eskaerak",
|
"friend_requests": "Jarraitzeko eskaerak",
|
||||||
|
@ -106,6 +107,15 @@
|
||||||
"expired": "Inkesta {0} bukatu zen",
|
"expired": "Inkesta {0} bukatu zen",
|
||||||
"not_enough_options": "Aukera gutxiegi inkestan"
|
"not_enough_options": "Aukera gutxiegi inkestan"
|
||||||
},
|
},
|
||||||
|
"emoji": {
|
||||||
|
"stickers": "Pegatinak",
|
||||||
|
"emoji": "Emoji",
|
||||||
|
"keep_open": "Mantendu hautatzailea zabalik",
|
||||||
|
"search_emoji": "Bilatu emoji bat",
|
||||||
|
"add_emoji": "Emoji bat gehitu",
|
||||||
|
"custom": "Ohiko emojiak",
|
||||||
|
"unicode": "Unicode emojiak"
|
||||||
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "Pegatina gehitu"
|
"add_sticker": "Pegatina gehitu"
|
||||||
},
|
},
|
||||||
|
@ -199,12 +209,12 @@
|
||||||
"avatarRadius": "Avatarrak",
|
"avatarRadius": "Avatarrak",
|
||||||
"background": "Atzeko planoa",
|
"background": "Atzeko planoa",
|
||||||
"bio": "Biografia",
|
"bio": "Biografia",
|
||||||
"block_export": "Bloke esportatzea",
|
"block_export": "Blokeatu dituzunak esportatu",
|
||||||
"block_export_button": "Esportatu zure blokeak csv fitxategi batera",
|
"block_export_button": "Esportatu blokeatutakoak csv fitxategi batera",
|
||||||
"block_import": "Bloke inportazioa",
|
"block_import": "Blokeatu dituzunak inportatu",
|
||||||
"block_import_error": "Errorea blokeak inportatzen",
|
"block_import_error": "Errorea blokeatutakoak inportatzen",
|
||||||
"blocks_imported": "Blokeak inportaturik! Hauek prozesatzeak denbora hartuko du.",
|
"blocks_imported": "Blokeatutakoak inportaturik! Hauek prozesatzeak denbora hartuko du.",
|
||||||
"blocks_tab": "Blokeak",
|
"blocks_tab": "Blokeatutakoak",
|
||||||
"btnRadius": "Botoiak",
|
"btnRadius": "Botoiak",
|
||||||
"cBlue": "Urdina (erantzun, jarraitu)",
|
"cBlue": "Urdina (erantzun, jarraitu)",
|
||||||
"cGreen": "Berdea (Bertxiotu)",
|
"cGreen": "Berdea (Bertxiotu)",
|
||||||
|
@ -222,7 +232,9 @@
|
||||||
"data_import_export_tab": "Datuak Inportatu / Esportatu",
|
"data_import_export_tab": "Datuak Inportatu / Esportatu",
|
||||||
"default_vis": "Lehenetsitako ikusgaitasunak",
|
"default_vis": "Lehenetsitako ikusgaitasunak",
|
||||||
"delete_account": "Ezabatu kontua",
|
"delete_account": "Ezabatu kontua",
|
||||||
|
"discoverable": "Baimendu zure kontua kanpo bilaketa-emaitzetan eta bestelako zerbitzuetan agertzea",
|
||||||
"delete_account_description": "Betirako ezabatu zure kontua eta zure mezu guztiak",
|
"delete_account_description": "Betirako ezabatu zure kontua eta zure mezu guztiak",
|
||||||
|
"pad_emoji": "Zuriuneak gehitu emoji bat aukeratzen denean",
|
||||||
"delete_account_error": "Arazo bat gertatu da zure kontua ezabatzerakoan. Arazoa jarraitu eskero, administratzailearekin harremanetan jarri.",
|
"delete_account_error": "Arazo bat gertatu da zure kontua ezabatzerakoan. Arazoa jarraitu eskero, administratzailearekin harremanetan jarri.",
|
||||||
"delete_account_instructions": "Idatzi zure pasahitza kontua ezabatzeko.",
|
"delete_account_instructions": "Idatzi zure pasahitza kontua ezabatzeko.",
|
||||||
"avatar_size_instruction": "Avatar irudien gomendatutako gutxieneko tamaina 150x150 pixel dira.",
|
"avatar_size_instruction": "Avatar irudien gomendatutako gutxieneko tamaina 150x150 pixel dira.",
|
||||||
|
@ -254,7 +266,7 @@
|
||||||
"instance_default": "(lehenetsia: {value})",
|
"instance_default": "(lehenetsia: {value})",
|
||||||
"instance_default_simple": "(lehenetsia)",
|
"instance_default_simple": "(lehenetsia)",
|
||||||
"interface": "Interfazea",
|
"interface": "Interfazea",
|
||||||
"interfaceLanguage": "Interfaze hizkuntza",
|
"interfaceLanguage": "Interfazearen hizkuntza",
|
||||||
"invalid_theme_imported": "Hautatutako fitxategia ez da onartutako Pleroma gaia. Ez da zure gaian aldaketarik burutu.",
|
"invalid_theme_imported": "Hautatutako fitxategia ez da onartutako Pleroma gaia. Ez da zure gaian aldaketarik burutu.",
|
||||||
"limited_availability": "Ez dago erabilgarri zure nabigatzailean",
|
"limited_availability": "Ez dago erabilgarri zure nabigatzailean",
|
||||||
"links": "Estekak",
|
"links": "Estekak",
|
||||||
|
@ -277,6 +289,8 @@
|
||||||
"no_mutes": "Ez daude erabiltzaile mututuak",
|
"no_mutes": "Ez daude erabiltzaile mututuak",
|
||||||
"hide_follows_description": "Ez erakutsi nor jarraitzen ari naizen",
|
"hide_follows_description": "Ez erakutsi nor jarraitzen ari naizen",
|
||||||
"hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen",
|
"hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen",
|
||||||
|
"hide_follows_count_description": "Ez erakutsi jarraitzen ari naizen kontuen kopurua",
|
||||||
|
"hide_followers_count_description": "Ez erakutsi nire jarraitzaileen kontuen kopurua",
|
||||||
"show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan",
|
"show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan",
|
||||||
"show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan",
|
"show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan",
|
||||||
"nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko",
|
"nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko",
|
||||||
|
@ -449,7 +463,7 @@
|
||||||
},
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"title": "Bertsioa",
|
"title": "Bertsioa",
|
||||||
"backend_version": "Backend Bertsio",
|
"backend_version": "Backend Bertsioa",
|
||||||
"frontend_version": "Frontend Bertsioa"
|
"frontend_version": "Frontend Bertsioa"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -529,6 +543,7 @@
|
||||||
"follows_you": "Jarraitzen dizu!",
|
"follows_you": "Jarraitzen dizu!",
|
||||||
"its_you": "Zu zara!",
|
"its_you": "Zu zara!",
|
||||||
"media": "Multimedia",
|
"media": "Multimedia",
|
||||||
|
"mention": "Aipatu",
|
||||||
"mute": "Isilarazi",
|
"mute": "Isilarazi",
|
||||||
"muted": "Isilduta",
|
"muted": "Isilduta",
|
||||||
"per_day": "eguneko",
|
"per_day": "eguneko",
|
||||||
|
@ -543,6 +558,8 @@
|
||||||
"unmute": "Isiltasuna kendu",
|
"unmute": "Isiltasuna kendu",
|
||||||
"unmute_progress": "Isiltasuna kentzen...",
|
"unmute_progress": "Isiltasuna kentzen...",
|
||||||
"mute_progress": "Isiltzen...",
|
"mute_progress": "Isiltzen...",
|
||||||
|
"hide_repeats": "Ezkutatu errepikapenak",
|
||||||
|
"show_repeats": "Erakutsi errpekiapenak",
|
||||||
"admin_menu": {
|
"admin_menu": {
|
||||||
"moderation": "Moderazioa",
|
"moderation": "Moderazioa",
|
||||||
"grant_admin": "Administratzaile baimena",
|
"grant_admin": "Administratzaile baimena",
|
||||||
|
@ -618,6 +635,8 @@
|
||||||
"return_home": "Itzuli hasierara",
|
"return_home": "Itzuli hasierara",
|
||||||
"not_found": "Ezin izan dugu helbide elektroniko edo erabiltzaile hori aurkitu.",
|
"not_found": "Ezin izan dugu helbide elektroniko edo erabiltzaile hori aurkitu.",
|
||||||
"too_many_requests": "Saiakera gehiegi burutu ditzu, saiatu berriro geroxeago.",
|
"too_many_requests": "Saiakera gehiegi burutu ditzu, saiatu berriro geroxeago.",
|
||||||
"password_reset_disabled": "Pasahitza berrezartzea debekatuta dago. Mesedez, jarri harremanetan instantzia administratzailearekin."
|
"password_reset_disabled": "Pasahitza berrezartzea debekatuta dago. Mesedez, jarri harremanetan instantzia administratzailearekin.",
|
||||||
|
"password_reset_required": "Pasahitza berrezarri behar duzu saioa hasteko.",
|
||||||
|
"password_reset_required_but_mailer_is_disabled": "Pasahitza berrezarri behar duzu, baina pasahitza berrezartzeko aukera desgaituta dago. Mesedez, jarri harremanetan instantziaren administratzailearekin."
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -127,6 +127,9 @@
|
||||||
"cGreen": "Повторить",
|
"cGreen": "Повторить",
|
||||||
"cOrange": "Нравится",
|
"cOrange": "Нравится",
|
||||||
"cRed": "Отменить",
|
"cRed": "Отменить",
|
||||||
|
"change_email": "Сменить email",
|
||||||
|
"change_email_error": "Произошла ошибка при попытке изменить email.",
|
||||||
|
"changed_email": "Email изменён успешно.",
|
||||||
"change_password": "Сменить пароль",
|
"change_password": "Сменить пароль",
|
||||||
"change_password_error": "Произошла ошибка при попытке изменить пароль.",
|
"change_password_error": "Произошла ошибка при попытке изменить пароль.",
|
||||||
"changed_password": "Пароль изменён успешно.",
|
"changed_password": "Пароль изменён успешно.",
|
||||||
|
@ -169,6 +172,7 @@
|
||||||
"loop_video_silent_only": "Зацикливать только беззвучные видео (т.е. \"гифки\" с Mastodon)",
|
"loop_video_silent_only": "Зацикливать только беззвучные видео (т.е. \"гифки\" с Mastodon)",
|
||||||
"name": "Имя",
|
"name": "Имя",
|
||||||
"name_bio": "Имя и описание",
|
"name_bio": "Имя и описание",
|
||||||
|
"new_email": "Новый email",
|
||||||
"new_password": "Новый пароль",
|
"new_password": "Новый пароль",
|
||||||
"notification_visibility": "Показывать уведомления",
|
"notification_visibility": "Показывать уведомления",
|
||||||
"notification_visibility_follows": "Подписки",
|
"notification_visibility_follows": "Подписки",
|
||||||
|
|
|
@ -41,7 +41,13 @@ Vue.use(VueChatScroll)
|
||||||
Vue.use(VueClickOutside)
|
Vue.use(VueClickOutside)
|
||||||
Vue.use(PortalVue)
|
Vue.use(PortalVue)
|
||||||
Vue.use(VBodyScrollLock)
|
Vue.use(VBodyScrollLock)
|
||||||
Vue.use(VTooltip)
|
Vue.use(VTooltip, {
|
||||||
|
popover: {
|
||||||
|
defaultTrigger: 'hover click',
|
||||||
|
defaultContainer: false,
|
||||||
|
defaultOffset: 5
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const i18n = new VueI18n({
|
const i18n = new VueI18n({
|
||||||
// By default, use the browser locale, we will update it if neccessary
|
// By default, use the browser locale, we will update it if neccessary
|
||||||
|
|
|
@ -3,8 +3,9 @@ import { setPreset, applyTheme } from '../services/style_setter/style_setter.js'
|
||||||
|
|
||||||
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
||||||
|
|
||||||
const defaultState = {
|
export const defaultState = {
|
||||||
colors: {},
|
colors: {},
|
||||||
|
// bad name: actually hides posts of muted USERS
|
||||||
hideMutedPosts: undefined, // instance default
|
hideMutedPosts: undefined, // instance default
|
||||||
collapseMessageWithSubject: undefined, // instance default
|
collapseMessageWithSubject: undefined, // instance default
|
||||||
padEmoji: true,
|
padEmoji: true,
|
||||||
|
@ -37,11 +38,37 @@ const defaultState = {
|
||||||
subjectLineBehavior: undefined, // instance default
|
subjectLineBehavior: undefined, // instance default
|
||||||
alwaysShowSubjectInput: undefined, // instance default
|
alwaysShowSubjectInput: undefined, // instance default
|
||||||
postContentType: undefined, // instance default
|
postContentType: undefined, // instance default
|
||||||
minimalScopesMode: undefined // instance default
|
minimalScopesMode: undefined, // instance default
|
||||||
|
// This hides statuses filtered via a word filter
|
||||||
|
hideFilteredStatuses: undefined, // instance default
|
||||||
|
playVideosInModal: false,
|
||||||
|
useOneClickNsfw: false,
|
||||||
|
useContainFit: false,
|
||||||
|
hidePostStats: undefined, // instance default
|
||||||
|
hideUserStats: undefined // instance default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// caching the instance default properties
|
||||||
|
export const instanceDefaultProperties = Object.entries(defaultState)
|
||||||
|
.filter(([key, value]) => value === undefined)
|
||||||
|
.map(([key, value]) => key)
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
state: defaultState,
|
state: defaultState,
|
||||||
|
getters: {
|
||||||
|
mergedConfig (state, getters, rootState, rootGetters) {
|
||||||
|
const { instance } = rootState
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...instanceDefaultProperties
|
||||||
|
.map(key => [key, state[key] === undefined
|
||||||
|
? instance[key]
|
||||||
|
: state[key]
|
||||||
|
])
|
||||||
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setOption (state, { name, value }) {
|
setOption (state, { name, value }) {
|
||||||
set(state, name, value)
|
set(state, name, value)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { set } from 'vue'
|
import { set } from 'vue'
|
||||||
import { setPreset } from '../services/style_setter/style_setter.js'
|
import { setPreset } from '../services/style_setter/style_setter.js'
|
||||||
|
import { instanceDefaultProperties } from './config.js'
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
// Stuff from static/config.json and apiConfig
|
// Stuff from static/config.json and apiConfig
|
||||||
|
@ -74,6 +75,13 @@ const instance = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getters: {
|
||||||
|
instanceDefaultConfig (state) {
|
||||||
|
return instanceDefaultProperties
|
||||||
|
.map(key => [key, state[key]])
|
||||||
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
||||||
|
}
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setInstanceOption ({ commit, dispatch }, { name, value }) {
|
setInstanceOption ({ commit, dispatch }, { name, value }) {
|
||||||
commit('setInstanceOption', { name, value })
|
commit('setInstanceOption', { name, value })
|
||||||
|
|
|
@ -537,6 +537,10 @@ const statuses = {
|
||||||
setNotificationsSilence ({ rootState, commit }, { value }) {
|
setNotificationsSilence ({ rootState, commit }, { value }) {
|
||||||
commit('setNotificationsSilence', { value })
|
commit('setNotificationsSilence', { value })
|
||||||
},
|
},
|
||||||
|
fetchStatus ({ rootState, dispatch }, id) {
|
||||||
|
rootState.api.backendInteractor.fetchStatus({ id })
|
||||||
|
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
|
||||||
|
},
|
||||||
deleteStatus ({ rootState, commit }, status) {
|
deleteStatus ({ rootState, commit }, status) {
|
||||||
commit('setDeleted', { status })
|
commit('setDeleted', { status })
|
||||||
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
|
||||||
|
|
|
@ -60,6 +60,18 @@ const unmuteUser = (store, id) => {
|
||||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hideReblogs = (store, userId) => {
|
||||||
|
return store.rootState.api.backendInteractor.followUser({ id: userId, reblogs: false })
|
||||||
|
.then((relationship) => {
|
||||||
|
store.commit('updateUserRelationship', [relationship])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const showReblogs = (store, userId) => {
|
||||||
|
return store.rootState.api.backendInteractor.followUser({ id: userId, reblogs: true })
|
||||||
|
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
||||||
|
}
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
setMuted (state, { user: { id }, muted }) {
|
setMuted (state, { user: { id }, muted }) {
|
||||||
const user = state.usersObject[id]
|
const user = state.usersObject[id]
|
||||||
|
@ -135,6 +147,7 @@ export const mutations = {
|
||||||
user.muted = relationship.muting
|
user.muted = relationship.muting
|
||||||
user.statusnet_blocking = relationship.blocking
|
user.statusnet_blocking = relationship.blocking
|
||||||
user.subscribed = relationship.subscribing
|
user.subscribed = relationship.subscribing
|
||||||
|
user.showing_reblogs = relationship.showing_reblogs
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -272,6 +285,12 @@ const users = {
|
||||||
unmuteUser (store, id) {
|
unmuteUser (store, id) {
|
||||||
return unmuteUser(store, id)
|
return unmuteUser(store, id)
|
||||||
},
|
},
|
||||||
|
hideReblogs (store, id) {
|
||||||
|
return hideReblogs(store, id)
|
||||||
|
},
|
||||||
|
showReblogs (store, id) {
|
||||||
|
return showReblogs(store, id)
|
||||||
|
},
|
||||||
muteUsers (store, ids = []) {
|
muteUsers (store, ids = []) {
|
||||||
return Promise.all(ids.map(id => muteUser(store, id)))
|
return Promise.all(ids.map(id => muteUser(store, id)))
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,6 +8,7 @@ const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications
|
||||||
const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import'
|
const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import'
|
||||||
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
|
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
|
||||||
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
|
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
|
||||||
|
const CHANGE_EMAIL_URL = '/api/pleroma/change_email'
|
||||||
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
|
||||||
const TAG_USER_URL = '/api/pleroma/admin/users/tag'
|
const TAG_USER_URL = '/api/pleroma/admin/users/tag'
|
||||||
const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}`
|
const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}`
|
||||||
|
@ -16,12 +17,12 @@ const ADMIN_USERS_URL = '/api/pleroma/admin/users'
|
||||||
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
const SUGGESTIONS_URL = '/api/v1/suggestions'
|
||||||
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
|
||||||
|
|
||||||
const MFA_SETTINGS_URL = '/api/pleroma/profile/mfa'
|
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
|
||||||
const MFA_BACKUP_CODES_URL = '/api/pleroma/profile/mfa/backup_codes'
|
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
|
||||||
|
|
||||||
const MFA_SETUP_OTP_URL = '/api/pleroma/profile/mfa/setup/totp'
|
const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp'
|
||||||
const MFA_CONFIRM_OTP_URL = '/api/pleroma/profile/mfa/confirm/totp'
|
const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp'
|
||||||
const MFA_DISABLE_OTP_URL = '/api/pleroma/profile/mfa/totp'
|
const MFA_DISABLE_OTP_URL = '/api/pleroma/account/mfa/totp'
|
||||||
|
|
||||||
const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
|
const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
|
||||||
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
|
const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
|
||||||
|
@ -219,10 +220,16 @@ const authHeaders = (accessToken) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const followUser = ({ id, credentials }) => {
|
const followUser = ({ id, credentials, ...options }) => {
|
||||||
let url = MASTODON_FOLLOW_URL(id)
|
let url = MASTODON_FOLLOW_URL(id)
|
||||||
|
const form = {}
|
||||||
|
if (options.reblogs !== undefined) { form['reblogs'] = options.reblogs }
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
headers: authHeaders(credentials),
|
body: JSON.stringify(form),
|
||||||
|
headers: {
|
||||||
|
...authHeaders(credentials),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
@ -685,6 +692,20 @@ const deleteAccount = ({ credentials, password }) => {
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeEmail = ({ credentials, email, password }) => {
|
||||||
|
const form = new FormData()
|
||||||
|
|
||||||
|
form.append('email', email)
|
||||||
|
form.append('password', password)
|
||||||
|
|
||||||
|
return fetch(CHANGE_EMAIL_URL, {
|
||||||
|
body: form,
|
||||||
|
method: 'POST',
|
||||||
|
headers: authHeaders(credentials)
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
}
|
||||||
|
|
||||||
const changePassword = ({ credentials, password, newPassword, newPasswordConfirmation }) => {
|
const changePassword = ({ credentials, password, newPassword, newPasswordConfirmation }) => {
|
||||||
const form = new FormData()
|
const form = new FormData()
|
||||||
|
|
||||||
|
@ -960,6 +981,7 @@ const apiService = {
|
||||||
importBlocks,
|
importBlocks,
|
||||||
importFollows,
|
importFollows,
|
||||||
deleteAccount,
|
deleteAccount,
|
||||||
|
changeEmail,
|
||||||
changePassword,
|
changePassword,
|
||||||
settingsMFA,
|
settingsMFA,
|
||||||
mfaDisableOTP,
|
mfaDisableOTP,
|
||||||
|
|
|
@ -31,8 +31,8 @@ const backendInteractorService = credentials => {
|
||||||
return apiService.fetchUserRelationship({ id, credentials })
|
return apiService.fetchUserRelationship({ id, credentials })
|
||||||
}
|
}
|
||||||
|
|
||||||
const followUser = (id) => {
|
const followUser = ({ id, reblogs }) => {
|
||||||
return apiService.followUser({ credentials, id })
|
return apiService.followUser({ credentials, id, reblogs })
|
||||||
}
|
}
|
||||||
|
|
||||||
const unfollowUser = (id) => {
|
const unfollowUser = (id) => {
|
||||||
|
@ -131,6 +131,7 @@ const backendInteractorService = credentials => {
|
||||||
const importFollows = (file) => apiService.importFollows({ file, credentials })
|
const importFollows = (file) => apiService.importFollows({ file, credentials })
|
||||||
|
|
||||||
const deleteAccount = ({ password }) => apiService.deleteAccount({ credentials, password })
|
const deleteAccount = ({ password }) => apiService.deleteAccount({ credentials, password })
|
||||||
|
const changeEmail = ({ email, password }) => apiService.changeEmail({ credentials, email, password })
|
||||||
const changePassword = ({ password, newPassword, newPasswordConfirmation }) =>
|
const changePassword = ({ password, newPassword, newPasswordConfirmation }) =>
|
||||||
apiService.changePassword({ credentials, password, newPassword, newPasswordConfirmation })
|
apiService.changePassword({ credentials, password, newPassword, newPasswordConfirmation })
|
||||||
|
|
||||||
|
@ -195,6 +196,7 @@ const backendInteractorService = credentials => {
|
||||||
importBlocks,
|
importBlocks,
|
||||||
importFollows,
|
importFollows,
|
||||||
deleteAccount,
|
deleteAccount,
|
||||||
|
changeEmail,
|
||||||
changePassword,
|
changePassword,
|
||||||
fetchSettingsMFA,
|
fetchSettingsMFA,
|
||||||
generateMfaBackupCodes,
|
generateMfaBackupCodes,
|
||||||
|
|
|
@ -69,6 +69,7 @@ export const parseUser = (data) => {
|
||||||
output.following = relationship.following
|
output.following = relationship.following
|
||||||
output.statusnet_blocking = relationship.blocking
|
output.statusnet_blocking = relationship.blocking
|
||||||
output.muted = relationship.muting
|
output.muted = relationship.muting
|
||||||
|
output.showing_reblogs = relationship.showing_reblogs
|
||||||
output.subscribed = relationship.subscribing
|
output.subscribed = relationship.subscribing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
export const requestFollow = (user, store) => new Promise((resolve, reject) => {
|
export const requestFollow = (user, store) => new Promise((resolve, reject) => {
|
||||||
store.state.api.backendInteractor.followUser(user.id)
|
store.state.api.backendInteractor.followUser({ id: user.id })
|
||||||
.then((updated) => {
|
.then((updated) => {
|
||||||
store.commit('updateUserRelationship', [updated])
|
store.commit('updateUserRelationship', [updated])
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,10 @@ const update = ({ store, notifications, older }) => {
|
||||||
|
|
||||||
const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
const fetchAndUpdate = ({ store, credentials, older = false }) => {
|
||||||
const args = { credentials }
|
const args = { credentials }
|
||||||
|
const { getters } = store
|
||||||
const rootState = store.rootState || store.state
|
const rootState = store.rootState || store.state
|
||||||
const timelineData = rootState.statuses.notifications
|
const timelineData = rootState.statuses.notifications
|
||||||
const hideMutedPosts = typeof rootState.config.hideMutedPosts === 'undefined'
|
const hideMutedPosts = getters.mergedConfig.hideMutedPosts
|
||||||
? rootState.instance.hideMutedPosts
|
|
||||||
: rootState.config.hideMutedPosts
|
|
||||||
|
|
||||||
args['withMuted'] = !hideMutedPosts
|
args['withMuted'] = !hideMutedPosts
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,21 @@ const update = ({ store, statuses, timeline, showImmediately, userId }) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchAndUpdate = ({ store, credentials, timeline = 'friends', older = false, showImmediately = false, userId = false, tag = false, until }) => {
|
const fetchAndUpdate = ({
|
||||||
|
store,
|
||||||
|
credentials,
|
||||||
|
timeline = 'friends',
|
||||||
|
older = false,
|
||||||
|
showImmediately = false,
|
||||||
|
userId = false,
|
||||||
|
tag = false,
|
||||||
|
until
|
||||||
|
}) => {
|
||||||
const args = { timeline, credentials }
|
const args = { timeline, credentials }
|
||||||
const rootState = store.rootState || store.state
|
const rootState = store.rootState || store.state
|
||||||
|
const { getters } = store
|
||||||
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
|
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
|
||||||
const hideMutedPosts = typeof rootState.config.hideMutedPosts === 'undefined'
|
const hideMutedPosts = getters.mergedConfig.hideMutedPosts
|
||||||
? rootState.instance.hideMutedPosts
|
|
||||||
: rootState.config.hideMutedPosts
|
|
||||||
|
|
||||||
if (older) {
|
if (older) {
|
||||||
args['until'] = until || timelineData.minId
|
args['until'] = until || timelineData.minId
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
|
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
|
||||||
"ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ],
|
"ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ],
|
||||||
"monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ],
|
"monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ],
|
||||||
"mammal": [ "Mammal", "#272c37", "#444b5d", "#f8f8f8", "#9bacc8", "#7f3142", "#2bd850", "#2b90d9", "#ca8f04" ],
|
|
||||||
|
|
||||||
"redmond-xx": "/static/themes/redmond-xx.json",
|
"redmond-xx": "/static/themes/redmond-xx.json",
|
||||||
"redmond-xx-se": "/static/themes/redmond-xx-se.json",
|
"redmond-xx-se": "/static/themes/redmond-xx-se.json",
|
||||||
"redmond-xxi": "/static/themes/redmond-xxi.json",
|
"redmond-xxi": "/static/themes/redmond-xxi.json",
|
||||||
"breezy-dark": "/static/themes/breezy-dark.json",
|
"breezy-dark": "/static/themes/breezy-dark.json",
|
||||||
"breezy-light": "/static/themes/breezy-light.json"
|
"breezy-light": "/static/themes/breezy-light.json",
|
||||||
|
"mammal": "/static/themes/mammal.json"
|
||||||
}
|
}
|
||||||
|
|
57
static/themes/mammal.json
Normal file
57
static/themes/mammal.json
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"_pleroma_theme_version": 2,
|
||||||
|
"name": "Mammal",
|
||||||
|
"theme": {
|
||||||
|
"shadows": {
|
||||||
|
"button": [],
|
||||||
|
"buttonHover": [
|
||||||
|
{
|
||||||
|
"x": "0",
|
||||||
|
"y": "0",
|
||||||
|
"blur": "0",
|
||||||
|
"spread": 1024,
|
||||||
|
"color": "#56a7e1",
|
||||||
|
"alpha": "1",
|
||||||
|
"inset": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buttonPressed": [
|
||||||
|
{
|
||||||
|
"x": "0",
|
||||||
|
"y": "0",
|
||||||
|
"blur": "0",
|
||||||
|
"spread": 1024,
|
||||||
|
"color": "#56a7e1",
|
||||||
|
"alpha": "1",
|
||||||
|
"inset": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"panel": [],
|
||||||
|
"panelHeader": [],
|
||||||
|
"topBar": []
|
||||||
|
},
|
||||||
|
"opacity": { "input": "1" },
|
||||||
|
"colors": {
|
||||||
|
"bg": "#282c37",
|
||||||
|
"text": "#f8f8f8",
|
||||||
|
"link": "#9bacc8",
|
||||||
|
"fg": "#444b5d",
|
||||||
|
"input": "#FFFFFF",
|
||||||
|
"inputText": "#282c37",
|
||||||
|
"btn": "#2b90d9",
|
||||||
|
"btnText": "#FFFFFF",
|
||||||
|
"cRed": "#7f3142",
|
||||||
|
"cBlue": "#2b90d9",
|
||||||
|
"cGreen": "#2bd850",
|
||||||
|
"cOrange": "#ca8f04"
|
||||||
|
},
|
||||||
|
"radii": {
|
||||||
|
"btn": 4,
|
||||||
|
"input": 4,
|
||||||
|
"panel": "0",
|
||||||
|
"avatar": "4",
|
||||||
|
"avatarAlt": "4",
|
||||||
|
"attachment": "4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,8 +12,8 @@ const generateInput = (value, padEmoji = true) => {
|
||||||
},
|
},
|
||||||
mocks: {
|
mocks: {
|
||||||
$store: {
|
$store: {
|
||||||
state: {
|
getters: {
|
||||||
config: {
|
mergedConfig: {
|
||||||
padEmoji
|
padEmoji
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,14 @@ const actions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testGetters = {
|
const testGetters = {
|
||||||
findUser: state => getters.findUser(state.users)
|
findUser: state => getters.findUser(state.users),
|
||||||
|
mergedConfig: state => ({
|
||||||
|
colors: '',
|
||||||
|
highlight: {},
|
||||||
|
customTheme: {
|
||||||
|
colors: []
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const localUser = {
|
const localUser = {
|
||||||
|
@ -45,13 +52,6 @@ const externalProfileStore = new Vuex.Store({
|
||||||
interface: {
|
interface: {
|
||||||
browserSupport: ''
|
browserSupport: ''
|
||||||
},
|
},
|
||||||
config: {
|
|
||||||
colors: '',
|
|
||||||
highlight: {},
|
|
||||||
customTheme: {
|
|
||||||
colors: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
instance: {
|
instance: {
|
||||||
hideUserStats: true
|
hideUserStats: true
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue