Merge branch 'develop' of git.pleroma.social:pleroma/pleroma-fe into develop

This commit is contained in:
sadposter 2021-06-25 10:07:29 +01:00
commit 214d85189c
84 changed files with 2237 additions and 1153 deletions

View file

@ -6,10 +6,7 @@
:bound-to="{ x: 'container' }"
remove-padding
>
<div
slot="content"
class="account-tools-popover"
>
<template v-slot:content>
<div class="dropdown-menu">
<template v-if="relationship.following">
<button
@ -59,16 +56,15 @@
{{ $t('user_card.message') }}
</button>
</div>
</div>
<div
slot="trigger"
class="ellipsis-button"
>
<FAIcon
class="icon"
icon="ellipsis-v"
/>
</div>
</template>
<template v-slot:trigger>
<button class="button-unstyled ellipsis-button">
<FAIcon
class="icon"
icon="ellipsis-v"
/>
</button>
</template>
</Popover>
</div>
</template>
@ -83,7 +79,6 @@
}
.ellipsis-button {
cursor: pointer;
width: 2.5em;
margin: -0.5em 0;
padding: 0.5em 0;

View file

@ -1,4 +1,5 @@
import StillImage from '../still-image/still-image.vue'
import Flash from '../flash/flash.vue'
import VideoAttachment from '../video_attachment/video_attachment.vue'
import nsfwImage from '../../assets/nsfw.png'
import fileTypeService from '../../services/file_type/file_type.service.js'
@ -43,6 +44,7 @@ const Attachment = {
}
},
components: {
Flash,
StillImage,
VideoAttachment
},

View file

@ -117,6 +117,11 @@
<!-- eslint-enable vue/no-v-html -->
</div>
</div>
<Flash
v-if="type === 'flash'"
:src="attachment.large_thumb_url || attachment.url"
/>
</div>
</template>
@ -172,6 +177,7 @@
}
.non-gallery.attachment {
&.flash,
&.video {
flex: 1 0 40%;
}

View file

@ -23,10 +23,7 @@
class="timeline"
>
<List :items="sortedChatList">
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<ChatListItem
:key="item.id"
:compact="false"

View file

@ -50,7 +50,7 @@
@show="menuOpened = true"
@close="menuOpened = false"
>
<div slot="content">
<template v-slot:content>
<div class="dropdown-menu">
<button
class="button-default dropdown-item dropdown-item-icon"
@ -59,26 +59,28 @@
<FAIcon icon="times" /> {{ $t("chats.delete") }}
</button>
</div>
</div>
<button
slot="trigger"
class="button-default menu-icon"
:title="$t('chats.more')"
>
<FAIcon icon="ellipsis-h" />
</button>
</template>
<template v-slot:trigger>
<button
class="button-default menu-icon"
:title="$t('chats.more')"
>
<FAIcon icon="ellipsis-h" />
</button>
</template>
</Popover>
</div>
<StatusContent
:status="messageForStatusContent"
:full-content="true"
>
<span
slot="footer"
class="created-at"
>
{{ createdAt }}
</span>
<template v-slot:footer>
<span
class="created-at"
>
{{ createdAt }}
</span>
</template>
</StatusContent>
</div>
</div>

View file

@ -9,7 +9,7 @@
class="btn button-default"
>
{{ $t('domain_mute_card.unmute') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('domain_mute_card.unmute_progress') }}
</template>
</ProgressButton>
@ -19,7 +19,7 @@
class="btn button-default"
>
{{ $t('domain_mute_card.mute') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('domain_mute_card.mute_progress') }}
</template>
</ProgressButton>

View file

@ -57,6 +57,7 @@ const EmojiInput = {
required: true,
type: Function
},
// TODO VUE3: change to modelValue, change 'input' event to 'input'
value: {
/**
* Used for v-model
@ -143,32 +144,31 @@ const EmojiInput = {
}
},
mounted () {
const slots = this.$slots.default
if (!slots || slots.length === 0) return
const input = slots.find(slot => ['input', 'textarea'].includes(slot.tag))
const { root } = this.$refs
const input = root.querySelector('.emoji-input > input') || root.querySelector('.emoji-input > textarea')
if (!input) return
this.input = input
this.resize()
input.elm.addEventListener('blur', this.onBlur)
input.elm.addEventListener('focus', this.onFocus)
input.elm.addEventListener('paste', this.onPaste)
input.elm.addEventListener('keyup', this.onKeyUp)
input.elm.addEventListener('keydown', this.onKeyDown)
input.elm.addEventListener('click', this.onClickInput)
input.elm.addEventListener('transitionend', this.onTransition)
input.elm.addEventListener('input', this.onInput)
input.addEventListener('blur', this.onBlur)
input.addEventListener('focus', this.onFocus)
input.addEventListener('paste', this.onPaste)
input.addEventListener('keyup', this.onKeyUp)
input.addEventListener('keydown', this.onKeyDown)
input.addEventListener('click', this.onClickInput)
input.addEventListener('transitionend', this.onTransition)
input.addEventListener('input', this.onInput)
},
unmounted () {
const { input } = this
if (input) {
input.elm.removeEventListener('blur', this.onBlur)
input.elm.removeEventListener('focus', this.onFocus)
input.elm.removeEventListener('paste', this.onPaste)
input.elm.removeEventListener('keyup', this.onKeyUp)
input.elm.removeEventListener('keydown', this.onKeyDown)
input.elm.removeEventListener('click', this.onClickInput)
input.elm.removeEventListener('transitionend', this.onTransition)
input.elm.removeEventListener('input', this.onInput)
input.removeEventListener('blur', this.onBlur)
input.removeEventListener('focus', this.onFocus)
input.removeEventListener('paste', this.onPaste)
input.removeEventListener('keyup', this.onKeyUp)
input.removeEventListener('keydown', this.onKeyDown)
input.removeEventListener('click', this.onClickInput)
input.removeEventListener('transitionend', this.onTransition)
input.removeEventListener('input', this.onInput)
}
},
watch: {
@ -216,7 +216,7 @@ const EmojiInput = {
}, 0)
},
togglePicker () {
this.input.elm.focus()
this.input.focus()
this.showPicker = !this.showPicker
if (this.showPicker) {
this.scrollIntoView()
@ -262,13 +262,13 @@ const EmojiInput = {
this.$emit('input', newValue)
const position = this.caret + (insertion + spaceAfter + spaceBefore).length
if (!keepOpen) {
this.input.elm.focus()
this.input.focus()
}
this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion
// Set selection right after the replacement instead of the very end
this.input.elm.setSelectionRange(position, position)
this.input.setSelectionRange(position, position)
this.caret = position
})
},
@ -285,9 +285,9 @@ const EmojiInput = {
this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion
this.input.elm.focus()
this.input.focus()
// Set selection right after the replacement instead of the very end
this.input.elm.setSelectionRange(position, position)
this.input.setSelectionRange(position, position)
this.caret = position
})
e.preventDefault()
@ -349,7 +349,7 @@ const EmojiInput = {
}
this.$nextTick(() => {
const { offsetHeight } = this.input.elm
const { offsetHeight } = this.input
const { picker } = this.$refs
const pickerBottom = picker.$el.getBoundingClientRect().bottom
if (pickerBottom > window.innerHeight) {
@ -414,8 +414,8 @@ const EmojiInput = {
// Scroll the input element to the position of the cursor
this.$nextTick(() => {
this.input.elm.blur()
this.input.elm.focus()
this.input.blur()
this.input.focus()
})
}
// Disable suggestions hotkeys if suggestions are hidden
@ -444,7 +444,7 @@ const EmojiInput = {
// de-focuses the element (i.e. default browser behavior)
if (key === 'Escape') {
if (!this.temporarilyHideSuggestions) {
this.input.elm.focus()
this.input.focus()
}
}
@ -480,7 +480,7 @@ const EmojiInput = {
if (!panel) return
const picker = this.$refs.picker.$el
const panelBody = this.$refs['panel-body']
const { offsetHeight, offsetTop } = this.input.elm
const { offsetHeight, offsetTop } = this.input
const offsetBottom = offsetTop + offsetHeight
this.setPlacement(panelBody, panel, offsetBottom)
@ -494,7 +494,7 @@ const EmojiInput = {
if (this.placement === 'top' || (this.placement === 'auto' && this.overflowsBottom(container))) {
target.style.top = 'auto'
target.style.bottom = this.input.elm.offsetHeight + 'px'
target.style.bottom = this.input.offsetHeight + 'px'
}
},
overflowsBottom (el) {

View file

@ -3,6 +3,7 @@
v-click-outside="onClickOutside"
class="emoji-input"
:class="{ 'with-picker': !hideEmojiButton }"
ref='root'
>
<slot />
<template v-if="enableEmojiPicker">

View file

@ -7,10 +7,7 @@
:bound-to="{ x: 'container' }"
remove-padding
>
<div
slot="content"
slot-scope="{close}"
>
<template v-slot:content="{close}">
<div class="dropdown-menu">
<button
v-if="canMute && !status.thread_muted"
@ -120,16 +117,15 @@
/><span>{{ $t("user_card.report") }}</span>
</button>
</div>
</div>
<span
slot="trigger"
class="popover-trigger"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="ellipsis-h"
/>
</span>
</template>
<template v-slot:trigger>
<button class="button-unstyled popover-trigger">
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="ellipsis-h"
/>
</button>
</template>
</Popover>
</template>

View file

@ -2,7 +2,7 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for
const FeaturesPanel = {
computed: {
chat: function () { return this.$store.state.instance.chatAvailable },
shout: function () { return this.$store.state.instance.shoutAvailable },
pleromaChatMessages: function () { return this.$store.state.instance.pleromaChatMessagesAvailable },
gopher: function () { return this.$store.state.instance.gopherAvailable },
whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },

View file

@ -8,8 +8,8 @@
</div>
<div class="panel-body features-panel">
<ul>
<li v-if="chat">
{{ $t('features_panel.chat') }}
<li v-if="shout">
{{ $t('features_panel.shout') }}
</li>
<li v-if="pleromaChatMessages">
{{ $t('features_panel.pleroma_chat_messages') }}

View file

@ -0,0 +1,52 @@
import RuffleService from '../../services/ruffle_service/ruffle_service.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faStop,
faExclamationTriangle
} from '@fortawesome/free-solid-svg-icons'
library.add(
faStop,
faExclamationTriangle
)
const Flash = {
props: [ 'src' ],
data () {
return {
player: false, // can be true, "hidden", false. hidden = element exists
loaded: false,
ruffleInstance: null
}
},
methods: {
openPlayer () {
if (this.player) return // prevent double-loading, or re-loading on failure
this.player = 'hidden'
RuffleService.getRuffle().then((ruffle) => {
const player = ruffle.newest().createPlayer()
player.config = {
letterbox: 'on'
}
const container = this.$refs.container
container.appendChild(player)
player.style.width = '100%'
player.style.height = '100%'
player.load(this.src).then(() => {
this.player = true
}).catch((e) => {
console.error('Error loading ruffle', e)
this.player = 'error'
})
this.ruffleInstance = player
})
},
closePlayer () {
console.log(this.ruffleInstance)
this.ruffleInstance.remove()
this.player = false
}
}
}
export default Flash

View file

@ -0,0 +1,88 @@
<template>
<div class="Flash">
<div
v-if="player === true || player === 'hidden'"
ref="container"
class="player"
:class="{ hidden: player === 'hidden' }"
/>
<button
v-if="player !== true"
class="button-unstyled placeholder"
@click="openPlayer"
>
<span
v-if="player === 'hidden'"
class="label"
>
{{ $t('general.loading') }}
</span>
<span
v-if="player === 'error'"
class="label"
>
{{ $t('general.flash_fail') }}
</span>
<span
v-else
class="label"
>
<p>
{{ $t('general.flash_content') }}
</p>
<p>
<FAIcon icon="exclamation-triangle" />
{{ $t('general.flash_security') }}
</p>
</span>
</button>
<button
v-if="player"
class="button-unstyled hider"
@click="closePlayer"
>
<FAIcon icon="stop" />
</button>
</div>
</template>
<script src="./flash.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.Flash {
width: 100%;
height: 260px;
position: relative;
.player {
height: 100%;
width: 100%;
}
.hider {
top: 0;
}
.label {
text-align: center;
flex: 1 1 0;
line-height: 1.2;
white-space: normal;
word-wrap: normal;
}
.hidden {
display: none;
visibility: 'hidden';
}
.placeholder {
height: 100%;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View file

@ -1,14 +1,10 @@
import { set } from 'vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown
)
import Select from '../select/select.vue'
export default {
components: {
Select
},
props: [
'name', 'label', 'value', 'fallback', 'options', 'no-inherit'
],

View file

@ -22,30 +22,20 @@
class="opt-l"
:for="name + '-o'"
/>
<label
:for="name + '-font-switcher'"
class="select"
<Select
:id="name + '-font-switcher'"
v-model="preset"
:disabled="!present"
class="font-switcher"
>
<select
:id="name + '-font-switcher'"
v-model="preset"
:disabled="!present"
class="font-switcher"
<option
v-for="option in availableOptions"
:key="option"
:value="option"
>
<option
v-for="option in availableOptions"
:key="option"
:value="option"
>
{{ option === 'custom' ? $t('settings.style.fonts.custom') : option }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ option === 'custom' ? $t('settings.style.fonts.custom') : option }}
</option>
</Select>
<input
v-if="isCustom"
:id="name"
@ -65,7 +55,8 @@
min-width: 10em;
}
&.custom {
.select {
/* TODO Should make proper joiners... */
.font-switcher {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

View file

@ -3,27 +3,18 @@
<label for="interface-language-switcher">
{{ $t('settings.interfaceLanguage') }}
</label>
<label
for="interface-language-switcher"
class="select"
<Select
id="interface-language-switcher"
v-model="language"
>
<select
id="interface-language-switcher"
v-model="language"
<option
v-for="lang in languages"
:key="lang.code"
:value="lang.code"
>
<option
v-for="lang in languages"
:key="lang.code"
:value="lang.code"
>
{{ lang.name }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ lang.name }}
</option>
</Select>
</div>
</template>
@ -32,16 +23,12 @@ import languagesObject from '../../i18n/messages'
import localeService from '../../services/locale/locale.service.js'
import ISO6391 from 'iso-639-1'
import _ from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown
)
import Select from '../select/select.vue'
export default {
components: {
Select
},
computed: {
languages () {
return _.map(languagesObject.languages, (code) => ({ code: code, name: this.getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name))

View file

@ -8,7 +8,7 @@
@show="setToggled(true)"
@close="setToggled(false)"
>
<div slot="content">
<template v-slot:content>
<div class="dropdown-menu">
<span v-if="user.is_local">
<button
@ -121,26 +121,27 @@
</button>
</span>
</div>
</div>
<button
slot="trigger"
class="btn button-default btn-block moderation-tools-button"
:class="{ toggled }"
>
{{ $t('user_card.admin_menu.moderation') }}
<FAIcon icon="chevron-down" />
</button>
</template>
<template v-slot:trigger>
<button
class="btn button-default btn-block moderation-tools-button"
:class="{ toggled }"
>
{{ $t('user_card.admin_menu.moderation') }}
<FAIcon icon="chevron-down" />
</button>
</template>
</Popover>
<portal to="modal">
<DialogModal
v-if="showDeleteUserDialog"
:on-cancel="deleteUserDialog.bind(this, false)"
>
<template slot="header">
<template v-slot:header>
{{ $t('user_card.admin_menu.delete_user') }}
</template>
<p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p>
<template slot="footer">
<template v-slot:footer>
<button
class="btn button-default"
@click="deleteUserDialog(false)"

View file

@ -5,9 +5,7 @@
placement="bottom"
:bound-to="{ x: 'container' }"
>
<template
v-slot:content
>
<template v-slot:content>
<div class="dropdown-menu">
<button
class="button-default dropdown-item"
@ -66,7 +64,9 @@
</div>
</template>
<template v-slot:trigger>
<FAIcon icon="filter" />
<button class="button-unstyled">
<FAIcon icon="filter" />
</button>
</template>
</Popover>
</template>

View file

@ -1,19 +1,21 @@
import * as DateUtils from 'src/services/date_utils/date_utils.js'
import { uniq } from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core'
import Select from '../select/select.vue'
import {
faTimes,
faChevronDown,
faPlus
} from '@fortawesome/free-solid-svg-icons'
library.add(
faTimes,
faChevronDown,
faPlus
)
export default {
components: {
Select
},
name: 'PollForm',
props: ['visible'],
data: () => ({

View file

@ -46,23 +46,19 @@
class="poll-type"
:title="$t('polls.type')"
>
<label
for="poll-type-selector"
class="select"
<Select
v-model="pollType"
class="poll-type-select"
unstyled="true"
@change="updatePollToParent"
>
<select
v-model="pollType"
class="select"
@change="updatePollToParent"
>
<option value="single">{{ $t('polls.single_choice') }}</option>
<option value="multiple">{{ $t('polls.multiple_choices') }}</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
<option value="single">
{{ $t('polls.single_choice') }}
</option>
<option value="multiple">
{{ $t('polls.multiple_choices') }}
</option>
</Select>
</div>
<div
class="poll-expiry"
@ -76,24 +72,20 @@
:max="maxExpirationInCurrentUnit"
@change="expiryAmountChange"
>
<label class="expiry-unit select">
<select
v-model="expiryUnit"
@change="expiryAmountChange"
<Select
v-model="expiryUnit"
unstyled="true"
class="expiry-unit"
@change="expiryAmountChange"
>
<option
v-for="unit in expiryUnits"
:key="unit"
:value="unit"
>
<option
v-for="unit in expiryUnits"
:key="unit"
:value="unit"
>
{{ $t(`time.${unit}_short`, ['']) }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ $t(`time.${unit}_short`, ['']) }}
</option>
</Select>
</div>
</div>
</div>
@ -147,10 +139,8 @@
.poll-type {
margin-right: 0.75em;
flex: 1 1 60%;
.select {
border: none;
box-shadow: none;
background-color: transparent;
.poll-type-select {
padding-right: 0.75em;
}
}
@ -162,12 +152,6 @@
width: 3em;
text-align: right;
}
.expiry-unit {
border: none;
box-shadow: none;
background-color: transparent;
}
}
}
</style>

View file

@ -54,7 +54,7 @@ const Popover = {
}
// Popover will be anchored around this element, trigger ref is the container, so
// its children are what are inside the slot. Expect only one slot="trigger".
// its children are what are inside the slot. Expect only one v-slot:trigger.
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
// SVGs don't have offsetWidth/Height, use fallback
const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth

View file

@ -11,10 +11,10 @@ import { reject, map, uniqBy, debounce } from 'lodash'
import suggestor from '../emoji_input/suggestor.js'
import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown,
faSmileBeam,
faPollH,
faUpload,
@ -24,7 +24,6 @@ import {
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown,
faSmileBeam,
faPollH,
faUpload,
@ -84,6 +83,7 @@ const PostStatusForm = {
PollForm,
ScopeSelector,
Checkbox,
Select,
Attachment,
StatusContent
},

View file

@ -189,28 +189,19 @@
v-if="postFormats.length > 1"
class="text-format"
>
<label
for="post-content-type"
class="select"
<Select
id="post-content-type"
v-model="newStatus.contentType"
class="form-control"
>
<select
id="post-content-type"
v-model="newStatus.contentType"
class="form-control"
<option
v-for="postFormat in postFormats"
:key="postFormat"
:value="postFormat"
>
<option
v-for="postFormat in postFormats"
:key="postFormat"
:value="postFormat"
>
{{ $t(`post_status.content_type["${postFormat}"]`) }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ $t(`post_status.content_type["${postFormat}"]`) }}
</option>
</Select>
</div>
<div
v-if="postFormats.length === 1 && postFormats[0] !== 'text/plain'"

View file

@ -8,10 +8,7 @@
remove-padding
@show="focusInput"
>
<div
slot="content"
slot-scope="{close}"
>
<template v-slot:content="{close}">
<div class="reaction-picker-filter">
<input
v-model="filterWord"
@ -41,17 +38,18 @@
</span>
<div class="reaction-bottom-fader" />
</div>
</div>
<span
slot="trigger"
class="popover-trigger"
:title="$t('tool_tip.add_reaction')"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
:icon="['far', 'smile-beam']"
/>
</span>
</template>
<template v-slot:trigger>
<button
class="button-unstyled popover-trigger"
:title="$t('tool_tip.add_reaction')"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
:icon="['far', 'smile-beam']"
/>
</button>
</template>
</Popover>
</template>

View file

@ -0,0 +1,21 @@
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown
)
export default {
model: {
prop: 'value',
event: 'change'
},
props: [
'value',
'disabled',
'unstyled',
'kind'
]
}

View file

@ -0,0 +1,62 @@
<template>
<label
class="Select input"
:class="{ disabled, unstyled }"
>
<select
:disabled="disabled"
:value="value"
@change="$emit('change', $event.target.value)"
>
<slot />
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
</template>
<script src="./select.js"> </script>
<style lang="scss">
@import '../../_variables.scss';
.Select {
padding: 0;
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
color: $fallback--text;
color: var(--inputText, --text, $fallback--text);
margin: 0;
padding: 0 2em 0 .2em;
font-family: sans-serif;
font-family: var(--inputFont, sans-serif);
font-size: 14px;
width: 100%;
z-index: 1;
height: 28px;
line-height: 16px;
}
.select-down-icon {
position: absolute;
top: 0;
bottom: 0;
right: 5px;
height: 100%;
color: $fallback--text;
color: var(--inputText, $fallback--text);
line-height: 28px;
z-index: 0;
pointer-events: none;
}
}
</style>

View file

@ -24,10 +24,7 @@
:items="items"
:get-key="getKey"
>
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<div
class="selectable-list-item-inner"
:class="{ 'selectable-list-item-selected-inner': isSelected(item) }"
@ -44,7 +41,7 @@
/>
</div>
</template>
<template slot="empty">
<template v-slot:empty>
<slot name="empty" />
</template>
</List>

View file

@ -0,0 +1,30 @@
import { get, set } from 'lodash'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import ModifiedIndicator from './modified_indicator.vue'
export default {
components: {
Checkbox,
ModifiedIndicator
},
props: [
'path',
'disabled'
],
computed: {
pathDefault () {
const [firstSegment, ...rest] = this.path.split('.')
return [firstSegment + 'DefaultValue', ...rest].join('.')
},
state () {
return get(this.$parent, this.path)
},
isChanged () {
return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault)
}
},
methods: {
update (e) {
set(this.$parent, this.path, e)
}
}
}

View file

@ -18,40 +18,4 @@
</label>
</template>
<script>
import { get, set } from 'lodash'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import ModifiedIndicator from './modified_indicator.vue'
export default {
components: {
Checkbox,
ModifiedIndicator
},
props: [
'path',
'disabled'
],
computed: {
pathDefault () {
const [firstSegment, ...rest] = this.path.split('.')
return [firstSegment + 'DefaultValue', ...rest].join('.')
},
state () {
return get(this.$parent, this.path)
},
isChanged () {
return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault)
}
},
methods: {
update (e) {
set(this.$parent, this.path, e)
}
}
}
</script>
<style lang="scss">
.BooleanSetting {
}
</style>
<script src="./boolean_setting.js"></script>

View file

@ -0,0 +1,34 @@
import { get, set } from 'lodash'
import Select from 'src/components/select/select.vue'
import ModifiedIndicator from './modified_indicator.vue'
export default {
components: {
Select,
ModifiedIndicator
},
props: [
'path',
'disabled',
'options'
],
computed: {
pathDefault () {
const [firstSegment, ...rest] = this.path.split('.')
return [firstSegment + 'DefaultValue', ...rest].join('.')
},
state () {
return get(this.$parent, this.path)
},
defaultState () {
return get(this.$parent, this.pathDefault)
},
isChanged () {
return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault)
}
},
methods: {
update (e) {
set(this.$parent, this.path, e)
}
}
}

View file

@ -0,0 +1,29 @@
<template>
<label
class="ChoiceSetting"
>
<slot />
<Select
:value="state"
:disabled="disabled"
@change="update"
>
<option
v-for="option in options"
:key="option.key"
:value="option.value"
>
{{ option.label }}
{{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }}
</option>
</Select>
<ModifiedIndicator :changed="isChanged" />
</label>
</template>
<script src="./choice_setting.js"></script>
<style lang="scss">
.ChoiceSetting {
}
</style>

View file

@ -6,18 +6,18 @@
<Popover
trigger="hover"
>
<span slot="trigger">
<template v-slot:trigger>
&nbsp;
<FAIcon
icon="wrench"
:aria-label="$t('settings.setting_changed')"
/>
</span>
<div
slot="content"
class="modified-tooltip"
>
{{ $t('settings.setting_changed') }}
</div>
</template>
<template v-slot:content>
<div class="modified-tooltip">
{{ $t('settings.setting_changed') }}
</div>
</template>
</Popover>
</span>
</template>

View file

@ -62,20 +62,18 @@
:bound-to="{ x: 'container' }"
remove-padding
>
<button
slot="trigger"
class="btn button-default"
:title="$t('general.close')"
>
<span>{{ $t("settings.file_export_import.backup_restore") }}</span>
<FAIcon
icon="chevron-down"
/>
</button>
<div
slot="content"
slot-scope="{close}"
>
<template v-slot:trigger>
<button
class="btn button-default"
:title="$t('general.close')"
>
<span>{{ $t("settings.file_export_import.backup_restore") }}</span>
<FAIcon
icon="chevron-down"
/>
</button>
</template>
<template v-slot:content="{close}">
<div class="dropdown-menu">
<button
class="button-default dropdown-item dropdown-item-icon"
@ -108,7 +106,7 @@
/><span>{{ $t("settings.file_export_import.restore_settings") }}</span>
</button>
</div>
</div>
</template>
</Popover>
</div>
</div>

View file

@ -7,13 +7,24 @@
margin: 1em 1em 1.4em;
padding-bottom: 1.4em;
> div {
> div,
> label {
display: block;
margin-bottom: .5em;
&:last-child {
margin-bottom: 0;
}
}
.select-multiple {
display: flex;
.option-list {
margin: 0;
padding-left: .5em;
}
}
&:last-child {
border-bottom: none;
padding-bottom: 0;

View file

@ -1,24 +1,23 @@
import { filter, trim } from 'lodash'
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown
)
const FilteringTab = {
data () {
return {
muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n')
muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n'),
replyVisibilityOptions: ['all', 'following', 'self'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.reply_visibility_${mode}`)
}))
}
},
components: {
BooleanSetting
BooleanSetting,
ChoiceSetting
},
computed: {
...SharedComputedObject(),

View file

@ -36,29 +36,13 @@
</li>
</ul>
</div>
<div>
<ChoiceSetting
id="replyVisibility"
path="replyVisibility"
:options="replyVisibilityOptions"
>
{{ $t('settings.replies_in_timeline') }}
<label
for="replyVisibility"
class="select"
>
<select
id="replyVisibility"
v-model="replyVisibility"
>
<option
value="all"
selected
>{{ $t('settings.reply_visibility_all') }}</option>
<option value="following">{{ $t('settings.reply_visibility_following') }}</option>
<option value="self">{{ $t('settings.reply_visibility_self') }}</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
</div>
</ChoiceSetting>
<div>
<BooleanSetting path="hidePostStats">
{{ $t('settings.hide_post_stats') }}

View file

@ -1,21 +1,25 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown,
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown,
faGlobe
)
const GeneralTab = {
data () {
return {
subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
})),
loopSilentAvailable:
// Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
@ -27,17 +31,26 @@ const GeneralTab = {
},
components: {
BooleanSetting,
ChoiceSetting,
InterfaceLanguageSwitcher
},
computed: {
postFormats () {
return this.$store.state.instance.postFormats || []
},
postContentOptions () {
return this.postFormats.map(format => ({
key: format,
value: format,
label: this.$t(`post_status.content_type["${format}"]`)
}))
},
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
instanceWallpaperUsed () {
return this.$store.state.instance.background &&
!this.$store.state.users.currentUser.background_image
},
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
...SharedComputedObject()
}
}

View file

@ -11,11 +11,21 @@
{{ $t('settings.hide_isp') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="sidebarRight">
{{ $t('settings.right_sidebar') }}
</BooleanSetting>
</li>
<li v-if="instanceWallpaperUsed">
<BooleanSetting path="hideInstanceWallpaper">
{{ $t('settings.hide_wallpaper') }}
</BooleanSetting>
</li>
<li v-if="instanceShoutboxPresent">
<BooleanSetting path="hideShoutbox">
{{ $t('settings.hide_shoutbox') }}
</BooleanSetting>
</li>
</ul>
</div>
<div class="setting-item">
@ -85,62 +95,22 @@
</BooleanSetting>
</li>
<li>
<div>
<ChoiceSetting
id="subjectLineBehavior"
path="subjectLineBehavior"
:options="subjectLineOptions"
>
{{ $t('settings.subject_line_behavior') }}
<label
for="subjectLineBehavior"
class="select"
>
<select
id="subjectLineBehavior"
v-model="subjectLineBehavior"
>
<option value="email">
{{ $t('settings.subject_line_email') }}
{{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }}
</option>
<option value="masto">
{{ $t('settings.subject_line_mastodon') }}
{{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }}
</option>
<option value="noop">
{{ $t('settings.subject_line_noop') }}
{{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
</div>
</ChoiceSetting>
</li>
<li v-if="postFormats.length > 0">
<div>
<ChoiceSetting
id="postContentType"
path="postContentType"
:options="postContentOptions"
>
{{ $t('settings.post_status_content_type') }}
<label
for="postContentType"
class="select"
>
<select
id="postContentType"
v-model="postContentType"
>
<option
v-for="postFormat in postFormats"
:key="postFormat"
:value="postFormat"
>
{{ $t(`post_status.content_type["${postFormat}"]`) }}
{{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
</div>
</ChoiceSetting>
</li>
<li>
<BooleanSetting path="minimalScopesMode">

View file

@ -10,20 +10,18 @@
:query="queryUserIds"
:placeholder="$t('settings.search_user_to_block')"
>
<BlockCard
slot-scope="row"
:user-id="row.item"
/>
<template v-slot="row">
<BlockCard
:user-id="row.item"
/>
</template>
</Autosuggest>
</div>
<BlockList
:refresh="true"
:get-key="i => i"
>
<template
slot="header"
slot-scope="{selected}"
>
<template v-slot:header="{selected}">
<div class="bulk-actions">
<ProgressButton
v-if="selected.length > 0"
@ -31,7 +29,7 @@
:click="() => blockUsers(selected)"
>
{{ $t('user_card.block') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('user_card.block_progress') }}
</template>
</ProgressButton>
@ -41,19 +39,16 @@
:click="() => unblockUsers(selected)"
>
{{ $t('user_card.unblock') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('user_card.unblock_progress') }}
</template>
</ProgressButton>
</div>
</template>
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<BlockCard :user-id="item" />
</template>
<template slot="empty">
<template v-slot:empty>
{{ $t('settings.no_blocks') }}
</template>
</BlockList>
@ -68,20 +63,18 @@
:query="queryUserIds"
:placeholder="$t('settings.search_user_to_mute')"
>
<MuteCard
slot-scope="row"
:user-id="row.item"
/>
<template v-slot="row">
<MuteCard
:user-id="row.item"
/>
</template>
</Autosuggest>
</div>
<MuteList
:refresh="true"
:get-key="i => i"
>
<template
slot="header"
slot-scope="{selected}"
>
<template v-slot:header="{selected}">
<div class="bulk-actions">
<ProgressButton
v-if="selected.length > 0"
@ -89,7 +82,7 @@
:click="() => muteUsers(selected)"
>
{{ $t('user_card.mute') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('user_card.mute_progress') }}
</template>
</ProgressButton>
@ -99,19 +92,16 @@
:click="() => unmuteUsers(selected)"
>
{{ $t('user_card.unmute') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('user_card.unmute_progress') }}
</template>
</ProgressButton>
</div>
</template>
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<MuteCard :user-id="item" />
</template>
<template slot="empty">
<template v-slot:empty>
{{ $t('settings.no_mutes') }}
</template>
</MuteList>
@ -124,20 +114,18 @@
:query="queryKnownDomains"
:placeholder="$t('settings.type_domains_to_mute')"
>
<DomainMuteCard
slot-scope="row"
:domain="row.item"
/>
<template v-slot="row">
<DomainMuteCard
:domain="row.item"
/>
</template>
</Autosuggest>
</div>
<DomainMuteList
:refresh="true"
:get-key="i => i"
>
<template
slot="header"
slot-scope="{selected}"
>
<template v-slot:header="{selected}">
<div class="bulk-actions">
<ProgressButton
v-if="selected.length > 0"
@ -145,19 +133,16 @@
:click="() => unmuteDomains(selected)"
>
{{ $t('domain_mute_card.unmute') }}
<template slot="progress">
<template v-slot:progress>
{{ $t('domain_mute_card.unmute_progress') }}
</template>
</ProgressButton>
</div>
</template>
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<DomainMuteCard :domain="item" />
</template>
<template slot="empty">
<template v-slot:empty>
{{ $t('settings.no_mutes') }}
</template>
</DomainMuteList>

View file

@ -36,16 +36,9 @@ import FontControl from 'src/components/font_control/font_control.vue'
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Select from 'src/components/select/select.vue'
import Preview from './preview.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faChevronDown
} from '@fortawesome/free-solid-svg-icons'
library.add(
faChevronDown
)
// List of color values used in v1
const v1OnlyNames = [
@ -395,7 +388,8 @@ export default {
FontControl,
TabSwitcher,
Preview,
Checkbox
Checkbox,
Select
},
methods: {
loadTheme (

View file

@ -55,7 +55,7 @@
for="preset-switcher"
class="select"
>
<select
<Select
id="preset-switcher"
v-model="selected"
class="preset-switcher"
@ -71,11 +71,7 @@
>
{{ style[0] || style.name }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</Select>
</label>
</div>
<div class="export-import">
@ -907,28 +903,19 @@
<div class="tab-header shadow-selector">
<div class="select-container">
{{ $t('settings.style.shadows.component') }}
<label
for="shadow-switcher"
class="select"
<Select
id="shadow-switcher"
v-model="shadowSelected"
class="shadow-switcher"
>
<select
id="shadow-switcher"
v-model="shadowSelected"
class="shadow-switcher"
<option
v-for="shadow in shadowsAvailable"
:key="shadow"
:value="shadow"
>
<option
v-for="shadow in shadowsAvailable"
:key="shadow"
:value="shadow"
>
{{ $t('settings.style.shadows.components.' + shadow) }}
</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
{{ $t('settings.style.shadows.components.' + shadow) }}
</option>
</Select>
</div>
<div class="override">
<label

View file

@ -1,5 +1,6 @@
import ColorInput from '../color_input/color_input.vue'
import OpacityInput from '../opacity_input/opacity_input.vue'
import Select from '../select/select.vue'
import { getCssShadow } from '../../services/style_setter/style_setter.js'
import { hex2rgb } from '../../services/color_convert/color_convert.js'
import { library } from '@fortawesome/fontawesome-svg-core'
@ -45,7 +46,8 @@ export default {
},
components: {
ColorInput,
OpacityInput
OpacityInput,
Select
},
methods: {
add () {

View file

@ -59,30 +59,20 @@
:disabled="usingFallback"
class="id-control style-control"
>
<label
for="shadow-switcher"
class="select"
<Select
id="shadow-switcher"
v-model="selectedId"
class="shadow-switcher"
:disabled="!ready || usingFallback"
>
<select
id="shadow-switcher"
v-model="selectedId"
class="shadow-switcher"
:disabled="!ready || usingFallback"
<option
v-for="(shadow, index) in cValue"
:key="index"
:value="index"
>
<option
v-for="(shadow, index) in cValue"
:key="index"
:value="index"
>
{{ $t('settings.style.shadows.shadow_id', { value: index }) }}
</option>
</select>
<FAIcon
icon="chevron-down"
class="select-down-icon"
/>
</label>
{{ $t('settings.style.shadows.shadow_id', { value: index }) }}
</option>
</Select>
<button
class="btn button-default"
:disabled="!ready || !present"
@ -316,20 +306,20 @@
.id-control {
align-items: stretch;
.select, .btn {
.shadow-switcher {
flex: 1;
}
.shadow-switcher, .btn {
min-width: 1px;
margin-right: 5px;
}
.btn {
padding: 0 .4em;
margin: 0 .1em;
}
.select {
flex: 1;
select {
align-self: initial;
}
}
}
}
}

View file

@ -10,7 +10,7 @@ library.add(
faTimes
)
const chatPanel = {
const shoutPanel = {
props: [ 'floating' ],
data () {
return {
@ -21,12 +21,12 @@ const chatPanel = {
},
computed: {
messages () {
return this.$store.state.chat.messages
return this.$store.state.shout.messages
}
},
methods: {
submit (message) {
this.$store.state.chat.channel.push('new_msg', { text: message }, 10000)
this.$store.state.shout.channel.push('new_msg', { text: message }, 10000)
this.currentMessage = ''
},
togglePanel () {
@ -50,4 +50,4 @@ const chatPanel = {
}
}
export default chatPanel
export default shoutPanel

View file

@ -1,12 +1,12 @@
<template>
<div
v-if="!collapsed || !floating"
class="chat-panel"
class="shout-panel"
>
<div class="panel panel-default">
<div
class="panel-heading timeline-heading"
:class="{ 'chat-heading': floating }"
:class="{ 'shout-heading': floating }"
@click.stop.prevent="togglePanel"
>
<div class="title">
@ -18,33 +18,33 @@
/>
</div>
</div>
<div class="chat-window">
<div class="shout-window">
<div
v-for="message in messages"
:key="message.id"
class="chat-message"
class="shout-message"
>
<span class="chat-avatar">
<span class="shout-avatar">
<img :src="message.author.avatar">
</span>
<div class="chat-content">
<div class="shout-content">
<router-link
class="chat-name"
class="shout-name"
:to="userProfileLink(message.author)"
>
{{ message.author.username }}
</router-link>
<br>
<span class="chat-text">
<span class="shout-text">
{{ message.text }}
</span>
</div>
</div>
</div>
<div class="chat-input">
<div class="shout-input">
<textarea
v-model="currentMessage"
class="chat-input-textarea"
class="shout-input-textarea"
rows="1"
@keyup.enter="submit(currentMessage)"
/>
@ -53,11 +53,11 @@
</div>
<div
v-else
class="chat-panel"
class="shout-panel"
>
<div class="panel panel-default">
<div
class="panel-heading stub timeline-heading chat-heading"
class="panel-heading stub timeline-heading shout-heading"
@click.stop.prevent="togglePanel"
>
<div class="title">
@ -72,12 +72,12 @@
</div>
</template>
<script src="./chat_panel.js"></script>
<script src="./shout_panel.js"></script>
<style lang="scss">
@import '../../_variables.scss';
.floating-chat {
.floating-shout {
position: fixed;
right: 0px;
bottom: 0px;
@ -85,8 +85,8 @@
max-width: 25em;
}
.chat-panel {
.chat-heading {
.shout-panel {
.shout-heading {
cursor: pointer;
.icon {
@ -102,22 +102,22 @@
}
}
.chat-window {
.shout-window {
overflow-y: auto;
overflow-x: hidden;
max-height: 20em;
}
.chat-window-container {
.shout-window-container {
height: 100%;
}
.chat-message {
.shout-message {
display: flex;
padding: 0.2em 0.5em
}
.chat-avatar {
.shout-avatar {
img {
height: 24px;
width: 24px;
@ -128,7 +128,7 @@
}
}
.chat-input {
.shout-input {
display: flex;
textarea {
flex: 1;
@ -138,7 +138,7 @@
}
}
.chat-panel {
.shout-panel {
.title {
display: flex;
justify-content: space-between;

View file

@ -49,7 +49,6 @@ const SideDrawer = {
currentUser () {
return this.$store.state.users.currentUser
},
chat () { return this.$store.state.chat.channel.state === 'joined' },
unseenNotifications () {
return unseenNotificationsFromStore(this.$store)
},

View file

@ -273,9 +273,7 @@
--icon: var(--popoverIcon, $fallback--icon);
.badge {
position: absolute;
right: 0.7rem;
top: 1em;
margin-left: 10px;
}
}

View file

@ -5,12 +5,10 @@
:bound-to="{ x: 'container' }"
@show="enter"
>
<template slot="trigger">
<template v-slot:trigger>
<slot />
</template>
<div
slot="content"
>
<template v-slot:content>
<Status
v-if="status"
:is-preview="true"
@ -33,7 +31,7 @@
size="2x"
/>
</div>
</div>
</template>
</Popover>
</template>

View file

@ -4,77 +4,78 @@
class="TimelineQuickSettings"
:bound-to="{ x: 'container' }"
>
<div
slot="content"
class="dropdown-menu"
>
<div v-if="loggedIn">
<template v-slot:content>
<div class="dropdown-menu">
<div v-if="loggedIn">
<button
class="button-default dropdown-item"
@click="replyVisibilityAll = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityAll }"
/>{{ $t('settings.reply_visibility_all') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilityFollowing = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityFollowing }"
/>{{ $t('settings.reply_visibility_following_short') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilitySelf = true"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilitySelf }"
/>{{ $t('settings.reply_visibility_self_short') }}
</button>
<div
role="separator"
class="dropdown-divider"
/>
</div>
<button
class="button-default dropdown-item"
@click="replyVisibilityAll = true"
@click="hideMedia = !hideMedia"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityAll }"
/>{{ $t('settings.reply_visibility_all') }}
:class="{ 'menu-checkbox-checked': hideMedia }"
/>{{ $t('settings.hide_media_previews') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilityFollowing = true"
@click="hideMutedPosts = !hideMutedPosts"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilityFollowing }"
/>{{ $t('settings.reply_visibility_following_short') }}
:class="{ 'menu-checkbox-checked': hideMutedPosts }"
/>{{ $t('settings.hide_all_muted_posts') }}
</button>
<button
class="button-default dropdown-item"
@click="replyVisibilitySelf = true"
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('filtering')"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-radio': replyVisibilitySelf }"
/>{{ $t('settings.reply_visibility_self_short') }}
<FAIcon icon="font" />{{ $t('settings.word_filter') }}
</button>
<button
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('general')"
>
<FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
</button>
<div
role="separator"
class="dropdown-divider"
/>
</div>
<button
class="button-default dropdown-item"
@click="hideMedia = !hideMedia"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hideMedia }"
/>{{ $t('settings.hide_media_previews') }}
</template>
<template v-slot:trigger>
<button class="button-unstyled">
<FAIcon icon="filter" />
</button>
<button
class="button-default dropdown-item"
@click="hideMutedPosts = !hideMutedPosts"
>
<span
class="menu-checkbox"
:class="{ 'menu-checkbox-checked': hideMutedPosts }"
/>{{ $t('settings.hide_all_muted_posts') }}
</button>
<button
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('filtering')"
>
<FAIcon icon="font" />{{ $t('settings.word_filter') }}
</button>
<button
class="button-default dropdown-item dropdown-item-icon"
@click="openTab('general')"
>
<FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
</button>
</div>
<div slot="trigger">
<FAIcon icon="filter" />
</div>
</template>
</Popover>
</template>

View file

@ -9,28 +9,26 @@
@show="openMenu"
@close="() => isOpen = false"
>
<div
slot="content"
class="timeline-menu-popover panel panel-default"
>
<TimelineMenuContent />
</div>
<div
slot="trigger"
class="title timeline-menu-title"
>
<span class="timeline-title">{{ timelineName() }}</span>
<span>
<FAIcon
size="sm"
icon="chevron-down"
<template v-slot:content>
<div class="timeline-menu-popover popover-default">
<TimelineMenuContent />
</div>
</template>
<template v-slot:trigger>
<button class="button-unstyled title timeline-menu-title">
<span class="timeline-title">{{ timelineName() }}</span>
<span>
<FAIcon
size="sm"
icon="chevron-down"
/>
</span>
<span
class="click-blocker"
@click="blockOpen"
/>
</span>
<span
class="click-blocker"
@click="blockOpen"
/>
</div>
</button>
</template>
</Popover>
</template>

View file

@ -4,13 +4,13 @@ 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 AccountActions from '../account_actions/account_actions.vue'
import Select from '../select/select.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faBell,
faRss,
faChevronDown,
faSearchPlus,
faExternalLinkAlt
} from '@fortawesome/free-solid-svg-icons'
@ -18,7 +18,6 @@ import {
library.add(
faRss,
faBell,
faChevronDown,
faSearchPlus,
faExternalLinkAlt
)
@ -118,7 +117,8 @@ export default {
ModerationTools,
AccountActions,
ProgressButton,
FollowButton
FollowButton,
Select
},
methods: {
refetchRelationship () {

View file

@ -53,17 +53,17 @@
>
{{ user.name }}
</div>
<a
<button
v-if="isOtherUser && !user.is_local"
:href="user.statusnet_profile_url"
target="_blank"
class="external-link-button"
class="button-unstyled external-link-button"
>
<FAIcon
class="icon"
icon="external-link-alt"
/>
</a>
</button>
<AccountActions
v-if="isOtherUser && loggedIn"
:user="user"
@ -132,25 +132,24 @@
class="userHighlightCl"
type="color"
>
<label
for="theme_tab"
class="userHighlightSel select"
<Select
:id="'userHighlightSel'+user.id"
v-model="userHighlightType"
class="userHighlightSel"
>
<select
:id="'userHighlightSel'+user.id"
v-model="userHighlightType"
class="userHighlightSel"
>
<option value="disabled">{{ $t('user_card.highlight.disabled') }}</option>
<option value="solid">{{ $t('user_card.highlight.solid') }}</option>
<option value="striped">{{ $t('user_card.highlight.striped') }}</option>
<option value="side">{{ $t('user_card.highlight.side') }}</option>
</select>
<FAIcon
class="select-down-icon"
icon="chevron-down"
/>
</label>
<option value="disabled">
{{ $t('user_card.highlight.disabled') }}
</option>
<option value="solid">
{{ $t('user_card.highlight.solid') }}
</option>
<option value="striped">
{{ $t('user_card.highlight.striped') }}
</option>
<option value="side">
{{ $t('user_card.highlight.side') }}
</option>
</Select>
</div>
</div>
<div
@ -557,15 +556,11 @@
flex: 1 0 auto;
}
.userHighlightSel,
.userHighlightSel.select {
.userHighlightSel {
padding-top: 0;
padding-bottom: 0;
flex: 1 0 auto;
}
.userHighlightSel.select svg {
line-height: 22px;
}
.userHighlightText {
width: 70px;
@ -574,9 +569,7 @@
.userHighlightCl,
.userHighlightText,
.userHighlightSel,
.userHighlightSel.select {
height: 22px;
.userHighlightSel {
vertical-align: top;
margin-right: .5em;
margin-bottom: .25em;

View file

@ -4,40 +4,39 @@
placement="top"
:offset="{ y: 5 }"
>
<template slot="trigger">
<template v-slot:trigger>
<slot />
</template>
<div
slot="content"
class="user-list-popover"
>
<div v-if="users.length">
<div
v-for="(user) in usersCapped"
:key="user.id"
class="user-list-row"
>
<UserAvatar
:user="user"
class="avatar-small"
:compact="true"
/>
<div class="user-list-names">
<!-- eslint-disable vue/no-v-html -->
<span v-html="user.name_html" />
<!-- eslint-enable vue/no-v-html -->
<span class="user-list-screen-name">{{ user.screen_name_ui }}</span>
<template v-slot:content>
<div class="user-list-popover">
<template v-if="users.length">
<div
v-for="(user) in usersCapped"
:key="user.id"
class="user-list-row"
>
<UserAvatar
:user="user"
class="avatar-small"
:compact="true"
/>
<div class="user-list-names">
<!-- eslint-disable vue/no-v-html -->
<span v-html="user.name_html" />
<!-- eslint-enable vue/no-v-html -->
<span class="user-list-screen-name">{{ user.screen_name_ui }}</span>
</div>
</div>
</div>
</template>
<template v-else>
<FAIcon
icon="circle-notch"
spin
size="3x"
/>
</template>
</div>
<div v-else>
<FAIcon
icon="circle-notch"
spin
size="3x"
/>
</div>
</div>
</template>
</Popover>
</template>

View file

@ -60,10 +60,7 @@
:disabled="!user.friends_count"
>
<FriendList :user-id="userId">
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<FollowCard :user="item" />
</template>
</FriendList>
@ -75,10 +72,7 @@
:disabled="!user.followers_count"
>
<FollowerList :user-id="userId">
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<FollowCard
:user="item"
:no-follows-you="isUs"

View file

@ -45,10 +45,7 @@
</div>
<div class="user-reporting-panel-right">
<List :items="statuses">
<template
slot="item"
slot-scope="{item}"
>
<template v-slot:item="{item}">
<div class="status-fadein user-reporting-panel-sitem">
<Status
:in-conversation="false"