diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba0ad7c9..1d2b48b9 100644
--- 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
index e11396bf..372155df 100644
--- 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
index 9e410610..c53f6a9c 100644
--- 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 }}
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js
index 2780be75..b54f5fb2 100644
--- 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
index aef58495..3a26686c 100644
--- 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
index e424bb1f..edfbe7a4 100644
--- 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
index 2068a598..dc03bc9f 100644
--- 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
index 1d6c760e..ad62484d 100644
--- a/src/components/emoji_input/emoji_input.vue
+++ b/src/components/emoji_input/emoji_input.vue
@@ -9,8 +9,8 @@
diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js
index 14a2b41e..e8efbd1e 100644
--- 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
index 8457ec5d..7c594228 100644
--- a/src/components/mfa_form/recovery_form.vue
+++ b/src/components/mfa_form/recovery_form.vue
@@ -25,16 +25,16 @@
diff --git a/src/components/mfa_form/totp_form.vue b/src/components/mfa_form/totp_form.vue
index 5d80e185..4ee13992 100644
--- a/src/components/mfa_form/totp_form.vue
+++ b/src/components/mfa_form/totp_form.vue
@@ -27,16 +27,16 @@
diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue
index f56aa977..0081dee4 100644
--- a/src/components/notification/notification.vue
+++ b/src/components/notification/notification.vue
@@ -11,7 +11,7 @@
>
- {{ notification.from_profile.screen_name }}
+ {{ notification.from_profile.screen_name_ui }}
{{ notification.from_profile.name }}
- @{{ notification.from_profile.screen_name }}
+ @{{ notification.from_profile.screen_name_ui }}
- @{{ notification.target.screen_name }}
+ @{{ notification.target.screen_name_ui }}
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
index 61cc728d..391d6422 100644
--- 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
index 4148381c..e540654b 100644
--- 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
index 5e7b7580..ce82c90d 100644
--- 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
index 04734674..4839024c 100644
--- 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"
>
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index f93f4ea0..9228c78e 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -144,7 +144,12 @@
- {{ $t('settings.minimal_scopes_mode') }} {{ minimalScopesModeDefaultValue }}
+ {{ $t('settings.minimal_scopes_mode') }}
+
+
+
+
+ {{ $t('settings.sensitive_by_default') }}
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 2bf93a9e..470c01f1 100644
--- 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
index 6ee8117f..00e962f3 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -26,7 +26,7 @@
icon="retweet"
/>
- {{ status.user.screen_name }}
+ {{ status.user.screen_name_ui }}
- {{ status.user.screen_name }}
+ {{ status.user.screen_name_ui }}
- @{{ user.screen_name }}
+ @{{ user.screen_name_ui }}
- {{ user.screen_name }}
+ {{ user.screen_name_ui }}
diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue
index fb43094f..a71c02c1 100644
--- a/src/components/user_reporting_modal/user_reporting_modal.vue
+++ b/src/components/user_reporting_modal/user_reporting_modal.vue
@@ -6,7 +6,7 @@
- {{ $t('user_reporting.title', [user.screen_name]) }}
+ {{ $t('user_reporting.title', [user.screen_name_ui]) }}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index fa338652..bf775363 100644
--- 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
index f992519e..eca58c12 100644
--- 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
index 411b1caa..96de73ca 100644
--- 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
index 33c68c57..ac5d25c4 100644
--- 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
index 6ed663e1..9b2b30e6 100644
--- 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
index d912d19f..6fef1022 100644
--- 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
index 80092b41..142db73c 100644
--- 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
index a3f49b2c..759539e0 100644
--- 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
index 6682841b..6d0095db 100644
--- 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"