From 44dea9f3646a5c27083dfe6cd6b1522e11c7dc69 Mon Sep 17 00:00:00 2001 From: xenofem Date: Sun, 9 Feb 2020 17:25:24 -0500 Subject: [PATCH 01/32] Allow emoji suggestions based on a match anywhere in the emoji name, but improve sorting --- src/components/emoji_input/suggestor.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index aec5c39d..9e437ccc 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -29,17 +29,21 @@ export default data => input => { export const suggestEmoji = emojis => input => { const noPrefix = input.toLowerCase().substr(1) return emojis - .filter(({ displayText }) => displayText.toLowerCase().startsWith(noPrefix)) + .filter(({ displayText }) => displayText.toLowerCase().match(noPrefix)) .sort((a, b) => { let aScore = 0 let bScore = 0 - // Make custom emojis a priority - aScore += a.imageUrl ? 10 : 0 - bScore += b.imageUrl ? 10 : 0 + // Prioritize emoji that start with the input string + aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 + bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 - // Sort alphabetically - const alphabetically = a.displayText > b.displayText ? 1 : -1 + // Sort by length + aScore -= a.displayText.length + bScore -= b.displayText.length + + // Break ties alphabetically + const alphabetically = a.displayText > b.displayText ? 0.5 : -0.5 return bScore - aScore + alphabetically }) From 02864bc07b2ab2f08232ba1c4c27079454dc87ef Mon Sep 17 00:00:00 2001 From: xenofem Date: Mon, 10 Feb 2020 09:32:07 -0500 Subject: [PATCH 02/32] Prioritize custom emoji a lot and boost exact matches to the top --- src/components/emoji_input/suggestor.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index 9e437ccc..15a71eff 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -34,7 +34,15 @@ export const suggestEmoji = emojis => input => { let aScore = 0 let bScore = 0 - // Prioritize emoji that start with the input string + // An exact match always wins + aScore += a.displayText.toLowerCase() === noPrefix ? 200 : 0 + bScore += b.displayText.toLowerCase() === noPrefix ? 200 : 0 + + // Prioritize custom emoji a lot + aScore += a.imageUrl ? 100 : 0 + bScore += b.imageUrl ? 100 : 0 + + // Prioritize prefix matches somewhat aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 From 86561592d002b08d6b2cd9549e8057a4ffd091cb Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 24 Feb 2020 11:19:00 -0600 Subject: [PATCH 03/32] First attempt at not requiring email address for registration --- src/boot/after_store.js | 3 +++ src/components/registration/registration.js | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/boot/after_store.js b/src/boot/after_store.js index d70e1058..9fb9a853 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -241,6 +241,9 @@ const getNodeInfo = async ({ store }) => { : federation.enabled }) + const accountActivationRequired = metadata.accountActivationRequired + store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired }) + const accounts = metadata.staffAccounts resolveStaffAccounts({ store, accounts }) } else { diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index ace8cc7c..fd2942a5 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -1,5 +1,5 @@ import { validationMixin } from 'vuelidate' -import { required, sameAs } from 'vuelidate/lib/validators' +import { required, requiredIf, sameAs } from 'vuelidate/lib/validators' import { mapActions, mapState } from 'vuex' const registration = { @@ -16,7 +16,7 @@ const registration = { }), validations: { user: { - email: { required }, + email: requiredIf('accountActivationRequired'), username: { required }, fullname: { required }, password: { required }, @@ -24,6 +24,11 @@ const registration = { required, sameAsPassword: sameAs('password') } + }, + nested: { + required: requiredIf(function (nestedModel) { + return this.accountActivationRequired + }) } }, created () { From 39e3917118293912b2af09f509457d718f0207c9 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 24 Feb 2020 11:23:16 -0600 Subject: [PATCH 04/32] Remove unneccessary nested --- src/components/registration/registration.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index fd2942a5..1d8109e4 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -24,11 +24,6 @@ const registration = { required, sameAsPassword: sameAs('password') } - }, - nested: { - required: requiredIf(function (nestedModel) { - return this.accountActivationRequired - }) } }, created () { From 7fa5eb07ddeb6d8c2b572e869d82a27bdd7a7fbf Mon Sep 17 00:00:00 2001 From: xenofem Date: Mon, 24 Feb 2020 18:10:15 -0500 Subject: [PATCH 05/32] Refactor status showing/hiding code for better handling of edge cases and easier comprehension --- src/components/status/status.js | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/components/status/status.js b/src/components/status/status.js index fc5956ec..61d66301 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -188,23 +188,22 @@ const Status = { } return this.status.attentions.length > 0 }, + + // When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status. + mightHideBecauseSubject () { + return this.status.summary && (!this.tallStatus || this.localCollapseSubjectDefault) + }, + mightHideBecauseTall () { + return this.tallStatus && (!this.status.summary || !this.localCollapseSubjectDefault) + }, hideSubjectStatus () { - if (this.tallStatus && !this.localCollapseSubjectDefault) { - return false - } - return !this.expandingSubject && this.status.summary + return this.mightHideBecauseSubject && !this.expandingSubject }, hideTallStatus () { - if (this.status.summary && this.localCollapseSubjectDefault) { - return false - } - if (this.showingTall) { - return false - } - return this.tallStatus + return this.mightHideBecauseTall && !this.showingTall }, showingMore () { - return (this.tallStatus && this.showingTall) || (this.status.summary && this.expandingSubject) + return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject) }, nsfwClickthrough () { if (!this.status.nsfw) { @@ -408,14 +407,10 @@ const Status = { this.userExpanded = !this.userExpanded }, toggleShowMore () { - if (this.showingTall) { - this.showingTall = false - } else if (this.expandingSubject && this.status.summary) { - this.expandingSubject = false - } else if (this.hideTallStatus) { - this.showingTall = true - } else if (this.hideSubjectStatus && this.status.summary) { - this.expandingSubject = true + if (this.mightHideBecauseTall) { + this.showingTall = !this.showingTall + } else if (this.mightHideBecauseSubject) { + this.expandingSubject = !this.expandingSubject } }, generateUserProfileLink (id, name) { From e4ded887964dc18513735d5d32f61a4e479314dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Tomaszewski?= Date: Sun, 1 Mar 2020 17:32:22 +0100 Subject: [PATCH 06/32] Update polish translation --- src/i18n/pl.json | 320 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 310 insertions(+), 10 deletions(-) diff --git a/src/i18n/pl.json b/src/i18n/pl.json index 51cadfb6..4a4b1e31 100644 --- a/src/i18n/pl.json +++ b/src/i18n/pl.json @@ -1,7 +1,47 @@ { + "about": { + "mrf": { + "federation": "Federacja", + "keyword": { + "keyword_policies": "Zasady słów kluczowych", + "ftl_removal": "Usunięcie z \"Całej znanej sieci\"", + "reject": "Odrzucanie", + "replace": "Zastąpienie", + "is_replaced_by": "→" + }, + "mrf_policies": "Włączone zasady MRF", + "mrf_policies_desc": "Zasady MRF zmieniają zachowanie federowania instancji. Następujące zasady są włączone:", + "simple": { + "simple_policies": "Zasady specyficzne dla instancji", + "accept": "Akceptowanie", + "accept_desc": "Ta instancja akceptuje tylko posty z wymienionych instancji:", + "reject": "Odrzucanie", + "reject_desc": "Ta instancja odrzuca posty z wymienionych instancji:", + "quarantine": "Kwarantanna", + "quarantine_desc": "Ta instancja wysyła tylko publiczne posty do wymienionych instancji:", + "ftl_removal": "Usunięcie z \"Całej znanej sieci\"", + "ftl_removal_desc": "Ta instancja usuwa te instancje z \"Całej znanej sieci\"", + "media_removal": "Usuwanie multimediów", + "media_removal_desc": "Ta instancja usuwa multimedia z postów od wymienionych instancji:", + "media_nsfw": "Multimedia ustawione jako wrażliwe", + "media_nsfw_desc": "Ta instancja wymusza, by multimedia z wymienionych instancji były ustawione jako wrażliwe:" + } + }, + "staff": "Obsługa" + }, "chat": { "title": "Czat" }, + "domain_mute_card": { + "mute": "Wycisz", + "mute_progress": "Wyciszam...", + "unmute": "Odcisz", + "unmute_progress": "Odciszam..." + }, + "exporter": { + "export": "Eksportuj", + "processing": "Przetwarzam, za chwilę zostaniesz zapytany o ściągnięcie pliku" + }, "features_panel": { "chat": "Czat", "gopher": "Gopher", @@ -20,7 +60,15 @@ "submit": "Wyślij", "more": "Więcej", "generic_error": "Wystąpił błąd", - "optional": "nieobowiązkowe" + "optional": "nieobowiązkowe", + "show_more": "Pokaż więcej", + "show_less": "Pokaż mniej", + "dismiss": "Odrzuć", + "cancel": "Anuluj", + "disable": "Wyłącz", + "enable": "Włącz", + "confirm": "Potwierdź", + "verify": "Zweryfikuj" }, "image_cropper": { "crop_picture": "Przytnij obrazek", @@ -28,6 +76,11 @@ "save_without_cropping": "Zapisz bez przycinania", "cancel": "Anuluj" }, + "importer": { + "submit": "Wyślij", + "success": "Zaimportowano pomyślnie", + "error": "Wystąpił błąd podczas importowania pliku." + }, "login": { "login": "Zaloguj", "description": "Zaloguj używając OAuth", @@ -36,7 +89,15 @@ "placeholder": "n.p. lain", "register": "Zarejestruj", "username": "Użytkownik", - "hint": "Zaloguj się, aby dołączyć do dyskusji" + "hint": "Zaloguj się, aby dołączyć do dyskusji", + "authentication_code": "Kod weryfikacyjny", + "enter_recovery_code": "Wprowadź kod zapasowy", + "enter_two_factor_code": "Wprowadź kod weryfikacyjny", + "recovery_code": "Kod zapasowy", + "heading" : { + "totp" : "Weryfikacja dwuetapowa", + "recovery" : "Zapasowa weryfikacja dwuetapowa" + } }, "media_modal": { "previous": "Poprzednie", @@ -44,15 +105,18 @@ }, "nav": { "about": "O nas", + "administration": "Administracja", "back": "Wróć", "chat": "Lokalny czat", "friend_requests": "Prośby o możliwość obserwacji", "mentions": "Wzmianki", + "interactions": "Interakcje", "dms": "Wiadomości prywatne", "public_tl": "Publiczna oś czasu", "timeline": "Oś czasu", "twkn": "Cała znana sieć", "user_search": "Wyszukiwanie użytkowników", + "search": "Wyszukiwanie", "who_to_follow": "Sugestie obserwacji", "preferences": "Preferencje" }, @@ -64,7 +128,40 @@ "notifications": "Powiadomienia", "read": "Przeczytane!", "repeated_you": "powtórzył(-a) twój status", - "no_more_notifications": "Nie masz więcej powiadomień" + "no_more_notifications": "Nie masz więcej powiadomień", + "migrated_to": "wyemigrował do", + "reacted_with": "zareagował z {0}" + }, + "polls": { + "add_poll": "Dodaj ankietę", + "add_option": "Dodaj opcję", + "option": "Opcja", + "votes": "głosów", + "vote": "Głosuj", + "type": "Typ ankiety", + "single_choice": "jednokrotnego wyboru", + "multiple_choices": "wielokrotnego wyboru", + "expiry": "Czas trwania ankiety", + "expires_in": "Ankieta kończy się za{0}", + "expired": "Ankieta skończyła się {0} temu", + "not_enough_options": "Zbyt mało unikalnych opcji w ankiecie" + }, + "emoji": { + "stickers": "Naklejki", + "emoji": "Emoji", + "keep_open": "Zostaw selektor otwarty", + "search_emoji": "Wyszukaj emoji", + "add_emoji": "Wstaw emoji", + "custom": "Niestandardowe emoji", + "unicode": "Emoji unicode", + "load_all_hint": "Załadowano pierwsze {saneAmount} emoji, Załadowanie wszystkich emoji może spowodować problemy z wydajnością.", + "load_all": "Ładuję wszystkie {emojiAmount} emoji" + }, + "interactions": { + "favs_repeats": "Powtórzenia i ulubione", + "follows": "Nowi obserwujący", + "moves": "Użytkownik migruje", + "load_older": "Załaduj starsze interakcje" }, "post_status": { "new_status": "Dodaj nowy status", @@ -79,8 +176,14 @@ }, "content_warning": "Temat (nieobowiązkowy)", "default": "Właśnie wróciłem z kościoła", - "direct_warning": "Ten wpis zobaczą tylko osoby, o których wspomniałeś(-aś).", + "direct_warning_to_all": "Ten wpis zobaczą wszystkie osoby, o których wspomniałeś(-aś).", + "direct_warning_to_first_only": "Ten wpis zobaczą tylko te osoby, o których wspomniałeś(-aś) na początku wiadomości.", "posting": "Wysyłanie", + "scope_notice": { + "public": "Ten post będzie widoczny dla każdego", + "private": "Ten post będzie widoczny tylko dla twoich obserwujących", + "unlisted": "Ten post nie będzie widoczny na publicznej osi czasu i całej znanej sieci" + }, "scope": { "direct": "Bezpośredni – Tylko dla wspomnianych użytkowników", "private": "Tylko dla obserwujących – Umieść dla osób, które cię obserwują", @@ -109,8 +212,40 @@ "password_confirmation_match": "musi być takie jak hasło" } }, + "remote_user_resolver": { + "remote_user_resolver": "Wyszukiwarka użytkowników nietutejszych", + "searching_for": "Szukam", + "error": "Nie znaleziono." + }, + "selectable_list": { + "select_all": "Zaznacz wszystko" + }, "settings": { "app_name": "Nazwa aplikacji", + "security": "Bezpieczeństwo", + "enter_current_password_to_confirm": "Wprowadź obecne hasło, by potwierdzić twoją tożsamość", + "mfa": { + "otp" : "OTP", + "setup_otp" : "Ustaw OTP", + "wait_pre_setup_otp" : "początkowe ustawianie OTP", + "confirm_and_enable" : "Potwierdź i włącz OTP", + "title": "Weryfikacja dwuetapowa", + "generate_new_recovery_codes" : "Wygeneruj nowe kody zapasowe", + "warning_of_generate_new_codes" : "Po tym gdy generujesz nowe kody zapasowe, stare przestaną działać.", + "recovery_codes" : "Kody zapasowe.", + "waiting_a_recovery_codes": "Otrzymuję kody zapasowe...", + "recovery_codes_warning" : "Spisz kody na kartce papieru, albo zapisz je w bezpiecznym miejscu - inaczej nie zobaczysz ich już nigdy. Jeśli stracisz dostęp do twojej aplikacji 2FA i kodów zapasowych, nie będziesz miał dostępu do swojego konta.", + "authentication_methods" : "Metody weryfikacji", + "scan": { + "title": "Skanuj", + "desc": "Zeskanuj ten kod QR używając twojej aplikacji 2FA albo wpisz ten klucz:", + "secret_code": "Klucz" + }, + "verify": { + "desc": "By włączyć weryfikację dwuetapową, wpisz kod z twojej aplikacji 2FA:" + } + }, + "allow_following_move": "Zezwalaj na automatyczną obserwację gdy obserwowane konto migruje", "attachmentRadius": "Załączniki", "attachments": "Załączniki", "autoload": "Włącz automatyczne ładowanie po przewinięciu do końca strony", @@ -119,12 +254,20 @@ "avatarRadius": "Awatary", "background": "Tło", "bio": "Bio", + "block_export": "Eksport blokad", + "block_export_button": "Eksportuj twoje blokady do pliku .csv", + "block_import": "Import blokad", + "block_import_error": "Wystąpił błąd podczas importowania blokad", + "blocks_imported": "Zaimportowano blokady, przetwarzanie może zająć trochę czasu.", "blocks_tab": "Bloki", "btnRadius": "Przyciski", "cBlue": "Niebieski (odpowiedz, obserwuj)", "cGreen": "Zielony (powtórzenia)", "cOrange": "Pomarańczowy (ulubione)", "cRed": "Czerwony (anuluj)", + "change_email": "Zmień email", + "change_email_error": "Wystąpił problem podczas zmiany emaila.", + "changed_email": "Pomyślnie zmieniono email!", "change_password": "Zmień hasło", "change_password_error": "Podczas zmiany hasła wystąpił problem.", "changed_password": "Pomyślnie zmieniono hasło!", @@ -140,16 +283,20 @@ "delete_account_description": "Trwale usuń konto i wszystkie posty.", "delete_account_error": "Wystąpił problem z usuwaniem twojego konta. Jeżeli problem powtarza się, poinformuj administratora swojej instancji.", "delete_account_instructions": "Wprowadź swoje hasło w poniższe pole aby potwierdzić usunięcie konta.", + "discoverable": "Zezwól na odkrywanie tego konta w wynikach wyszukiwania i innych usługa.", + "domain_mutes": "Domeny", "avatar_size_instruction": "Zalecany minimalny rozmiar awatarów to 150x150 pikseli.", + "pad_emoji": "Dodaj odstęp z obu stron emoji podczas dodawania selektorem", + "emoji_reactions_on_timeline": "Pokaż reakcje emoji na osi czasu", "export_theme": "Zapisz motyw", "filtering": "Filtrowanie", "filtering_explanation": "Wszystkie statusy zawierające te słowa będą wyciszone. Jedno słowo na linijkę.", "follow_export": "Eksport obserwowanych", "follow_export_button": "Eksportuj swoją listę obserwowanych do pliku CSV", - "follow_export_processing": "Przetwarzanie, wkrótce twój plik zacznie się ściągać.", "follow_import": "Import obserwowanych", "follow_import_error": "Błąd przy importowaniu obserwowanych", "follows_imported": "Obserwowani zaimportowani! Przetwarzanie może trochę potrwać.", + "accent": "Akcent", "foreground": "Pierwszy plan", "general": "Ogólne", "hide_attachments_in_convo": "Ukrywaj załączniki w rozmowach", @@ -162,6 +309,7 @@ "hide_post_stats": "Ukrywaj statysyki postów (np. liczbę polubień)", "hide_user_stats": "Ukrywaj statysyki użytkowników (np. liczbę obserwujących)", "hide_filtered_statuses": "Ukrywaj filtrowane statusy", + "import_blocks_from_a_csv_file": "Importuj blokady z pliku CSV", "import_followers_from_a_csv_file": "Importuj obserwowanych z pliku CSV", "import_theme": "Załaduj motyw", "inputRadius": "Pola tekstowe", @@ -181,17 +329,22 @@ "use_contain_fit": "Nie przycinaj załączników na miniaturach", "name": "Imię", "name_bio": "Imię i bio", + "new_email": "Nowy email", "new_password": "Nowe hasło", "notification_visibility": "Rodzaje powiadomień do wyświetlania", "notification_visibility_follows": "Obserwacje", "notification_visibility_likes": "Ulubione", "notification_visibility_mentions": "Wzmianki", "notification_visibility_repeats": "Powtórzenia", + "notification_visibility_moves": "Użytkownik migruje", + "notification_visibility_emoji_reactions": "Reakcje", "no_rich_text_description": "Usuwaj formatowanie ze wszystkich postów", "no_blocks": "Bez blokad", "no_mutes": "Bez wyciszeń", "hide_follows_description": "Nie pokazuj kogo obserwuję", "hide_followers_description": "Nie pokazuj kto mnie obserwuje", + "hide_follows_count_description": "Nie pokazuj licznika obserwowanych", + "hide_followers_count_description": "Nie pokazuj licznika obserwujących", "show_admin_badge": "Pokazuj odznakę Administrator na moim profilu", "show_moderator_badge": "Pokazuj odznakę Moderator na moim profilu", "nsfw_clickthrough": "Włącz domyślne ukrywanie załączników o treści nieprzyzwoitej (NSFW)", @@ -212,10 +365,14 @@ "reply_visibility_all": "Pokazuj wszystkie odpowiedzi", "reply_visibility_following": "Pokazuj tylko odpowiedzi skierowane do mnie i osób które obserwuję", "reply_visibility_self": "Pokazuj tylko odpowiedzi skierowane do mnie", + "autohide_floating_post_button": "Ukryj automatycznie przycisk \"Nowy post\" (mobile)", "saving_err": "Nie udało się zapisać ustawień", "saving_ok": "Zapisano ustawienia", + "search_user_to_block": "Wyszukaj kogo chcesz zablokować", + "search_user_to_mute": "Wyszukaj kogo chcesz wyciszyć", "security_tab": "Bezpieczeństwo", "scope_copy": "Kopiuj zakres podczas odpowiadania (DM-y zawsze są kopiowane)", + "minimal_scopes_mode": "Zminimalizuj opcje wyboru zakresu postów", "set_new_avatar": "Ustaw nowy awatar", "set_new_profile_background": "Ustaw nowe tło profilu", "set_new_profile_banner": "Ustaw nowy banner profilu", @@ -228,19 +385,32 @@ "post_status_content_type": "Post status content type", "stop_gifs": "Odtwarzaj GIFy po najechaniu kursorem", "streaming": "Włącz automatycznie strumieniowanie nowych postów gdy jesteś na początku strony", + "user_mutes": "Users", + "useStreamingApi": "Otrzymuj posty i powiadomienia w czasie rzeczywistym", + "useStreamingApiWarning": "(Niezalecane, eksperymentalne, pomija posty)", "text": "Tekst", "theme": "Motyw", "theme_help": "Użyj kolorów w notacji szesnastkowej (#rrggbb), by stworzyć swój motyw.", "theme_help_v2_1": "Możesz też zastąpić kolory i widoczność poszczególnych komponentów przełączając pola wyboru, użyj „Wyczyść wszystko” aby usunąć wszystkie zastąpienia.", "theme_help_v2_2": "Ikony pod niektórych wpisami są wskaźnikami kontrastu pomiędzy tłem a tekstem, po najechaniu na nie otrzymasz szczegółowe informacje. Zapamiętaj, że jeżeli używasz przezroczystości, wskaźniki pokazują najgorszy możliwy przypadek.", "tooltipRadius": "Etykiety/alerty", + "type_domains_to_mute": "Wpisz domeny, które chcesz wyciszyć", "upload_a_photo": "Wyślij zdjęcie", "user_settings": "Ustawienia użytkownika", "values": { "false": "nie", "true": "tak" }, + "fun": "Zabawa", + "greentext": "Memiczne strzałki", "notifications": "Powiadomienia", + "notification_setting": "Otrzymuj powiadomienia od:", + "notification_setting_follows": "Ludzi których obserwujesz", + "notification_setting_non_follows": "Ludzi których nie obserwujesz", + "notification_setting_followers": "Ludzi którzy obserwują ciebie", + "notification_setting_non_followers": "Ludzi którzy nie obserwują ciebie", + "notification_mutes": "By przestać otrzymywać powiadomienia od jednego użytkownika, wycisz go", + "notification_blocks": "Blokowanie uzytkownika zatrzymuje wszystkie powiadomienia i odsubskrybowuje go.", "enable_web_push_notifications": "Włącz powiadomienia push", "style": { "switcher": { @@ -252,7 +422,24 @@ "save_load_hint": "Opcje „zachowaj” pozwalają na pozostanie przy obecnych opcjach po wybraniu lub załadowaniu motywu, jak i przechowywanie ich podczas eksportowania motywu. Jeżeli wszystkie są odznaczone, eksportowanie motywu spowoduje zapisanie wszystkiego.", "reset": "Wyzeruj", "clear_all": "Wyczyść wszystko", - "clear_opacity": "Wyczyść widoczność" + "clear_opacity": "Wyczyść widoczność", + "load_theme": "Załaduj motyw", + "keep_as_is": "Zostaw po staremu", + "use_snapshot": "Stara wersja", + "use_source": "Nowa wersja", + "help": { + "upgraded_from_v2": "PleromaFE zostało zaaktualizowane, motyw może wyglądać nieco inaczej niż sobie zapamiętałeś.", + "v2_imported": "Plik który zaimportowałeś został stworzony dla starszego FE. Próbujemy zwiększyć kompatybiliność, lecz wciąż mogą występować rozbieżności.", + "future_version_imported": "Plik który zaimportowałeś został stworzony w nowszej wersji FE.", + "older_version_imported": "Plik który zaimportowałeś został stworzony w starszej wersji FE.", + "snapshot_present": "Migawka motywu jest załadowana, więc wszystkie wartości zostały nadpisane. Zamiast tego, możesz załadować właściwe dane motywu", + "snapshot_missing": "Nie znaleziono migawki motywu w pliku, więc motyw może wyglądać inaczej niż pierwotnie zaplanowano.", + "fe_upgraded": "Silnik motywów PleromaFE został zaaktualizowany.", + "fe_downgraded": "Wersja PleromaFE została cofnięta.", + "migration_snapshot_ok": "Żeby być bezpiecznym, migawka motywu została załadowana. Możesz spróbować załadować dane motywu.", + "migration_napshot_gone": "Z jakiegoś powodu migawka zniknęła, niektóre rzeczy mogą wyglądać inaczej niż sobie zapamiętałeś.", + "snapshot_source_mismatch": "Konflikt wersji: najprawdopodobniej FE zostało cofnięte do poprzedniej wersji i zaaktualizowane ponownie, jeśli zmieniłeś motyw używając starszej wersji FE, najprawdopodobniej chcesz używać starszej wersji, w przeciwnym razie użyj nowej wersji." + } }, "common": { "color": "Kolor", @@ -280,14 +467,28 @@ "_tab_label": "Zaawansowane", "alert": "Tło alertu", "alert_error": "Błąd", + "alert_warning": "Ostrzeżenie", + "alert_neutral": "Neutralne", + "post": "Posty/Bio użytkowników", "badge": "Tło odznaki", + "popover": "Etykiety, menu, popovery", "badge_notification": "Powiadomienie", "panel_header": "Nagłówek panelu", "top_bar": "Górny pasek", "borders": "Granice", "buttons": "Przyciski", "inputs": "Pola wejścia", - "faint_text": "Zanikający tekst" + "faint_text": "Zanikający tekst", + "underlay": "Podkład", + "poll": "Wykres ankiety", + "icons": "Ikony", + "highlight": "Podświetlone elementy", + "pressed": "Naciśnięte", + "selectedPost": "Wybrany post", + "selectedMenu": "Wybrany element menu", + "disabled": "Wyłączone", + "toggled": "Przełączone", + "tabs": "Karty" }, "radii": { "_tab_label": "Zaokrąglenie" @@ -300,7 +501,7 @@ "blur": "Rozmycie", "spread": "Szerokość", "inset": "Inset", - "hint": "Możesz też używać --zmiennych jako kolorów, aby wykorzystać zmienne CSS3. Pamiętaj, że ustawienie widoczności nie będzie wtedy działać.", + "hintV3": "Dla cieni możesz również użyć notacji {0} by użyć inny slot koloru.", "filter_hint": { "always_drop_shadow": "Ostrzeżenie, ten cień zawsze używa {0} jeżeli to obsługiwane przez przeglądarkę.", "drop_shadow_syntax": "{0} nie obsługuje parametru {1} i słowa kluczowego {2}.", @@ -357,6 +558,40 @@ "frontend_version": "Wersja front-endu" } }, + "time": { + "day": "{0} dzień", + "days": "{0} dni", + "day_short": "{0}d", + "days_short": "{0}d", + "hour": "{0} godzina", + "hours": "{0} godzin", + "hour_short": "{0} godz.", + "hours_short": "{0} godz.", + "in_future": "za {0}", + "in_past": "{0} temu", + "minute": "{0} minuta", + "minutes": "{0} minut", + "minute_short": "{0}min", + "minutes_short": "{0}min", + "month": "{0} miesiąc", + "months": "{0} miesięcy", + "month_short": "{0} mies.", + "months_short": "{0} mies.", + "now": "teraz", + "now_short": "teraz", + "second": "{0} sekunda", + "seconds": "{0} sekund", + "second_short": "{0}s", + "seconds_short": "{0}s", + "week": "{0} tydzień", + "weeks": "{0} tygodni", + "week_short": "{0} tydz.", + "weeks_short": "{0} tyg.", + "year": "{0} rok", + "years": "{0} lata", + "year_short": "{0} r.", + "years_short": "{0} lata" + }, "timeline": { "collapse": "Zwiń", "conversation": "Rozmowa", @@ -370,8 +605,17 @@ "no_statuses": "Brak statusów" }, "status": { + "favorites": "Ulubione", + "repeats": "Powtórzenia", + "delete": "Usuń status", + "pin": "Przypnij na profilu", + "unpin": "Odepnij z profilu", + "pinned": "Przypnięte", + "delete_confirm": "Czy naprawdę chcesz usunąć ten status?", "reply_to": "Odpowiedź dla", - "replies_list": "Odpowiedzi:" + "replies_list": "Odpowiedzi:", + "mute_conversation": "Wycisz konwersację", + "unmute_conversation": "Odcisz konwersację" }, "user_card": { "approve": "Przyjmij", @@ -388,25 +632,60 @@ "followers": "Obserwujący", "following": "Obserwowany!", "follows_you": "Obserwuje cię!", + "hidden": "Ukryte", "its_you": "To ty!", "media": "Media", + "mention": "Wspomnienie", "mute": "Wycisz", "muted": "Wyciszony(-a)", "per_day": "dziennie", "remote_follow": "Zdalna obserwacja", + "report": "Raportuj", "statuses": "Statusy", + "subscribe": "Subskrybuj", + "unsubscribe": "Odsubskrybuj", "unblock": "Odblokuj", "unblock_progress": "Odblokowuję…", "block_progress": "Blokuję…", "unmute": "Cofnij wyciszenie", "unmute_progress": "Cofam wyciszenie…", - "mute_progress": "Wyciszam…" + "mute_progress": "Wyciszam…", + "hide_repeats": "Ukryj powtórzenia", + "show_repeats": "Pokaż powtórzenia", + "admin_menu": { + "moderation": "Moderacja", + "grant_admin": "Przyznaj admina", + "revoke_admin": "Odwołaj admina", + "grant_moderator": "Przyznaj moderatora", + "revoke_moderator": "Odwołaj moderatora", + "activate_account": "Aktywuj konto", + "deactivate_account": "Dezaktywuj konto", + "delete_account": "Usuń konto", + "force_nsfw": "Oznacz wszystkie posty jako NSFW", + "strip_media": "Usuń multimedia z postów", + "force_unlisted": "Wymuś posty na niepubliczne", + "sandbox": "Wymuś by posty były tylko dla obserwujących", + "disable_remote_subscription": "Zakaż obserwowania użytkownika ze zdalnych instancji", + "disable_any_subscription": "Zakaż całkowicie obserwowania użytkownika", + "quarantine": "Zakaż federowania postów od tego użytkownika", + "delete_user": "Usuń użytkownika", + "delete_user_confirmation": "Czy jesteś absolutnie pewny? Ta operacja nie może być cofnięta." + } }, "user_profile": { "timeline_title": "Oś czasu użytkownika", "profile_does_not_exist": "Przepraszamy, ten profil nie istnieje.", "profile_loading_error": "Przepraszamy, wystąpił błąd podczas ładowania tego profilu." }, + "user_reporting": { + "title": "Raportowanie {0}", + "add_comment_description": "Raport zostanie wysłany do moderatorów instancji. Możesz dodać powód dlaczego raportujesz to konto poniżej:", + "additional_comments": "Dodatkowe komentarze", + "forward_description": "To konto jest z innego serwera. Wysłać również tam kopię raportu?", + "forward_to": "Przekaż do{0}", + "submit": "Wyślij", + "generic_error": "Wystąpił błąd podczas przetwarzania twojej prośby." + }, "who_to_follow": { "more": "Więcej", "who_to_follow": "Propozycje obserwacji" @@ -416,6 +695,7 @@ "repeat": "Powtórz", "reply": "Odpowiedz", "favorite": "Dodaj do ulubionych", + "add_reaction": "Dodaj reakcję", "user_settings": "Ustawienia użytkownika" }, "upload":{ @@ -431,5 +711,25 @@ "GiB": "GiB", "TiB": "TiB" } + }, + "search": { + "people": "Ludzie", + "hashtags": "Hasztagi", + "person_talking": "{count} osoba rozmawia o tym", + "people_talking": "{count} osób rozmawia o tym", + "no_results": "Brak wyników" + }, + "password_reset": { + "forgot_password": "Zapomniałeś hasła?", + "password_reset": "Reset hasła", + "instruction": "Wprowadź swój adres email lub nazwę użytkownika. Wyślemy ci link z którym możesz zresetować hasło.", + "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ś 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ć.", + "password_reset_required_but_mailer_is_disabled": "Musisz zresetować hasło, ale resetowanie hasła jest wyłączone. Proszę skontaktuj się z administratorem tej instancji." } } From 40005240eb30ce59035299dc348a3962626e32c6 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 31 Mar 2020 14:46:38 -0500 Subject: [PATCH 07/32] Send credentials for favourited_by and reblogged_by API endpoints This ensures the data is fetchable on private instances --- src/services/api/api.service.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 03e88ae2..7db1d094 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -880,12 +880,20 @@ const fetchPoll = ({ pollId, credentials }) => { ) } -const fetchFavoritedByUsers = ({ id }) => { - return promisedRequest({ url: MASTODON_STATUS_FAVORITEDBY_URL(id) }).then((users) => users.map(parseUser)) +const fetchFavoritedByUsers = ({ id, credentials }) => { + return promisedRequest({ + url: MASTODON_STATUS_FAVORITEDBY_URL(id), + method: 'GET', + credentials + }).then((users) => users.map(parseUser)) } -const fetchRebloggedByUsers = ({ id }) => { - return promisedRequest({ url: MASTODON_STATUS_REBLOGGEDBY_URL(id) }).then((users) => users.map(parseUser)) +const fetchRebloggedByUsers = ({ id, credentials }) => { + return promisedRequest({ + url: MASTODON_STATUS_REBLOGGEDBY_URL(id), + method: 'GET', + credentials + }).then((users) => users.map(parseUser)) } const fetchEmojiReactions = ({ id, credentials }) => { From b4e8b4554a69fcdb86a12484a2c6116207c0500b Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 8 Apr 2020 00:04:53 +0300 Subject: [PATCH 08/32] CHANGELOG.md: Add entries for upcoming 2.0.2 release --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c193b9..375e560f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +## [2.0.2] - 2020-04-08 +### Fixed +- Favorite/Repeat avatars not showing up on private instances/non-public posts +- Autocorrect getting triggered in the captcha field +- Overflow on long domains in follow/move notifications + +### Changed +- Polish translation updated + ## [2.0.0] - 2020-02-28 ### Added - Tons of color slots including ones for hover/pressed/toggled buttons From 18fa338d43b6b7b61b484ae6106ef3b95e5adeee Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 13 Apr 2020 15:26:55 +0400 Subject: [PATCH 09/32] Fix pagination --- src/services/api/api.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 7db1d094..ad2b2ad5 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -540,7 +540,7 @@ const fetchTimeline = ({ params.push(['with_move', withMove]) } - params.push(['count', 20]) + params.push(['limit', 20]) params.push(['with_muted', withMuted]) const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') From 2fbb94fe5c7f6743a699e6e1ccb2cd8de341ccca Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Sat, 18 Apr 2020 18:48:45 +0200 Subject: [PATCH 10/32] Fix user names with the RTL char in notifications --- src/components/notification/notification.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 411c0271..51875747 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -47,7 +47,7 @@
- Date: Mon, 27 Apr 2020 08:09:31 +0000 Subject: [PATCH 11/32] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 375e560f..f45561d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ 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] +### Fixed +- Show more/less works correctly with auto-collapsed subjects and long posts +- RTL characters won't look messed up in notifications + +### Changed +- Emoji autocomplete will match any part of the word and not just start, for example :drool will now helpfully suggest :blobcatdrool: and :blobcatdroolreach: ## [2.0.2] - 2020-04-08 ### Fixed From 01b07f01e9340935faf51e5a3c8034cc90423989 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Sat, 25 Apr 2020 07:04:39 +0300 Subject: [PATCH 12/32] Add support for follow request notifications --- CHANGELOG.md | 3 ++ src/components/notification/notification.js | 19 +++++++ src/components/notification/notification.vue | 50 +++++++++++++------ .../notifications/notifications.scss | 15 ++++++ src/i18n/en.json | 5 +- src/modules/config.js | 3 +- src/modules/statuses.js | 22 +++++++- src/services/api/api.service.js | 11 ++++ .../entity_normalizer.service.js | 5 +- .../notification_utils/notification_utils.js | 7 ++- static/fontello.json | 14 +++++- 11 files changed, 131 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f45561d0..685fe629 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ 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 +- Follow request notification support + ## [2.0.2] - 2020-04-08 ### Fixed - Favorite/Repeat avatars not showing up on private instances/non-public posts diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index e7bd769e..6deee7d5 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -2,6 +2,7 @@ import Status from '../status/status.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import UserCard from '../user_card/user_card.vue' import Timeago from '../timeago/timeago.vue' +import { isStatusNotification } from '../../services/notification_utils/notification_utils.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -32,6 +33,21 @@ const Notification = { }, toggleMute () { this.unmuted = !this.unmuted + }, + approveUser () { + this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) + this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('updateNotification', { + id: this.notification.id, + updater: notification => { + notification.type = 'follow' + } + }) + }, + denyUser () { + this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('dismissNotification', { id: this.notification.id }) } }, computed: { @@ -57,6 +73,9 @@ const Notification = { }, needMute () { return this.user.muted + }, + isStatusNotification () { + return isStatusNotification(this.notification.type) } } } diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 51875747..02802776 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -74,6 +74,10 @@ {{ $t('notifications.followed_you') }} + + + {{ $t('notifications.follow_request') }} + {{ $t('notifications.migrated_to') }} @@ -87,18 +91,7 @@
- - - -
-
+
+ + + +
{ each(notifications, (notification) => { - if (notification.type !== 'follow' && notification.type !== 'move') { + if (isStatusNotification(notification.type)) { notification.action = addStatusToGlobalStorage(state, notification.action).item notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item } @@ -361,13 +362,16 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot case 'move': i18nString = 'migrated_to' break + case 'follow_request': + i18nString = 'follow_request' + break } if (notification.type === 'pleroma:emoji_reaction') { notifObj.body = rootGetters.i18n.t('notifications.reacted_with', [notification.emoji]) } else if (i18nString) { notifObj.body = rootGetters.i18n.t('notifications.' + i18nString) - } else { + } else if (isStatusNotification(notification.type)) { notifObj.body = notification.status.text } @@ -521,6 +525,13 @@ export const mutations = { notification.seen = true }) }, + dismissNotification (state, { id }) { + state.notifications.data = state.notifications.data.filter(n => n.id !== id) + }, + updateNotification (state, { id, updater }) { + const notification = find(state.notifications.data, n => n.id === id) + notification && updater(notification) + }, queueFlush (state, { timeline, id }) { state.timelines[timeline].flushMarker = id }, @@ -680,6 +691,13 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + dismissNotification ({ rootState, commit }, { id }) { + rootState.api.backendInteractor.dismissNotification({ id }) + .then(() => commit('dismissNotification', { id })) + }, + updateNotification ({ rootState, commit }, { id, updater }) { + commit('updateNotification', { id, updater }) + }, fetchFavsAndRepeats ({ rootState, commit }, id) { Promise.all([ rootState.api.backendInteractor.fetchFavoritedByUsers({ id }), diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index ad2b2ad5..cda61ee2 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -29,6 +29,7 @@ const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials' const MASTODON_REGISTRATION_URL = '/api/v1/accounts' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications' +const MASTODON_DISMISS_NOTIFICATION_URL = id => `/api/v1/notifications/${id}/dismiss` const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite` const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite` const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog` @@ -1010,6 +1011,15 @@ const unmuteDomain = ({ domain, credentials }) => { }) } +const dismissNotification = ({ credentials, id }) => { + return promisedRequest({ + url: MASTODON_DISMISS_NOTIFICATION_URL(id), + method: 'POST', + payload: { id }, + credentials + }) +} + export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => { return Object.entries({ ...(credentials @@ -1165,6 +1175,7 @@ const apiService = { denyUser, suggestions, markNotificationsAsSeen, + dismissNotification, vote, fetchPoll, fetchFavoritedByUsers, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 84169a7b..6cacd0b8 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -1,4 +1,5 @@ import escape from 'escape-html' +import { isStatusNotification } from '../notification_utils/notification_utils.js' const qvitterStatusType = (status) => { if (status.is_post_verb) { @@ -346,9 +347,7 @@ export const parseNotification = (data) => { if (masto) { output.type = mastoDict[data.type] || data.type output.seen = data.pleroma.is_seen - output.status = output.type === 'follow' || output.type === 'move' - ? null - : parseStatus(data.status) + output.status = isStatusNotification(output.type) ? parseStatus(data.status) : null output.action = output.status // TODO: Refactor, this is unneeded output.target = output.type !== 'move' ? null diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index b17bd7bf..eb479227 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -1,4 +1,4 @@ -import { filter, sortBy } from 'lodash' +import { filter, sortBy, includes } from 'lodash' export const notificationsFromStore = store => store.state.statuses.notifications.data @@ -7,10 +7,15 @@ export const visibleTypes = store => ([ store.state.config.notificationVisibility.mentions && 'mention', store.state.config.notificationVisibility.repeats && 'repeat', store.state.config.notificationVisibility.follows && 'follow', + store.state.config.notificationVisibility.followRequest && 'follow_request', store.state.config.notificationVisibility.moves && 'move', store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction' ].filter(_ => _)) +const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction'] + +export const isStatusNotification = (type) => includes(statusNotifications, type) + const sortById = (a, b) => { const seqA = Number(a.id) const seqB = Number(b.id) diff --git a/static/fontello.json b/static/fontello.json index 5a7086a2..5963b68b 100755 --- a/static/fontello.json +++ b/static/fontello.json @@ -345,6 +345,18 @@ "css": "link", "code": 59427, "src": "fontawesome" + }, + { + "uid": "8b80d36d4ef43889db10bc1f0dc9a862", + "css": "user", + "code": 59428, + "src": "fontawesome" + }, + { + "uid": "12f4ece88e46abd864e40b35e05b11cd", + "css": "ok", + "code": 59431, + "src": "fontawesome" } ] -} +} \ No newline at end of file From 02c8a9e3143f2b12f44d24f307e2718dec22987b Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Fri, 1 May 2020 17:26:07 +0300 Subject: [PATCH 13/32] remove with_move param --- src/services/api/api.service.js | 6 +----- .../notifications_fetcher/notifications_fetcher.service.js | 3 --- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index ad2b2ad5..3c6b8f4e 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -495,8 +495,7 @@ const fetchTimeline = ({ until = false, userId = false, tag = false, - withMuted = false, - withMove = false + withMuted = false }) => { const timelineUrls = { public: MASTODON_PUBLIC_TIMELINE, @@ -536,9 +535,6 @@ const fetchTimeline = ({ if (timeline === 'public' || timeline === 'publicAndExternal') { params.push(['only_media', false]) } - if (timeline === 'notifications') { - params.push(['with_move', withMove]) - } params.push(['limit', 20]) params.push(['with_muted', withMuted]) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index 864e32f8..64499a1b 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -11,12 +11,9 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => { const rootState = store.rootState || store.state const timelineData = rootState.statuses.notifications const hideMutedPosts = getters.mergedConfig.hideMutedPosts - const allowFollowingMove = rootState.users.currentUser.allow_following_move args['withMuted'] = !hideMutedPosts - args['withMove'] = !allowFollowingMove - args['timeline'] = 'notifications' if (older) { if (timelineData.minId !== Number.POSITIVE_INFINITY) { From af3e69743e3192898f185fbc867defa1d155a4d4 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Fri, 1 May 2020 19:28:26 +0000 Subject: [PATCH 14/32] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f45561d0..86d981da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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] +## [2.0.3] - 2020-05-02 ### Fixed - Show more/less works correctly with auto-collapsed subjects and long posts - RTL characters won't look messed up in notifications From 406fdd8edec210d14589e0ff684a166250236779 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 10:19:47 +0300 Subject: [PATCH 15/32] follow request bugfixes, wrong text, notifs not being marked as read, approving from follow request view --- .../follow_request_card.js | 19 +++++++++++++++++++ src/components/notification/notification.js | 1 + src/components/notification/notification.vue | 6 +++--- .../notifications/notifications.scss | 19 +++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index a8931787..2a9d3db5 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -1,4 +1,5 @@ import BasicUserCard from '../basic_user_card/basic_user_card.vue' +import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js' const FollowRequestCard = { props: ['user'], @@ -6,13 +7,31 @@ const FollowRequestCard = { BasicUserCard }, methods: { + findFollowRequestNotificationId () { + const notif = notificationsFromStore(this.$store).find( + (notif) => notif.from_profile.id === this.user.id && notif.type === 'follow_request' + ) + return notif && notif.id + }, approveUser () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + + const notifId = this.findFollowRequestNotificationId() + this.$store.dispatch('updateNotification', { + id: notifId, + updater: notification => { + notification.type = 'follow' + notification.seen = true + } + }) }, denyUser () { this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + + const notifId = this.findFollowRequestNotificationId() + this.$store.dispatch('dismissNotification', { id: notifId }) } } } diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 6deee7d5..8c20ff09 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -41,6 +41,7 @@ const Notification = { id: this.notification.id, updater: notification => { notification.type = 'follow' + notification.seen = true } }) }, diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 02802776..f6da07dd 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -137,13 +137,13 @@ style="white-space: nowrap;" >
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 80dad28b..9efcfcf8 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -79,6 +79,25 @@ } } + .follow-request-accept { + cursor: pointer; + + &:hover { + color: $fallback--text; + color: var(--text, $fallback--text); + } + } + + .follow-request-reject { + cursor: pointer; + + &:hover { + color: $fallback--cRed; + color: var(--cRed, $fallback--cRed); + } + } + + .follow-text, .move-text { padding: 0.5em 0; overflow-wrap: break-word; From 75519223f9a715aacb99d3780ee681089a479292 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 10:52:57 +0300 Subject: [PATCH 16/32] mark single notifs as seen properly on server --- .../follow_request_card/follow_request_card.js | 2 +- src/components/notification/notification.js | 2 +- src/modules/statuses.js | 12 ++++++++++++ src/services/api/api.service.js | 12 ++++++++---- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index 2a9d3db5..33e2699e 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -18,11 +18,11 @@ const FollowRequestCard = { this.$store.dispatch('removeFollowRequest', this.user) const notifId = this.findFollowRequestNotificationId() + this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId }) this.$store.dispatch('updateNotification', { id: notifId, updater: notification => { notification.type = 'follow' - notification.seen = true } }) }, diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 8c20ff09..abe3bebe 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -37,11 +37,11 @@ const Notification = { approveUser () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id }) this.$store.dispatch('updateNotification', { id: this.notification.id, updater: notification => { notification.type = 'follow' - notification.seen = true } }) }, diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 239f41eb..2a8b9581 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -525,6 +525,10 @@ export const mutations = { notification.seen = true }) }, + markSingleNotificationAsSeen (state, { id }) { + const notification = find(state.notifications.data, n => n.id === id) + if (notification) notification.seen = true + }, dismissNotification (state, { id }) { state.notifications.data = state.notifications.data.filter(n => n.id !== id) }, @@ -691,6 +695,14 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + markSingleNotificationAsSeen ({ rootState, commit }, { id }) { + commit('markSingleNotificationAsSeen', { id }) + apiService.markNotificationsAsSeen({ + single: true, + id, + credentials: rootState.users.currentUser.credentials + }) + }, dismissNotification ({ rootState, commit }, { id }) { rootState.api.backendInteractor.dismissNotification({ id }) .then(() => commit('dismissNotification', { id })) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 3a58c38d..72c8874f 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -4,7 +4,6 @@ import 'whatwg-fetch' import { RegistrationError, StatusCodeError } from '../errors/errors' /* eslint-env browser */ -const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' @@ -17,6 +16,7 @@ const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate' const ADMIN_USERS_URL = '/api/pleroma/admin/users' const SUGGESTIONS_URL = '/api/v1/suggestions' const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' +const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa' const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes' @@ -841,12 +841,16 @@ const suggestions = ({ credentials }) => { }).then((data) => data.json()) } -const markNotificationsAsSeen = ({ id, credentials }) => { +const markNotificationsAsSeen = ({ id, credentials, single = false }) => { const body = new FormData() - body.append('latest_id', id) + if (single) { + body.append('id', id) + } else { + body.append('max_id', id) + } - return fetch(QVITTER_USER_NOTIFICATIONS_READ_URL, { + return fetch(NOTIFICATION_READ_URL, { body, headers: authHeaders(credentials), method: 'POST' From 92ccaa97bb0a2c15b96e2fbcf03823ba902dc516 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 11:51:39 +0300 Subject: [PATCH 17/32] don't dismiss a rejected follow request on server --- .../follow_request_card/follow_request_card.js | 9 +++++---- src/components/notification/notification.js | 6 ++++-- src/modules/statuses.js | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index 33e2699e..cbd75311 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -27,11 +27,12 @@ const FollowRequestCard = { }) }, denyUser () { - this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) - this.$store.dispatch('removeFollowRequest', this.user) - const notifId = this.findFollowRequestNotificationId() - this.$store.dispatch('dismissNotification', { id: notifId }) + this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + .then(() => { + this.$store.dispatch('dismissNotificationLocal', { id: notifId }) + this.$store.dispatch('removeFollowRequest', this.user) + }) } } } diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index abe3bebe..1ae81ce4 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -47,8 +47,10 @@ const Notification = { }, denyUser () { this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) - this.$store.dispatch('removeFollowRequest', this.user) - this.$store.dispatch('dismissNotification', { id: this.notification.id }) + .then(() => { + this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id }) + this.$store.dispatch('removeFollowRequest', this.user) + }) } }, computed: { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2a8b9581..cd8c1dba 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -703,9 +703,12 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + dismissNotificationLocal ({ rootState, commit }, { id }) { + commit('dismissNotification', { id }) + }, dismissNotification ({ rootState, commit }, { id }) { + commit('dismissNotification', { id }) rootState.api.backendInteractor.dismissNotification({ id }) - .then(() => commit('dismissNotification', { id })) }, updateNotification ({ rootState, commit }, { id, updater }) { commit('updateNotification', { id, updater }) From 068abb4d268d20ab6265a0a1a2520ea74fc45aa2 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Sat, 2 May 2020 13:05:45 +0000 Subject: [PATCH 18/32] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f9a2eb..ebd0e613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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] +### Changed +- Removed the use of with_move parameters when fetching notifications ## [2.0.3] - 2020-05-02 ### Fixed From 0ba34eeca58ae5caf13faf244bd3004800291051 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 13 Apr 2020 15:26:55 +0400 Subject: [PATCH 19/32] Fix pagination --- src/services/api/api.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 7db1d094..ad2b2ad5 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -540,7 +540,7 @@ const fetchTimeline = ({ params.push(['with_move', withMove]) } - params.push(['count', 20]) + params.push(['limit', 20]) params.push(['with_muted', withMuted]) const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') From c4d1c2131ccd79bae84f668b64116d8fbf16840d Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Sat, 18 Apr 2020 18:48:45 +0200 Subject: [PATCH 20/32] Fix user names with the RTL char in notifications --- src/components/notification/notification.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 411c0271..51875747 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -47,7 +47,7 @@
- Date: Mon, 24 Feb 2020 18:10:15 -0500 Subject: [PATCH 21/32] Refactor status showing/hiding code for better handling of edge cases and easier comprehension --- src/components/status/status.js | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/components/status/status.js b/src/components/status/status.js index fc5956ec..61d66301 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -188,23 +188,22 @@ const Status = { } return this.status.attentions.length > 0 }, + + // When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status. + mightHideBecauseSubject () { + return this.status.summary && (!this.tallStatus || this.localCollapseSubjectDefault) + }, + mightHideBecauseTall () { + return this.tallStatus && (!this.status.summary || !this.localCollapseSubjectDefault) + }, hideSubjectStatus () { - if (this.tallStatus && !this.localCollapseSubjectDefault) { - return false - } - return !this.expandingSubject && this.status.summary + return this.mightHideBecauseSubject && !this.expandingSubject }, hideTallStatus () { - if (this.status.summary && this.localCollapseSubjectDefault) { - return false - } - if (this.showingTall) { - return false - } - return this.tallStatus + return this.mightHideBecauseTall && !this.showingTall }, showingMore () { - return (this.tallStatus && this.showingTall) || (this.status.summary && this.expandingSubject) + return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject) }, nsfwClickthrough () { if (!this.status.nsfw) { @@ -408,14 +407,10 @@ const Status = { this.userExpanded = !this.userExpanded }, toggleShowMore () { - if (this.showingTall) { - this.showingTall = false - } else if (this.expandingSubject && this.status.summary) { - this.expandingSubject = false - } else if (this.hideTallStatus) { - this.showingTall = true - } else if (this.hideSubjectStatus && this.status.summary) { - this.expandingSubject = true + if (this.mightHideBecauseTall) { + this.showingTall = !this.showingTall + } else if (this.mightHideBecauseSubject) { + this.expandingSubject = !this.expandingSubject } }, generateUserProfileLink (id, name) { From aef03d53b2082f7a1198f63940a18dd112021982 Mon Sep 17 00:00:00 2001 From: xenofem Date: Sun, 9 Feb 2020 17:25:24 -0500 Subject: [PATCH 22/32] Allow emoji suggestions based on a match anywhere in the emoji name, but improve sorting --- src/components/emoji_input/suggestor.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index aec5c39d..9e437ccc 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -29,17 +29,21 @@ export default data => input => { export const suggestEmoji = emojis => input => { const noPrefix = input.toLowerCase().substr(1) return emojis - .filter(({ displayText }) => displayText.toLowerCase().startsWith(noPrefix)) + .filter(({ displayText }) => displayText.toLowerCase().match(noPrefix)) .sort((a, b) => { let aScore = 0 let bScore = 0 - // Make custom emojis a priority - aScore += a.imageUrl ? 10 : 0 - bScore += b.imageUrl ? 10 : 0 + // Prioritize emoji that start with the input string + aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 + bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 - // Sort alphabetically - const alphabetically = a.displayText > b.displayText ? 1 : -1 + // Sort by length + aScore -= a.displayText.length + bScore -= b.displayText.length + + // Break ties alphabetically + const alphabetically = a.displayText > b.displayText ? 0.5 : -0.5 return bScore - aScore + alphabetically }) From fe4282f44b4aff457c9b7473cb815b310fe6cb54 Mon Sep 17 00:00:00 2001 From: xenofem Date: Mon, 10 Feb 2020 09:32:07 -0500 Subject: [PATCH 23/32] Prioritize custom emoji a lot and boost exact matches to the top --- src/components/emoji_input/suggestor.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/emoji_input/suggestor.js b/src/components/emoji_input/suggestor.js index 9e437ccc..15a71eff 100644 --- a/src/components/emoji_input/suggestor.js +++ b/src/components/emoji_input/suggestor.js @@ -34,7 +34,15 @@ export const suggestEmoji = emojis => input => { let aScore = 0 let bScore = 0 - // Prioritize emoji that start with the input string + // An exact match always wins + aScore += a.displayText.toLowerCase() === noPrefix ? 200 : 0 + bScore += b.displayText.toLowerCase() === noPrefix ? 200 : 0 + + // Prioritize custom emoji a lot + aScore += a.imageUrl ? 100 : 0 + bScore += b.imageUrl ? 100 : 0 + + // Prioritize prefix matches somewhat aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0 From 372eb723dba981550a4b258ad74c727e0d40ba60 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Mon, 27 Apr 2020 08:09:31 +0000 Subject: [PATCH 24/32] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 375e560f..f45561d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ 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] +### Fixed +- Show more/less works correctly with auto-collapsed subjects and long posts +- RTL characters won't look messed up in notifications + +### Changed +- Emoji autocomplete will match any part of the word and not just start, for example :drool will now helpfully suggest :blobcatdrool: and :blobcatdroolreach: ## [2.0.2] - 2020-04-08 ### Fixed From 4d1a67463436c963288d7c65a5856dee69fa4c93 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Fri, 1 May 2020 19:28:26 +0000 Subject: [PATCH 25/32] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f45561d0..86d981da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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] +## [2.0.3] - 2020-05-02 ### Fixed - Show more/less works correctly with auto-collapsed subjects and long posts - RTL characters won't look messed up in notifications From ab3c0e8512e231a5bfb9464379d5090b876034fe Mon Sep 17 00:00:00 2001 From: eugenijm Date: Sat, 25 Apr 2020 07:04:39 +0300 Subject: [PATCH 26/32] Add support for follow request notifications --- CHANGELOG.md | 3 ++ src/components/notification/notification.js | 19 +++++++ src/components/notification/notification.vue | 50 +++++++++++++------ .../notifications/notifications.scss | 15 ++++++ src/i18n/en.json | 5 +- src/modules/config.js | 3 +- src/modules/statuses.js | 22 +++++++- src/services/api/api.service.js | 11 ++++ .../entity_normalizer.service.js | 5 +- .../notification_utils/notification_utils.js | 7 ++- static/fontello.json | 14 +++++- 11 files changed, 131 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86d981da..32f9a2eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ 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 +- Follow request notification support + ## [2.0.2] - 2020-04-08 ### Fixed - Favorite/Repeat avatars not showing up on private instances/non-public posts diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index e7bd769e..6deee7d5 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -2,6 +2,7 @@ import Status from '../status/status.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import UserCard from '../user_card/user_card.vue' import Timeago from '../timeago/timeago.vue' +import { isStatusNotification } from '../../services/notification_utils/notification_utils.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -32,6 +33,21 @@ const Notification = { }, toggleMute () { this.unmuted = !this.unmuted + }, + approveUser () { + this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) + this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('updateNotification', { + id: this.notification.id, + updater: notification => { + notification.type = 'follow' + } + }) + }, + denyUser () { + this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('dismissNotification', { id: this.notification.id }) } }, computed: { @@ -57,6 +73,9 @@ const Notification = { }, needMute () { return this.user.muted + }, + isStatusNotification () { + return isStatusNotification(this.notification.type) } } } diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 51875747..02802776 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -74,6 +74,10 @@ {{ $t('notifications.followed_you') }} + + + {{ $t('notifications.follow_request') }} + {{ $t('notifications.migrated_to') }} @@ -87,18 +91,7 @@
- - - -
-
+
+ + + +
{ each(notifications, (notification) => { - if (notification.type !== 'follow' && notification.type !== 'move') { + if (isStatusNotification(notification.type)) { notification.action = addStatusToGlobalStorage(state, notification.action).item notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item } @@ -361,13 +362,16 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot case 'move': i18nString = 'migrated_to' break + case 'follow_request': + i18nString = 'follow_request' + break } if (notification.type === 'pleroma:emoji_reaction') { notifObj.body = rootGetters.i18n.t('notifications.reacted_with', [notification.emoji]) } else if (i18nString) { notifObj.body = rootGetters.i18n.t('notifications.' + i18nString) - } else { + } else if (isStatusNotification(notification.type)) { notifObj.body = notification.status.text } @@ -521,6 +525,13 @@ export const mutations = { notification.seen = true }) }, + dismissNotification (state, { id }) { + state.notifications.data = state.notifications.data.filter(n => n.id !== id) + }, + updateNotification (state, { id, updater }) { + const notification = find(state.notifications.data, n => n.id === id) + notification && updater(notification) + }, queueFlush (state, { timeline, id }) { state.timelines[timeline].flushMarker = id }, @@ -680,6 +691,13 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + dismissNotification ({ rootState, commit }, { id }) { + rootState.api.backendInteractor.dismissNotification({ id }) + .then(() => commit('dismissNotification', { id })) + }, + updateNotification ({ rootState, commit }, { id, updater }) { + commit('updateNotification', { id, updater }) + }, fetchFavsAndRepeats ({ rootState, commit }, id) { Promise.all([ rootState.api.backendInteractor.fetchFavoritedByUsers({ id }), diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index ad2b2ad5..cda61ee2 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -29,6 +29,7 @@ const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials' const MASTODON_REGISTRATION_URL = '/api/v1/accounts' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications' +const MASTODON_DISMISS_NOTIFICATION_URL = id => `/api/v1/notifications/${id}/dismiss` const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite` const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite` const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog` @@ -1010,6 +1011,15 @@ const unmuteDomain = ({ domain, credentials }) => { }) } +const dismissNotification = ({ credentials, id }) => { + return promisedRequest({ + url: MASTODON_DISMISS_NOTIFICATION_URL(id), + method: 'POST', + payload: { id }, + credentials + }) +} + export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => { return Object.entries({ ...(credentials @@ -1165,6 +1175,7 @@ const apiService = { denyUser, suggestions, markNotificationsAsSeen, + dismissNotification, vote, fetchPoll, fetchFavoritedByUsers, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 84169a7b..6cacd0b8 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -1,4 +1,5 @@ import escape from 'escape-html' +import { isStatusNotification } from '../notification_utils/notification_utils.js' const qvitterStatusType = (status) => { if (status.is_post_verb) { @@ -346,9 +347,7 @@ export const parseNotification = (data) => { if (masto) { output.type = mastoDict[data.type] || data.type output.seen = data.pleroma.is_seen - output.status = output.type === 'follow' || output.type === 'move' - ? null - : parseStatus(data.status) + output.status = isStatusNotification(output.type) ? parseStatus(data.status) : null output.action = output.status // TODO: Refactor, this is unneeded output.target = output.type !== 'move' ? null diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index b17bd7bf..eb479227 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -1,4 +1,4 @@ -import { filter, sortBy } from 'lodash' +import { filter, sortBy, includes } from 'lodash' export const notificationsFromStore = store => store.state.statuses.notifications.data @@ -7,10 +7,15 @@ export const visibleTypes = store => ([ store.state.config.notificationVisibility.mentions && 'mention', store.state.config.notificationVisibility.repeats && 'repeat', store.state.config.notificationVisibility.follows && 'follow', + store.state.config.notificationVisibility.followRequest && 'follow_request', store.state.config.notificationVisibility.moves && 'move', store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction' ].filter(_ => _)) +const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction'] + +export const isStatusNotification = (type) => includes(statusNotifications, type) + const sortById = (a, b) => { const seqA = Number(a.id) const seqB = Number(b.id) diff --git a/static/fontello.json b/static/fontello.json index 5a7086a2..5963b68b 100755 --- a/static/fontello.json +++ b/static/fontello.json @@ -345,6 +345,18 @@ "css": "link", "code": 59427, "src": "fontawesome" + }, + { + "uid": "8b80d36d4ef43889db10bc1f0dc9a862", + "css": "user", + "code": 59428, + "src": "fontawesome" + }, + { + "uid": "12f4ece88e46abd864e40b35e05b11cd", + "css": "ok", + "code": 59431, + "src": "fontawesome" } ] -} +} \ No newline at end of file From 36dcfa8cc1315be65a47c041a8c926ff961b3ae4 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 10:19:47 +0300 Subject: [PATCH 27/32] follow request bugfixes, wrong text, notifs not being marked as read, approving from follow request view --- .../follow_request_card.js | 19 +++++++++++++++++++ src/components/notification/notification.js | 1 + src/components/notification/notification.vue | 6 +++--- .../notifications/notifications.scss | 19 +++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index a8931787..2a9d3db5 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -1,4 +1,5 @@ import BasicUserCard from '../basic_user_card/basic_user_card.vue' +import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js' const FollowRequestCard = { props: ['user'], @@ -6,13 +7,31 @@ const FollowRequestCard = { BasicUserCard }, methods: { + findFollowRequestNotificationId () { + const notif = notificationsFromStore(this.$store).find( + (notif) => notif.from_profile.id === this.user.id && notif.type === 'follow_request' + ) + return notif && notif.id + }, approveUser () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + + const notifId = this.findFollowRequestNotificationId() + this.$store.dispatch('updateNotification', { + id: notifId, + updater: notification => { + notification.type = 'follow' + notification.seen = true + } + }) }, denyUser () { this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + + const notifId = this.findFollowRequestNotificationId() + this.$store.dispatch('dismissNotification', { id: notifId }) } } } diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 6deee7d5..8c20ff09 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -41,6 +41,7 @@ const Notification = { id: this.notification.id, updater: notification => { notification.type = 'follow' + notification.seen = true } }) }, diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 02802776..f6da07dd 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -137,13 +137,13 @@ style="white-space: nowrap;" >
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 80dad28b..9efcfcf8 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -79,6 +79,25 @@ } } + .follow-request-accept { + cursor: pointer; + + &:hover { + color: $fallback--text; + color: var(--text, $fallback--text); + } + } + + .follow-request-reject { + cursor: pointer; + + &:hover { + color: $fallback--cRed; + color: var(--cRed, $fallback--cRed); + } + } + + .follow-text, .move-text { padding: 0.5em 0; overflow-wrap: break-word; From 20b53d58b753075bb1bda49d0595eba02ed5f755 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 10:52:57 +0300 Subject: [PATCH 28/32] mark single notifs as seen properly on server --- .../follow_request_card/follow_request_card.js | 2 +- src/components/notification/notification.js | 2 +- src/modules/statuses.js | 12 ++++++++++++ src/services/api/api.service.js | 12 ++++++++---- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index 2a9d3db5..33e2699e 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -18,11 +18,11 @@ const FollowRequestCard = { this.$store.dispatch('removeFollowRequest', this.user) const notifId = this.findFollowRequestNotificationId() + this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId }) this.$store.dispatch('updateNotification', { id: notifId, updater: notification => { notification.type = 'follow' - notification.seen = true } }) }, diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 8c20ff09..abe3bebe 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -37,11 +37,11 @@ const Notification = { approveUser () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id }) this.$store.dispatch('updateNotification', { id: this.notification.id, updater: notification => { notification.type = 'follow' - notification.seen = true } }) }, diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 239f41eb..2a8b9581 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -525,6 +525,10 @@ export const mutations = { notification.seen = true }) }, + markSingleNotificationAsSeen (state, { id }) { + const notification = find(state.notifications.data, n => n.id === id) + if (notification) notification.seen = true + }, dismissNotification (state, { id }) { state.notifications.data = state.notifications.data.filter(n => n.id !== id) }, @@ -691,6 +695,14 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + markSingleNotificationAsSeen ({ rootState, commit }, { id }) { + commit('markSingleNotificationAsSeen', { id }) + apiService.markNotificationsAsSeen({ + single: true, + id, + credentials: rootState.users.currentUser.credentials + }) + }, dismissNotification ({ rootState, commit }, { id }) { rootState.api.backendInteractor.dismissNotification({ id }) .then(() => commit('dismissNotification', { id })) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index cda61ee2..9d1ce393 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -4,7 +4,6 @@ import 'whatwg-fetch' import { RegistrationError, StatusCodeError } from '../errors/errors' /* eslint-env browser */ -const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' @@ -17,6 +16,7 @@ const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate' const ADMIN_USERS_URL = '/api/pleroma/admin/users' const SUGGESTIONS_URL = '/api/v1/suggestions' const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' +const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa' const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes' @@ -845,12 +845,16 @@ const suggestions = ({ credentials }) => { }).then((data) => data.json()) } -const markNotificationsAsSeen = ({ id, credentials }) => { +const markNotificationsAsSeen = ({ id, credentials, single = false }) => { const body = new FormData() - body.append('latest_id', id) + if (single) { + body.append('id', id) + } else { + body.append('max_id', id) + } - return fetch(QVITTER_USER_NOTIFICATIONS_READ_URL, { + return fetch(NOTIFICATION_READ_URL, { body, headers: authHeaders(credentials), method: 'POST' From b095d2e17e03189adb62227da95ca5431bc64f5a Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 11:51:39 +0300 Subject: [PATCH 29/32] don't dismiss a rejected follow request on server --- .../follow_request_card/follow_request_card.js | 9 +++++---- src/components/notification/notification.js | 6 ++++-- src/modules/statuses.js | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index 33e2699e..cbd75311 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -27,11 +27,12 @@ const FollowRequestCard = { }) }, denyUser () { - this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) - this.$store.dispatch('removeFollowRequest', this.user) - const notifId = this.findFollowRequestNotificationId() - this.$store.dispatch('dismissNotification', { id: notifId }) + this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + .then(() => { + this.$store.dispatch('dismissNotificationLocal', { id: notifId }) + this.$store.dispatch('removeFollowRequest', this.user) + }) } } } diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index abe3bebe..1ae81ce4 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -47,8 +47,10 @@ const Notification = { }, denyUser () { this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) - this.$store.dispatch('removeFollowRequest', this.user) - this.$store.dispatch('dismissNotification', { id: this.notification.id }) + .then(() => { + this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id }) + this.$store.dispatch('removeFollowRequest', this.user) + }) } }, computed: { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2a8b9581..cd8c1dba 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -703,9 +703,12 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + dismissNotificationLocal ({ rootState, commit }, { id }) { + commit('dismissNotification', { id }) + }, dismissNotification ({ rootState, commit }, { id }) { + commit('dismissNotification', { id }) rootState.api.backendInteractor.dismissNotification({ id }) - .then(() => commit('dismissNotification', { id })) }, updateNotification ({ rootState, commit }, { id, updater }) { commit('updateNotification', { id, updater }) From 2618c1b702d1881970cd1ee1109c421b24f2229e Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Sat, 2 May 2020 13:05:45 +0000 Subject: [PATCH 30/32] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f9a2eb..ebd0e613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ 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] +### Changed +- Removed the use of with_move parameters when fetching notifications ## [2.0.3] - 2020-05-02 ### Fixed From f7f8a579fa17102a994dc7bd7a4c7808e0964d55 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 4 May 2020 12:56:39 +0300 Subject: [PATCH 31/32] make email validation conditional work --- src/components/registration/registration.js | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index 1d8109e4..dab06e1e 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -14,15 +14,17 @@ const registration = { }, captcha: {} }), - validations: { - user: { - email: requiredIf('accountActivationRequired'), - username: { required }, - fullname: { required }, - password: { required }, - confirm: { - required, - sameAsPassword: sameAs('password') + validations () { + return { + user: { + email: { required: requiredIf(() => this.accountActivationRequired) }, + username: { required }, + fullname: { required }, + password: { required }, + confirm: { + required, + sameAsPassword: sameAs('password') + } } } }, @@ -43,7 +45,8 @@ const registration = { signedIn: (state) => !!state.users.currentUser, isPending: (state) => state.users.signUpPending, serverValidationErrors: (state) => state.users.signUpErrors, - termsOfService: (state) => state.instance.tos + termsOfService: (state) => state.instance.tos, + accountActivationRequired: (state) => state.instance.accountActivationRequired }) }, methods: { From b3003d4e8de46ebf0ade12e6c6527bbfdb016e1d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 6 May 2020 11:46:40 -0500 Subject: [PATCH 32/32] Add notification privacy option to user settings --- src/components/user_settings/user_settings.vue | 12 ++++++++++++ src/i18n/en.json | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index 8b2336b4..ad184520 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -379,6 +379,7 @@ :label="$t('settings.notifications')" >
+

{{ $t('settings.notification_setting_filters') }}

{{ $t('settings.notification_setting') }}
    @@ -404,6 +405,17 @@
+
+ +
+

{{ $t('settings.notification_setting_privacy') }}

+

+ + {{ $t('settings.notification_setting_privacy_option') }} + +

+
+

{{ $t('settings.notification_mutes') }}

{{ $t('settings.notification_blocks') }}