forked from AkkomaGang/akkoma-fe
Merge branch 'develop' of git.pleroma.social:pleroma/pleroma-fe into develop
This commit is contained in:
commit
d1bffd7659
59 changed files with 609 additions and 143 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -4,12 +4,29 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Added a quick settings to timeline header for easier access
|
||||||
|
- Added option to mark posts as sensitive by default
|
||||||
|
|
||||||
|
## [2.3.0] - 2021-03-01
|
||||||
### Fixed
|
### Fixed
|
||||||
- Button to remove uploaded media in post status form is now properly placed and sized.
|
- Button to remove uploaded media in post status form is now properly placed and sized.
|
||||||
- Fixed shoutbox not working in mobile layout
|
- Fixed shoutbox not working in mobile layout
|
||||||
|
- Fixed missing highlighted border in expanded conversations again
|
||||||
|
- Fixed some UI jumpiness when opening images particularly in chat view
|
||||||
|
- Fixed chat unread badge looking weird
|
||||||
|
- Fixed punycode names not working properly
|
||||||
|
- Fixed notifications crashing on an invalid notification
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Display 'people voted' instead of 'votes' for multi-choice polls
|
- Display 'people voted' instead of 'votes' for multi-choice polls
|
||||||
|
- Optimized chat to not get horrible performance after keeping the same chat open for a long time
|
||||||
|
- When opening emoji picker or react picker, it automatically focuses the search field
|
||||||
|
- Language picker now uses native language names
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added reason field for registration when approval is required
|
||||||
|
- Group staff members by role in the About page
|
||||||
|
|
||||||
## [2.2.3] - 2021-01-18
|
## [2.2.3] - 2021-01-18
|
||||||
### Added
|
### Added
|
||||||
|
@ -21,7 +38,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Don't filter own posts when they hit your wordfilter
|
- Don't filter own posts when they hit your wordfilter
|
||||||
- Language picker now uses native language names
|
|
||||||
|
|
||||||
|
|
||||||
## [2.2.2] - 2020-12-22
|
## [2.2.2] - 2020-12-22
|
||||||
|
@ -31,7 +47,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Added some missing unicode emoji
|
- Added some missing unicode emoji
|
||||||
- Added the upload limit to the Features panel in the About page
|
- Added the upload limit to the Features panel in the About page
|
||||||
- Support for solid color wallpaper, instance doesn't have to define a wallpaper anymore
|
- Support for solid color wallpaper, instance doesn't have to define a wallpaper anymore
|
||||||
- Group staff members by role in the About page
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed the occasional bug where screen would scroll 1px when typing into a reply form
|
- Fixed the occasional bug where screen would scroll 1px when typing into a reply form
|
||||||
|
|
|
@ -103,7 +103,7 @@
|
||||||
"selenium-server": "2.53.1",
|
"selenium-server": "2.53.1",
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"serviceworker-webpack-plugin": "^1.0.0",
|
"serviceworker-webpack-plugin": "^1.0.0",
|
||||||
"shelljs": "^0.7.4",
|
"shelljs": "^0.8.4",
|
||||||
"sinon": "^2.1.0",
|
"sinon": "^2.1.0",
|
||||||
"sinon-chai": "^2.8.0",
|
"sinon-chai": "^2.8.0",
|
||||||
"stylelint": "^13.6.1",
|
"stylelint": "^13.6.1",
|
||||||
|
|
|
@ -586,6 +586,7 @@ nav {
|
||||||
color: var(--faint, $fallback--faint);
|
color: var(--faint, $fallback--faint);
|
||||||
box-shadow: 0px 0px 4px rgba(0,0,0,.6);
|
box-shadow: 0px 0px 4px rgba(0,0,0,.6);
|
||||||
box-shadow: var(--topBarShadow);
|
box-shadow: var(--topBarShadow);
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter-active, .fade-leave-active {
|
.fade-enter-active, .fade-leave-active {
|
||||||
|
@ -878,6 +879,11 @@ nav {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
// Get rid of scrollbar on body as scrolling happens on different element
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures the fixed position of the mobile browser bars on scroll up / down events.
|
// Ensures the fixed position of the mobile browser bars on scroll up / down events.
|
||||||
// Prevents the mobile browser bars from overlapping or hiding the message posting form.
|
// Prevents the mobile browser bars from overlapping or hiding the message posting form.
|
||||||
@media all and (max-width: 800px) {
|
@media all and (max-width: 800px) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ const getInstanceConfig = async ({ store }) => {
|
||||||
const vapidPublicKey = data.pleroma.vapid_public_key
|
const vapidPublicKey = data.pleroma.vapid_public_key
|
||||||
|
|
||||||
store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit })
|
store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit })
|
||||||
|
store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.approval_required })
|
||||||
|
|
||||||
if (vapidPublicKey) {
|
if (vapidPublicKey) {
|
||||||
store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey })
|
store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey })
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
class="basic-user-card-screen-name"
|
class="basic-user-card-screen-name"
|
||||||
:to="userProfileLink(user)"
|
:to="userProfileLink(user)"
|
||||||
>
|
>
|
||||||
@{{ user.screen_name }}
|
@{{ user.screen_name_ui }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<slot />
|
<slot />
|
||||||
|
|
|
@ -73,7 +73,7 @@ const Chat = {
|
||||||
},
|
},
|
||||||
formPlaceholder () {
|
formPlaceholder () {
|
||||||
if (this.recipient) {
|
if (this.recipient) {
|
||||||
return this.$t('chats.message_user', { nickname: this.recipient.screen_name })
|
return this.$t('chats.message_user', { nickname: this.recipient.screen_name_ui })
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
@ -234,6 +234,13 @@ const Chat = {
|
||||||
const scrollable = this.$refs.scrollable
|
const scrollable = this.$refs.scrollable
|
||||||
return scrollable && scrollable.scrollTop <= 0
|
return scrollable && scrollable.scrollTop <= 0
|
||||||
},
|
},
|
||||||
|
cullOlderCheck () {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
|
||||||
|
this.$store.dispatch('cullOlderMessages', this.currentChatMessageService.chatId)
|
||||||
|
}
|
||||||
|
}, 5000)
|
||||||
|
},
|
||||||
handleScroll: _.throttle(function () {
|
handleScroll: _.throttle(function () {
|
||||||
if (!this.currentChat) { return }
|
if (!this.currentChat) { return }
|
||||||
|
|
||||||
|
@ -241,6 +248,7 @@ const Chat = {
|
||||||
this.fetchChat({ maxId: this.currentChatMessageService.minId })
|
this.fetchChat({ maxId: this.currentChatMessageService.minId })
|
||||||
} else if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
|
} else if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
|
||||||
this.jumpToBottomButtonVisible = false
|
this.jumpToBottomButtonVisible = false
|
||||||
|
this.cullOlderCheck()
|
||||||
if (this.newMessageCount > 0) {
|
if (this.newMessageCount > 0) {
|
||||||
// Use a delay before marking as read to prevent situation where new messages
|
// Use a delay before marking as read to prevent situation where new messages
|
||||||
// arrive just as you're leaving the view and messages that you didn't actually
|
// arrive just as you're leaving the view and messages that you didn't actually
|
||||||
|
|
|
@ -98,10 +98,10 @@
|
||||||
.unread-message-count {
|
.unread-message-count {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, 0);
|
|
||||||
border-radius: 100%;
|
|
||||||
margin-top: -1rem;
|
margin-top: -1rem;
|
||||||
padding: 0;
|
padding: 0.1em;
|
||||||
|
border-radius: 50px;
|
||||||
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-loading-error {
|
.chat-loading-error {
|
||||||
|
|
|
@ -12,7 +12,7 @@ export default Vue.component('chat-title', {
|
||||||
],
|
],
|
||||||
computed: {
|
computed: {
|
||||||
title () {
|
title () {
|
||||||
return this.user ? this.user.screen_name : ''
|
return this.user ? this.user.screen_name_ui : ''
|
||||||
},
|
},
|
||||||
htmlTitle () {
|
htmlTitle () {
|
||||||
return this.user ? this.user.name_html : ''
|
return this.user ? this.user.name_html : ''
|
||||||
|
|
|
@ -50,7 +50,6 @@
|
||||||
|
|
||||||
.Conversation {
|
.Conversation {
|
||||||
.conversation-status {
|
.conversation-status {
|
||||||
border-left: none;
|
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
border-bottom-style: solid;
|
border-bottom-style: solid;
|
||||||
border-bottom-color: var(--border, $fallback--border);
|
border-bottom-color: var(--border, $fallback--border);
|
||||||
|
|
|
@ -194,11 +194,18 @@ const EmojiInput = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
focusPickerInput () {
|
||||||
|
const pickerEl = this.$refs.picker.$el
|
||||||
|
if (!pickerEl) return
|
||||||
|
const pickerInput = pickerEl.querySelector('input')
|
||||||
|
if (pickerInput) pickerInput.focus()
|
||||||
|
},
|
||||||
triggerShowPicker () {
|
triggerShowPicker () {
|
||||||
this.showPicker = true
|
this.showPicker = true
|
||||||
this.$refs.picker.startEmojiLoad()
|
this.$refs.picker.startEmojiLoad()
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollIntoView()
|
this.scrollIntoView()
|
||||||
|
this.focusPickerInput()
|
||||||
})
|
})
|
||||||
// This temporarily disables "click outside" handler
|
// This temporarily disables "click outside" handler
|
||||||
// since external trigger also means click originates
|
// since external trigger also means click originates
|
||||||
|
@ -214,6 +221,7 @@ const EmojiInput = {
|
||||||
if (this.showPicker) {
|
if (this.showPicker) {
|
||||||
this.scrollIntoView()
|
this.scrollIntoView()
|
||||||
this.$refs.picker.startEmojiLoad()
|
this.$refs.picker.startEmojiLoad()
|
||||||
|
this.$nextTick(this.focusPickerInput)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replace (replacement) {
|
replace (replacement) {
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
<button
|
<button
|
||||||
v-if="!hideEmojiButton"
|
v-if="!hideEmojiButton"
|
||||||
class="button-unstyled emoji-picker-icon"
|
class="button-unstyled emoji-picker-icon"
|
||||||
@click.prevent="togglePicker"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click.prevent="togglePicker"
|
||||||
>
|
>
|
||||||
<FAIcon :icon="['far', 'smile-beam']" />
|
<FAIcon :icon="['far', 'smile-beam']" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -116,8 +116,8 @@ export const suggestUsers = ({ dispatch, state }) => {
|
||||||
|
|
||||||
return diff + nameAlphabetically + screenNameAlphabetically
|
return diff + nameAlphabetically + screenNameAlphabetically
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
}).map(({ screen_name, name, profile_image_url_original }) => ({
|
}).map(({ screen_name, screen_name_ui, name, profile_image_url_original }) => ({
|
||||||
displayText: screen_name,
|
displayText: screen_name_ui,
|
||||||
detailText: name,
|
detailText: name,
|
||||||
imageUrl: profile_image_url_original,
|
imageUrl: profile_image_url_original,
|
||||||
replacement: '@' + screen_name + ' '
|
replacement: '@' + screen_name + ' '
|
||||||
|
|
|
@ -73,11 +73,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes media-fadein {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.modal-image {
|
.modal-image {
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5);
|
box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5);
|
||||||
image-orientation: from-image; // NOTE: only FF supports this
|
image-orientation: from-image; // NOTE: only FF supports this
|
||||||
|
animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-view-button-arrow {
|
.modal-view-button-arrow {
|
||||||
|
|
|
@ -25,16 +25,16 @@
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled -link"
|
class="button-unstyled -link"
|
||||||
@click.prevent="requireTOTP"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click.prevent="requireTOTP"
|
||||||
>
|
>
|
||||||
{{ $t('login.enter_two_factor_code') }}
|
{{ $t('login.enter_two_factor_code') }}
|
||||||
</button>
|
</button>
|
||||||
<br>
|
<br>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled -link"
|
class="button-unstyled -link"
|
||||||
@click.prevent="abortMFA"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click.prevent="abortMFA"
|
||||||
>
|
>
|
||||||
{{ $t('general.cancel') }}
|
{{ $t('general.cancel') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -27,16 +27,16 @@
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled -link"
|
class="button-unstyled -link"
|
||||||
@click.prevent="requireRecovery"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click.prevent="requireRecovery"
|
||||||
>
|
>
|
||||||
{{ $t('login.enter_recovery_code') }}
|
{{ $t('login.enter_recovery_code') }}
|
||||||
</button>
|
</button>
|
||||||
<br>
|
<br>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled -link"
|
class="button-unstyled -link"
|
||||||
@click.prevent="abortMFA"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click.prevent="abortMFA"
|
||||||
>
|
>
|
||||||
{{ $t('general.cancel') }}
|
{{ $t('general.cancel') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -50,74 +50,74 @@
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.FORCE_NSFW)"
|
@click="toggleTag(tags.FORCE_NSFW)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.force_nsfw') }}
|
|
||||||
<span
|
<span
|
||||||
class="menu-checkbox"
|
class="menu-checkbox"
|
||||||
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }"
|
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }"
|
||||||
/>
|
/>
|
||||||
|
{{ $t('user_card.admin_menu.force_nsfw') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.STRIP_MEDIA)"
|
@click="toggleTag(tags.STRIP_MEDIA)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.strip_media') }}
|
|
||||||
<span
|
<span
|
||||||
class="menu-checkbox"
|
class="menu-checkbox"
|
||||||
:class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }"
|
:class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }"
|
||||||
/>
|
/>
|
||||||
|
{{ $t('user_card.admin_menu.strip_media') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.FORCE_UNLISTED)"
|
@click="toggleTag(tags.FORCE_UNLISTED)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.force_unlisted') }}
|
|
||||||
<span
|
<span
|
||||||
class="menu-checkbox"
|
class="menu-checkbox"
|
||||||
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }"
|
:class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }"
|
||||||
/>
|
/>
|
||||||
|
{{ $t('user_card.admin_menu.force_unlisted') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.SANDBOX)"
|
@click="toggleTag(tags.SANDBOX)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.sandbox') }}
|
|
||||||
<span
|
<span
|
||||||
class="menu-checkbox"
|
class="menu-checkbox"
|
||||||
:class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }"
|
:class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }"
|
||||||
/>
|
/>
|
||||||
|
{{ $t('user_card.admin_menu.sandbox') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="user.is_local"
|
v-if="user.is_local"
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)"
|
@click="toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.disable_remote_subscription') }}
|
|
||||||
<span
|
<span
|
||||||
class="menu-checkbox"
|
class="menu-checkbox"
|
||||||
:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_REMOTE_SUBSCRIPTION) }"
|
:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_REMOTE_SUBSCRIPTION) }"
|
||||||
/>
|
/>
|
||||||
|
{{ $t('user_card.admin_menu.disable_remote_subscription') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="user.is_local"
|
v-if="user.is_local"
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)"
|
@click="toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.disable_any_subscription') }}
|
|
||||||
<span
|
<span
|
||||||
class="menu-checkbox"
|
class="menu-checkbox"
|
||||||
:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_ANY_SUBSCRIPTION) }"
|
:class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_ANY_SUBSCRIPTION) }"
|
||||||
/>
|
/>
|
||||||
|
{{ $t('user_card.admin_menu.disable_any_subscription') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="user.is_local"
|
v-if="user.is_local"
|
||||||
class="button-default dropdown-item"
|
class="button-default dropdown-item"
|
||||||
@click="toggleTag(tags.QUARANTINE)"
|
@click="toggleTag(tags.QUARANTINE)"
|
||||||
>
|
>
|
||||||
{{ $t('user_card.admin_menu.quarantine') }}
|
|
||||||
<span
|
<span
|
||||||
class="menu-checkbox"
|
class="menu-checkbox"
|
||||||
:class="{ 'menu-checkbox-checked': hasTag(tags.QUARANTINE) }"
|
:class="{ 'menu-checkbox-checked': hasTag(tags.QUARANTINE) }"
|
||||||
/>
|
/>
|
||||||
|
{{ $t('user_card.admin_menu.quarantine') }}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -163,25 +163,6 @@
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import '../../_variables.scss';
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
.menu-checkbox {
|
|
||||||
float: right;
|
|
||||||
min-width: 22px;
|
|
||||||
max-width: 22px;
|
|
||||||
min-height: 22px;
|
|
||||||
max-height: 22px;
|
|
||||||
line-height: 22px;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 0px;
|
|
||||||
background-color: $fallback--fg;
|
|
||||||
background-color: var(--input, $fallback--fg);
|
|
||||||
box-shadow: 0px 0px 2px black inset;
|
|
||||||
box-shadow: var(--inputShadow);
|
|
||||||
|
|
||||||
&.menu-checkbox-checked::after {
|
|
||||||
content: '✓';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.moderation-tools-popover {
|
.moderation-tools-popover {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.trigger {
|
.trigger {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
>
|
>
|
||||||
<small>
|
<small>
|
||||||
<router-link :to="userProfileLink">
|
<router-link :to="userProfileLink">
|
||||||
{{ notification.from_profile.screen_name }}
|
{{ notification.from_profile.screen_name_ui }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</small>
|
</small>
|
||||||
<button
|
<button
|
||||||
|
@ -54,14 +54,14 @@
|
||||||
<bdi
|
<bdi
|
||||||
v-if="!!notification.from_profile.name_html"
|
v-if="!!notification.from_profile.name_html"
|
||||||
class="username"
|
class="username"
|
||||||
:title="'@'+notification.from_profile.screen_name"
|
:title="'@'+notification.from_profile.screen_name_ui"
|
||||||
v-html="notification.from_profile.name_html"
|
v-html="notification.from_profile.name_html"
|
||||||
/>
|
/>
|
||||||
<!-- eslint-enable vue/no-v-html -->
|
<!-- eslint-enable vue/no-v-html -->
|
||||||
<span
|
<span
|
||||||
v-else
|
v-else
|
||||||
class="username"
|
class="username"
|
||||||
:title="'@'+notification.from_profile.screen_name"
|
:title="'@'+notification.from_profile.screen_name_ui"
|
||||||
>{{ notification.from_profile.name }}</span>
|
>{{ notification.from_profile.name }}</span>
|
||||||
<span v-if="notification.type === 'like'">
|
<span v-if="notification.type === 'like'">
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
:to="userProfileLink"
|
:to="userProfileLink"
|
||||||
class="follow-name"
|
class="follow-name"
|
||||||
>
|
>
|
||||||
@{{ notification.from_profile.screen_name }}
|
@{{ notification.from_profile.screen_name_ui }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<div
|
<div
|
||||||
v-if="notification.type === 'follow_request'"
|
v-if="notification.type === 'follow_request'"
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
class="move-text"
|
class="move-text"
|
||||||
>
|
>
|
||||||
<router-link :to="targetUserProfileLink">
|
<router-link :to="targetUserProfileLink">
|
||||||
@{{ notification.target.screen_name }}
|
@{{ notification.target.screen_name_ui }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
|
@ -151,6 +151,7 @@
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
padding-right: 0.75em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,25 +3,32 @@ const Popover = {
|
||||||
props: {
|
props: {
|
||||||
// Action to trigger popover: either 'hover' or 'click'
|
// Action to trigger popover: either 'hover' or 'click'
|
||||||
trigger: String,
|
trigger: String,
|
||||||
|
|
||||||
// Either 'top' or 'bottom'
|
// Either 'top' or 'bottom'
|
||||||
placement: String,
|
placement: String,
|
||||||
|
|
||||||
// Takes object with properties 'x' and 'y', values of these can be
|
// Takes object with properties 'x' and 'y', values of these can be
|
||||||
// 'container' for using offsetParent as boundaries for either axis
|
// 'container' for using offsetParent as boundaries for either axis
|
||||||
// or 'viewport'
|
// or 'viewport'
|
||||||
boundTo: Object,
|
boundTo: Object,
|
||||||
|
|
||||||
// Takes a selector to use as a replacement for the parent container
|
// Takes a selector to use as a replacement for the parent container
|
||||||
// for getting boundaries for x an y axis
|
// for getting boundaries for x an y axis
|
||||||
boundToSelector: String,
|
boundToSelector: String,
|
||||||
|
|
||||||
// Takes a top/bottom/left/right object, how much space to leave
|
// Takes a top/bottom/left/right object, how much space to leave
|
||||||
// between boundary and popover element
|
// between boundary and popover element
|
||||||
margin: Object,
|
margin: Object,
|
||||||
|
|
||||||
// Takes a x/y object and tells how many pixels to offset from
|
// Takes a x/y object and tells how many pixels to offset from
|
||||||
// anchor point on either axis
|
// anchor point on either axis
|
||||||
offset: Object,
|
offset: Object,
|
||||||
|
|
||||||
// Replaces the classes you may want for the popover container.
|
// Replaces the classes you may want for the popover container.
|
||||||
// Use 'popover-default' in addition to get the default popover
|
// Use 'popover-default' in addition to get the default popover
|
||||||
// styles with your custom class.
|
// styles with your custom class.
|
||||||
popoverClass: String,
|
popoverClass: String,
|
||||||
|
|
||||||
// If true, subtract padding when calculating position for the popover,
|
// If true, subtract padding when calculating position for the popover,
|
||||||
// use it when popover offset looks to be different on top vs bottom.
|
// use it when popover offset looks to be different on top vs bottom.
|
||||||
removePadding: Boolean
|
removePadding: Boolean
|
||||||
|
@ -121,9 +128,12 @@ const Popover = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showPopover () {
|
showPopover () {
|
||||||
if (this.hidden) this.$emit('show')
|
const wasHidden = this.hidden
|
||||||
this.hidden = false
|
this.hidden = false
|
||||||
this.$nextTick(this.updateStyles)
|
this.$nextTick(() => {
|
||||||
|
if (wasHidden) this.$emit('show')
|
||||||
|
this.updateStyles()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
hidePopover () {
|
hidePopover () {
|
||||||
if (!this.hidden) this.$emit('close')
|
if (!this.hidden) this.$emit('close')
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<button
|
<button
|
||||||
ref="trigger"
|
ref="trigger"
|
||||||
class="button-unstyled -fullwidth popover-trigger-button"
|
class="button-unstyled -fullwidth popover-trigger-button"
|
||||||
@click="onClick"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<slot name="trigger" />
|
<slot name="trigger" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -82,10 +82,9 @@
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
line-height: 21px;
|
line-height: 21px;
|
||||||
margin-right: 5px;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
display: block;
|
display: block;
|
||||||
padding: .25rem 1.0rem .25rem 1.5rem;
|
padding: .5em 0.75em;
|
||||||
clear: both;
|
clear: both;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
text-align: inherit;
|
text-align: inherit;
|
||||||
|
@ -101,10 +100,9 @@
|
||||||
--btnText: var(--popoverText, $fallback--text);
|
--btnText: var(--popoverText, $fallback--text);
|
||||||
|
|
||||||
&-icon {
|
&-icon {
|
||||||
padding-left: 0.5rem;
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-right: 0.25rem;
|
width: 22px;
|
||||||
|
margin-right: 0.75rem;
|
||||||
color: var(--menuPopoverIcon, $fallback--icon)
|
color: var(--menuPopoverIcon, $fallback--icon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +121,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-checkbox {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
min-width: 22px;
|
||||||
|
max-width: 22px;
|
||||||
|
min-height: 22px;
|
||||||
|
max-height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 0px;
|
||||||
|
background-color: $fallback--fg;
|
||||||
|
background-color: var(--input, $fallback--fg);
|
||||||
|
box-shadow: 0px 0px 2px black inset;
|
||||||
|
box-shadow: var(--inputShadow);
|
||||||
|
margin-right: 0.75em;
|
||||||
|
|
||||||
|
&.menu-checkbox-checked::after {
|
||||||
|
font-size: 1.25em;
|
||||||
|
content: '✓';
|
||||||
|
}
|
||||||
|
|
||||||
|
&.menu-checkbox-radio::after {
|
||||||
|
font-size: 2em;
|
||||||
|
content: '•';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -115,7 +115,7 @@ const PostStatusForm = {
|
||||||
? this.copyMessageScope
|
? this.copyMessageScope
|
||||||
: this.$store.state.users.currentUser.default_scope
|
: this.$store.state.users.currentUser.default_scope
|
||||||
|
|
||||||
const { postContentType: contentType } = this.$store.getters.mergedConfig
|
const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dropFiles: [],
|
dropFiles: [],
|
||||||
|
@ -126,7 +126,7 @@ const PostStatusForm = {
|
||||||
newStatus: {
|
newStatus: {
|
||||||
spoilerText: this.subject || '',
|
spoilerText: this.subject || '',
|
||||||
status: statusText,
|
status: statusText,
|
||||||
nsfw: false,
|
nsfw: !!sensitiveByDefault,
|
||||||
files: [],
|
files: [],
|
||||||
poll: {},
|
poll: {},
|
||||||
mediaDescriptions: {},
|
mediaDescriptions: {},
|
||||||
|
|
|
@ -23,6 +23,12 @@ const ReactButton = {
|
||||||
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
||||||
}
|
}
|
||||||
close()
|
close()
|
||||||
|
},
|
||||||
|
focusInput () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const input = this.$el.querySelector('input')
|
||||||
|
if (input) input.focus()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
:offset="{ y: 5 }"
|
:offset="{ y: 5 }"
|
||||||
:bound-to="{ x: 'container' }"
|
:bound-to="{ x: 'container' }"
|
||||||
remove-padding
|
remove-padding
|
||||||
|
@show="focusInput"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
slot="content"
|
slot="content"
|
||||||
|
|
|
@ -10,7 +10,8 @@ const registration = {
|
||||||
fullname: '',
|
fullname: '',
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirm: ''
|
confirm: '',
|
||||||
|
reason: ''
|
||||||
},
|
},
|
||||||
captcha: {}
|
captcha: {}
|
||||||
}),
|
}),
|
||||||
|
@ -24,7 +25,8 @@ const registration = {
|
||||||
confirm: {
|
confirm: {
|
||||||
required,
|
required,
|
||||||
sameAsPassword: sameAs('password')
|
sameAsPassword: sameAs('password')
|
||||||
}
|
},
|
||||||
|
reason: { required: requiredIf(() => this.accountApprovalRequired) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -38,7 +40,10 @@ const registration = {
|
||||||
computed: {
|
computed: {
|
||||||
token () { return this.$route.params.token },
|
token () { return this.$route.params.token },
|
||||||
bioPlaceholder () {
|
bioPlaceholder () {
|
||||||
return this.$t('registration.bio_placeholder').replace(/\s*\n\s*/g, ' \n')
|
return this.replaceNewlines(this.$t('registration.bio_placeholder'))
|
||||||
|
},
|
||||||
|
reasonPlaceholder () {
|
||||||
|
return this.replaceNewlines(this.$t('registration.reason_placeholder'))
|
||||||
},
|
},
|
||||||
...mapState({
|
...mapState({
|
||||||
registrationOpen: (state) => state.instance.registrationOpen,
|
registrationOpen: (state) => state.instance.registrationOpen,
|
||||||
|
@ -46,7 +51,8 @@ const registration = {
|
||||||
isPending: (state) => state.users.signUpPending,
|
isPending: (state) => state.users.signUpPending,
|
||||||
serverValidationErrors: (state) => state.users.signUpErrors,
|
serverValidationErrors: (state) => state.users.signUpErrors,
|
||||||
termsOfService: (state) => state.instance.tos,
|
termsOfService: (state) => state.instance.tos,
|
||||||
accountActivationRequired: (state) => state.instance.accountActivationRequired
|
accountActivationRequired: (state) => state.instance.accountActivationRequired,
|
||||||
|
accountApprovalRequired: (state) => state.instance.accountApprovalRequired
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -73,6 +79,9 @@ const registration = {
|
||||||
},
|
},
|
||||||
setCaptcha () {
|
setCaptcha () {
|
||||||
this.getCaptcha().then(cpt => { this.captcha = cpt })
|
this.getCaptcha().then(cpt => { this.captcha = cpt })
|
||||||
|
},
|
||||||
|
replaceNewlines (str) {
|
||||||
|
return str.replace(/\s*\n\s*/g, ' \n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,6 +162,23 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="accountApprovalRequired"
|
||||||
|
class="form-group"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="form--label"
|
||||||
|
for="reason"
|
||||||
|
>{{ $t('registration.reason') }}</label>
|
||||||
|
<textarea
|
||||||
|
id="reason"
|
||||||
|
v-model="user.reason"
|
||||||
|
:disabled="isPending"
|
||||||
|
class="form-control"
|
||||||
|
:placeholder="reasonPlaceholder"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="captcha.type != 'none'"
|
v-if="captcha.type != 'none'"
|
||||||
id="captcha-group"
|
id="captcha-group"
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
class="button-unstyled scope"
|
class="button-unstyled scope"
|
||||||
:class="css.direct"
|
:class="css.direct"
|
||||||
:title="$t('post_status.scope.direct')"
|
:title="$t('post_status.scope.direct')"
|
||||||
@click="changeVis('direct')"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click="changeVis('direct')"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="envelope"
|
icon="envelope"
|
||||||
|
@ -21,8 +21,8 @@
|
||||||
class="button-unstyled scope"
|
class="button-unstyled scope"
|
||||||
:class="css.private"
|
:class="css.private"
|
||||||
:title="$t('post_status.scope.private')"
|
:title="$t('post_status.scope.private')"
|
||||||
@click="changeVis('private')"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click="changeVis('private')"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="lock"
|
icon="lock"
|
||||||
|
@ -34,8 +34,8 @@
|
||||||
class="button-unstyled scope"
|
class="button-unstyled scope"
|
||||||
:class="css.unlisted"
|
:class="css.unlisted"
|
||||||
:title="$t('post_status.scope.unlisted')"
|
:title="$t('post_status.scope.unlisted')"
|
||||||
@click="changeVis('unlisted')"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click="changeVis('unlisted')"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="lock-open"
|
icon="lock-open"
|
||||||
|
@ -47,8 +47,8 @@
|
||||||
class="button-unstyled scope"
|
class="button-unstyled scope"
|
||||||
:class="css.public"
|
:class="css.public"
|
||||||
:title="$t('post_status.scope.public')"
|
:title="$t('post_status.scope.public')"
|
||||||
@click="changeVis('public')"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click="changeVis('public')"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="globe"
|
icon="globe"
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="btn button-default search-button"
|
class="btn button-default search-button"
|
||||||
@click="newQuery(searchTerm)"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@click="newQuery(searchTerm)"
|
||||||
>
|
>
|
||||||
<FAIcon icon="search" />
|
<FAIcon icon="search" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
v-if="hidden"
|
v-if="hidden"
|
||||||
class="button-unstyled nav-icon"
|
class="button-unstyled nav-icon"
|
||||||
:title="$t('nav.search')"
|
:title="$t('nav.search')"
|
||||||
@click.prevent.stop="toggleHidden"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click.prevent.stop="toggleHidden"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
|
@ -28,8 +28,8 @@
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="button-default search-button"
|
class="button-default search-button"
|
||||||
@click="find(searchTerm)"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@click="find(searchTerm)"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
|
@ -38,8 +38,8 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button-unstyled cancel-search"
|
class="button-unstyled cancel-search"
|
||||||
@click.prevent.stop="toggleHidden"
|
|
||||||
type="button"
|
type="button"
|
||||||
|
@click.prevent.stop="toggleHidden"
|
||||||
>
|
>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
fixed-width
|
fixed-width
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
:checked="state"
|
:checked="state"
|
||||||
@change="update"
|
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
@change="update"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="!!$slots.default"
|
v-if="!!$slots.default"
|
||||||
class="label"
|
class="label"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</span>
|
</span>
|
||||||
<ModifiedIndicator :changed="isChanged" />
|
<ModifiedIndicator :changed="isChanged" />
|
||||||
|
@ -23,14 +23,14 @@ import { get, set } from 'lodash'
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
import ModifiedIndicator from './modified_indicator.vue'
|
import ModifiedIndicator from './modified_indicator.vue'
|
||||||
export default {
|
export default {
|
||||||
props: [
|
|
||||||
'path',
|
|
||||||
'disabled'
|
|
||||||
],
|
|
||||||
components: {
|
components: {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
ModifiedIndicator
|
ModifiedIndicator
|
||||||
},
|
},
|
||||||
|
props: [
|
||||||
|
'path',
|
||||||
|
'disabled'
|
||||||
|
],
|
||||||
computed: {
|
computed: {
|
||||||
pathDefault () {
|
pathDefault () {
|
||||||
const [firstSegment, ...rest] = this.path.split('.')
|
const [firstSegment, ...rest] = this.path.split('.')
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
<span
|
<span
|
||||||
v-if="changed"
|
v-if="changed"
|
||||||
class="ModifiedIndicator"
|
class="ModifiedIndicator"
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
>
|
>
|
||||||
<span slot="trigger">
|
<span slot="trigger">
|
||||||
|
|
||||||
<FAIcon
|
<FAIcon
|
||||||
icon="wrench"
|
icon="wrench"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="modified-tooltip"
|
|
||||||
slot="content"
|
slot="content"
|
||||||
>
|
class="modified-tooltip"
|
||||||
{{ $t('settings.setting_changed') }}
|
>
|
||||||
|
{{ $t('settings.setting_changed') }}
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</span>
|
</span>
|
||||||
|
@ -32,8 +32,8 @@ library.add(
|
||||||
)
|
)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['changed'],
|
components: { Popover },
|
||||||
components: { Popover }
|
props: ['changed']
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -75,8 +75,8 @@
|
||||||
<p>{{ $t('settings.filtering_explanation') }}</p>
|
<p>{{ $t('settings.filtering_explanation') }}</p>
|
||||||
<textarea
|
<textarea
|
||||||
id="muteWords"
|
id="muteWords"
|
||||||
class="resize-height"
|
|
||||||
v-model="muteWordsString"
|
v-model="muteWordsString"
|
||||||
|
class="resize-height"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -144,7 +144,12 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="minimalScopesMode">
|
<BooleanSetting path="minimalScopesMode">
|
||||||
{{ $t('settings.minimal_scopes_mode') }} {{ minimalScopesModeDefaultValue }}
|
{{ $t('settings.minimal_scopes_mode') }}
|
||||||
|
</BooleanSetting>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<BooleanSetting path="sensitiveByDefault">
|
||||||
|
{{ $t('settings.sensitive_by_default') }}
|
||||||
</BooleanSetting>
|
</BooleanSetting>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -136,7 +136,7 @@ const Status = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
retweet () { return !!this.statusoid.retweeted_status },
|
retweet () { return !!this.statusoid.retweeted_status },
|
||||||
retweeter () { return this.statusoid.user.name || this.statusoid.user.screen_name },
|
retweeter () { return this.statusoid.user.name || this.statusoid.user.screen_name_ui },
|
||||||
retweeterHtml () { return this.statusoid.user.name_html },
|
retweeterHtml () { return this.statusoid.user.name_html },
|
||||||
retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) },
|
retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) },
|
||||||
status () {
|
status () {
|
||||||
|
@ -216,7 +216,7 @@ const Status = {
|
||||||
return this.status.in_reply_to_screen_name
|
return this.status.in_reply_to_screen_name
|
||||||
} else {
|
} else {
|
||||||
const user = this.$store.getters.findUser(this.status.in_reply_to_user_id)
|
const user = this.$store.getters.findUser(this.status.in_reply_to_user_id)
|
||||||
return user && user.screen_name
|
return user && user.screen_name_ui
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replySubject () {
|
replySubject () {
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
icon="retweet"
|
icon="retweet"
|
||||||
/>
|
/>
|
||||||
<router-link :to="userProfileLink">
|
<router-link :to="userProfileLink">
|
||||||
{{ status.user.screen_name }}
|
{{ status.user.screen_name_ui }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</small>
|
</small>
|
||||||
<small
|
<small
|
||||||
|
@ -156,10 +156,10 @@
|
||||||
</h4>
|
</h4>
|
||||||
<router-link
|
<router-link
|
||||||
class="account-name"
|
class="account-name"
|
||||||
:title="status.user.screen_name"
|
:title="status.user.screen_name_ui"
|
||||||
:to="userProfileLink"
|
:to="userProfileLink"
|
||||||
>
|
>
|
||||||
{{ status.user.screen_name }}
|
{{ status.user.screen_name_ui }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<img
|
<img
|
||||||
v-if="!!(status.user && status.user.favicon)"
|
v-if="!!(status.user && status.user.favicon)"
|
||||||
|
|
|
@ -2,12 +2,14 @@ import Status from '../status/status.vue'
|
||||||
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
|
import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js'
|
||||||
import Conversation from '../conversation/conversation.vue'
|
import Conversation from '../conversation/conversation.vue'
|
||||||
import TimelineMenu from '../timeline_menu/timeline_menu.vue'
|
import TimelineMenu from '../timeline_menu/timeline_menu.vue'
|
||||||
|
import TimelineQuickSettings from './timeline_quick_settings.vue'
|
||||||
import { debounce, throttle, keyBy } from 'lodash'
|
import { debounce, throttle, keyBy } from 'lodash'
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
import { faCircleNotch, faCog } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faCircleNotch
|
faCircleNotch,
|
||||||
|
faCog
|
||||||
)
|
)
|
||||||
|
|
||||||
export const getExcludedStatusIdsByPinning = (statuses, pinnedStatusIds) => {
|
export const getExcludedStatusIdsByPinning = (statuses, pinnedStatusIds) => {
|
||||||
|
@ -47,7 +49,8 @@ const Timeline = {
|
||||||
components: {
|
components: {
|
||||||
Status,
|
Status,
|
||||||
Conversation,
|
Conversation,
|
||||||
TimelineMenu
|
TimelineMenu,
|
||||||
|
TimelineQuickSettings
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
newStatusCount () {
|
newStatusCount () {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
>
|
>
|
||||||
{{ $t('timeline.up_to_date') }}
|
{{ $t('timeline.up_to_date') }}
|
||||||
</div>
|
</div>
|
||||||
|
<TimelineQuickSettings v-if="!embedded" />
|
||||||
</div>
|
</div>
|
||||||
<div :class="classes.body">
|
<div :class="classes.body">
|
||||||
<div
|
<div
|
||||||
|
@ -103,9 +104,12 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.loadmore-button {
|
.loadmore-button {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loadmore-text {
|
.loadmore-text {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
|
|
63
src/components/timeline/timeline_quick_settings.js
Normal file
63
src/components/timeline/timeline_quick_settings.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import Popover from '../popover/popover.vue'
|
||||||
|
import BooleanSetting from '../settings_modal/helpers/boolean_setting.vue'
|
||||||
|
import { mapGetters } from 'vuex'
|
||||||
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
library.add(
|
||||||
|
faFilter,
|
||||||
|
faFont,
|
||||||
|
faWrench
|
||||||
|
)
|
||||||
|
|
||||||
|
const TimelineQuickSettings = {
|
||||||
|
components: {
|
||||||
|
Popover,
|
||||||
|
BooleanSetting
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setReplyVisibility (visibility) {
|
||||||
|
this.$store.dispatch('setOption', { name: 'replyVisibility', value: visibility })
|
||||||
|
this.$store.dispatch('queueFlushAll')
|
||||||
|
},
|
||||||
|
openTab (tab) {
|
||||||
|
this.$store.dispatch('openSettingsModalTab', tab)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['mergedConfig']),
|
||||||
|
loggedIn () {
|
||||||
|
return !!this.$store.state.users.currentUser
|
||||||
|
},
|
||||||
|
replyVisibilitySelf: {
|
||||||
|
get () { return this.mergedConfig.replyVisibility === 'self' },
|
||||||
|
set () { this.setReplyVisibility('self') }
|
||||||
|
},
|
||||||
|
replyVisibilityFollowing: {
|
||||||
|
get () { return this.mergedConfig.replyVisibility === 'following' },
|
||||||
|
set () { this.setReplyVisibility('following') }
|
||||||
|
},
|
||||||
|
replyVisibilityAll: {
|
||||||
|
get () { return this.mergedConfig.replyVisibility === 'all' },
|
||||||
|
set () { this.setReplyVisibility('all') }
|
||||||
|
},
|
||||||
|
hideMedia: {
|
||||||
|
get () { return this.mergedConfig.hideAttachments || this.mergedConfig.hideAttachmentsInConv },
|
||||||
|
set () {
|
||||||
|
const value = !this.hideMedia
|
||||||
|
this.$store.dispatch('setOption', { name: 'hideAttachments', value })
|
||||||
|
this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideMutedPosts: {
|
||||||
|
get () { return this.mergedConfig.hideMutedPosts || this.mergedConfig.hideFilteredStatuses },
|
||||||
|
set () {
|
||||||
|
const value = !this.hideMutedPosts
|
||||||
|
this.$store.dispatch('setOption', { name: 'hideMutedPosts', value })
|
||||||
|
this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimelineQuickSettings
|
107
src/components/timeline/timeline_quick_settings.vue
Normal file
107
src/components/timeline/timeline_quick_settings.vue
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
<template>
|
||||||
|
<Popover
|
||||||
|
trigger="click"
|
||||||
|
class="TimelineQuickSettings"
|
||||||
|
:bound-to="{ x: 'container' }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
slot="content"
|
||||||
|
class="timeline-settings-menu dropdown-menu"
|
||||||
|
>
|
||||||
|
<div v-if="loggedIn">
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="replyVisibilityAll = true"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-radio': replyVisibilityAll }"
|
||||||
|
/>{{ $t('settings.reply_visibility_all') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="replyVisibilityFollowing = true"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-radio': replyVisibilityFollowing }"
|
||||||
|
/>{{ $t('settings.reply_visibility_following_short') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="replyVisibilitySelf = true"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-radio': replyVisibilitySelf }"
|
||||||
|
/>{{ $t('settings.reply_visibility_self_short') }}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
role="separator"
|
||||||
|
class="dropdown-divider"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="hideMedia = !hideMedia"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': hideMedia }"
|
||||||
|
/>{{ $t('settings.hide_media_previews') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item"
|
||||||
|
@click="hideMutedPosts = !hideMutedPosts"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="menu-checkbox"
|
||||||
|
:class="{ 'menu-checkbox-checked': hideMutedPosts }"
|
||||||
|
/>{{ $t('settings.hide_all_muted_posts') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
@click="openTab('filtering')"
|
||||||
|
>
|
||||||
|
<FAIcon icon="font" />{{ $t('settings.word_filter') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button-default dropdown-item dropdown-item-icon"
|
||||||
|
@click="openTab('general')"
|
||||||
|
>
|
||||||
|
<FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div slot="trigger">
|
||||||
|
<FAIcon icon="filter" />
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./timeline_quick_settings.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
.TimelineQuickSettings {
|
||||||
|
align-self: stretch;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding-left: 0.7em;
|
||||||
|
padding-right: 0.2em;
|
||||||
|
line-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-settings-menu {
|
||||||
|
display: flex;
|
||||||
|
min-width: 12em;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -2,8 +2,8 @@
|
||||||
<StillImage
|
<StillImage
|
||||||
v-if="user"
|
v-if="user"
|
||||||
class="Avatar"
|
class="Avatar"
|
||||||
:alt="user.screen_name"
|
:alt="user.screen_name_ui"
|
||||||
:title="user.screen_name"
|
:title="user.screen_name_ui"
|
||||||
:src="imgSrc(user.profile_image_url_original)"
|
:src="imgSrc(user.profile_image_url_original)"
|
||||||
:class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }"
|
:class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }"
|
||||||
:image-load-error="imageLoadError"
|
:image-load-error="imageLoadError"
|
||||||
|
|
|
@ -73,10 +73,10 @@
|
||||||
<div class="bottom-line">
|
<div class="bottom-line">
|
||||||
<router-link
|
<router-link
|
||||||
class="user-screen-name"
|
class="user-screen-name"
|
||||||
:title="user.screen_name"
|
:title="user.screen_name_ui"
|
||||||
:to="userProfileLink(user)"
|
:to="userProfileLink(user)"
|
||||||
>
|
>
|
||||||
@{{ user.screen_name }}
|
@{{ user.screen_name_ui }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<template v-if="!hideBio">
|
<template v-if="!hideBio">
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<span v-html="user.name_html" />
|
<span v-html="user.name_html" />
|
||||||
<!-- eslint-enable vue/no-v-html -->
|
<!-- eslint-enable vue/no-v-html -->
|
||||||
<span class="user-list-screen-name">{{ user.screen_name }}</span>
|
<span class="user-list-screen-name">{{ user.screen_name_ui }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="user-reporting-panel panel">
|
<div class="user-reporting-panel panel">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
{{ $t('user_reporting.title', [user.screen_name]) }}
|
{{ $t('user_reporting.title', [user.screen_name_ui]) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
|
@ -228,6 +228,8 @@
|
||||||
"username_placeholder": "e.g. lain",
|
"username_placeholder": "e.g. lain",
|
||||||
"fullname_placeholder": "e.g. Lain Iwakura",
|
"fullname_placeholder": "e.g. Lain Iwakura",
|
||||||
"bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.",
|
"bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.",
|
||||||
|
"reason": "Reason to register",
|
||||||
|
"reason_placeholder": "This instance approves registrations manually.\nLet the administration know why you want to register.",
|
||||||
"validations": {
|
"validations": {
|
||||||
"username_required": "cannot be left blank",
|
"username_required": "cannot be left blank",
|
||||||
"fullname_required": "cannot be left blank",
|
"fullname_required": "cannot be left blank",
|
||||||
|
@ -325,6 +327,7 @@
|
||||||
"export_theme": "Save preset",
|
"export_theme": "Save preset",
|
||||||
"filtering": "Filtering",
|
"filtering": "Filtering",
|
||||||
"filtering_explanation": "All statuses containing these words will be muted, one per line",
|
"filtering_explanation": "All statuses containing these words will be muted, one per line",
|
||||||
|
"word_filter": "Word filter",
|
||||||
"follow_export": "Follow export",
|
"follow_export": "Follow export",
|
||||||
"follow_export_button": "Export your follows to a csv file",
|
"follow_export_button": "Export your follows to a csv file",
|
||||||
"follow_import": "Follow import",
|
"follow_import": "Follow import",
|
||||||
|
@ -335,7 +338,9 @@
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"hide_attachments_in_convo": "Hide attachments in conversations",
|
"hide_attachments_in_convo": "Hide attachments in conversations",
|
||||||
"hide_attachments_in_tl": "Hide attachments in timeline",
|
"hide_attachments_in_tl": "Hide attachments in timeline",
|
||||||
|
"hide_media_previews": "Hide media previews",
|
||||||
"hide_muted_posts": "Hide posts of muted users",
|
"hide_muted_posts": "Hide posts of muted users",
|
||||||
|
"hide_all_muted_posts": "Hide muted posts",
|
||||||
"max_thumbnails": "Maximum amount of thumbnails per post",
|
"max_thumbnails": "Maximum amount of thumbnails per post",
|
||||||
"hide_isp": "Hide instance-specific panel",
|
"hide_isp": "Hide instance-specific panel",
|
||||||
"hide_wallpaper": "Hide instance wallpaper",
|
"hide_wallpaper": "Hide instance wallpaper",
|
||||||
|
@ -405,6 +410,8 @@
|
||||||
"reply_visibility_all": "Show all replies",
|
"reply_visibility_all": "Show all replies",
|
||||||
"reply_visibility_following": "Only show replies directed at me or users I'm following",
|
"reply_visibility_following": "Only show replies directed at me or users I'm following",
|
||||||
"reply_visibility_self": "Only show replies directed at me",
|
"reply_visibility_self": "Only show replies directed at me",
|
||||||
|
"reply_visibility_following_short": "Show replies to my follows",
|
||||||
|
"reply_visibility_self_short": "Show replies to self only",
|
||||||
"autohide_floating_post_button": "Automatically hide New Post button (mobile)",
|
"autohide_floating_post_button": "Automatically hide New Post button (mobile)",
|
||||||
"saving_err": "Error saving settings",
|
"saving_err": "Error saving settings",
|
||||||
"saving_ok": "Settings saved",
|
"saving_ok": "Settings saved",
|
||||||
|
@ -431,6 +438,7 @@
|
||||||
"subject_line_mastodon": "Like mastodon: copy as is",
|
"subject_line_mastodon": "Like mastodon: copy as is",
|
||||||
"subject_line_noop": "Do not copy",
|
"subject_line_noop": "Do not copy",
|
||||||
"post_status_content_type": "Post status content type",
|
"post_status_content_type": "Post status content type",
|
||||||
|
"sensitive_by_default": "Mark posts as sensitive by default",
|
||||||
"stop_gifs": "Play-on-hover GIFs",
|
"stop_gifs": "Play-on-hover GIFs",
|
||||||
"streaming": "Enable automatic streaming of new posts when scrolled to the top",
|
"streaming": "Enable automatic streaming of new posts when scrolled to the top",
|
||||||
"user_mutes": "Users",
|
"user_mutes": "Users",
|
||||||
|
@ -460,6 +468,7 @@
|
||||||
"notification_mutes": "To stop receiving notifications from a specific user, use a mute.",
|
"notification_mutes": "To stop receiving notifications from a specific user, use a mute.",
|
||||||
"notification_blocks": "Blocking a user stops all notifications as well as unsubscribes them.",
|
"notification_blocks": "Blocking a user stops all notifications as well as unsubscribes them.",
|
||||||
"enable_web_push_notifications": "Enable web push notifications",
|
"enable_web_push_notifications": "Enable web push notifications",
|
||||||
|
"more_settings": "More settings",
|
||||||
"style": {
|
"style": {
|
||||||
"switcher": {
|
"switcher": {
|
||||||
"keep_color": "Keep colors",
|
"keep_color": "Keep colors",
|
||||||
|
|
|
@ -35,7 +35,11 @@
|
||||||
"retry": "Reprovi",
|
"retry": "Reprovi",
|
||||||
"error_retry": "Bonvolu reprovi",
|
"error_retry": "Bonvolu reprovi",
|
||||||
"loading": "Enlegante…",
|
"loading": "Enlegante…",
|
||||||
"peek": "Antaŭmontri"
|
"peek": "Antaŭmontri",
|
||||||
|
"role": {
|
||||||
|
"moderator": "Reguligisto",
|
||||||
|
"admin": "Administranto"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"image_cropper": {
|
"image_cropper": {
|
||||||
"crop_picture": "Tondi bildon",
|
"crop_picture": "Tondi bildon",
|
||||||
|
@ -365,7 +369,8 @@
|
||||||
"post": "Afiŝoj/Priskriboj de uzantoj",
|
"post": "Afiŝoj/Priskriboj de uzantoj",
|
||||||
"alert_neutral": "Neŭtrala",
|
"alert_neutral": "Neŭtrala",
|
||||||
"alert_warning": "Averto",
|
"alert_warning": "Averto",
|
||||||
"toggled": "Ŝaltita"
|
"toggled": "Ŝaltita",
|
||||||
|
"wallpaper": "Fonbildo"
|
||||||
},
|
},
|
||||||
"radii": {
|
"radii": {
|
||||||
"_tab_label": "Rondeco"
|
"_tab_label": "Rondeco"
|
||||||
|
@ -516,7 +521,9 @@
|
||||||
"mute_import_error": "Eraris enporto de silentigoj",
|
"mute_import_error": "Eraris enporto de silentigoj",
|
||||||
"mute_import": "Enporto de silentigoj",
|
"mute_import": "Enporto de silentigoj",
|
||||||
"mute_export_button": "Elportu viajn silentigojn al CSV-dosiero",
|
"mute_export_button": "Elportu viajn silentigojn al CSV-dosiero",
|
||||||
"mute_export": "Elporto de silentigoj"
|
"mute_export": "Elporto de silentigoj",
|
||||||
|
"hide_wallpaper": "Kaŝi fonbildon de nodo",
|
||||||
|
"setting_changed": "Agordo malsamas de la implicita"
|
||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"collapse": "Maletendi",
|
"collapse": "Maletendi",
|
||||||
|
@ -586,7 +593,8 @@
|
||||||
"show_repeats": "Montri ripetojn",
|
"show_repeats": "Montri ripetojn",
|
||||||
"hide_repeats": "Kaŝi ripetojn",
|
"hide_repeats": "Kaŝi ripetojn",
|
||||||
"unsubscribe": "Ne ricevi sciigojn",
|
"unsubscribe": "Ne ricevi sciigojn",
|
||||||
"subscribe": "Ricevi sciigojn"
|
"subscribe": "Ricevi sciigojn",
|
||||||
|
"bot": "Roboto"
|
||||||
},
|
},
|
||||||
"user_profile": {
|
"user_profile": {
|
||||||
"timeline_title": "Historio de uzanto",
|
"timeline_title": "Historio de uzanto",
|
||||||
|
@ -612,7 +620,8 @@
|
||||||
"error": {
|
"error": {
|
||||||
"base": "Alŝuto malsukcesis.",
|
"base": "Alŝuto malsukcesis.",
|
||||||
"file_too_big": "Dosiero estas tro granda [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
|
"file_too_big": "Dosiero estas tro granda [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
|
||||||
"default": "Reprovu pli poste"
|
"default": "Reprovu pli poste",
|
||||||
|
"message": "Malsukcesis alŝuto: {0}"
|
||||||
},
|
},
|
||||||
"file_size_units": {
|
"file_size_units": {
|
||||||
"B": "B",
|
"B": "B",
|
||||||
|
@ -645,7 +654,9 @@
|
||||||
"votes": "voĉoj",
|
"votes": "voĉoj",
|
||||||
"option": "Elekteblo",
|
"option": "Elekteblo",
|
||||||
"add_option": "Aldoni elekteblon",
|
"add_option": "Aldoni elekteblon",
|
||||||
"add_poll": "Aldoni enketon"
|
"add_poll": "Aldoni enketon",
|
||||||
|
"votes_count": "{count} voĉdono | {count} voĉdonoj",
|
||||||
|
"people_voted_count": "{count} persono voĉdonis | {count} personoj voĉdonis"
|
||||||
},
|
},
|
||||||
"importer": {
|
"importer": {
|
||||||
"error": "Eraris enporto de ĉi tiu dosiero.",
|
"error": "Eraris enporto de ĉi tiu dosiero.",
|
||||||
|
@ -732,7 +743,9 @@
|
||||||
"repeats": "Ripetoj",
|
"repeats": "Ripetoj",
|
||||||
"favorites": "Ŝatoj",
|
"favorites": "Ŝatoj",
|
||||||
"status_deleted": "Ĉi tiu afiŝo foriĝis",
|
"status_deleted": "Ĉi tiu afiŝo foriĝis",
|
||||||
"nsfw": "Konsterna"
|
"nsfw": "Konsterna",
|
||||||
|
"expand": "Etendi",
|
||||||
|
"external_source": "Ekstera fonto"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"years_short": "{0}j",
|
"years_short": "{0}j",
|
||||||
|
|
|
@ -584,7 +584,9 @@
|
||||||
"fullname_placeholder": "es. Lupo Lucio",
|
"fullname_placeholder": "es. Lupo Lucio",
|
||||||
"username_placeholder": "es. mister_wolf",
|
"username_placeholder": "es. mister_wolf",
|
||||||
"new_captcha": "Clicca l'immagine per avere un altro captcha",
|
"new_captcha": "Clicca l'immagine per avere un altro captcha",
|
||||||
"captcha": "CAPTCHA"
|
"captcha": "CAPTCHA",
|
||||||
|
"reason_placeholder": "L'amministratore esamina ciascuna richiesta.\nFornisci il motivo della tua iscrizione.",
|
||||||
|
"reason": "Motivo dell'iscrizione"
|
||||||
},
|
},
|
||||||
"user_profile": {
|
"user_profile": {
|
||||||
"timeline_title": "Sequenza dell'Utente",
|
"timeline_title": "Sequenza dell'Utente",
|
||||||
|
|
|
@ -201,7 +201,9 @@
|
||||||
"password_required": "必須",
|
"password_required": "必須",
|
||||||
"password_confirmation_required": "必須",
|
"password_confirmation_required": "必須",
|
||||||
"password_confirmation_match": "パスワードが違います"
|
"password_confirmation_match": "パスワードが違います"
|
||||||
}
|
},
|
||||||
|
"reason_placeholder": "このインスタンスは、新規登録を手動で受け付けています。\n登録したい理由を、インスタンスの管理者に教えてください。",
|
||||||
|
"reason": "登録するための目的"
|
||||||
},
|
},
|
||||||
"selectable_list": {
|
"selectable_list": {
|
||||||
"select_all": "すべて選択"
|
"select_all": "すべて選択"
|
||||||
|
@ -411,8 +413,8 @@
|
||||||
"contrast": {
|
"contrast": {
|
||||||
"hint": "コントラストは {ratio} です。{level}。({context})",
|
"hint": "コントラストは {ratio} です。{level}。({context})",
|
||||||
"level": {
|
"level": {
|
||||||
"aa": "AAレベルガイドライン (ミニマル) を満たします",
|
"aa": "AAレベルガイドライン (最低限) を満たします",
|
||||||
"aaa": "AAAレベルガイドライン (レコメンデッド) を満たします",
|
"aaa": "AAAレベルガイドライン (推奨) を満たします",
|
||||||
"bad": "ガイドラインを満たしません"
|
"bad": "ガイドラインを満たしません"
|
||||||
},
|
},
|
||||||
"context": {
|
"context": {
|
||||||
|
@ -571,7 +573,8 @@
|
||||||
"mute_export": "ミュートのエクスポート",
|
"mute_export": "ミュートのエクスポート",
|
||||||
"allow_following_move": "フォロー中のアカウントが引っ越したとき、自動フォローを許可する",
|
"allow_following_move": "フォロー中のアカウントが引っ越したとき、自動フォローを許可する",
|
||||||
"setting_changed": "規定の設定と異なっています",
|
"setting_changed": "規定の設定と異なっています",
|
||||||
"greentext": "引用を緑色で表示"
|
"greentext": "引用を緑色で表示",
|
||||||
|
"sensitive_by_default": "はじめから投稿をセンシティブとして設定"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"day": "{0}日",
|
"day": "{0}日",
|
||||||
|
|
|
@ -35,7 +35,11 @@
|
||||||
"retry": "다시 시도하십시오",
|
"retry": "다시 시도하십시오",
|
||||||
"error_retry": "다시 시도하십시오",
|
"error_retry": "다시 시도하십시오",
|
||||||
"generic_error": "잘못되었습니다",
|
"generic_error": "잘못되었습니다",
|
||||||
"more": "더 보기"
|
"more": "더 보기",
|
||||||
|
"role": {
|
||||||
|
"moderator": "중재자",
|
||||||
|
"admin": "관리자"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "로그인",
|
"login": "로그인",
|
||||||
|
@ -85,7 +89,8 @@
|
||||||
"repeated_you": "당신의 게시물을 리핏",
|
"repeated_you": "당신의 게시물을 리핏",
|
||||||
"no_more_notifications": "알림이 없습니다",
|
"no_more_notifications": "알림이 없습니다",
|
||||||
"migrated_to": "이사했습니다",
|
"migrated_to": "이사했습니다",
|
||||||
"reacted_with": "{0} 로 반응했습니다"
|
"reacted_with": "{0} 로 반응했습니다",
|
||||||
|
"error": "알림 불러오기 실패: {0}"
|
||||||
},
|
},
|
||||||
"post_status": {
|
"post_status": {
|
||||||
"new_status": "새 게시물 게시",
|
"new_status": "새 게시물 게시",
|
||||||
|
@ -93,7 +98,10 @@
|
||||||
"account_not_locked_warning_link": "잠김",
|
"account_not_locked_warning_link": "잠김",
|
||||||
"attachments_sensitive": "첨부물을 민감함으로 설정",
|
"attachments_sensitive": "첨부물을 민감함으로 설정",
|
||||||
"content_type": {
|
"content_type": {
|
||||||
"text/plain": "평문"
|
"text/plain": "평문",
|
||||||
|
"text/bbcode": "BBCode",
|
||||||
|
"text/markdown": "Markdown",
|
||||||
|
"text/html": "HTML"
|
||||||
},
|
},
|
||||||
"content_warning": "주제 (필수 아님)",
|
"content_warning": "주제 (필수 아님)",
|
||||||
"default": "인천공항에 도착했습니다.",
|
"default": "인천공항에 도착했습니다.",
|
||||||
|
@ -106,7 +114,13 @@
|
||||||
"unlisted": "비공개 - 공개 타임라인에 게시 안 함"
|
"unlisted": "비공개 - 공개 타임라인에 게시 안 함"
|
||||||
},
|
},
|
||||||
"preview_empty": "아무것도 없습니다",
|
"preview_empty": "아무것도 없습니다",
|
||||||
"preview": "미리보기"
|
"preview": "미리보기",
|
||||||
|
"scope_notice": {
|
||||||
|
"public": "이 글은 누구나 볼 수 있습니다"
|
||||||
|
},
|
||||||
|
"media_description_error": "파일을 올리지 못하였습니다. 다시한번 시도하여 주십시오",
|
||||||
|
"empty_status_error": "글을 입력하십시오",
|
||||||
|
"media_description": "첨부파일 설명"
|
||||||
},
|
},
|
||||||
"registration": {
|
"registration": {
|
||||||
"bio": "소개",
|
"bio": "소개",
|
||||||
|
@ -288,7 +302,16 @@
|
||||||
"borders": "테두리",
|
"borders": "테두리",
|
||||||
"buttons": "버튼",
|
"buttons": "버튼",
|
||||||
"inputs": "입력칸",
|
"inputs": "입력칸",
|
||||||
"faint_text": "흐려진 텍스트"
|
"faint_text": "흐려진 텍스트",
|
||||||
|
"chat": {
|
||||||
|
"border": "경계선",
|
||||||
|
"outgoing": "송신",
|
||||||
|
"incoming": "수신"
|
||||||
|
},
|
||||||
|
"selectedMenu": "선택된 메뉴 요소",
|
||||||
|
"selectedPost": "선택된 글",
|
||||||
|
"icons": "아이콘",
|
||||||
|
"alert_warning": "경고"
|
||||||
},
|
},
|
||||||
"radii": {
|
"radii": {
|
||||||
"_tab_label": "둥글기"
|
"_tab_label": "둥글기"
|
||||||
|
@ -364,9 +387,25 @@
|
||||||
"generate_new_recovery_codes": "새로운 복구 코드를 작성",
|
"generate_new_recovery_codes": "새로운 복구 코드를 작성",
|
||||||
"title": "2단계인증",
|
"title": "2단계인증",
|
||||||
"confirm_and_enable": "OTP 확인과 활성화",
|
"confirm_and_enable": "OTP 확인과 활성화",
|
||||||
"setup_otp": "OTP 설치"
|
"setup_otp": "OTP 설치",
|
||||||
|
"otp": "OTP"
|
||||||
},
|
},
|
||||||
"security": "보안"
|
"security": "보안",
|
||||||
|
"emoji_reactions_on_timeline": "이모지 반응을 타임라인으로 표시",
|
||||||
|
"avatar_size_instruction": "크기를 150x150 이상으로 설정할 것을 추장합니다.",
|
||||||
|
"blocks_tab": "차단",
|
||||||
|
"notification_setting_privacy": "보안",
|
||||||
|
"user_mutes": "사용자",
|
||||||
|
"notification_visibility_emoji_reactions": "반응",
|
||||||
|
"profile_fields": {
|
||||||
|
"value": "내용"
|
||||||
|
},
|
||||||
|
"mutes_and_blocks": "침묵과 차단",
|
||||||
|
"chatMessageRadius": "챗 메시지",
|
||||||
|
"change_email": "전자메일 주소 바꾸기",
|
||||||
|
"changed_email": "메일주소가 갱신되었습니다!",
|
||||||
|
"bot": "이 계정은 bot입니다",
|
||||||
|
"mutes_tab": "침묵"
|
||||||
},
|
},
|
||||||
"timeline": {
|
"timeline": {
|
||||||
"collapse": "접기",
|
"collapse": "접기",
|
||||||
|
@ -445,7 +484,11 @@
|
||||||
"votes": "표",
|
"votes": "표",
|
||||||
"vote": "투표",
|
"vote": "투표",
|
||||||
"type": "투표 형식",
|
"type": "투표 형식",
|
||||||
"expiry": "투표 기간"
|
"expiry": "투표 기간",
|
||||||
|
"votes_count": "{count} 표 | {count} 표",
|
||||||
|
"people_voted_count": "{count} 명 투표 | {count} 명 투표",
|
||||||
|
"option": "선택지",
|
||||||
|
"add_option": "선택지 추가"
|
||||||
},
|
},
|
||||||
"media_modal": {
|
"media_modal": {
|
||||||
"next": "다음",
|
"next": "다음",
|
||||||
|
@ -500,5 +543,44 @@
|
||||||
},
|
},
|
||||||
"federation": "연합"
|
"federation": "연합"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"shoutbox": {
|
||||||
|
"title": "Shoutbox"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"years_short": "{0} 년",
|
||||||
|
"year_short": "{0} 년",
|
||||||
|
"years": "{0} 년",
|
||||||
|
"year": "{0} 년",
|
||||||
|
"weeks_short": "{0} 주일",
|
||||||
|
"week_short": "{0} 주일",
|
||||||
|
"weeks": "{0} 주일",
|
||||||
|
"week": "{0} 주일",
|
||||||
|
"seconds_short": "{0} 초",
|
||||||
|
"second_short": "{0} 초",
|
||||||
|
"seconds": "{0} 초",
|
||||||
|
"second": "{0} 초",
|
||||||
|
"now_short": "방금",
|
||||||
|
"now": "방끔",
|
||||||
|
"months_short": "{0} 달 전",
|
||||||
|
"month_short": "{0} 달 전",
|
||||||
|
"months": "{0} 달 전",
|
||||||
|
"month": "{0} 달 전",
|
||||||
|
"minutes_short": "{0} 분",
|
||||||
|
"minute_short": "{0} 분",
|
||||||
|
"minutes": "{0} 분",
|
||||||
|
"minute": "{0} 분",
|
||||||
|
"in_past": "{0} 전",
|
||||||
|
"hours_short": "{0} 시간",
|
||||||
|
"hour_short": "{0} 시간",
|
||||||
|
"hours": "{0} 시간",
|
||||||
|
"hour": "{0} 시간",
|
||||||
|
"days_short": "{0} 일",
|
||||||
|
"day_short": "{0} 일",
|
||||||
|
"days": "{0} 일",
|
||||||
|
"day": "{0} 일"
|
||||||
|
},
|
||||||
|
"remote_user_resolver": {
|
||||||
|
"error": "찾을 수 없습니다."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,11 @@
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"retry": "重试",
|
"retry": "重试",
|
||||||
"error_retry": "请重试",
|
"error_retry": "请重试",
|
||||||
"loading": "载入中…"
|
"loading": "载入中…",
|
||||||
|
"role": {
|
||||||
|
"moderator": "监察员",
|
||||||
|
"admin": "管理员"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"image_cropper": {
|
"image_cropper": {
|
||||||
"crop_picture": "裁剪图片",
|
"crop_picture": "裁剪图片",
|
||||||
|
@ -120,7 +124,9 @@
|
||||||
"expiry": "投票期限",
|
"expiry": "投票期限",
|
||||||
"expires_in": "投票于 {0} 后结束",
|
"expires_in": "投票于 {0} 后结束",
|
||||||
"expired": "投票 {0} 前已结束",
|
"expired": "投票 {0} 前已结束",
|
||||||
"not_enough_options": "投票的选项太少"
|
"not_enough_options": "投票的选项太少",
|
||||||
|
"votes_count": "{count} 票 | {count} 票",
|
||||||
|
"people_voted_count": "{count} 人已投票 | {count} 人已投票"
|
||||||
},
|
},
|
||||||
"stickers": {
|
"stickers": {
|
||||||
"add_sticker": "添加贴纸"
|
"add_sticker": "添加贴纸"
|
||||||
|
@ -183,7 +189,9 @@
|
||||||
"password_required": "不能留空",
|
"password_required": "不能留空",
|
||||||
"password_confirmation_required": "不能留空",
|
"password_confirmation_required": "不能留空",
|
||||||
"password_confirmation_match": "密码不一致"
|
"password_confirmation_match": "密码不一致"
|
||||||
}
|
},
|
||||||
|
"reason_placeholder": "此实例的注册需要手动批准。\n请让管理员知道您为什么想要注册。",
|
||||||
|
"reason": "注册理由"
|
||||||
},
|
},
|
||||||
"selectable_list": {
|
"selectable_list": {
|
||||||
"select_all": "选择全部"
|
"select_all": "选择全部"
|
||||||
|
@ -552,7 +560,8 @@
|
||||||
"mute_import": "隐藏名单导入",
|
"mute_import": "隐藏名单导入",
|
||||||
"mute_export_button": "导出你的隐藏名单到一个 csv 文件",
|
"mute_export_button": "导出你的隐藏名单到一个 csv 文件",
|
||||||
"mute_export": "隐藏名单导出",
|
"mute_export": "隐藏名单导出",
|
||||||
"hide_wallpaper": "隐藏实例壁纸"
|
"hide_wallpaper": "隐藏实例壁纸",
|
||||||
|
"setting_changed": "与默认设置不同"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"day": "{0} 天",
|
"day": "{0} 天",
|
||||||
|
@ -683,7 +692,8 @@
|
||||||
"show_repeats": "显示转发",
|
"show_repeats": "显示转发",
|
||||||
"hide_repeats": "隐藏转发",
|
"hide_repeats": "隐藏转发",
|
||||||
"message": "消息",
|
"message": "消息",
|
||||||
"mention": "提及"
|
"mention": "提及",
|
||||||
|
"bot": "机器人"
|
||||||
},
|
},
|
||||||
"user_profile": {
|
"user_profile": {
|
||||||
"timeline_title": "用户时间线",
|
"timeline_title": "用户时间线",
|
||||||
|
|
|
@ -115,6 +115,9 @@ const chats = {
|
||||||
},
|
},
|
||||||
handleMessageError ({ commit }, value) {
|
handleMessageError ({ commit }, value) {
|
||||||
commit('handleMessageError', { commit, ...value })
|
commit('handleMessageError', { commit, ...value })
|
||||||
|
},
|
||||||
|
cullOlderMessages ({ commit }, chatId) {
|
||||||
|
commit('cullOlderMessages', chatId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
@ -227,6 +230,9 @@ const chats = {
|
||||||
handleMessageError (state, { chatId, fakeId, isRetry }) {
|
handleMessageError (state, { chatId, fakeId, isRetry }) {
|
||||||
const chatMessageService = state.openedChatMessageServices[chatId]
|
const chatMessageService = state.openedChatMessageServices[chatId]
|
||||||
chatService.handleMessageError(chatMessageService, fakeId, isRetry)
|
chatService.handleMessageError(chatMessageService, fakeId, isRetry)
|
||||||
|
},
|
||||||
|
cullOlderMessages (state, chatId) {
|
||||||
|
chatService.cullOlderMessages(state.openedChatMessageServices[chatId])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,8 @@ export const defaultState = {
|
||||||
greentext: undefined, // instance default
|
greentext: undefined, // instance default
|
||||||
hidePostStats: undefined, // instance default
|
hidePostStats: undefined, // instance default
|
||||||
hideUserStats: undefined, // instance default
|
hideUserStats: undefined, // instance default
|
||||||
virtualScrolling: undefined // instance default
|
virtualScrolling: undefined, // instance default
|
||||||
|
sensitiveByDefault: undefined // instance default
|
||||||
}
|
}
|
||||||
|
|
||||||
// caching the instance default properties
|
// caching the instance default properties
|
||||||
|
|
|
@ -43,6 +43,7 @@ const defaultState = {
|
||||||
subjectLineBehavior: 'email',
|
subjectLineBehavior: 'email',
|
||||||
theme: 'pleroma-dark',
|
theme: 'pleroma-dark',
|
||||||
virtualScrolling: true,
|
virtualScrolling: true,
|
||||||
|
sensitiveByDefault: false,
|
||||||
|
|
||||||
// Nasty stuff
|
// Nasty stuff
|
||||||
customEmoji: [],
|
customEmoji: [],
|
||||||
|
|
|
@ -13,7 +13,11 @@ import {
|
||||||
omitBy
|
omitBy
|
||||||
} from 'lodash'
|
} from 'lodash'
|
||||||
import { set } from 'vue'
|
import { set } from 'vue'
|
||||||
import { isStatusNotification, maybeShowNotification } from '../services/notification_utils/notification_utils.js'
|
import {
|
||||||
|
isStatusNotification,
|
||||||
|
isValidNotification,
|
||||||
|
maybeShowNotification
|
||||||
|
} from '../services/notification_utils/notification_utils.js'
|
||||||
import apiService from '../services/api/api.service.js'
|
import apiService from '../services/api/api.service.js'
|
||||||
|
|
||||||
const emptyTl = (userId = 0) => ({
|
const emptyTl = (userId = 0) => ({
|
||||||
|
@ -310,8 +314,24 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateNotificationsMinMaxId = (state, notification) => {
|
||||||
|
state.notifications.maxId = notification.id > state.notifications.maxId
|
||||||
|
? notification.id
|
||||||
|
: state.notifications.maxId
|
||||||
|
state.notifications.minId = notification.id < state.notifications.minId
|
||||||
|
? notification.id
|
||||||
|
: state.notifications.minId
|
||||||
|
}
|
||||||
|
|
||||||
const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters, newNotificationSideEffects }) => {
|
const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters, newNotificationSideEffects }) => {
|
||||||
each(notifications, (notification) => {
|
each(notifications, (notification) => {
|
||||||
|
// If invalid notification, update ids but don't add it to store
|
||||||
|
if (!isValidNotification(notification)) {
|
||||||
|
console.error('Invalid notification:', notification)
|
||||||
|
updateNotificationsMinMaxId(state, notification)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isStatusNotification(notification.type)) {
|
if (isStatusNotification(notification.type)) {
|
||||||
notification.action = addStatusToGlobalStorage(state, notification.action).item
|
notification.action = addStatusToGlobalStorage(state, notification.action).item
|
||||||
notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
|
notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
|
||||||
|
@ -323,12 +343,7 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
|
||||||
|
|
||||||
// Only add a new notification if we don't have one for the same action
|
// Only add a new notification if we don't have one for the same action
|
||||||
if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
|
if (!state.notifications.idStore.hasOwnProperty(notification.id)) {
|
||||||
state.notifications.maxId = notification.id > state.notifications.maxId
|
updateNotificationsMinMaxId(state, notification)
|
||||||
? notification.id
|
|
||||||
: state.notifications.maxId
|
|
||||||
state.notifications.minId = notification.id < state.notifications.minId
|
|
||||||
? notification.id
|
|
||||||
: state.notifications.minId
|
|
||||||
|
|
||||||
state.notifications.data.push(notification)
|
state.notifications.data.push(notification)
|
||||||
state.notifications.idStore[notification.id] = notification
|
state.notifications.idStore[notification.id] = notification
|
||||||
|
|
|
@ -48,6 +48,22 @@ const deleteMessage = (storage, messageId) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cullOlderMessages = (storage) => {
|
||||||
|
const maxIndex = storage.messages.length
|
||||||
|
const minIndex = maxIndex - 50
|
||||||
|
if (maxIndex <= 50) return
|
||||||
|
|
||||||
|
storage.messages = _.sortBy(storage.messages, ['id'])
|
||||||
|
storage.minId = storage.messages[minIndex].id
|
||||||
|
for (const message of storage.messages) {
|
||||||
|
if (message.id < storage.minId) {
|
||||||
|
delete storage.idIndex[message.id]
|
||||||
|
delete storage.idempotencyKeyIndex[message.idempotency_key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storage.messages = storage.messages.slice(minIndex, maxIndex)
|
||||||
|
}
|
||||||
|
|
||||||
const handleMessageError = (storage, fakeId, isRetry) => {
|
const handleMessageError = (storage, fakeId, isRetry) => {
|
||||||
if (!storage) { return }
|
if (!storage) { return }
|
||||||
const fakeMessage = storage.idIndex[fakeId]
|
const fakeMessage = storage.idIndex[fakeId]
|
||||||
|
@ -201,6 +217,7 @@ const ChatService = {
|
||||||
empty,
|
empty,
|
||||||
getView,
|
getView,
|
||||||
deleteMessage,
|
deleteMessage,
|
||||||
|
cullOlderMessages,
|
||||||
resetNewMessageCount,
|
resetNewMessageCount,
|
||||||
clear,
|
clear,
|
||||||
handleMessageError
|
handleMessageError
|
||||||
|
|
|
@ -203,7 +203,8 @@ export const parseUser = (data) => {
|
||||||
output.rights = output.rights || {}
|
output.rights = output.rights || {}
|
||||||
output.notification_settings = output.notification_settings || {}
|
output.notification_settings = output.notification_settings || {}
|
||||||
|
|
||||||
// Convert punycode to unicode
|
// Convert punycode to unicode for UI
|
||||||
|
output.screen_name_ui = output.screen_name
|
||||||
if (output.screen_name.includes('@')) {
|
if (output.screen_name.includes('@')) {
|
||||||
const parts = output.screen_name.split('@')
|
const parts = output.screen_name.split('@')
|
||||||
let unicodeDomain = punycode.toUnicode(parts[1])
|
let unicodeDomain = punycode.toUnicode(parts[1])
|
||||||
|
@ -211,7 +212,7 @@ export const parseUser = (data) => {
|
||||||
// Add some identifier so users can potentially spot spoofing attempts:
|
// Add some identifier so users can potentially spot spoofing attempts:
|
||||||
// lain.com and xn--lin-6cd.com would appear identical otherwise.
|
// lain.com and xn--lin-6cd.com would appear identical otherwise.
|
||||||
unicodeDomain = '🌏' + unicodeDomain
|
unicodeDomain = '🌏' + unicodeDomain
|
||||||
output.screen_name = [parts[0], unicodeDomain].join('@')
|
output.screen_name_ui = [parts[0], unicodeDomain].join('@')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,13 @@ const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reactio
|
||||||
|
|
||||||
export const isStatusNotification = (type) => includes(statusNotifications, type)
|
export const isStatusNotification = (type) => includes(statusNotifications, type)
|
||||||
|
|
||||||
|
export const isValidNotification = (notification) => {
|
||||||
|
if (isStatusNotification(notification.type) && !notification.status) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const sortById = (a, b) => {
|
const sortById = (a, b) => {
|
||||||
const seqA = Number(a.id)
|
const seqA = Number(a.id)
|
||||||
const seqB = Number(b.id)
|
const seqB = Number(b.id)
|
||||||
|
|
|
@ -31,13 +31,15 @@ const testGetters = {
|
||||||
const localUser = {
|
const localUser = {
|
||||||
id: 100,
|
id: 100,
|
||||||
is_local: true,
|
is_local: true,
|
||||||
screen_name: 'testUser'
|
screen_name: 'testUser',
|
||||||
|
screen_name_ui: 'testUser'
|
||||||
}
|
}
|
||||||
|
|
||||||
const extUser = {
|
const extUser = {
|
||||||
id: 100,
|
id: 100,
|
||||||
is_local: false,
|
is_local: false,
|
||||||
screen_name: 'testUser@test.instance'
|
screen_name: 'testUser@test.instance',
|
||||||
|
screen_name_ui: 'testUser@test.instance'
|
||||||
}
|
}
|
||||||
|
|
||||||
const externalProfileStore = new Vuex.Store({
|
const externalProfileStore = new Vuex.Store({
|
||||||
|
|
|
@ -88,4 +88,21 @@ describe('chatService', () => {
|
||||||
expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message'])
|
expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('.cullOlderMessages', () => {
|
||||||
|
it('keeps 50 newest messages and idIndex matches', () => {
|
||||||
|
const chat = chatService.empty()
|
||||||
|
|
||||||
|
for (let i = 100; i > 0; i--) {
|
||||||
|
// Use decimal values with toFixed to hack together constant length predictable strings
|
||||||
|
chatService.add(chat, { messages: [{ ...message1, id: 'a' + (i / 1000).toFixed(3), idempotency_key: i }] })
|
||||||
|
}
|
||||||
|
chatService.cullOlderMessages(chat)
|
||||||
|
expect(chat.messages.length).to.eql(50)
|
||||||
|
expect(chat.messages[0].id).to.eql('a0.051')
|
||||||
|
expect(chat.minId).to.eql('a0.051')
|
||||||
|
expect(chat.messages[49].id).to.eql('a0.100')
|
||||||
|
expect(Object.keys(chat.idIndex).length).to.eql(50)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -315,7 +315,7 @@ describe('API Entities normalizer', () => {
|
||||||
it('converts IDN to unicode and marks it as internatonal', () => {
|
it('converts IDN to unicode and marks it as internatonal', () => {
|
||||||
const user = makeMockUserMasto({ acct: 'lain@xn--lin-6cd.com' })
|
const user = makeMockUserMasto({ acct: 'lain@xn--lin-6cd.com' })
|
||||||
|
|
||||||
expect(parseUser(user)).to.have.property('screen_name').that.equal('lain@🌏lаin.com')
|
expect(parseUser(user)).to.have.property('screen_name_ui').that.equal('lain@🌏lаin.com')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -7842,9 +7842,10 @@ shebang-regex@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||||
|
|
||||||
shelljs@^0.7.4:
|
shelljs@^0.8.4:
|
||||||
version "0.7.8"
|
version "0.8.4"
|
||||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3"
|
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2"
|
||||||
|
integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.0.0"
|
glob "^7.0.0"
|
||||||
interpret "^1.0.0"
|
interpret "^1.0.0"
|
||||||
|
|
Loading…
Reference in a new issue