logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: 91f93d4a55cbf927a6adee35dc184b09d1f9b75d
parent 670abd633f97e75cf35981ee0f617d0be1b43816
Author: Shpuld Shpludson <shp@cock.li>
Date:   Sun, 28 Feb 2021 15:37:39 +0000

Merge branch 'develop' into 'feat/timeline-quick-settings'

# Conflicts:
#   CHANGELOG.md

Diffstat:

MCHANGELOG.md5+++++
Mpackage.json2+-
Msrc/components/basic_user_card/basic_user_card.vue2+-
Msrc/components/chat/chat.js2+-
Msrc/components/chat/chat.scss6+++---
Msrc/components/chat_title/chat_title.js2+-
Msrc/components/emoji_input/emoji_input.js8++++++++
Msrc/components/emoji_input/emoji_input.vue2+-
Msrc/components/emoji_input/suggestor.js4++--
Msrc/components/mfa_form/recovery_form.vue4++--
Msrc/components/mfa_form/totp_form.vue4++--
Msrc/components/notification/notification.vue10+++++-----
Msrc/components/popover/popover.js7+++++--
Msrc/components/post_status_form/post_status_form.js4++--
Msrc/components/react_button/react_button.js6++++++
Msrc/components/react_button/react_button.vue1+
Msrc/components/scope_selector/scope_selector.vue8++++----
Msrc/components/search/search.vue2+-
Msrc/components/search_bar/search_bar.vue6+++---
Msrc/components/settings_modal/helpers/modified_indicator.vue20++++++++++----------
Msrc/components/settings_modal/tabs/filtering_tab.vue2+-
Msrc/components/settings_modal/tabs/general_tab.vue7++++++-
Msrc/components/status/status.js4++--
Msrc/components/status/status.vue6+++---
Msrc/components/user_avatar/user_avatar.vue4++--
Msrc/components/user_card/user_card.vue4++--
Msrc/components/user_list_popover/user_list_popover.vue2+-
Msrc/components/user_reporting_modal/user_reporting_modal.vue2+-
Msrc/i18n/en.json1+
Msrc/modules/config.js3++-
Msrc/modules/instance.js1+
Msrc/modules/statuses.js29++++++++++++++++++++++-------
Msrc/services/entity_normalizer/entity_normalizer.service.js5+++--
Msrc/services/notification_utils/notification_utils.js7+++++++
Mtest/unit/specs/components/user_profile.spec.js6++++--
Mtest/unit/specs/services/entity_normalizer/entity_normalizer.spec.js2+-
Myarn.lock7++++---
37 files changed, 127 insertions(+), 70 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -9,14 +9,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - 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 - 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 ### Added - Added reason field for registration when approval is required - Added a quick settings to timeline header for easier access +- Added option to mark posts as sensitive by default ## [2.2.3] - 2021-01-18 ### Added diff --git a/package.json b/package.json @@ -103,7 +103,7 @@ "selenium-server": "2.53.1", "semver": "^5.3.0", "serviceworker-webpack-plugin": "^1.0.0", - "shelljs": "^0.7.4", + "shelljs": "^0.8.4", "sinon": "^2.1.0", "sinon-chai": "^2.8.0", "stylelint": "^13.6.1", diff --git a/src/components/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue @@ -42,7 +42,7 @@ class="basic-user-card-screen-name" :to="userProfileLink(user)" > - @{{ user.screen_name }} + @{{ user.screen_name_ui }} </router-link> </div> <slot /> diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js @@ -73,7 +73,7 @@ const Chat = { }, formPlaceholder () { 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 { return '' } diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss @@ -98,10 +98,10 @@ .unread-message-count { font-size: 0.8em; left: 50%; - transform: translate(-50%, 0); - border-radius: 100%; margin-top: -1rem; - padding: 0; + padding: 0.1em; + border-radius: 50px; + position: absolute; } .chat-loading-error { diff --git a/src/components/chat_title/chat_title.js b/src/components/chat_title/chat_title.js @@ -12,7 +12,7 @@ export default Vue.component('chat-title', { ], computed: { title () { - return this.user ? this.user.screen_name : '' + return this.user ? this.user.screen_name_ui : '' }, htmlTitle () { return this.user ? this.user.name_html : '' diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js @@ -194,11 +194,18 @@ const EmojiInput = { } }, methods: { + focusPickerInput () { + const pickerEl = this.$refs.picker.$el + if (!pickerEl) return + const pickerInput = pickerEl.querySelector('input') + if (pickerInput) pickerInput.focus() + }, triggerShowPicker () { this.showPicker = true this.$refs.picker.startEmojiLoad() this.$nextTick(() => { this.scrollIntoView() + this.focusPickerInput() }) // This temporarily disables "click outside" handler // since external trigger also means click originates @@ -214,6 +221,7 @@ const EmojiInput = { if (this.showPicker) { this.scrollIntoView() this.$refs.picker.startEmojiLoad() + this.$nextTick(this.focusPickerInput) } }, replace (replacement) { diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue @@ -9,8 +9,8 @@ <button v-if="!hideEmojiButton" class="button-unstyled emoji-picker-icon" - @click.prevent="togglePicker" type="button" + @click.prevent="togglePicker" > <FAIcon :icon="['far', 'smile-beam']" /> </button> diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js @@ -116,8 +116,8 @@ export const suggestUsers = ({ dispatch, state }) => { return diff + nameAlphabetically + screenNameAlphabetically /* eslint-disable camelcase */ - }).map(({ screen_name, name, profile_image_url_original }) => ({ - displayText: screen_name, + }).map(({ screen_name, screen_name_ui, name, profile_image_url_original }) => ({ + displayText: screen_name_ui, detailText: name, imageUrl: profile_image_url_original, replacement: '@' + screen_name + ' ' diff --git a/src/components/mfa_form/recovery_form.vue b/src/components/mfa_form/recovery_form.vue @@ -25,16 +25,16 @@ <div> <button class="button-unstyled -link" - @click.prevent="requireTOTP" type="button" + @click.prevent="requireTOTP" > {{ $t('login.enter_two_factor_code') }} </button> <br> <button class="button-unstyled -link" - @click.prevent="abortMFA" type="button" + @click.prevent="abortMFA" > {{ $t('general.cancel') }} </button> diff --git a/src/components/mfa_form/totp_form.vue b/src/components/mfa_form/totp_form.vue @@ -27,16 +27,16 @@ <div> <button class="button-unstyled -link" - @click.prevent="requireRecovery" type="button" + @click.prevent="requireRecovery" > {{ $t('login.enter_recovery_code') }} </button> <br> <button class="button-unstyled -link" - @click.prevent="abortMFA" type="button" + @click.prevent="abortMFA" > {{ $t('general.cancel') }} </button> diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue @@ -11,7 +11,7 @@ > <small> <router-link :to="userProfileLink"> - {{ notification.from_profile.screen_name }} + {{ notification.from_profile.screen_name_ui }} </router-link> </small> <button @@ -54,14 +54,14 @@ <bdi v-if="!!notification.from_profile.name_html" class="username" - :title="'@'+notification.from_profile.screen_name" + :title="'@'+notification.from_profile.screen_name_ui" v-html="notification.from_profile.name_html" /> <!-- eslint-enable vue/no-v-html --> <span v-else class="username" - :title="'@'+notification.from_profile.screen_name" + :title="'@'+notification.from_profile.screen_name_ui" >{{ notification.from_profile.name }}</span> <span v-if="notification.type === 'like'"> <FAIcon @@ -152,7 +152,7 @@ :to="userProfileLink" class="follow-name" > - @{{ notification.from_profile.screen_name }} + @{{ notification.from_profile.screen_name_ui }} </router-link> <div v-if="notification.type === 'follow_request'" @@ -177,7 +177,7 @@ class="move-text" > <router-link :to="targetUserProfileLink"> - @{{ notification.target.screen_name }} + @{{ notification.target.screen_name_ui }} </router-link> </div> <template v-else> diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js @@ -128,9 +128,12 @@ const Popover = { } }, showPopover () { - if (this.hidden) this.$emit('show') + const wasHidden = this.hidden this.hidden = false - this.$nextTick(this.updateStyles) + this.$nextTick(() => { + if (wasHidden) this.$emit('show') + this.updateStyles() + }) }, hidePopover () { if (!this.hidden) this.$emit('close') diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js @@ -115,7 +115,7 @@ const PostStatusForm = { ? this.copyMessageScope : this.$store.state.users.currentUser.default_scope - const { postContentType: contentType } = this.$store.getters.mergedConfig + const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig return { dropFiles: [], @@ -126,7 +126,7 @@ const PostStatusForm = { newStatus: { spoilerText: this.subject || '', status: statusText, - nsfw: false, + nsfw: !!sensitiveByDefault, files: [], poll: {}, mediaDescriptions: {}, diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js @@ -23,6 +23,12 @@ const ReactButton = { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) } close() + }, + focusInput () { + this.$nextTick(() => { + const input = this.$el.querySelector('input') + if (input) input.focus() + }) } }, computed: { diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue @@ -6,6 +6,7 @@ :offset="{ y: 5 }" :bound-to="{ x: 'container' }" remove-padding + @show="focusInput" > <div slot="content" diff --git a/src/components/scope_selector/scope_selector.vue b/src/components/scope_selector/scope_selector.vue @@ -8,8 +8,8 @@ class="button-unstyled scope" :class="css.direct" :title="$t('post_status.scope.direct')" - @click="changeVis('direct')" type="button" + @click="changeVis('direct')" > <FAIcon icon="envelope" @@ -21,8 +21,8 @@ class="button-unstyled scope" :class="css.private" :title="$t('post_status.scope.private')" - @click="changeVis('private')" type="button" + @click="changeVis('private')" > <FAIcon icon="lock" @@ -34,8 +34,8 @@ class="button-unstyled scope" :class="css.unlisted" :title="$t('post_status.scope.unlisted')" - @click="changeVis('unlisted')" type="button" + @click="changeVis('unlisted')" > <FAIcon icon="lock-open" @@ -47,8 +47,8 @@ class="button-unstyled scope" :class="css.public" :title="$t('post_status.scope.public')" - @click="changeVis('public')" type="button" + @click="changeVis('public')" > <FAIcon icon="globe" diff --git a/src/components/search/search.vue b/src/components/search/search.vue @@ -15,8 +15,8 @@ > <button class="btn button-default search-button" - @click="newQuery(searchTerm)" type="submit" + @click="newQuery(searchTerm)" > <FAIcon icon="search" /> </button> diff --git a/src/components/search_bar/search_bar.vue b/src/components/search_bar/search_bar.vue @@ -7,8 +7,8 @@ v-if="hidden" class="button-unstyled nav-icon" :title="$t('nav.search')" - @click.prevent.stop="toggleHidden" type="button" + @click.prevent.stop="toggleHidden" > <FAIcon fixed-width @@ -28,8 +28,8 @@ > <button class="button-default search-button" - @click="find(searchTerm)" type="submit" + @click="find(searchTerm)" > <FAIcon fixed-width @@ -38,8 +38,8 @@ </button> <button class="button-unstyled cancel-search" - @click.prevent.stop="toggleHidden" type="button" + @click.prevent.stop="toggleHidden" > <FAIcon fixed-width diff --git a/src/components/settings_modal/helpers/modified_indicator.vue b/src/components/settings_modal/helpers/modified_indicator.vue @@ -2,21 +2,21 @@ <span v-if="changed" class="ModifiedIndicator" - > + > <Popover trigger="hover" - > + > <span slot="trigger"> &nbsp; - <FAIcon - icon="wrench" - /> + <FAIcon + icon="wrench" + /> </span> <div - class="modified-tooltip" slot="content" - > - {{ $t('settings.setting_changed') }} + class="modified-tooltip" + > + {{ $t('settings.setting_changed') }} </div> </Popover> </span> @@ -32,8 +32,8 @@ library.add( ) export default { - props: ['changed'], - components: { Popover } + components: { Popover }, + props: ['changed'] } </script> diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue @@ -75,8 +75,8 @@ <p>{{ $t('settings.filtering_explanation') }}</p> <textarea id="muteWords" - class="resize-height" v-model="muteWordsString" + class="resize-height" /> </div> <div> diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue @@ -144,7 +144,12 @@ </li> <li> <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> </li> <li> diff --git a/src/components/status/status.js b/src/components/status/status.js @@ -136,7 +136,7 @@ const 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 }, retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) }, status () { @@ -216,7 +216,7 @@ const Status = { return this.status.in_reply_to_screen_name } else { 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 () { diff --git a/src/components/status/status.vue b/src/components/status/status.vue @@ -26,7 +26,7 @@ icon="retweet" /> <router-link :to="userProfileLink"> - {{ status.user.screen_name }} + {{ status.user.screen_name_ui }} </router-link> </small> <small @@ -156,10 +156,10 @@ </h4> <router-link class="account-name" - :title="status.user.screen_name" + :title="status.user.screen_name_ui" :to="userProfileLink" > - {{ status.user.screen_name }} + {{ status.user.screen_name_ui }} </router-link> <img v-if="!!(status.user && status.user.favicon)" diff --git a/src/components/user_avatar/user_avatar.vue b/src/components/user_avatar/user_avatar.vue @@ -2,8 +2,8 @@ <StillImage v-if="user" class="Avatar" - :alt="user.screen_name" - :title="user.screen_name" + :alt="user.screen_name_ui" + :title="user.screen_name_ui" :src="imgSrc(user.profile_image_url_original)" :class="{ 'avatar-compact': compact, 'better-shadow': betterShadow }" :image-load-error="imageLoadError" diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue @@ -73,10 +73,10 @@ <div class="bottom-line"> <router-link class="user-screen-name" - :title="user.screen_name" + :title="user.screen_name_ui" :to="userProfileLink(user)" > - @{{ user.screen_name }} + @{{ user.screen_name_ui }} </router-link> <template v-if="!hideBio"> <span diff --git a/src/components/user_list_popover/user_list_popover.vue b/src/components/user_list_popover/user_list_popover.vue @@ -26,7 +26,7 @@ <!-- eslint-disable vue/no-v-html --> <span v-html="user.name_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> diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue @@ -6,7 +6,7 @@ <div class="user-reporting-panel panel"> <div class="panel-heading"> <div class="title"> - {{ $t('user_reporting.title', [user.screen_name]) }} + {{ $t('user_reporting.title', [user.screen_name_ui]) }} </div> </div> <div class="panel-body"> diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -434,6 +434,7 @@ "subject_line_mastodon": "Like mastodon: copy as is", "subject_line_noop": "Do not copy", "post_status_content_type": "Post status content type", + "sensitive_by_default": "Mark posts as sensitive by default", "stop_gifs": "Play-on-hover GIFs", "streaming": "Enable automatic streaming of new posts when scrolled to the top", "user_mutes": "Users", diff --git a/src/modules/config.js b/src/modules/config.js @@ -67,7 +67,8 @@ export const defaultState = { greentext: undefined, // instance default hidePostStats: undefined, // instance default hideUserStats: undefined, // instance default - virtualScrolling: undefined // instance default + virtualScrolling: undefined, // instance default + sensitiveByDefault: undefined // instance default } // caching the instance default properties diff --git a/src/modules/instance.js b/src/modules/instance.js @@ -43,6 +43,7 @@ const defaultState = { subjectLineBehavior: 'email', theme: 'pleroma-dark', virtualScrolling: true, + sensitiveByDefault: false, // Nasty stuff customEmoji: [], diff --git a/src/modules/statuses.js b/src/modules/statuses.js @@ -13,7 +13,11 @@ import { omitBy } from 'lodash' 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' 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 }) => { 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)) { notification.action = addStatusToGlobalStorage(state, notification.action).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 if (!state.notifications.idStore.hasOwnProperty(notification.id)) { - 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 + updateNotificationsMinMaxId(state, notification) state.notifications.data.push(notification) state.notifications.idStore[notification.id] = notification diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js @@ -203,7 +203,8 @@ export const parseUser = (data) => { output.rights = output.rights || {} 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('@')) { const parts = output.screen_name.split('@') let unicodeDomain = punycode.toUnicode(parts[1]) @@ -211,7 +212,7 @@ export const parseUser = (data) => { // Add some identifier so users can potentially spot spoofing attempts: // lain.com and xn--lin-6cd.com would appear identical otherwise. unicodeDomain = '🌏' + unicodeDomain - output.screen_name = [parts[0], unicodeDomain].join('@') + output.screen_name_ui = [parts[0], unicodeDomain].join('@') } } diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js @@ -22,6 +22,13 @@ const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reactio 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 seqA = Number(a.id) const seqB = Number(b.id) diff --git a/test/unit/specs/components/user_profile.spec.js b/test/unit/specs/components/user_profile.spec.js @@ -31,13 +31,15 @@ const testGetters = { const localUser = { id: 100, is_local: true, - screen_name: 'testUser' + screen_name: 'testUser', + screen_name_ui: 'testUser' } const extUser = { id: 100, is_local: false, - screen_name: 'testUser@test.instance' + screen_name: 'testUser@test.instance', + screen_name_ui: 'testUser@test.instance' } const externalProfileStore = new Vuex.Store({ diff --git a/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js b/test/unit/specs/services/entity_normalizer/entity_normalizer.spec.js @@ -315,7 +315,7 @@ describe('API Entities normalizer', () => { it('converts IDN to unicode and marks it as internatonal', () => { 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') }) }) diff --git a/yarn.lock b/yarn.lock @@ -7842,9 +7842,10 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" -shelljs@^0.7.4: - version "0.7.8" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" +shelljs@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== dependencies: glob "^7.0.0" interpret "^1.0.0"