aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.scss65
-rw-r--r--src/boot/after_store.js3
-rw-r--r--src/components/account_actions/account_actions.vue15
-rw-r--r--src/components/async_component_error/async_component_error.vue2
-rw-r--r--src/components/attachment/attachment.js8
-rw-r--r--src/components/attachment/attachment.vue30
-rw-r--r--src/components/block_card/block_card.vue4
-rw-r--r--src/components/chat_list/chat_list.vue5
-rw-r--r--src/components/chat_message/chat_message.scss3
-rw-r--r--src/components/chat_message/chat_message.vue4
-rw-r--r--src/components/conversation/conversation.vue20
-rw-r--r--src/components/desktop_nav/desktop_nav.scss11
-rw-r--r--src/components/desktop_nav/desktop_nav.vue40
-rw-r--r--src/components/domain_mute_card/domain_mute_card.vue4
-rw-r--r--src/components/emoji_input/emoji_input.js35
-rw-r--r--src/components/emoji_input/emoji_input.vue8
-rw-r--r--src/components/emoji_input/suggestor.js140
-rw-r--r--src/components/emoji_reactions/emoji_reactions.vue2
-rw-r--r--src/components/export_import/export_import.vue4
-rw-r--r--src/components/exporter/exporter.vue2
-rw-r--r--src/components/extra_buttons/extra_buttons.vue32
-rw-r--r--src/components/favorite_button/favorite_button.js5
-rw-r--r--src/components/favorite_button/favorite_button.vue69
-rw-r--r--src/components/follow_button/follow_button.vue2
-rw-r--r--src/components/follow_request_card/follow_request_card.vue4
-rw-r--r--src/components/image_cropper/image_cropper.vue6
-rw-r--r--src/components/importer/importer.vue2
-rw-r--r--src/components/link-preview/link-preview.js15
-rw-r--r--src/components/link-preview/link-preview.vue17
-rw-r--r--src/components/login_form/login_form.vue2
-rw-r--r--src/components/media_upload/media_upload.vue54
-rw-r--r--src/components/mfa_form/recovery_form.vue14
-rw-r--r--src/components/mfa_form/totp_form.vue14
-rw-r--r--src/components/mobile_nav/mobile_nav.vue14
-rw-r--r--src/components/mobile_post_status_button/mobile_post_status_button.vue2
-rw-r--r--src/components/moderation_tools/moderation_tools.vue28
-rw-r--r--src/components/mute_card/mute_card.vue4
-rw-r--r--src/components/notification/notification.vue29
-rw-r--r--src/components/notifications/notifications.js3
-rw-r--r--src/components/notifications/notifications.vue15
-rw-r--r--src/components/password_reset/password_reset.vue2
-rw-r--r--src/components/poll/poll.vue2
-rw-r--r--src/components/popover/popover.js13
-rw-r--r--src/components/popover/popover.vue9
-rw-r--r--src/components/post_status_form/post_status_form.js3
-rw-r--r--src/components/post_status_form/post_status_form.vue34
-rw-r--r--src/components/react_button/react_button.js10
-rw-r--r--src/components/react_button/react_button.vue22
-rw-r--r--src/components/registration/registration.vue2
-rw-r--r--src/components/reply_button/reply_button.vue53
-rw-r--r--src/components/retweet_button/retweet_button.js5
-rw-r--r--src/components/retweet_button/retweet_button.vue74
-rw-r--r--src/components/scope_selector/scope_selector.vue24
-rw-r--r--src/components/search/search.vue2
-rw-r--r--src/components/search_bar/search_bar.vue32
-rw-r--r--src/components/settings_modal/settings_modal.vue4
-rw-r--r--src/components/settings_modal/tabs/mutes_and_blocks_tab.vue10
-rw-r--r--src/components/settings_modal/tabs/notifications_tab.vue2
-rw-r--r--src/components/settings_modal/tabs/profile_tab.js8
-rw-r--r--src/components/settings_modal/tabs/profile_tab.vue8
-rw-r--r--src/components/settings_modal/tabs/security_tab/confirm.vue4
-rw-r--r--src/components/settings_modal/tabs/security_tab/mfa.vue10
-rw-r--r--src/components/settings_modal/tabs/security_tab/mfa_totp.vue4
-rw-r--r--src/components/settings_modal/tabs/security_tab/security_tab.vue10
-rw-r--r--src/components/settings_modal/tabs/theme_tab/preview.vue4
-rw-r--r--src/components/settings_modal/tabs/theme_tab/theme_tab.vue28
-rw-r--r--src/components/shadow_control/shadow_control.vue8
-rw-r--r--src/components/side_drawer/side_drawer.vue14
-rw-r--r--src/components/status/status.scss2
-rw-r--r--src/components/status/status.vue35
-rw-r--r--src/components/status_content/status_content.vue37
-rw-r--r--src/components/tab_switcher/tab_switcher.js2
-rw-r--r--src/components/timeline/timeline.js12
-rw-r--r--src/components/timeline/timeline.vue36
-rw-r--r--src/components/timeline_menu/timeline_menu.js5
-rw-r--r--src/components/user_card/user_card.vue10
-rw-r--r--src/components/user_reporting_modal/user_reporting_modal.vue2
-rw-r--r--src/components/video_attachment/video_attachment.vue1
-rw-r--r--src/hocs/with_load_more/with_load_more.js6
-rw-r--r--src/i18n/en.json8
-rw-r--r--src/i18n/eo.json32
-rw-r--r--src/i18n/es.json12
-rw-r--r--src/i18n/he.json8
-rw-r--r--src/i18n/it.json9
-rw-r--r--src/i18n/ru.json15
-rw-r--r--src/i18n/uk.json99
-rw-r--r--src/i18n/zh.json10
-rw-r--r--src/modules/statuses.js23
-rw-r--r--src/modules/users.js6
-rw-r--r--src/services/api/api.service.js2
-rw-r--r--src/services/chat_service/chat_service.js2
-rw-r--r--src/services/entity_normalizer/entity_normalizer.service.js12
-rw-r--r--src/services/favicon_service/favicon_service.js61
-rw-r--r--src/services/notifications_fetcher/notifications_fetcher.service.js18
-rw-r--r--src/services/timeline_fetcher/timeline_fetcher.service.js18
95 files changed, 963 insertions, 657 deletions
diff --git a/src/App.scss b/src/App.scss
index ca7d33cd..cdc3209c 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -33,6 +33,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 +70,7 @@ a {
color: var(--link, $fallback--link);
}
-button {
+.button-default {
user-select: none;
color: $fallback--text;
color: var(--btnText, $fallback--text);
@@ -85,7 +86,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 +109,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 +123,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 +138,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 +154,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 {
@@ -442,6 +471,7 @@ main-router {
color: $fallback--faint;
color: var(--panelFaint, $fallback--faint);
}
+
.faint-link {
color: $fallback--faint;
color: var(--faintLink, $fallback--faint);
@@ -453,11 +483,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 +495,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 +522,8 @@ main-router {
}
}
- a {
+ a,
+ .-link {
color: $fallback--link;
color: var(--panelLink, $fallback--link)
}
@@ -507,15 +538,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 +828,7 @@ nav {
}
}
-.btn.btn-default {
+.btn.button-default {
min-height: 28px;
}
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 3cbbf020..b472fcf6 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -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 })
diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue
index e3ae376e..ab5d1d29 100644
--- a/src/components/account_actions/account_actions.vue
+++ b/src/components/account_actions/account_actions.vue
@@ -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"
diff --git a/src/components/async_component_error/async_component_error.vue b/src/components/async_component_error/async_component_error.vue
index b68b98f9..b1b59638 100644
--- a/src/components/async_component_error/async_component_error.vue
+++ b/src/components/async_component_error/async_component_error.vue
@@ -8,7 +8,7 @@
{{ $t('general.error_retry') }}
</p>
<button
- class="btn"
+ class="btn button-default"
@click="retry"
>
{{ $t('general.retry') }}
diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index e23fcb1b..5f5779a0 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -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 = {
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index f1fac2c8..2c1c1682 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -42,15 +42,13 @@
icon="play-circle"
/>
</a>
- <div
+ <button
v-if="nsfw && hideNsfwLocal && !hidden"
- class="hider"
+ class="button-unstyled hider"
+ @click.prevent="toggleHidden"
>
- <a
- href="#"
- @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 {
diff --git a/src/components/block_card/block_card.vue b/src/components/block_card/block_card.vue
index 5b00b738..2fe66d4c 100644
--- a/src/components/block_card/block_card.vue
+++ b/src/components/block_card/block_card.vue
@@ -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"
>
diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue
index 17e2f795..e23eec13 100644
--- a/src/components/chat_list/chat_list.vue
+++ b/src/components/chat_list/chat_list.vue
@@ -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>
diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss
index 5af744a3..e4351d3b 100644
--- a/src/components/chat_message/chat_message.scss
+++ b/src/components/chat_message/chat_message.scss
@@ -31,9 +31,6 @@
color: $fallback--text;
color: var(--text, $fallback--text);
}
-
- border-radius: $fallback--chatMessageRadius;
- border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius);
}
.popover {
diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue
index 3849ab6e..0777f880 100644
--- a/src/components/chat_message/chat_message.vue
+++ b/src/components/chat_message/chat_message.vue
@@ -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" />
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
index e0b9fcc5..353859b8 100644
--- a/src/components/conversation/conversation.vue
+++ b/src/components/conversation/conversation.vue
@@ -10,12 +10,13 @@
class="panel-heading conversation-heading"
>
<span class="title"> {{ $t('timeline.conversation') }} </span>
- <span v-if="collapsable">
- <a
- href="#"
- @click.prevent="toggleExpanded"
- >{{ $t('timeline.collapse') }}</a>
- </span>
+ <button
+ v-if="collapsable"
+ class="button-unstyled -link"
+ @click.prevent="toggleExpanded"
+ >
+ {{ $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;
diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss
index 028692a9..8fd8e620 100644
--- a/src/components/desktop_nav/desktop_nav.scss
+++ b/src/components/desktop_nav/desktop_nav.scss
@@ -21,7 +21,7 @@
grid-template-areas: "logo sitename actions";
}
- button {
+ .button-default {
&, svg {
color: $fallback--text;
color: var(--btnTopBarText, $fallback--text);
@@ -80,12 +80,13 @@
.nav-icon {
margin-left: 0.2em;
width: 2em;
+ height: 100%;
text-align: center;
- }
- a, a svg {
- color: $fallback--link;
- color: var(--topBarLink, $fallback--link);
+ .svg-inline--fa {
+ color: $fallback--link;
+ color: var(--topBarLink, $fallback--link);
+ }
}
.sitename {
diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue
index 3a6e4033..762aa610 100644
--- a/src/components/desktop_nav/desktop_nav.vue
+++ b/src/components/desktop_nav/desktop_nav.vue
@@ -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
- fixed-width
- class="fa-scale-110 fa-old-padding"
- icon="tachometer-alt"
- :title="$t('nav.administration')"
- /></a>
- <a
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding"
+ icon="tachometer-alt"
+ :title="$t('nav.administration')"
+ />
+ </a>
+ <button
v-if="currentUser"
- href="#"
- class="nav-icon"
+ class="button-unstyled nav-icon"
@click.prevent="logout"
- ><FAIcon
- fixed-width
- class="fa-scale-110 fa-old-padding"
- icon="sign-out-alt"
- :title="$t('login.logout')"
- /></a>
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding"
+ icon="sign-out-alt"
+ :title="$t('login.logout')"
+ />
+ </button>
</div>
</div>
</nav>
diff --git a/src/components/domain_mute_card/domain_mute_card.vue b/src/components/domain_mute_card/domain_mute_card.vue
index 97aee243..3b5aec14 100644
--- a/src/components/domain_mute_card/domain_mute_card.vue
+++ b/src/components/domain_mute_card/domain_mute_card.vue
@@ -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">
diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js
index 87303d08..2068a598 100644
--- a/src/components/emoji_input/emoji_input.js
+++ b/src/components/emoji_input/emoji_input.js
@@ -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: {
diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue
index 224e72cf..4becdc41 100644
--- a/src/components/emoji_input/emoji_input.vue
+++ b/src/components/emoji_input/emoji_input.vue
@@ -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">
diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js
index 8330345b..14a2b41e 100644
--- a/src/components/emoji_input/suggestor.js
+++ b/src/components/emoji_input/suggestor.js
@@ -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,19 +10,19 @@ 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 => {
- const firstChar = input[0]
- if (firstChar === ':' && data.emoji) {
- return suggestEmoji(data.emoji)(input)
- }
- if (firstChar === '@' && data.users) {
- return suggestUsers(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 emojiCurry(input)
+ }
+ if (firstChar === '@' && usersCurry) {
+ return usersCurry(input)
+ }
+ return []
}
- return []
}
export const suggestEmoji = emojis => input => {
@@ -57,50 +56,75 @@ export const suggestEmoji = emojis => input => {
})
}
-export const suggestUsers = data => input => {
- const noPrefix = input.toLowerCase().substr(1)
- const users = data.users
-
- const newUsers = 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
-
- // Matches on screen name (i.e. user@instance) makes a priority
- aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
- bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
-
- // Matches on name takes second priority
- aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
- bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
-
- const diff = (bScore - aScore) * 10
-
- // Then sort alphabetically
- const nameAlphabetically = a.name > b.name ? 1 : -1
- const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1
-
- return diff + nameAlphabetically + screenNameAlphabetically
- /* eslint-disable camelcase */
- }).map(({ screen_name, name, profile_image_url_original }) => ({
- displayText: screen_name,
- detailText: name,
- imageUrl: profile_image_url_original,
- replacement: '@' + screen_name + ' '
- }))
-
- // BE search users to get more comprehensive results
- if (data.updateUsersList) {
- debounceUserSearch(data, noPrefix)
+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 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)
+ ).slice(0, 20).sort((a, b) => {
+ let aScore = 0
+ let bScore = 0
+
+ // Matches on screen name (i.e. user@instance) makes a priority
+ aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
+ bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
+
+ // Matches on name takes second priority
+ aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
+ bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
+
+ const diff = (bScore - aScore) * 10
+
+ // Then sort alphabetically
+ const nameAlphabetically = a.name > b.name ? 1 : -1
+ const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1
+
+ return diff + nameAlphabetically + screenNameAlphabetically
+ /* eslint-disable camelcase */
+ }).map(({ screen_name, name, profile_image_url_original }) => ({
+ displayText: screen_name,
+ detailText: name,
+ imageUrl: profile_image_url_original,
+ replacement: '@' + screen_name + ' '
+ }))
+ /* eslint-enable camelcase */
+
+ suggestions = newSuggestions || []
+ return suggestions
}
- return newUsers
- /* eslint-enable camelcase */
}
diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue
index 2f14b5b2..51d50359 100644
--- a/src/components/emoji_reactions/emoji_reactions.vue
+++ b/src/components/emoji_reactions/emoji_reactions.vue
@@ -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()"
diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue
index ae00487f..8ffe34f8 100644
--- a/src/components/export_import/export_import.vue
+++ b/src/components/export_import/export_import.vue
@@ -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 }}
diff --git a/src/components/exporter/exporter.vue b/src/components/exporter/exporter.vue
index ecd71bf1..d6a03088 100644
--- a/src/components/exporter/exporter.vue
+++ b/src/components/exporter/exporter.vue
@@ -11,7 +11,7 @@
</div>
<button
v-else
- class="btn btn-default"
+ class="btn button-default"
@click="process"
>
{{ exportButtonLabel }}
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index a33f6e87..e687d487 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -2,8 +2,9 @@
<Popover
trigger="click"
placement="top"
- class="extra-button-popover"
+ :offset="{ y: 5 }"
:bound-to="{ x: 'container' }"
+ remove-padding
>
<div
slot="content"
@@ -12,7 +13,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 +23,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 +33,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 +44,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 +55,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 +66,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 +77,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 +87,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"
>
@@ -97,9 +98,12 @@
</button>
</div>
</div>
- <span slot="trigger">
+ <span
+ slot="trigger"
+ class="ExtraButtons"
+ >
<FAIcon
- class="ExtraButtons fa-scale-110 fa-old-padding"
+ class="fa-scale-110 fa-old-padding"
icon="ellipsis-h"
/>
</span>
@@ -112,11 +116,11 @@
@import '../../_variables.scss';
.ExtraButtons {
- cursor: pointer;
position: static;
+ padding: 10px;
+ margin: -10px;
- &:hover,
- .extra-button-popover.open & {
+ &:hover .svg-inline--fa {
color: $fallback--text;
color: var(--text, $fallback--text);
}
diff --git a/src/components/favorite_button/favorite_button.js b/src/components/favorite_button/favorite_button.js
index 2a2ee84a..5cd05f73 100644
--- a/src/components/favorite_button/favorite_button.js
+++ b/src/components/favorite_button/favorite_button.js
@@ -31,11 +31,6 @@ const FavoriteButton = {
}
},
computed: {
- classes () {
- return {
- '-favorited': this.status.favorited
- }
- },
...mapGetters(['mergedConfig'])
}
}
diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue
index dfe12f86..dce25e24 100644
--- a/src/components/favorite_button/favorite_button.vue
+++ b/src/components/favorite_button/favorite_button.vue
@@ -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')"
- :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>
- <FAIcon
- :class="classes"
- class="FavoriteButton 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>
+ >
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ :icon="[status.favorited ? 'fas' : 'far', 'star']"
+ :spin="animated"
+ />
+ </button>
+ <span v-else>
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ :title="$t('tool_tip.favorite')"
+ :icon="['far', 'star']"
+ />
+ </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;
- animation-duration: 0.6s;
+ 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>
diff --git a/src/components/follow_button/follow_button.vue b/src/components/follow_button/follow_button.vue
index bfdc137b..7f85f1d7 100644
--- a/src/components/follow_button/follow_button.vue
+++ b/src/components/follow_button/follow_button.vue
@@ -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"
diff --git a/src/components/follow_request_card/follow_request_card.vue b/src/components/follow_request_card/follow_request_card.vue
index b217b8ed..1b12ba4b 100644
--- a/src/components/follow_request_card/follow_request_card.vue
+++ b/src/components/follow_request_card/follow_request_card.vue
@@ -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') }}
diff --git a/src/components/image_cropper/image_cropper.vue b/src/components/image_cropper/image_cropper.vue
index 75def612..448461b4 100644
--- a/src/components/image_cropper/image_cropper.vue
+++ b/src/components/image_cropper/image_cropper.vue
@@ -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)"
diff --git a/src/components/importer/importer.vue b/src/components/importer/importer.vue
index c4fe5b00..210823f5 100644
--- a/src/components/importer/importer.vue
+++ b/src/components/importer/importer.vue
@@ -15,7 +15,7 @@
/>
<button
v-else
- class="btn btn-default"
+ class="btn button-default"
@click="submit"
>
{{ submitButtonLabel }}
diff --git a/src/components/link-preview/link-preview.js b/src/components/link-preview/link-preview.js
index 444aafbe..add7c563 100644
--- a/src/components/link-preview/link-preview.js
+++ b/src/components/link-preview/link-preview.js
@@ -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) {
diff --git a/src/components/link-preview/link-preview.vue b/src/components/link-preview/link-preview.vue
index 69171977..d3ca39b8 100644
--- a/src/components/link-preview/link-preview.vue
+++ b/src/components/link-preview/link-preview.vue
@@ -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;
diff --git a/src/components/login_form/login_form.vue b/src/components/login_form/login_form.vue
index a1f77210..bfabb946 100644
--- a/src/components/login_form/login_form.vue
+++ b/src/components/login_form/login_form.vue
@@ -61,7 +61,7 @@
<button
:disabled="loggingIn"
type="submit"
- class="btn btn-default"
+ class="btn button-default"
>
{{ $t('login.login') }}
</button>
diff --git a/src/components/media_upload/media_upload.vue b/src/components/media_upload/media_upload.vue
index 88251a26..e955aa72 100644
--- a/src/components/media_upload/media_upload.vue
+++ b/src/components/media_upload/media_upload.vue
@@ -1,33 +1,29 @@
<template>
- <div
+ <label
class="media-upload"
:class="{ disabled: disabled }"
+ :title="$t('tool_tip.media_upload')"
>
- <label
- class="label"
- :title="$t('tool_tip.media_upload')"
+ <FAIcon
+ v-if="uploading"
+ class="progress-icon"
+ icon="circle-notch"
+ spin
+ />
+ <FAIcon
+ v-if="!uploading"
+ class="new-icon"
+ icon="upload"
+ />
+ <input
+ v-if="uploadReady"
+ :disabled="disabled"
+ type="file"
+ style="position: fixed; top: -100em"
+ multiple="true"
+ @change="change"
>
- <FAIcon
- v-if="uploading"
- class="progress-icon"
- icon="circle-notch"
- spin
- />
- <FAIcon
- v-if="!uploading"
- class="new-icon"
- icon="upload"
- />
- <input
- v-if="uploadReady"
- :disabled="disabled"
- type="file"
- style="position: fixed; top: -100em"
- multiple="true"
- @change="change"
- >
- </label>
- </div>
+ </label>
</template>
<script src="./media_upload.js" ></script>
@@ -36,12 +32,6 @@
@import '../../_variables.scss';
.media-upload {
- .label {
- display: inline-block;
- }
-
- .new-icon {
- cursor: pointer;
- }
+ cursor: pointer;
}
</style>
diff --git a/src/components/mfa_form/recovery_form.vue b/src/components/mfa_form/recovery_form.vue
index 78953649..0bf68e27 100644
--- a/src/components/mfa_form/recovery_form.vue
+++ b/src/components/mfa_form/recovery_form.vue
@@ -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>
diff --git a/src/components/mfa_form/totp_form.vue b/src/components/mfa_form/totp_form.vue
index 9401cad5..79230148 100644
--- a/src/components/mfa_form/totp_form.vue
+++ b/src/components/mfa_form/totp_form.vue
@@ -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>
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
index 5304a500..0f0ea457 100644
--- a/src/components/mobile_nav/mobile_nav.vue
+++ b/src/components/mobile_nav/mobile_nav.vue
@@ -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
diff --git a/src/components/mobile_post_status_button/mobile_post_status_button.vue b/src/components/mobile_post_status_button/mobile_post_status_button.vue
index 50529878..767f8244 100644
--- a/src/components/mobile_post_status_button/mobile_post_status_button.vue
+++ b/src/components/mobile_post_status_button/mobile_post_status_button.vue
@@ -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"
>
diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue
index 60fa6ceb..5c7b82ec 100644
--- a/src/components/moderation_tools/moderation_tools.vue
+++ b/src/components/moderation_tools/moderation_tools.vue
@@ -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') }}
diff --git a/src/components/mute_card/mute_card.vue b/src/components/mute_card/mute_card.vue
index 9611fb82..ca33c6c5 100644
--- a/src/components/mute_card/mute_card.vue
+++ b/src/components/mute_card/mute_card.vue
@@ -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"
>
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index 2bbde108..f56aa977 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -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
- class="fa-scale-110 fa-old-padding"
- icon="eye-slash"
- /></a>
+ >
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ icon="eye-slash"
+ />
+ </button>
</div>
<div
v-else
@@ -132,14 +133,16 @@
/>
</span>
</div>
- <a
+ <button
v-if="needMute"
- href="#"
+ class="button-unstyled"
@click.prevent="toggleMute"
- ><FAIcon
- class="fa-scale-110 fa-old-padding"
- icon="eye-slash"
- /></a>
+ >
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ icon="eye-slash"
+ />
+ </button>
</span>
<div
v-if="notification.type === 'follow' || notification.type === 'follow_request'"
diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js
index 4b479e13..49258563 100644
--- a/src/components/notifications/notifications.js
+++ b/src/components/notifications/notifications.js
@@ -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', '')
}
}
diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue
index bd875cca..725d1ad4 100644
--- a/src/components/notifications/notifications.vue
+++ b/src/components/notifications/notifications.vue
@@ -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"
diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue
index 0deb9ccf..a931cb5a 100644
--- a/src/components/password_reset/password_reset.vue
+++ b/src/components/password_reset/password_reset.vue
@@ -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>
diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue
index 5f54b416..264a5f03 100644
--- a/src/components/poll/poll.vue
+++ b/src/components/poll/poll.vue
@@ -49,7 +49,7 @@
<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"
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index 695f73b9..5e417fa0 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.js
@@ -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
diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue
index 9b8680e5..020eab05 100644
--- a/src/components/popover/popover.vue
+++ b/src/components/popover/popover.vue
@@ -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;
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 3ff4a3c8..4148381c 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -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 () {
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 42d3152b..ed830f57 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -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"
+ <button
+ class="emoji-icon button-unstyled"
+ :title="$t('emoji.add_emoji')"
+ @click="showEmojiPicker"
>
- <div
- :title="$t('emoji.add_emoji')"
- class="btn btn-default"
- @click="showEmojiPicker"
- >
- <FAIcon icon="smile-beam" />
- </div>
- </div>
- <div
+ <FAIcon icon="smile-beam" />
+ </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)"
>
diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js
index de0df70c..5e7b7580 100644
--- a/src/components/react_button/react_button.js
+++ b/src/components/react_button/react_button.js
@@ -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])) {
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index e508a3e9..dde67d21 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -3,8 +3,8 @@
trigger="click"
placement="top"
:offset="{ y: 5 }"
- class="react-button-popover"
:bound-to="{ x: 'container' }"
+ remove-padding
>
<div
slot="content"
@@ -22,15 +22,17 @@
v-for="emoji in commonEmojis"
:key="emoji"
class="emoji-button"
+ :title="emoji.displayText"
@click="addReaction($event, emoji, 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">
+ <span
+ slot="trigger"
+ class="ReactButton"
+ :title="$t('tool_tip.add_reaction')"
+ >
<FAIcon
- class="fa-scale-110 fa-old-padding add-reaction-button"
+ class="fa-scale-110 fa-old-padding"
:icon="['far', 'smile-beam']"
- :title="$t('tool_tip.add_reaction')"
/>
</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);
}
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index a83ca1e5..100df0d6 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -211,7 +211,7 @@
<button
:disabled="isPending"
type="submit"
- class="btn btn-default"
+ class="btn button-default"
>
{{ $t('general.submit') }}
</button>
diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue
index a0ac8941..c17041da 100644
--- a/src/components/reply_button/reply_button.vue
+++ b/src/components/reply_button/reply_button.vue
@@ -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}"
- @click.prevent="$emit('toggle')"
- />
- <FAIcon
- v-else
- icon="reply"
- class="ReplyButton fa-scale-110 fa-old-padding"
:title="$t('tool_tip.reply')"
- />
- <span v-if="status.replies_count > 0">
+ @click.prevent="$emit('toggle')"
+ >
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ icon="reply"
+ />
+ </button>
+ <span v-else>
+ <FAIcon
+ icon="reply"
+ class="fa-scale-110 fa-old-padding"
+ :title="$t('tool_tip.reply')"
+ />
+ </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>
diff --git a/src/components/retweet_button/retweet_button.js b/src/components/retweet_button/retweet_button.js
index 5ee4179a..2103fd0b 100644
--- a/src/components/retweet_button/retweet_button.js
+++ b/src/components/retweet_button/retweet_button.js
@@ -24,11 +24,6 @@ const RetweetButton = {
}
},
computed: {
- classes () {
- return {
- '-repeated': this.status.repeated
- }
- },
mergedConfig () {
return this.$store.getters.mergedConfig
}
diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue
index b234f3d9..859ce499 100644
--- a/src/components/retweet_button/retweet_button.vue
+++ b/src/components/retweet_button/retweet_button.vue
@@ -1,33 +1,38 @@
<template>
- <div v-if="loggedIn">
- <template v-if="visibility !== 'private' && visibility !== 'direct'">
+ <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()"
+ >
<FAIcon
- :class="classes"
- class="RetweetButton fa-scale-110 fa-old-padding -interactive"
+ class="fa-scale-110 fa-old-padding"
icon="retweet"
:spin="animated"
- :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>
+ </button>
+ <span v-else-if="loggedIn">
<FAIcon
- :class="classes"
- class="RetweetButton fa-scale-110 fa-old-padding"
+ class="fa-scale-110 fa-old-padding"
icon="lock"
:title="$t('timeline.no_retweet_hint')"
/>
- </template>
- </div>
- <div v-else-if="!loggedIn">
- <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-else>
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ icon="retweet"
+ :title="$t('tool_tip.repeat')"
+ />
+ </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;
- animation-duration: 0.6s;
+ display: flex;
- &:hover {
+ > :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 .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>
diff --git a/src/components/scope_selector/scope_selector.vue b/src/components/scope_selector/scope_selector.vue
index a22a4fda..66ac612e 100644
--- a/src/components/scope_selector/scope_selector.vue
+++ b/src/components/scope_selector/scope_selector.vue
@@ -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>
diff --git a/src/components/search/search.vue b/src/components/search/search.vue
index 665390f9..a6503c9f 100644
--- a/src/components/search/search.vue
+++ b/src/components/search/search.vue
@@ -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" />
diff --git a/src/components/search_bar/search_bar.vue b/src/components/search_bar/search_bar.vue
index 89a601c8..6cf9179e 100644
--- a/src/components/search_bar/search_bar.vue
+++ b/src/components/search_bar/search_bar.vue
@@ -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
- fixed-width
- class="fa-scale-110 fa-old-padding"
- icon="search"
@click.prevent.stop="toggleHidden"
- /></a>
+ >
+ <FAIcon
+ fixed-width
+ class="fa-scale-110 fa-old-padding"
+ icon="search"
+ />
+ </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);
}
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index 6bc64ed0..552ca41f 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -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') }}
diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
index 5a1cf2c0..63d36bf9 100644
--- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
+++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
@@ -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') }}
diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue
index 86eed3f5..8f8fe48e 100644
--- a/src/components/settings_modal/tabs/notifications_tab.vue
+++ b/src/components/settings_modal/tabs/notifications_tab.vue
@@ -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') }}
diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js
index a3e4feaf..a4fed629 100644
--- a/src/components/settings_modal/tabs/profile_tab.js
+++ b/src/components/settings_modal/tabs/profile_tab.js
@@ -68,8 +68,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 +78,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
diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue
index d62bc392..50d3ee63 100644
--- a/src/components/settings_modal/tabs/profile_tab.vue
+++ b/src/components/settings_modal/tabs/profile_tab.vue
@@ -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,7 +224,7 @@
/>
<button
v-else-if="bannerPreview"
- class="btn btn-default"
+ class="btn button-default"
@click="submitBanner(banner)"
>
{{ $t('general.submit') }}
@@ -274,7 +274,7 @@
/>
<button
v-else-if="backgroundPreview"
- class="btn btn-default"
+ class="btn button-default"
@click="submitBackground(background)"
>
{{ $t('general.submit') }}
diff --git a/src/components/settings_modal/tabs/security_tab/confirm.vue b/src/components/settings_modal/tabs/security_tab/confirm.vue
index 69b3811b..38c2a610 100644
--- a/src/components/settings_modal/tabs/security_tab/confirm.vue
+++ b/src/components/settings_modal/tabs/security_tab/confirm.vue
@@ -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"
>
diff --git a/src/components/settings_modal/tabs/security_tab/mfa.vue b/src/components/settings_modal/tabs/security_tab/mfa.vue
index 7aca3c8d..455d17b6 100644
--- a/src/components/settings_modal/tabs/security_tab/mfa.vue
+++ b/src/components/settings_modal/tabs/security_tab/mfa.vue
@@ -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') }}
diff --git a/src/components/settings_modal/tabs/security_tab/mfa_totp.vue b/src/components/settings_modal/tabs/security_tab/mfa_totp.vue
index c6f2cc7b..8e767bd0 100644
--- a/src/components/settings_modal/tabs/security_tab/mfa_totp.vue
+++ b/src/components/settings_modal/tabs/security_tab/mfa_totp.vue
@@ -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"
>
diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue
index 3d32d73d..56bea1f4 100644
--- a/src/components/settings_modal/tabs/security_tab/security_tab.vue
+++ b/src/components/settings_modal/tabs/security_tab/security_tab.vue
@@ -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') }}
diff --git a/src/components/settings_modal/tabs/theme_tab/preview.vue b/src/components/settings_modal/tabs/theme_tab/preview.vue
index 02fea0b6..7ac7b9d3 100644
--- a/src/components/settings_modal/tabs/theme_tab/preview.vue
+++ b/src/components/settings_modal/tabs/theme_tab/preview.vue
@@ -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>
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
index 280e1955..4ab793d6 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
@@ -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') }}
@@ -806,7 +806,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 +936,7 @@
/>
</div>
<button
- class="btn"
+ class="btn button-default"
@click="clearShadows"
>
{{ $t('settings.style.switcher.clear_all') }}
@@ -980,7 +980,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 +1017,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') }}
diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue
index 78f0e544..37d491f0 100644
--- a/src/components/shadow_control/shadow_control.vue
+++ b/src/components/shadow_control/shadow_control.vue
@@ -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"
>
diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue
index 28c888fe..695ae03b 100644
--- a/src/components/side_drawer/side_drawer.vue
+++ b/src/components/side_drawer/side_drawer.vue
@@ -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;
diff --git a/src/components/status/status.scss b/src/components/status/status.scss
index 0a94de32..70c6d03d 100644
--- a/src/components/status/status.scss
+++ b/src/components/status/status.scss
@@ -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 {
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 21412faa..896635ee 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -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>
@@ -201,9 +200,9 @@
icon="external-link-square-alt"
/>
</a>
- <a
+ <button
v-if="expandable && !isPreview"
- href="#"
+ class="button-unstyled"
title="Expand"
@click.prevent="toggleExpanded"
>
@@ -211,17 +210,17 @@
class="fa-scale-110 fa-old-padding"
icon="plus-square"
/>
- </a>
- <a
+ </button>
+ <button
v-if="unmuted"
- href="#"
+ class="button-unstyled"
@click.prevent="toggleMute"
>
<FAIcon
icon="eye-slash"
class="fa-scale-110 fa-old-padding"
/>
- </a>
+ </button>
</span>
</div>
@@ -237,9 +236,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 +251,7 @@
>
{{ $t('status.reply_to') }}
</span>
- </a>
+ </button>
</StatusPopover>
<span
@@ -286,11 +284,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>
diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue
index 321cd477..90bfaf40 100644
--- a/src/components/status_content/status_content.vue
+++ b/src/components/status_content/status_content.vue
@@ -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">
diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js
index 6e6e8193..76e7ef03 100644
--- a/src/components/tab_switcher/tab_switcher.js
+++ b/src/components/tab_switcher/tab_switcher.js
@@ -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')
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index cba46daf..665d195e 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -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
diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue
index 04859852..0326342b 100644
--- a/src/components/timeline/timeline.vue
+++ b/src/components/timeline/timeline.vue
@@ -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"
diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js
index 4ccd52b4..ef8a5813 100644
--- a/src/components/timeline_menu/timeline_menu.js
+++ b/src/components/timeline_menu/timeline_menu.js
@@ -19,7 +19,7 @@ library.add(
faChevronDown
)
-// Route -> i18n key mapping, exported andnot in the computed
+// Route -> i18n key mapping, exported and not in the computed
// because nav panel benefits from the same information.
export const timelineNames = () => {
return {
@@ -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'
}
}
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index f916af9d..16dd5249 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -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') }}
diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue
index 2a8d8d48..fb43094f 100644
--- a/src/components/user_reporting_modal/user_reporting_modal.vue
+++ b/src/components/user_reporting_modal/user_reporting_modal.vue
@@ -29,7 +29,7 @@
</div>
<div>
<button
- class="btn btn-default"
+ class="btn button-default"
:disabled="processing"
@click="reportUser"
>
diff --git a/src/components/video_attachment/video_attachment.vue b/src/components/video_attachment/video_attachment.vue
index a4bf01e8..8a3ea1e3 100644
--- a/src/components/video_attachment/video_attachment.vue
+++ b/src/components/video_attachment/video_attachment.vue
@@ -1,6 +1,7 @@
<template>
<video
class="video"
+ preload="metadata"
:src="attachment.url"
:loop="loopVideo"
:controls="controls"
diff --git a/src/hocs/with_load_more/with_load_more.js b/src/hocs/with_load_more/with_load_more.js
index afb51a0f..7df9dbb2 100644
--- a/src/hocs/with_load_more/with_load_more.js
+++ b/src/hocs/with_load_more/with_load_more.js
@@ -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>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index d3d57562..ef23efd6 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -130,6 +130,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",
@@ -376,7 +377,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",
@@ -634,7 +635,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",
@@ -666,7 +667,8 @@
"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"
},
"user_card": {
"approve": "Approve",
diff --git a/src/i18n/eo.json b/src/i18n/eo.json
index 1247d50d..b0a15cfe 100644
--- a/src/i18n/eo.json
+++ b/src/i18n/eo.json
@@ -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",
diff --git a/src/i18n/es.json b/src/i18n/es.json
index 6889df9a..0c2cc3e9 100644
--- a/src/i18n/es.json
+++ b/src/i18n/es.json
@@ -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",
diff --git a/src/i18n/he.json b/src/i18n/he.json
index 7f2bf58f..4b920536 100644
--- a/src/i18n/he.json
+++ b/src/i18n/he.json
@@ -390,5 +390,13 @@
"GiB": "GiB",
"TiB": "TiB"
}
+ },
+ "about": {
+ "mrf": {
+ "keyword": {
+ "keyword_policies": "פוליסת מילות מפתח"
+ },
+ "federation": "פדרציה"
+ }
}
}
diff --git a/src/i18n/it.json b/src/i18n/it.json
index 67e92b32..58dafca5 100644
--- a/src/i18n/it.json
+++ b/src/i18n/it.json
@@ -50,7 +50,8 @@
"follow_request": "vuole seguirti",
"no_more_notifications": "Fine delle notifiche",
"migrated_to": "è migrato verso",
- "reacted_with": "ha reagito con {0}"
+ "reacted_with": "ha reagito con {0}",
+ "error": "Errore nel caricare le notifiche: {0}"
},
"settings": {
"attachments": "Allegati",
@@ -427,7 +428,8 @@
"repeated": "condiviso",
"no_statuses": "Nessun messaggio",
"no_more_statuses": "Fine dei messaggi",
- "reload": "Ricarica"
+ "reload": "Ricarica",
+ "error": "Errore nel caricare la sequenza: {0}"
},
"user_card": {
"follow": "Segui",
@@ -703,7 +705,8 @@
"delete_confirm": "Vuoi veramente eliminare questo messaggio?",
"unbookmark": "Rimuovi segnalibro",
"bookmark": "Aggiungi segnalibro",
- "status_deleted": "Questo messagio è stato cancellato"
+ "status_deleted": "Questo messagio è stato cancellato",
+ "nsfw": "Pruriginoso"
},
"time": {
"years_short": "{0}a",
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index 8f421b50..f636bdf8 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -18,7 +18,13 @@
"generic_error": "Произошла ошибка",
"optional": "не обязательно",
"show_less": "Показать меньше",
- "show_more": "Показать больше"
+ "show_more": "Показать больше",
+ "peek": "Взглянуть",
+ "dismiss": "Закрыть",
+ "retry": "Попробуйте еще раз",
+ "error_retry": "Пожалуйста попробуйте еще раз",
+ "close": "Закрыть",
+ "loading": "Загрузка…"
},
"login": {
"login": "Войти",
@@ -468,7 +474,9 @@
"media_proxy": "Прокси для внешних вложений",
"text_limit": "Лимит символов",
"title": "Особенности",
- "gopher": "Gopher"
+ "gopher": "Gopher",
+ "who_to_follow": "Предложения кого читать",
+ "pleroma_chat_messages": "Pleroma Чат"
},
"tool_tip": {
"accept_follow_request": "Принять запрос на чтение",
@@ -477,6 +485,7 @@
"image_cropper": {
"save_without_cropping": "Сохранить не обрезая",
"save": "Сохранить",
- "crop_picture": "Обрезать картинку"
+ "crop_picture": "Обрезать картинку",
+ "cancel": "Отмена"
}
}
diff --git a/src/i18n/uk.json b/src/i18n/uk.json
new file mode 100644
index 00000000..73006e6e
--- /dev/null
+++ b/src/i18n/uk.json
@@ -0,0 +1,99 @@
+{
+ "general": {
+ "dismiss": "Закрити",
+ "close": "Закрити",
+ "verify": "Перевірити",
+ "confirm": "Підтвердити",
+ "enable": "Увімкнути",
+ "disable": "Вимкнути",
+ "cancel": "Скасувати",
+ "show_less": "Показати менше",
+ "show_more": "Показати більше",
+ "optional": "необов'язково",
+ "retry": "Спробуйте ще раз",
+ "error_retry": "Будь ласка, спробуйте ще раз",
+ "generic_error": "Виникла помилка",
+ "loading": "Завантаження…",
+ "more": "Більше",
+ "submit": "Відправити",
+ "apply": "Застосувати",
+ "peek": "Глянути"
+ },
+ "finder": {
+ "error_fetching_user": "Користувача не знайдено",
+ "find_user": "Знайти користувача"
+ },
+ "features_panel": {
+ "gopher": "Gopher",
+ "pleroma_chat_messages": "Чат Pleroma",
+ "chat": "Чат",
+ "who_to_follow": "Кого відстежувати",
+ "title": "Особливості",
+ "scope_options": "Параметри осягу",
+ "media_proxy": "Посередник медіа-даних",
+ "text_limit": "Ліміт символів"
+ },
+ "exporter": {
+ "processing": "Опрацьовую, скоро ви зможете завантажити файл",
+ "export": "Експорт"
+ },
+ "domain_mute_card": {
+ "unmute_progress": "Вимикаю…",
+ "unmute": "Вимкнути ігнорування",
+ "mute_progress": "Вмикаю…",
+ "mute": "Ігнорувати"
+ },
+ "shoutbox": {
+ "title": "Для воплів"
+ },
+ "about": {
+ "staff": "Адміністрація",
+ "mrf": {
+ "simple": {
+ "media_nsfw_desc": "Даний інстанс примусово позначає вкладення з наступних інстансів як NSFW:",
+ "media_nsfw": "Примусове визначення вкладення як дратівливого",
+ "media_removal_desc": "Поточний інстанс видаляє вкладення на перелічених інстансах:",
+ "media_removal": "Видалення вкладень",
+ "ftl_removal_desc": "Цей інстанс видаляє перелічені інстанси з \"Усієї відомої мережі\":",
+ "ftl_removal": "Видалення з \"Вся відома мережа\"",
+ "quarantine_desc": "Поточний інстанс буде надсилати тільки публічні пости наступним інстансам:",
+ "quarantine": "Карантин",
+ "reject_desc": "Поточний інстанс не прийматиме повідомлення з перелічених інстансів:",
+ "accept": "Прийняти",
+ "reject": "Відхилити",
+ "accept_desc": "Поточний інстанс приймає повідомлення тільки з перелічених інстансів:",
+ "simple_policies": "Правила поточного інстансу"
+ },
+ "mrf_policies_desc": "Правила MRF розповсюджуються на данний інстанс. Наступні правила активні:",
+ "mrf_policies": "Активні правила MRF (модуль переписування повідомлень)",
+ "keyword": {
+ "is_replaced_by": "→",
+ "replace": "Замінити",
+ "reject": "Відхилити",
+ "ftl_removal": "Прибрати з федеративної стрічки",
+ "keyword_policies": "Політика щодо ключових слів"
+ },
+ "federation": "Федерація"
+ }
+ },
+ "login": {
+ "hint": "Увійдіть, щоб доєднатися до дискусії",
+ "username": "Ім'я користувача",
+ "register": "Зареєструватись",
+ "password": "Пароль",
+ "logout": "Вийти",
+ "description": "Увійти за допомогою OAuth",
+ "login": "Увійти"
+ },
+ "importer": {
+ "error": "Під час імпортування файлу сталася помилка.",
+ "success": "Імпортовано успішно.",
+ "submit": "Відправити"
+ },
+ "image_cropper": {
+ "cancel": "Відмінити",
+ "save_without_cropping": "Зберегти не обрізаючи",
+ "crop_picture": "Обрізати малюнок",
+ "save": "Зберегти"
+ }
+}
diff --git a/src/i18n/zh.json b/src/i18n/zh.json
index 09e2ab0d..7f8e5593 100644
--- a/src/i18n/zh.json
+++ b/src/i18n/zh.json
@@ -22,7 +22,7 @@
},
"general": {
"apply": "应用",
- "submit": "提交",
+ "submit": "发送",
"more": "更多",
"generic_error": "发生了一个错误",
"optional": "可选",
@@ -297,7 +297,7 @@
"hide_follows_description": "不要显示我所关注的人",
"hide_followers_description": "不要显示关注我的人",
"show_admin_badge": "显示管理徽章",
- "show_moderator_badge": "显示版主徽章",
+ "show_moderator_badge": "在我的个人资料中显示监察员标志",
"nsfw_clickthrough": "将不和谐附件隐藏,点击才能打开",
"oauth_tokens": "OAuth令牌",
"token": "令牌",
@@ -655,8 +655,8 @@
"moderation": "权限",
"grant_admin": "赋予管理权限",
"revoke_admin": "撤销管理权限",
- "grant_moderator": "赋予版主权限",
- "revoke_moderator": "撤销版主权限",
+ "grant_moderator": "赋予监察员权限",
+ "revoke_moderator": "撤销监察员权限",
"activate_account": "激活账号",
"deactivate_account": "关闭账号",
"delete_account": "删除账号",
@@ -683,7 +683,7 @@
},
"user_reporting": {
"title": "报告 {0}",
- "add_comment_description": "此报告会发送给您的实例管理员。您可以在下面提供更多详细信息解释报告的缘由:",
+ "add_comment_description": "此报告会发送给您的实例监察员。您可以在下面提供更多详细信息解释报告的缘由:",
"additional_comments": "其它信息",
"forward_description": "这个账号是从另外一个服务器。同时发送一个副本到那里?",
"forward_to": "转发 {0}",
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index e673141d..33c68c57 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -39,8 +39,7 @@ const emptyNotifications = () => ({
minId: Number.POSITIVE_INFINITY,
data: [],
idStore: {},
- loading: false,
- error: false
+ loading: false
})
export const defaultState = () => ({
@@ -50,8 +49,6 @@ export const defaultState = () => ({
maxId: 0,
notifications: emptyNotifications(),
favorites: new Set(),
- error: false,
- errorData: null,
timelines: {
mentions: emptyTl(),
public: emptyTl(),
@@ -462,18 +459,9 @@ export const mutations = {
const newStatus = state.allStatusesObject[id]
newStatus.nsfw = nsfw
},
- setError (state, { value }) {
- state.error = value
- },
- setErrorData (state, { value }) {
- state.errorData = value
- },
setNotificationsLoading (state, { value }) {
state.notifications.loading = value
},
- setNotificationsError (state, { value }) {
- state.notifications.error = value
- },
setNotificationsSilence (state, { value }) {
state.notifications.desktopNotificationSilence = value
},
@@ -588,18 +576,9 @@ const statuses = {
}
commit('addNewNotifications', { dispatch, notifications, older, rootGetters, newNotificationSideEffects })
},
- setError ({ rootState, commit }, { value }) {
- commit('setError', { value })
- },
- setErrorData ({ rootState, commit }, { value }) {
- commit('setErrorData', { value })
- },
setNotificationsLoading ({ rootState, commit }, { value }) {
commit('setNotificationsLoading', { value })
},
- setNotificationsError ({ rootState, commit }, { value }) {
- commit('setNotificationsError', { value })
- },
setNotificationsSilence ({ rootState, commit }, { value }) {
commit('setNotificationsSilence', { value })
},
diff --git a/src/modules/users.js b/src/modules/users.js
index 9245db5c..655db4c7 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -137,11 +137,11 @@ export const mutations = {
},
saveFriendIds (state, { id, friendIds }) {
const user = state.usersObject[id]
- user.friendIds = uniq(concat(user.friendIds, friendIds))
+ user.friendIds = uniq(concat(user.friendIds || [], friendIds))
},
saveFollowerIds (state, { id, followerIds }) {
const user = state.usersObject[id]
- user.followerIds = uniq(concat(user.followerIds, followerIds))
+ user.followerIds = uniq(concat(user.followerIds || [], followerIds))
},
// Because frontend doesn't have a reason to keep these stuff in memory
// outside of viewing someones user profile.
@@ -202,7 +202,9 @@ export const mutations = {
},
setPinnedToUser (state, status) {
const user = state.usersObject[status.user.id]
+ user.pinnedStatusIds = user.pinnedStatusIds || []
const index = user.pinnedStatusIds.indexOf(status.id)
+
if (status.pinned && index === -1) {
user.pinnedStatusIds.push(status.id)
} else if (!status.pinned && index !== -1) {
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 22b5e8ba..8da933c4 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -560,7 +560,7 @@ const fetchTimeline = ({
})
.then((data) => data.json())
.then((data) => {
- if (!data.error) {
+ if (!data.errors) {
return { data: data.map(isNotifications ? parseNotification : parseStatus), pagination }
} else {
data.status = status
diff --git a/src/services/chat_service/chat_service.js b/src/services/chat_service/chat_service.js
index 1fc4e390..e653ebc1 100644
--- a/src/services/chat_service/chat_service.js
+++ b/src/services/chat_service/chat_service.js
@@ -21,7 +21,7 @@ const clear = (storage) => {
failedMessageIds.push(message.id)
} else {
delete storage.idIndex[message.id]
- delete storage.idempotencyKeyIndex[message.id]
+ delete storage.idempotencyKeyIndex[message.idempotency_key]
}
}
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 093472e4..0fd01176 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -2,6 +2,15 @@ import escape from 'escape-html'
import parseLinkHeader from 'parse-link-header'
import { isStatusNotification } from '../notification_utils/notification_utils.js'
+/** NOTICE! **
+ * Do not initialize UI-generated data here.
+ * It will override existing data.
+ *
+ * i.e. user.pinnedStatusIds was set to [] here
+ * UI code would update it with data but upon next user fetch
+ * it would be reverted back to []
+ */
+
const qvitterStatusType = (status) => {
if (status.is_post_verb) {
return 'status'
@@ -173,9 +182,6 @@ export const parseUser = (data) => {
output.locked = data.locked
output.followers_count = data.followers_count
output.statuses_count = data.statuses_count
- output.friendIds = []
- output.followerIds = []
- output.pinnedStatusIds = []
if (data.pleroma) {
output.follow_request_count = data.pleroma.follow_request_count
diff --git a/src/services/favicon_service/favicon_service.js b/src/services/favicon_service/favicon_service.js
new file mode 100644
index 00000000..d1ddee41
--- /dev/null
+++ b/src/services/favicon_service/favicon_service.js
@@ -0,0 +1,61 @@
+import { find } from 'lodash'
+
+const createFaviconService = () => {
+ let favimg, favcanvas, favcontext, favicon
+ const faviconWidth = 128
+ const faviconHeight = 128
+ const badgeRadius = 32
+
+ const initFaviconService = () => {
+ const nodes = document.getElementsByTagName('link')
+ favicon = find(nodes, node => node.rel === 'icon')
+ if (favicon) {
+ favcanvas = document.createElement('canvas')
+ favcanvas.width = faviconWidth
+ favcanvas.height = faviconHeight
+ favimg = new Image()
+ favimg.src = favicon.href
+ favcontext = favcanvas.getContext('2d')
+ }
+ }
+
+ const isImageLoaded = (img) => img.complete && img.naturalHeight !== 0
+
+ const clearFaviconBadge = () => {
+ if (!favimg || !favcontext || !favicon) return
+
+ favcontext.clearRect(0, 0, faviconWidth, faviconHeight)
+ if (isImageLoaded(favimg)) {
+ favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
+ }
+ favicon.href = favcanvas.toDataURL('image/png')
+ }
+
+ const drawFaviconBadge = () => {
+ if (!favimg || !favcontext || !favcontext) return
+
+ clearFaviconBadge()
+
+ const style = getComputedStyle(document.body)
+ const badgeColor = `${style.getPropertyValue('--badgeNotification') || 'rgb(240, 100, 100)'}`
+
+ if (isImageLoaded(favimg)) {
+ favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
+ }
+ favcontext.fillStyle = badgeColor
+ favcontext.beginPath()
+ favcontext.arc(faviconWidth - badgeRadius, badgeRadius, badgeRadius, 0, 2 * Math.PI, false)
+ favcontext.fill()
+ favicon.href = favcanvas.toDataURL('image/png')
+ }
+
+ return {
+ initFaviconService,
+ clearFaviconBadge,
+ drawFaviconBadge
+ }
+}
+
+const FaviconService = createFaviconService()
+
+export default FaviconService
diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js
index c908b644..beeb167c 100644
--- a/src/services/notifications_fetcher/notifications_fetcher.service.js
+++ b/src/services/notifications_fetcher/notifications_fetcher.service.js
@@ -2,7 +2,6 @@ import apiService from '../api/api.service.js'
import { promiseInterval } from '../promise_interval/promise_interval.js'
const update = ({ store, notifications, older }) => {
- store.dispatch('setNotificationsError', { value: false })
store.dispatch('addNewNotifications', { notifications, older })
}
@@ -47,11 +46,22 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
const fetchNotifications = ({ store, args, older }) => {
return apiService.fetchTimeline(args)
- .then(({ data: notifications }) => {
+ .then((response) => {
+ if (response.errors) {
+ throw new Error(`${response.status} ${response.statusText}`)
+ }
+ const notifications = response.data
update({ store, notifications, older })
return notifications
- }, () => store.dispatch('setNotificationsError', { value: true }))
- .catch(() => store.dispatch('setNotificationsError', { value: true }))
+ })
+ .catch((error) => {
+ store.dispatch('pushGlobalNotice', {
+ level: 'error',
+ messageKey: 'notifications.error',
+ messageArgs: [error.message],
+ timeout: 5000
+ })
+ })
}
const startFetching = ({ credentials, store }) => {
diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js
index 72ea4890..921df3ed 100644
--- a/src/services/timeline_fetcher/timeline_fetcher.service.js
+++ b/src/services/timeline_fetcher/timeline_fetcher.service.js
@@ -6,9 +6,6 @@ import { promiseInterval } from '../promise_interval/promise_interval.js'
const update = ({ store, statuses, timeline, showImmediately, userId, pagination }) => {
const ccTimeline = camelCase(timeline)
- store.dispatch('setError', { value: false })
- store.dispatch('setErrorData', { value: null })
-
store.dispatch('addNewStatuses', {
timeline: ccTimeline,
userId,
@@ -52,9 +49,8 @@ const fetchAndUpdate = ({
return apiService.fetchTimeline(args)
.then(response => {
- if (response.error) {
- store.dispatch('setErrorData', { value: response })
- return
+ if (response.errors) {
+ throw new Error(`${response.status} ${response.statusText}`)
}
const { data: statuses, pagination } = response
@@ -63,7 +59,15 @@ const fetchAndUpdate = ({
}
update({ store, statuses, timeline, showImmediately, userId, pagination })
return { statuses, pagination }
- }, () => store.dispatch('setError', { value: true }))
+ })
+ .catch((error) => {
+ store.dispatch('pushGlobalNotice', {
+ level: 'error',
+ messageKey: 'timeline.error',
+ messageArgs: [error.message],
+ timeout: 5000
+ })
+ })
}
const startFetching = ({ timeline = 'friends', credentials, store, userId = false, tag = false }) => {