From 553155fc490d289242a0e57efab8de2834278959 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 9 Nov 2020 14:42:16 +0200 Subject: prevent call to scroll if the value doesn't change because firefox is stupid --- src/components/post_status_form/post_status_form.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/components/post_status_form') diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index de583269..6a4efc2c 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -533,10 +533,12 @@ const PostStatusForm = { const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0 const targetScroll = currentScroll + totalDelta - if (scrollerRef === window) { - scrollerRef.scroll(0, targetScroll) - } else { - scrollerRef.scrollTop = targetScroll + if (totalDelta >= 1) { + if (scrollerRef === window) { + scrollerRef.scroll(0, targetScroll) + } else { + scrollerRef.scrollTop = targetScroll + } } this.$refs['emoji-input'].resize() -- cgit v1.2.3-70-g09d2 From c1c207788a3599602888a9c5cadedd99561266af Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 9 Nov 2020 15:02:48 +0200 Subject: change method of fix to rounding --- src/components/post_status_form/post_status_form.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/components/post_status_form') diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 6a4efc2c..3ff4a3c8 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -531,14 +531,12 @@ const PostStatusForm = { !(isFormBiggerThanScroller && this.$refs.textarea.selectionStart !== this.$refs.textarea.value.length) const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0 - const targetScroll = currentScroll + totalDelta + const targetScroll = Math.round(currentScroll + totalDelta) - if (totalDelta >= 1) { - if (scrollerRef === window) { - scrollerRef.scroll(0, targetScroll) - } else { - scrollerRef.scrollTop = targetScroll - } + if (scrollerRef === window) { + scrollerRef.scroll(0, targetScroll) + } else { + scrollerRef.scrollTop = targetScroll } this.$refs['emoji-input'].resize() -- cgit v1.2.3-70-g09d2 From dd3c8631bf0ec21735d97c41e9e64d0d05643dbc Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Tue, 17 Nov 2020 17:46:26 +0200 Subject: store failed experiment --- src/components/emoji_input/emoji_input.js | 37 +++--- src/components/emoji_input/suggestor.js | 132 ++++++++++++--------- .../post_status_form/post_status_form.js | 3 +- src/modules/users.js | 4 +- 4 files changed, 98 insertions(+), 78 deletions(-) (limited to 'src/components/post_status_form') diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index 87303d08..7fe25ff4 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -114,7 +114,8 @@ const EmojiInput = { showPicker: false, temporarilyHideSuggestions: false, keepOpen: false, - disableClickOutside: false + disableClickOutside: false, + suggestions: [] } }, components: { @@ -124,21 +125,6 @@ const EmojiInput = { padEmoji () { return this.$store.getters.mergedConfig.padEmoji }, - suggestions () { - const firstchar = this.textAtCaret.charAt(0) - if (this.textAtCaret === firstchar) { return [] } - const matchedSuggestions = this.suggest(this.textAtCaret) - if (matchedSuggestions.length <= 0) { - return [] - } - return take(matchedSuggestions, 5) - .map(({ imageUrl, ...rest }, index) => ({ - ...rest, - // eslint-disable-next-line camelcase - img: imageUrl || '', - highlighted: index === this.highlighted - })) - }, showSuggestions () { return this.focused && this.suggestions && @@ -187,7 +173,25 @@ const EmojiInput = { }, watch: { showSuggestions: function (newValue) { + console.log('showSuggestions watch', newValue, this.suggestions) this.$emit('shown', newValue) + }, + textAtCaret: async function (textAtCaret) { + const firstchar = textAtCaret.charAt(0) + this.suggestions = [] + if (textAtCaret === firstchar) return + const matchedSuggestions = await this.suggest(textAtCaret) + // Async, cancel if textAtCaret has been updated while waiting + if (this.textAtCaret !== textAtCaret) return + if (matchedSuggestions.length <= 0) return + this.suggestions = take(matchedSuggestions, 10) + .map(({ imageUrl, ...rest }, index) => ({ + ...rest, + // eslint-disable-next-line camelcase + img: imageUrl || '', + highlighted: index === this.highlighted + })) + this.scrollIntoView() } }, methods: { @@ -341,6 +345,7 @@ const EmojiInput = { const { offsetHeight } = this.input.elm const { picker } = this.$refs const pickerBottom = picker.$el.getBoundingClientRect().bottom + console.log('setting bottom', pickerBottom > window.innerHeight) if (pickerBottom > window.innerHeight) { picker.$el.style.top = 'auto' picker.$el.style.bottom = offsetHeight + 'px' diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index 8330345b..9bf53183 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -1,4 +1,3 @@ -import { debounce } from 'lodash' /** * suggest - generates a suggestor function to be used by emoji-input * data: object providing source information for specific types of suggestions: @@ -11,19 +10,19 @@ import { debounce } from 'lodash' * doesn't support user linking you can just provide only emoji. */ -const debounceUserSearch = debounce((data, input) => { - data.updateUsersList(input) -}, 500) - -export default data => input => { - const firstChar = input[0] - if (firstChar === ':' && data.emoji) { - return suggestEmoji(data.emoji)(input) - } - if (firstChar === '@' && data.users) { - return suggestUsers(data)(input) +export default data => { + const emojiCurry = suggestEmoji(data.emoji) + const usersCurry = suggestUsers(data.dispatch) + return input => { + const firstChar = input[0] + if (firstChar === ':' && data.emoji) { + return emojiCurry(input) + } + if (firstChar === '@' && data.dispatch) { + return usersCurry(input) + } + return [] } - return [] } export const suggestEmoji = emojis => input => { @@ -57,50 +56,67 @@ export const suggestEmoji = emojis => input => { }) } -export const suggestUsers = data => input => { - const noPrefix = input.toLowerCase().substr(1) - const users = data.users - - const newUsers = users.filter( - user => - user.screen_name.toLowerCase().startsWith(noPrefix) || - user.name.toLowerCase().startsWith(noPrefix) - - /* taking only 20 results so that sorting is a bit cheaper, we display - * only 5 anyway. could be inaccurate, but we ideally we should query - * backend anyway - */ - ).slice(0, 20).sort((a, b) => { - let aScore = 0 - let bScore = 0 - - // Matches on screen name (i.e. user@instance) makes a priority - aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 - bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 - - // Matches on name takes second priority - aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 - bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 - - const diff = (bScore - aScore) * 10 - - // Then sort alphabetically - const nameAlphabetically = a.name > b.name ? 1 : -1 - const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1 - - return diff + nameAlphabetically + screenNameAlphabetically - /* eslint-disable camelcase */ - }).map(({ screen_name, name, profile_image_url_original }) => ({ - displayText: screen_name, - detailText: name, - imageUrl: profile_image_url_original, - replacement: '@' + screen_name + ' ' - })) - - // BE search users to get more comprehensive results - if (data.updateUsersList) { - debounceUserSearch(data, noPrefix) +export const suggestUsers = (dispatch) => { + let suggestions = [] + let previousQuery = '' + let timeout = null + + const userSearch = (query) => dispatch('searchUsers', { query, saveUsers: false }) + const debounceUserSearch = (query) => { + return new Promise((resolve, reject) => { + clearTimeout(timeout) + timeout = setTimeout(() => { + userSearch(query).then(resolve).catch(reject) + }, 300) + }) + } + + return async input => { + const noPrefix = input.toLowerCase().substr(1) + if (previousQuery === noPrefix) return suggestions + + suggestions = [] + previousQuery = noPrefix + const users = await debounceUserSearch(noPrefix) + + const newUsers = users.filter( + user => + user.screen_name.toLowerCase().startsWith(noPrefix) || + user.name.toLowerCase().startsWith(noPrefix) + + /* taking only 20 results so that sorting is a bit cheaper, we display + * only 5 anyway. could be inaccurate, but we ideally we should query + * backend anyway + */ + ).slice(0, 20).sort((a, b) => { + let aScore = 0 + let bScore = 0 + + // Matches on screen name (i.e. user@instance) makes a priority + aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 + bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0 + + // Matches on name takes second priority + aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 + bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0 + + const diff = (bScore - aScore) * 10 + + // Then sort alphabetically + const nameAlphabetically = a.name > b.name ? 1 : -1 + const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1 + + return diff + nameAlphabetically + screenNameAlphabetically + /* eslint-disable camelcase */ + }).map(({ screen_name, name, profile_image_url_original }) => ({ + displayText: screen_name, + detailText: name, + imageUrl: profile_image_url_original, + replacement: '@' + screen_name + ' ' + })) + + suggestions = newUsers || [] + return suggestions + /* eslint-enable camelcase */ } - return newUsers - /* eslint-enable camelcase */ } diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 3ff4a3c8..e38f1c0c 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -159,8 +159,7 @@ const PostStatusForm = { ...this.$store.state.instance.emoji, ...this.$store.state.instance.customEmoji ], - users: this.$store.state.users.users, - updateUsersList: (query) => this.$store.dispatch('searchUsers', { query }) + dispatch: this.$store.dispatch }) }, emojiSuggestor () { diff --git a/src/modules/users.js b/src/modules/users.js index 9245db5c..13df9df4 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -440,10 +440,10 @@ const users = { store.commit('setUserForNotification', notification) }) }, - searchUsers ({ rootState, commit }, { query }) { + searchUsers ({ rootState, commit }, { query, saveUsers = true }) { return rootState.api.backendInteractor.searchUsers({ query }) .then((users) => { - commit('addNewUsers', users) + if (saveUsers) commit('addNewUsers', users) return users }) }, -- cgit v1.2.3-70-g09d2 From 0f386ccbc7115fd1d74615377beca9982dec00bf Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Wed, 18 Nov 2020 18:43:24 +0200 Subject: use users state + fetching with delay --- src/components/emoji_input/emoji_input.js | 8 +++---- src/components/emoji_input/suggestor.js | 28 +++++++++++++++------- .../post_status_form/post_status_form.js | 2 +- src/components/settings_modal/tabs/profile_tab.js | 8 ++----- 4 files changed, 27 insertions(+), 19 deletions(-) (limited to 'src/components/post_status_form') diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index 7fe25ff4..16243a56 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -173,7 +173,6 @@ const EmojiInput = { }, watch: { showSuggestions: function (newValue) { - console.log('showSuggestions watch', newValue, this.suggestions) this.$emit('shown', newValue) }, textAtCaret: async function (textAtCaret) { @@ -184,14 +183,16 @@ const EmojiInput = { // Async, cancel if textAtCaret has been updated while waiting if (this.textAtCaret !== textAtCaret) return if (matchedSuggestions.length <= 0) return - this.suggestions = take(matchedSuggestions, 10) + this.suggestions = take(matchedSuggestions, 5) .map(({ imageUrl, ...rest }, index) => ({ ...rest, // eslint-disable-next-line camelcase img: imageUrl || '', highlighted: index === this.highlighted })) - this.scrollIntoView() + }, + suggestions (newValue) { + this.$nextTick(this.resize) } }, methods: { @@ -345,7 +346,6 @@ const EmojiInput = { const { offsetHeight } = this.input.elm const { picker } = this.$refs const pickerBottom = picker.$el.getBoundingClientRect().bottom - console.log('setting bottom', pickerBottom > window.innerHeight) if (pickerBottom > window.innerHeight) { picker.$el.style.top = 'auto' picker.$el.style.bottom = offsetHeight + 'px' diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index 9bf53183..0d268f85 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -12,13 +12,13 @@ export default data => { const emojiCurry = suggestEmoji(data.emoji) - const usersCurry = suggestUsers(data.dispatch) + const usersCurry = data.store && suggestUsers(data.store) return input => { const firstChar = input[0] if (firstChar === ':' && data.emoji) { return emojiCurry(input) } - if (firstChar === '@' && data.dispatch) { + if (firstChar === '@' && usersCurry) { return usersCurry(input) } return [] @@ -56,18 +56,24 @@ export const suggestEmoji = emojis => input => { }) } -export const suggestUsers = (dispatch) => { +export const suggestUsers = ({ dispatch, state }) => { let suggestions = [] let previousQuery = '' let timeout = null + let cancelUserSearch = null - const userSearch = (query) => dispatch('searchUsers', { query, saveUsers: false }) + const userSearch = (query) => dispatch('searchUsers', { query }) const debounceUserSearch = (query) => { + cancelUserSearch && cancelUserSearch() return new Promise((resolve, reject) => { clearTimeout(timeout) timeout = setTimeout(() => { userSearch(query).then(resolve).catch(reject) }, 300) + cancelUserSearch = () => { + clearTimeout(timeout) + resolve([]) + } }) } @@ -77,9 +83,15 @@ export const suggestUsers = (dispatch) => { suggestions = [] previousQuery = noPrefix - const users = await debounceUserSearch(noPrefix) + // Fetch more and wait, don't fetch if there's the 2nd @ because + // the backend user search can't deal with it. + // Reference semantics make it so that we get the updated data after + // the await. + if (!noPrefix.includes('@')) { + await debounceUserSearch(noPrefix) + } - const newUsers = users.filter( + const newSuggestions = state.users.users.filter( user => user.screen_name.toLowerCase().startsWith(noPrefix) || user.name.toLowerCase().startsWith(noPrefix) @@ -114,9 +126,9 @@ export const suggestUsers = (dispatch) => { imageUrl: profile_image_url_original, replacement: '@' + screen_name + ' ' })) + /* eslint-enable camelcase */ - suggestions = newUsers || [] + suggestions = newSuggestions || [] return suggestions - /* eslint-enable camelcase */ } } diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index e38f1c0c..4148381c 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -159,7 +159,7 @@ const PostStatusForm = { ...this.$store.state.instance.emoji, ...this.$store.state.instance.customEmoji ], - dispatch: this.$store.dispatch + store: this.$store }) }, emojiSuggestor () { diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index a3e4feaf..a4fed629 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -68,8 +68,7 @@ const ProfileTab = { ...this.$store.state.instance.emoji, ...this.$store.state.instance.customEmoji ], - users: this.$store.state.users.users, - updateUsersList: (query) => this.$store.dispatch('searchUsers', { query }) + store: this.$store }) }, emojiSuggestor () { @@ -79,10 +78,7 @@ const ProfileTab = { ] }) }, userSuggestor () { - return suggestor({ - users: this.$store.state.users.users, - updateUsersList: (query) => this.$store.dispatch('searchUsers', { query }) - }) + return suggestor({ store: this.$store }) }, fieldsLimits () { return this.$store.state.instance.fieldsLimits -- cgit v1.2.3-70-g09d2 From 7b99d98c553f40ec4d633d0d4fdf65f275c80e77 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Tue, 24 Nov 2020 12:32:42 +0200 Subject: Replace all use of + href='#' with proper buttons --- src/App.scss | 32 +++++++++++-- src/components/account_actions/account_actions.vue | 14 +++--- .../async_component_error.vue | 2 +- src/components/attachment/attachment.js | 6 ++- src/components/attachment/attachment.vue | 28 ++++++----- src/components/block_card/block_card.vue | 4 +- src/components/chat_list/chat_list.vue | 5 +- src/components/chat_message/chat_message.scss | 3 -- src/components/chat_message/chat_message.vue | 4 +- src/components/conversation/conversation.vue | 13 +++--- src/components/desktop_nav/desktop_nav.scss | 10 ++-- src/components/desktop_nav/desktop_nav.vue | 40 ++++++++-------- .../domain_mute_card/domain_mute_card.vue | 4 +- src/components/emoji_reactions/emoji_reactions.vue | 2 +- src/components/export_import/export_import.vue | 4 +- src/components/exporter/exporter.vue | 2 +- src/components/extra_buttons/extra_buttons.vue | 28 +++++------ src/components/favorite_button/favorite_button.js | 5 -- src/components/favorite_button/favorite_button.vue | 38 +++++++-------- src/components/follow_button/follow_button.vue | 2 +- .../follow_request_card/follow_request_card.vue | 4 +- src/components/image_cropper/image_cropper.vue | 6 +-- src/components/importer/importer.vue | 2 +- src/components/login_form/login_form.vue | 2 +- src/components/media_upload/media_upload.vue | 54 +++++++++------------- src/components/mfa_form/recovery_form.vue | 14 +++--- src/components/mfa_form/totp_form.vue | 14 +++--- src/components/mobile_nav/mobile_nav.vue | 14 +++--- .../mobile_post_status_button.vue | 2 +- .../moderation_tools/moderation_tools.vue | 6 +-- src/components/mute_card/mute_card.vue | 4 +- src/components/notification/notification.vue | 13 +++--- src/components/notifications/notifications.vue | 8 ++-- src/components/password_reset/password_reset.vue | 2 +- src/components/poll/poll.vue | 2 +- .../post_status_form/post_status_form.vue | 34 ++++++-------- src/components/react_button/react_button.vue | 13 ++++-- src/components/registration/registration.vue | 2 +- src/components/reply_button/reply_button.vue | 19 ++++---- src/components/retweet_button/retweet_button.js | 5 -- src/components/retweet_button/retweet_button.vue | 48 +++++++++---------- src/components/search_bar/search_bar.vue | 32 +++++++------ src/components/settings_modal/settings_modal.vue | 4 +- .../settings_modal/tabs/mutes_and_blocks_tab.vue | 10 ++-- .../settings_modal/tabs/notifications_tab.vue | 2 +- src/components/settings_modal/tabs/profile_tab.vue | 8 ++-- .../settings_modal/tabs/security_tab/confirm.vue | 4 +- .../settings_modal/tabs/security_tab/mfa.vue | 10 ++-- .../settings_modal/tabs/security_tab/mfa_totp.vue | 4 +- .../tabs/security_tab/security_tab.vue | 10 ++-- .../settings_modal/tabs/theme_tab/preview.vue | 4 +- .../settings_modal/tabs/theme_tab/theme_tab.vue | 28 +++++------ src/components/shadow_control/shadow_control.vue | 8 ++-- src/components/side_drawer/side_drawer.vue | 15 +++--- src/components/status/status.vue | 35 +++++++------- src/components/status_content/status_content.vue | 37 +++++++-------- src/components/tab_switcher/tab_switcher.js | 2 +- src/components/timeline/timeline.vue | 8 ++-- src/components/user_card/user_card.vue | 10 ++-- .../user_reporting_modal/user_reporting_modal.vue | 2 +- 60 files changed, 384 insertions(+), 363 deletions(-) (limited to 'src/components/post_status_form') diff --git a/src/App.scss b/src/App.scss index ca7d33cd..ef2a13b1 100644 --- a/src/App.scss +++ b/src/App.scss @@ -33,6 +33,7 @@ h4 { max-width: 980px; align-content: flex-start; } + .underlay { background-color: rgba(0,0,0,0.15); background-color: var(--underlay, rgba(0,0,0,0.15)); @@ -69,7 +70,7 @@ a { color: var(--link, $fallback--link); } -button { +.button-default { user-select: none; color: $fallback--text; color: var(--btnText, $fallback--text); @@ -85,7 +86,9 @@ button { font-family: sans-serif; font-family: var(--interfaceFont, sans-serif); - i[class*=icon-], .svg-inline--fa { + i[class*=icon-], + :not(&.-icon), + .svg-inline--fa { color: $fallback--text; color: var(--btnText, $fallback--text); } @@ -149,6 +152,29 @@ button { } } +.button-unstyled { + background: none; + border: none; + outline: none; + display: inline; + text-align: initial; + font-size: 100%; + font-family: inherit; + padding: 0; + line-height: unset; + cursor: pointer; + + &.-link { + color: $fallback--link; + color: var(--link, $fallback--link); + } + + &.-padded { + padding: 5px; + margin: -5px; + } +} + input, textarea, .select, .input { &.unstyled { @@ -797,7 +823,7 @@ nav { } } -.btn.btn-default { +.btn.button-default { min-height: 28px; } diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index e3ae376e..c10b09b8 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -13,14 +13,14 @@ diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss index 5af744a3..e4351d3b 100644 --- a/src/components/chat_message/chat_message.scss +++ b/src/components/chat_message/chat_message.scss @@ -31,9 +31,6 @@ color: $fallback--text; color: var(--text, $fallback--text); } - - border-radius: $fallback--chatMessageRadius; - border-radius: var(--chatMessageRadius, $fallback--chatMessageRadius); } .popover { diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue index 3849ab6e..0777f880 100644 --- a/src/components/chat_message/chat_message.vue +++ b/src/components/chat_message/chat_message.vue @@ -53,7 +53,7 @@ - - + - + + + diff --git a/src/components/domain_mute_card/domain_mute_card.vue b/src/components/domain_mute_card/domain_mute_card.vue index 97aee243..3b5aec14 100644 --- a/src/components/domain_mute_card/domain_mute_card.vue +++ b/src/components/domain_mute_card/domain_mute_card.vue @@ -6,7 +6,7 @@ {{ $t('domain_mute_card.unmute') }}