diff --git a/CHANGELOG.md b/CHANGELOG.md index 86d1a97f..18dafa8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,21 +2,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + ## [Unreleased] +## [Unreleased patch] + ### Changed -- Greentext now has separate color slot for it -- Removed the use of with_move parameters when fetching notifications -- Push notifications now are the same as normal notfication, and are localized. -- Updated Notification Settings to match new BE API +- Polls will be hidden with status content if "Collapse posts with subjects" is enabled and the post is collapsed. ### Fixed -- Weird bug related to post being sent seemingly after pasting with keyboard (hopefully) -- Multiple issues with muted statuses/notifications +- 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: ?" -## [Unreleased patch] -### Add -- Added private notifications option for push notifications -- 'Copy link' button for statuses (in the ellipsis menu) +## [2.1.0] - 2020-08-28 +### Added - Autocomplete domains from list of known instances - 'Bot' settings option and badge - Added profile meta data fields that can be set in profile settings @@ -25,15 +23,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added status preview option to preview your statuses before posting - When a post is a reply to an unavailable post, the 'Reply to'-text has a strike-through style - Added ability to see all favoriting or repeating users when hovering the number on highlighted statuses +- Bookmarks ### Changed -- Registration page no longer requires email if the server is configured not to require it - Change heart to thumbs up in reaction picker - Close the media modal on navigation events - Add colons to the emoji alt text, to make them copyable - Add better visual indication for drag-and-drop for files - When disabling attachments, the placeholder links now show an icon and the description instead of just IMAGE or VIDEO etc - Remove unnecessary options for 'automatic loading when loading older' and 'reply previews' +- Greentext now has separate color slot for it +- Removed the use of with_move parameters when fetching notifications +- Push notifications now are the same as normal notfication, and are localized. +- Updated Notification Settings to match new BE API ### Fixed - Custom Emoji will display in poll options now. @@ -52,6 +54,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Reply filtering options in Settings -> Filtering now work again using filtering on server - Don't show just blank-screen when cookies are disabled - Add status idempotency to prevent accidental double posting when posting returns an error +- Weird bug related to post being sent seemingly after pasting with keyboard (hopefully) +- Multiple issues with muted statuses/notifications + +## [2.0.5] - 2020-05-12 +### Added +- Added private notifications option for push notifications +- 'Copy link' button for statuses (in the ellipsis menu) + +### Changed +- Registration page no longer requires email if the server is configured not to require it + +### Fixed +- Status ellipsis menu closes properly when selecting certain options ## [2.0.3] - 2020-05-02 ### Fixed @@ -61,7 +76,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Emoji autocomplete will match any part of the word and not just start, for example :drool will now helpfully suggest :blobcatdrool: and :blobcatdroolreach: -### Add +### Added - Follow request notification support ## [2.0.2] - 2020-04-08 diff --git a/src/components/chat_message/chat_message.scss b/src/components/chat_message/chat_message.scss index 240beea4..7d4ff60c 100644 --- a/src/components/chat_message/chat_message.scss +++ b/src/components/chat_message/chat_message.scss @@ -2,7 +2,7 @@ .chat-message-wrapper { &.hovered-message-chain { - .animated.avatar { + .animated.Avatar { canvas { display: none; } diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 1bd91995..c6b2a5b5 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -39,7 +39,7 @@ word-wrap: break-word; word-break: break-word; - &:hover .animated.avatar { + &:hover .animated.Avatar { canvas { display: none; } diff --git a/src/components/password_reset/password_reset.js b/src/components/password_reset/password_reset.js index 62e74e30..5d21d720 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/components/poll/poll.vue b/src/components/poll/poll.vue index ceba9eea..1858f3e1 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -96,6 +96,7 @@ align-items: center; padding: 0.1em 0.25em; z-index: 1; + word-break: break-word; } .result-percentage { width: 3.5em; diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 57e54481..779baf48 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -117,7 +117,7 @@ /> -
+
+ + -
+
diff --git a/src/i18n/de.json b/src/i18n/de.json index 3014b870..6fe6ab2c 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 e05ac907..8540f551 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 b66f557a..e73ac2f8 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 3f313eb3..50bdcfb4 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 f04203f0..ad3e02a0 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 510b2234..3832dcaa 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 794ed812..3b7eefaf 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 b88fdd29..474e7fde 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 255648e7..991f3762 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 07fea45d..e2de1066 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 e7509f12..a01e57a0 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 5863ba8e..05a7edf7 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 df172935..3444a26d 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 24b799df..8c693f4d 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": "密码重置已经被禁用。请联系你的实例管理员。" }, diff --git a/src/services/completion/completion.js b/src/services/completion/completion.js index df83d03d..8a6eba7e 100644 --- a/src/services/completion/completion.js +++ b/src/services/completion/completion.js @@ -5,7 +5,7 @@ export const replaceWord = (str, toReplace, replacement) => { } 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) @@ -34,36 +34,36 @@ export const addPositionToWords = (words) => { }, []) } -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 8a41c653..81d3a26a 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) })