diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/boot/after_store.js | 2 | ||||
| -rw-r--r-- | src/components/attachment/attachment.vue | 1 | ||||
| -rw-r--r-- | src/components/notifications/notifications.js | 3 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.js | 3 | ||||
| -rw-r--r-- | src/components/post_status_form/post_status_form.vue | 10 | ||||
| -rw-r--r-- | src/components/settings/settings.js | 3 | ||||
| -rw-r--r-- | src/components/settings/settings.vue | 14 | ||||
| -rw-r--r-- | src/components/status/status.vue | 5 | ||||
| -rw-r--r-- | src/components/timeline/timeline.js | 4 | ||||
| -rw-r--r-- | src/components/user_card/user_card.js | 14 | ||||
| -rw-r--r-- | src/i18n/cs.json | 427 | ||||
| -rw-r--r-- | src/i18n/en.json | 5 | ||||
| -rw-r--r-- | src/i18n/eo.json | 1 | ||||
| -rw-r--r-- | src/i18n/es.json | 1 | ||||
| -rw-r--r-- | src/i18n/ja.json | 1 | ||||
| -rw-r--r-- | src/i18n/messages.js | 1 | ||||
| -rw-r--r-- | src/i18n/oc.json | 1 | ||||
| -rw-r--r-- | src/i18n/pt.json | 1 | ||||
| -rw-r--r-- | src/main.js | 3 | ||||
| -rw-r--r-- | src/modules/instance.js | 1 | ||||
| -rw-r--r-- | src/modules/statuses.js | 52 | ||||
| -rw-r--r-- | src/modules/users.js | 2 | ||||
| -rw-r--r-- | src/services/timeline_fetcher/timeline_fetcher.service.js | 2 |
23 files changed, 517 insertions, 40 deletions
diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 53ecc083..a8e2bf35 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -169,6 +169,8 @@ const afterStoreSetup = ({ store, i18n }) => { store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') }) store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') }) + store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats }) + store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames }) const suggestions = metadata.suggestions diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 76affe2d..c58bebd3 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -160,6 +160,7 @@ .hider { position: absolute; + right: 0; white-space: nowrap; margin: 10px; padding: 5px; diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 5e95631a..9fc5e38a 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -11,7 +11,8 @@ const Notifications = { const store = this.$store const credentials = store.state.users.currentUser.credentials - notificationsFetcher.startFetching({ store, credentials }) + const fetcherId = notificationsFetcher.startFetching({ store, credentials }) + this.$store.commit('setNotificationFetcher', { fetcherId }) }, data () { return { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index c28c51bf..23a2c7e2 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -171,6 +171,9 @@ const PostStatusForm = { }, formattingOptionsEnabled () { return this.$store.state.instance.formattingOptionsEnabled + }, + postFormats () { + return this.$store.state.instance.postFormats || [] } }, methods: { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 5085570b..0ddde4ea 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -30,15 +30,17 @@ @drop="fileDrop" @dragover.prevent="fileDrag" @input="resize" - @paste="paste"> + @paste="paste" + :disabled="posting" + > </textarea> <div class="visibility-tray"> <span class="text-format" v-if="formattingOptionsEnabled"> <label for="post-content-type" class="select"> <select id="post-content-type" v-model="newStatus.contentType" class="form-control"> - <option value="text/plain">{{$t('post_status.content_type.plain_text')}}</option> - <option value="text/html">HTML</option> - <option value="text/markdown">Markdown</option> + <option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat"> + {{$t(`post_status.content_type["${postFormat}"]`)}} + </option> </select> <i class="icon-down-open"></i> </label> diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index 6e2dff7b..979457a5 100644 --- a/src/components/settings/settings.js +++ b/src/components/settings/settings.js @@ -93,6 +93,9 @@ const settings = { currentSaveStateNotice () { return this.$store.state.interface.settings.currentSaveStateNotice }, + postFormats () { + return this.$store.state.instance.postFormats || [] + }, instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel } }, watch: { diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 5041b3a3..d2346747 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -105,17 +105,9 @@ {{$t('settings.post_status_content_type')}} <label for="postContentType" class="select"> <select id="postContentType" v-model="postContentTypeLocal"> - <option value="text/plain"> - {{$t('settings.status_content_type_plain')}} - {{postContentTypeDefault == 'text/plain' ? $t('settings.instance_default_simple') : ''}} - </option> - <option value="text/html"> - HTML - {{postContentTypeDefault == 'text/html' ? $t('settings.instance_default_simple') : ''}} - </option> - <option value="text/markdown"> - Markdown - {{postContentTypeDefault == 'text/markdown' ? $t('settings.instance_default_simple') : ''}} + <option v-for="postFormat in postFormats" :key="postFormat" :value="postFormat"> + {{$t(`post_status.content_type["${postFormat}"]`)}} + {{postContentTypeDefault === postFormat ? $t('settings.instance_default_simple') : ''}} </option> </select> <i class="icon-down-open"/> diff --git a/src/components/status/status.vue b/src/components/status/status.vue index c234bc03..1f6d0325 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -419,6 +419,11 @@ $status-margin: 0.75em; max-height: 400px; vertical-align: middle; object-fit: contain; + + &.emoji { + width: 32px; + height: 32px; + } } blockquote { diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 655bfb3f..c45f8947 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -132,7 +132,9 @@ const Timeline = { } if (count > 0) { // only 'stream' them when you're scrolled to the top - if (window.pageYOffset < 15 && + const doc = document.documentElement + const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) + if (top < 15 && !this.paused && !(this.unfocused && this.$store.state.config.pauseOnUnfocused) ) { diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 81c938c0..80d15a27 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -100,22 +100,30 @@ export default { }, methods: { followUser () { + const store = this.$store this.followRequestInProgress = true - requestFollow(this.user, this.$store).then(({sent}) => { + requestFollow(this.user, store).then(({sent}) => { this.followRequestInProgress = false this.followRequestSent = sent }) }, unfollowUser () { + const store = this.$store this.followRequestInProgress = true - requestUnfollow(this.user, this.$store).then(() => { + requestUnfollow(this.user, store).then(() => { this.followRequestInProgress = false + store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) }) }, blockUser () { const store = this.$store store.state.api.backendInteractor.blockUser(this.user.id) - .then((blockedUser) => store.commit('addNewUsers', [blockedUser])) + .then((blockedUser) => { + store.commit('addNewUsers', [blockedUser]) + store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) + store.commit('removeStatus', { timeline: 'public', userId: this.user.id }) + store.commit('removeStatus', { timeline: 'publicAndExternal', userId: this.user.id }) + }) }, unblockUser () { const store = this.$store diff --git a/src/i18n/cs.json b/src/i18n/cs.json new file mode 100644 index 00000000..6326032c --- /dev/null +++ b/src/i18n/cs.json @@ -0,0 +1,427 @@ +{ + "chat": { + "title": "Chat" + }, + "features_panel": { + "chat": "Chat", + "gopher": "Gopher", + "media_proxy": "Mediální proxy", + "scope_options": "Možnosti rozsahů", + "text_limit": "Textový limit", + "title": "Vlastnosti", + "who_to_follow": "Koho sledovat" + }, + "finder": { + "error_fetching_user": "Chyba při načítání uživatele", + "find_user": "Najít uživatele" + }, + "general": { + "apply": "Použít", + "submit": "Odeslat", + "more": "Více", + "generic_error": "Vyskytla se chyba", + "optional": "volitelné" + }, + "image_cropper": { + "crop_picture": "Oříznout obrázek", + "save": "Uložit", + "cancel": "Zrušit" + }, + "login": { + "login": "Přihlásit", + "description": "Přihlásit pomocí OAuth", + "logout": "Odhlásit", + "password": "Heslo", + "placeholder": "např. lain", + "register": "Registrovat", + "username": "Uživatelské jméno", + "hint": "Chcete-li se přidat do diskuze, přihlaste se" + }, + "media_modal": { + "previous": "Předchozí", + "next": "Další" + }, + "nav": { + "about": "O instanci", + "back": "Zpět", + "chat": "Místní chat", + "friend_requests": "Požadavky o sledování", + "mentions": "Zmínky", + "dms": "Přímé zprávy", + "public_tl": "Veřejná časová osa", + "timeline": "Časová osa", + "twkn": "Celá známá síť", + "user_search": "Hledání uživatelů", + "who_to_follow": "Koho sledovat", + "preferences": "Předvolby" + }, + "notifications": { + "broken_favorite": "Neznámý příspěvek, hledám jej…", + "favorited_you": "si oblíbil/a váš příspěvek", + "followed_you": "vás nyní sleduje", + "load_older": "Načíst starší oznámení", + "notifications": "Oznámení", + "read": "Číst!", + "repeated_you": "zopakoval/a váš příspěvek", + "no_more_notifications": "Žádná další oznámení" + }, + "post_status": { + "new_status": "Napsat nový příspěvek", + "account_not_locked_warning": "Váš účet není {0}. Kdokoliv vás může sledovat a vidět vaše příspěvky pouze pro sledující.", + "account_not_locked_warning_link": "uzamčen", + "attachments_sensitive": "Označovat přílohy jako citlivé", + "content_type": { + "plain_text": "Prostý text" + }, + "content_warning": "Předmět (volitelný)", + "default": "Právě jsem přistál v L.A.", + "direct_warning": "Tento příspěvek uvidí pouze všichni zmínění uživatelé.", + "posting": "Přispívání", + "scope": { + "direct": "Přímý - Poslat pouze zmíněným uživatelům", + "private": "Pouze pro sledující - Poslat pouze sledujícím", + "public": "Veřejný - Poslat na veřejné časové osy", + "unlisted": "Neuvedený - Neposlat na veřejné časové osy" + } + }, + "registration": { + "bio": "O vás", + "email": "E-mail", + "fullname": "Zobrazované jméno", + "password_confirm": "Potvrzení hesla", + "registration": "Registrace", + "token": "Token pozvánky", + "captcha": "CAPTCHA", + "new_captcha": "Kliknutím na obrázek získáte novou CAPTCHA", + "username_placeholder": "např. lain", + "fullname_placeholder": "např. Lain Iwakura", + "bio_placeholder": "např.\nNazdar, jsem Lain\nJsem anime dívka a žiji v příměstském Japonsku. Možná mě znáte z Wired.", + "validations": { + "username_required": "nemůže být prázdné", + "fullname_required": "nemůže být prázdné", + "email_required": "nemůže být prázdný", + "password_required": "nemůže být prázdné", + "password_confirmation_required": "nemůže být prázdné", + "password_confirmation_match": "musí být stejné jako heslo" + } + }, + "settings": { + "app_name": "Název aplikace", + "attachmentRadius": "Přílohy", + "attachments": "Přílohy", + "autoload": "Povolit automatické načítání při rolování dolů", + "avatar": "Avatar", + "avatarAltRadius": "Avatary (oznámení)", + "avatarRadius": "Avatary", + "background": "Pozadí", + "bio": "O vás", + "blocks_tab": "Blokování", + "btnRadius": "Tlačítka", + "cBlue": "Modrá (Odpovědět, sledovat)", + "cGreen": "Zelená (Zopakovat)", + "cOrange": "Oranžová (Oblíbit)", + "cRed": "Červená (Zrušit)", + "change_password": "Změnit heslo", + "change_password_error": "Při změně vašeho hesla se vyskytla chyba.", + "changed_password": "Heslo bylo úspěšně změněno!", + "collapse_subject": "Zabalit příspěvky s předměty", + "composing": "Komponování", + "confirm_new_password": "Potvrďte nové heslo", + "current_avatar": "Váš současný avatar", + "current_password": "Současné heslo", + "current_profile_banner": "Váš současný profilový banner", + "data_import_export_tab": "Import/export dat", + "default_vis": "Výchozí rozsah viditelnosti", + "delete_account": "Smazat účet", + "delete_account_description": "Trvale smaže váš účet a všechny vaše příspěvky.", + "delete_account_error": "Při mazání vašeho účtu nastala chyba. Pokud tato chyba bude trvat, kontaktujte prosím admministrátora vaší instance.", + "delete_account_instructions": "Pro potvrzení smazání účtu napište své heslo do pole níže.", + "avatar_size_instruction": "Doporučená minimální velikost pro avatarové obrázky je 150x150 pixelů.", + "export_theme": "Uložit přednastavení", + "filtering": "Filtrování", + "filtering_explanation": "Všechny příspěvky obsahující tato slova budou skryty. Napište jedno slovo na každý řádek", + "follow_export": "Export sledovaných", + "follow_export_button": "Exportovat vaše sledované do souboru CSV", + "follow_export_processing": "Zpracovávám, brzy si budete moci stáhnout váš soubor", + "follow_import": "Import sledovaných", + "follow_import_error": "Chyba při importování sledovaných", + "follows_imported": "Sledovaní importováni! Jejich zpracování bude chvilku trvat.", + "foreground": "Popředí", + "general": "Obecné", + "hide_attachments_in_convo": "Skrývat přílohy v konverzacích", + "hide_attachments_in_tl": "Skrývat přílohy v časové ose", + "max_thumbnails": "Maximální počet miniatur na příspěvek", + "hide_isp": "Skrýt panel specifický pro instanci", + "preload_images": "Přednačítat obrázky", + "use_one_click_nsfw": "Otevírat citlivé přílohy pouze jedním kliknutím", + "hide_post_stats": "Skrývat statistiky příspěvků (např. počet oblíbení)", + "hide_user_stats": "Skrývat statistiky uživatelů (např. počet sledujících)", + "hide_filtered_statuses": "Skrývat filtrované příspěvky", + "import_followers_from_a_csv_file": "Importovat sledované ze souboru CSV", + "import_theme": "Načíst přednastavení", + "inputRadius": "Vstupní pole", + "checkboxRadius": "Zaškrtávací pole", + "instance_default": "(výchozí: {value})", + "instance_default_simple": "(výchozí)", + "interface": "Rozhraní", + "interfaceLanguage": "Jazyk rozhraní", + "invalid_theme_imported": "Zvolený soubor není podporovaný motiv Pleroma. Nebyly provedeny žádné změny s vaším motivem.", + "limited_availability": "Nedostupné ve vašem prohlížeči", + "links": "Odkazy", + "lock_account_description": "Omezit váš účet pouze na schválené sledující", + "loop_video": "Opakovat videa", + "loop_video_silent_only": "Opakovat pouze videa beze zvuku (t.j. „GIFy“ na Mastodonu)", + "mutes_tab": "Ignorování", + "play_videos_in_modal": "Přehrávat videa přímo v prohlížeči médií", + "use_contain_fit": "Neořezávat přílohu v miniaturách", + "name": "Jméno", + "name_bio": "Jméno a popis", + "new_password": "Nové heslo", + "notification_visibility": "Typy oznámení k zobrazení", + "notification_visibility_follows": "Sledující", + "notification_visibility_likes": "Oblíbení", + "notification_visibility_mentions": "Zmínky", + "notification_visibility_repeats": "Zopakování", + "no_rich_text_description": "Odstranit ze všech příspěvků formátování textu", + "no_blocks": "Žádná blokování", + "no_mutes": "Žádná ignorování", + "hide_follows_description": "Nezobrazovat, koho sleduji", + "hide_followers_description": "Nezobrazovat, kdo mě sleduje", + "show_admin_badge": "Zobrazovat v mém profilu odznak administrátora", + "show_moderator_badge": "Zobrazovat v mém profilu odznak moderátora", + "nsfw_clickthrough": "Povolit prokliknutelné skrývání citlivých příloh", + "oauth_tokens": "Tokeny OAuth", + "token": "Token", + "refresh_token": "Obnovit token", + "valid_until": "Platný do", + "revoke_token": "Odvolat", + "panelRadius": "Panely", + "pause_on_unfocused": "Pozastavit streamování, pokud není záložka prohlížeče v soustředění", + "presets": "Přednastavení", + "profile_background": "Profilové pozadí", + "profile_banner": "Profilový banner", + "profile_tab": "Profil", + "radii_help": "Nastavit zakulacení rohů rozhraní (v pixelech)", + "replies_in_timeline": "Odpovědi v časové ose", + "reply_link_preview": "Povolit náhledy odkazu pro odpověď při přejetí myši", + "reply_visibility_all": "Zobrazit všechny odpovědiShow all replies", + "reply_visibility_following": "Zobrazit pouze odpovědi směřované na mě nebo uživatele, které sleduji", + "reply_visibility_self": "Zobrazit pouze odpovědi směřované na mě", + "saving_err": "Chyba při ukládání nastavení", + "saving_ok": "Nastavení uložena", + "security_tab": "Bezpečnost", + "scope_copy": "Kopírovat rozsah při odpovídání (přímé zprávy jsou vždy kopírovány)", + "set_new_avatar": "Nastavit nový avatar", + "set_new_profile_background": "Nastavit nové profilové pozadí", + "set_new_profile_banner": "Nastavit nový profilový banner", + "settings": "Nastavení", + "subject_input_always_show": "Vždy zobrazit pole pro předmět", + "subject_line_behavior": "Kopírovat předmět při odpovídání", + "subject_line_email": "Jako u e-mailu: „re: předmět“", + "subject_line_mastodon": "Jako u Mastodonu: zkopírovat tak, jak je", + "subject_line_noop": "Nekopírovat", + "post_status_content_type": "Publikovat typ obsahu příspěvku", + "status_content_type_plain": "Prostý text", + "stop_gifs": "Přehrávat GIFy při přejetí myši", + "streaming": "Povolit automatické streamování nových příspěvků při rolování nahoru", + "text": "Text", + "theme": "Motiv", + "theme_help": "Použijte hexadecimální barevné kódy (#rrggbb) pro přizpůsobení vašeho barevného motivu.", + "theme_help_v2_1": "Zaškrtnutím pole můžete také přepsat barvy a průhlednost některých komponentů, pro smazání všech přednastavení použijte tlačítko „Smazat vše“.", + "theme_help_v2_2": "Ikony pod některými položkami jsou indikátory kontrastu pozadí/textu, pro detailní informace nad nimi přejeďte myší. Prosím berte na vědomí, že při používání kontrastu průhlednosti ukazují indikátory nejhorší možný případ.", + "tooltipRadius": "Popisky/upozornění", + "upload_a_photo": "Nahrát fotku", + "user_settings": "Uživatelská nastavení", + "values": { + "false": "ne", + "true": "ano" + }, + "notifications": "Oznámení", + "enable_web_push_notifications": "Povolit webová push oznámení", + "style": { + "switcher": { + "keep_color": "Ponechat barvy", + "keep_shadows": "Ponechat stíny", + "keep_opacity": "Ponechat průhlednost", + "keep_roundness": "Ponechat kulatost", + "keep_fonts": "Keep fonts", + "save_load_hint": "Možnosti „Ponechat“ dočasně ponechávají aktuálně nastavené možností při volení či nahrávání motivů, také tyto možnosti ukládají při exportování motivu. Pokud není žádné pole zaškrtnuto, uloží export motivu všechno.", + "reset": "Resetovat", + "clear_all": "Vymazat vše", + "clear_opacity": "Vymazat průhlednost" + }, + "common": { + "color": "Barva", + "opacity": "Průhlednost", + "contrast": { + "hint": "Poměr kontrastu je {ratio}, {level} {context}", + "level": { + "aa": "splňuje směrnici úrovně AA (minimální)", + "aaa": "splňuje směrnici úrovně AAA (doporučováno)", + "bad": "nesplňuje žádné směrnice přístupnosti" + }, + "context": { + "18pt": "pro velký (18+ bodů) text", + "text": "pro text" + } + } + }, + "common_colors": { + "_tab_label": "Obvyklé", + "main": "Obvyklé barvy", + "foreground_hint": "Pro detailnější kontrolu viz záložka „Pokročilé“", + "rgbo": "Ikony, odstíny, odznaky" + }, + "advanced_colors": { + "_tab_label": "Pokročilé", + "alert": "Pozadí upozornění", + "alert_error": "Chyba", + "badge": "Pozadí odznaků", + "badge_notification": "Oznámení", + "panel_header": "Záhlaví panelu", + "top_bar": "Vrchní pruh", + "borders": "Okraje", + "buttons": "Tlačítka", + "inputs": "Vstupní pole", + "faint_text": "Vybledlý text" + }, + "radii": { + "_tab_label": "Kulatost" + }, + "shadows": { + "_tab_label": "Stín a osvětlení", + "component": "Komponent", + "override": "Přepsat", + "shadow_id": "Stín #{value}", + "blur": "Rozmazání", + "spread": "Rozsah", + "inset": "Vsazení", + "hint": "Pro stíny můžete také použít --variable jako hodnotu barvy pro použití proměnných CSS3. Prosím berte na vědomí, že nastavení průhlednosti v tomto případě nebude fungovat.", + "filter_hint": { + "always_drop_shadow": "Varování, tento stín vždy používá {0}, když to prohlížeč podporuje.", + "drop_shadow_syntax": "{0} nepodporuje parametr {1} a klíčové slovo {2}.", + "avatar_inset": "Prosím berte na vědomí, že kombinování vsazených i nevsazených stínů u avatarů může u průhledných avatarů dát neočekávané výsledky.", + "spread_zero": "Stíny s rozsahem > 0 se zobrazí, jako kdyby byl rozsah nastaven na nulu", + "inset_classic": "Vsazené stíny budou používat {0}" + }, + "components": { + "panel": "Panel", + "panelHeader": "Záhlaví panelu", + "topBar": "Vrchní pruh", + "avatar": "Avatar uživatele (v zobrazení profilu)", + "avatarStatus": "Avatar uživatele (v zobrazení příspěvku)", + "popup": "Vyskakovací okna a popisky", + "button": "Tlačítko", + "buttonHover": "Tlačítko (přejetí myši)", + "buttonPressed": "Tlačítko (stisknuto)", + "buttonPressedHover": "Button (stisknuto+přejetí myši)", + "input": "Vstupní pole" + } + }, + "fonts": { + "_tab_label": "Písma", + "help": "Zvolte písmo, které bude použito pro prvky rozhraní. U možnosti „vlastní“ musíte zadat přesný název písma tak, jak se zobrazuje v systému.", + "components": { + "interface": "Rozhraní", + "input": "Vstupní pole", + "post": "Text příspěvků", + "postCode": "Neproporcionální text v příspěvku (formátovaný text)" + }, + "family": "Název písma", + "size": "Velikost (v pixelech)", + "weight": "Tloušťka", + "custom": "Vlastní" + }, + "preview": { + "header": "Náhled", + "content": "Obsah", + "error": "Příklad chyby", + "button": "Tlačítko", + "text": "Spousta dalšího {0} a {1}", + "mono": "obsahu", + "input": "Just landed in L.A.", + "faint_link": "pomocný manuál", + "fine_print": "Přečtěte si náš {0} a nenaučte se nic užitečného!", + "header_faint": "Tohle je v pohodě", + "checkbox": "Pročetl/a jsem podmínky používání", + "link": "hezký malý odkaz" + } + } + }, + "timeline": { + "collapse": "Zabalit", + "conversation": "Konverzace", + "error_fetching": "Chyba při načítání aktualizací", + "load_older": "Načíst starší příspěvky", + "no_retweet_hint": "Příspěvek je označen jako pouze pro sledující či přímý a nemůže být zopakován", + "repeated": "zopakoval/a", + "show_new": "Zobrazit nové", + "up_to_date": "Aktuální", + "no_more_statuses": "Žádné další příspěvky", + "no_statuses": "Žádné příspěvky" + }, + "status": { + "reply_to": "Odpovědět uživateli", + "replies_list": "Odpovědi:" + }, + + "user_card": { + "approve": "Schválit", + "block": "Blokovat", + "blocked": "Blokován/a!", + "deny": "Zamítnout", + "favorites": "Oblíbené", + "follow": "Sledovat", + "follow_sent": "Požadavek odeslán!", + "follow_progress": "Odeslílám požadavek…", + "follow_again": "Odeslat požadavek znovu?", + "follow_unfollow": "Přestat sledovat", + "followees": "Sledovaní", + "followers": "Sledující", + "following": "Sledujete!", + "follows_you": "Sleduje vás!", + "its_you": "Jste to vy!", + "media": "Média", + "mute": "Ignorovat", + "muted": "Ignorován/a", + "per_day": "za den", + "remote_follow": "Vzdálené sledování", + "statuses": "Příspěvky", + "unblock": "Odblokovat", + "unblock_progress": "Odblokuji…", + "block_progress": "Blokuji…", + "unmute": "Přestat ignorovat", + "unmute_progress": "Ruším ignorování…", + "mute_progress": "Ignoruji…" + }, + "user_profile": { + "timeline_title": "Uživatelská časová osa", + "profile_does_not_exist": "Omlouváme se, tento profil neexistuje.", + "profile_loading_error": "Omlouváme se, při načítání tohoto profilu se vyskytla chyba." + }, + "who_to_follow": { + "more": "Více", + "who_to_follow": "Koho sledovat" + }, + "tool_tip": { + "media_upload": "Nahrát média", + "repeat": "Zopakovat", + "reply": "Odpovědět", + "favorite": "Oblíbit", + "user_settings": "Uživatelské nastavení" + }, + "upload":{ + "error": { + "base": "Nahrávání selhalo.", + "file_too_big": "Soubor je úříliš velký [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", + "default": "Zkuste to znovu později" + }, + "file_size_units": { + "B": "B", + "KiB": "KiB", + "MiB": "MiB", + "GiB": "GiB", + "TiB": "TiB" + } + } +} diff --git a/src/i18n/en.json b/src/i18n/en.json index c5a4a90d..01fe2fba 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -71,7 +71,9 @@ "account_not_locked_warning_link": "locked", "attachments_sensitive": "Mark attachments as sensitive", "content_type": { - "plain_text": "Plain text" + "text/plain": "Plain text", + "text/html": "HTML", + "text/markdown": "Markdown" }, "content_warning": "Subject (optional)", "default": "Just landed in L.A.", @@ -221,7 +223,6 @@ "subject_line_mastodon": "Like mastodon: copy as is", "subject_line_noop": "Do not copy", "post_status_content_type": "Post status content type", - "status_content_type_plain": "Plain text", "stop_gifs": "Play-on-hover GIFs", "streaming": "Enable automatic streaming of new posts when scrolled to the top", "text": "Text", diff --git a/src/i18n/eo.json b/src/i18n/eo.json index 2438b4d5..34851a44 100644 --- a/src/i18n/eo.json +++ b/src/i18n/eo.json @@ -221,7 +221,6 @@ "subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe", "subject_line_noop": "Ne kopii", "post_status_content_type": "Afiŝi specon de la enhavo de la stato", - "status_content_type_plain": "Plata teksto", "stop_gifs": "Movi GIF-bildojn dum musa ŝvebo", "streaming": "Ŝalti memfaran fluigon de novaj afiŝoj ĉe la supro de la paĝo", "text": "Teksto", diff --git a/src/i18n/es.json b/src/i18n/es.json index 167e8c42..fe96dd08 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -202,7 +202,6 @@ "subject_line_mastodon": "Tipo mastodon: copiar como es", "subject_line_noop": "No copiar", "post_status_content_type": "Formato de publicación", - "status_content_type_plain": "Texto plano", "stop_gifs": "Iniciar GIFs al pasar el ratón", "streaming": "Habilite la transmisión automática de nuevas publicaciones cuando se desplaza hacia la parte superior", "text": "Texto", diff --git a/src/i18n/ja.json b/src/i18n/ja.json index b51fa7fd..f39a5a7c 100644 --- a/src/i18n/ja.json +++ b/src/i18n/ja.json @@ -202,7 +202,6 @@ "subject_line_mastodon": "マストドンふう: そのままコピー", "subject_line_noop": "コピーしない", "post_status_content_type": "とうこうのコンテントタイプ", - "status_content_type_plain": "プレーンテキスト", "stop_gifs": "カーソルをかさねたとき、GIFをうごかす", "streaming": "うえまでスクロールしたとき、じどうてきにストリーミングする", "text": "もじ", diff --git a/src/i18n/messages.js b/src/i18n/messages.js index 1adadc32..ab697948 100644 --- a/src/i18n/messages.js +++ b/src/i18n/messages.js @@ -10,6 +10,7 @@ const messages = { ar: require('./ar.json'), ca: require('./ca.json'), + cs: require('./cs.json'), de: require('./de.json'), en: require('./en.json'), eo: require('./eo.json'), diff --git a/src/i18n/oc.json b/src/i18n/oc.json index ef32f83b..fd5ccc97 100644 --- a/src/i18n/oc.json +++ b/src/i18n/oc.json @@ -221,7 +221,6 @@ "subject_line_mastodon": "Coma mastodon : copiar tal coma es", "subject_line_noop": "Copiar pas", "post_status_content_type": "Publicar lo tipe de contengut dels estatuts", - "status_content_type_plain": "Tèxte brut", "stop_gifs": "Lançar los GIFs al subrevòl", "streaming": "Activar lo cargament automatic dels novèls estatus en anar amont", "text": "Tèxt", diff --git a/src/i18n/pt.json b/src/i18n/pt.json index 39ff6c63..cbc2c9a3 100644 --- a/src/i18n/pt.json +++ b/src/i18n/pt.json @@ -221,7 +221,6 @@ "subject_line_mastodon": "Como o Mastodon: copiar como está", "subject_line_noop": "Não copiar", "post_status_content_type": "Postar tipo de conteúdo do status", - "status_content_type_plain": "Texto puro", "stop_gifs": "Reproduzir GIFs ao passar o cursor em cima", "streaming": "Habilitar o fluxo automático de postagens quando ao topo da página", "text": "Texto", diff --git a/src/main.js b/src/main.js index 2844194e..a3265e3a 100644 --- a/src/main.js +++ b/src/main.js @@ -30,8 +30,9 @@ const currentLocale = (window.navigator.language || 'en').split('-')[0] Vue.use(Vuex) Vue.use(VueRouter) Vue.use(VueTimeago, { - locale: currentLocale === 'ja' ? 'ja' : 'en', + locale: currentLocale === 'cs' ? 'cs' : currentLocale === 'ja' ? 'ja' : 'en', locales: { + 'cs': require('../static/timeago-cs.json'), 'en': require('../static/timeago-en.json'), 'ja': require('../static/timeago-ja.json') } diff --git a/src/modules/instance.js b/src/modules/instance.js index c31d02b9..24c52f9c 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -37,6 +37,7 @@ const defaultState = { emoji: [], customEmoji: [], restrictedNicknames: [], + postFormats: [], // Feature-set, apparently, not everything here is reported... mediaProxyAvailable: false, diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 826b544c..7571b62a 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,4 @@ -import { remove, slice, each, find, maxBy, minBy, merge, last, isArray } from 'lodash' +import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray } from 'lodash' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -10,6 +10,7 @@ const emptyTl = (userId = 0) => ({ visibleStatusesObject: {}, newStatusCount: 0, maxId: 0, + minId: 0, minVisibleId: 0, loading: false, followers: [], @@ -18,7 +19,7 @@ const emptyTl = (userId = 0) => ({ flushMarker: 0 }) -export const defaultState = { +export const defaultState = () => ({ allStatuses: [], allStatusesObject: {}, maxId: 0, @@ -29,7 +30,8 @@ export const defaultState = { data: [], idStore: {}, loading: false, - error: false + error: false, + fetcherId: null }, favorites: new Set(), error: false, @@ -44,7 +46,7 @@ export const defaultState = { tag: emptyTl(), dms: emptyTl() } -} +}) export const prepareStatus = (status) => { // Set deleted flag @@ -117,11 +119,16 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us const timelineObject = state.timelines[timeline] const maxNew = statuses.length > 0 ? maxBy(statuses, 'id').id : 0 - const older = timeline && maxNew < timelineObject.maxId + const minNew = statuses.length > 0 ? minBy(statuses, 'id').id : 0 + const newer = timeline && maxNew > timelineObject.maxId && statuses.length > 0 + const older = timeline && (minNew < timelineObject.minId || timelineObject.minId === 0) && statuses.length > 0 - if (timeline && !noIdUpdate && statuses.length > 0 && !older) { + if (!noIdUpdate && newer) { timelineObject.maxId = maxNew } + if (!noIdUpdate && older) { + timelineObject.minId = minNew + } // This makes sure that user timeline won't get data meant for other // user. I.e. opening different user profiles makes request which could @@ -255,12 +262,9 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us processor(status) }) - // Keep the visible statuses sorted + // Keep the visible statuses sorted if (timeline) { sortTimeline(timelineObject) - if ((older || timelineObject.minVisibleId <= 0) && statuses.length > 0) { - timelineObject.minVisibleId = minBy(statuses, 'id').id - } } } @@ -309,9 +313,20 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot }) } +const removeStatus = (state, { timeline, userId }) => { + const timelineObject = state.timelines[timeline] + if (userId) { + remove(timelineObject.statuses, { user: { id: userId } }) + remove(timelineObject.visibleStatuses, { user: { id: userId } }) + timelineObject.minVisibleId = timelineObject.visibleStatuses.length > 0 ? last(timelineObject.visibleStatuses).id : 0 + timelineObject.maxId = timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0 + } +} + export const mutations = { addNewStatuses, addNewNotifications, + removeStatus, showNewStatuses (state, { timeline }) { const oldTimeline = (state.timelines[timeline]) @@ -321,6 +336,15 @@ export const mutations = { oldTimeline.visibleStatusesObject = {} each(oldTimeline.visibleStatuses, (status) => { oldTimeline.visibleStatusesObject[status.id] = status }) }, + setNotificationFetcher (state, { fetcherId }) { + state.notifications.fetcherId = fetcherId + }, + resetStatuses (state) { + const emptyState = defaultState() + Object.entries(emptyState).forEach(([key, value]) => { + state[key] = value + }) + }, clearTimeline (state, { timeline }) { state.timelines[timeline] = emptyTl(state.timelines[timeline].userId) }, @@ -371,7 +395,7 @@ export const mutations = { } const statuses = { - state: defaultState, + state: defaultState(), actions: { addNewStatuses ({ rootState, commit }, { statuses, showImmediately = false, timeline = false, noIdUpdate = false, userId }) { commit('addNewStatuses', { statuses, showImmediately, timeline, noIdUpdate, user: rootState.users.currentUser, userId }) @@ -391,6 +415,12 @@ const statuses = { setNotificationsSilence ({ rootState, commit }, { value }) { commit('setNotificationsSilence', { value }) }, + stopFetchingNotifications ({ rootState, commit }) { + if (rootState.statuses.notifications.fetcherId) { + window.clearInterval(rootState.statuses.notifications.fetcherId) + } + commit('setNotificationFetcher', { fetcherId: null }) + }, deleteStatus ({ rootState, commit }, status) { commit('setDeleted', { status }) apiService.deleteStatus({ id: status.id, credentials: rootState.users.currentUser.credentials }) diff --git a/src/modules/users.js b/src/modules/users.js index 093af497..4159964c 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -295,6 +295,8 @@ const users = { store.commit('setToken', false) store.dispatch('stopFetching', 'friends') store.commit('setBackendInteractor', backendInteractorService()) + store.dispatch('stopFetchingNotifications') + store.commit('resetStatuses') }, loginUser (store, accessToken) { return new Promise((resolve, reject) => { diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 64f8f468..6f99616f 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -21,7 +21,7 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false const timelineData = rootState.statuses.timelines[camelCase(timeline)] if (older) { - args['until'] = until || timelineData.minVisibleId + args['until'] = until || timelineData.minId } else { args['since'] = timelineData.maxId } |
