Merge remote-tracking branch 'origin/develop' into settings-changed

* origin/develop:
  fix fontello
  Translated using Weblate (Russian)
  Translated using Weblate (Italian)
  lint fix
  fixed copy-pasting leftovers
  improved algorithm, possibly speed too
  fix 8x spaces inside this paren
  feat/reorder-emojis-by-position-of-keyword
  rename to gravestone
  Apply 1 suggestion(s) to 1 file(s)
  change i18n phrasing
  separate reply button to its own component, add changelog entry
  add basic deletes support that works with masto WS
This commit is contained in:
Henry Jameson 2020-10-17 21:26:13 +03:00
commit a664fde02f
16 changed files with 127 additions and 26 deletions

View file

@ -8,7 +8,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- New option to optimize timeline rendering to make the site more responsive (enabled by default) - New option to optimize timeline rendering to make the site more responsive (enabled by default)
## [Unreleased patch] ## [Unreleased patch]
### Fixed ### Fixed
- Fixed chats list not updating its order when new messages come in - Fixed chats list not updating its order when new messages come in
- Fixed chat messages sometimes getting lost when you receive a message at the same time - Fixed chat messages sometimes getting lost when you receive a message at the same time
@ -16,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added ### Added
- Import/export a muted users - Import/export a muted users
- Proper handling of deletes when using websocket streaming
## [2.1.1] - 2020-09-08 ## [2.1.1] - 2020-09-08
### Changed ### Changed

View file

@ -97,6 +97,7 @@ module.exports = {
}), }),
new FontelloPlugin({ new FontelloPlugin({
config: require('../static/fontello.json'), config: require('../static/fontello.json'),
host: 'https://fontello.com',
name: 'fontello', name: 'fontello',
output: { output: {
css: 'static/[name].' + now + '.css', // [hash] is not supported. Use the current timestamp instead for versioning. css: 'static/[name].' + now + '.css', // [hash] is not supported. Use the current timestamp instead for versioning.

View file

@ -8,10 +8,20 @@ const LOAD_EMOJI_BY = 60
const LOAD_EMOJI_MARGIN = 64 const LOAD_EMOJI_MARGIN = 64
const filterByKeyword = (list, keyword = '') => { const filterByKeyword = (list, keyword = '') => {
if (keyword === '') return list
const keywordLowercase = keyword.toLowerCase() const keywordLowercase = keyword.toLowerCase()
return list.filter(emoji => let orderedEmojiList = []
emoji.displayText.toLowerCase().includes(keywordLowercase) for (const emoji of list) {
) const indexOfKeyword = emoji.displayText.toLowerCase().indexOf(keywordLowercase)
if (indexOfKeyword > -1) {
if (!Array.isArray(orderedEmojiList[indexOfKeyword])) {
orderedEmojiList[indexOfKeyword] = []
}
orderedEmojiList[indexOfKeyword].push(emoji)
}
}
return orderedEmojiList.flat()
} }
const EmojiPicker = { const EmojiPicker = {

View file

@ -28,9 +28,17 @@ const ReactButton = {
emojis () { emojis () {
if (this.filterWord !== '') { if (this.filterWord !== '') {
const filterWordLowercase = this.filterWord.toLowerCase() const filterWordLowercase = this.filterWord.toLowerCase()
return this.$store.state.instance.emoji.filter(emoji => let orderedEmojiList = []
emoji.displayText.toLowerCase().includes(filterWordLowercase) for (const emoji of this.$store.state.instance.emoji) {
) const indexOfFilterWord = emoji.displayText.toLowerCase().indexOf(filterWordLowercase)
if (indexOfFilterWord > -1) {
if (!Array.isArray(orderedEmojiList[indexOfFilterWord])) {
orderedEmojiList[indexOfFilterWord] = []
}
orderedEmojiList[indexOfFilterWord].push(emoji)
}
}
return orderedEmojiList.flat()
} }
return this.$store.state.instance.emoji || [] return this.$store.state.instance.emoji || []
}, },

View file

@ -0,0 +1,12 @@
const ReplyButton = {
name: 'ReplyButton',
props: ['status', 'replying'],
computed: {
loggedIn () {
return !!this.$store.state.users.currentUser
}
}
}
export default ReplyButton

View file

@ -0,0 +1,21 @@
<template>
<div>
<i
v-if="loggedIn"
class="button-icon button-reply icon-reply"
:title="$t('tool_tip.reply')"
:class="{'-active': replying}"
@click.prevent="$emit('toggle')"
/>
<i
v-else
class="button-icon button-reply -disabled icon-reply"
:title="$t('tool_tip.reply')"
/>
<span v-if="status.replies_count > 0">
{{ status.replies_count }}
</span>
</div>
</template>
<script src="./reply_button.js"></script>

View file

@ -1,3 +1,4 @@
import ReplyButton from '../reply_button/reply_button.vue'
import FavoriteButton from '../favorite_button/favorite_button.vue' import FavoriteButton from '../favorite_button/favorite_button.vue'
import ReactButton from '../react_button/react_button.vue' import ReactButton from '../react_button/react_button.vue'
import RetweetButton from '../retweet_button/retweet_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue'
@ -19,6 +20,7 @@ import { unescape, uniqBy } from 'lodash'
const Status = { const Status = {
name: 'Status', name: 'Status',
components: { components: {
ReplyButton,
FavoriteButton, FavoriteButton,
ReactButton, ReactButton,
RetweetButton, RetweetButton,
@ -158,7 +160,7 @@ const Status = {
return this.mergedConfig.hideFilteredStatuses return this.mergedConfig.hideFilteredStatuses
}, },
hideStatus () { hideStatus () {
return this.deleted || (this.muted && this.hideFilteredStatuses) || this.virtualHidden return (this.muted && this.hideFilteredStatuses) || this.virtualHidden
}, },
isFocused () { isFocused () {
// retweet or root of an expanded conversation // retweet or root of an expanded conversation

View file

@ -30,6 +30,18 @@ $status-margin: 0.75em;
border-left-style: solid; border-left-style: solid;
} }
.gravestone {
padding: $status-margin;
color: $fallback--faint;
color: var(--faint, $fallback--faint);
display: flex;
.deleted-text {
margin: 0.5em 0;
align-items: center;
}
}
.status-container { .status-container {
display: flex; display: flex;
padding: $status-margin; padding: $status-margin;

View file

@ -95,6 +95,7 @@
</div> </div>
<div <div
v-if="!deleted"
:class="[userClass, { highlighted: userStyle, '-repeat': retweet && !inConversation }]" :class="[userClass, { highlighted: userStyle, '-repeat': retweet && !inConversation }]"
:style="[ userStyle ]" :style="[ userStyle ]"
class="status-container" class="status-container"
@ -323,21 +324,11 @@
v-if="!noHeading && !isPreview" v-if="!noHeading && !isPreview"
class="status-actions" class="status-actions"
> >
<div> <reply-button
<i :replying="replying"
v-if="loggedIn" :status="status"
class="button-icon button-reply icon-reply" @toggle="toggleReplying"
:title="$t('tool_tip.reply')"
:class="{'-active': replying}"
@click.prevent="toggleReplying"
/> />
<i
v-else
class="button-icon button-reply -disabled icon-reply"
:title="$t('tool_tip.reply')"
/>
<span v-if="status.replies_count > 0">{{ status.replies_count }}</span>
</div>
<retweet-button <retweet-button
:visibility="status.visibility" :visibility="status.visibility"
:logged-in="loggedIn" :logged-in="loggedIn"
@ -360,6 +351,25 @@
</div> </div>
</div> </div>
<div
v-else
class="gravestone"
>
<div class="left-side">
<UserAvatar :compact="compact" />
</div>
<div class="right-side">
<div class="deleted-text">
{{ $t('status.status_deleted') }}
</div>
<reply-button
v-if="replying"
:replying="replying"
:status="status"
@toggle="toggleReplying"
/>
</div>
</div>
<div <div
v-if="replying" v-if="replying"
class="status-container reply-form" class="status-container reply-form"

View file

@ -1,5 +1,6 @@
<template> <template>
<StillImage <StillImage
v-if="user"
class="Avatar" class="Avatar"
:alt="user.screen_name" :alt="user.screen_name"
:title="user.screen_name" :title="user.screen_name"
@ -7,6 +8,11 @@
:class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }" :class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }"
:image-load-error="imageLoadError" :image-load-error="imageLoadError"
/> />
<div
v-else
class="Avatar -placeholder"
:class="{ 'avatar-compact': compact }"
/>
</template> </template>
<script src="./user_avatar.js"></script> <script src="./user_avatar.js"></script>
@ -42,5 +48,10 @@
border-radius: $fallback--avatarAltRadius; border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
} }
&.-placeholder {
background-color: $fallback--fg;
background-color: var(--fg, $fallback--fg);
}
} }
</style> </style>

View file

@ -665,7 +665,8 @@
"show_full_subject": "Show full subject", "show_full_subject": "Show full subject",
"hide_full_subject": "Hide full subject", "hide_full_subject": "Hide full subject",
"show_content": "Show content", "show_content": "Show content",
"hide_content": "Hide content" "hide_content": "Hide content",
"status_deleted": "This post was deleted"
}, },
"user_card": { "user_card": {
"approve": "Approve", "approve": "Approve",

View file

@ -578,7 +578,8 @@
"show_full_subject": "Näytä koko otsikko", "show_full_subject": "Näytä koko otsikko",
"hide_full_subject": "Piilota koko otsikko", "hide_full_subject": "Piilota koko otsikko",
"show_content": "Näytä sisältö", "show_content": "Näytä sisältö",
"hide_content": "Piilota sisältö" "hide_content": "Piilota sisältö",
"status_deleted": "Poistettu viesti"
}, },
"user_card": { "user_card": {
"approve": "Hyväksy", "approve": "Hyväksy",

View file

@ -702,7 +702,8 @@
"reply_to": "Rispondi a", "reply_to": "Rispondi a",
"delete_confirm": "Vuoi veramente eliminare questo messaggio?", "delete_confirm": "Vuoi veramente eliminare questo messaggio?",
"unbookmark": "Rimuovi segnalibro", "unbookmark": "Rimuovi segnalibro",
"bookmark": "Aggiungi segnalibro" "bookmark": "Aggiungi segnalibro",
"status_deleted": "Questo messagio è stato cancellato"
}, },
"time": { "time": {
"years_short": "{0}a", "years_short": "{0}a",

View file

@ -473,5 +473,10 @@
"tool_tip": { "tool_tip": {
"accept_follow_request": "Принять запрос на чтение", "accept_follow_request": "Принять запрос на чтение",
"reject_follow_request": "Отклонить запрос на чтение" "reject_follow_request": "Отклонить запрос на чтение"
},
"image_cropper": {
"save_without_cropping": "Сохранить не обрезая",
"save": "Сохранить",
"crop_picture": "Обрезать картинку"
} }
} }

View file

@ -72,6 +72,8 @@ const api = {
showImmediately: timelineData.visibleStatuses.length === 0, showImmediately: timelineData.visibleStatuses.length === 0,
timeline: 'friends' timeline: 'friends'
}) })
} else if (message.event === 'delete') {
dispatch('deleteStatusById', message.id)
} else if (message.event === 'pleroma:chat_update') { } else if (message.event === 'pleroma:chat_update') {
dispatch('addChatMessages', { dispatch('addChatMessages', {
chatId: message.chatUpdate.id, chatId: message.chatUpdate.id,

View file

@ -611,6 +611,10 @@ const statuses = {
commit('setDeleted', { status }) commit('setDeleted', { status })
apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials }) apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials })
}, },
deleteStatusById ({ rootState, commit }, id) {
const status = rootState.statuses.allStatusesObject[id]
commit('setDeleted', { status })
},
markStatusesAsDeleted ({ commit }, condition) { markStatusesAsDeleted ({ commit }, condition) {
commit('setManyDeleted', condition) commit('setManyDeleted', condition)
}, },