diff options
Diffstat (limited to 'src')
30 files changed, 266 insertions, 230 deletions
diff --git a/src/boot/after_store.js b/src/boot/after_store.js index d2e7f488..9c1f007b 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -253,6 +253,7 @@ const getNodeInfo = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') }) store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') }) store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') }) + store.dispatch('setInstanceOption', { name: 'pleromaCustomEmojiReactionsAvailable', value: features.includes('pleroma_custom_emoji_reactions') }) store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') }) store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') }) store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') }) diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index 8a8d098d..68654f69 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -134,6 +134,9 @@ const EmojiInput = { padEmoji () { return this.$store.getters.mergedConfig.padEmoji }, + defaultCandidateIndex () { + return this.$store.getters.mergedConfig.autocompleteSelect ? 0 : -1 + }, preText () { return this.modelValue.slice(0, this.caret) }, @@ -287,7 +290,7 @@ const EmojiInput = { ...rest, img: imageUrl || '' })) - this.highlighted = -1 + this.highlighted = this.defaultCandidateIndex this.$refs.screenReaderNotice.announce( this.$tc('tool_tip.autocomplete_available', this.suggestions.length, diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index adaa879e..e746dcd7 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -94,8 +94,9 @@ export const suggestUsers = ({ dispatch, state }) => { const newSuggestions = state.users.users.filter( user => - user.screen_name.toLowerCase().startsWith(noPrefix) || - user.name.toLowerCase().startsWith(noPrefix) + user.screen_name && user.name && ( + user.screen_name.toLowerCase().startsWith(noPrefix) || + user.name.toLowerCase().startsWith(noPrefix)) ).slice(0, 20).sort((a, b) => { let aScore = 0 let bScore = 0 diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index 0d7ca812..349b043d 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -98,6 +98,11 @@ const EmojiPicker = { required: false, type: Boolean, default: false + }, + hideCustomEmoji: { + required: false, + type: Boolean, + default: false } }, data () { @@ -280,6 +285,9 @@ const EmojiPicker = { return 0 }, allCustomGroups () { + if (this.hideCustomEmoji) { + return {} + } const emojis = this.$store.getters.groupedCustomEmojis if (emojis.unpacked) { emojis.unpacked.text = this.$t('emoji.unpacked') diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index a63daa97..eb46018e 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -2,7 +2,7 @@ <div class="EmojiReactions"> <UserListPopover v-for="(reaction) in emojiReactions" - :key="reaction.name" + :key="reaction.url || reaction.name" :users="accountsForEmoji[reaction.name]" > <button @@ -11,7 +11,21 @@ @click="emojiOnClick(reaction.name, $event)" @mouseenter="fetchEmojiReactionsByIfMissing()" > - <span class="reaction-emoji">{{ reaction.name }}</span> + <span + class="reaction-emoji" + > + <img + v-if="reaction.url" + :src="reaction.url" + :title="reaction.name" + class="reaction-emoji-content" + width="1em" + > + <span + v-else + class="reaction-emoji reaction-emoji-content" + >{{ reaction.name }}</span> + </span> <span>{{ reaction.count }}</span> </button> </UserListPopover> @@ -35,6 +49,8 @@ margin-top: 0.25em; flex-wrap: wrap; + --emoji-size: calc(1.25em * var(--emojiReactionsScale, 1)); + .emoji-reaction { padding: 0 0.5em; margin-right: 0.5em; @@ -45,8 +61,24 @@ box-sizing: border-box; .reaction-emoji { - width: 1.25em; + width: var(--emoji-size); + height: var(--emoji-size); margin-right: 0.25em; + line-height: var(--emoji-size); + display: flex; + justify-content: center; + align-items: center; + } + + .reaction-emoji-content { + max-width: 100%; + max-height: 100%; + width: auto; + height: auto; + line-height: inherit; + overflow: hidden; + font-size: calc(var(--emoji-size) * 0.8); + margin: 0; } &:focus { diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index bb7e64bc..e2ba74d1 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -4,6 +4,7 @@ :class="{ custom: isCustom }" > <label + :id="name + '-label'" :for="preset === 'custom' ? name : name + '-font-switcher'" class="label" > @@ -12,7 +13,8 @@ <input v-if="typeof fallback !== 'undefined'" :id="name + '-o'" - class="opt exlcude-disabled" + :aria-labelledby="name + '-label'" + class="opt exlcude-disabled visible-for-screenreader-only" type="checkbox" :checked="present" @change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)" @@ -21,6 +23,7 @@ v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'" + :aria-hidden="true" /> {{ ' ' }} <Select diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index 7f096316..face430e 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -80,3 +80,21 @@ export const ROOT_ITEMS = { criteria: ['announcements'] } } + +export function routeTo (item, currentUser) { + if (!item.route && !item.routeObject) return null + + let route + + if (item.routeObject) { + route = item.routeObject + } else { + route = { name: (item.anon || currentUser) ? item.route : item.anonRoute } + } + + if (USERNAME_ROUTES.has(route.name)) { + route.params = { username: currentUser.screen_name, name: currentUser.screen_name } + } + + return route +} diff --git a/src/components/navigation/navigation_entry.js b/src/components/navigation/navigation_entry.js index 81cc936a..22ed77d9 100644 --- a/src/components/navigation/navigation_entry.js +++ b/src/components/navigation/navigation_entry.js @@ -1,5 +1,5 @@ import { mapState } from 'vuex' -import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js' +import { routeTo } from 'src/components/navigation/navigation.js' import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faThumbtack } from '@fortawesome/free-solid-svg-icons' @@ -26,17 +26,7 @@ const NavigationEntry = { }, computed: { routeTo () { - if (!this.item.route && !this.item.routeObject) return null - let route - if (this.item.routeObject) { - route = this.item.routeObject - } else { - route = { name: (this.item.anon || this.currentUser) ? this.item.route : this.item.anonRoute } - } - if (USERNAME_ROUTES.has(route.name)) { - route.params = { username: this.currentUser.screen_name, name: this.currentUser.screen_name } - } - return route + return routeTo(this.item, this.currentUser) }, getters () { return this.$store.getters diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index 9dd795aa..ef78e44c 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -1,5 +1,5 @@ import { mapState } from 'vuex' -import { TIMELINES, ROOT_ITEMS, USERNAME_ROUTES } from 'src/components/navigation/navigation.js' +import { TIMELINES, ROOT_ITEMS, routeTo } from 'src/components/navigation/navigation.js' import { getListEntries, filterNavigation } from 'src/components/navigation/filter.js' import { library } from '@fortawesome/fontawesome-svg-core' @@ -31,14 +31,7 @@ const NavPanel = { props: ['limit'], methods: { getRouteTo (item) { - if (item.routeObject) { - return item.routeObject - } - const route = { name: (item.anon || this.currentUser) ? item.route : item.anonRoute } - if (USERNAME_ROUTES.has(route.name)) { - route.params = { username: this.currentUser.screen_name } - } - return route + return routeTo(item, this.currentUser) } }, computed: { diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index e1ea42ad..4d801c5e 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -121,7 +121,16 @@ scope="global" keypath="notifications.reacted_with" > - <span class="emoji-reaction-emoji">{{ notification.emoji }}</span> + <img + v-if="notification.emoji_url" + class="emoji-reaction-emoji emoji-reaction-emoji-image" + :src="notification.emoji_url" + :name="notification.emoji" + > + <span + v-else + class="emoji-reaction-emoji" + >{{ notification.emoji }}</span> </i18n-t> </small> </span> @@ -153,9 +162,9 @@ </router-link> <button class="button-unstyled expand-icon" - @click.prevent="toggleStatusExpanded" - :title="$t('tool_tip.toggle_expand')" :aria-expanded="statusExpanded" + :title="$t('tool_tip.toggle_expand')" + @click.prevent="toggleStatusExpanded" > <FAIcon class="fa-scale-110" diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 41cfcef0..61f7317e 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -129,6 +129,13 @@ .emoji-reaction-emoji { font-size: 1.3em; + max-width: 1.25em; + height: 1.25em; + width: auto; + } + + .emoji-reaction-emoji-image { + vertical-align: middle; } .notification-details { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 64a8887c..86c1f907 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -281,12 +281,10 @@ > {{ $t('post_status.post') }} </button> - <!-- touchstart is used to keep the OSK at the same position after a message send --> <button v-else :disabled="uploadingFiles || disableSubmit" class="btn button-default" - @touchstart.stop.prevent="postStatus($event, newStatus)" @click.stop.prevent="postStatus($event, newStatus)" > {{ $t('post_status.post') }} diff --git a/src/components/range_input/range_input.vue b/src/components/range_input/range_input.vue index 1e7e42d5..1e720105 100644 --- a/src/components/range_input/range_input.vue +++ b/src/components/range_input/range_input.vue @@ -4,6 +4,7 @@ :class="{ disabled: !present || disabled }" > <label + :id="name + '-label'" :for="name" class="label" > @@ -12,7 +13,8 @@ <input v-if="typeof fallback !== 'undefined'" :id="name + '-o'" - class="opt" + :aria-labelledby="name + '-label'" + class="opt visible-for-screenreader-only" type="checkbox" :checked="present" @change="$emit('update:modelValue', !present ? fallback : undefined)" @@ -21,6 +23,7 @@ v-if="typeof fallback !== 'undefined'" class="opt-l" :for="name + '-o'" + :aria-hidden="true" /> <input :id="name" @@ -34,9 +37,10 @@ @input="$emit('update:modelValue', $event.target.value)" > <input - :id="name" + :id="name + '-numeric'" class="input-number" type="number" + :aria-labelledby="name + '-label'" :value="modelValue || fallback" :disabled="!present || disabled" :max="hardMax" diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js index 47a48623..8eed4b60 100644 --- a/src/components/react_button/react_button.js +++ b/src/components/react_button/react_button.js @@ -1,9 +1,8 @@ import Popover from '../popover/popover.vue' -import { ensureFinalFallback } from '../../i18n/languages.js' +import EmojiPicker from '../emoji_picker/emoji_picker.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons' import { faSmileBeam } from '@fortawesome/free-regular-svg-icons' -import { trim } from 'lodash' library.add( faPlus, @@ -20,105 +19,34 @@ const ReactButton = { } }, components: { - Popover + Popover, + EmojiPicker }, methods: { - addReaction (event, emoji, close) { + addReaction (event) { + const emoji = event.insertion const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji) if (existingReaction && existingReaction.me) { this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) } else { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) } - close() + }, + show () { + if (!this.expanded) { + this.$refs.picker.showPicker() + } }, onShow () { this.expanded = true - this.focusInput() }, onClose () { this.expanded = false - }, - focusInput () { - this.$nextTick(() => { - const input = document.querySelector('.reaction-picker-filter > input') - if (input) input.focus() - }) - }, - // Vaguely adjusted copypaste from emoji_input and emoji_picker! - maybeLocalizedEmojiNamesAndKeywords (emoji) { - const names = [emoji.displayText] - const keywords = [] - - if (emoji.displayTextI18n) { - names.push(this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args)) - } - - if (emoji.annotations) { - this.languages.forEach(lang => { - names.push(emoji.annotations[lang]?.name) - - keywords.push(...(emoji.annotations[lang]?.keywords || [])) - }) - } - - return { - names: names.filter(k => k), - keywords: keywords.filter(k => k) - } - }, - maybeLocalizedEmojiName (emoji) { - if (!emoji.annotations) { - return emoji.displayText - } - - if (emoji.displayTextI18n) { - return this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args) - } - - for (const lang of this.languages) { - if (emoji.annotations[lang]?.name) { - return emoji.annotations[lang].name - } - } - - return emoji.displayText } }, computed: { - commonEmojis () { - const hardcodedSet = new Set(['👍', '😠', '👀', '😂', '🔥']) - return this.$store.getters.standardEmojiList.filter(emoji => hardcodedSet.has(emoji.replacement)) - }, - languages () { - return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage) - }, - emojis () { - if (this.filterWord !== '') { - const keywordLowercase = trim(this.filterWord.toLowerCase()) - - const orderedEmojiList = [] - for (const emoji of this.$store.getters.standardEmojiList) { - const indices = this.maybeLocalizedEmojiNamesAndKeywords(emoji) - .keywords - .map(k => k.toLowerCase().indexOf(keywordLowercase)) - .filter(k => k > -1) - - const indexOfKeyword = indices.length ? Math.min(...indices) : -1 - - if (indexOfKeyword > -1) { - if (!Array.isArray(orderedEmojiList[indexOfKeyword])) { - orderedEmojiList[indexOfKeyword] = [] - } - orderedEmojiList[indexOfKeyword].push(emoji) - } - } - return orderedEmojiList.flat() - } - return this.$store.getters.standardEmojiList || [] - }, - mergedConfig () { - return this.$store.getters.mergedConfig + hideCustomEmoji () { + return !this.$store.state.instance.pleromaChatMessagesAvailable } } } diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index a813b6fd..947536a1 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -1,73 +1,39 @@ <template> - <Popover - trigger="click" - class="ReactButton" - placement="top" - :offset="{ y: 5 }" - :bound-to="{ x: 'container' }" - remove-padding - popover-class="ReactButton popover-default" - @show="onShow" - @close="onClose" - > - <template #content="{close}"> - <div class="reaction-picker-filter"> - <input - v-model="filterWord" - size="1" - :placeholder="$t('emoji.search_emoji')" - @input="$event.target.composing = false" - > - </div> - <div class="reaction-picker"> - <span - v-for="emoji in commonEmojis" - :key="emoji.replacement" - class="emoji-button" - :title="maybeLocalizedEmojiName(emoji)" - @click="addReaction($event, emoji.replacement, close)" - > - {{ emoji.replacement }} - </span> - <div class="reaction-picker-divider" /> - <span - v-for="(emoji, key) in emojis" - :key="key" - class="emoji-button" - :title="maybeLocalizedEmojiName(emoji)" - @click="addReaction($event, emoji.replacement, close)" - > - {{ emoji.replacement }} - </span> - <div class="reaction-bottom-fader" /> - </div> - </template> - <template #trigger> - <span - class="button-unstyled popover-trigger" - :title="$t('tool_tip.add_reaction')" - > - <FALayers> - <FAIcon - class="fa-scale-110 fa-old-padding" - :icon="['far', 'smile-beam']" - /> - <FAIcon - v-show="!expanded" - class="focus-marker" - transform="shrink-6 up-9 right-17" - icon="plus" - /> - <FAIcon - v-show="expanded" - class="focus-marker" - transform="shrink-6 up-9 right-17" - icon="times" - /> - </FALayers> - </span> - </template> - </Popover> + <span class="ReactButton"> + <EmojiPicker + ref="picker" + :enable-sticker-picker="enableStickerPicker" + :hide-custom-emoji="hideCustomEmoji" + class="emoji-picker-panel" + @emoji="addReaction" + @show="onShow" + @close="onClose" + /> + <span + class="button-unstyled popover-trigger" + :title="$t('tool_tip.add_reaction')" + @click.stop.prevent="show" + > + <FALayers> + <FAIcon + class="fa-scale-110 fa-old-padding" + :icon="['far', 'smile-beam']" + /> + <FAIcon + v-show="!expanded" + class="focus-marker" + transform="shrink-6 up-9 right-17" + icon="plus" + /> + <FAIcon + v-show="expanded" + class="focus-marker" + transform="shrink-6 up-9 right-17" + icon="times" + /> + </FALayers> + </span> + </span> </template> <script src="./react_button.js"></script> @@ -135,11 +101,6 @@ color: $fallback--text; color: var(--text, $fallback--text); } - } - - .popover-trigger-button { - /* override of popover internal stuff */ - width: auto; @include unfocused-style { .focus-marker { diff --git a/src/components/settings_modal/helpers/float_setting.vue b/src/components/settings_modal/helpers/float_setting.vue new file mode 100644 index 00000000..15edb3c3 --- /dev/null +++ b/src/components/settings_modal/helpers/float_setting.vue @@ -0,0 +1,16 @@ +<template> + <NumberSetting + v-bind="$attrs" + > + <slot /> + </NumberSetting> +</template> + +<script> +import NumberSetting from './number_setting.vue' +export default { + components: { + NumberSetting + } +} +</script> diff --git a/src/components/settings_modal/helpers/integer_setting.vue b/src/components/settings_modal/helpers/integer_setting.vue index 695e2673..43fa7e1a 100644 --- a/src/components/settings_modal/helpers/integer_setting.vue +++ b/src/components/settings_modal/helpers/integer_setting.vue @@ -1,27 +1,17 @@ <template> - <span - v-if="matchesExpertLevel" - class="IntegerSetting" + <NumberSetting + v-bind="$attrs" + truncate="1" > - <label :for="path"> - <slot /> - </label> - <input - :id="path" - class="number-input" - type="number" - step="1" - :disabled="disabled" - :min="min || 0" - :value="state" - @change="update" - > - {{ ' ' }} - <ModifiedIndicator - :changed="isChanged" - :onclick="reset" - /> - </span> + <slot /> + </NumberSetting> </template> -<script src="./integer_setting.js"></script> +<script> +import NumberSetting from './number_setting.vue' +export default { + components: { + NumberSetting + } +} +</script> diff --git a/src/components/settings_modal/helpers/integer_setting.js b/src/components/settings_modal/helpers/number_setting.js index e64d0cee..73c39948 100644 --- a/src/components/settings_modal/helpers/integer_setting.js +++ b/src/components/settings_modal/helpers/number_setting.js @@ -8,6 +8,8 @@ export default { path: String, disabled: Boolean, min: Number, + step: Number, + truncate: Number, expert: [Number, String] }, computed: { @@ -15,8 +17,11 @@ export default { const [firstSegment, ...rest] = this.path.split('.') return [firstSegment + 'DefaultValue', ...rest].join('.') }, + parent () { + return this.$parent.$parent + }, state () { - const value = get(this.$parent, this.path) + const value = get(this.parent, this.path) if (value === undefined) { return this.defaultState } else { @@ -24,21 +29,28 @@ export default { } }, defaultState () { - return get(this.$parent, this.pathDefault) + return get(this.parent, this.pathDefault) }, isChanged () { return this.state !== this.defaultState }, matchesExpertLevel () { - return (this.expert || 0) <= this.$parent.expertLevel + return (this.expert || 0) <= this.parent.expertLevel } }, methods: { + truncateValue (value) { + if (!this.truncate) { + return value + } + + return Math.trunc(value / this.truncate) * this.truncate + }, update (e) { - set(this.$parent, this.path, parseInt(e.target.value)) + set(this.parent, this.path, this.truncateValue(parseFloat(e.target.value))) }, reset () { - set(this.$parent, this.path, this.defaultState) + set(this.parent, this.path, this.defaultState) } } } diff --git a/src/components/settings_modal/helpers/number_setting.vue b/src/components/settings_modal/helpers/number_setting.vue new file mode 100644 index 00000000..3eab5178 --- /dev/null +++ b/src/components/settings_modal/helpers/number_setting.vue @@ -0,0 +1,27 @@ +<template> + <span + v-if="matchesExpertLevel" + class="NumberSetting" + > + <label :for="path"> + <slot /> + </label> + <input + :id="path" + class="number-input" + type="number" + :step="step || 1" + :disabled="disabled" + :min="min || 0" + :value="state" + @change="update" + > + {{ ' ' }} + <ModifiedIndicator + :changed="isChanged" + :onclick="reset" + /> + </span> +</template> + +<script src="./number_setting.js"></script> diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index ea24d6ad..be97710f 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -2,6 +2,7 @@ import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' import ScopeSelector from 'src/components/scope_selector/scope_selector.vue' import IntegerSetting from '../helpers/integer_setting.vue' +import FloatSetting from '../helpers/float_setting.vue' import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' @@ -62,6 +63,7 @@ const GeneralTab = { BooleanSetting, ChoiceSetting, IntegerSetting, + FloatSetting, SizeSetting, InterfaceLanguageSwitcher, ScopeSelector, diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 703e94a0..21e2d855 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -271,6 +271,15 @@ {{ $t('settings.no_rich_text_description') }} </BooleanSetting> </li> + <li> + <FloatSetting + v-if="user" + path="emojiReactionsScale" + expert="1" + > + {{ $t('settings.emoji_reactions_scale') }} + </FloatSetting> + </li> <h3>{{ $t('settings.attachments') }}</h3> <li> <BooleanSetting @@ -501,6 +510,14 @@ {{ $t('settings.pad_emoji') }} </BooleanSetting> </li> + <li> + <BooleanSetting + path="autocompleteSelect" + expert="1" + > + {{ $t('settings.autocomplete_select_first') }} + </BooleanSetting> + </li> </ul> </div> </div> diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 7546535d..1f3c26aa 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -129,12 +129,13 @@ v-model="selected.inset" :disabled="!present" name="inset" - class="input-inset" + class="input-inset visible-for-screenreader-only" type="checkbox" > <label class="checkbox-label" for="inset" + :aria-hidden="true" /> </div> <div diff --git a/src/components/tab_switcher/tab_switcher.jsx b/src/components/tab_switcher/tab_switcher.jsx index c8d390bc..a7ef8560 100644 --- a/src/components/tab_switcher/tab_switcher.jsx +++ b/src/components/tab_switcher/tab_switcher.jsx @@ -117,6 +117,7 @@ export default { onClick={this.clickTab(index)} class={classesTab.join(' ')} type="button" + role="tab" > <img src={props.image} title={props['image-tooltip']}/> {props.label ? '' : props.label} @@ -131,6 +132,7 @@ export default { onClick={this.clickTab(index)} class={classesTab.join(' ')} type="button" + role="tab" > {!props.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={props.icon}/>)} <span class="text"> @@ -167,11 +169,15 @@ export default { return ( <div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}> - <div class="tabs"> + <div + class="tabs" + role="tablist" + > {tabs} </div> <div ref="contents" + role="tabpanel" class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')} v-body-scroll-lock={this.bodyScrollLock} > diff --git a/src/i18n/en.json b/src/i18n/en.json index dba8a13f..10ba2ef8 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -465,7 +465,9 @@ "domain_mutes": "Domains", "avatar_size_instruction": "The recommended minimum size for avatar images is 150x150 pixels.", "pad_emoji": "Pad emoji with spaces when adding from picker", + "autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available", "emoji_reactions_on_timeline": "Show emoji reactions on timeline", + "emoji_reactions_scale": "Reactions scale factor", "export_theme": "Save preset", "filtering": "Filtering", "wordfilter": "Wordfilter", diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 90f840a7..8ea4c1e0 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -295,7 +295,7 @@ "change_password_error": "修改密码的时候出了点问题。", "changed_password": "成功修改了密码!", "collapse_subject": "折叠带主题的内容", - "composing": "写作", + "composing": "撰写", "confirm_new_password": "确认新密码", "current_avatar": "当前头像", "current_password": "当前密码", @@ -737,7 +737,8 @@ "mention_link_use_tooltip": "点击提及链接时显示用户卡片", "mention_link_show_avatar": "在链接旁边显示用户头像", "mention_link_show_avatar_quick": "在提及内容旁边显示用户头像", - "user_popover_avatar_action_open": "打开个人资料" + "user_popover_avatar_action_open": "打开个人资料", + "autocomplete_select_first": "当有自动完成的结果时,自动选择第一个候选项" }, "time": { "day": "{0} 天", diff --git a/src/modules/config.js b/src/modules/config.js index eb33f95f..7597886e 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -97,6 +97,7 @@ export const defaultState = { sidebarColumnWidth: '25rem', contentColumnWidth: '45rem', notifsColumnWidth: '25rem', + emojiReactionsScale: 1.0, navbarColumnStretch: false, greentext: undefined, // instance default useAtIcon: undefined, // instance default @@ -115,7 +116,8 @@ export const defaultState = { conversationTreeAdvanced: undefined, // instance default conversationOtherRepliesButton: undefined, // instance default conversationTreeFadeAncestors: undefined, // instance default - maxDepthInThread: undefined // instance default + maxDepthInThread: undefined, // instance default + autocompleteSelect: undefined // instance default } // caching the instance default properties @@ -184,6 +186,7 @@ const config = { case 'sidebarColumnWidth': case 'contentColumnWidth': case 'notifsColumnWidth': + case 'emojiReactionsScale': applyConfig(state) break case 'customTheme': diff --git a/src/modules/instance.js b/src/modules/instance.js index 16f72583..bb0292da 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -104,6 +104,7 @@ const defaultState = { conversationOtherRepliesButton: 'below', conversationTreeFadeAncestors: false, maxDepthInThread: 6, + autocompleteSelect: false, // Nasty stuff customEmoji: [], @@ -122,6 +123,7 @@ const defaultState = { // Feature-set, apparently, not everything here is reported... shoutAvailable: false, pleromaChatMessagesAvailable: false, + pleromaCustomEmojiReactionsAvailable: false, gopherAvailable: false, mediaProxyAvailable: false, suggestionsEnabled: false, diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 94bb0c54..f93e277a 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -840,7 +840,7 @@ const postStatus = ({ }) if (pollOptions.some(option => option !== '')) { const normalizedPoll = { - expires_in: poll.expiresIn, + expires_in: parseInt(poll.expiresIn, 10), multiple: poll.multiple } Object.keys(normalizedPoll).forEach(key => { @@ -897,7 +897,7 @@ const editStatus = ({ if (pollOptions.some(option => option !== '')) { const normalizedPoll = { - expires_in: poll.expiresIn, + expires_in: parseInt(poll.expiresIn, 10), multiple: poll.multiple } Object.keys(normalizedPoll).forEach(key => { diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 53c3108c..adefc5a5 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -441,6 +441,7 @@ export const parseNotification = (data) => { : parseUser(data.target) output.from_profile = parseUser(data.account) output.emoji = data.emoji + output.emoji_url = data.emoji_url if (data.report) { output.report = data.report output.report.content = data.report.content diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index d6e973a1..43fe3c73 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -21,8 +21,8 @@ export const applyTheme = (input) => { body.classList.remove('hidden') } -const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) => - ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) +const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) => + ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) const defaultConfigColumns = configColumns(defaultState) |
