From f5e4ad601ac47f7b0a2f3cdc24e0f6be9be17d21 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Thu, 27 Aug 2020 16:46:23 +0300 Subject: [PATCH 1/5] wip start --- src/services/completion/completion.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/completion/completion.js b/src/services/completion/completion.js index df83d03d14..f74f8048eb 100644 --- a/src/services/completion/completion.js +++ b/src/services/completion/completion.js @@ -4,6 +4,7 @@ export const replaceWord = (str, toReplace, replacement) => { return str.slice(0, toReplace.start) + replacement + str.slice(toReplace.end) } +// This seems to work fine export const wordAtPosition = (str, pos) => { const words = splitIntoWords(str) const wordsWithPosition = addPositionToWords(words) @@ -11,6 +12,7 @@ export const wordAtPosition = (str, pos) => { return find(wordsWithPosition, ({ start, end }) => start <= pos && end > pos) } +// This works fine export const addPositionToWords = (words) => { return reduce(words, (result, word) => { const data = { @@ -34,6 +36,7 @@ export const addPositionToWords = (words) => { }, []) } +// This needs to be altered, split words at space export const splitIntoWords = (str) => { // Split at word boundaries const regex = /\b/ From 0347d79bda97a90eab3afaf9ab6aafb784b2e10d Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Fri, 28 Aug 2020 12:02:52 +0300 Subject: [PATCH 2/5] Rewrite word split imperatively for control --- src/services/completion/completion.js | 53 +++++++++---------- .../services/completion/completion.spec.js | 43 ++++++++++----- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/src/services/completion/completion.js b/src/services/completion/completion.js index f74f8048eb..8a6eba7e4d 100644 --- a/src/services/completion/completion.js +++ b/src/services/completion/completion.js @@ -4,15 +4,13 @@ export const replaceWord = (str, toReplace, replacement) => { return str.slice(0, toReplace.start) + replacement + str.slice(toReplace.end) } -// This seems to work fine export const wordAtPosition = (str, pos) => { - const words = splitIntoWords(str) + const words = splitByWhitespaceBoundary(str) const wordsWithPosition = addPositionToWords(words) return find(wordsWithPosition, ({ start, end }) => start <= pos && end > pos) } -// This works fine export const addPositionToWords = (words) => { return reduce(words, (result, word) => { const data = { @@ -36,37 +34,36 @@ export const addPositionToWords = (words) => { }, []) } -// This needs to be altered, split words at space -export const splitIntoWords = (str) => { - // Split at word boundaries - const regex = /\b/ - const triggers = /[@#:]+$/ - - let split = str.split(regex) - - // Add trailing @ and # to the following word. - const words = reduce(split, (result, word) => { - if (result.length > 0) { - let previous = result.pop() - const matches = previous.match(triggers) - if (matches) { - previous = previous.replace(triggers, '') - word = matches[0] + word - } - result.push(previous) +export const splitByWhitespaceBoundary = (str) => { + let result = [] + let currentWord = '' + for (let i = 0; i < str.length; i++) { + const currentChar = str[i] + // Starting a new word + if (!currentWord) { + currentWord = currentChar + continue } - result.push(word) - - return result - }, []) - - return words + // current character is whitespace while word isn't, or vice versa: + // add our current word to results, start over the current word. + if (!!currentChar.trim() !== !!currentWord.trim()) { + result.push(currentWord) + currentWord = currentChar + continue + } + currentWord += currentChar + } + // Add the last word we were working on + if (currentWord) { + result.push(currentWord) + } + return result } const completion = { wordAtPosition, addPositionToWords, - splitIntoWords, + splitByWhitespaceBoundary, replaceWord } diff --git a/test/unit/specs/services/completion/completion.spec.js b/test/unit/specs/services/completion/completion.spec.js index 8a41c6537a..81d3a26a26 100644 --- a/test/unit/specs/services/completion/completion.spec.js +++ b/test/unit/specs/services/completion/completion.spec.js @@ -1,8 +1,8 @@ -import { replaceWord, addPositionToWords, wordAtPosition, splitIntoWords } from '../../../../../src/services/completion/completion.js' +import { replaceWord, addPositionToWords, wordAtPosition, splitByWhitespaceBoundary } from '../../../../../src/services/completion/completion.js' describe('addPositiontoWords', () => { it('adds the position to a word list', () => { - const words = ['hey', 'this', 'is', 'fun'] + const words = ['hey', ' ', 'this', ' ', 'is', ' ', 'fun'] const expected = [ { @@ -11,19 +11,34 @@ describe('addPositiontoWords', () => { end: 3 }, { - word: 'this', + word: ' ', start: 3, - end: 7 + end: 4 }, { - word: 'is', - start: 7, + word: 'this', + start: 4, + end: 8 + }, + { + word: ' ', + start: 8, end: 9 }, { - word: 'fun', + word: 'is', start: 9, + end: 11 + }, + { + word: ' ', + start: 11, end: 12 + }, + { + word: 'fun', + start: 12, + end: 15 } ] @@ -33,11 +48,11 @@ describe('addPositiontoWords', () => { }) }) -describe('splitIntoWords', () => { +describe('splitByWhitespaceBoundary', () => { it('splits at whitespace boundaries', () => { - const str = 'This is a #nice @test for you, @idiot.' - const expected = ['This', ' ', 'is', ' ', 'a', ' ', '#nice', ' ', '@test', ' ', 'for', ' ', 'you', ', ', '@idiot', '.'] - const res = splitIntoWords(str) + const str = 'This is a #nice @test for you, @idiot@idiot.com' + const expected = ['This', ' ', 'is', ' ', 'a', ' ', '#nice', ' ', '@test', ' ', 'for', ' ', 'you,', ' ', '@idiot@idiot.com'] + const res = splitByWhitespaceBoundary(str) expect(res).to.eql(expected) }) @@ -57,13 +72,13 @@ describe('wordAtPosition', () => { describe('replaceWord', () => { it('replaces a word (with start and end) with another word in a given string', () => { - const str = 'hey @take, how are you' - const wordsWithPosition = addPositionToWords(splitIntoWords(str)) + const str = 'hey @take , how are you' + const wordsWithPosition = addPositionToWords(splitByWhitespaceBoundary(str)) const toReplace = wordsWithPosition[2] expect(toReplace.word).to.eql('@take') - const expected = 'hey @takeshitakenji, how are you' + const expected = 'hey @takeshitakenji , how are you' const res = replaceWord(str, toReplace, '@takeshitakenji') expect(res).to.eql(expected) }) From 4da248f8bc592fc1b7d24fbc1e2021bab2663f29 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Fri, 28 Aug 2020 12:35:02 +0300 Subject: [PATCH 3/5] update changelog for autocomplete fixes --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e53e603c..66abfa654f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ## [Unreleased patch] +### Fixed +- Autocomplete won't stop at the second @, so it'll still work with "@lain@l" and not start over. +- Fixed weird autocomplete behavior when you write ":custom_emoji: ?" ## [2.1.0] - 2020-08-28 ### Added From 5809f33c860663165d1211d0251f0da4c2a374cd Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 1 Sep 2020 16:48:51 -0500 Subject: [PATCH 4/5] Password reset no longer informs user of errors or account existence --- src/components/password_reset/password_reset.js | 5 ----- src/i18n/de.json | 1 - src/i18n/en.json | 1 - src/i18n/eo.json | 1 - src/i18n/es.json | 1 - src/i18n/eu.json | 1 - src/i18n/fi.json | 1 - src/i18n/fr.json | 1 - src/i18n/it.json | 1 - src/i18n/ja_easy.json | 1 - src/i18n/ja_pedantic.json | 1 - src/i18n/nl.json | 1 - src/i18n/pl.json | 1 - src/i18n/ru.json | 1 - src/i18n/zh.json | 1 - 15 files changed, 19 deletions(-) diff --git a/src/components/password_reset/password_reset.js b/src/components/password_reset/password_reset.js index 62e74e30f2..5d21d72054 100644 --- a/src/components/password_reset/password_reset.js +++ b/src/components/password_reset/password_reset.js @@ -47,11 +47,6 @@ const passwordReset = { if (status === 204) { this.success = true this.error = null - } else if (status === 404 || status === 400) { - this.error = this.$t('password_reset.not_found') - this.$nextTick(() => { - this.$refs.email.focus() - }) } else if (status === 429) { this.throttled = true this.error = this.$t('password_reset.too_many_requests') diff --git a/src/i18n/de.json b/src/i18n/de.json index 3014b87042..6fe6ab2c07 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -478,7 +478,6 @@ "placeholder": "Dein Benutzername oder die zugehörige E-Mail-Adresse", "check_email": "Im E-Mail-Posteingang des angebenen Kontos müsste sich jetzt (oder zumindest in Kürze) die E-Mail mit dem Link zum Passwortzurücksetzen befinden.", "return_home": "Zurück zur Heimseite", - "not_found": "Benutzername/E-Mail-Adresse nicht gefunden. Vertippt?", "too_many_requests": "Kurze Pause. Zu viele Versuche. Bitte, später nochmal probieren.", "password_reset_disabled": "Passwortzurücksetzen deaktiviert. Bitte Administrator kontaktieren.", "password_reset_required": "Passwortzurücksetzen erforderlich.", diff --git a/src/i18n/en.json b/src/i18n/en.json index e05ac907af..8540f551fc 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -774,7 +774,6 @@ "placeholder": "Your email or username", "check_email": "Check your email for a link to reset your password.", "return_home": "Return to the home page", - "not_found": "We couldn't find that email or username.", "too_many_requests": "You have reached the limit of attempts, try again later.", "password_reset_disabled": "Password reset is disabled. Please contact your instance administrator.", "password_reset_required": "You must reset your password to log in.", diff --git a/src/i18n/eo.json b/src/i18n/eo.json index b66f557a6a..e73ac2f8ec 100644 --- a/src/i18n/eo.json +++ b/src/i18n/eo.json @@ -776,7 +776,6 @@ "password_reset_required": "Vi devas restarigi vian pasvorton por saluti.", "password_reset_disabled": "Restarigado de pasvortoj estas malŝaltita. Bonvolu kontakti la administranton de via nodo.", "too_many_requests": "Vi atingis la limon de provoj, reprovu pli poste.", - "not_found": "Ni ne trovis tiun retpoŝtadreson aŭ uzantonomon.", "return_home": "Reiri al la hejmpaĝo", "check_email": "Kontrolu vian retpoŝton pro ligilo por restarigi vian pasvorton.", "placeholder": "Via retpoŝtadreso aŭ uzantonomo", diff --git a/src/i18n/es.json b/src/i18n/es.json index 3f313eb329..50bdcfb4ac 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -624,7 +624,6 @@ "placeholder": "Su correo electrónico o nombre de usuario", "check_email": "Revise su correo electrónico para obtener un enlace para restablecer su contraseña.", "return_home": "Volver a la página de inicio", - "not_found": "No pudimos encontrar ese correo electrónico o nombre de usuario.", "too_many_requests": "Has alcanzado el límite de intentos, vuelve a intentarlo más tarde.", "password_reset_disabled": "El restablecimiento de contraseñas está deshabilitado. Póngase en contacto con el administrador de su instancia." } diff --git a/src/i18n/eu.json b/src/i18n/eu.json index f04203f0ce..ad3e02a023 100644 --- a/src/i18n/eu.json +++ b/src/i18n/eu.json @@ -626,7 +626,6 @@ "placeholder": "Zure e-posta edo erabiltzaile izena", "check_email": "Begiratu zure posta elektronikoa pasahitza berrezarri ahal izateko.", "return_home": "Itzuli hasierara", - "not_found": "Ezin izan dugu helbide elektroniko edo erabiltzaile hori aurkitu.", "too_many_requests": "Saiakera gehiegi burutu ditzu, saiatu berriro geroxeago.", "password_reset_disabled": "Pasahitza berrezartzea debekatuta dago. Mesedez, jarri harremanetan instantzia administratzailearekin.", "password_reset_required": "Pasahitza berrezarri behar duzu saioa hasteko.", diff --git a/src/i18n/fi.json b/src/i18n/fi.json index 510b22343b..3832dcaa88 100644 --- a/src/i18n/fi.json +++ b/src/i18n/fi.json @@ -752,7 +752,6 @@ "password_reset": "Salasanan nollaus", "placeholder": "Sähköpostiosoite tai käyttäjänimi", "return_home": "Palaa etusivulle", - "not_found": "Sähköpostiosoitetta tai käyttäjänimeä ei löytynyt.", "too_many_requests": "Olet käyttänyt kaikki yritykset, yritä uudelleen myöhemmin.", "password_reset_required": "Sinun täytyy vaihtaa salasana kirjautuaksesi." }, diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 794ed812c0..3b7eefaf51 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -730,7 +730,6 @@ "instruction": "Entrer votre address de courriel ou votre nom utilisateur. Nous enverrons un lien pour changer votre mot de passe.", "placeholder": "Votre email ou nom d'utilisateur", "return_home": "Retourner à la page d'accueil", - "not_found": "Email ou nom d'utilisateur inconnu.", "too_many_requests": "Vos avez atteint la limite d'essais, essayez plus tard.", "password_reset_required": "Vous devez changer votre mot de passe pour vous authentifier." } diff --git a/src/i18n/it.json b/src/i18n/it.json index b88fdd294f..474e7fde70 100644 --- a/src/i18n/it.json +++ b/src/i18n/it.json @@ -745,7 +745,6 @@ "password_reset_required": "Devi reimpostare la tua password per poter continuare.", "password_reset_disabled": "Non puoi azzerare la tua password. Contatta il tuo amministratore.", "too_many_requests": "Hai raggiunto il numero massimo di tentativi, riprova più tardi.", - "not_found": "Non ho trovato questa email o nome utente.", "return_home": "Torna alla pagina principale", "check_email": "Controlla la tua posta elettronica.", "placeholder": "La tua email o nome utente", diff --git a/src/i18n/ja_easy.json b/src/i18n/ja_easy.json index 255648e75c..991f3762f2 100644 --- a/src/i18n/ja_easy.json +++ b/src/i18n/ja_easy.json @@ -666,7 +666,6 @@ "placeholder": "あなたのメールアドレスかユーザーめい", "check_email": "パスワードをリセットするためのリンクがかかれたメールが、とどいているかどうか、みてください。", "return_home": "ホームページにもどる", - "not_found": "そのメールアドレスまたはユーザーめいを、みつけることができませんでした。", "too_many_requests": "パスワードリセットを、ためすことが、おおすぎます。しばらくしてから、ためしてください。", "password_reset_disabled": "このインスタンスでは、パスワードリセットは、できません。インスタンスのアドミニストレーターに、おといあわせください。", "password_reset_required": "ログインするには、パスワードをリセットしてください。", diff --git a/src/i18n/ja_pedantic.json b/src/i18n/ja_pedantic.json index 07fea45d51..e2de10669d 100644 --- a/src/i18n/ja_pedantic.json +++ b/src/i18n/ja_pedantic.json @@ -625,7 +625,6 @@ "placeholder": "メールアドレスまたはユーザー名", "check_email": "パスワードをリセットするためのリンクが記載されたメールが届いているか確認してください。", "return_home": "ホームページに戻る", - "not_found": "メールアドレスまたはユーザー名が見つかりませんでした。", "too_many_requests": "試行回数の制限に達しました。しばらく時間を置いてから再試行してください。", "password_reset_disabled": "このインスタンスではパスワードリセットは無効になっています。インスタンスの管理者に連絡してください。" } diff --git a/src/i18n/nl.json b/src/i18n/nl.json index e7509f12fb..a01e57a097 100644 --- a/src/i18n/nl.json +++ b/src/i18n/nl.json @@ -677,7 +677,6 @@ "password_reset_required": "Je dient je wachtwoord opnieuw in te stellen om in te kunnen loggen.", "password_reset_disabled": "Wachtwoord reset is uitgeschakeld. Neem contact op met de beheerder van deze instantie.", "too_many_requests": "Je hebt het maximaal aantal pogingen bereikt, probeer het later opnieuw.", - "not_found": "We kunnen die email of gebruikersnaam niet vinden.", "return_home": "Terugkeren naar de home pagina", "check_email": "Controleer je email inbox voor een link om je wachtwoord opnieuw in te stellen.", "placeholder": "Je email of gebruikersnaam", diff --git a/src/i18n/pl.json b/src/i18n/pl.json index 5863ba8e28..05a7edf7d5 100644 --- a/src/i18n/pl.json +++ b/src/i18n/pl.json @@ -753,7 +753,6 @@ "placeholder": "Twój email lub nazwa użytkownika", "check_email": "Sprawdź pocztę, aby uzyskać link do zresetowania hasła.", "return_home": "Wróć do strony głównej", - "not_found": "Nie mogliśmy znaleźć tego emaila lub nazwy użytkownika.", "too_many_requests": "Przekroczyłeś(-aś) limit prób, spróbuj ponownie później.", "password_reset_disabled": "Resetowanie hasła jest wyłączone. Proszę skontaktuj się z administratorem tej instancji.", "password_reset_required": "Musisz zresetować hasło, by się zalogować.", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index df17293543..3444a26d48 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -420,7 +420,6 @@ "placeholder": "Ваш email или имя пользователя", "check_email": "Проверьте ваш email и перейдите по ссылке для сброса пароля.", "return_home": "Вернуться на главную страницу", - "not_found": "Мы не смогли найти аккаунт с таким email-ом или именем пользователя.", "too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.", "password_reset_disabled": "Сброс пароля отключен. Cвяжитесь с администратором вашего сервера." }, diff --git a/src/i18n/zh.json b/src/i18n/zh.json index 24b799df50..8c693f4d66 100644 --- a/src/i18n/zh.json +++ b/src/i18n/zh.json @@ -640,7 +640,6 @@ "placeholder": "你的电邮地址或者用户名", "check_email": "检查你的邮箱,会有一个链接用于重置密码。", "return_home": "回到首页", - "not_found": "我们无法找到匹配的邮箱地址或者用户名。", "too_many_requests": "你触发了尝试的限制,请稍后再试。", "password_reset_disabled": "密码重置已经被禁用。请联系你的实例管理员。" }, From f281663b4972ac82b0392b1ab31b2106f9dceac7 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Thu, 3 Sep 2020 15:45:13 +0300 Subject: [PATCH 5/5] Add hacky functionality to open specific settings tabs --- .../post_status_form/post_status_form.js | 3 ++ .../post_status_form/post_status_form.vue | 7 +++-- .../settings_modal/settings_modal_content.js | 28 +++++++++++++++++++ .../settings_modal/settings_modal_content.vue | 9 ++++++ src/components/tab_switcher/tab_switcher.js | 23 ++++++++------- src/modules/interface.js | 11 ++++++++ 6 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index e7094bec94..ad149506ee 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -555,6 +555,9 @@ const PostStatusForm = { }, updateIdempotencyKey () { this.idempotencyKey = Date.now().toString() + }, + openProfileTab () { + this.$store.dispatch('openSettingsModalTab', 'profile') } } } diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 520c03ea1e..d67d9ae9c6 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -23,9 +23,12 @@ tag="p" class="visibility-notice" > - + {{ $t('post_status.account_not_locked_warning_link') }} - +

{ + return elm.data && elm.data.attrs['data-tab-name'] === targetTab + }) + if (tabIndex >= 0) { + this.$refs.tabSwitcher.setTab(tabIndex) + } + } + // Clear the state of target tab, so that next time settings is opened + // it doesn't force it. + this.$store.dispatch('clearSettingsModalTargetTab') + } + }, + mounted () { + this.onOpen() + }, + watch: { + open: function (value) { + if (value) this.onOpen() } } } diff --git a/src/components/settings_modal/settings_modal_content.vue b/src/components/settings_modal/settings_modal_content.vue index 2156844f28..bc30a0ff7d 100644 --- a/src/components/settings_modal/settings_modal_content.vue +++ b/src/components/settings_modal/settings_modal_content.vue @@ -8,6 +8,7 @@

@@ -15,6 +16,7 @@ v-if="isLoggedIn" :label="$t('settings.profile_tab')" icon="user" + data-tab-name="profile" > @@ -22,18 +24,21 @@ v-if="isLoggedIn" :label="$t('settings.security_tab')" icon="lock" + data-tab-name="security" >
@@ -41,6 +46,7 @@ v-if="isLoggedIn" :label="$t('settings.notifications')" icon="bell-ringing-o" + data-tab-name="notifications" > @@ -48,6 +54,7 @@ v-if="isLoggedIn" :label="$t('settings.data_import_export_tab')" icon="download" + data-tab-name="dataImportExport" > @@ -56,12 +63,14 @@ :label="$t('settings.mutes_and_blocks')" :fullHeight="true" icon="eye-off" + data-tab-name="mutesAndBlocks" >
diff --git a/src/components/tab_switcher/tab_switcher.js b/src/components/tab_switcher/tab_switcher.js index 40b5b3ca15..9c1da35451 100644 --- a/src/components/tab_switcher/tab_switcher.js +++ b/src/components/tab_switcher/tab_switcher.js @@ -60,16 +60,19 @@ export default Vue.component('tab-switcher', { } }, methods: { - activateTab (index) { + clickTab (index) { return (e) => { e.preventDefault() - if (typeof this.onSwitch === 'function') { - this.onSwitch.call(null, this.$slots.default[index].key) - } - this.active = index - if (this.scrollableTabs) { - this.$refs.contents.scrollTop = 0 - } + this.setTab(index) + } + }, + setTab (index) { + if (typeof this.onSwitch === 'function') { + this.onSwitch.call(null, this.$slots.default[index].key) + } + this.active = index + if (this.scrollableTabs) { + this.$refs.contents.scrollTop = 0 } } }, @@ -88,7 +91,7 @@ export default Vue.component('tab-switcher', {