Merge branch 'rc/2.2.2' into 'master'

prepare master for 2.2.2

See merge request pleroma/pleroma-fe!1315
This commit is contained in:
Shpuld Shpludson 2020-12-22 16:00:12 +00:00
commit a39866308c
122 changed files with 7580 additions and 1777 deletions

View file

@ -4,6 +4,35 @@ 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/).
## [2.2.2] - 2020-12-22
### Added
- Mouseover titles for emojis in reaction picker
- Support to input emoji into the search box in reaction picker
- Added some missing unicode emoji
- Added the upload limit to the Features panel in the About page
- Support for solid color wallpaper, instance doesn't have to define a wallpaper anymore
### Fixed
- Fixed the occasional bug where screen would scroll 1px when typing into a reply form
- Fixed timeline errors locking timelines
- Fixed missing highlighted border in expanded conversations
- Fixed custom emoji not working in profile field names
- Fixed pinned statuses not appearing in user profiles
- Fixed some elements not being keyboard navigation friendly
- Fixed error handling when updating various profile images
- Fixed your latest chat messages disappearing when closing chat view and opening it again during the same session
- Fixed custom emoji not showing in poll options before voting
- Fixed link color not applied to instance name in topbar
### Changed
- Errors when fetching are now shown with popup errors instead of "Error fetching updates" in panel headers
- Made reply/fav/repeat etc buttons easier to hit
- Adjusted timeline menu clickable area to match the visible button
- Moved external source link from status heading to the ellipsis menu
- Disabled horizontal textarea resize
- Wallpaper is now top-aligned, horizontally centered.
## [2.2.1] - 2020-11-11
### Fixed
- Fixed regression in react popup alignment and overflowing
@ -16,6 +45,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Import/export a muted users
- Proper handling of deletes when using websocket streaming
- Added optimistic chat message sending, so you can start writing next message before the previous one has been sent
- Added a small red badge to the favicon when there's unread notifications
- Added the NSFW alert to link previews
### Fixed
- Fixed clicking NSFW hider through status popover

View file

@ -3,7 +3,6 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<title>Pleroma</title>
<!--server-generated-meta-->
<link rel="icon" type="image/png" href="/favicon.png">
</head>

View file

@ -15,6 +15,7 @@ import UserReportingModal from './components/user_reporting_modal/user_reporting
import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
import GlobalNoticeList from './components/global_notice_list/global_notice_list.vue'
import { windowWidth, windowHeight } from './services/window_utils/window_utils'
import { mapGetters } from 'vuex'
export default {
name: 'app',
@ -50,18 +51,19 @@ export default {
},
computed: {
currentUser () { return this.$store.state.users.currentUser },
background () {
return this.currentUser.background_image || this.$store.state.instance.background
userBackground () { return this.currentUser.background_image },
instanceBackground () {
return this.mergedConfig.hideInstanceWallpaper
? null
: this.$store.state.instance.background
},
background () { return this.userBackground || this.instanceBackground },
bgStyle () {
return {
'background-image': `url(${this.background})`
}
},
bgAppStyle () {
if (this.background) {
return {
'--body-background-image': `url(${this.background})`
}
}
},
chat () { return this.$store.state.chat.channel.state === 'joined' },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
@ -77,7 +79,8 @@ export default {
return {
'order': this.$store.state.instance.sidebarRight ? 99 : 0
}
}
},
...mapGetters(['mergedConfig'])
},
methods: {
updateMobileState () {

View file

@ -14,7 +14,9 @@
right: -20px;
background-size: cover;
background-repeat: no-repeat;
background-position: 0 50%;
background-color: var(--wallpaper);
background-image: var(--body-background-image);
background-position: 50% 50px;
}
i[class^='icon-'] {
@ -33,6 +35,7 @@ h4 {
max-width: 980px;
align-content: flex-start;
}
.underlay {
background-color: rgba(0,0,0,0.15);
background-color: var(--underlay, rgba(0,0,0,0.15));
@ -69,7 +72,7 @@ a {
color: var(--link, $fallback--link);
}
button {
.button-default {
user-select: none;
color: $fallback--text;
color: var(--btnText, $fallback--text);
@ -85,7 +88,8 @@ button {
font-family: sans-serif;
font-family: var(--interfaceFont, sans-serif);
i[class*=icon-], .svg-inline--fa {
i[class*=icon-],
.svg-inline--fa {
color: $fallback--text;
color: var(--btnText, $fallback--text);
}
@ -107,7 +111,8 @@ button {
background-color: $fallback--fg;
background-color: var(--btnPressed, $fallback--fg);
svg, i {
svg,
i {
color: $fallback--text;
color: var(--btnPressedText, $fallback--text);
}
@ -120,7 +125,8 @@ button {
background-color: $fallback--fg;
background-color: var(--btnDisabled, $fallback--fg);
svg, i {
svg,
i {
color: $fallback--text;
color: var(--btnDisabledText, $fallback--text);
}
@ -134,7 +140,8 @@ button {
box-shadow: 0px 0px 4px 0px rgba(255, 255, 255, 0.3), 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset;
box-shadow: var(--buttonPressedShadow);
svg, i {
svg,
i {
color: $fallback--text;
color: var(--btnToggledText, $fallback--text);
}
@ -149,6 +156,30 @@ button {
}
}
.button-unstyled {
background: none;
border: none;
outline: none;
display: inline;
text-align: initial;
font-size: 100%;
font-family: inherit;
padding: 0;
line-height: unset;
cursor: pointer;
box-sizing: content-box;
color: inherit;
&.-link {
color: $fallback--link;
color: var(--link, $fallback--link);
}
&.-fullwidth {
width: 100%;
}
}
input, textarea, .select, .input {
&.unstyled {
@ -303,6 +334,10 @@ input, textarea, .select, .input {
box-sizing: border-box;
}
}
&.resize-height {
resize: vertical;
}
}
option {
@ -442,6 +477,7 @@ main-router {
color: $fallback--faint;
color: var(--panelFaint, $fallback--faint);
}
.faint-link {
color: $fallback--faint;
color: var(--faintLink, $fallback--faint);
@ -453,11 +489,8 @@ main-router {
overflow-x: hidden;
}
button {
flex-shrink: 0;
}
button, .alert {
.button-default,
.alert {
// height: 100%;
line-height: 21px;
min-height: 0;
@ -468,8 +501,11 @@ main-router {
align-self: stretch;
}
button {
&, i[class*=icon-] {
.button-default {
flex-shrink: 0;
&,
i[class*=icon-] {
color: $fallback--text;
color: var(--btnPanelText, $fallback--text);
}
@ -492,7 +528,8 @@ main-router {
}
}
a {
a,
.-link {
color: $fallback--link;
color: var(--panelLink, $fallback--link)
}
@ -507,15 +544,15 @@ main-router {
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
.faint {
color: $fallback--faint;
color: var(--panelFaint, $fallback--faint);
}
a {
a,
.-link {
color: $fallback--link;
color: var(--panelLink, $fallback--link)
color: var(--panelLink, $fallback--link);
}
}
@ -797,7 +834,7 @@ nav {
}
}
.btn.btn-default {
.btn.button-default {
min-height: 28px;
}

View file

@ -1,12 +1,11 @@
<template>
<div
id="app"
:style="bgAppStyle"
:style="bgStyle"
>
<div
id="app_bg_wrapper"
class="app-bg-wrapper"
:style="bgStyle"
/>
<MobileNav v-if="isMobileLayout" />
<DesktopNav v-else />

View file

@ -7,6 +7,7 @@ import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { applyTheme } from '../services/style_setter/style_setter.js'
import FaviconService from '../services/favicon_service/favicon_service.js'
let staticInitialResults = null
@ -326,6 +327,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
const width = windowWidth()
store.dispatch('setMobileLayout', width <= 800)
FaviconService.initFaviconService()
const overrides = window.___pleromafe_dev_overrides || {}
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
store.dispatch('setInstanceOption', { name: 'server', value: server })

View file

@ -4,6 +4,7 @@
trigger="click"
placement="bottom"
:bound-to="{ x: 'container' }"
remove-padding
>
<div
slot="content"
@ -13,14 +14,14 @@
<template v-if="relationship.following">
<button
v-if="relationship.showing_reblogs"
class="btn btn-default dropdown-item"
class="btn button-default dropdown-item"
@click="hideRepeats"
>
{{ $t('user_card.hide_repeats') }}
</button>
<button
v-if="!relationship.showing_reblogs"
class="btn btn-default dropdown-item"
class="btn button-default dropdown-item"
@click="showRepeats"
>
{{ $t('user_card.show_repeats') }}
@ -32,27 +33,27 @@
</template>
<button
v-if="relationship.blocking"
class="btn btn-default btn-block dropdown-item"
class="btn button-default btn-block dropdown-item"
@click="unblockUser"
>
{{ $t('user_card.unblock') }}
</button>
<button
v-else
class="btn btn-default btn-block dropdown-item"
class="btn button-default btn-block dropdown-item"
@click="blockUser"
>
{{ $t('user_card.block') }}
</button>
<button
class="btn btn-default btn-block dropdown-item"
class="btn button-default btn-block dropdown-item"
@click="reportUser"
>
{{ $t('user_card.report') }}
</button>
<button
v-if="pleromaChatMessagesAvailable"
class="btn btn-default btn-block dropdown-item"
class="btn button-default btn-block dropdown-item"
@click="openChat"
>
{{ $t('user_card.message') }}
@ -61,7 +62,7 @@
</div>
<div
slot="trigger"
class="btn btn-default ellipsis-button"
class="ellipsis-button"
>
<FAIcon
class="icon"

View file

@ -8,7 +8,7 @@
{{ $t('general.error_retry') }}
</p>
<button
class="btn"
class="btn button-default"
@click="retry"
>
{{ $t('general.retry') }}

View file

@ -8,14 +8,18 @@ import {
faFile,
faMusic,
faImage,
faVideo
faVideo,
faPlayCircle,
faTimes
} from '@fortawesome/free-solid-svg-icons'
library.add(
faFile,
faMusic,
faImage,
faVideo
faVideo,
faPlayCircle,
faTimes
)
const Attachment = {

View file

@ -42,15 +42,13 @@
icon="play-circle"
/>
</a>
<div
<button
v-if="nsfw && hideNsfwLocal && !hidden"
class="hider"
>
<a
href="#"
class="button-unstyled hider"
@click.prevent="toggleHidden"
>Hide</a>
</div>
>
<FAIcon icon="times" />
</button>
<a
v-if="type === 'image' && (!hidden || preloadImage)"
@ -234,15 +232,23 @@
.hider {
position: absolute;
right: 0;
white-space: nowrap;
margin: 10px;
padding: 5px;
background: rgba(230,230,230,0.6);
font-weight: bold;
padding: 0;
z-index: 4;
line-height: 1;
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
text-align: center;
width: 2em;
height: 2em;
font-size: 1.25em;
// TODO: theming? hard to theme with unknown background image color
background: rgba(230, 230, 230, 0.7);
.svg-inline--fa {
color: rgba(0, 0, 0, 0.6);
}
&:hover .svg-inline--fa {
color: rgba(0, 0, 0, 0.9);
}
}
video {

View file

@ -3,7 +3,7 @@
<div class="block-card-content-container">
<button
v-if="blocked"
class="btn btn-default"
class="btn button-default"
:disabled="progress"
@click="unblockUser"
>
@ -16,7 +16,7 @@
</button>
<button
v-else
class="btn btn-default"
class="btn button-default"
:disabled="progress"
@click="blockUser"
>

View file

@ -10,7 +10,10 @@
<span class="title">
{{ $t("chats.chats") }}
</span>
<button @click="newChat">
<button
class="button-default"
@click="newChat"
>
{{ $t("chats.new") }}
</button>
</div>

View file

@ -31,9 +31,6 @@
color: $fallback--text;
color: var(--text, $fallback--text);
}
border-radius: $fallback--chatMessageRadius;
border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius);
}
.popover {

View file

@ -53,7 +53,7 @@
<div slot="content">
<div class="dropdown-menu">
<button
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click="deleteMessage"
>
<FAIcon icon="times" /> {{ $t("chats.delete") }}
@ -62,7 +62,7 @@
</div>
<button
slot="trigger"
class="menu-icon"
class="button-default menu-icon"
:title="$t('chats.more')"
>
<FAIcon icon="ellipsis-h" />

View file

@ -10,12 +10,13 @@
class="panel-heading conversation-heading"
>
<span class="title"> {{ $t('timeline.conversation') }} </span>
<span v-if="collapsable">
<a
href="#"
<button
v-if="collapsable"
class="button-unstyled -link"
@click.prevent="toggleExpanded"
>{{ $t('timeline.collapse') }}</a>
</span>
>
{{ $t('timeline.collapse') }}
</button>
</div>
<status
v-for="status in conversation"
@ -57,13 +58,6 @@
}
&.-expanded {
.conversation-status {
border-color: $fallback--border;
border-color: var(--border, $fallback--border);
border-left-color: $fallback--cRed;
border-left-color: var(--cRed, $fallback--cRed);
}
.conversation-status:last-child {
border-bottom: none;
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;

View file

@ -5,6 +5,10 @@
width: 100%;
position: fixed;
a {
color: var(--topBarLink, $fallback--link);
}
.inner-nav {
display: grid;
grid-template-rows: 50px;
@ -21,7 +25,7 @@
grid-template-areas: "logo sitename actions";
}
button {
.button-default {
&, svg {
color: $fallback--text;
color: var(--btnTopBarText, $fallback--text);
@ -80,13 +84,14 @@
.nav-icon {
margin-left: 0.2em;
width: 2em;
height: 100%;
text-align: center;
}
a, a svg {
.svg-inline--fa {
color: $fallback--link;
color: var(--topBarLink, $fallback--link);
}
}
.sitename {
grid-area: sitename;

View file

@ -36,9 +36,8 @@
@toggled="onSearchBarToggled"
@click.stop.native
/>
<a
href="#"
class="nav-icon"
<button
class="button-unstyled nav-icon"
@click.stop="openSettingsModal"
>
<FAIcon
@ -47,29 +46,32 @@
icon="cog"
:title="$t('nav.preferences')"
/>
</a>
</button>
<a
v-if="currentUser && currentUser.role === 'admin'"
href="/pleroma/admin/#/login-pleroma"
class="nav-icon"
target="_blank"
><FAIcon
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="tachometer-alt"
:title="$t('nav.administration')"
/></a>
<a
/>
</a>
<button
v-if="currentUser"
href="#"
class="nav-icon"
class="button-unstyled nav-icon"
@click.prevent="logout"
><FAIcon
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="sign-out-alt"
:title="$t('login.logout')"
/></a>
/>
</button>
</div>
</div>
</nav>

View file

@ -6,7 +6,7 @@
<ProgressButton
v-if="muted"
:click="unmuteDomain"
class="btn btn-default"
class="btn button-default"
>
{{ $t('domain_mute_card.unmute') }}
<template slot="progress">
@ -16,7 +16,7 @@
<ProgressButton
v-else
:click="muteDomain"
class="btn btn-default"
class="btn button-default"
>
{{ $t('domain_mute_card.mute') }}
<template slot="progress">

View file

@ -114,7 +114,8 @@ const EmojiInput = {
showPicker: false,
temporarilyHideSuggestions: false,
keepOpen: false,
disableClickOutside: false
disableClickOutside: false,
suggestions: []
}
},
components: {
@ -124,21 +125,6 @@ const EmojiInput = {
padEmoji () {
return this.$store.getters.mergedConfig.padEmoji
},
suggestions () {
const firstchar = this.textAtCaret.charAt(0)
if (this.textAtCaret === firstchar) { return [] }
const matchedSuggestions = this.suggest(this.textAtCaret)
if (matchedSuggestions.length <= 0) {
return []
}
return take(matchedSuggestions, 5)
.map(({ imageUrl, ...rest }, index) => ({
...rest,
// eslint-disable-next-line camelcase
img: imageUrl || '',
highlighted: index === this.highlighted
}))
},
showSuggestions () {
return this.focused &&
this.suggestions &&
@ -188,6 +174,23 @@ const EmojiInput = {
watch: {
showSuggestions: function (newValue) {
this.$emit('shown', newValue)
},
textAtCaret: async function (newWord) {
const firstchar = newWord.charAt(0)
this.suggestions = []
if (newWord === firstchar) return
const matchedSuggestions = await this.suggest(newWord)
// Async: cancel if textAtCaret has changed during wait
if (this.textAtCaret !== newWord) return
if (matchedSuggestions.length <= 0) return
this.suggestions = take(matchedSuggestions, 5)
.map(({ imageUrl, ...rest }) => ({
...rest,
img: imageUrl || ''
}))
},
suggestions (newValue) {
this.$nextTick(this.resize)
}
},
methods: {

View file

@ -6,13 +6,13 @@
>
<slot />
<template v-if="enableEmojiPicker">
<div
<button
v-if="!hideEmojiButton"
class="emoji-picker-icon"
class="button-unstyled emoji-picker-icon"
@click.prevent="togglePicker"
>
<FAIcon :icon="['far', 'smile-beam']" />
</div>
</button>
<EmojiPicker
v-if="enableEmojiPicker"
ref="picker"
@ -37,7 +37,7 @@
v-for="(suggestion, index) in suggestions"
:key="index"
class="autocomplete-item"
:class="{ highlighted: suggestion.highlighted }"
:class="{ highlighted: index === highlighted }"
@click.stop.prevent="onClick($event, suggestion)"
>
<span class="image">

View file

@ -1,4 +1,3 @@
import { debounce } from 'lodash'
/**
* suggest - generates a suggestor function to be used by emoji-input
* data: object providing source information for specific types of suggestions:
@ -11,20 +10,20 @@ import { debounce } from 'lodash'
* doesn't support user linking you can just provide only emoji.
*/
const debounceUserSearch = debounce((data, input) => {
data.updateUsersList(input)
}, 500)
export default data => input => {
export default data => {
const emojiCurry = suggestEmoji(data.emoji)
const usersCurry = data.store && suggestUsers(data.store)
return input => {
const firstChar = input[0]
if (firstChar === ':' && data.emoji) {
return suggestEmoji(data.emoji)(input)
return emojiCurry(input)
}
if (firstChar === '@' && data.users) {
return suggestUsers(data)(input)
if (firstChar === '@' && usersCurry) {
return usersCurry(input)
}
return []
}
}
export const suggestEmoji = emojis => input => {
const noPrefix = input.toLowerCase().substr(1)
@ -57,19 +56,46 @@ export const suggestEmoji = emojis => input => {
})
}
export const suggestUsers = data => input => {
const noPrefix = input.toLowerCase().substr(1)
const users = data.users
export const suggestUsers = ({ dispatch, state }) => {
// Keep some persistent values in closure, most importantly for the
// custom debounce to work. Lodash debounce does not return a promise.
let suggestions = []
let previousQuery = ''
let timeout = null
let cancelUserSearch = null
const newUsers = users.filter(
const userSearch = (query) => dispatch('searchUsers', { query })
const debounceUserSearch = (query) => {
cancelUserSearch && cancelUserSearch()
return new Promise((resolve, reject) => {
timeout = setTimeout(() => {
userSearch(query).then(resolve).catch(reject)
}, 300)
cancelUserSearch = () => {
clearTimeout(timeout)
resolve([])
}
})
}
return async input => {
const noPrefix = input.toLowerCase().substr(1)
if (previousQuery === noPrefix) return suggestions
suggestions = []
previousQuery = noPrefix
// Fetch more and wait, don't fetch if there's the 2nd @ because
// the backend user search can't deal with it.
// Reference semantics make it so that we get the updated data after
// the await.
if (!noPrefix.includes('@')) {
await debounceUserSearch(noPrefix)
}
const newSuggestions = state.users.users.filter(
user =>
user.screen_name.toLowerCase().startsWith(noPrefix) ||
user.name.toLowerCase().startsWith(noPrefix)
/* taking only 20 results so that sorting is a bit cheaper, we display
* only 5 anyway. could be inaccurate, but we ideally we should query
* backend anyway
*/
).slice(0, 20).sort((a, b) => {
let aScore = 0
let bScore = 0
@ -96,11 +122,9 @@ export const suggestUsers = data => input => {
imageUrl: profile_image_url_original,
replacement: '@' + screen_name + ' '
}))
// BE search users to get more comprehensive results
if (data.updateUsersList) {
debounceUserSearch(data, noPrefix)
}
return newUsers
/* eslint-enable camelcase */
suggestions = newSuggestions || []
return suggestions
}
}

View file

@ -6,7 +6,7 @@
:users="accountsForEmoji[reaction.name]"
>
<button
class="emoji-reaction btn btn-default"
class="emoji-reaction btn button-default"
:class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }"
@click="emojiOnClick(reaction.name, $event)"
@mouseenter="fetchEmojiReactionsByIfMissing()"

View file

@ -2,13 +2,13 @@
<div class="import-export-container">
<slot name="before" />
<button
class="btn"
class="btn button-default"
@click="exportData"
>
{{ exportLabel }}
</button>
<button
class="btn"
class="btn button-default"
@click="importData"
>
{{ importLabel }}

View file

@ -11,7 +11,7 @@
</div>
<button
v-else
class="btn btn-default"
class="btn button-default"
@click="process"
>
{{ exportButtonLabel }}

View file

@ -5,7 +5,8 @@ import {
faBookmark,
faEyeSlash,
faThumbtack,
faShareAlt
faShareAlt,
faExternalLinkAlt
} from '@fortawesome/free-solid-svg-icons'
import {
faBookmark as faBookmarkReg
@ -17,7 +18,8 @@ library.add(
faBookmarkReg,
faEyeSlash,
faThumbtack,
faShareAlt
faShareAlt,
faExternalLinkAlt
)
const ExtraButtons = {

View file

@ -1,9 +1,11 @@
<template>
<Popover
class="ExtraButtons"
trigger="click"
placement="top"
class="extra-button-popover"
:offset="{ y: 5 }"
:bound-to="{ x: 'container' }"
remove-padding
>
<div
slot="content"
@ -12,7 +14,7 @@
<div class="dropdown-menu">
<button
v-if="canMute && !status.thread_muted"
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="muteConversation"
>
<FAIcon
@ -22,7 +24,7 @@
</button>
<button
v-if="canMute && status.thread_muted"
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="unmuteConversation"
>
<FAIcon
@ -32,7 +34,7 @@
</button>
<button
v-if="!status.pinned && canPin"
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="pinStatus"
@click="close"
>
@ -43,7 +45,7 @@
</button>
<button
v-if="status.pinned && canPin"
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="unpinStatus"
@click="close"
>
@ -54,7 +56,7 @@
</button>
<button
v-if="!status.bookmarked"
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="bookmarkStatus"
@click="close"
>
@ -65,7 +67,7 @@
</button>
<button
v-if="status.bookmarked"
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="unbookmarkStatus"
@click="close"
>
@ -76,7 +78,7 @@
</button>
<button
v-if="canDelete"
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="deleteStatus"
@click="close"
>
@ -86,7 +88,7 @@
/><span>{{ $t("status.delete") }}</span>
</button>
<button
class="dropdown-item dropdown-item-icon"
class="button-default dropdown-item dropdown-item-icon"
@click.prevent="copyLink"
@click="close"
>
@ -95,11 +97,26 @@
icon="share-alt"
/><span>{{ $t("status.copy_link") }}</span>
</button>
</div>
</div>
<span slot="trigger">
<a
v-if="!status.is_local"
class="button-default dropdown-item dropdown-item-icon"
title="Source"
:href="status.external_url"
target="_blank"
>
<FAIcon
class="ExtraButtons fa-scale-110 fa-old-padding"
fixed-width
icon="external-link-alt"
/><span>{{ $t("status.external_source") }}</span>
</a>
</div>
</div>
<span
slot="trigger"
class="popover-trigger"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="ellipsis-h"
/>
</span>
@ -112,13 +129,15 @@
@import '../../_variables.scss';
.ExtraButtons {
cursor: pointer;
.popover-trigger {
position: static;
padding: 10px;
margin: -10px;
&:hover,
.extra-button-popover.open & {
&:hover .svg-inline--fa {
color: $fallback--text;
color: var(--text, $fallback--text);
}
}
}
</style>

View file

@ -31,11 +31,6 @@ const FavoriteButton = {
}
},
computed: {
classes () {
return {
'-favorited': this.status.favorited
}
},
...mapGetters(['mergedConfig'])
}
}

View file

@ -1,23 +1,31 @@
<template>
<div v-if="loggedIn">
<FAIcon
:class="classes"
class="FavoriteButton fa-scale-110 fa-old-padding -interactive"
<div class="FavoriteButton">
<button
v-if="loggedIn"
class="button-unstyled interactive"
:class="status.favorited && '-favorited'"
:title="$t('tool_tip.favorite')"
@click.prevent="favorite()"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
:icon="[status.favorited ? 'fas' : 'far', 'star']"
:spin="animated"
@click.prevent="favorite()"
/>
<span v-if="!mergedConfig.hidePostStats && status.fave_num > 0">{{ status.fave_num }}</span>
</div>
<div v-else>
</button>
<span v-else>
<FAIcon
:class="classes"
class="FavoriteButton fa-scale-110 fa-old-padding"
class="fa-scale-110 fa-old-padding"
:title="$t('tool_tip.favorite')"
:icon="['far', 'star']"
/>
<span v-if="!mergedConfig.hidePostStats && status.fave_num > 0">{{ status.fave_num }}</span>
</span>
<span
v-if="!mergedConfig.hidePostStats && status.fave_num > 0"
class="action-counter"
>
{{ status.fave_num }}
</span>
</div>
</template>
@ -27,19 +35,28 @@
@import '../../_variables.scss';
.FavoriteButton {
&.-interactive {
cursor: pointer;
display: flex;
> :first-child {
padding: 10px;
margin: -10px -8px -10px -10px;
}
.action-counter {
pointer-events: none;
user-select: none;
}
.interactive {
.svg-inline--fa {
animation-duration: 0.6s;
}
&:hover {
&:hover .svg-inline--fa,
&.-favorited .svg-inline--fa {
color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange);
}
}
&.-favorited {
color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange);
}
}
</style>

View file

@ -1,3 +1,5 @@
import fileSizeFormatService from '../../services/file_size_format/file_size_format.js'
const FeaturesPanel = {
computed: {
chat: function () { return this.$store.state.instance.chatAvailable },
@ -6,7 +8,8 @@ const FeaturesPanel = {
whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },
mediaProxy: function () { return this.$store.state.instance.mediaProxyAvailable },
minimalScopesMode: function () { return this.$store.state.instance.minimalScopesMode },
textlimit: function () { return this.$store.state.instance.textlimit }
textlimit: function () { return this.$store.state.instance.textlimit },
uploadlimit: function () { return fileSizeFormatService.fileSizeFormat(this.$store.state.instance.uploadlimit) }
}
}

View file

@ -25,6 +25,7 @@
</li>
<li>{{ $t('features_panel.scope_options') }}</li>
<li>{{ $t('features_panel.text_limit') }} = {{ textlimit }}</li>
<li>{{ $t('features_panel.upload_limit') }} = {{ uploadlimit.num }} {{ $t('upload.file_size_units.' + uploadlimit.unit) }}</li>
</ul>
</div>
</div>

View file

@ -1,6 +1,6 @@
<template>
<button
class="btn btn-default follow-button"
class="btn button-default follow-button"
:class="{ toggled: isPressed }"
:disabled="inProgress"
:title="title"

View file

@ -2,13 +2,13 @@
<basic-user-card :user="user">
<div class="follow-request-card-content-container">
<button
class="btn btn-default"
class="btn button-default"
@click="approveUser"
>
{{ $t('user_card.approve') }}
</button>
<button
class="btn btn-default"
class="btn button-default"
@click="denyUser"
>
{{ $t('user_card.deny') }}

View file

@ -9,11 +9,15 @@
<div class="notice-message">
{{ $t(notice.messageKey, notice.messageArgs) }}
</div>
<button
class="button-unstyled close-notice"
@click="closeNotice(notice)"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
@click="closeNotice(notice)"
/>
</button>
</div>
</div>
</template>
@ -54,7 +58,7 @@
.global-error {
background-color: var(--alertPopupError, $fallback--cRed);
color: var(--alertPopupErrorText, $fallback--text);
i {
.svg-inline--fa {
color: var(--alertPopupErrorText, $fallback--text);
}
}
@ -62,7 +66,7 @@
.global-warning {
background-color: var(--alertPopupWarning, $fallback--cOrange);
color: var(--alertPopupWarningText, $fallback--text);
i {
.svg-inline--fa {
color: var(--alertPopupWarningText, $fallback--text);
}
}
@ -70,9 +74,16 @@
.global-info {
background-color: var(--alertPopupNeutral, $fallback--fg);
color: var(--alertPopupNeutralText, $fallback--text);
i {
.svg-inline--fa {
color: var(--alertPopupNeutralText, $fallback--text);
}
}
.close-notice {
padding-right: 0.2em;
.svg-inline--fa:hover {
opacity: 0.6;
}
}
}
</style>

View file

@ -2,12 +2,10 @@ import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faTimes,
faCircleNotch
} from '@fortawesome/free-solid-svg-icons'
library.add(
faTimes,
faCircleNotch
)
@ -53,8 +51,7 @@ const ImageCropper = {
cropper: undefined,
dataUrl: undefined,
filename: undefined,
submitting: false,
submitError: null
submitting: false
}
},
computed: {
@ -66,9 +63,6 @@ const ImageCropper = {
},
cancelText () {
return this.cancelButtonLabel || this.$t('image_cropper.cancel')
},
submitErrorMsg () {
return this.submitError && this.submitError instanceof Error ? this.submitError.toString() : this.submitError
}
},
methods: {
@ -82,12 +76,8 @@ const ImageCropper = {
},
submit (cropping = true) {
this.submitting = true
this.avatarUploadError = null
this.submitHandler(cropping && this.cropper, this.file)
.then(() => this.destroy())
.catch((err) => {
this.submitError = err
})
.finally(() => {
this.submitting = false
})
@ -113,9 +103,6 @@ const ImageCropper = {
reader.readAsDataURL(this.file)
this.$emit('changed', this.file, reader)
}
},
clearError () {
this.submitError = null
}
},
mounted () {

View file

@ -11,21 +11,21 @@
</div>
<div class="image-cropper-buttons-wrapper">
<button
class="btn"
class="button-default btn"
type="button"
:disabled="submitting"
@click="submit()"
v-text="saveText"
/>
<button
class="btn"
class="button-default btn"
type="button"
:disabled="submitting"
@click="destroy"
v-text="cancelText"
/>
<button
class="btn"
class="button-default btn"
type="button"
:disabled="submitting"
@click="submit(false)"
@ -37,17 +37,6 @@
icon="circle-notch"
/>
</div>
<div
v-if="submitError"
class="alert error"
>
{{ submitErrorMsg }}
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
@click="clearError"
/>
</div>
</div>
<input
ref="input"

View file

@ -15,7 +15,7 @@
/>
<button
v-else
class="btn btn-default"
class="btn button-default"
@click="submit"
>
{{ submitButtonLabel }}

View file

@ -1,3 +1,5 @@
import { mapGetters } from 'vuex'
const LinkPreview = {
name: 'LinkPreview',
props: [
@ -15,11 +17,20 @@ const LinkPreview = {
// Currently BE shoudn't give cards if tagged NSFW, this is a bit paranoid
// as it makes sure to hide the image if somehow NSFW tagged preview can
// exist.
return this.card.image && !this.nsfw && this.size !== 'hide'
return this.card.image && !this.censored && this.size !== 'hide'
},
censored () {
return this.nsfw && this.hideNsfwConfig
},
useDescription () {
return this.card.description && /\S/.test(this.card.description)
}
},
hideNsfwConfig () {
return this.mergedConfig.hideNsfw
},
...mapGetters([
'mergedConfig'
])
},
created () {
if (this.useImage) {

View file

@ -9,12 +9,17 @@
<div
v-if="useImage && imageLoaded"
class="card-image"
:class="{ 'small-image': size === 'small' }"
>
<img :src="card.image">
</div>
<div class="card-content">
<span class="card-host faint">{{ card.provider_name }}</span>
<span class="card-host faint">
<span
v-if="censored"
class="nsfw-alert alert warning"
>{{ $t('status.nsfw') }}</span>
{{ card.provider_name }}
</span>
<h4 class="card-title">{{ card.title }}</h4>
<p
v-if="useDescription"
@ -50,10 +55,6 @@
}
}
.small-image {
width: 80px;
}
.card-content {
max-height: 100%;
margin: 0.5em;
@ -76,6 +77,10 @@
max-height: calc(1.2em * 3 - 1px);
}
.nsfw-alert {
margin: 2em 0;
}
color: $fallback--text;
color: var(--text, $fallback--text);
border-style: solid;

View file

@ -61,7 +61,7 @@
<button
:disabled="loggingIn"
type="submit"
class="btn btn-default"
class="btn button-default"
>
{{ $t('login.login') }}
</button>

View file

@ -1,10 +1,7 @@
<template>
<div
<label
class="media-upload"
:class="{ disabled: disabled }"
>
<label
class="label"
:title="$t('tool_tip.media_upload')"
>
<FAIcon
@ -27,7 +24,6 @@
@change="change"
>
</label>
</div>
</template>
<script src="./media_upload.js" ></script>
@ -36,12 +32,6 @@
@import '../../_variables.scss';
.media-upload {
.label {
display: inline-block;
}
.new-icon {
cursor: pointer;
}
}
</style>

View file

@ -23,23 +23,23 @@
<div class="form-group">
<div class="login-bottom">
<div>
<a
href="#"
<button
class="button-unstyled -link"
@click.prevent="requireTOTP"
>
{{ $t('login.enter_two_factor_code') }}
</a>
</button>
<br>
<a
href="#"
<button
class="button-unstyled -link"
@click.prevent="abortMFA"
>
{{ $t('general.cancel') }}
</a>
</button>
</div>
<button
type="submit"
class="btn btn-default"
class="btn button-default"
>
{{ $t('general.verify') }}
</button>

View file

@ -25,23 +25,23 @@
<div class="form-group">
<div class="login-bottom">
<div>
<a
href="#"
<button
class="button-unstyled -link"
@click.prevent="requireRecovery"
>
{{ $t('login.enter_recovery_code') }}
</a>
</button>
<br>
<a
href="#"
<button
class="button-unstyled -link"
@click.prevent="abortMFA"
>
{{ $t('general.cancel') }}
</a>
</button>
</div>
<button
type="submit"
class="btn btn-default"
class="btn button-default"
>
{{ $t('general.verify') }}
</button>

View file

@ -9,9 +9,8 @@
@click="scrollToTop()"
>
<div class="item">
<a
href="#"
class="mobile-nav-button"
<button
class="button-unstyled mobile-nav-button"
@click.stop.prevent="toggleMobileSidebar()"
>
<FAIcon
@ -22,7 +21,7 @@
v-if="unreadChatCount"
class="alert-dot"
/>
</a>
</button>
<router-link
v-if="!hideSitename"
class="site-name"
@ -33,10 +32,9 @@
</router-link>
</div>
<div class="item right">
<a
<button
v-if="currentUser"
class="mobile-nav-button"
href="#"
class="button-unstyled mobile-nav-button"
@click.stop.prevent="openMobileNotifications()"
>
<FAIcon
@ -47,7 +45,7 @@
v-if="unseenNotificationsCount"
class="alert-dot"
/>
</a>
</button>
</div>
</nav>
<div

View file

@ -1,7 +1,7 @@
<template>
<div v-if="isLoggedIn">
<button
class="new-status-button"
class="button-default new-status-button"
:class="{ 'hidden': isHidden }"
@click="openPostForm"
>

View file

@ -12,13 +12,13 @@
<div class="dropdown-menu">
<span v-if="user.is_local">
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleRight(&quot;admin&quot;)"
>
{{ $t(!!user.rights.admin ? 'user_card.admin_menu.revoke_admin' : 'user_card.admin_menu.grant_admin') }}
</button>
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleRight(&quot;moderator&quot;)"
>
{{ $t(!!user.rights.moderator ? 'user_card.admin_menu.revoke_moderator' : 'user_card.admin_menu.grant_moderator') }}
@ -29,13 +29,13 @@
/>
</span>
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleActivationStatus()"
>
{{ $t(!!user.deactivated ? 'user_card.admin_menu.activate_account' : 'user_card.admin_menu.deactivate_account') }}
</button>
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="deleteUserDialog(true)"
>
{{ $t('user_card.admin_menu.delete_account') }}
@ -47,7 +47,7 @@
/>
<span v-if="hasTagPolicy">
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleTag(tags.FORCE_NSFW)"
>
{{ $t('user_card.admin_menu.force_nsfw') }}
@ -57,7 +57,7 @@
/>
</button>
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleTag(tags.STRIP_MEDIA)"
>
{{ $t('user_card.admin_menu.strip_media') }}
@ -67,7 +67,7 @@
/>
</button>
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleTag(tags.FORCE_UNLISTED)"
>
{{ $t('user_card.admin_menu.force_unlisted') }}
@ -77,7 +77,7 @@
/>
</button>
<button
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleTag(tags.SANDBOX)"
>
{{ $t('user_card.admin_menu.sandbox') }}
@ -88,7 +88,7 @@
</button>
<button
v-if="user.is_local"
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)"
>
{{ $t('user_card.admin_menu.disable_remote_subscription') }}
@ -99,7 +99,7 @@
</button>
<button
v-if="user.is_local"
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)"
>
{{ $t('user_card.admin_menu.disable_any_subscription') }}
@ -110,7 +110,7 @@
</button>
<button
v-if="user.is_local"
class="dropdown-item"
class="button-default dropdown-item"
@click="toggleTag(tags.QUARANTINE)"
>
{{ $t('user_card.admin_menu.quarantine') }}
@ -124,7 +124,7 @@
</div>
<button
slot="trigger"
class="btn btn-default btn-block"
class="btn button-default btn-block"
:class="{ toggled }"
>
{{ $t('user_card.admin_menu.moderation') }}
@ -141,13 +141,13 @@
<p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p>
<template slot="footer">
<button
class="btn btn-default"
class="btn button-default"
@click="deleteUserDialog(false)"
>
{{ $t('general.cancel') }}
</button>
<button
class="btn btn-default danger"
class="btn button-default danger"
@click="deleteUser()"
>
{{ $t('user_card.admin_menu.delete_user') }}

View file

@ -3,7 +3,7 @@
<div class="mute-card-content-container">
<button
v-if="muted"
class="btn btn-default"
class="btn button-default"
:disabled="progress"
@click="unmuteUser"
>
@ -16,7 +16,7 @@
</button>
<button
v-else
class="btn btn-default"
class="btn button-default"
:disabled="progress"
@click="muteUser"
>

View file

@ -14,14 +14,15 @@
{{ notification.from_profile.screen_name }}
</router-link>
</small>
<a
href="#"
class="unmute"
<button
class="button-unstyled unmute"
@click.prevent="toggleMute"
><FAIcon
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="eye-slash"
/></a>
/>
</button>
</div>
<div
v-else
@ -132,14 +133,16 @@
/>
</span>
</div>
<a
<button
v-if="needMute"
href="#"
class="button-unstyled"
@click.prevent="toggleMute"
><FAIcon
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="eye-slash"
/></a>
/>
</button>
</span>
<div
v-if="notification.type === 'follow' || notification.type === 'follow_request'"

View file

@ -6,6 +6,7 @@ import {
filteredNotificationsFromStore,
unseenNotificationsFromStore
} from '../../services/notification_utils/notification_utils.js'
import FaviconService from '../../services/favicon_service/favicon_service.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
@ -75,8 +76,10 @@ const Notifications = {
watch: {
unseenCountTitle (count) {
if (count > 0) {
FaviconService.drawFaviconBadge()
this.$store.dispatch('setPageTitle', `(${count})`)
} else {
FaviconService.clearFaviconBadge()
this.$store.dispatch('setPageTitle', '')
}
}

View file

@ -15,16 +15,9 @@
class="badge badge-notification unseen-count"
>{{ unseenCount }}</span>
</div>
<div
v-if="error"
class="loadmore-error alert error"
@click.prevent
>
{{ $t('timeline.error_fetching') }}
</div>
<button
v-if="unseenCount"
class="read-button"
class="button-default read-button"
@click.prevent="markAsSeen"
>
{{ $t('notifications.read') }}
@ -48,15 +41,15 @@
>
{{ $t('notifications.no_more_notifications') }}
</div>
<a
<button
v-else-if="!loading"
href="#"
class="button-unstyled -link -fullwidth"
@click.prevent="fetchOlderNotifications()"
>
<div class="new-status-notification text-center panel-footer">
{{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }}
</div>
</a>
</button>
<div
v-else
class="new-status-notification text-center panel-footer"

View file

@ -51,7 +51,7 @@
<button
:disabled="isPending"
type="submit"
class="btn btn-default btn-block"
class="btn button-default btn-block"
>
{{ $t('general.submit') }}
</button>

View file

@ -42,14 +42,15 @@
:value="index"
>
<label class="option-vote">
<div>{{ option.title }}</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="option.title_html" />
</label>
</div>
</div>
<div class="footer faint">
<button
v-if="!showResults"
class="btn btn-default poll-vote-button"
class="btn button-default poll-vote-button"
type="button"
:disabled="isDisabled"
@click="vote"

View file

@ -21,7 +21,10 @@ const Popover = {
// Replaces the classes you may want for the popover container.
// Use 'popover-default' in addition to get the default popover
// styles with your custom class.
popoverClass: String
popoverClass: String,
// If true, subtract padding when calculating position for the popover,
// use it when popover offset looks to be different on top vs bottom.
removePadding: Boolean
},
data () {
return {
@ -96,9 +99,15 @@ const Popover = {
if (origin.y + content.offsetHeight > yBounds.max) usingTop = true
if (origin.y - content.offsetHeight < yBounds.min) usingTop = false
let vPadding = 0
if (this.removePadding && usingTop) {
const anchorStyle = getComputedStyle(anchorEl)
vPadding = parseFloat(anchorStyle.paddingTop) + parseFloat(anchorStyle.paddingBottom)
}
const yOffset = (this.offset && this.offset.y) || 0
const translateY = usingTop
? -anchorEl.offsetHeight - yOffset - content.offsetHeight
? -anchorEl.offsetHeight + vPadding - yOffset - content.offsetHeight
: yOffset
const xOffset = (this.offset && this.offset.x) || 0

View file

@ -3,12 +3,13 @@
@mouseenter="onMouseenter"
@mouseleave="onMouseleave"
>
<div
<button
ref="trigger"
class="button-unstyled -fullwidth popover-trigger-button"
@click="onClick"
>
<slot name="trigger" />
</div>
</button>
<div
v-if="!hidden"
ref="content"
@ -30,6 +31,10 @@
<style lang="scss">
@import '../../_variables.scss';
.popover-trigger-button {
display: block;
}
.popover {
z-index: 8;
position: absolute;
@ -90,6 +95,7 @@
box-shadow: none;
width: 100%;
height: 100%;
box-sizing: border-box;
--btnText: var(--popoverText, $fallback--text);

View file

@ -159,8 +159,7 @@ const PostStatusForm = {
...this.$store.state.instance.emoji,
...this.$store.state.instance.customEmoji
],
users: this.$store.state.users.users,
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
store: this.$store
})
},
emojiSuggestor () {
@ -531,7 +530,7 @@ const PostStatusForm = {
!(isFormBiggerThanScroller &&
this.$refs.textarea.selectionStart !== this.$refs.textarea.value.length)
const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0
const targetScroll = currentScroll + totalDelta
const targetScroll = Math.round(currentScroll + totalDelta)
if (scrollerRef === window) {
scrollerRef.scroll(0, targetScroll)

View file

@ -24,12 +24,12 @@
tag="p"
class="visibility-notice"
>
<a
href="#"
<button
class="button-unstyled -link"
@click="openProfileTab"
>
{{ $t('post_status.account_not_locked_warning_link') }}
</a>
</button>
</i18n>
<p
v-if="!hideScopeNotice && newStatus.visibility === 'public'"
@ -243,38 +243,34 @@
@upload-failed="uploadFailed"
@all-uploaded="finishedUploadingFiles"
/>
<div
class="emoji-icon"
>
<div
<button
class="emoji-icon button-unstyled"
:title="$t('emoji.add_emoji')"
class="btn btn-default"
@click="showEmojiPicker"
>
<FAIcon icon="smile-beam" />
</div>
</div>
<div
</button>
<button
v-if="pollsAvailable"
class="poll-icon"
class="poll-icon button-unstyled"
:class="{ selected: pollFormVisible }"
:title="$t('polls.add_poll')"
@click="togglePollForm"
>
<FAIcon icon="poll-h" />
</div>
</button>
</div>
<button
v-if="posting"
disabled
class="btn btn-default"
class="btn button-default"
>
{{ $t('post_status.posting') }}
</button>
<button
v-else-if="isOverLengthLimit"
disabled
class="btn btn-default"
class="btn button-default"
>
{{ $t('general.submit') }}
</button>
@ -282,7 +278,7 @@
<button
v-else
:disabled="uploadingFiles || disableSubmit"
class="btn btn-default"
class="btn button-default"
@touchstart.stop.prevent="postStatus($event, newStatus)"
@click.stop.prevent="postStatus($event, newStatus)"
>

View file

@ -27,13 +27,21 @@ const ReactButton = {
},
computed: {
commonEmojis () {
return ['👍', '😠', '👀', '😂', '🔥']
return [
{ displayText: 'thumbsup', replacement: '👍' },
{ displayText: 'angry', replacement: '😠' },
{ displayText: 'eyes', replacement: '👀' },
{ displayText: 'joy', replacement: '😂' },
{ displayText: 'fire', replacement: '🔥' }
]
},
emojis () {
if (this.filterWord !== '') {
const filterWordLowercase = this.filterWord.toLowerCase()
let orderedEmojiList = []
for (const emoji of this.$store.state.instance.emoji) {
if (emoji.replacement === this.filterWord) return [emoji]
const indexOfFilterWord = emoji.displayText.toLowerCase().indexOf(filterWordLowercase)
if (indexOfFilterWord > -1) {
if (!Array.isArray(orderedEmojiList[indexOfFilterWord])) {

View file

@ -3,8 +3,8 @@
trigger="click"
placement="top"
:offset="{ y: 5 }"
class="react-button-popover"
:bound-to="{ x: 'container' }"
remove-padding
>
<div
slot="content"
@ -20,17 +20,19 @@
<div class="reaction-picker">
<span
v-for="emoji in commonEmojis"
:key="emoji"
:key="emoji.replacement"
class="emoji-button"
@click="addReaction($event, emoji, close)"
:title="emoji.displayText"
@click="addReaction($event, emoji.replacement, close)"
>
{{ emoji }}
{{ emoji.replacement }}
</span>
<div class="reaction-picker-divider" />
<span
v-for="(emoji, key) in emojis"
:key="key"
class="emoji-button"
:title="emoji.displayText"
@click="addReaction($event, emoji.replacement, close)"
>
{{ emoji.replacement }}
@ -38,11 +40,14 @@
<div class="reaction-bottom-fader" />
</div>
</div>
<span slot="trigger">
<FAIcon
class="fa-scale-110 fa-old-padding add-reaction-button"
:icon="['far', 'smile-beam']"
<span
slot="trigger"
class="ReactButton"
:title="$t('tool_tip.add_reaction')"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
:icon="['far', 'smile-beam']"
/>
</span>
</Popover>
@ -102,10 +107,11 @@
}
}
.add-reaction-button {
cursor: pointer;
.ReactButton {
padding: 10px;
margin: -10px;
&:hover {
&:hover .svg-inline--fa {
color: $fallback--text;
color: var(--text, $fallback--text);
}

View file

@ -211,7 +211,7 @@
<button
:disabled="isPending"
type="submit"
class="btn btn-default"
class="btn button-default"
>
{{ $t('general.submit') }}
</button>

View file

@ -16,7 +16,7 @@
>
<button
click="submit"
class="remote-button"
class="button-default remote-button"
>
{{ $t('user_card.remote_follow') }}
</button>

View file

@ -1,20 +1,28 @@
<template>
<div>
<FAIcon
<div class="ReplyButton">
<button
v-if="loggedIn"
class="ReplyButton fa-scale-110 fa-old-padding -interactive"
icon="reply"
:title="$t('tool_tip.reply')"
class="button-unstyled interactive"
:class="{'-active': replying}"
:title="$t('tool_tip.reply')"
@click.prevent="$emit('toggle')"
/>
>
<FAIcon
v-else
class="fa-scale-110 fa-old-padding"
icon="reply"
class="ReplyButton fa-scale-110 fa-old-padding"
/>
</button>
<span v-else>
<FAIcon
icon="reply"
class="fa-scale-110 fa-old-padding"
:title="$t('tool_tip.reply')"
/>
<span v-if="status.replies_count > 0">
</span>
<span
v-if="status.replies_count > 0"
class="action-counter"
>
{{ status.replies_count }}
</span>
</div>
@ -26,14 +34,25 @@
@import '../../_variables.scss';
.ReplyButton {
&.-interactive {
cursor: pointer;
display: flex;
&:hover,
&.-active {
> :first-child {
padding: 10px;
margin: -10px -8px -10px -10px;
}
.action-counter {
pointer-events: none;
user-select: none;
}
.interactive {
&:hover .svg-inline--fa,
&.-active .svg-inline--fa {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
}
}
</style>

View file

@ -24,11 +24,6 @@ const RetweetButton = {
}
},
computed: {
classes () {
return {
'-repeated': this.status.repeated
}
},
mergedConfig () {
return this.$store.getters.mergedConfig
}

View file

@ -1,33 +1,38 @@
<template>
<div v-if="loggedIn">
<template v-if="visibility !== 'private' && visibility !== 'direct'">
<FAIcon
:class="classes"
class="RetweetButton fa-scale-110 fa-old-padding -interactive"
icon="retweet"
:spin="animated"
<div class="RetweetButton">
<button
v-if="visibility !== 'private' && visibility !== 'direct' && loggedIn"
class="button-unstyled interactive"
:class="status.repeated && '-repeated'"
:title="$t('tool_tip.repeat')"
@click.prevent="retweet()"
/>
<span v-if="!mergedConfig.hidePostStats && status.repeat_num > 0">{{ status.repeat_num }}</span>
</template>
<template v-else>
>
<FAIcon
:class="classes"
class="RetweetButton fa-scale-110 fa-old-padding"
class="fa-scale-110 fa-old-padding"
icon="retweet"
:spin="animated"
/>
</button>
<span v-else-if="loggedIn">
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="lock"
:title="$t('timeline.no_retweet_hint')"
/>
</template>
</div>
<div v-else-if="!loggedIn">
</span>
<span v-else>
<FAIcon
:class="classes"
class="fa-scale-110 fa-old-padding"
icon="retweet"
:title="$t('tool_tip.repeat')"
/>
<span v-if="!mergedConfig.hidePostStats && status.repeat_num > 0">{{ status.repeat_num }}</span>
</span>
<span
v-if="!mergedConfig.hidePostStats && status.repeat_num > 0"
class="no-event"
>
{{ status.repeat_num }}
</span>
</div>
</template>
@ -37,19 +42,28 @@
@import '../../_variables.scss';
.RetweetButton {
&.-interactive {
cursor: pointer;
display: flex;
> :first-child {
padding: 10px;
margin: -10px -8px -10px -10px;
}
.action-counter {
pointer-events: none;
user-select: none;
}
.interactive {
.svg-inline--fa {
animation-duration: 0.6s;
}
&:hover {
&:hover .svg-inline--fa,
&.-repeated .svg-inline--fa {
color: $fallback--cGreen;
color: var(--cGreen, $fallback--cGreen);
}
}
&.-repeated {
color: $fallback--cGreen;
color: var(--cGreen, $fallback--cGreen);
}
}
</style>

View file

@ -3,9 +3,9 @@
v-if="!showNothing"
class="ScopeSelector"
>
<span
<button
v-if="showDirect"
class="scope"
class="button-unstyled scope"
:class="css.direct"
:title="$t('post_status.scope.direct')"
@click="changeVis('direct')"
@ -14,10 +14,10 @@
icon="envelope"
class="fa-scale-110 fa-old-padding"
/>
</span>
<span
</button>
<button
v-if="showPrivate"
class="scope"
class="button-unstyled scope"
:class="css.private"
:title="$t('post_status.scope.private')"
@click="changeVis('private')"
@ -26,10 +26,10 @@
icon="lock"
class="fa-scale-110 fa-old-padding"
/>
</span>
<span
</button>
<button
v-if="showUnlisted"
class="scope"
class="button-unstyled scope"
:class="css.unlisted"
:title="$t('post_status.scope.unlisted')"
@click="changeVis('unlisted')"
@ -38,10 +38,10 @@
icon="lock-open"
class="fa-scale-110 fa-old-padding"
/>
</span>
<span
</button>
<button
v-if="showPublic"
class="scope"
class="button-unstyled scope"
:class="css.public"
:title="$t('post_status.scope.public')"
@click="changeVis('public')"
@ -50,7 +50,7 @@
icon="globe"
class="fa-scale-110 fa-old-padding"
/>
</span>
</button>
</div>
</template>

View file

@ -14,7 +14,7 @@
@keyup.enter="newQuery(searchTerm)"
>
<button
class="btn search-button"
class="btn button-default search-button"
@click="newQuery(searchTerm)"
>
<FAIcon icon="search" />

View file

@ -3,17 +3,18 @@
class="SearchBar"
:class="{ '-expanded': !hidden }"
>
<a
<button
v-if="hidden"
href="#"
class="nav-icon"
class="button-unstyled nav-icon"
:title="$t('nav.search')"
><FAIcon
@click.prevent.stop="toggleHidden"
>
<FAIcon
fixed-width
class="fa-scale-110 fa-old-padding"
icon="search"
@click.prevent.stop="toggleHidden"
/></a>
/>
</button>
<template v-else>
<input
id="search-bar-input"
@ -25,7 +26,7 @@
@keyup.enter="find(searchTerm)"
>
<button
class="btn search-button"
class="button-default search-button"
@click="find(searchTerm)"
>
<FAIcon
@ -33,14 +34,16 @@
icon="search"
/>
</button>
<span>
<button
class="button-unstyled cancel-search"
@click.prevent.stop="toggleHidden"
>
<FAIcon
fixed-width
icon="times"
class="cancel-icon fa-scale-110 fa-old-padding"
@click.prevent.stop="toggleHidden"
/>
</span>
</button>
</template>
</div>
</template>
@ -69,8 +72,11 @@
flex: 1 0 auto;
}
.cancel-search {
height: 50px;
}
.cancel-icon {
cursor: pointer;
color: $fallback--text;
color: var(--btnTopBarText, $fallback--text);
}

View file

@ -30,13 +30,13 @@
</template>
</transition>
<button
class="btn"
class="btn button-default"
@click="peekModal"
>
{{ $t('general.peek') }}
</button>
<button
class="btn"
class="btn button-default"
@click="closeModal"
>
{{ $t('general.close') }}

View file

@ -75,6 +75,7 @@
<p>{{ $t('settings.filtering_explanation') }}</p>
<textarea
id="muteWords"
class="resize-height"
v-model="muteWordsString"
/>
</div>

View file

@ -34,6 +34,10 @@ const GeneralTab = {
return this.$store.state.instance.postFormats || []
},
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
instanceWallpaperUsed () {
return this.$store.state.instance.background &&
!this.$store.state.users.currentUser.background_image
},
...SharedComputedObject()
}
}

View file

@ -11,6 +11,11 @@
{{ $t('settings.hide_isp') }}
</Checkbox>
</li>
<li v-if="instanceWallpaperUsed">
<Checkbox v-model="hideInstanceWallpaper">
{{ $t('settings.hide_wallpaper') }}
</Checkbox>
</li>
</ul>
</div>
<div class="setting-item">

View file

@ -27,7 +27,7 @@
<div class="bulk-actions">
<ProgressButton
v-if="selected.length > 0"
class="btn btn-default bulk-action-button"
class="btn button-default bulk-action-button"
:click="() => blockUsers(selected)"
>
{{ $t('user_card.block') }}
@ -37,7 +37,7 @@
</ProgressButton>
<ProgressButton
v-if="selected.length > 0"
class="btn btn-default"
class="btn button-default"
:click="() => unblockUsers(selected)"
>
{{ $t('user_card.unblock') }}
@ -85,7 +85,7 @@
<div class="bulk-actions">
<ProgressButton
v-if="selected.length > 0"
class="btn btn-default"
class="btn button-default"
:click="() => muteUsers(selected)"
>
{{ $t('user_card.mute') }}
@ -95,7 +95,7 @@
</ProgressButton>
<ProgressButton
v-if="selected.length > 0"
class="btn btn-default"
class="btn button-default"
:click="() => unmuteUsers(selected)"
>
{{ $t('user_card.unmute') }}
@ -141,7 +141,7 @@
<div class="bulk-actions">
<ProgressButton
v-if="selected.length > 0"
class="btn btn-default"
class="btn button-default"
:click="() => unmuteDomains(selected)"
>
{{ $t('domain_mute_card.unmute') }}

View file

@ -21,7 +21,7 @@
<p>{{ $t('settings.notification_mutes') }}</p>
<p>{{ $t('settings.notification_blocks') }}</p>
<button
class="btn btn-default"
class="btn button-default"
@click="updateNotificationSettings"
>
{{ $t('general.submit') }}

View file

@ -45,9 +45,7 @@ const ProfileTab = {
banner: null,
bannerPreview: null,
background: null,
backgroundPreview: null,
bannerUploadError: null,
backgroundUploadError: null
backgroundPreview: null
}
},
components: {
@ -68,8 +66,7 @@ const ProfileTab = {
...this.$store.state.instance.emoji,
...this.$store.state.instance.customEmoji
],
users: this.$store.state.users.users,
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
store: this.$store
})
},
emojiSuggestor () {
@ -79,10 +76,7 @@ const ProfileTab = {
] })
},
userSuggestor () {
return suggestor({
users: this.$store.state.users.users,
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
})
return suggestor({ store: this.$store })
},
fieldsLimits () {
return this.$store.state.instance.fieldsLimits
@ -166,18 +160,18 @@ const ProfileTab = {
if (file.size > this.$store.state.instance[slot + 'limit']) {
const filesize = fileSizeFormatService.fileSizeFormat(file.size)
const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
this[slot + 'UploadError'] = [
this.$t('upload.error.base'),
this.$t(
'upload.error.file_too_big',
{
this.$store.dispatch('pushGlobalNotice', {
messageKey: 'upload.error.message',
messageArgs: [
this.$t('upload.error.file_too_big', {
filesize: filesize.num,
filesizeunit: filesize.unit,
allowedsize: allowedsize.num,
allowedsizeunit: allowedsize.unit
}
)
].join(' ')
})
],
level: 'error'
})
return
}
// eslint-disable-next-line no-undef
@ -217,8 +211,9 @@ const ProfileTab = {
that.$store.commit('setCurrentUser', user)
resolve()
})
.catch((err) => {
reject(new Error(that.$t('upload.error.base') + ' ' + err.message))
.catch((error) => {
that.displayUploadError(error)
reject(error)
})
}
@ -239,24 +234,27 @@ const ProfileTab = {
this.$store.commit('setCurrentUser', user)
this.bannerPreview = null
})
.catch((err) => {
this.bannerUploadError = this.$t('upload.error.base') + ' ' + err.message
})
.then(() => { this.bannerUploading = false })
.catch(this.displayUploadError)
.finally(() => { this.bannerUploading = false })
},
submitBackground (background) {
if (!this.backgroundPreview && background !== '') { return }
this.backgroundUploading = true
this.$store.state.api.backendInteractor.updateProfileImages({ background }).then((data) => {
if (!data.error) {
this.$store.state.api.backendInteractor.updateProfileImages({ background })
.then((data) => {
this.$store.commit('addNewUsers', [data])
this.$store.commit('setCurrentUser', data)
this.backgroundPreview = null
} else {
this.backgroundUploadError = this.$t('upload.error.base') + data.error
}
this.backgroundUploading = false
})
.catch(this.displayUploadError)
.finally(() => { this.backgroundUploading = false })
},
displayUploadError (error) {
this.$store.dispatch('pushGlobalNotice', {
messageKey: 'upload.error.message',
messageArgs: [error.message],
level: 'error'
})
}
}

View file

@ -11,7 +11,7 @@
<input
id="username"
v-model="newName"
classname="name-changer"
class="name-changer"
>
</EmojiInput>
<p>{{ $t('settings.bio') }}</p>
@ -22,7 +22,7 @@
>
<textarea
v-model="newBio"
classname="bio"
class="bio resize-height"
/>
</EmojiInput>
<p>
@ -150,7 +150,7 @@
</p>
<button
:disabled="newName && newName.length === 0"
class="btn btn-default"
class="btn button-default"
@click="updateProfile"
>
{{ $t('general.submit') }}
@ -179,7 +179,7 @@
<button
v-show="pickAvatarBtnVisible"
id="pick-avatar"
class="btn"
class="button-default btn"
type="button"
>
{{ $t('settings.upload_a_photo') }}
@ -224,22 +224,11 @@
/>
<button
v-else-if="bannerPreview"
class="btn btn-default"
class="btn button-default"
@click="submitBanner(banner)"
>
{{ $t('general.submit') }}
</button>
<div
v-if="bannerUploadError"
class="alert error"
>
Error: {{ bannerUploadError }}
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
@click="clearUploadError('banner')"
/>
</div>
</div>
<div class="setting-item">
<h2>{{ $t('settings.profile_background') }}</h2>
@ -274,23 +263,11 @@
/>
<button
v-else-if="backgroundPreview"
class="btn btn-default"
class="btn button-default"
@click="submitBackground(background)"
>
{{ $t('general.submit') }}
</button>
<div
v-if="backgroundUploadError"
class="alert error"
>
Error: {{ backgroundUploadError }}
<FAIcon
size="lg"
class="fa-scale-110 fa-old-padding"
icon="times"
@click="clearUploadError('background')"
/>
</div>
</div>
</div>
</template>

View file

@ -2,14 +2,14 @@
<div>
<slot />
<button
class="btn btn-default"
class="btn button-default"
:disabled="disabled"
@click="confirm"
>
{{ $t('general.confirm') }}
</button>
<button
class="btn btn-default"
class="btn button-default"
:disabled="disabled"
@click="cancel"
>

View file

@ -29,7 +29,7 @@
/>
<button
v-if="!confirmNewBackupCodes"
class="btn btn-default"
class="btn button-default"
@click="getBackupCodes"
>
{{ $t('settings.mfa.generate_new_recovery_codes') }}
@ -61,7 +61,7 @@
<button
v-if="canSetupOTP"
class="btn btn-default"
class="btn button-default"
@click="cancelSetup"
>
{{ $t('general.cancel') }}
@ -69,7 +69,7 @@
<button
v-if="canSetupOTP"
class="btn btn-default"
class="btn button-default"
@click="setupOTP"
>
{{ $t('settings.mfa.setup_otp') }}
@ -108,13 +108,13 @@
>
<div class="confirm-otp-actions">
<button
class="btn btn-default"
class="btn button-default"
@click="doConfirmOTP"
>
{{ $t('settings.mfa.confirm_and_enable') }}
</button>
<button
class="btn btn-default"
class="btn button-default"
@click="cancelSetup"
>
{{ $t('general.cancel') }}

View file

@ -4,7 +4,7 @@
<strong>{{ $t('settings.mfa.otp') }}</strong>
<button
v-if="!isActivated"
class="btn btn-default"
class="btn button-default"
@click="doActivate"
>
{{ $t('general.enable') }}
@ -12,7 +12,7 @@
<button
v-if="isActivated"
class="btn btn-default"
class="btn button-default"
:disabled="deactivate"
@click="doDeactivate"
>

View file

@ -19,7 +19,7 @@
>
</div>
<button
class="btn btn-default"
class="btn button-default"
@click="changeEmail"
>
{{ $t('general.submit') }}
@ -57,7 +57,7 @@
>
</div>
<button
class="btn btn-default"
class="btn button-default"
@click="changePassword"
>
{{ $t('general.submit') }}
@ -92,7 +92,7 @@
<td>{{ oauthToken.validUntil }}</td>
<td class="actions">
<button
class="btn btn-default"
class="btn button-default"
@click="revokeToken(oauthToken.id)"
>
{{ $t('settings.revoke_token') }}
@ -116,7 +116,7 @@
type="password"
>
<button
class="btn btn-default"
class="btn button-default"
@click="deleteAccount"
>
{{ $t('settings.delete_account') }}
@ -130,7 +130,7 @@
</p>
<button
v-if="!deletingAccount"
class="btn btn-default"
class="btn button-default"
@click="confirmDelete"
>
{{ $t('general.submit') }}

View file

@ -15,7 +15,7 @@
<span class="alert error">
{{ $t('settings.style.preview.error') }}
</span>
<button class="btn">
<button class="btn button-default">
{{ $t('settings.style.preview.button') }}
</button>
</div>
@ -102,7 +102,7 @@
>
<label for="preview_checkbox">{{ $t('settings.style.preview.checkbox') }}</label>
</span>
<button class="btn">
<button class="btn button-default">
{{ $t('settings.style.preview.button') }}
</button>
</div>

View file

@ -165,7 +165,8 @@
border-color: var(--border, $fallback--border);
margin: 1em 0;
padding: 1em;
background: var(--body-background-image);
background-color: var(--wallpaper);
background-image: var(--body-background-image);
background-size: cover;
background-position: 50% 50%;

View file

@ -12,13 +12,13 @@
<div class="buttons">
<template v-if="themeWarning.type === 'snapshot_source_mismatch'">
<button
class="btn"
class="btn button-default"
@click="forceLoad"
>
{{ $t('settings.style.switcher.use_source') }}
</button>
<button
class="btn"
class="btn button-default"
@click="forceSnapshot"
>
{{ $t('settings.style.switcher.use_snapshot') }}
@ -26,7 +26,7 @@
</template>
<template v-else-if="themeWarning.noActionsPossible">
<button
class="btn"
class="btn button-default"
@click="dismissWarning"
>
{{ $t('general.dismiss') }}
@ -34,13 +34,13 @@
</template>
<template v-else>
<button
class="btn"
class="btn button-default"
@click="forceLoad"
>
{{ $t('settings.style.switcher.load_theme') }}
</button>
<button
class="btn"
class="btn button-default"
@click="dismissWarning"
>
{{ $t('settings.style.switcher.keep_as_is') }}
@ -131,13 +131,13 @@
<p>{{ $t('settings.theme_help') }}</p>
<div class="tab-header-buttons">
<button
class="btn"
class="btn button-default"
@click="clearOpacity"
>
{{ $t('settings.style.switcher.clear_opacity') }}
</button>
<button
class="btn"
class="btn button-default"
@click="clearV1"
>
{{ $t('settings.style.switcher.clear_all') }}
@ -238,13 +238,13 @@
<div class="tab-header">
<p>{{ $t('settings.theme_help') }}</p>
<button
class="btn"
class="btn button-default"
@click="clearOpacity"
>
{{ $t('settings.style.switcher.clear_opacity') }}
</button>
<button
class="btn"
class="btn button-default"
@click="clearV1"
>
{{ $t('settings.style.switcher.clear_all') }}
@ -616,6 +616,15 @@
:disabled="underlayOpacityLocal === 'transparent'"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.wallpaper') }}</h4>
<ColorInput
v-model="wallpaperColorLocal"
name="wallpaper"
:label="$t('settings.style.advanced_colors.wallpaper')"
:fallback="previewTheme.colors.wallpaper"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.poll') }}</h4>
<ColorInput
@ -806,7 +815,7 @@
<div class="tab-header">
<p>{{ $t('settings.radii_help') }}</p>
<button
class="btn"
class="btn button-default"
@click="clearRoundness"
>
{{ $t('settings.style.switcher.clear_all') }}
@ -936,7 +945,7 @@
/>
</div>
<button
class="btn"
class="btn button-default"
@click="clearShadows"
>
{{ $t('settings.style.switcher.clear_all') }}
@ -980,7 +989,7 @@
<div class="tab-header">
<p>{{ $t('settings.style.fonts.help') }}</p>
<button
class="btn"
class="btn button-default"
@click="clearFonts"
>
{{ $t('settings.style.switcher.clear_all') }}
@ -1017,14 +1026,14 @@
<div class="apply-container">
<button
class="btn submit"
class="btn button-default submit"
:disabled="!themeValid"
@click="setCustomTheme"
>
{{ $t('general.apply') }}
</button>
<button
class="btn"
class="btn button-default"
@click="clearAll"
>
{{ $t('settings.style.switcher.reset') }}

View file

@ -84,7 +84,7 @@
/>
</label>
<button
class="btn btn-default"
class="btn button-default"
:disabled="!ready || !present"
@click="del"
>
@ -94,7 +94,7 @@
/>
</button>
<button
class="btn btn-default"
class="btn button-default"
:disabled="!moveUpValid"
@click="moveUp"
>
@ -104,7 +104,7 @@
/>
</button>
<button
class="btn btn-default"
class="btn button-default"
:disabled="!moveDnValid"
@click="moveDn"
>
@ -114,7 +114,7 @@
/>
</button>
<button
class="btn btn-default"
class="btn button-default"
:disabled="usingFallback"
@click="add"
>

View file

@ -144,8 +144,8 @@
</router-link>
</li>
<li @click="toggleDrawer">
<a
href="#"
<button
class="button-unstyled -link -fullwidth"
@click="openSettingsModal"
>
<FAIcon
@ -153,7 +153,7 @@
class="fa-scale-110 fa-old-padding"
icon="cog"
/> {{ $t("settings.settings") }}
</a>
</button>
</li>
<li @click="toggleDrawer">
<router-link :to="{ name: 'about'}">
@ -183,8 +183,8 @@
v-if="currentUser"
@click="toggleDrawer"
>
<a
href="#"
<button
class="button-unstyled -link -fullwidth"
@click="doLogout"
>
<FAIcon
@ -192,7 +192,7 @@
class="fa-scale-110 fa-old-padding"
icon="sign-out-alt"
/> {{ $t("login.logout") }}
</a>
</button>
</li>
</ul>
</div>
@ -331,7 +331,7 @@
.side-drawer li {
padding: 0;
a {
a, button {
box-sizing: border-box;
display: block;
height: 3em;

View file

@ -26,7 +26,6 @@ import {
faTimes,
faRetweet,
faReply,
faExternalLinkSquareAlt,
faPlusSquare,
faSmileBeam,
faEllipsisH,
@ -44,7 +43,6 @@ library.add(
faTimes,
faRetweet,
faReply,
faExternalLinkSquareAlt,
faPlusSquare,
faStar,
faSmileBeam,

View file

@ -29,6 +29,8 @@ $status-margin: 0.75em;
&.-conversation {
border-left-width: 4px;
border-left-style: solid;
border-left-color: $fallback--cRed;
border-left-color: var(--cRed, $fallback--cRed);
}
.gravestone {
@ -137,6 +139,20 @@ $status-margin: 0.75em;
.heading-right {
display: flex;
flex-shrink: 0;
.button-unstyled {
padding: 5px;
margin: -5px;
&:hover svg {
color: $fallback--lightText;
color: var(--lightText, $fallback--lightText);
}
}
.svg-inline--fa {
margin-left: 0.25em;
}
}
.timeago {

View file

@ -47,16 +47,15 @@
>
{{ muteWordHits.join(', ') }}
</small>
<a
href="#"
class="unmute fa-scale-110 fa-old-padding"
<button
class="unmute button-unstyled"
@click.prevent="toggleMute"
>
<FAIcon
icon="eye-slash"
class="fa-scale-110 fa-old-padding"
/>
</a>
</button>
</div>
</template>
<template v-else>
@ -185,43 +184,34 @@
:title="status.visibility | capitalize"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
fixed-width
class="fa-scale-110"
:icon="visibilityIcon(status.visibility)"
/>
</span>
<a
v-if="!status.is_local && !isPreview"
:href="status.external_url"
target="_blank"
class="source_url"
title="Source"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="external-link-square-alt"
/>
</a>
<a
<button
v-if="expandable && !isPreview"
href="#"
title="Expand"
class="button-unstyled"
:title="$t('status.expand')"
@click.prevent="toggleExpanded"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
fixed-width
class="fa-scale-110"
icon="plus-square"
/>
</a>
<a
</button>
<button
v-if="unmuted"
href="#"
class="button-unstyled"
@click.prevent="toggleMute"
>
<FAIcon
fixed-width
icon="eye-slash"
class="fa-scale-110 fa-old-padding"
class="fa-scale-110"
/>
</a>
</button>
</span>
</div>
@ -237,9 +227,8 @@
style="min-width: 0"
:class="{ '-strikethrough': !status.parent_visible }"
>
<a
class="reply-to"
href="#"
<button
class="button-unstyled reply-to"
:aria-label="$t('tool_tip.reply')"
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
>
@ -253,7 +242,7 @@
>
{{ $t('status.reply_to') }}
</span>
</a>
</button>
</StatusPopover>
<span
@ -286,11 +275,12 @@
:key="reply.id"
:status-id="reply.id"
>
<a
href="#"
class="reply-link"
<button
class="button-unstyled -link reply-link"
@click.prevent="gotoOriginal(reply.id)"
>{{ reply.name }}</a>
>
{{ reply.name }}
</button>
</StatusPopover>
</div>
</div>

View file

@ -12,35 +12,34 @@
@click.prevent="linkClicked"
v-html="status.summary_html"
/>
<a
<button
v-if="longSubject && showingLongSubject"
href="#"
class="tall-subject-hider"
class="button-unstyled -link tall-subject-hider"
@click.prevent="showingLongSubject=false"
>{{ $t("status.hide_full_subject") }}</a>
<a
>
{{ $t("status.hide_full_subject") }}
</button>
<button
v-else-if="longSubject"
class="tall-subject-hider"
class="button-unstyled -link tall-subject-hider"
:class="{ 'tall-subject-hider_focused': focused }"
href="#"
@click.prevent="showingLongSubject=true"
>
{{ $t("status.show_full_subject") }}
</a>
</button>
</div>
<div
:class="{'tall-status': hideTallStatus}"
class="status-content-wrapper"
>
<a
<button
v-if="hideTallStatus"
class="tall-status-hider"
class="button-unstyled -link tall-status-hider"
:class="{ 'tall-status-hider_focused': focused }"
href="#"
@click.prevent="toggleShowMore"
>
{{ $t("general.show_more") }}
</a>
</button>
<div
v-if="!hideSubjectStatus"
:class="{ 'single-line': singleLine }"
@ -48,10 +47,9 @@
@click.prevent="linkClicked"
v-html="postBodyHtml"
/>
<a
<button
v-if="hideSubjectStatus"
href="#"
class="cw-status-hider"
class="button-unstyled -link cw-status-hider"
@click.prevent="toggleShowMore"
>
{{ $t("status.show_content") }}
@ -79,15 +77,14 @@
v-if="status.card"
icon="link"
/>
</a>
<a
</button>
<button
v-if="showingMore && !fullContent"
href="#"
class="status-unhider"
class="button-unstyled -link status-unhider"
@click.prevent="toggleShowMore"
>
{{ tallStatus ? $t("general.show_less") : $t("status.hide_content") }}
</a>
</button>
</div>
<div v-if="status.poll && status.poll.options && !hideSubjectStatus">

View file

@ -81,7 +81,7 @@ export default Vue.component('tab-switcher', {
const tabs = this.$slots.default
.map((slot, index) => {
if (!slot.tag) return
const classesTab = ['tab']
const classesTab = ['tab', 'button-default']
const classesWrapper = ['tab-wrapper']
if (this.activeIndex === index) {
classesTab.push('active')

View file

@ -50,17 +50,10 @@ const Timeline = {
TimelineMenu
},
computed: {
timelineError () {
return this.$store.state.statuses.error
},
errorData () {
return this.$store.state.statuses.errorData
},
newStatusCount () {
return this.timeline.newStatusCount
},
showLoadButton () {
if (this.timelineError || this.errorData) return false
return this.timeline.newStatusCount > 0 || this.timeline.flushMarker !== 0
},
loadButtonString () {
@ -171,11 +164,12 @@ const Timeline = {
userId: this.userId,
tag: this.tag
}).then(({ statuses }) => {
store.commit('setLoading', { timeline: this.timelineName, value: false })
if (statuses && statuses.length === 0) {
this.bottomedOut = true
}
})
}).finally(() =>
store.commit('setLoading', { timeline: this.timelineName, value: false })
)
}, 1000, this),
determineVisibleStatuses () {
if (!this.$refs.timeline) return

View file

@ -2,23 +2,9 @@
<div :class="[classes.root, 'Timeline']">
<div :class="classes.header">
<TimelineMenu v-if="!embedded" />
<div
v-if="timelineError"
class="loadmore-error alert error"
@click.prevent
>
{{ $t('timeline.error_fetching') }}
</div>
<div
v-else-if="errorData"
class="loadmore-error alert error"
@click.prevent
>
{{ errorData.statusText }}
</div>
<button
v-else-if="showLoadButton"
class="loadmore-button"
v-if="showLoadButton"
class="button-default loadmore-button"
@click.prevent="showNewStatuses"
>
{{ loadButtonString }}
@ -75,19 +61,15 @@
>
{{ $t('timeline.no_more_statuses') }}
</div>
<a
v-else-if="!timeline.loading && !errorData"
href="#"
<button
v-else-if="!timeline.loading"
class="button-unstyled -link -fullwidth"
@click.prevent="fetchOlderStatuses()"
>
<div class="new-status-notification text-center panel-footer">{{ $t('timeline.load_older') }}</div>
</a>
<a
v-else-if="errorData"
href="#"
>
<div class="new-status-notification text-center panel-footer">{{ errorData.error }}</div>
</a>
<div class="new-status-notification text-center panel-footer">
{{ $t('timeline.load_older') }}
</div>
</button>
<div
v-else
class="new-status-notification text-center panel-footer"
@ -120,6 +102,7 @@
.timeline-heading {
max-width: 100%;
flex-wrap: nowrap;
align-items: center;
.loadmore-button {
flex-shrink: 0;
}

View file

@ -27,8 +27,7 @@ export const timelineNames = () => {
'bookmarks': 'nav.bookmarks',
'dms': 'nav.dms',
'public-timeline': 'nav.public_tl',
'public-external-timeline': 'nav.twkn',
'tag-timeline': 'tag'
'public-external-timeline': 'nav.twkn'
}
}
@ -60,6 +59,14 @@ const TimelineMenu = {
this.isOpen = true
}, 25)
},
blockOpen (event) {
// For the blank area inside the button element.
// Just setting @click.stop="" makes unintuitive behavior when
// menu is open and clicking on the blank area doesn't close it.
if (!this.isOpen) {
event.stopPropagation()
}
},
timelineName () {
const route = this.$route.name
if (route === 'tag-timeline') {

View file

@ -65,11 +65,17 @@
slot="trigger"
class="title timeline-menu-title"
>
<span>{{ timelineName() }}</span>
<span class="timeline-title">{{ timelineName() }}</span>
<span>
<FAIcon
size="sm"
icon="chevron-down"
/>
</span>
<span
class="click-blocker"
@click="blockOpen"
/>
</div>
</Popover>
</template>
@ -117,8 +123,9 @@
cursor: pointer;
user-select: none;
width: 100%;
display: flex;
span {
.timeline-menu-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -128,6 +135,11 @@
margin-left: 0.6em;
transition: transform 100ms;
}
.click-blocker {
cursor: default;
flex-grow: 1;
}
}
&.open .timeline-menu-title svg {

View file

@ -162,7 +162,7 @@
<template v-if="relationship.following">
<ProgressButton
v-if="!relationship.subscribing"
class="btn btn-default"
class="btn button-default"
:click="subscribeUser"
:title="$t('user_card.subscribe')"
>
@ -170,7 +170,7 @@
</ProgressButton>
<ProgressButton
v-else
class="btn btn-default toggled"
class="btn button-default toggled"
:click="unsubscribeUser"
:title="$t('user_card.unsubscribe')"
>
@ -192,14 +192,14 @@
<div>
<button
v-if="relationship.muting"
class="btn btn-default btn-block toggled"
class="btn button-default btn-block toggled"
@click="unmuteUser"
>
{{ $t('user_card.muted') }}
</button>
<button
v-else
class="btn btn-default btn-block"
class="btn button-default btn-block"
@click="muteUser"
>
{{ $t('user_card.mute') }}
@ -207,7 +207,7 @@
</div>
<div>
<button
class="btn btn-default btn-block"
class="btn button-default btn-block"
@click="mentionUser"
>
{{ $t('user_card.mention') }}

View file

@ -20,14 +20,13 @@
:key="index"
class="user-profile-field"
>
<!-- eslint-disable vue/no-v-html -->
<dt
:title="user.fields_text[index].name"
class="user-profile-field-name"
@click.prevent="linkClicked"
>
{{ field.name }}
</dt>
<!-- eslint-disable vue/no-v-html -->
v-html="field.name"
/>
<dd
:title="user.fields_text[index].value"
class="user-profile-field-value"

View file

@ -29,7 +29,7 @@
</div>
<div>
<button
class="btn btn-default"
class="btn button-default"
:disabled="processing"
@click="reportUser"
>

View file

@ -1,6 +1,7 @@
<template>
<video
class="video"
preload="metadata"
:src="attachment.url"
:loop="loopVideo"
:controls="controls"

View file

@ -91,7 +91,11 @@ const withLoadMore = ({
{children}
</WrappedComponent>
<div class="with-load-more-footer">
{this.error && <a onClick={this.fetchEntries} class="alert error">{this.$t('general.generic_error')}</a>}
{this.error &&
<button onClick={this.fetchEntries} class="button-unstyled -link -fullwidth alert error">
{this.$t('general.generic_error')}
</button>
}
{!this.error && this.loading && <FAIcon spin icon="circle-notch"/>}
{!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>}
</div>

View file

@ -50,7 +50,8 @@
"scope_options": "Scope options",
"text_limit": "Text limit",
"title": "Features",
"who_to_follow": "Who to follow"
"who_to_follow": "Who to follow",
"upload_limit": "Upload limit"
},
"finder": {
"error_fetching_user": "Error fetching user",
@ -130,6 +131,7 @@
},
"notifications": {
"broken_favorite": "Unknown status, searching for it…",
"error": "Error fetching notifications: {0}",
"favorited_you": "favorited your status",
"followed_you": "followed you",
"follow_request": "wants to follow you",
@ -327,6 +329,7 @@
"hide_muted_posts": "Hide posts of muted users",
"max_thumbnails": "Maximum amount of thumbnails per post",
"hide_isp": "Hide instance-specific panel",
"hide_wallpaper": "Hide instance wallpaper",
"preload_images": "Preload images",
"use_one_click_nsfw": "Open NSFW attachments with just one click",
"hide_post_stats": "Hide post statistics (e.g. the number of favorites)",
@ -376,7 +379,7 @@
"hide_followers_count_description": "Don't show follower count",
"show_admin_badge": "Show Admin badge in my profile",
"show_moderator_badge": "Show Moderator badge in my profile",
"nsfw_clickthrough": "Enable clickthrough NSFW attachment hiding",
"nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses",
"oauth_tokens": "OAuth tokens",
"token": "Token",
"refresh_token": "Refresh Token",
@ -514,6 +517,7 @@
"inputs": "Input fields",
"faint_text": "Faded text",
"underlay": "Underlay",
"wallpaper": "Wallpaper",
"poll": "Poll graph",
"icons": "Icons",
"highlight": "Highlighted elements",
@ -634,7 +638,7 @@
"timeline": {
"collapse": "Collapse",
"conversation": "Conversation",
"error_fetching": "Error fetching updates",
"error": "Error fetching timeline: {0}",
"load_older": "Load older statuses",
"no_retweet_hint": "Post is marked as followers-only or direct and cannot be repeated",
"repeated": "repeated",
@ -660,13 +664,16 @@
"unmute_conversation": "Unmute conversation",
"status_unavailable": "Status unavailable",
"copy_link": "Copy link to status",
"external_source": "External source",
"thread_muted": "Thread muted",
"thread_muted_and_words": ", has words:",
"show_full_subject": "Show full subject",
"hide_full_subject": "Hide full subject",
"show_content": "Show content",
"hide_content": "Hide content",
"status_deleted": "This post was deleted"
"status_deleted": "This post was deleted",
"nsfw": "NSFW",
"expand": "Expand"
},
"user_card": {
"approve": "Approve",
@ -756,6 +763,7 @@
"upload": {
"error": {
"base": "Upload failed.",
"message": "Upload failed: {0}",
"file_too_big": "File too big [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
"default": "Try again later"
},

View file

@ -134,14 +134,14 @@
"registration": {
"bio": "Priskribo",
"email": "Retpoŝtadreso",
"fullname": "Vidiga nomo",
"fullname": "Prezenta nomo",
"password_confirm": "Konfirmo de pasvorto",
"registration": "Registriĝo",
"token": "Invita ĵetono",
"captcha": "TESTO DE HOMECO",
"new_captcha": "Klaku la bildon por akiri novan teston",
"username_placeholder": "ekz. lain",
"fullname_placeholder": "ekz. Lain Iwakura",
"fullname_placeholder": "ekz. Lain Ivakura",
"bio_placeholder": "ekz.\nSaluton, mi estas Lain.\nMi estas animea knabino vivanta en Japanujo. Eble vi konas min pro la retejo «Wired».",
"validations": {
"username_required": "ne povas resti malplena",
@ -164,7 +164,7 @@
"blocks_tab": "Blokitoj",
"btnRadius": "Butonoj",
"cBlue": "Blua (respondi, aboni)",
"cGreen": "Verda (kunhavigi)",
"cGreen": "Verda (diskonigi)",
"cOrange": "Oranĝa (ŝati)",
"cRed": "Ruĝa (nuligi)",
"change_password": "Ŝanĝi pasvorton",
@ -207,8 +207,8 @@
"import_theme": "Enlegi antaŭagordojn",
"inputRadius": "Enigaj kampoj",
"checkboxRadius": "Markbutonoj",
"instance_default": "(implicita: {value})",
"instance_default_simple": "(implicita)",
"instance_default": "(originale: {value})",
"instance_default_simple": "(originale)",
"interface": "Fasado",
"interfaceLanguage": "Lingvo de fasado",
"invalid_theme_imported": "La elektita dosiero ne estas subtenata haŭto de Pleromo. Neniuj ŝanĝoj al via haŭto okazis.",
@ -219,7 +219,7 @@
"loop_video_silent_only": "Ripetadi nur filmojn sen sono (ekz. la «GIF-ojn» de Mastodon)",
"mutes_tab": "Silentigoj",
"play_videos_in_modal": "Ludi filmojn en ŝpruca kadro",
"use_contain_fit": "Ne tondi la kunsendaĵon en bildetoj",
"use_contain_fit": "Ne pritondi bildetojn de kunsendaĵoj",
"name": "Nomo",
"name_bio": "Nomo kaj priskribo",
"new_password": "Nova pasvorto",
@ -265,7 +265,7 @@
"subject_line_email": "Kiel retpoŝto: «re: temo»",
"subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe",
"subject_line_noop": "Ne kopii",
"post_status_content_type": "Afiŝi specon de la enhavo de la stato",
"post_status_content_type": "Speco de enhavo de afiŝo",
"stop_gifs": "Movi GIF-bildojn dum ŝvebo de muso",
"streaming": "Ŝalti memagan fluigon de novaj afiŝoj kiam vi vidas la supron de la paĝo",
"text": "Teksto",
@ -379,7 +379,7 @@
"hint": "Por ombroj vi ankaŭ povas uzi --variable kiel koloran valoron, por uzi variantojn de CSS3. Bonvolu rimarki, ke tiuokaze agordoj de maltravidebleco ne funkcios.",
"filter_hint": {
"always_drop_shadow": "Averto: ĉi tiu ombro ĉiam uzas {0} kiam la foliumilo tion subtenas.",
"drop_shadow_syntax": "{0} ne subtenas parametron {1} kaj ŝlosilvorton {2}.",
"drop_shadow_syntax": "{0} ne subtenas parametron {1} kaj ĉefvorton {2}.",
"avatar_inset": "Bonvolu rimarki, ke agordi ambaŭ internajn kaj eksterajn ombrojn por profilbildoj povas redoni neatenditajn rezultojn ĉe profilbildoj travideblaj.",
"spread_zero": "Ombroj kun vastigo > 0 aperos kvazaŭ ĝi estus fakte nulo",
"inset_classic": "Internaj ombroj uzos {0}"
@ -394,7 +394,7 @@
"button": "Butono",
"buttonHover": "Butono (je ŝvebo)",
"buttonPressed": "Butono (premita)",
"buttonPressedHover": "Butono (premita kaj je ŝvebo)",
"buttonPressedHover": "Butono (je premo kaj ŝvebo)",
"input": "Eniga kampo"
},
"hintV3": "Kolorojn de ombroj vi ankaŭ povas skribi per la sistemo {0}."
@ -683,7 +683,7 @@
"replace": "Anstataŭigi",
"reject": "Rifuzi",
"ftl_removal": "Forigo el la historio de «La tuta konata reto»",
"keyword_policies": "Politiko pri ŝlosilvortoj"
"keyword_policies": "Politiko pri ĉefvortoj"
},
"federation": "Federado",
"mrf_policies_desc": "Politikoj de Mesaĝa ŝanĝilaro (MRF) efikas sur federa konduto de la nodo. La sekvaj politikoj estas ŝaltitaj:"
@ -739,8 +739,8 @@
"week_short": "{0}s",
"weeks": "{0} semajnoj",
"week": "{0} semajno",
"seconds_short": "{0}s",
"second_short": "{0}s",
"seconds_short": "{0}sek",
"second_short": "{0}sek",
"seconds": "{0} sekundoj",
"second": "{0} sekundo",
"now_short": "nun",
@ -749,14 +749,14 @@
"month_short": "{0}m",
"months": "{0} monatoj",
"month": "{0} monato",
"minutes_short": "{0}m",
"minute_short": "{0}m",
"minutes_short": "{0}min",
"minute_short": "{0}min",
"minutes": "{0} minutoj",
"minute": "{0} minuto",
"in_past": "antaŭ {0}",
"in_future": "post {0}",
"hours_short": "{0}h",
"hour_short": "{0}h",
"hours_short": "{0}hor",
"hour_short": "{0}hor",
"hours": "{0} horoj",
"hour": "{0} horo",
"days_short": "{0}t",

View file

@ -104,7 +104,8 @@
"no_more_notifications": "No hay más notificaciones",
"reacted_with": "reaccionó con {0}",
"migrated_to": "migrado a",
"follow_request": "quiere seguirte"
"follow_request": "quiere seguirte",
"error": "Error obteniendo notificaciones:{0}"
},
"polls": {
"add_poll": "Añadir encuesta",
@ -313,7 +314,7 @@
"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_moderator_badge": "Mostrar la insignia de Moderador en mi perfil",
"nsfw_clickthrough": "Activar el clic para ocultar los adjuntos NSFW",
"nsfw_clickthrough": "Habilitar la ocultación de la imagen de vista previa del enlace y el adjunto para los estados NSFW por defecto",
"oauth_tokens": "Tokens de OAuth",
"token": "Token",
"refresh_token": "Actualizar el token",
@ -605,7 +606,8 @@
"up_to_date": "Actualizado",
"no_more_statuses": "No hay más estados",
"no_statuses": "Sin estados",
"reload": "Recargar"
"reload": "Recargar",
"error": "Error obteniendo la linea de tiempo:{0}"
},
"status": {
"favorites": "Favoritos",
@ -628,7 +630,9 @@
"copy_link": "Copiar el enlace al estado",
"status_unavailable": "Estado no disponible",
"bookmark": "Marcar",
"unbookmark": "Desmarcar"
"unbookmark": "Desmarcar",
"status_deleted": "Esta entrada ha sido eliminada",
"nsfw": "NSFW (No apropiado para el trabajo)"
},
"user_card": {
"approve": "Aprobar",

View file

@ -390,5 +390,13 @@
"GiB": "GiB",
"TiB": "TiB"
}
},
"about": {
"mrf": {
"keyword": {
"keyword_policies": "פוליסת מילות מפתח"
},
"federation": "פדרציה"
}
}
}

Some files were not shown because too many files have changed in this diff Show more