From 049cea30b0f3900484d45b0d88a0f073ad0c6cc6 Mon Sep 17 00:00:00 2001 From: Naoki Kosaka Date: Fri, 30 Jun 2017 12:37:17 +0900 Subject: Fix media-gallery, overflow is hidden. (#4008) --- app/javascript/styles/components.scss | 1 + 1 file changed, 1 insertion(+) (limited to 'app/javascript') diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 88431fc69..4df4b0685 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -3433,6 +3433,7 @@ button.icon-button.active i.fa-retweet { &, img { width: 100%; + overflow: hidden; } } -- cgit From a27879c0cf89d99fb79e2ffbe7ecfdf72733a1c4 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 30 Jun 2017 12:37:41 +0900 Subject: Replace state to /web when root path (#4009) --- app/javascript/mastodon/main.js | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.js index 02e0f56f9..aca64c075 100644 --- a/app/javascript/mastodon/main.js +++ b/app/javascript/mastodon/main.js @@ -20,6 +20,14 @@ function main() { require.context('../images/', true); + if (window.history && history.replaceState) { + const { pathname, search, hash } = window.location; + const path = pathname + search + hash; + if (!(/^\/web[$/]/).test(path)) { + history.replaceState(null, document.title, `/web${path}`); + } + } + onDomContentLoaded(() => { const mountNode = document.getElementById('mastodon'); const props = JSON.parse(mountNode.getAttribute('data-props')); -- cgit From 0e09048537fee9906ab582bc0704e1a3395c04ed Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 30 Jun 2017 20:40:00 +0900 Subject: Fix broken style in media gallery (regression from #3963) (#4014) --- app/javascript/styles/components.scss | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 4df4b0685..28cb9ec65 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -3421,19 +3421,16 @@ button.icon-button.active i.fa-retweet { } .media-gallery__item-thumbnail { - background-position: center; - background-repeat: no-repeat; - background-size: cover; cursor: zoom-in; - display: flex; - align-items: center; + display: block; text-decoration: none; height: 100%; &, img { width: 100%; - overflow: hidden; + height: 100%; + object-fit: cover; } } -- cgit From a978b88997169782ac35f416bf88d6afd60edd1e Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Fri, 30 Jun 2017 08:29:22 -0700 Subject: Faster emojify() algorithm, avoid regex replace (#4019) * Faster emojify() algorithm, avoid regex replace * add semicolon --- app/javascript/mastodon/emoji.js | 43 ++++++++++++++++++++------ spec/javascript/components/emojify.test.js | 49 ++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 spec/javascript/components/emojify.test.js (limited to 'app/javascript') diff --git a/app/javascript/mastodon/emoji.js b/app/javascript/mastodon/emoji.js index 01d01fb72..d0df71ea3 100644 --- a/app/javascript/mastodon/emoji.js +++ b/app/javascript/mastodon/emoji.js @@ -19,16 +19,41 @@ const unicodeToImage = str => { }); }; -const shortnameToImage = str => str.replace(emojione.regShortNames, shortname => { - if (typeof shortname === 'undefined' || shortname === '' || !(shortname in emojione.emojioneList)) { - return shortname; +const shortnameToImage = str => { + // This walks through the string from end to start, ignoring any tags (

,
, etc.) + // and replacing valid shortnames like :smile: and :wink: that _aren't_ within + // tags with an version. + // The goal is to be the same as an emojione.regShortNames replacement, but faster. + // The reason we go backwards is because then we can replace substrings as we go. + let i = str.length; + let insideTag = false; + let insideShortname = false; + let shortnameEndIndex = -1; + while (i--) { + const char = str.charAt(i); + if (insideShortname && char === ':') { + const shortname = str.substring(i, shortnameEndIndex + 1); + if (shortname in emojione.emojioneList) { + const unicode = emojione.emojioneList[shortname].unicode[emojione.emojioneList[shortname].unicode.length - 1]; + const alt = emojione.convert(unicode.toUpperCase()); + const replacement = `${alt}`; + str = str.substring(0, i) + replacement + str.substring(shortnameEndIndex + 1); + } else { + i++; // stray colon, try again + } + insideShortname = false; + } else if (insideTag && char === '<') { + insideTag = false; + } else if (char === '>') { + insideTag = true; + insideShortname = false; + } else if (!insideTag && char === ':') { + insideShortname = true; + shortnameEndIndex = i; + } } - - const unicode = emojione.emojioneList[shortname].unicode[emojione.emojioneList[shortname].unicode.length - 1]; - const alt = emojione.convert(unicode.toUpperCase()); - - return `${alt}`; -}); + return str; +}; export default function emojify(text) { return toImage(text); diff --git a/spec/javascript/components/emojify.test.js b/spec/javascript/components/emojify.test.js new file mode 100644 index 000000000..7a496623e --- /dev/null +++ b/spec/javascript/components/emojify.test.js @@ -0,0 +1,49 @@ +import { expect } from 'chai'; +import emojify from '../../../app/javascript/mastodon/emoji'; + +describe('emojify', () => { + it('does a basic emojify', () => { + expect(emojify(':smile:')).to.equal( + '😄'); + }); + + it('does a double emojify', () => { + expect(emojify(':smile: and :wink:')).to.equal( + '😄 and 😉'); + }); + + it('works with random colons', () => { + expect(emojify(':smile: : :wink:')).to.equal( + '😄 : 😉'); + expect(emojify(':smile::::wink:')).to.equal( + '😄::😉'); + expect(emojify(':smile:::::wink:')).to.equal( + '😄:::😉'); + }); + + it('works with tags', () => { + expect(emojify('

:smile:

')).to.equal( + '

😄

'); + expect(emojify('

:smile:

and

:wink:

')).to.equal( + '

😄

and

😉

'); + }); + + it('ignores unknown shortcodes', () => { + expect(emojify(':foobarbazfake:')).to.equal(':foobarbazfake:'); + }); + + it('ignores shortcodes inside of tags', () => { + expect(emojify('

')).to.equal('

'); + }); + + it('works with unclosed tags', () => { + expect(emojify('hello>')).to.equal('hello>'); + expect(emojify(' { + expect(emojify('smile:')).to.equal('smile:'); + expect(emojify(':smile')).to.equal(':smile'); + }); + +}); -- cgit From bf50e3e5aefc88f7a6d9ab4aafe5beab4360292b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 1 Jul 2017 14:50:10 +0200 Subject: Fix height issue in report modal --- app/javascript/styles/components.scss | 1 + 1 file changed, 1 insertion(+) (limited to 'app/javascript') diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 28cb9ec65..a87aa5d79 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -3278,6 +3278,7 @@ button.icon-button.active i.fa-retweet { .report-modal__statuses { min-height: 20vh; + max-height: 40vh; overflow-y: auto; overflow-x: hidden; } -- cgit From 60da49f8562b970e514ba4403cac1944229a5768 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sun, 2 Jul 2017 15:55:50 +0200 Subject: fix(components/columns_area): Increase delta for swipe detection (#4037) --- app/javascript/mastodon/features/ui/components/columns_area.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index 3c3e9425d..01167b6e5 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -53,7 +53,7 @@ export default class ColumnsArea extends ImmutablePureComponent { if (singleColumn) { return ( - + {children} ); -- cgit From 133b892e0d95c27de28c22e3d0efe284c75f7700 Mon Sep 17 00:00:00 2001 From: Damien Erambert Date: Sun, 2 Jul 2017 09:36:35 -0700 Subject: Update French locales (#4034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add missing locales for French translation * accent "Media" in the front-end locales * images => médias * Change 'rapport' to 'signalement' in French locales to be more coherent * fix typo * remove duplicate EN locale * translate missing locales * update missing locale * fix typo * unify with "utilisateur⋅ice⋅s" * address PR comments --- app/javascript/mastodon/locales/fr.json | 16 ++--- config/locales/fr.yml | 122 ++++++++++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 15 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 1a69235c8..fd2b30444 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -7,7 +7,7 @@ "account.followers": "Abonné⋅e⋅s", "account.follows": "Abonnements", "account.follows_you": "Vous suit", - "account.media": "Media", + "account.media": "Média", "account.mention": "Mentionner", "account.mute": "Masquer", "account.posts": "Statuts", @@ -61,11 +61,11 @@ "emoji_button.travel": "Lieux et voyages", "empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !", "empty_column.hashtag": "Il n’y a encore aucun contenu relatif à ce hashtag", - "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateurs⋅trices.", + "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateur⋅ice⋅s.", "empty_column.home.inactivity": "Votre accueil est vide. Si vous ne vous êtes pas connecté⋅e depuis un moment, il se remplira automatiquement très bientôt.", "empty_column.home.public_timeline": "le fil public", - "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateurs⋅trices pour débuter la conversation.", - "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateurs⋅trices d’autres instances pour remplir le fil public.", + "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateur⋅ice⋅s pour débuter la conversation.", + "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateur⋅ice⋅s d’autres instances pour remplir le fil public.", "follow_request.authorize": "Autoriser", "follow_request.reject": "Rejeter", "getting_started.appsshort": "Applications", @@ -108,11 +108,11 @@ "notifications.column_settings.sound": "Émettre un son", "onboarding.done": "Effectué", "onboarding.next": "Suivant", - "onboarding.page_five.public_timelines": "Le fil public global affiche les posts de tou⋅te⋅s les utilisateurs⋅trices suivi⋅es par les membres de {domain}. Le fil public local est identique mais se limite aux utilisateurs⋅trices de {domain}.", - "onboarding.page_four.home": "L’Accueil affiche les posts de tou⋅te⋅s les utilisateurs⋅trices que vous suivez", + "onboarding.page_five.public_timelines": "Le fil public global affiche les posts de tou⋅te⋅s les utilisateur⋅ice⋅s suivi⋅es par les membres de {domain}. Le fil public local est identique mais se limite aux utilisateur⋅ice⋅s de {domain}.", + "onboarding.page_four.home": "L’Accueil affiche les posts de tou⋅te⋅s les utilisateur⋅ice⋅s que vous suivez", "onboarding.page_four.notifications": "Les Notifications vous informent lorsque quelqu’un interagit avec vous", "onboarding.page_one.federation": "Mastodon est un réseau social qui appartient à tou⋅te⋅s.", - "onboarding.page_one.handle": "Vous êtes sur {domain}, une des nombreuses instances indépendantes de Mastodon. Votre nom d’utilisateur⋅trice complet est {handle}", + "onboarding.page_one.handle": "Vous êtes sur {domain}, une des nombreuses instances indépendantes de Mastodon. Votre nom d'utilisateur⋅ice complet est {handle}", "onboarding.page_one.welcome": "Bienvenue sur Mastodon !", "onboarding.page_six.admin": "L’administrateur⋅trice de votre instance est {admin}", "onboarding.page_six.almost_done": "Nous y sommes presque…", @@ -123,7 +123,7 @@ "onboarding.page_six.read_guidelines": "S’il vous plaît, n’oubliez pas de lire les {guidelines} !", "onboarding.page_six.various_app": "applications mobiles", "onboarding.page_three.profile": "Modifiez votre profil pour changer votre avatar, votre description ainsi que votre nom. Vous y trouverez également d’autres préférences.", - "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateurs⋅trices et regarder des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son nom d’utilisateur⋅trice complet.", + "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateur⋅ice⋅s et regarder des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son nom d'utilisateur⋅ice complet.", "onboarding.page_two.compose": "Écrivez depuis la colonne de composition. Vous pouvez ajouter des images, changer les réglages de confidentialité, et ajouter des avertissements de contenu (Content Warning) grâce aux icônes en dessous.", "onboarding.skip": "Passer", "privacy.change": "Ajuster la confidentialité du message", diff --git a/config/locales/fr.yml b/config/locales/fr.yml index dfe5ff990..5a3e0c552 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -27,8 +27,9 @@ fr: status_count_after: posts status_count_before: Ayant publié terms: Conditions d’utilisation - user_count_after: utilisateurs⋅trices + user_count_after: utilisateur⋅ice⋅s user_count_before: Abrite + version: Version accounts: follow: Suivre followers: Abonné⋅es @@ -38,10 +39,23 @@ fr: people_who_follow: Personnes qui suivent %{name} posts: Statuts remote_follow: Suivre à distance + reserved_username: Ce nom d'utilisateur⋅ice est réservé unfollow: Ne plus suivre + activitypub: + activity: + announce: + name: "%{account_name} a partagé une activité." + create: + name: "%{account_name} a créé une note." + outbox: + name: "Boîte d'envoi de %{account_name}" + summary: Liste d'activités de %{account_name} admin: accounts: - are_you_sure: Êtes-vous certain ? + are_you_sure: Êtes-vous certain⋅e ? + confirm: Confirmer + confirmed: Confirmé + disable_two_factor_authentication: Désactiver l'authentification à deux facteurs display_name: Nom affiché domain: Domaine edit: Éditer @@ -49,6 +63,7 @@ fr: feed_url: URL du flux followers: Abonné⋅es follows: Abonnements + ip: Adresse IP location: all: Tous local: Local @@ -71,14 +86,24 @@ fr: profile_url: URL du profil public: Public push_subscription_expires: Expiration de l'abonnement PuSH + redownload: Rafraîchir les avatars + reset: Réinitialiser reset_password: Réinitialiser le mot de passe + resubscribe: Se réabonner salmon_url: URL Salmon + search: Rechercher + show: + created_reports: Signalements créés par ce compte + report: signalement + targeted_reports: Signalements créés visant ce compte silence: Rendre muet statuses: Statuts + subscribe: S'abonner title: Comptes undo_silenced: Annuler le silence undo_suspension: Annuler la suspension - username: Nom d'utilisateur + unsubscribe: Se désabonner + username: Nom d'utilisateur⋅ice web: Web domain_blocks: add_new: Ajouter @@ -110,14 +135,24 @@ fr: undo: Annuler title: Blocage de domaines undo: Annuler + instances: + account_count: Comptes connus + domain_name: Domaine + title: Instances connues reports: + action_taken_by: Intervention de + are_you_sure: Êtes vous certain⋅e? comment: label: Commentaire none: Aucun delete: Supprimer id: ID mark_as_resolved: Marquer comme résolu + nsfw: + 'false': Ré-afficher les médias + 'true': Masquer les médias report: 'Signalement #%{id}' + report_contents: Contenu reported_account: Compte signalé reported_by: Signalé par resolved: Résolus @@ -132,7 +167,7 @@ fr: contact_information: email: Entrez une adresse courriel publique label: Informations de contact - username: Entrez un nom d'utilisateur + username: Entrez un nom d'utilisateur⋅ice registrations: closed_message: desc_html: Affiché sur la page d'accueil lorsque les inscriptions sont fermées
Vous pouvez utiliser des balises HTML @@ -158,6 +193,10 @@ fr: title: PubSubHubbub topic: Sujet title: Administration + admin_mailer: + new_report: + body: "%{reporter} a signalé %{target}" + subject: Nouveau signalement sur %{instance} (#%{id}) application_mailer: settings: 'Changer les préférences courriel : %{link}' signature: Notifications de Mastodon depuis %{instance} @@ -166,6 +205,8 @@ fr: invalid_url: L'URL fournie est invalide auth: change_password: Changer de mot de passe + delete_account: Supprimer le compte + delete_account_html: Si vous désirez supprimer votre compte, vous pouvez cliquer ici. Il vous sera demandé de confirmer cette action. didnt_get_confirmation: Vous n’avez pas reçu les consignes de confirmation ? forgot_password: Mot de passe oublié ? login: Se connecter @@ -199,18 +240,41 @@ fr: x_minutes: "%{count}min" x_months: "%{count}mois" x_seconds: "%{count}s" + deletes: + bad_password_msg: Bien essayé ! Mot de passe incorrect + confirm_password: Entrez votre mot de passe pour vérifier votre identité + description_html: Cela va supprimer votre compte et le désactiver de manière permanente et irréversible. Votre nom d'utilisateur⋅ice restera réservé afin d'éviter la confusion + proceed: Supprimer compte + success_msg: Votre compte a été supprimé avec succès + warning_html: Seule la suppression du contenu depuis cette instance est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les serveurs hors-lignes ainsi que ceux n'étant plus abonnés à vos publications ne mettront pas leur base de données à jour. + warning_title: Disponibilité du contenu disséminé errors: + '403': Vous n'avez pas accès à cette page. '404': La page que vous recherchez n'existe pas. '410': La page que vous recherchez n'existe plus. '422': content: Vérification de sécurité échouée. Bloquez-vous les cookies ? title: Vérification de sécurité échouée + '429': Trop de requêtes émises dans un délai donné. + noscript: Pour utiliser Mastodon, veuillez activer JavaScript exports: blocks: Vous bloquez csv: CSV follows: Vous suivez mutes: Vous faites taire storage: Médias stockés + followers: + domain: Domaine + explanation_html: Si vous voulez être sûr⋅e que vos status restent privés, vous devez savoir qui vous suit. Vos status privés seront diffusés à toutes les instances des utilisateur⋅ice⋅s qui vous suivent. Vous voudrez peut-être les passer en revue et les supprimer si vous n'êtes pas sûr⋅e que votre vie privée sera respectée par l'administration ou le logiciel de ces instances. + followers_count: Nombre d'abonné⋅es + lock_link: Rendez votre compte privé + purge: Retirer de la liste d'abonné⋅es + success: + one: Suppression des abonné⋅es venant d'un domaine en cours... + other: Suppression des abonné⋅es venant de %{count} domaines en cours... + true_privacy_html: Soyez conscient⋅es qu'une vraie confidentialité ne peut être atteinte que par un chiffrement de bout-en-bout. + unlocked_warning_html: N'importe qui peut vous suivre et voir vos status privés. %{lock_link} afin de pouvoir vérifier et rejeter des abonné⋅es. + unlocked_warning_title: Votre compte n'est pas privé generic: changes_saved_msg: Les modifications ont été enregistrées avec succès ! powered_by: propulsé par %{link} @@ -222,9 +286,9 @@ fr: preface: Vous pouvez importer certaines données comme les personnes que vous suivez ou bloquez sur votre compte sur cette instance à partir de fichiers créés sur une autre instance. success: Vos données ont été importées avec succès et seront traitées en temps et en heure types: - blocking: Liste d'utilisateurs⋅trices bloqué⋅es - following: Liste d'utilisateurs⋅trices suivi⋅es - muting: Liste d'utilisateurs⋅trices que vous faites taire + blocking: Liste d'utilisateur⋅ice⋅s bloqué⋅es + following: Liste d'utilisateur⋅ice⋅s suivi⋅es + muting: Liste d'utilisateur⋅ice⋅s que vous faites taire upload: Importer landing_strip_html: %{name} utilise %{link_to_root_path}. Vous pouvez le/la suivre et interagir si vous possédez un compte quelque part dans le "fediverse". landing_strip_signup_html: Si ce n'est pas le cas, vous pouvez en créer un ici. @@ -265,11 +329,50 @@ fr: missing_resource: L'URL de redirection n'a pas pu être trouvée proceed: Continuez pour suivre prompt: 'Vous allez suivre :' + sessions: + activity: Dernière activité + browser: Navigateur + browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + firefox: Firefox + generic: Navigateur inconnu + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: Session courante + description: "%{browser} sur %{platform}" + explanation: Ceci est la liste des navigateurs actuellement connectés à votre compte Mastodon. + ip: Adresse IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: système inconnu + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + title: Sessions settings: authorized_apps: Applications autorisées back: Retour vers Mastodon + delete: Suppression de compte edit_profile: Modifier le profil export: Export de données + followers: Abonné⋅es autorisé⋅es import: Import de données preferences: Préférences settings: Réglages @@ -280,8 +383,11 @@ fr: show_more: Afficher plus visibilities: private: Abonné⋅es uniquement + private_long: Seul⋅es vos abonné⋅es verront vos status public: Public + public_long: Tout le monde peut voir vos status unlisted: Public sans être affiché sur le fil public + unlisted_long: Tout le monde peut voir vos status mais ils ne seront pas sur listés sur les fils publics stream_entries: click_to_show: Cliquer pour afficher reblogged: partagé @@ -294,11 +400,13 @@ fr: description_html: Si vous activez l'identification à deux facteurs, vous devrez être en possession de votre téléphone afin de générer un code de connexion. disable: Désactiver enable: Activer + enabled: L'authentification à deux facteurs est activée enabled_success: Identification à deux facteurs activée avec succès generate_recovery_codes: Générer les codes de récupération instructions_html: "Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone. Désormais, cette application générera des jetons que vous devrez saisir à chaque connexion." lost_recovery_codes: Les codes de récupération vous permettent de retrouver les accès à votre comptre si vous perdez votre téléphone. Si vous perdez vos codes de récupération, vous pouvez les générer à nouveau ici. Vos anciens codes de récupération seront invalidés. manual_instructions: 'Si vous ne pouvez pas scanner ce QR code et devez l''entrer manuellement, voici le secret en clair :' + recovery_codes: Codes de récupération recovery_codes_regenerated: Codes de récupération régénérés avec succès recovery_instructions_html: Si vous perdez l'accès à votre téléphone, vous pouvez utiliser un des codes de récupération ci-dessous pour récupérer l'accès à votre compte. Conservez les codes de récupération en toute sécurité, par exemple, en les imprimant et en les stockant avec vos autres documents importants. setup: Installer -- cgit From e28258010182b56f27cfbd3f9f9a58fd9cd8870d Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Mon, 3 Jul 2017 02:02:36 -0700 Subject: Faster emojify() by avoiding str.replace() entirely (#4049) --- app/javascript/mastodon/emoji.js | 69 ++++++++++++++---------------- package.json | 1 + spec/javascript/components/emojify.test.js | 34 +++++++++++++++ yarn.lock | 4 ++ 4 files changed, 71 insertions(+), 37 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/emoji.js b/app/javascript/mastodon/emoji.js index d0df71ea3..7043d5f3a 100644 --- a/app/javascript/mastodon/emoji.js +++ b/app/javascript/mastodon/emoji.js @@ -1,60 +1,55 @@ import emojione from 'emojione'; +import Trie from 'substring-trie'; -const toImage = str => shortnameToImage(unicodeToImage(str)); +const mappedUnicode = emojione.mapUnicodeToShort(); +const trie = new Trie(Object.keys(emojione.jsEscapeMap)); -const unicodeToImage = str => { - const mappedUnicode = emojione.mapUnicodeToShort(); - - return str.replace(emojione.regUnicode, unicodeChar => { - if (typeof unicodeChar === 'undefined' || unicodeChar === '' || !(unicodeChar in emojione.jsEscapeMap)) { - return unicodeChar; - } - - const unicode = emojione.jsEscapeMap[unicodeChar]; - const short = mappedUnicode[unicode]; - const filename = emojione.emojioneList[short].fname; - const alt = emojione.convert(unicode.toUpperCase()); - - return `${alt}`; - }); -}; - -const shortnameToImage = str => { - // This walks through the string from end to start, ignoring any tags (

,
, etc.) - // and replacing valid shortnames like :smile: and :wink: that _aren't_ within - // tags with an version. - // The goal is to be the same as an emojione.regShortNames replacement, but faster. - // The reason we go backwards is because then we can replace substrings as we go. - let i = str.length; +function emojify(str) { + // This walks through the string from start to end, ignoring any tags (

,
, etc.) + // and replacing valid shortnames like :smile: and :wink: as well as unicode strings + // that _aren't_ within tags with an version. + // The goal is to be the same as an emojione.regShortNames/regUnicode replacement, but faster. + let i = -1; let insideTag = false; let insideShortname = false; - let shortnameEndIndex = -1; - while (i--) { + let shortnameStartIndex = -1; + let match; + while (++i < str.length) { const char = str.charAt(i); if (insideShortname && char === ':') { - const shortname = str.substring(i, shortnameEndIndex + 1); + const shortname = str.substring(shortnameStartIndex, i + 1); if (shortname in emojione.emojioneList) { const unicode = emojione.emojioneList[shortname].unicode[emojione.emojioneList[shortname].unicode.length - 1]; const alt = emojione.convert(unicode.toUpperCase()); const replacement = `${alt}`; - str = str.substring(0, i) + replacement + str.substring(shortnameEndIndex + 1); + str = str.substring(0, shortnameStartIndex) + replacement + str.substring(i + 1); + i += (replacement.length - shortname.length - 1); // jump ahead the length we've added to the string } else { - i++; // stray colon, try again + i--; // stray colon, try again } insideShortname = false; - } else if (insideTag && char === '<') { + } else if (insideTag && char === '>') { insideTag = false; - } else if (char === '>') { + } else if (char === '<') { insideTag = true; insideShortname = false; } else if (!insideTag && char === ':') { insideShortname = true; - shortnameEndIndex = i; + shortnameStartIndex = i; + } else if (!insideTag && (match = trie.search(str.substring(i)))) { + const unicodeStr = match; + if (unicodeStr in emojione.jsEscapeMap) { + const unicode = emojione.jsEscapeMap[unicodeStr]; + const short = mappedUnicode[unicode]; + const filename = emojione.emojioneList[short].fname; + const alt = emojione.convert(unicode.toUpperCase()); + const replacement = `${alt}`; + str = str.substring(0, i) + replacement + str.substring(i + unicodeStr.length); + i += (replacement.length - unicodeStr.length); // jump ahead the length we've added to the string + } } } return str; -}; +} -export default function emojify(text) { - return toImage(text); -}; +export default emojify; diff --git a/package.json b/package.json index 7fa80a0c2..d5c05dae3 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "sass-loader": "^6.0.6", "stringz": "^0.2.2", "style-loader": "^0.18.2", + "substring-trie": "^1.0.0", "throng": "^4.0.0", "tiny-queue": "^0.2.1", "uuid": "^3.1.0", diff --git a/spec/javascript/components/emojify.test.js b/spec/javascript/components/emojify.test.js index 7a496623e..3e8b25af9 100644 --- a/spec/javascript/components/emojify.test.js +++ b/spec/javascript/components/emojify.test.js @@ -46,4 +46,38 @@ describe('emojify', () => { expect(emojify(':smile')).to.equal(':smile'); }); + it('does two emoji next to each other', () => { + expect(emojify(':smile::wink:')).to.equal( + '😄😉'); + }); + + it('does unicode', () => { + expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).to.equal( + '👩‍👩‍👦‍👦'); + expect(emojify('\uD83D\uDC68\uD83D\uDC69\uD83D\uDC67\uD83D\uDC67')).to.equal( + '👨👩👧👧'); + expect(emojify('\uD83D\uDC69\uD83D\uDC69\uD83D\uDC66')).to.equal('👩👩👦'); + expect(emojify('\u2757')).to.equal( + '❗'); + }); + + it('does multiple unicode', () => { + expect(emojify('\u2757 #\uFE0F\u20E3')).to.equal( + '❗ #️⃣'); + expect(emojify('\u2757#\uFE0F\u20E3')).to.equal( + '❗#️⃣'); + expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).to.equal( + '❗ #️⃣ ❗'); + expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).to.equal( + 'foo ❗ #️⃣ bar'); + }); + + it('does mixed unicode and shortnames', () => { + expect(emojify(':smile:#\uFE0F\u20E3:wink:\u2757')).to.equal('😄#️⃣😉❗'); + }); + + it('ignores unicode inside of tags', () => { + expect(emojify('

')).to.equal('

'); + }); + }); diff --git a/yarn.lock b/yarn.lock index adabca08d..609f256c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6819,6 +6819,10 @@ style-loader@^0.18.2: loader-utils "^1.0.2" schema-utils "^0.3.0" +substring-trie@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/substring-trie/-/substring-trie-1.0.0.tgz#5a7ecb83aefcca7b3720f7897cf69e97023be143" + sugarss@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-1.0.0.tgz#65e51b3958432fb70d5451a68bb33e32d0cf1ef7" -- cgit From 4cddef1cea3b7c44c5c951b2cd6a118edd0332c5 Mon Sep 17 00:00:00 2001 From: Gyuhwan Park Date: Tue, 4 Jul 2017 23:11:23 +0900 Subject: i18n: Add korean translation (#4064) * Added Korean Translation (based on japanese) * Update korean translation * Update korean translation: fix syntax error * Updated korean translation * Update korean translation * Update ko.json Translate non-translated parts * Update ko.yml Translated missed parts - and fixed some typos * Create simple_form.ko.yml * Updated korean translation * i18n: fix test fails --- app/helpers/settings_helper.rb | 1 + app/javascript/mastodon/locales/ko.json | 176 +++++++++ app/javascript/mastodon/locales/whitelist_ko.json | 2 + config/application.rb | 1 + config/locales/ko.yml | 411 ++++++++++++++++++++++ config/locales/simple_form.ko.yml | 58 +++ 6 files changed, 649 insertions(+) create mode 100644 app/javascript/mastodon/locales/ko.json create mode 100644 app/javascript/mastodon/locales/whitelist_ko.json create mode 100644 config/locales/ko.yml create mode 100644 config/locales/simple_form.ko.yml (limited to 'app/javascript') diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 847eff2e7..af950aa63 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -19,6 +19,7 @@ module SettingsHelper io: 'Ido', it: 'Italiano', ja: '日本語', + ko: '한국어', nl: 'Nederlands', no: 'Norsk', oc: 'Occitan', diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json new file mode 100644 index 000000000..0a2d76d3a --- /dev/null +++ b/app/javascript/mastodon/locales/ko.json @@ -0,0 +1,176 @@ +{ + "account.block": "차단", + "account.block_domain": "{domain} 전체를 숨김", + "account.disclaimer": "이 사용자는 다른 인스턴스에 소속되어 있으므로, 수치가 정확하지 않을 수도 있습니다.", + "account.edit_profile": "프로필 편집", + "account.follow": "팔로우", + "account.followers": "팔로워", + "account.follows": "팔로우", + "account.follows_you": "날 팔로우합니다", + "account.media": "미디어", + "account.mention": "답장", + "account.mute": "뮤트", + "account.posts": "포스트", + "account.report": "신고", + "account.requested": "승인 대기 중", + "account.unblock": "차단 해제", + "account.unblock_domain": "{domain} 숨김 해제", + "account.unfollow": "팔로우 해제", + "account.unmute": "뮤트 해제", + "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.", + "column.blocks": "차단 중인 사용자", + "column.community": "로컬 타임라인", + "column.favourites": "즐겨찾기", + "column.follow_requests": "팔로우 요청", + "column.home": "홈", + "column.mutes": "뮤트 중인 사용자", + "column.notifications": "알림", + "column.public": "연합 타임라인", + "column_back_button.label": "돌아가기", + "column_header.pin": "고정하기", + "column_header.unpin": "고정 해제", + "column_subheading.navigation": "내비게이션", + "column_subheading.settings": "설정", + "compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.", + "compose_form.lock_disclaimer.lock": "비공개", + "compose_form.placeholder": "지금 무엇을 하고 있나요?", + "compose_form.privacy_disclaimer": "이 계정의 비공개 포스트는 멘션된 사용자가 소속된 {domains}으로 전송됩니다. {domainsCount, plural, one {이 서버를} other {이 서버들을}} 신뢰할 수 있습니까? 포스팅의 프라이버시 보호는 Mastodon 서버에서만 유효합니다. {domains}가 Mastodon 인스턴스가 아닐 경우, 이 투고가 사적인 것으로 취급되지 않은 채 부스트 되거나 원하지 않는 사용자에게 보여질 가능성이 있습니다.", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive": "이 미디어를 민감한 미디어로 취급", + "compose_form.spoiler": "텍스트 숨기기", + "compose_form.spoiler_placeholder": "경고", + "confirmation_modal.cancel": "취소", + "confirmations.block.confirm": "차단", + "confirmations.block.message": "정말로 {name}를 차단하시겠습니까?", + "confirmations.delete.confirm": "삭제", + "confirmations.delete.message": "정말로 삭제하시겠습니까?", + "confirmations.domain_block.confirm": "도메인 전체를 숨김", + "confirmations.domain_block.message": "정말로 {domain} 전체를 숨기시겠습니까? 대부분의 경우 개별 차단이나 뮤트로 충분합니다.", + "confirmations.mute.confirm": "뮤트", + "confirmations.mute.message": "정말로 {name}를 뮤트하시겠습니까?", + "emoji_button.activity": "활동", + "emoji_button.flags": "국기", + "emoji_button.food": "음식", + "emoji_button.label": "emoji를 추가", + "emoji_button.nature": "자연", + "emoji_button.objects": "물건", + "emoji_button.people": "사람들", + "emoji_button.search": "검색...", + "emoji_button.symbols": "기호", + "emoji_button.travel": "여행과 장소", + "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!", + "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.", + "empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.", + "empty_column.home.inactivity": "홈 피드에 아무 것도 없습니다. 한동안 활동하지 않은 경우 곧 원래대로 돌아올 것입니다.", + "empty_column.home.public_timeline": "연합 타임라인", + "empty_column.notifications": "아직 알림이 없습니다. 다른 사람과 대화를 시작해 보세요!", + "empty_column.public": "여기엔 아직 아무 것도 없습니다! 공개적으로 무언가 포스팅하거나, 다른 인스턴스 유저를 팔로우 해서 가득 채워보세요!", + "follow_request.authorize": "허가", + "follow_request.reject": "거부", + "getting_started.appsshort": "어플리케이션", + "getting_started.faq": "자주 있는 질문", + "getting_started.heading": "시작", + "getting_started.open_source_notice": "Mastodon은 오픈 소스 소프트웨어입니다. 누구나 GitHub({github})에서 개발에 참여하거나, 문제를 보고할 수 있습니다.", + "getting_started.userguide": "사용자 가이드", + "home.column_settings.advanced": "고급 사용자용", + "home.column_settings.basic": "기본 설정", + "home.column_settings.filter_regex": "정규 표현식으로 필터링", + "home.column_settings.show_reblogs": "부스트 표시", + "home.column_settings.show_replies": "답글 표시", + "home.settings": "컬럼 설정", + "lightbox.close": "닫기", + "loading_indicator.label": "불러오는 중...", + "media_gallery.toggle_visible": "표시 전환", + "missing_indicator.label": "찾을 수 없습니다", + "navigation_bar.blocks": "차단한 사용자", + "navigation_bar.community_timeline": "로컬 타임라인", + "navigation_bar.edit_profile": "프로필 편집", + "navigation_bar.favourites": "즐겨찾기", + "navigation_bar.follow_requests": "팔로우 요청", + "navigation_bar.info": "이 인스턴스에 대해서", + "navigation_bar.logout": "로그아웃", + "navigation_bar.mutes": "뮤트 중인 사용자", + "navigation_bar.preferences": "사용자 설정", + "navigation_bar.public_timeline": "연합 타임라인", + "notification.favourite": "{name}님이 즐겨찾기 했습니다", + "notification.follow": "{name}님이 나를 팔로우 했습니다", + "notification.mention": "{name}님이 답글을 보냈습니다", + "notification.reblog": "{name}님이 부스트 했습니다", + "notifications.clear": "알림 지우기", + "notifications.clear_confirmation": "정말로 알림을 삭제하시겠습니까?", + "notifications.column_settings.alert": "데스크탑 알림", + "notifications.column_settings.favourite": "즐겨찾기", + "notifications.column_settings.follow": "새 팔로워", + "notifications.column_settings.mention": "답글", + "notifications.column_settings.reblog": "부스트", + "notifications.column_settings.show": "컬럼에 표시", + "notifications.column_settings.sound": "효과음 재생", + "onboarding.done": "완료", + "onboarding.next": "다음", + "onboarding.page_five.public_timelines": "연합 타임라인에서는 {domain}의 사람들이 팔로우 중인 Mastodon 전체 인스턴스의 공개 포스트를 표시합니다. 로컬 타임라인에서는 {domain} 만의 공개 포스트를 표시합니다.", + "onboarding.page_four.home": "홈 타임라인에서는 내가 팔로우 중인 사람들의 포스트를 표시합니다.", + "onboarding.page_four.notifications": "알림에서는 다른 사람들과의 연결을 표시합니다.", + "onboarding.page_one.federation": "Mastodon은 누구나 참가할 수 있는 SNS입니다.", + "onboarding.page_one.handle": "여러분은 지금 수많은 Mastodon 인스턴스 중 하나인 {domain}에 있습니다. あなたのフルハンドルは{handle}です。", + "onboarding.page_one.welcome": "Mastodon에 어서 오세요!", + "onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.", + "onboarding.page_six.almost_done": "이상입니다.", + "onboarding.page_six.appetoot": "Bon Appetoot!", + "onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.", + "onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.", + "onboarding.page_six.guidelines": "커뮤니티 가이드라인", + "onboarding.page_six.read_guidelines": "{guidelines}을 확인하는 것을 잊지 마세요.", + "onboarding.page_six.various_app": "다양한 모바일 어플리케이션", + "onboarding.page_three.profile": "[프로필 편집] 에서 자기 소개나 이름을 변경할 수 있습니다. 또한 다른 설정도 변경할 수 있습니다.", + "onboarding.page_three.search": "검색 바에서 {illustration} 나 {introductions} 와 같이 특정 해시태그가 달린 포스트를 보거나, 사용자를 찾을 수 있습니다.", + "onboarding.page_two.compose": "이 폼에서 포스팅 할 수 있습니다. 이미지나 공개 범위 설정, 스포일러 경고 설정은 아래 아이콘으로 설정할 수 있습니다.", + "onboarding.skip": "건너뛰기", + "privacy.change": "포스트의 프라이버시 설정을 변경", + "privacy.direct.long": "멘션한 사용자에게만 공개", + "privacy.direct.short": "다이렉트", + "privacy.private.long": "팔로워에게만 공개", + "privacy.private.short": "비공개", + "privacy.public.long": "공개 타임라인에 표시", + "privacy.public.short": "공개", + "privacy.unlisted.long": "공개 타임라인에 표시하지 않음", + "privacy.unlisted.short": "Unlisted", + "reply_indicator.cancel": "취소", + "report.heading": "신고", + "report.placeholder": "코멘트", + "report.submit": "신고하기", + "report.target": "문제가 된 사용자", + "search.placeholder": "검색", + "search_results.total": "{count, number}건의 결과", + "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", + "status.delete": "삭제", + "status.favourite": "즐겨찾기", + "status.load_more": "더 보기", + "status.media_hidden": "미디어 숨겨짐", + "status.mention": "답장", + "status.mute_conversation": "이 대화를 뮤트", + "status.open": "상세 정보 표시", + "status.reblog": "부스트", + "status.reblogged_by": "{name}님이 부스트 했습니다", + "status.reply": "답장", + "status.replyAll": "전원에게 답장", + "status.report": "신고", + "status.sensitive_toggle": "클릭해서 표시하기", + "status.sensitive_warning": "민감한 미디어", + "status.show_less": "숨기기", + "status.show_more": "더 보기", + "status.unmute_conversation": "이 대화의 뮤트 해제하기", + "tabs_bar.compose": "포스트", + "tabs_bar.federated_timeline": "연합", + "tabs_bar.home": "홈", + "tabs_bar.local_timeline": "로컬", + "tabs_bar.notifications": "알림", + "upload_area.title": "드래그 & 드롭으로 업로드", + "upload_button.label": "미디어 추가", + "upload_form.undo": "재시도", + "upload_progress.label": "업로드 중...", + "video_player.expand": "동영상 자세히 보기", + "video_player.toggle_sound": "소리 토글하기", + "video_player.toggle_visible": "표시 전환", + "video_player.video_error": "동영상 재생에 실패했습니다" +} diff --git a/app/javascript/mastodon/locales/whitelist_ko.json b/app/javascript/mastodon/locales/whitelist_ko.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_ko.json @@ -0,0 +1,2 @@ +[ +] diff --git a/config/application.rb b/config/application.rb index 6b121009e..6bd47cd6c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -45,6 +45,7 @@ module Mastodon :io, :it, :ja, + :ko, :nl, :no, :oc, diff --git a/config/locales/ko.yml b/config/locales/ko.yml new file mode 100644 index 000000000..7f238ab73 --- /dev/null +++ b/config/locales/ko.yml @@ -0,0 +1,411 @@ +--- +ko: + about: + about_mastodon: Mastodon 은자유로운 오픈 소스소셜 네트워크입니다. 상용 플랫폼의 대체로써 분산형 구조를 채택해, 여러분의 대화가 한 회사에 독점되는 것을 방지합니다. 신뢰할 수 있는 인스턴스를 선택하세요 — 어떤 인스턴스를 고르더라도, 누구와도 대화할 수 있습니다. 누구나 자신만의 Mastodon 인스턴스를 만들 수 있으며, Seamless하게 소셜 네트워크에 참가할 수 있습니다. + about_this: 이 인스턴스에 대해서 + apps: 어플리케이션 + business_email: '비즈니스 메일 주소:' + closed_registrations: 현재 이 인스턴스에서는 신규 등록을 받고 있지 않습니다. + contact: 연락처 + description_headline: "%{domain} 는 무엇인가요?" + domain_count_after: 개의 인스턴스 + domain_count_before: 연결됨 + features: + api: 어플리케이션이나 그 외 서비스에 API를 공개 + blocks: 강력한 차단 및 뮤트 기능 + characters: 한번에 500자까지 포스팅 가능 + chronology: 시간 순서의 타임라인 + ethics: 광고도 트래킹도 없습니다 + gifv: GIFV나 짧은 동영상도 지원 + privacy: 각 포스팅마다 공개 범위를 상세히 설정 가능 + public: 공개 타임라인 + features_headline: Mastodon 의 특징 + get_started: 참가하기 + links: 링크 + other_instances: 다른 인스턴스 + source_code: 소스 코드 + status_count_after: Toot + status_count_before: Toot 수 + terms: 개인 정보 보호 정책 + user_count_after: 명 + user_count_before: 사용자 수 + version: 버전 + accounts: + follow: 팔로우 + followers: 팔로워 + following: 팔로잉 + nothing_here: 아무 것도 없습니다. + people_followed_by: "%{name} 님이 팔로우 중인 계정" + people_who_follow: "%{name} 님을 팔로우 중인 계정" + posts: 포스트 + remote_follow: 리모트 팔로우 + reserved_username: 이 사용자 명은 예약되어 있습니다. + unfollow: 팔로우 해제 + activitypub: + activity: + announce: + name: "%{account_name} 님이 액티비티를 공유했습니다" + create: + name: "%{account_name} 님이 노트를 작성했습니다" + outbox: + name: "%{account_name} 님의 송신함" + summary: "%{account_name} 님의 액티비티 모음" + admin: + accounts: + are_you_sure: 정말로 실행하시겠습니까? + confirm: 확인 + confirmed: 확인됨 + disable_two_factor_authentication: 2단계 인증을 비활성화 + display_name: 계정명 + domain: 도메인 + edit: 편집 + email: E-mail + feed_url: 피드 URL + followers: 팔로워 수 + follows: 팔로잉 수 + ip: IP + location: + all: 전체 + local: 로컬 + remote: 리모트 + title: 위치 + media_attachments: 첨부된 미디어 + moderation: + all: 전체 + silenced: 침묵 중 + suspended: 정지 중 + title: 모더레이션 + most_recent_activity: 최근 활동 + most_recent_ip: 최근 IP + not_subscribed: 구독하지 않음 + order: + alphabetic: 알파벳 순 + most_recent: 최근 활동 순 + title: 순서 + perform_full_suspension: 완전히 정지시키기 + profile_url: 프로필 URL + public: 전체 공개 + push_subscription_expires: PuSH 구독 기간 만료 + redownload: 아바타 업데이트 + reset: 초기화 + reset_password: 비밀번호 초기화 + resubscribe: 다시 구독 + salmon_url: Salmon URL + search: 검색 + show: + created_reports: 이 계정에서 제출된 신고 + report: 신고 + targeted_reports: 이 계정에 대한 신고 + silence: 침묵 + statuses: Toot 수 + subscribe: 구독하기 + title: 계정 + undo_silenced: 침묵 해제 + undo_suspension: 정지 해제 + unsubscribe: 구독 해제 + username: 사용자명 + web: Web + domain_blocks: + add_new: 추가하기 + created_msg: 도메인 차단 처리를 완료했습니다. + destroyed_msg: 도메인 차단을 해제했습니다. + domain: 도메인 + new: + create: 차단 추가 + hint: 도메인 차단은 내부 데이터베이스에 계정이 생성되는 것까지는 막을 수 없지만, 그 도메인에서 생성된 계정에 자동적으로 특정한 모더레이션을 적용하게 할 수 있습니다. + severity: + desc_html: "침묵은 계정을 팔로우 하지 않고 있는 사람들에겐 계정의 Toot을 보이지 않게 합니다. 정지는 계정의 컨텐츠, 미디어, 프로필 데이터를 삭제합니다." + silence: 침묵 + suspend: 정지 + title: 새로운 도메인 차단 + reject_media: 미디어 파일 거부하기 + reject_media_hint: 로컬에 저장된 미디어 파일을 삭제하고, 이후로도 다운로드를 거부합니다. 정지하고는 관계 없습니다. + severities: + silence: 침묵 + suspend: 정지 + severity: 심각도 + show: + affected_accounts: + one: 데이터베이스 중 1개의 계정에 영향을 끼칩니다 + other: 데이터베이스 중 %{count}개의 계정에 영향을 끼칩니다 + retroactive: + silence: 이 도메인에 존재하는 모든 계정의 침묵를 해제 + suspend: 이 도메인에 존재하는 모든 계정의 계정 정지를 해제 + title: "%{domain}의 도메인 차단을 해제" + undo: 실행 취소 + title: 도메인 차단 + undo: 실행 취소 + instances: + account_count: 알려진 계정의 수 + domain_name: 도메인 이름 + title: 알려진 인스턴스들 + reports: + action_taken_by: 신고 처리자 + are_you_sure: 정말로 실행하시겠습니까? + comment: + label: 코멘트 + none: 없음 + delete: 삭제 + id: ID + mark_as_resolved: 해결 완료 처리 + nsfw: + 'false': NSFW 꺼짐 + 'true': NSFW 켜짐 + report: "신고 #%{id}" + report_contents: 내용 + reported_account: 신고 대상 계정 + reported_by: 신고자 + resolved: 해결됨 + silence_account: 계정을 침묵 처리 + status: 상태 + suspend_account: 계정을 정지 + target: 대상 + title: 신고 + unresolved: 미해결 + view: 표시 + settings: + contact_information: + email: 공개할 메일 주소를 입력 + label: 연락처 정보 + username: 사용자명을 입력 + registrations: + closed_message: + desc_html: 신규 등록을 받지 않을 때 프론트 페이지에 표시됩니다.
HTML 태그를 사용할 수 있습니다. + title: 신규 등록 정지 시 메시지 + open: + disabled: 꺼짐 + enabled: 켜짐 + title: 신규 등록을 받음 + setting: 설정 + site_description: + desc_html: 탑 페이지와 meta 태그에 사용됩니다.
HTML 태그, 예를 들어<a> 태그와 <em> 태그를 사용할 수 있습니다. + title: 사이트 설명 + site_description_extended: + desc_html: 인스턴스 정보 페이지에 표시됩니다.
HTML 태그를 사용할 수 있습니다. + title: 사이트 상세 설명 + site_title: 사이트 이름 + title: 사이트 설정 + subscriptions: + callback_url: 콜백 URL + confirmed: 확인됨 + expires_in: 기한 + last_delivery: 최종 발송 + title: PubSubHubbub + topic: 토픽 + title: 관리 + admin_mailer: + new_report: + body: "%{reporter} 가 %{target} 를 신고했습니다" + subject: "%{instance} 에 새 신고 등록됨 (#%{id})" + application_mailer: + settings: '메일 설정을 변경: %{link}' + signature: Mastodon %{instance} 인스턴스로에서 알림 + view: 'View:' + applications: + invalid_url: 올바르지 않은 URL입니다 + auth: + change_password: 보안 + delete_account: 계정 삭제 + delete_account_html: 계정을 삭제하고 싶은 경우, 여기서 삭제할 수 있습니다. 삭제 전 확인 화면이 표시됩니다. + didnt_get_confirmation: 확인 메일을 받지 못하셨습니까? + forgot_password: 비밀번호를 잊어버리셨습니까? + login: 로그인 + logout: 로그아웃 + register: 등록하기 + resend_confirmation: 확인 메일을 다시 보내기 + reset_password: 비밀번호 재설정 + set_new_password: 새 비밀번호 + authorize_follow: + error: 리모트 팔로우 도중 오류가 발생했습니다. + follow: 팔로우 + prompt_html: '나(%{self}) 는 아래 계정의 팔로우를 요청했습니다:' + title: "%{acct} 를 팔로우" + datetime: + distance_in_words: + about_x_hours: "%{count}시간" + about_x_months: "%{count}월" + about_x_years: "%{count}년" + almost_x_years: "%{count}년" + half_a_minute: 지금 + less_than_x_minutes: "%{count}분" + less_than_x_seconds: 지금 + over_x_years: "%{count}년" + x_days: "%{count}일" + x_minutes: "%{count}분" + x_months: "%{count}월" + x_seconds: "%{count}초" + deletes: + bad_password_msg: 비밀번호가 올바르지 않습니다 + confirm_password: 본인 확인을 위해, 현재 사용 중인 비밀번호를 입력해 주십시오. + description_html: 계정에 업로드된 모든 컨텐츠가 삭제되며, 계정은 비활성화 됩니다. 이것은 영구적으로 이루어지는 것이므로 되돌릴 수 없습니다. 사칭 행위를 방지하기 위해 같은 계정명으로 다시 등록하는 것은 불가능합니다. + proceed: 계정 삭제 + success_msg: 계정이 정상적으로 삭제되었습니다. + warning_html: 삭제가 보장되는 것은 이 인스턴스 상에서의 컨텐츠에 한합니다. 타 인스턴스 등, 외부에 멀리 공유된 컨텐츠는 흔적이 남아 삭제되지 않는 경우도 있습니다. 그리고 현재 접속이 불가능한 서버나, 업데이트를 받지 않게 된 서버에 대해서는 삭제가 반영되지 않을 수도 있습니다. + warning_title: 공유된 컨텐츠에 대해서 + errors: + '403': 이 페이지를 표시할 권한이 없습니다 + '404': 페이지를 찾을 수 없습니다 + '410': 이 페이지는 더 이상 존재하지 않습니다 + '422': + content: 보안 인증에 실패했습니다. Cookie를 차단하고 있진 않습니까? + title: 보안 인증 실패 + '429': 요청 횟수 제한에 도달했습니다. + noscript: Mastodon을 사용하기 위해서는 JavaScript를 켜 주십시오. + exports: + blocks: 차단 + csv: CSV + follows: 팔로우 + mutes: 뮤트 + storage: 미디어 + followers: + domain: 도메인 + explanation_html: 프라이버시를 확보하고 싶은 경우, 누가 여러분을 팔로우 하고 있는지 파악해둘 필요가 있습니다. 프라이빗 포스팅은 여러분의 팔로워가 소속하는 모든 인스턴스로 배달됩니다. 팔로워가 소속된 인스턴스 관리자나 소프트웨어가 여러분의 프라이버시를 존중하고 있는지 잘 모를 경우, 그 팔로워를 삭제하는 것이 좋을 수도 있습니다. + followers_count: 팔로워 수 + lock_link: 비공개 계정 + purge: 팔로워에서 삭제 + success: + one: 1개 도메인에서 팔로워를 soft-block 처리 중... + other: "%{count}개 도메인에서 팔로워를 soft-block 처리 중..." + true_privacy_html: "프라이버시 보호는 End-to-End 암호화로만 이루어 질 수 있다는 것에 유의해 주십시오." + unlocked_warning_html: 누구든 여러분을 팔로우 할 수 있으며, 여러분의 프라이빗 투고를 볼 수 있습니다. 팔로우 할 수 있는 사람을 제한하고 싶은 경우 %{lock_link}에서 설정해 주십시오. + unlocked_warning_title: 이 계정은 비공개로 설정되어 있지 않습니다. + generic: + changes_saved_msg: 정상적으로 변경되었습니다. + powered_by: powered by %{link} + save_changes: 변경 사항을 저장 + validation_errors: + one: 오류가 발생했습니다. 아래 오류를 확인해 주십시오 + other: 오류가 발생했습니다. 아래 %{count}개 오류를 확인해 주십시오 + imports: + preface: 다른 인스턴스에서 내보내기 한 파일에서 팔로우 / 차단 정보를 이 인스턴스 계정으로 불러올 수 있습니다. + success: 파일이 정상적으로 업로드 되었으며, 현재 처리 중입니다. 잠시 후 다시 확인해 주십시오. + types: + blocking: 차단한 계정 목록 + following: 팔로우 중인 계정 목록 + muting: 뮤트 중인 계정 목록 + upload: 업로드 + landing_strip_html: "%{name} 님은 %{link_to_root_path} 인스턴스의 사용자입니다. 계정을 가지고 있다면 팔로우 하거나 대화할 수 있습니다." + landing_strip_signup_html: 아직 계정이 없다면 여기서 등록할 수 있습니다. + media_attachments: + validations: + images_and_video: 이미 사진이 첨부되어 있으므로 동영상을 첨부할 수 없습니다. + too_many: 최대 4개까지 첨부할 수 있습니다. + notification_mailer: + digest: + body: "%{instance} 에서 마지막 로그인 뒤로 일어난 일:" + mention: "%{name} 님이 답장했습니다:" + new_followers_summary: + one: 새 팔로워를 획득했습니다 + other: "%{count} 명의 새 팔로워를 획득했습니다!" + subject: + one: "1건의 새로운 알림 \U0001F418" + other: "%{count}건의 새로운 알림 \U0001F418" + favourite: + body: '%{name} 님이 내 Toot을 즐겨찾기에 등록했습니다.' + subject: "%{name} 님이 내 Toot을 즐겨찾기에 등록했습니다" + follow: + body: "%{name} 님이 나를 팔로우 했습니다" + subject: "%{name} 님이 나를 팔로우 했습니다" + follow_request: + body: "%{name} 님이 내게 팔로우 요청을 보냈습니다." + subject: "%{name} 님으로터의 팔로우 요청" + mention: + body: "%{name} 님이 답장을 보냈습니다:" + subject: "%{name} 님이 답장을 보냈습니다" + reblog: + body: "%{name} 님이 내 Toot을 부스트 했습니다:" + subject: "%{name} 님이 내 Toot을 부스트 했습니다" + pagination: + next: 다음 + prev: 이전 + truncate: "…" + remote_follow: + acct: 사용자명@도메인을 입력해 주십시오 + missing_resource: 리디렉션 대상을 찾을 수 없습니다 + proceed: 팔로우 하기 + prompt: '팔로우 하려 하고 있습니다' + sessions: + activity: 마지막 활동 + browser: 브라우저 + browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + firefox: Firefox + generic: 알 수 없는 브라우저 + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: 현재 세션 + description: "%{browser} on %{platform}" + explanation: 내 Mastodon 계정에 현재 로그인 중인 웹 브라우저 목록입니다. + ip: IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: 알 수 없는 플랫폼 + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + title: 세션 + settings: + authorized_apps: 인증된 어플리케이션 + back: 돌아가기 + delete: 계정 삭제 + edit_profile: 프로필 편집 + export: 데이터 내보내기 + followers: 신뢰 중인 인스턴스 + import: 데이터 가져오기 + preferences: 사용자 설정 + settings: 설정 + two_factor_authentication: 2단계 인증 + statuses: + open_in_web: Web으로 열기 + over_character_limit: 최대 %{max}자까지 입력할 수 있습니다 + show_more: 더 보기 + visibilities: + private: 비공개 + private_long: 팔로워에게만 표시됩니다 + public: 공개 + public_long: 누구나 볼 수 있으며, 공개 타임라인에 표시됩니다 + unlisted: Unlisted + unlisted_long: 누구나 볼 수 있지만, 공개 타임라인에는 표시되지 않습니다 + stream_entries: + click_to_show: 클릭해서 표시 + reblogged: 님이 부스트 했습니다 + sensitive_content: 민감한 컨텐츠 + time: + formats: + default: "%Y년 %m월 %d일 %H:%M" + two_factor_authentication: + code_hint: 확인하기 위해서 인증 어플리케이션에서 표시된 코드를 입력해 주십시오 + description_html: "2단계 인증을 활성화 하면 로그인 시 전화로 인증 코드를 받을 필요가 있습니다." + disable: 비활성화 + enable: 활성화 + enabled: 2단계 인증이 활성화 되어 있습니다 + enabled_success: 2단계 인증이 활성화 되었습니다 + generate_recovery_codes: 복구 코드 생성 + instructions_html: "Google Authenticator, 또는 타 TOTP 어플리케이션에서 이 QR 코드를 스캔해 주십시오. 이후 로그인 시에는 이 어플리케이션에서 생성되는 코드가 필요합니다." + lost_recovery_codes: 복구 코드를 사용하면 휴대전화를 분실한 경우에도 계정에 접근할 수 있게 됩니다. 복구 코드를 분실한 경우에도 여기서 다시 생성할 수 있지만, 예전 복구 코드는 비활성화 됩니다. + manual_instructions: 'QR 코드를 스캔할 수 없어 수동으로 등록을 원하시는 경우 이 비밀 코드를 사용해 주십시오: ' + recovery_codes: 복구 코드 + recovery_codes_regenerated: 복구 코드가 다시 생성되었습니다. + recovery_instructions_html: 휴대전화를 분실한 경우, 아래 복구 코드 중 하나를 사용해 계정에 접근할 수 있습니다. 복구 코드는 안전하게 보관해 주십시오. 이 코드를 인쇄해 중요한 서류와 함께 보관하는 것도 좋습니다. + setup: 초기 설정 + wrong_code: 코드가 올바르지 않습니다. 서버와 휴대전화 간 시간이 일치하는지 확인해 주십시오. + users: + invalid_email: 메일 주소가 올바르지 않습니다 + invalid_otp_token: 2단계 인증 코드가 올바르지 않습니다 diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml new file mode 100644 index 000000000..85abddcf3 --- /dev/null +++ b/config/locales/simple_form.ko.yml @@ -0,0 +1,58 @@ +--- +ko: + simple_form: + hints: + defaults: + avatar: PNG, GIF 혹은 JPG. 최대 2MB. 120x120px로 다운스케일 됨 + display_name: + one: 1 글자 남음 + other: %{count} 글자 남음 + header: PNG, GIF 혹은 JPG. 최대 2MB. 700x335px로 다운스케일 됨 + locked: 수동으로 팔로워를 승인하고, 기본 Toot 프라이버시 설정을 팔로워 전용으로 변경 + note: + one: 1 글자 남음 + other: %{count} 글자 남음 + imports: + data: 다른 마스토돈 인스턴스에서 추출된 CSV 파일 + sessions: + otp: 2단계 인증 코드를 휴대전화를 보고 입력하거나, 복구 코드 중 하나를 사용 + user: + filtered_languages: 선택된 언어가 공개 타임라인에서 제외 될 것입니다. + labels: + defaults: + avatar: 아바타 + confirm_new_password: 새로운 비밀번호를 입력 + confirm_password: 현재 비밀번호를 다시 입력 + current_password: 현재 비밀번호를 입력 + data: 데이터 + display_name: 표시되는 이름 + email: 이메일 주소 + header: 헤더 + locale: 언어 + locked: 계정 잠금 + new_password: 새로운 비밀번호 + note: 자기소개 + otp_attempt: 2단계 인증 코드 + password: 비밀번호 + setting_auto_play_gif: 애니메이션 GIF를 자동 재생 + setting_boost_modal: 부스트 전 확인 창을 보여주기 + setting_default_privacy: Toot 프라이버시 + setting_delete_modal: Toot 삭제 전 확인 창을 보여주기 + severity: 심각도 + type: 불러오기 종류 + username: 유저 이름 + interactions: + must_be_follower: 나를 팔로우 하지 않는 사람에게서 온 알림을 차단 + must_be_following: 내가 팔로우 하지 않는 사람에게서 온 알림을 차단 + notification_emails: + digest: 요약 이메일 보내기 + favourite: 누군가 내 상태를 즐겨찾기로 등록했을 때 이메일 보내기 + follow: 누군가 나를 팔로우 했을 때 이메일 보내기 + follow_request: 누군가 나를 팔로우 하길 원할 때 이메일 보내기 + mention: 누군가 나에게 답장했을 때 이메일 보내기 + reblog: 누군가 내 Toot을 부스트 했을 때 이메일 보내기 + 'no': '아니오' + required: + mark: "*" + text: 필수 항목 + 'yes': '네' -- cgit From 976c18aa5f159d91b26c8aa0d410a519b7b2e3fd Mon Sep 17 00:00:00 2001 From: Minori Hiraoka Date: Tue, 4 Jul 2017 23:48:22 +0900 Subject: Fix Korean translation (#4065) * Added Korean Translation (based on japanese) * Update korean translation * Update korean translation: fix syntax error * Updated korean translation * Update korean translation * Update ko.json Translate non-translated parts * Update ko.yml Translated missed parts - and fixed some typos * Create simple_form.ko.yml * Update simple_form.ko.yml Translation error fix - password change form * Update simple_form.ko.yml * Update ko.json Missing translation --- app/javascript/mastodon/locales/ko.json | 2 +- config/locales/simple_form.ko.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 0a2d76d3a..e88d4a531 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -112,7 +112,7 @@ "onboarding.page_four.home": "홈 타임라인에서는 내가 팔로우 중인 사람들의 포스트를 표시합니다.", "onboarding.page_four.notifications": "알림에서는 다른 사람들과의 연결을 표시합니다.", "onboarding.page_one.federation": "Mastodon은 누구나 참가할 수 있는 SNS입니다.", - "onboarding.page_one.handle": "여러분은 지금 수많은 Mastodon 인스턴스 중 하나인 {domain}에 있습니다. あなたのフルハンドルは{handle}です。", + "onboarding.page_one.handle": "여러분은 지금 수많은 Mastodon 인스턴스 중 하나인 {domain}에 있습니다. 당신의 유저 이름은 {handle} 입니다.", "onboarding.page_one.welcome": "Mastodon에 어서 오세요!", "onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.", "onboarding.page_six.almost_done": "이상입니다.", diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 85abddcf3..b7dbc8bef 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -21,16 +21,16 @@ ko: labels: defaults: avatar: 아바타 - confirm_new_password: 새로운 비밀번호를 입력 - confirm_password: 현재 비밀번호를 다시 입력 - current_password: 현재 비밀번호를 입력 + confirm_new_password: 새로운 비밀번호 다시 입력 + confirm_password: 현재 비밀번호 다시 입력 + current_password: 현재 비밀번호 입력 data: 데이터 display_name: 표시되는 이름 email: 이메일 주소 header: 헤더 locale: 언어 locked: 계정 잠금 - new_password: 새로운 비밀번호 + new_password: 새로운 비밀번호 입력 note: 자기소개 otp_attempt: 2단계 인증 코드 password: 비밀번호 -- cgit From b52a5e6bd60be3f9548cf59eb313b1e6c2f5920e Mon Sep 17 00:00:00 2001 From: unarist Date: Wed, 5 Jul 2017 21:51:53 +0900 Subject: Show LoadMore button on Notifications even if all items are filtered (#4077) --- app/javascript/mastodon/features/notifications/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index ed4b3ad98..2f545fa4a 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -122,7 +122,7 @@ export default class Notifications extends React.PureComponent { let unread = ''; let scrollContainer = ''; - if (!isLoading && notifications.size > 0 && hasMore) { + if (!isLoading && hasMore) { loadMore = ; } @@ -132,7 +132,7 @@ export default class Notifications extends React.PureComponent { if (isLoading && this.scrollableArea) { scrollableArea = this.scrollableArea; - } else if (notifications.size > 0) { + } else if (notifications.size > 0 || hasMore) { scrollableArea = (
{unread} -- cgit From 6d106d3943f47cc48fb4799712bc6dd673b2506b Mon Sep 17 00:00:00 2001 From: m4sk1n Date: Thu, 6 Jul 2017 15:25:27 +0200 Subject: i18n: minor changes in Polish translation (#4087) * i18n: minor changes in Polish translation * Update pl.json --- app/javascript/mastodon/locales/pl.json | 2 +- config/locales/pl.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index bf425501f..2a69824ee 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -6,7 +6,7 @@ "account.follow": "Obserwuj", "account.followers": "Obserwujący", "account.follows": "Obserwacje", - "account.follows_you": "Obserwują cię", + "account.follows_you": "Obserwuje cię", "account.media": "Media", "account.mention": "Wspomnij o @{name}", "account.mute": "Wycisz @{name}", diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 1bf3d9724..f0546dd0c 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -209,9 +209,9 @@ pl: auth: change_password: Bezpieczeństwo delete_account: Usunięcie konta - delete_account_html: Jeżeli próbowałeś usunąć konto, przejdź tutaj. Otrzymasz prośbę o potwierdzenie. + delete_account_html: Jeżeli chcesz usunąć konto, przejdź tutaj. Otrzymasz prośbę o potwierdzenie. didnt_get_confirmation: Nie otrzymałeś instrukcji weryfikacji? - forgot_password: Zapomniane hasło + forgot_password: Nie pamiętasz hasła? login: Zaloguj się logout: Wyloguj się register: Rejestracja -- cgit From e7c0d87d984108a5c56c08285e8496a23fca28b8 Mon Sep 17 00:00:00 2001 From: Shin Kojima Date: Thu, 6 Jul 2017 22:27:02 +0900 Subject: Fix embedded SVG fill attribute (#4086) * Fix embedded SVG fill attribute SCSS darken/lighten functions may not return a color value, but a color name like "white". See following example: https://www.sassmeister.com/gist/c41da93b87d536890ddf30a1f42e7816 This patch will normalize $color argument to FFFFFF style. I also changed the function name from "url-friendly-colour" to "hex-color", Because... 1. The name "url-friendly" is not meaningful enough to describe what it does. 2. It is familier to me using "color" rather than "colour" kojima:kojiMac mastodon[master]$ git grep -l colour app/javascript/styles/boost.scss spec/fixtures/files/attachment.jpg kojima:kojiMac mastodon[master]$ git grep -l color .rspec .scss-lint.yml Gemfile.lock app/javascript/mastodon/features/status/components/action_bar.js app/javascript/styles/about.scss app/javascript/styles/accounts.scss app/javascript/styles/admin.scss app/javascript/styles/basics.scss app/javascript/styles/boost.scss app/javascript/styles/compact_header.scss app/javascript/styles/components.scss app/javascript/styles/containers.scss app/javascript/styles/footer.scss app/javascript/styles/forms.scss app/javascript/styles/landing_strip.scss app/javascript/styles/reset.scss app/javascript/styles/stream_entries.scss app/javascript/styles/tables.scss app/javascript/styles/variables.scss app/views/admin/subscriptions/_subscription.html.haml app/views/layouts/application.html.haml app/views/layouts/error.html.haml app/views/manifests/show.json.rabl bin/webpack-dev-server config/initializers/httplog.rb public/500.html public/emoji/1f1e6-1f1e8.svg public/emoji/1f1ec-1f1f8.svg public/emoji/1f1f3-1f1ee.svg public/emoji/1f1fb-1f1ec.svg spec/fixtures/requests/idn.txt yarn.lock * Add semicolon --- app/javascript/styles/boost.scss | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/styles/boost.scss b/app/javascript/styles/boost.scss index 7589828c6..8d6478e10 100644 --- a/app/javascript/styles/boost.scss +++ b/app/javascript/styles/boost.scss @@ -1,11 +1,14 @@ -@function url-friendly-colour($colour) { - @return '%23' + str-slice('#{$colour}', 2, -1) +@function hex-color($color) { + @if type-of($color) == 'color' { + $color: str-slice(ie-hex-str($color), 4); + } + @return '%23' + unquote($color) } button.icon-button i.fa-retweet { - background-image: url("data:image/svg+xml;utf8,"); + background-image: url("data:image/svg+xml;utf8,"); &:hover { - background-image: url("data:image/svg+xml;utf8,"); + background-image: url("data:image/svg+xml;utf8,"); } } -- cgit From 26949607d25872d4549f96ecf38c317c2774c225 Mon Sep 17 00:00:00 2001 From: Quent-in Date: Thu, 6 Jul 2017 21:10:12 +0200 Subject: l10n Occitan locale (#4089) * Small adjustments About the report part. * Update time format --- app/javascript/mastodon/locales/oc.json | 10 +++++----- config/locales/oc.yml | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index a2a82ae9f..5f226bc70 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -34,7 +34,7 @@ "compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als seguidors.", "compose_form.lock_disclaimer.lock": "clavat", "compose_form.placeholder": "A de qué pensatz ?", - "compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz d’aqueste{domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias a Mastodons. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas d’indicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists", + "compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz d’aqueste {domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias de Mastodon. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas d’indicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists", "compose_form.publish": "Tut", "compose_form.publish_loud": "{publish} !", "compose_form.sensitive": "Marcar lo mèdia coma sensible", @@ -51,7 +51,7 @@ "confirmations.mute.message": "Sètz segur de voler metre en silenci {name} ?", "emoji_button.activity": "Activitat", "emoji_button.flags": "Drapèus", - "emoji_button.food": "Manjar e beure", + "emoji_button.food": "Beure e manjar", "emoji_button.label": "Inserir un emoji", "emoji_button.nature": "Natura", "emoji_button.objects": "Objèctes", @@ -136,10 +136,10 @@ "privacy.unlisted.long": "Mostrar pas dins los fluxes publics", "privacy.unlisted.short": "Pas-listat", "reply_indicator.cancel": "Anullar", - "report.heading": "Nòu senhalament", + "report.heading": "Senhalar {target}", "report.placeholder": "Comentaris addicionals", - "report.submit": "Mandat", - "report.target": "Senhalament", + "report.submit": "Mandar", + "report.target": "Senhalar {target}", "search.placeholder": "Recercar", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}", "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat", diff --git a/config/locales/oc.yml b/config/locales/oc.yml index c882b43a1..631133f74 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -242,9 +242,9 @@ oc: - divendres - dissabte formats: - default: "%d/%m/%Y" - long: Lo %B %d de %Y - short: "%b %d" + default: "%e/%m/%Y" + long: Lo %e %B de %Y + short: "%e %b" month_names: - - de genièr -- cgit From 34c8a46d7de2b7e0b495b5c4a51c95c6a6b047b7 Mon Sep 17 00:00:00 2001 From: Mantas Date: Thu, 6 Jul 2017 22:26:07 +0300 Subject: Remove ugly blue highlight on Android browsers (#4031) --- app/javascript/styles/basics.scss | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/javascript') diff --git a/app/javascript/styles/basics.scss b/app/javascript/styles/basics.scss index 70a5be367..0cb271ddd 100644 --- a/app/javascript/styles/basics.scss +++ b/app/javascript/styles/basics.scss @@ -11,6 +11,8 @@ body { text-rendering: optimizelegibility; font-feature-settings: "kern"; text-size-adjust: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-tap-highlight-color: transparent; &.app-body { position: fixed; -- cgit From 9c03fd9caef575792f08fd4e5c396d8d72bad09f Mon Sep 17 00:00:00 2001 From: unarist Date: Fri, 7 Jul 2017 04:26:21 +0900 Subject: Unobserve status on unmount (#4013) This fixes a warning on status unmounting (e.g. deletion). This also resets IntersectionObserverWrapper on disconnect to avoid `unobserve()` calls which has bug in Edge. --- app/javascript/mastodon/components/status.js | 4 ++++ .../mastodon/features/ui/util/intersection_observer_wrapper.js | 9 +++++++++ 2 files changed, 13 insertions(+) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index a837659c2..ff574ab3d 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -90,6 +90,10 @@ export default class Status extends ImmutablePureComponent { } componentWillUnmount () { + if (this.props.intersectionObserverWrapper) { + this.props.intersectionObserverWrapper.unobserve(this.props.id, this.node); + } + this.componentMounted = false; } diff --git a/app/javascript/mastodon/features/ui/util/intersection_observer_wrapper.js b/app/javascript/mastodon/features/ui/util/intersection_observer_wrapper.js index 0e959f9ae..2b24c6583 100644 --- a/app/javascript/mastodon/features/ui/util/intersection_observer_wrapper.js +++ b/app/javascript/mastodon/features/ui/util/intersection_observer_wrapper.js @@ -37,9 +37,18 @@ class IntersectionObserverWrapper { } } + unobserve (id, node) { + if (this.observer) { + delete this.callbacks[id]; + this.observer.unobserve(node); + } + } + disconnect () { if (this.observer) { + this.callbacks = {}; this.observer.disconnect(); + this.observer = null; } } -- cgit From 6bf6d35637abc691cf85b1c96a54c74af8b8bc2e Mon Sep 17 00:00:00 2001 From: STJrInuyasha Date: Thu, 6 Jul 2017 12:30:37 -0700 Subject: Parse links in status content on update as well as mount (#4042) * Update links in status content on update as well as mount Fixes occasional bugs with mentions and hashtags not being set to open in a new column like they should, and instead opening in a new page * use classList instead of raw className --- app/javascript/mastodon/components/status_content.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index 19bde01bd..78656571d 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -25,12 +25,17 @@ export default class StatusContent extends React.PureComponent { hidden: true, }; - componentDidMount () { + _updateStatusLinks () { const node = this.node; const links = node.querySelectorAll('a'); for (var i = 0; i < links.length; ++i) { - let link = links[i]; + let link = links[i]; + if (link.classList.contains('status-link')) { + continue; + } + link.classList.add('status-link'); + let mention = this.props.status.get('mentions').find(item => link.href === item.get('url')); if (mention) { @@ -46,10 +51,15 @@ export default class StatusContent extends React.PureComponent { } } + componentDidMount () { + this._updateStatusLinks(); + } + componentDidUpdate () { if (this.props.onHeightUpdate) { this.props.onHeightUpdate(); } + this._updateStatusLinks(); } onMentionClick = (mention, e) => { -- cgit From f76e71825da3b185e549cd3267813fd12cee4050 Mon Sep 17 00:00:00 2001 From: abcang Date: Fri, 7 Jul 2017 04:31:03 +0900 Subject: Improve Activity stream spoiler (#4088) --- app/javascript/packs/public.js | 8 ++++++-- app/javascript/styles/stream_entries.scss | 12 ++++++++++++ app/views/stream_entries/_content_spoiler.html.haml | 10 +++++++--- app/views/stream_entries/_detailed_status.html.haml | 6 ++---- app/views/stream_entries/_simple_status.html.haml | 3 +-- 5 files changed, 28 insertions(+), 11 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index a0e511b0a..254250a3b 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -53,8 +53,12 @@ function main() { } }); - delegate(document, '.media-spoiler', 'click', ({ target }) => { - target.style.display = 'none'; + delegate(document, '.activity-stream .media-spoiler-wrapper .media-spoiler', 'click', function() { + this.parentNode.classList.add('media-spoiler-wrapper__visible'); + }); + + delegate(document, '.activity-stream .media-spoiler-wrapper .spoiler-button', 'click', function() { + this.parentNode.classList.remove('media-spoiler-wrapper__visible'); }); delegate(document, '.webapp-btn', 'click', ({ target, button }) => { diff --git a/app/javascript/styles/stream_entries.scss b/app/javascript/styles/stream_entries.scss index fcec32d44..e89cc3f09 100644 --- a/app/javascript/styles/stream_entries.scss +++ b/app/javascript/styles/stream_entries.scss @@ -330,6 +330,18 @@ } } + .media-spoiler-wrapper { + &.media-spoiler-wrapper__visible { + .media-spoiler { + display: none; + } + + .spoiler-button { + display: block; + } + } + } + .pre-header { padding: 14px 0; padding-left: (48px + 14px * 2); diff --git a/app/views/stream_entries/_content_spoiler.html.haml b/app/views/stream_entries/_content_spoiler.html.haml index d80ea46a0..798dfce67 100644 --- a/app/views/stream_entries/_content_spoiler.html.haml +++ b/app/views/stream_entries/_content_spoiler.html.haml @@ -1,3 +1,7 @@ -.media-spoiler - %span= t('stream_entries.sensitive_content') - %span= t('stream_entries.click_to_show') +.media-spoiler-wrapper{ class: sensitive == false && 'media-spoiler-wrapper__visible' } + .spoiler-button + .icon-button.overlayed + %i.fa.fa-fw.fa-eye + .media-spoiler + %span= t('stream_entries.sensitive_content') + %span= t('stream_entries.click_to_show') diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index ef60b9925..193cc6470 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -17,13 +17,11 @@ - unless status.media_attachments.empty? - if status.media_attachments.first.video? .video-player - - if status.sensitive? - = render partial: 'stream_entries/content_spoiler' + = render partial: 'stream_entries/content_spoiler', locals: { sensitive: status.sensitive? } %video.u-video{ src: status.media_attachments.first.file.url(:original), loop: true } - else .detailed-status__attachments - - if status.sensitive? - = render partial: 'stream_entries/content_spoiler' + = render partial: 'stream_entries/content_spoiler', locals: { sensitive: status.sensitive? } .status__attachments__inner - status.media_attachments.each do |media| = render partial: 'stream_entries/media', locals: { media: media } diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index db4e30fda..2df0cc850 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -22,8 +22,7 @@ - unless status.media_attachments.empty? .status__attachments - - if status.sensitive? - = render partial: 'stream_entries/content_spoiler' + = render partial: 'stream_entries/content_spoiler', locals: { sensitive: status.sensitive? } - if status.media_attachments.first.video? .status__attachments__inner .video-item -- cgit From 18d3fa953b5af8ab17cc93c33cb95cec37127712 Mon Sep 17 00:00:00 2001 From: Damien Erambert Date: Thu, 6 Jul 2017 13:39:56 -0700 Subject: Add a setting allowing the use of system's default font in Web UI (#4033) * add a system_font_ui setting on the server * Plug the system_font_ui on the front-end * add EN/FR locales for the new setting * put Roboto after all other fonts * remove trailing whitespace so CodeClimate is happy * fix user_spec.rb * correctly write user_spect this time * slightly better way of adding the classes * add comments to the system-font stack for clarification * use .system-font for the class instead * don't use multiple lines for comments * remove trailing whitespace * use the classnames module for consistency * use `mastodon-font-sans-serif` instead of Roboto directly --- app/controllers/settings/preferences_controller.rb | 1 + app/javascript/mastodon/features/ui/index.js | 14 ++++++++++++-- app/javascript/styles/basics.scss | 15 +++++++++++++++ app/lib/user_settings_decorator.rb | 5 +++++ app/models/user.rb | 4 ++++ app/views/home/initial_state.json.rabl | 1 + app/views/settings/preferences/show.html.haml | 1 + config/locales/simple_form.en.yml | 1 + config/locales/simple_form.fr.yml | 1 + config/settings.yml | 1 + spec/lib/user_settings_decorator_spec.rb | 7 +++++++ spec/models/user_spec.rb | 8 ++++++++ 12 files changed, 57 insertions(+), 2 deletions(-) (limited to 'app/javascript') diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 71f5a7c04..a15c26031 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -37,6 +37,7 @@ class Settings::PreferencesController < ApplicationController :setting_boost_modal, :setting_delete_modal, :setting_auto_play_gif, + :setting_system_font_ui, notification_emails: %i(follow follow_request reblog favourite mention digest), interactions: %i(must_be_follower must_be_following) ) diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 08d087da1..54e623d99 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -1,4 +1,5 @@ import React from 'react'; +import classNames from 'classnames'; import Switch from 'react-router-dom/Switch'; import Route from 'react-router-dom/Route'; import Redirect from 'react-router-dom/Redirect'; @@ -72,12 +73,17 @@ class WrappedRoute extends React.Component { } -@connect() +const mapStateToProps = state => ({ + systemFontUi: state.getIn(['meta', 'system_font_ui']), +}); + +@connect(mapStateToProps) export default class UI extends React.PureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, children: PropTypes.node, + systemFontUi: PropTypes.bool, }; state = { @@ -176,8 +182,12 @@ export default class UI extends React.PureComponent { const { width, draggingOver } = this.state; const { children } = this.props; + const className = classNames('ui', { + 'system-font': this.props.systemFontUi, + }); + return ( -
+
diff --git a/app/javascript/styles/basics.scss b/app/javascript/styles/basics.scss index 0cb271ddd..4da698e81 100644 --- a/app/javascript/styles/basics.scss +++ b/app/javascript/styles/basics.scss @@ -63,3 +63,18 @@ button { align-items: center; justify-content: center; } + +.system-font { + // system-ui => standard property (Chrome/Android WebView 56+, Opera 43+, Safari 11+) + // -apple-system => Safari <11 specific + // BlinkMacSystemFont => Chrome <56 on macOS specific + // Segoe UI => Windows 7/8/10 + // Oxygen => KDE + // Ubuntu => Unity/Ubuntu + // Cantarell => GNOME + // Fira Sans => Firefox OS + // Droid Sans => Older Androids (<4.0) + // Helvetica Neue => Older macOS <10.11 + // mastodon-font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0) + font-family: system-ui, -apple-system,BlinkMacSystemFont, "Segoe UI","Oxygen", "Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",mastodon-font-sans-serif, sans-serif; +} diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index af264bbd5..9c0cb4545 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -21,6 +21,7 @@ class UserSettingsDecorator user.settings['boost_modal'] = boost_modal_preference user.settings['delete_modal'] = delete_modal_preference user.settings['auto_play_gif'] = auto_play_gif_preference + user.settings['system_font_ui'] = system_font_ui_preference end def merged_notification_emails @@ -43,6 +44,10 @@ class UserSettingsDecorator boolean_cast_setting 'setting_delete_modal' end + def system_font_ui_preference + boolean_cast_setting 'setting_system_font_ui' + end + def auto_play_gif_preference boolean_cast_setting 'setting_auto_play_gif' end diff --git a/app/models/user.rb b/app/models/user.rb index c31a0c644..e2bb3d0ed 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -91,6 +91,10 @@ class User < ApplicationRecord settings.auto_play_gif end + def setting_system_font_ui + settings.system_font_ui + end + def activate_session(request) session_activations.activate(session_id: SecureRandom.hex, user_agent: request.user_agent, diff --git a/app/views/home/initial_state.json.rabl b/app/views/home/initial_state.json.rabl index e305f8e7a..291ff806b 100644 --- a/app/views/home/initial_state.json.rabl +++ b/app/views/home/initial_state.json.rabl @@ -11,6 +11,7 @@ node(:meta) do boost_modal: current_account.user.setting_boost_modal, delete_modal: current_account.user.setting_delete_modal, auto_play_gif: current_account.user.setting_auto_play_gif, + system_font_ui: current_account.user.setting_system_font_ui, } end diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 721ce6a21..26fbfdf82 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -44,6 +44,7 @@ .fields-group = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label + = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 3e769fb96..d8d3b8a6f 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -38,6 +38,7 @@ en: setting_boost_modal: Show confirmation dialog before boosting setting_default_privacy: Post privacy setting_delete_modal: Show confirmation dialog before deleting a toot + setting_system_font_ui: Use system's default font severity: Severity type: Import type username: Username diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index ae4975143..446c56947 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -28,6 +28,7 @@ fr: password: Mot de passe setting_boost_modal: Afficher un dialogue de confirmation avant de partager setting_default_privacy: Confidentialité des statuts + setting_system_font_ui: Utiliser la police par défaut du système severity: Séverité type: Type d'import username: Identifiant diff --git a/config/settings.yml b/config/settings.yml index 5aea232e7..18b70b51f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -19,6 +19,7 @@ defaults: &defaults boost_modal: false auto_play_gif: true delete_modal: true + system_font_ui: false notification_emails: follow: false reblog: false diff --git a/spec/lib/user_settings_decorator_spec.rb b/spec/lib/user_settings_decorator_spec.rb index 66e42fa0e..e1ba56d97 100644 --- a/spec/lib/user_settings_decorator_spec.rb +++ b/spec/lib/user_settings_decorator_spec.rb @@ -48,5 +48,12 @@ describe UserSettingsDecorator do settings.update(values) expect(user.settings['auto_play_gif']).to eq false end + + it 'updates the user settings value for system font in UI' do + values = { 'setting_system_font_ui' => '0' } + + settings.update(values) + expect(user.settings['system_font_ui']).to eq false + end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a6df3fb26..2019ec0f6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -184,6 +184,14 @@ RSpec.describe User, type: :model do expect(user.setting_auto_play_gif).to eq false end end + + describe '#setting_system_font_ui' do + it 'returns system font ui setting' do + user = Fabricate(:user) + user.settings[:system_font_ui] = false + expect(user.setting_system_font_ui).to eq false + end + end describe '#setting_boost_modal' do it 'returns boost modal setting' do -- cgit From 348d6f5e7551e632e7dea41e61c40f79aac59be9 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sat, 8 Jul 2017 00:06:02 +0200 Subject: Lazy load components (#3879) * feat: Lazy-load routes * feat: Lazy-load modals * feat: Lazy-load columns * refactor: Simplify Bundle API * feat: Optimize bundles * feat: Prevent flashing the waiting state * feat: Preload commonly used bundles * feat: Lazy load Compose reducers * feat: Lazy load Notifications reducer * refactor: Move all dynamic imports into one file * fix: Minor bugs * fix: Manually hydrate the lazy-loaded reducers * refactor: Move all dynamic imports to async-components * fix: Loading modal style * refactor: Avoid converting the raw state for each lazy hydration * refactor: Remove unused component * refactor: Maintain modal name * fix: Add as=script to preload link * chore: Fix lint error * fix(components/bundle): Check if timestamp is set when computing elapsed * fix: Load compose reducers for the onboarding modal --- app/javascript/mastodon/actions/bundles.js | 25 ++++ app/javascript/mastodon/actions/store.js | 8 ++ app/javascript/mastodon/components/status.js | 27 +++- app/javascript/mastodon/containers/mastodon.js | 5 +- .../compose/components/emoji_picker_dropdown.js | 3 +- .../mastodon/features/ui/components/bundle.js | 96 ++++++++++++++ .../features/ui/components/bundle_column_error.js | 44 +++++++ .../features/ui/components/bundle_modal_error.js | 53 ++++++++ .../features/ui/components/column_loading.js | 13 ++ .../features/ui/components/columns_area.js | 28 ++-- .../features/ui/components/modal_loading.js | 20 +++ .../mastodon/features/ui/components/modal_root.js | 51 +++++--- .../features/ui/containers/bundle_container.js | 19 +++ app/javascript/mastodon/features/ui/index.js | 89 +++++-------- .../mastodon/features/ui/util/async-components.js | 143 +++++++++++++++++++++ .../features/ui/util/react_router_helpers.js | 65 ++++++++++ app/javascript/mastodon/reducers/compose.js | 4 +- app/javascript/mastodon/reducers/index.js | 21 +-- .../mastodon/reducers/media_attachments.js | 4 +- app/javascript/mastodon/store/configureStore.js | 25 +++- app/javascript/styles/components.scss | 31 ++++- app/views/layouts/application.html.haml | 17 +++ 22 files changed, 680 insertions(+), 111 deletions(-) create mode 100644 app/javascript/mastodon/actions/bundles.js create mode 100644 app/javascript/mastodon/features/ui/components/bundle.js create mode 100644 app/javascript/mastodon/features/ui/components/bundle_column_error.js create mode 100644 app/javascript/mastodon/features/ui/components/bundle_modal_error.js create mode 100644 app/javascript/mastodon/features/ui/components/column_loading.js create mode 100644 app/javascript/mastodon/features/ui/components/modal_loading.js create mode 100644 app/javascript/mastodon/features/ui/containers/bundle_container.js create mode 100644 app/javascript/mastodon/features/ui/util/async-components.js create mode 100644 app/javascript/mastodon/features/ui/util/react_router_helpers.js (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/bundles.js b/app/javascript/mastodon/actions/bundles.js new file mode 100644 index 000000000..ecc9c8f7d --- /dev/null +++ b/app/javascript/mastodon/actions/bundles.js @@ -0,0 +1,25 @@ +export const BUNDLE_FETCH_REQUEST = 'BUNDLE_FETCH_REQUEST'; +export const BUNDLE_FETCH_SUCCESS = 'BUNDLE_FETCH_SUCCESS'; +export const BUNDLE_FETCH_FAIL = 'BUNDLE_FETCH_FAIL'; + +export function fetchBundleRequest(skipLoading) { + return { + type: BUNDLE_FETCH_REQUEST, + skipLoading, + }; +} + +export function fetchBundleSuccess(skipLoading) { + return { + type: BUNDLE_FETCH_SUCCESS, + skipLoading, + }; +} + +export function fetchBundleFail(error, skipLoading) { + return { + type: BUNDLE_FETCH_FAIL, + error, + skipLoading, + }; +} diff --git a/app/javascript/mastodon/actions/store.js b/app/javascript/mastodon/actions/store.js index 601cea001..08c2810ca 100644 --- a/app/javascript/mastodon/actions/store.js +++ b/app/javascript/mastodon/actions/store.js @@ -1,6 +1,7 @@ import Immutable from 'immutable'; export const STORE_HYDRATE = 'STORE_HYDRATE'; +export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY'; const convertState = rawState => Immutable.fromJS(rawState, (k, v) => @@ -15,3 +16,10 @@ export function hydrateStore(rawState) { state, }; }; + +export function hydrateStoreLazy(name, state) { + return { + type: `${STORE_HYDRATE_LAZY}-${name}`, + state, + }; +}; diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index ff574ab3d..18ce0198e 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -5,8 +5,6 @@ import Avatar from './avatar'; import AvatarOverlay from './avatar_overlay'; import RelativeTimestamp from './relative_timestamp'; import DisplayName from './display_name'; -import MediaGallery from './media_gallery'; -import VideoPlayer from './video_player'; import StatusContent from './status_content'; import StatusActionBar from './status_action_bar'; import { FormattedMessage } from 'react-intl'; @@ -14,6 +12,11 @@ import emojify from '../emoji'; import escapeTextContentForBrowser from 'escape-html'; import ImmutablePureComponent from 'react-immutable-pure-component'; import scheduleIdleTask from '../features/ui/util/schedule_idle_task'; +import { MediaGallery, VideoPlayer } from '../features/ui/util/async-components'; + +// We use the component (and not the container) since we do not want +// to use the progress bar to show download progress +import Bundle from '../features/ui/components/bundle'; export default class Status extends ImmutablePureComponent { @@ -154,6 +157,14 @@ export default class Status extends ImmutablePureComponent { this.setState({ isExpanded: !this.state.isExpanded }); }; + renderLoadingMediaGallery () { + return
; + } + + renderLoadingVideoPlayer () { + return
; + } + render () { let media = null; let statusAvatar; @@ -201,9 +212,17 @@ export default class Status extends ImmutablePureComponent { if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) { } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { - media = ; + media = ( + + {Component => } + + ); } else { - media = ; + media = ( + + {Component => } + + ); } } diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 3bd89902f..6e79f9e4f 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -22,9 +22,10 @@ import { getLocale } from '../locales'; const { localeData, messages } = getLocale(); addLocaleData(localeData); -const store = configureStore(); +export const store = configureStore(); const initialState = JSON.parse(document.getElementById('initial-state').textContent); -store.dispatch(hydrateStore(initialState)); +export const hydrateAction = hydrateStore(initialState); +store.dispatch(hydrateAction); export default class Mastodon extends React.PureComponent { diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js index c83dbb63e..83c66a5d5 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js @@ -2,6 +2,7 @@ import React from 'react'; import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl } from 'react-intl'; +import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components'; const messages = defineMessages({ emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, @@ -50,7 +51,7 @@ export default class EmojiPickerDropdown extends React.PureComponent { this.setState({ active: true }); if (!EmojiPicker) { this.setState({ loading: true }); - import(/* webpackChunkName: "emojione_picker" */ 'emojione-picker').then(TheEmojiPicker => { + EmojiPickerAsync().then(TheEmojiPicker => { EmojiPicker = TheEmojiPicker.default; this.setState({ loading: false }); }).catch(() => { diff --git a/app/javascript/mastodon/features/ui/components/bundle.js b/app/javascript/mastodon/features/ui/components/bundle.js new file mode 100644 index 000000000..e69a32f47 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/bundle.js @@ -0,0 +1,96 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const emptyComponent = () => null; +const noop = () => { }; + +class Bundle extends React.Component { + + static propTypes = { + fetchComponent: PropTypes.func.isRequired, + loading: PropTypes.func, + error: PropTypes.func, + children: PropTypes.func.isRequired, + renderDelay: PropTypes.number, + onRender: PropTypes.func, + onFetch: PropTypes.func, + onFetchSuccess: PropTypes.func, + onFetchFail: PropTypes.func, + } + + static defaultProps = { + loading: emptyComponent, + error: emptyComponent, + renderDelay: 0, + onRender: noop, + onFetch: noop, + onFetchSuccess: noop, + onFetchFail: noop, + } + + state = { + mod: undefined, + forceRender: false, + } + + componentWillMount() { + this.load(this.props); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.fetchComponent !== this.props.fetchComponent) { + this.load(nextProps); + } + } + + componentDidUpdate () { + this.props.onRender(); + } + + componentWillUnmount () { + if (this.timeout) { + clearTimeout(this.timeout); + } + } + + load = (props) => { + const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props; + + this.setState({ mod: undefined }); + onFetch(); + + if (renderDelay !== 0) { + this.timestamp = new Date(); + this.timeout = setTimeout(() => this.setState({ forceRender: true }), renderDelay); + } + + return fetchComponent() + .then((mod) => { + this.setState({ mod: mod.default }); + onFetchSuccess(); + }) + .catch((error) => { + this.setState({ mod: null }); + onFetchFail(error); + }); + } + + render() { + const { loading: Loading, error: Error, children, renderDelay } = this.props; + const { mod, forceRender } = this.state; + const elapsed = this.timestamp ? (new Date() - this.timestamp) : renderDelay; + + if (mod === undefined) { + return (elapsed >= renderDelay || forceRender) ? : null; + } + + if (mod === null) { + return ; + } + + return children(mod); + } + +} + +export default Bundle; diff --git a/app/javascript/mastodon/features/ui/components/bundle_column_error.js b/app/javascript/mastodon/features/ui/components/bundle_column_error.js new file mode 100644 index 000000000..cd124746a --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/bundle_column_error.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { defineMessages, injectIntl } from 'react-intl'; + +import Column from './column'; +import ColumnHeader from './column_header'; +import ColumnBackButtonSlim from '../../../components/column_back_button_slim'; +import IconButton from '../../../components/icon_button'; + +const messages = defineMessages({ + title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' }, + body: { id: 'bundle_column_error.body', defaultMessage: 'Something went wrong while loading this component.' }, + retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' }, +}); + +class BundleColumnError extends React.Component { + + static propTypes = { + onRetry: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + } + + handleRetry = () => { + this.props.onRetry(); + } + + render () { + const { intl: { formatMessage } } = this.props; + + return ( + + + +
+ + {formatMessage(messages.body)} +
+
+ ); + } + +} + +export default injectIntl(BundleColumnError); diff --git a/app/javascript/mastodon/features/ui/components/bundle_modal_error.js b/app/javascript/mastodon/features/ui/components/bundle_modal_error.js new file mode 100644 index 000000000..928bfe1f7 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/bundle_modal_error.js @@ -0,0 +1,53 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { defineMessages, injectIntl } from 'react-intl'; + +import IconButton from '../../../components/icon_button'; + +const messages = defineMessages({ + error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' }, + retry: { id: 'bundle_modal_error.retry', defaultMessage: 'Try again' }, + close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' }, +}); + +class BundleModalError extends React.Component { + + static propTypes = { + onRetry: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + } + + handleRetry = () => { + this.props.onRetry(); + } + + render () { + const { onClose, intl: { formatMessage } } = this.props; + + // Keep the markup in sync with + // (make sure they have the same dimensions) + return ( +
+
+ + {formatMessage(messages.error)} +
+ +
+
+ +
+
+
+ ); + } + +} + +export default injectIntl(BundleModalError); diff --git a/app/javascript/mastodon/features/ui/components/column_loading.js b/app/javascript/mastodon/features/ui/components/column_loading.js new file mode 100644 index 000000000..9bb9c14a1 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/column_loading.js @@ -0,0 +1,13 @@ +import React from 'react'; + +import Column from '../../../components/column'; +import ColumnHeader from '../../../components/column_header'; + +const ColumnLoading = () => ( + + +
+ +); + +export default ColumnLoading; diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index 01167b6e5..5fa27599f 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -2,15 +2,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; + import ReactSwipeable from 'react-swipeable'; -import HomeTimeline from '../../home_timeline'; -import Notifications from '../../notifications'; -import PublicTimeline from '../../public_timeline'; -import CommunityTimeline from '../../community_timeline'; -import HashtagTimeline from '../../hashtag_timeline'; -import Compose from '../../compose'; import { getPreviousLink, getNextLink } from './tabs_bar'; +import BundleContainer from '../containers/bundle_container'; +import ColumnLoading from './column_loading'; +import BundleColumnError from './bundle_column_error'; +import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline } from '../../ui/util/async-components'; + const componentMap = { 'COMPOSE': Compose, 'HOME': HomeTimeline, @@ -48,6 +48,14 @@ export default class ColumnsArea extends ImmutablePureComponent { } }; + renderLoading = () => { + return ; + } + + renderError = (props) => { + return ; + } + render () { const { columns, children, singleColumn } = this.props; @@ -62,9 +70,13 @@ export default class ColumnsArea extends ImmutablePureComponent { return (
{columns.map(column => { - const SpecificComponent = componentMap[column.get('id')]; const params = column.get('params', null) === null ? null : column.get('params').toJS(); - return ; + + return ( + + {SpecificComponent => } + + ); })} {React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))} diff --git a/app/javascript/mastodon/features/ui/components/modal_loading.js b/app/javascript/mastodon/features/ui/components/modal_loading.js new file mode 100644 index 000000000..f403ca4c9 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/modal_loading.js @@ -0,0 +1,20 @@ +import React from 'react'; + +import LoadingIndicator from '../../../components/loading_indicator'; + +// Keep the markup in sync with +// (make sure they have the same dimensions) +const ModalLoading = () => ( +
+
+ +
+
+
+
+
+
+); + +export default ModalLoading; diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index 48b048eb7..085299038 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -1,13 +1,18 @@ import React from 'react'; import PropTypes from 'prop-types'; -import MediaModal from './media_modal'; -import OnboardingModal from './onboarding_modal'; -import VideoModal from './video_modal'; -import BoostModal from './boost_modal'; -import ConfirmationModal from './confirmation_modal'; -import ReportModal from './report_modal'; import TransitionMotion from 'react-motion/lib/TransitionMotion'; import spring from 'react-motion/lib/spring'; +import BundleContainer from '../containers/bundle_container'; +import BundleModalError from './bundle_modal_error'; +import ModalLoading from './modal_loading'; +import { + MediaModal, + OnboardingModal, + VideoModal, + BoostModal, + ConfirmationModal, + ReportModal, +} from '../../../features/ui/util/async-components'; const MODAL_COMPONENTS = { 'MEDIA': MediaModal, @@ -49,6 +54,22 @@ export default class ModalRoot extends React.PureComponent { return { opacity: spring(0), scale: spring(0.98) }; } + renderModal = (SpecificComponent) => { + const { props, onClose } = this.props; + + return ; + } + + renderLoading = () => { + return ; + } + + renderError = (props) => { + const { onClose } = this.props; + + return ; + } + render () { const { type, props, onClose } = this.props; const visible = !!type; @@ -70,18 +91,14 @@ export default class ModalRoot extends React.PureComponent { > {interpolatedStyles =>
- {interpolatedStyles.map(({ key, data: { type, props }, style }) => { - const SpecificComponent = MODAL_COMPONENTS[type]; - - return ( -
-
-
- -
+ {interpolatedStyles.map(({ key, data: { type }, style }) => ( +
+
+
+ {this.renderModal}
- ); - })} +
+ ))}
} diff --git a/app/javascript/mastodon/features/ui/containers/bundle_container.js b/app/javascript/mastodon/features/ui/containers/bundle_container.js new file mode 100644 index 000000000..7e3f0c3a6 --- /dev/null +++ b/app/javascript/mastodon/features/ui/containers/bundle_container.js @@ -0,0 +1,19 @@ +import { connect } from 'react-redux'; + +import Bundle from '../components/bundle'; + +import { fetchBundleRequest, fetchBundleSuccess, fetchBundleFail } from '../../../actions/bundles'; + +const mapDispatchToProps = dispatch => ({ + onFetch () { + dispatch(fetchBundleRequest()); + }, + onFetchSuccess () { + dispatch(fetchBundleSuccess()); + }, + onFetchFail (error) { + dispatch(fetchBundleFail(error)); + }, +}); + +export default connect(null, mapDispatchToProps)(Bundle); diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 54e623d99..6057d8797 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -1,7 +1,5 @@ import React from 'react'; import classNames from 'classnames'; -import Switch from 'react-router-dom/Switch'; -import Route from 'react-router-dom/Route'; import Redirect from 'react-router-dom/Redirect'; import NotificationsContainer from './containers/notifications_container'; import PropTypes from 'prop-types'; @@ -14,64 +12,40 @@ import { debounce } from 'lodash'; import { uploadCompose } from '../../actions/compose'; import { refreshHomeTimeline } from '../../actions/timelines'; import { refreshNotifications } from '../../actions/notifications'; +import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; import UploadArea from './components/upload_area'; +import { store } from '../../containers/mastodon'; import ColumnsAreaContainer from './containers/columns_area_container'; -import Status from '../../features/status'; -import GettingStarted from '../../features/getting_started'; -import PublicTimeline from '../../features/public_timeline'; -import CommunityTimeline from '../../features/community_timeline'; -import AccountTimeline from '../../features/account_timeline'; -import AccountGallery from '../../features/account_gallery'; -import HomeTimeline from '../../features/home_timeline'; -import Compose from '../../features/compose'; -import Followers from '../../features/followers'; -import Following from '../../features/following'; -import Reblogs from '../../features/reblogs'; -import Favourites from '../../features/favourites'; -import HashtagTimeline from '../../features/hashtag_timeline'; -import Notifications from '../../features/notifications'; -import FollowRequests from '../../features/follow_requests'; -import GenericNotFound from '../../features/generic_not_found'; -import FavouritedStatuses from '../../features/favourited_statuses'; -import Blocks from '../../features/blocks'; -import Mutes from '../../features/mutes'; - -// Small wrapper to pass multiColumn to the route components -const WrappedSwitch = ({ multiColumn, children }) => ( - - {React.Children.map(children, child => React.cloneElement(child, { multiColumn }))} - -); - -WrappedSwitch.propTypes = { - multiColumn: PropTypes.bool, - children: PropTypes.node, -}; - -// Small Wraper to extract the params from the route and pass -// them to the rendered component, together with the content to -// be rendered inside (the children) -class WrappedRoute extends React.Component { - - static propTypes = { - component: PropTypes.func.isRequired, - content: PropTypes.node, - multiColumn: PropTypes.bool, - } - - renderComponent = ({ match: { params } }) => { - const { component: Component, content, multiColumn } = this.props; - - return {content}; - } - - render () { - const { component: Component, content, ...rest } = this.props; - - return ; - } +import { + Compose, + Status, + GettingStarted, + PublicTimeline, + CommunityTimeline, + AccountTimeline, + AccountGallery, + HomeTimeline, + Followers, + Following, + Reblogs, + Favourites, + HashtagTimeline, + Notifications as AsyncNotifications, + FollowRequests, + GenericNotFound, + FavouritedStatuses, + Blocks, + Mutes, +} from './util/async-components'; + +const Notifications = () => AsyncNotifications().then(component => { + store.dispatch(refreshNotifications()); + return component; +}); -} +// Dummy import, to make sure that ends up in the application bundle. +// Without this it ends up in ~8 very commonly used bundles. +import '../../components/status'; const mapStateToProps = state => ({ systemFontUi: state.getIn(['meta', 'system_font_ui']), @@ -162,7 +136,6 @@ export default class UI extends React.PureComponent { document.addEventListener('dragend', this.handleDragEnd, false); this.props.dispatch(refreshHomeTimeline()); - this.props.dispatch(refreshNotifications()); } componentWillUnmount () { diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js new file mode 100644 index 000000000..c9f81136d --- /dev/null +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -0,0 +1,143 @@ +import { store } from '../../../containers/mastodon'; +import { injectAsyncReducer } from '../../../store/configureStore'; + +// NOTE: When lazy-loading reducers, make sure to add them +// to application.html.haml (if the component is preloaded there) + +export function EmojiPicker () { + return import(/* webpackChunkName: "emojione_picker" */'emojione-picker'); +} + +export function Compose () { + return Promise.all([ + import(/* webpackChunkName: "features/compose" */'../../compose'), + import(/* webpackChunkName: "reducers/compose" */'../../../reducers/compose'), + import(/* webpackChunkName: "reducers/media_attachments" */'../../../reducers/media_attachments'), + import(/* webpackChunkName: "reducers/search" */'../../../reducers/search'), + ]).then(([component, composeReducer, mediaAttachmentsReducer, searchReducer]) => { + injectAsyncReducer(store, 'compose', composeReducer.default); + injectAsyncReducer(store, 'media_attachments', mediaAttachmentsReducer.default); + injectAsyncReducer(store, 'search', searchReducer.default); + + return component; + }); +} + +export function Notifications () { + return Promise.all([ + import(/* webpackChunkName: "features/notifications" */'../../notifications'), + import(/* webpackChunkName: "reducers/notifications" */'../../../reducers/notifications'), + ]).then(([component, notificationsReducer]) => { + injectAsyncReducer(store, 'notifications', notificationsReducer.default); + + return component; + }); +} + +export function HomeTimeline () { + return import(/* webpackChunkName: "features/home_timeline" */'../../home_timeline'); +} + +export function PublicTimeline () { + return import(/* webpackChunkName: "features/public_timeline" */'../../public_timeline'); +} + +export function CommunityTimeline () { + return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline'); +} + +export function HashtagTimeline () { + return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline'); +} + +export function Status () { + return import(/* webpackChunkName: "features/status" */'../../status'); +} + +export function GettingStarted () { + return import(/* webpackChunkName: "features/getting_started" */'../../getting_started'); +} + +export function AccountTimeline () { + return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline'); +} + +export function AccountGallery () { + return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery'); +} + +export function Followers () { + return import(/* webpackChunkName: "features/followers" */'../../followers'); +} + +export function Following () { + return import(/* webpackChunkName: "features/following" */'../../following'); +} + +export function Reblogs () { + return import(/* webpackChunkName: "features/reblogs" */'../../reblogs'); +} + +export function Favourites () { + return import(/* webpackChunkName: "features/favourites" */'../../favourites'); +} + +export function FollowRequests () { + return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests'); +} + +export function GenericNotFound () { + return import(/* webpackChunkName: "features/generic_not_found" */'../../generic_not_found'); +} + +export function FavouritedStatuses () { + return import(/* webpackChunkName: "features/favourited_statuses" */'../../favourited_statuses'); +} + +export function Blocks () { + return import(/* webpackChunkName: "features/blocks" */'../../blocks'); +} + +export function Mutes () { + return import(/* webpackChunkName: "features/mutes" */'../../mutes'); +} + +export function MediaModal () { + return import(/* webpackChunkName: "modals/media_modal" */'../components/media_modal'); +} + +export function OnboardingModal () { + return Promise.all([ + import(/* webpackChunkName: "modals/onboarding_modal" */'../components/onboarding_modal'), + import(/* webpackChunkName: "reducers/compose" */'../../../reducers/compose'), + import(/* webpackChunkName: "reducers/media_attachments" */'../../../reducers/media_attachments'), + ]).then(([component, composeReducer, mediaAttachmentsReducer]) => { + injectAsyncReducer(store, 'compose', composeReducer.default); + injectAsyncReducer(store, 'media_attachments', mediaAttachmentsReducer.default); + return component; + }); +} + +export function VideoModal () { + return import(/* webpackChunkName: "modals/video_modal" */'../components/video_modal'); +} + +export function BoostModal () { + return import(/* webpackChunkName: "modals/boost_modal" */'../components/boost_modal'); +} + +export function ConfirmationModal () { + return import(/* webpackChunkName: "modals/confirmation_modal" */'../components/confirmation_modal'); +} + +export function ReportModal () { + return import(/* webpackChunkName: "modals/report_modal" */'../components/report_modal'); +} + +export function MediaGallery () { + return import(/* webpackChunkName: "status/MediaGallery" */'../../../components/media_gallery'); +} + +export function VideoPlayer () { + return import(/* webpackChunkName: "status/VideoPlayer" */'../../../components/video_player'); +} diff --git a/app/javascript/mastodon/features/ui/util/react_router_helpers.js b/app/javascript/mastodon/features/ui/util/react_router_helpers.js new file mode 100644 index 000000000..e33a6df6f --- /dev/null +++ b/app/javascript/mastodon/features/ui/util/react_router_helpers.js @@ -0,0 +1,65 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Switch from 'react-router-dom/Switch'; +import Route from 'react-router-dom/Route'; + +import ColumnLoading from '../components/column_loading'; +import BundleColumnError from '../components/bundle_column_error'; +import BundleContainer from '../containers/bundle_container'; + +// Small wrapper to pass multiColumn to the route components +export const WrappedSwitch = ({ multiColumn, children }) => ( + + {React.Children.map(children, child => React.cloneElement(child, { multiColumn }))} + +); + +WrappedSwitch.propTypes = { + multiColumn: PropTypes.bool, + children: PropTypes.node, +}; + +// Small Wraper to extract the params from the route and pass +// them to the rendered component, together with the content to +// be rendered inside (the children) +export class WrappedRoute extends React.Component { + + static propTypes = { + component: PropTypes.func.isRequired, + content: PropTypes.node, + multiColumn: PropTypes.bool, + } + + renderComponent = ({ match }) => { + this.match = match; // Needed for this.renderBundle + + const { component } = this.props; + + return ( + + {this.renderBundle} + + ); + } + + renderLoading = () => { + return ; + } + + renderError = (props) => { + return ; + } + + renderBundle = (Component) => { + const { match: { params }, props: { content, multiColumn } } = this; + + return {content}; + } + + render () { + const { component: Component, content, ...rest } = this.props; + + return ; + } + +} diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index d0b47a85c..09db95e2d 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -23,7 +23,7 @@ import { COMPOSE_EMOJI_INSERT, } from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; -import { STORE_HYDRATE } from '../actions/store'; +import { STORE_HYDRATE_LAZY } from '../actions/store'; import Immutable from 'immutable'; import uuid from '../uuid'; @@ -134,7 +134,7 @@ const privacyPreference = (a, b) => { export default function compose(state = initialState, action) { switch(action.type) { - case STORE_HYDRATE: + case `${STORE_HYDRATE_LAZY}-compose`: return clearAll(state.merge(action.state.get('compose'))); case COMPOSE_MOUNT: return state.set('mounted', true); diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index be402a16b..79062f2f9 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -1,7 +1,6 @@ import { combineReducers } from 'redux-immutable'; import timelines from './timelines'; import meta from './meta'; -import compose from './compose'; import alerts from './alerts'; import { loadingBarReducer } from 'react-redux-loading-bar'; import modal from './modal'; @@ -9,20 +8,16 @@ import user_lists from './user_lists'; import accounts from './accounts'; import accounts_counters from './accounts_counters'; import statuses from './statuses'; -import media_attachments from './media_attachments'; import relationships from './relationships'; -import search from './search'; -import notifications from './notifications'; import settings from './settings'; import status_lists from './status_lists'; import cards from './cards'; import reports from './reports'; import contexts from './contexts'; -export default combineReducers({ +const reducers = { timelines, meta, - compose, alerts, loadingBar: loadingBarReducer, modal, @@ -30,13 +25,19 @@ export default combineReducers({ status_lists, accounts, accounts_counters, - media_attachments, statuses, relationships, - search, - notifications, settings, cards, reports, contexts, -}); +}; + +export function createReducer(asyncReducers) { + return combineReducers({ + ...reducers, + ...asyncReducers, + }); +} + +export default combineReducers(reducers); diff --git a/app/javascript/mastodon/reducers/media_attachments.js b/app/javascript/mastodon/reducers/media_attachments.js index 85bea4f0b..d17d465aa 100644 --- a/app/javascript/mastodon/reducers/media_attachments.js +++ b/app/javascript/mastodon/reducers/media_attachments.js @@ -1,4 +1,4 @@ -import { STORE_HYDRATE } from '../actions/store'; +import { STORE_HYDRATE_LAZY } from '../actions/store'; import Immutable from 'immutable'; const initialState = Immutable.Map({ @@ -7,7 +7,7 @@ const initialState = Immutable.Map({ export default function meta(state = initialState, action) { switch(action.type) { - case STORE_HYDRATE: + case `${STORE_HYDRATE_LAZY}-media_attachments`: return state.merge(action.state.get('media_attachments')); default: return state; diff --git a/app/javascript/mastodon/store/configureStore.js b/app/javascript/mastodon/store/configureStore.js index 1376d4cba..0fe29f031 100644 --- a/app/javascript/mastodon/store/configureStore.js +++ b/app/javascript/mastodon/store/configureStore.js @@ -1,15 +1,36 @@ import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; -import appReducer from '../reducers'; +import appReducer, { createReducer } from '../reducers'; +import { hydrateStoreLazy } from '../actions/store'; +import { hydrateAction } from '../containers/mastodon'; import loadingBarMiddleware from '../middleware/loading_bar'; import errorsMiddleware from '../middleware/errors'; import soundsMiddleware from '../middleware/sounds'; export default function configureStore() { - return createStore(appReducer, compose(applyMiddleware( + const store = createStore(appReducer, compose(applyMiddleware( thunk, loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }), errorsMiddleware(), soundsMiddleware() ), window.devToolsExtension ? window.devToolsExtension() : f => f)); + + store.asyncReducers = { }; + + return store; }; + +export function injectAsyncReducer(store, name, asyncReducer) { + if (!store.asyncReducers[name]) { + // Keep track that we injected this reducer + store.asyncReducers[name] = asyncReducer; + + // Add the current reducer to the store + store.replaceReducer(createReducer(store.asyncReducers)); + + // The state this reducer handles defaults to its initial state (stored inside the reducer) + // But that state may be out of date because of the server-side hydration, so we replay + // the hydration action but only for this reducer (all async reducers must listen for this dynamic action) + store.dispatch(hydrateStoreLazy(name, hydrateAction.state)); + } +} diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index a87aa5d79..9b500c7ad 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -2300,7 +2300,8 @@ button.icon-button.active i.fa-retweet { vertical-align: middle; } -.empty-column-indicator { +.empty-column-indicator, +.error-column { color: lighten($ui-base-color, 20%); background: $ui-base-color; text-align: center; @@ -2326,6 +2327,10 @@ button.icon-button.active i.fa-retweet { } } +.error-column { + flex-direction: column; +} + @keyframes pulse { 0% { opacity: 1; @@ -2909,7 +2914,8 @@ button.icon-button.active i.fa-retweet { z-index: 100; } -.onboarding-modal { +.onboarding-modal, +.error-modal { background: $ui-secondary-color; color: $ui-base-color; border-radius: 8px; @@ -2918,7 +2924,8 @@ button.icon-button.active i.fa-retweet { flex-direction: column; } -.onboarding-modal__pager { +.onboarding-modal__pager, +.error-modal__body { height: 80vh; width: 80vw; max-width: 520px; @@ -2943,6 +2950,14 @@ button.icon-button.active i.fa-retweet { } } +.error-modal__body { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; +} + @media screen and (max-width: 550px) { .onboarding-modal { width: 100%; @@ -2959,7 +2974,8 @@ button.icon-button.active i.fa-retweet { } } -.onboarding-modal__paginator { +.onboarding-modal__paginator, +.error-modal__footer { flex: 0 0 auto; background: darken($ui-secondary-color, 8%); display: flex; @@ -2969,7 +2985,8 @@ button.icon-button.active i.fa-retweet { min-width: 33px; } - .onboarding-modal__nav { + .onboarding-modal__nav, + .error-modal__nav { color: darken($ui-secondary-color, 34%); background-color: transparent; border: 0; @@ -2992,6 +3009,10 @@ button.icon-button.active i.fa-retweet { } } +.error-modal__footer { + justify-content: center; +} + .onboarding-modal__dots { flex: 1 1 auto; display: flex; diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index f991bc74f..68d346859 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -20,6 +20,23 @@ = stylesheet_pack_tag 'application', media: 'all' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' + + = javascript_pack_tag 'features/getting_started', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + + = javascript_pack_tag 'features/compose', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + = javascript_pack_tag 'reducers/compose', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + = javascript_pack_tag 'reducers/media_attachments', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + = javascript_pack_tag 'reducers/search', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + + = javascript_pack_tag 'features/home_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + + = javascript_pack_tag 'features/notifications', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + = javascript_pack_tag 'reducers/notifications', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + + = javascript_pack_tag 'features/community_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + + = javascript_pack_tag 'features/public_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' + = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' = csrf_meta_tags -- cgit From 8fecd8010801c17d0d086fbb27d4d9a67ccbb6af Mon Sep 17 00:00:00 2001 From: Sylvhem Date: Sat, 8 Jul 2017 01:27:22 +0200 Subject: Various fixes in the French translation (#4107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Changement de « Changement de mot de passe » en « Sécurité » * Suppression de « (Two-factor auth) » Change la valeur de la chaîne « two_factor_authentication » de « Identification à deux facteurs (Two-factor auth) » à « Identification à deux facteurs ». La traduction anglaise entre parathentèse était redondante et gênait la lecture. Change the value of the "two_factor_authentication" from "Identification à deux facteurs (Two-factor auth)" to "Identification à deux facteurs". The English translation in brackets was superflous and was getting in the way of the reader. * Remplace « ' » par « ’ » Retire de la traduction les apostrophes droites « ' » (U+0027) au profit des apostrophes typographiques « ’ » (U+2019). En typographie française, les apostrophes typographiques sont utilisées à la place des apostrophes droites. La traduction était jusqu’ici incohérente et utilisait les deux. Remove from the translation all the vertical apostrophes (U+0027) in favor of the curly ones (U+2019). In French typography, typographic apostrophes are used instead of vertical ones. The translation was incoherent and used both. * Remplace « ... » par « … » Remplace les séries de trois points par le caractère dédié « … » (U+2026). Replace all the series of three dots by the dedicated character "…" (U+2026). * Mise à jour Crée config/locales/activerecord.fr.yml, ajoute de nouvelles chaînes et met à jour certains textes. Les compteurs de caractères pour le pseudonyme et la biographie devrait maintenant pouvoir fonctionner même quand l’interface est en français. Create config/locales/activerecord.fr.yml, add new strings et update some textes. The caracters counters for the username and the biography should now work even when the interface is in French. * Remplace « A » par « À » Remplace « A » par « À » aux endroits où le mot est mal orthographié. Replace "A" by "À" when the wrong word is used. * Ajout d’espaces insécables Ajoute des espaces insécables suivant les régles nécessaires en typographie française. Add non-breaking spaces following rules of French typography. * Remplace « certain » par « certain·e » Harmonise la traduction en remplaçant « certain » par sa forme épicène. Harmonize the translation by replacing "certain" (sure) by its epicene form. * Corrige un angliscisme Remplace « adresse e-mail » par « adresse électronique ». Replace "adresse e-mail" (e-mail address) by "adresse électronique" (electronic address). --- app/javascript/mastodon/locales/fr.json | 46 ++++---- .../confirmation_instructions.fr.html.erb | 4 +- .../confirmation_instructions.fr.text.erb | 6 +- .../reset_password_instructions.fr.html.erb | 6 +- .../reset_password_instructions.fr.text.erb | 6 +- config/locales/activerecord.fr.yml | 12 ++ config/locales/devise.fr.yml | 16 +-- config/locales/doorkeeper.fr.yml | 54 ++++----- config/locales/fr.yml | 130 ++++++++++----------- config/locales/simple_form.fr.yml | 24 ++-- 10 files changed, 163 insertions(+), 141 deletions(-) create mode 100644 config/locales/activerecord.fr.yml (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index fd2b30444..cb7e1b5a7 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -31,10 +31,10 @@ "column_header.unpin": "Retirer", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Paramètres", - "compose_form.lock_disclaimer": "Votre compte n'est pas {locked}. Tout le monde peut vous suivre et voir vos pouets restreints.", + "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos pouets restreints.", "compose_form.lock_disclaimer.lock": "verrouillé", - "compose_form.placeholder": "Qu’avez-vous en tête ?", - "compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodon. Si {domains} {domainsCount, plural, one {n’est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n’y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d’une autre manière à d’autres personnes imprévues.", + "compose_form.placeholder": "Qu’avez-vous en tête ?", + "compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodon. Si {domains} {domainsCount, plural, one {n’est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n’y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d’une autre manière à d’autres personnes imprévues.", "compose_form.publish": "Pouet ", "compose_form.publish_loud": "{publish}!", "compose_form.sensitive": "Marquer le média comme délicat", @@ -42,13 +42,13 @@ "compose_form.spoiler_placeholder": "Avertissement", "confirmation_modal.cancel": "Annuler", "confirmations.block.confirm": "Bloquer", - "confirmations.block.message": "Confirmez vous le blocage de {name} ?", + "confirmations.block.message": "Confirmez vous le blocage de {name} ?", "confirmations.delete.confirm": "Supprimer", - "confirmations.delete.message": "Confirmez vous la suppression de ce pouet ?", + "confirmations.delete.message": "Confirmez vous la suppression de ce pouet ?", "confirmations.domain_block.confirm": "Masquer le domaine entier", - "confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûr⋅e de vouloir bloquer {domain} en entier ? Dans la plupart des cas, quelques blocages ou silenciations ciblés sont suffisants et préférables.", + "confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûr⋅e de vouloir bloquer {domain} en entier ? Dans la plupart des cas, quelques blocages ou silenciations ciblés sont suffisants et préférables.", "confirmations.mute.confirm": "Silencer", - "confirmations.mute.message": "Confirmez vous la silenciation {name} ?", + "confirmations.mute.message": "Confirmez vous la silenciation {name} ?", "emoji_button.activity": "Activités", "emoji_button.flags": "Drapeaux", "emoji_button.food": "Boire et manger", @@ -59,20 +59,20 @@ "emoji_button.search": "Recherche…", "emoji_button.symbols": "Symboles", "emoji_button.travel": "Lieux et voyages", - "empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !", + "empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !", "empty_column.hashtag": "Il n’y a encore aucun contenu relatif à ce hashtag", "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateur⋅ice⋅s.", "empty_column.home.inactivity": "Votre accueil est vide. Si vous ne vous êtes pas connecté⋅e depuis un moment, il se remplira automatiquement très bientôt.", "empty_column.home.public_timeline": "le fil public", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateur⋅ice⋅s pour débuter la conversation.", - "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateur⋅ice⋅s d’autres instances pour remplir le fil public.", + "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateur⋅ice⋅s d’autres instances pour remplir le fil public.", "follow_request.authorize": "Autoriser", "follow_request.reject": "Rejeter", "getting_started.appsshort": "Applications", "getting_started.faq": "FAQ", "getting_started.heading": "Pour commencer", "getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer et envoyer vos commentaires et rapports de bogues via {github} sur GitHub.", - "getting_started.userguide": "Guide d'utilisation", + "getting_started.userguide": "Guide d’utilisation", "home.column_settings.advanced": "Avancé", "home.column_settings.basic": "Basique", "home.column_settings.filter_regex": "Filtrer avec une expression rationnelle", @@ -93,17 +93,17 @@ "navigation_bar.mutes": "Comptes silencés", "navigation_bar.preferences": "Préférences", "navigation_bar.public_timeline": "Fil public global", - "notification.favourite": "{name} a ajouté à ses favoris :", + "notification.favourite": "{name} a ajouté à ses favoris :", "notification.follow": "{name} vous suit.", - "notification.mention": "{name} vous a mentionné⋅e :", - "notification.reblog": "{name} a partagé votre statut :", + "notification.mention": "{name} vous a mentionné⋅e :", + "notification.reblog": "{name} a partagé votre statut :", "notifications.clear": "Nettoyer", - "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?", + "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?", "notifications.column_settings.alert": "Notifications locales", - "notifications.column_settings.favourite": "Favoris :", - "notifications.column_settings.follow": "Nouveaux⋅elles abonn⋅é⋅s :", - "notifications.column_settings.mention": "Mentions :", - "notifications.column_settings.reblog": "Partages :", + "notifications.column_settings.favourite": "Favoris :", + "notifications.column_settings.follow": "Nouveaux⋅elles abonn⋅é⋅s :", + "notifications.column_settings.mention": "Mentions :", + "notifications.column_settings.reblog": "Partages :", "notifications.column_settings.show": "Afficher dans la colonne", "notifications.column_settings.sound": "Émettre un son", "onboarding.done": "Effectué", @@ -112,18 +112,18 @@ "onboarding.page_four.home": "L’Accueil affiche les posts de tou⋅te⋅s les utilisateur⋅ice⋅s que vous suivez", "onboarding.page_four.notifications": "Les Notifications vous informent lorsque quelqu’un interagit avec vous", "onboarding.page_one.federation": "Mastodon est un réseau social qui appartient à tou⋅te⋅s.", - "onboarding.page_one.handle": "Vous êtes sur {domain}, une des nombreuses instances indépendantes de Mastodon. Votre nom d'utilisateur⋅ice complet est {handle}", - "onboarding.page_one.welcome": "Bienvenue sur Mastodon !", + "onboarding.page_one.handle": "Vous êtes sur {domain}, une des nombreuses instances indépendantes de Mastodon. Votre nom d’utilisateur⋅ice complet est {handle}", + "onboarding.page_one.welcome": "Bienvenue sur Mastodon !", "onboarding.page_six.admin": "L’administrateur⋅trice de votre instance est {admin}", "onboarding.page_six.almost_done": "Nous y sommes presque…", "onboarding.page_six.appetoot": "Bon Appetoot!", "onboarding.page_six.apps_available": "De nombreuses {apps} sont disponibles pour iOS, Android et autres. Et maintenant… Bon Appetoot!", "onboarding.page_six.github": "Mastodon est un logiciel libre, gratuit et open-source. Vous pouvez rapporter des bogues, suggérer des fonctionnalités, ou contribuer à son développement sur {github}.", "onboarding.page_six.guidelines": "règles de la communauté", - "onboarding.page_six.read_guidelines": "S’il vous plaît, n’oubliez pas de lire les {guidelines} !", + "onboarding.page_six.read_guidelines": "S’il vous plaît, n’oubliez pas de lire les {guidelines} !", "onboarding.page_six.various_app": "applications mobiles", "onboarding.page_three.profile": "Modifiez votre profil pour changer votre avatar, votre description ainsi que votre nom. Vous y trouverez également d’autres préférences.", - "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateur⋅ice⋅s et regarder des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son nom d'utilisateur⋅ice complet.", + "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateur⋅ice⋅s et regarder des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son nom d’utilisateur⋅ice complet.", "onboarding.page_two.compose": "Écrivez depuis la colonne de composition. Vous pouvez ajouter des images, changer les réglages de confidentialité, et ajouter des avertissements de contenu (Content Warning) grâce aux icônes en dessous.", "onboarding.skip": "Passer", "privacy.change": "Ajuster la confidentialité du message", @@ -151,7 +151,7 @@ "status.mute_conversation": "Masquer la conversation", "status.open": "Déplier ce statut", "status.reblog": "Partager", - "status.reblogged_by": "{name} a partagé :", + "status.reblogged_by": "{name} a partagé :", "status.reply": "Répondre", "status.replyAll": "Répondre au fil", "status.report": "Signaler @{name}", diff --git a/app/views/user_mailer/confirmation_instructions.fr.html.erb b/app/views/user_mailer/confirmation_instructions.fr.html.erb index b0b3d0f51..fe3f0a010 100644 --- a/app/views/user_mailer/confirmation_instructions.fr.html.erb +++ b/app/views/user_mailer/confirmation_instructions.fr.html.erb @@ -5,10 +5,10 @@

Pour confirmer votre inscription, merci de cliquer sur le lien suivant :
<%= link_to 'Confirmer mon compte', confirmation_url(@resource, confirmation_token: @token) %>

-

Après votre première connexion, vous pourrez accéder à la documentation de l'outil.

+

Après votre première connexion, vous pourrez accéder à la documentation de l’outil.

Pensez également à jeter un œil à nos <%= link_to 'conditions d\'utilisation', terms_url %>.

Amicalement,

-

L'équipe <%= @instance %>

\ No newline at end of file +

L’équipe <%= @instance %>

diff --git a/app/views/user_mailer/confirmation_instructions.fr.text.erb b/app/views/user_mailer/confirmation_instructions.fr.text.erb index cf8e39689..7730715f8 100644 --- a/app/views/user_mailer/confirmation_instructions.fr.text.erb +++ b/app/views/user_mailer/confirmation_instructions.fr.text.erb @@ -5,10 +5,10 @@ Vous venez de vous créer un compte sur <%= @instance %> et nous vous en remerci Pour confirmer votre inscription, merci de cliquer sur le lien suivant : <%= confirmation_url(@resource, confirmation_token: @token) %> -Après votre première connexion, vous pourrez accéder à la documentation de l'outil. +Après votre première connexion, vous pourrez accéder à la documentation de l’outil. -Pour rappel, nos conditions d'utilisation sont indiquées ici <%= terms_url %> +Pour rappel, nos conditions d’utilisation sont indiquées ici <%= terms_url %> Amicalement, -L'équipe <%= @instance %> \ No newline at end of file +L’équipe <%= @instance %> diff --git a/app/views/user_mailer/reset_password_instructions.fr.html.erb b/app/views/user_mailer/reset_password_instructions.fr.html.erb index 95789e387..db55c5884 100644 --- a/app/views/user_mailer/reset_password_instructions.fr.html.erb +++ b/app/views/user_mailer/reset_password_instructions.fr.html.erb @@ -1,8 +1,8 @@

Bonjour <%= @resource.email %> !

-

Quelqu'un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous.

+

Quelqu’un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous.

<%= link_to 'Modifier mon mot de passe', edit_password_url(@resource, reset_password_token: @token) %>

-

Si vous n'êtes pas à l'origine de cette demande, vous pouvez ignorer ce message.

-

Votre mot de passe ne sera pas modifié tant que vous n'accéderez pas au lien ci-dessus et n'en choisirez pas un nouveau.

+

Si vous n’êtes pas à l’origine de cette demande, vous pouvez ignorer ce message.

+

Votre mot de passe ne sera pas modifié tant que vous n’accéderez pas au lien ci-dessus et n’en choisirez pas un nouveau.

diff --git a/app/views/user_mailer/reset_password_instructions.fr.text.erb b/app/views/user_mailer/reset_password_instructions.fr.text.erb index 73160cb4c..07fa3644a 100644 --- a/app/views/user_mailer/reset_password_instructions.fr.text.erb +++ b/app/views/user_mailer/reset_password_instructions.fr.text.erb @@ -1,8 +1,8 @@ Bonjour <%= @resource.email %> ! -Quelqu'un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous. +Quelqu’un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous. <%= edit_password_url(@resource, reset_password_token: @token) %> -Si vous n'êtes pas à l'origine de cette demande, vous pouvez ignorer ce message. -Votre mot de passe ne sera pas modifié tant que vous n'accéderez pas au lien ci-dessus et n'en choisirez pas un nouveau. +Si vous n’êtes pas à l’origine de cette demande, vous pouvez ignorer ce message. +Votre mot de passe ne sera pas modifié tant que vous n’accéderez pas au lien ci-dessus et n’en choisirez pas un nouveau. diff --git a/config/locales/activerecord.fr.yml b/config/locales/activerecord.fr.yml new file mode 100644 index 000000000..858777c0e --- /dev/null +++ b/config/locales/activerecord.fr.yml @@ -0,0 +1,12 @@ +fr: + activerecord: + errors: + models: + account: + attributes: + username: + invalid: seulement des lettres, des nombres et des tirets bas + status: + attributes: + reblog: + taken: du statut existe déjà diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml index c4dbc62e0..6805e4f38 100644 --- a/config/locales/devise.fr.yml +++ b/config/locales/devise.fr.yml @@ -3,8 +3,8 @@ fr: devise: confirmations: confirmed: Votre compte a été validé. - send_instructions: Vous allez recevoir les instructions nécessaires à la confirmation de votre compte dans quelques minutes. - send_paranoid_instructions: Si votre adresse e-mail existe dans notre base de données, vous allez bientôt recevoir un courriel contenant les instructions de confirmation de votre compte. + send_instructions: Vous allez recevoir les instructions nécessaires à la confirmation de votre compte dans quelques minutes. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables. + send_paranoid_instructions: Si votre adresse électronique existe dans notre base de données, vous allez bientôt recevoir un courriel contenant les instructions de confirmation de votre compte. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables. failure: already_authenticated: Vous êtes déjà connecté⋅e inactive: Votre compte n’est pas encore activé. @@ -25,12 +25,12 @@ fr: unlock_instructions: subject: Instructions pour déverrouiller votre compte omniauth_callbacks: - failure: 'Nous n’avons pas pu vous authentifier via %{kind} : ''%{reason}''.' + failure: 'Nous n’avons pas pu vous authentifier via %{kind} : ''%{reason}''.' success: Authentifié avec succès via %{kind}. passwords: - no_token: Vous ne pouvez accéder à cette page sans passer par un courriel de réinitialisation de mot de passe. Si vous êtes passé⋅e par un courriel de ce type, assurez-vous d'utiliser l'URL complète. - send_instructions: Vous allez recevoir les instructions de réinitialisation du mot de passe dans quelques instants - send_paranoid_instructions: Si votre addresse e-mail existe dans notre base de données, vous allez recevoir un lien de réinitialisation par courriel + no_token: Vous ne pouvez accéder à cette page sans passer par un courriel de réinitialisation de mot de passe. Si vous êtes passé⋅e par un courriel de ce type, assurez-vous d’utiliser l’URL complète. + send_instructions: Vous allez recevoir les instructions de réinitialisation du mot de passe dans quelques instants. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables. + send_paranoid_instructions: Si votre addresse électronique existe dans notre base de données, vous allez recevoir un lien de réinitialisation par courriel. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables. updated: Votre mot de passe a été modifié avec succès, vous êtes maintenant connecté⋅e updated_not_active: Votre mot de passe a été modifié avec succès. registrations: @@ -46,8 +46,8 @@ fr: signed_in: Connecté. signed_out: Déconnecté. unlocks: - send_instructions: Vous allez recevoir les instructions nécessaires au déverrouillage de votre compte dans quelques instants - send_paranoid_instructions: Si votre compte existe, vous allez bientôt recevoir un courriel contenant les instructions pour le déverrouiller. + send_instructions: Vous allez recevoir les instructions nécessaires au déverrouillage de votre compte dans quelques instants. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables. + send_paranoid_instructions: Si votre compte existe, vous allez bientôt recevoir un courriel contenant les instructions pour le déverrouiller. S’il vous plaît, dans le cas où vous ne recevriez pas ce message, vérifiez votre dossier d’indésirables. unlocked: Votre compte a été déverrouillé avec succès, vous êtes maintenant connecté⋅e. errors: messages: diff --git a/config/locales/doorkeeper.fr.yml b/config/locales/doorkeeper.fr.yml index 24538bc48..0e74532c1 100644 --- a/config/locales/doorkeeper.fr.yml +++ b/config/locales/doorkeeper.fr.yml @@ -6,12 +6,12 @@ fr: remote_follow: attributes: acct: - blank: Le nom d'utilisateur ne doit pas être vide + blank: Le nom d’utilisateur ne doit pas être vide activerecord: attributes: doorkeeper/application: name: Nom - redirect_uri: L'URL de redirection + redirect_uri: L’URL de redirection errors: messages: record_invalid: Données invalides @@ -50,17 +50,17 @@ fr: edit: Modifier submit: Envoyer confirmations: - destroy: Êtes-vous certain ? + destroy: Êtes-vous certain·e ? edit: - title: Modifier l'application + title: Modifier l’application form: - error: Oups ! Vérifier votre formulaire pour des erreurs possibles + error: Oups ! Vérifier votre formulaire pour des erreurs possibles help: native_redirect_uri: Utiliser %{native_redirect_uri} pour les tests locaux redirect_uri: Utiliser une ligne par URL scopes: Séparer les portées avec des espaces. Laisser vide pour utiliser les portées par défaut. index: - callback_url: URL de retour d'appel + callback_url: URL de retour d’appel name: Nom new: Nouvelle application title: Vos applications @@ -68,11 +68,11 @@ fr: title: Nouvelle application show: actions: Actions - application_id: ID de l'application - callback_urls: URL du retour d'appel + application_id: ID de l’application + callback_urls: URL du retour d’appel scopes: Portées secret: Secret - title: 'Application : %{name}' + title: 'Application : %{name}' authorizations: buttons: authorize: Autoriser @@ -81,15 +81,15 @@ fr: title: Une erreur est survenue new: able_to: Cette application pourra - prompt: Autoriser %{client_name} à utiliser votre compte ? + prompt: Autoriser %{client_name} à utiliser votre compte ? title: Autorisation requise show: - title: Code d'autorisation + title: Code d’autorisation authorized_applications: buttons: revoke: Annuler confirmations: - revoke: Êtes-vous certain ? + revoke: Êtes-vous certain·e ? index: application: Application created_at: Créé le @@ -98,24 +98,24 @@ fr: title: Vos applications autorisées errors: messages: - access_denied: Le propriétaire de la ressource ou le serveur d'autorisation a refusé la requête. - credential_flow_not_configured: Le flux des identifiants du mot de passe du propriétaire de la ressource a échoué car Doorkeeper.configure.resource_owner_from_credentials n'est pas configuré. - invalid_client: L'authentification du client a échoué à cause d'un client inconnu, d'aucune authentification de client incluse ou d'une méthode d'authentification non prise en charge. - invalid_grant: Le consentement d'autorisation accordé n'est pas valide, a expiré, est annulé, ne concorde pas avec l'URL de redirection utilisée dans la requête d'autorisation ou a été émis à un autre client. - invalid_redirect_uri: L'URL de redirection n'est pas valide. + access_denied: Le propriétaire de la ressource ou le serveur d’autorisation a refusé la requête. + credential_flow_not_configured: Le flux des identifiants du mot de passe du propriétaire de la ressource a échoué car Doorkeeper.configure.resource_owner_from_credentials n’est pas configuré. + invalid_client: L’authentification du client a échoué à cause d’un client inconnu, d’aucune authentification de client incluse ou d’une méthode d’authentification non prise en charge. + invalid_grant: Le consentement d’autorisation accordé n’est pas valide, a expiré, est annulé, ne concorde pas avec l’URL de redirection utilisée dans la requête d’autorisation ou a été émis à un autre client. + invalid_redirect_uri: L’URL de redirection n’est pas valide. invalid_request: La requête omet un paramètre requis, inclut une valeur de paramètre non prise en charge ou est autrement mal formée. invalid_resource_owner: Les identifiants fournis par le propriétaire de la ressource ne sont pas valides ou le propriétaire de la ressource ne peut être trouvé - invalid_scope: La portée demandée n'est pas valide, est inconnue ou mal formée. + invalid_scope: La portée demandée n’est pas valide, est inconnue ou mal formée. invalid_token: - expired: Le jeton d'accès a expiré - revoked: Le jeton d'accès a été révoqué - unknown: Le jeton d'accès n'est pas valide - resource_owner_authenticator_not_configured: La recherche du propriétaire de la ressource a échoué car Doorkeeper.configure.resource_owner_authenticator n'est pas configuré. - server_error: Le serveur d'autorisation a rencontré une condition inattendue l'empêchant de faire aboutir la requête. - temporarily_unavailable: Le serveur d'autorisation est actuellement incapable de traiter la requête à cause d'une surcharge ou d'une maintenance temporaire du serveur. - unauthorized_client: Le client n'est pas autorisé à effectuer cette requête à l'aide de cette méthode. - unsupported_grant_type: Le type de consentement d'autorisation n'est pas pris en charge par le serveur d'autorisation. - unsupported_response_type: Le serveur d'autorisation ne prend pas en charge ce type de réponse. + expired: Le jeton d’accès a expiré + revoked: Le jeton d’accès a été révoqué + unknown: Le jeton d’accès n’est pas valide + resource_owner_authenticator_not_configured: La recherche du propriétaire de la ressource a échoué car Doorkeeper.configure.resource_owner_authenticator n’est pas configuré. + server_error: Le serveur d’autorisation a rencontré une condition inattendue l’empêchant de faire aboutir la requête. + temporarily_unavailable: Le serveur d’autorisation est actuellement incapable de traiter la requête à cause d’une surcharge ou d’une maintenance temporaire du serveur. + unauthorized_client: Le client n’est pas autorisé à effectuer cette requête à l’aide de cette méthode. + unsupported_grant_type: Le type de consentement d’autorisation n’est pas pris en charge par le serveur d’autorisation. + unsupported_response_type: Le serveur d’autorisation ne prend pas en charge ce type de réponse. flash: applications: create: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 5a3e0c552..fcf5f6f9e 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -7,7 +7,7 @@ fr: business_email: Courriel professionnel closed_registrations: Les inscriptions sont actuellement fermées sur cette instance. contact: Contact - description_headline: Qu'est-ce que %{domain} ? + description_headline: Qu’est-ce que %{domain} ? domain_count_after: autres instances domain_count_before: Connectés à features: @@ -34,12 +34,12 @@ fr: follow: Suivre followers: Abonné⋅es following: Abonnements - nothing_here: Rien à voir ici ! + nothing_here: Rien à voir ici ! people_followed_by: Personnes suivies par %{name} people_who_follow: Personnes qui suivent %{name} posts: Statuts remote_follow: Suivre à distance - reserved_username: Ce nom d'utilisateur⋅ice est réservé + reserved_username: Ce nom d’utilisateur⋅ice est réservé unfollow: Ne plus suivre activitypub: activity: @@ -48,14 +48,14 @@ fr: create: name: "%{account_name} a créé une note." outbox: - name: "Boîte d'envoi de %{account_name}" - summary: Liste d'activités de %{account_name} + name: "Boîte d’envoi de %{account_name}" + summary: Liste d’activités de %{account_name} admin: accounts: - are_you_sure: Êtes-vous certain⋅e ? + are_you_sure: Êtes-vous certain⋅e ? confirm: Confirmer confirmed: Confirmé - disable_two_factor_authentication: Désactiver l'authentification à deux facteurs + disable_two_factor_authentication: Désactiver l’authentification à deux facteurs display_name: Nom affiché domain: Domaine edit: Éditer @@ -85,7 +85,7 @@ fr: perform_full_suspension: Effectuer une suspension complète profile_url: URL du profil public: Public - push_subscription_expires: Expiration de l'abonnement PuSH + push_subscription_expires: Expiration de l’abonnement PuSH redownload: Rafraîchir les avatars reset: Réinitialiser reset_password: Réinitialiser le mot de passe @@ -98,12 +98,12 @@ fr: targeted_reports: Signalements créés visant ce compte silence: Rendre muet statuses: Statuts - subscribe: S'abonner + subscribe: S’abonner title: Comptes undo_silenced: Annuler le silence undo_suspension: Annuler la suspension unsubscribe: Se désabonner - username: Nom d'utilisateur⋅ice + username: Nom d’utilisateur⋅ice web: Web domain_blocks: add_new: Ajouter @@ -112,14 +112,14 @@ fr: domain: Domaine new: create: Créer le blocage - hint: Le blocage de domaine n'empêchera pas la création de comptes dans la base de données, mais il appliquera automatiquement et rétrospectivement des méthodes de modération spécifiques sur ces comptes. + hint: Le blocage de domaine n’empêchera pas la création de comptes dans la base de données, mais il appliquera automatiquement et rétrospectivement des méthodes de modération spécifiques sur ces comptes. severity: desc_html: "Silence rendra les messages des comptes concernés invisibles à ceux qui ne les suivent pas. Suspend supprimera tout le contenu des comptes concernés, les médias, et les données du profil." silence: Muet suspend: Suspendre title: Nouveau blocage de domaine reject_media: Fichiers média rejetés - reject_media_hint: Supprime localement les fichiers média stockés et refuse d'en télécharger ultérieurement. Ne concerne pas les suspensions. + reject_media_hint: Supprime localement les fichiers média stockés et refuse d’en télécharger ultérieurement. Ne concerne pas les suspensions. severities: silence: Rendre muet suspend: Suspendre @@ -167,10 +167,10 @@ fr: contact_information: email: Entrez une adresse courriel publique label: Informations de contact - username: Entrez un nom d'utilisateur⋅ice + username: Entrez un nom d’utilisateur⋅ice registrations: closed_message: - desc_html: Affiché sur la page d'accueil lorsque les inscriptions sont fermées
Vous pouvez utiliser des balises HTML + desc_html: Affiché sur la page d’accueil lorsque les inscriptions sont fermées
Vous pouvez utiliser des balises HTML title: Message de fermeture des inscriptions open: disabled: Désactivées @@ -178,10 +178,10 @@ fr: title: Inscriptions setting: Paramètre site_description: - desc_html: Affichée sous la forme d'un paragraphe sur la page d'accueil et utilisée comme balise meta.
Vous pouvez utiliser des balises HTML, en particulier <a> et <em>. + desc_html: Affichée sous la forme d’un paragraphe sur la page d’accueil et utilisée comme balise meta.
Vous pouvez utiliser des balises HTML, en particulier <a> et <em>. title: Description du site site_description_extended: - desc_html: Affichée sur la page d'informations complémentaires du site
Vous pouvez utiliser des balises HTML + desc_html: Affichée sur la page d’informations complémentaires du site
Vous pouvez utiliser des balises HTML title: Description étendue du site site_title: Titre du site title: Paramètres du site @@ -198,17 +198,17 @@ fr: body: "%{reporter} a signalé %{target}" subject: Nouveau signalement sur %{instance} (#%{id}) application_mailer: - settings: 'Changer les préférences courriel : %{link}' + settings: 'Changer les préférences courriel : %{link}' signature: Notifications de Mastodon depuis %{instance} - view: 'Voir :' + view: 'Voir :' applications: - invalid_url: L'URL fournie est invalide + invalid_url: L’URL fournie est invalide auth: - change_password: Changer de mot de passe + change_password: Sécurité delete_account: Supprimer le compte delete_account_html: Si vous désirez supprimer votre compte, vous pouvez cliquer ici. Il vous sera demandé de confirmer cette action. - didnt_get_confirmation: Vous n’avez pas reçu les consignes de confirmation ? - forgot_password: Mot de passe oublié ? + didnt_get_confirmation: Vous n’avez pas reçu les consignes de confirmation ? + forgot_password: Mot de passe oublié ? login: Se connecter logout: Se déconnecter register: S’inscrire @@ -218,7 +218,7 @@ fr: authorize_follow: error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant follow: Suivre - prompt_html: 'Vous (%{self}) avez demandé à suivre :' + prompt_html: 'Vous (%{self}) avez demandé à suivre :' title: Suivre %{acct} datetime: distance_in_words: @@ -230,9 +230,9 @@ fr: almost_x_years: one: un an other: "%{count} ans" - half_a_minute: A l'instant + half_a_minute: À l’instant less_than_x_minutes: "%{count}min" - less_than_x_seconds: A l'instant + less_than_x_seconds: À l’instant over_x_years: one: un an other: "%{count} ans" @@ -241,19 +241,19 @@ fr: x_months: "%{count}mois" x_seconds: "%{count}s" deletes: - bad_password_msg: Bien essayé ! Mot de passe incorrect + bad_password_msg: Bien essayé ! Mot de passe incorrect confirm_password: Entrez votre mot de passe pour vérifier votre identité - description_html: Cela va supprimer votre compte et le désactiver de manière permanente et irréversible. Votre nom d'utilisateur⋅ice restera réservé afin d'éviter la confusion + description_html: Cela va supprimer votre compte et le désactiver de manière permanente et irréversible. Votre nom d’utilisateur⋅ice restera réservé afin d’éviter la confusion proceed: Supprimer compte success_msg: Votre compte a été supprimé avec succès - warning_html: Seule la suppression du contenu depuis cette instance est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les serveurs hors-lignes ainsi que ceux n'étant plus abonnés à vos publications ne mettront pas leur base de données à jour. + warning_html: Seule la suppression du contenu depuis cette instance est garantie. Le contenu qui a été partagé est susceptible de laisser des traces. Les serveurs hors-lignes ainsi que ceux n’étant plus abonnés à vos publications ne mettront pas leur base de données à jour. warning_title: Disponibilité du contenu disséminé errors: - '403': Vous n'avez pas accès à cette page. - '404': La page que vous recherchez n'existe pas. - '410': La page que vous recherchez n'existe plus. + '403': Vous n’avez pas accès à cette page. + '404': La page que vous recherchez n’existe pas. + '410': La page que vous recherchez n’existe plus. '422': - content: Vérification de sécurité échouée. Bloquez-vous les cookies ? + content: Vérification de sécurité échouée. Bloquez-vous les cookies ? title: Vérification de sécurité échouée '429': Trop de requêtes émises dans un délai donné. noscript: Pour utiliser Mastodon, veuillez activer JavaScript @@ -265,70 +265,70 @@ fr: storage: Médias stockés followers: domain: Domaine - explanation_html: Si vous voulez être sûr⋅e que vos status restent privés, vous devez savoir qui vous suit. Vos status privés seront diffusés à toutes les instances des utilisateur⋅ice⋅s qui vous suivent. Vous voudrez peut-être les passer en revue et les supprimer si vous n'êtes pas sûr⋅e que votre vie privée sera respectée par l'administration ou le logiciel de ces instances. - followers_count: Nombre d'abonné⋅es + explanation_html: Si vous voulez être sûr⋅e que vos status restent privés, vous devez savoir qui vous suit. Vos status privés seront diffusés à toutes les instances des utilisateur⋅ice⋅s qui vous suivent. Vous voudrez peut-être les passer en revue et les supprimer si vous n’êtes pas sûr⋅e que votre vie privée sera respectée par l’administration ou le logiciel de ces instances. + followers_count: Nombre d’abonné⋅es lock_link: Rendez votre compte privé - purge: Retirer de la liste d'abonné⋅es + purge: Retirer de la liste d’abonné⋅es success: - one: Suppression des abonné⋅es venant d'un domaine en cours... - other: Suppression des abonné⋅es venant de %{count} domaines en cours... - true_privacy_html: Soyez conscient⋅es qu'une vraie confidentialité ne peut être atteinte que par un chiffrement de bout-en-bout. - unlocked_warning_html: N'importe qui peut vous suivre et voir vos status privés. %{lock_link} afin de pouvoir vérifier et rejeter des abonné⋅es. - unlocked_warning_title: Votre compte n'est pas privé + one: Suppression des abonné⋅es venant d’un domaine en cours… + other: Suppression des abonné⋅es venant de %{count} domaines en cours… + true_privacy_html: Soyez conscient⋅es qu’une vraie confidentialité ne peut être atteinte que par un chiffrement de bout-en-bout. + unlocked_warning_html: N’importe qui peut vous suivre et voir vos status privés. %{lock_link} afin de pouvoir vérifier et rejeter des abonné⋅es. + unlocked_warning_title: Votre compte n’est pas privé generic: - changes_saved_msg: Les modifications ont été enregistrées avec succès ! + changes_saved_msg: Les modifications ont été enregistrées avec succès ! powered_by: propulsé par %{link} save_changes: Enregistrer les modifications validation_errors: - one: Quelque chose ne va pas ! Vérifiez l’erreur ci-dessous. - other: Certaines choses ne vont pas ! Vérifiez les erreurs ci-dessous. + one: Quelque chose ne va pas ! Vérifiez l’erreur ci-dessous. + other: Certaines choses ne vont pas ! Vérifiez les erreurs ci-dessous. imports: preface: Vous pouvez importer certaines données comme les personnes que vous suivez ou bloquez sur votre compte sur cette instance à partir de fichiers créés sur une autre instance. success: Vos données ont été importées avec succès et seront traitées en temps et en heure types: - blocking: Liste d'utilisateur⋅ice⋅s bloqué⋅es - following: Liste d'utilisateur⋅ice⋅s suivi⋅es - muting: Liste d'utilisateur⋅ice⋅s que vous faites taire + blocking: Liste d’utilisateur⋅ice⋅s bloqué⋅es + following: Liste d’utilisateur⋅ice⋅s suivi⋅es + muting: Liste d’utilisateur⋅ice⋅s que vous faites taire upload: Importer landing_strip_html: %{name} utilise %{link_to_root_path}. Vous pouvez le/la suivre et interagir si vous possédez un compte quelque part dans le "fediverse". - landing_strip_signup_html: Si ce n'est pas le cas, vous pouvez en créer un ici. + landing_strip_signup_html: Si ce n’est pas le cas, vous pouvez en créer un ici. media_attachments: validations: images_and_video: Impossible de joindre une vidéo à un statut contenant déjà des images too_many: Impossible de joindre plus de 4 fichiers notification_mailer: digest: - body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite (%{}) :' + body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite (%{}) :' mention: "%{name} vous a mentionné⋅e" new_followers_summary: - one: Vous avez un⋅e nouvel⋅le abonné⋅e ! Youpi ! - other: Vous avez %{count} nouveaux⋅elles abonné⋅e⋅s ! Incroyable ! + one: Vous avez un⋅e nouvel⋅le abonné⋅e ! Youpi ! + other: Vous avez %{count} nouveaux⋅elles abonné⋅e⋅s ! Incroyable ! subject: one: "Une nouvelle notification depuis votre dernière visite \U0001F418" other: "%{count} nouvelles notifications depuis votre dernière visite \U0001F418" favourite: - body: "%{name} a ajouté votre post à ses favoris :" + body: "%{name} a ajouté votre post à ses favoris :" subject: "%{name} a ajouté votre post à ses favoris" follow: - body: "%{name} vous suit !" + body: "%{name} vous suit !" subject: "%{name} vous suit" follow_request: body: "%{name} a demandé à vous suivre" - subject: 'Abonné⋅es en attente : %{name}' + subject: 'Abonné⋅es en attente : %{name}' mention: - body: "%{name} vous a mentionné⋅e dans :" + body: "%{name} vous a mentionné⋅e dans :" subject: "%{name} vous a mentionné⋅e" reblog: - body: "%{name} a partagé votre statut :" + body: "%{name} a partagé votre statut :" subject: "%{name} a partagé votre statut" pagination: next: Suivant prev: Précédent remote_follow: acct: Entrez votre pseudo@instance depuis lequel vous voulez suivre ce⋅tte utilisateur⋅trice - missing_resource: L'URL de redirection n'a pas pu être trouvée + missing_resource: L’URL de redirection n’a pas pu être trouvée proceed: Continuez pour suivre - prompt: 'Vous allez suivre :' + prompt: 'Vous allez suivre :' sessions: activity: Dernière activité browser: Navigateur @@ -376,7 +376,7 @@ fr: import: Import de données preferences: Préférences settings: Réglages - two_factor_authentication: Identification à deux facteurs (Two-factor auth) + two_factor_authentication: Identification à deux facteurs statuses: open_in_web: Ouvrir sur le web over_character_limit: limite de caractères dépassée de %{max} caractères @@ -397,20 +397,20 @@ fr: default: "%d %b %Y, %H:%M" two_factor_authentication: code_hint: Entrez le code généré par votre application pour confirmer - description_html: Si vous activez l'identification à deux facteurs, vous devrez être en possession de votre téléphone afin de générer un code de connexion. + description_html: Si vous activez l’identification à deux facteurs, vous devrez être en possession de votre téléphone afin de générer un code de connexion. disable: Désactiver enable: Activer - enabled: L'authentification à deux facteurs est activée + enabled: L’authentification à deux facteurs est activée enabled_success: Identification à deux facteurs activée avec succès generate_recovery_codes: Générer les codes de récupération instructions_html: "Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone. Désormais, cette application générera des jetons que vous devrez saisir à chaque connexion." lost_recovery_codes: Les codes de récupération vous permettent de retrouver les accès à votre comptre si vous perdez votre téléphone. Si vous perdez vos codes de récupération, vous pouvez les générer à nouveau ici. Vos anciens codes de récupération seront invalidés. - manual_instructions: 'Si vous ne pouvez pas scanner ce QR code et devez l''entrer manuellement, voici le secret en clair :' + manual_instructions: 'Si vous ne pouvez pas scanner ce QR code et devez l’entrer manuellement, voici le secret en clair :' recovery_codes: Codes de récupération recovery_codes_regenerated: Codes de récupération régénérés avec succès - recovery_instructions_html: Si vous perdez l'accès à votre téléphone, vous pouvez utiliser un des codes de récupération ci-dessous pour récupérer l'accès à votre compte. Conservez les codes de récupération en toute sécurité, par exemple, en les imprimant et en les stockant avec vos autres documents importants. + recovery_instructions_html: Si vous perdez l’accès à votre téléphone, vous pouvez utiliser un des codes de récupération ci-dessous pour récupérer l’accès à votre compte. Conservez les codes de récupération en toute sécurité, par exemple, en les imprimant et en les stockant avec vos autres documents importants. setup: Installer - wrong_code: Les codes entrés sont incorrects ! L'heure du serveur et celle de votre appareil sont-elles correctes ? + wrong_code: Les codes entrés sont incorrects ! L’heure du serveur et celle de votre appareil sont-elles correctes ? users: - invalid_email: L'adresse courriel est invalide - invalid_otp_token: Le code d'authentification à deux facteurs est invalide + invalid_email: L’adresse courriel est invalide + invalid_otp_token: Le code d’authentification à deux facteurs est invalide diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index 446c56947..8717a4abd 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -4,12 +4,20 @@ fr: hints: defaults: avatar: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à 120x120px - display_name: 30 caractères maximum + display_name: + one: 1 caractère restant + other: %{count} caractères restants header: Au format PNG, GIF ou JPG. 2Mo maximum. Sera réduit à 700x335px - locked: Vous devrez approuver chaque abonné⋅e et vos statuts ne s'afficheront qu'à vos abonné⋅es - note: 160 caractères maximum + locked: Vous devrez approuver chaque abonné⋅e et vos statuts ne s’afficheront qu’à vos abonné⋅es + note: + one: 1 caractère restant + other: %{count} caractères restants imports: data: Un fichier CSV généré par une autre instance de Mastodon + sessions: + otp: Entrez le code d’authentification à deux facteurs depuis votre téléphone ou utilisez un de vos codes de récupération. + user: + filtered_languages: Les langues sélectionnées seront retirées de vos fils publics. labels: defaults: avatar: Image de profil @@ -21,16 +29,18 @@ fr: email: Adresse courriel header: Image d’en-tête locale: Langue - locked: Rendre le compte privé + locked: Verrouiller le compte new_password: Nouveau mot de passe note: Présentation - otp_attempt: Code d'identification à deux facteurs + otp_attempt: Code d’identification à deux facteurs password: Mot de passe + setting_auto_play_gif: Lire automatiquement les GIFs animés setting_boost_modal: Afficher un dialogue de confirmation avant de partager setting_default_privacy: Confidentialité des statuts + setting_delete_modal: Afficher un dialogue de confirmation avant de supprimer un pouet setting_system_font_ui: Utiliser la police par défaut du système severity: Séverité - type: Type d'import + type: Type d’import username: Identifiant interactions: must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas @@ -39,7 +49,7 @@ fr: digest: Envoyer des courriels récapitulatifs favourite: Envoyer un courriel lorsque quelqu’un ajoute mes statuts à ses favoris follow: Envoyer un courriel lorsque quelqu’un me suit - follow_request: Envoyer un courriel lorsque quelqu'un demande à me suivre + follow_request: Envoyer un courriel lorsque quelqu’un demande à me suivre mention: Envoyer un courriel lorsque quelqu’un me mentionne reblog: Envoyer un courriel lorsque quelqu’un partage mes statuts 'no': Non -- cgit From 63b77f23202a6dece419e2eb7180395b2e276b09 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Fri, 7 Jul 2017 16:57:22 -0700 Subject: Avoid using getBoundingClientRect to calculate height (#4001) --- app/javascript/mastodon/components/status.js | 19 +++++++++---------- .../mastodon/components/status_content.js | 4 ---- .../mastodon/features/ui/components/bundle.js | 6 ------ .../features/ui/util/get_rect_from_entry.js | 21 +++++++++++++++++++++ 4 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 app/javascript/mastodon/features/ui/util/get_rect_from_entry.js (limited to 'app/javascript') diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 18ce0198e..df771f5a8 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -17,6 +17,7 @@ import { MediaGallery, VideoPlayer } from '../features/ui/util/async-components' // We use the component (and not the container) since we do not want // to use the progress bar to show download progress import Bundle from '../features/ui/components/bundle'; +import getRectFromEntry from '../features/ui/util/get_rect_from_entry'; export default class Status extends ImmutablePureComponent { @@ -101,6 +102,11 @@ export default class Status extends ImmutablePureComponent { } handleIntersection = (entry) => { + if (this.node && this.node.children.length !== 0) { + // save the height of the fully-rendered element + this.height = getRectFromEntry(entry).height; + } + // Edge 15 doesn't support isIntersecting, but we can infer it // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12156111/ // https://github.com/WICG/IntersectionObserver/issues/211 @@ -129,15 +135,8 @@ export default class Status extends ImmutablePureComponent { this.setState((prevState) => ({ isHidden: !prevState.isIntersecting })); } - saveHeight = () => { - if (this.node && this.node.children.length !== 0) { - this.height = this.node.getBoundingClientRect().height; - } - } - handleRef = (node) => { this.node = node; - this.saveHeight(); } handleClick = () => { @@ -213,13 +212,13 @@ export default class Status extends ImmutablePureComponent { } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { media = ( - + {Component => } ); } else { media = ( - + {Component => } ); @@ -246,7 +245,7 @@ export default class Status extends ImmutablePureComponent {
- + {media} diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js index 78656571d..02b4c8402 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.js @@ -17,7 +17,6 @@ export default class StatusContent extends React.PureComponent { status: ImmutablePropTypes.map.isRequired, expanded: PropTypes.bool, onExpandedToggle: PropTypes.func, - onHeightUpdate: PropTypes.func, onClick: PropTypes.func, }; @@ -56,9 +55,6 @@ export default class StatusContent extends React.PureComponent { } componentDidUpdate () { - if (this.props.onHeightUpdate) { - this.props.onHeightUpdate(); - } this._updateStatusLinks(); } diff --git a/app/javascript/mastodon/features/ui/components/bundle.js b/app/javascript/mastodon/features/ui/components/bundle.js index e69a32f47..3eed446fe 100644 --- a/app/javascript/mastodon/features/ui/components/bundle.js +++ b/app/javascript/mastodon/features/ui/components/bundle.js @@ -12,7 +12,6 @@ class Bundle extends React.Component { error: PropTypes.func, children: PropTypes.func.isRequired, renderDelay: PropTypes.number, - onRender: PropTypes.func, onFetch: PropTypes.func, onFetchSuccess: PropTypes.func, onFetchFail: PropTypes.func, @@ -22,7 +21,6 @@ class Bundle extends React.Component { loading: emptyComponent, error: emptyComponent, renderDelay: 0, - onRender: noop, onFetch: noop, onFetchSuccess: noop, onFetchFail: noop, @@ -43,10 +41,6 @@ class Bundle extends React.Component { } } - componentDidUpdate () { - this.props.onRender(); - } - componentWillUnmount () { if (this.timeout) { clearTimeout(this.timeout); diff --git a/app/javascript/mastodon/features/ui/util/get_rect_from_entry.js b/app/javascript/mastodon/features/ui/util/get_rect_from_entry.js new file mode 100644 index 000000000..c266cd7dc --- /dev/null +++ b/app/javascript/mastodon/features/ui/util/get_rect_from_entry.js @@ -0,0 +1,21 @@ + +// Get the bounding client rect from an IntersectionObserver entry. +// This is to work around a bug in Chrome: https://crbug.com/737228 + +let hasBoundingRectBug; + +function getRectFromEntry(entry) { + if (typeof hasBoundingRectBug !== 'boolean') { + const boundingRect = entry.target.getBoundingClientRect(); + const observerRect = entry.boundingClientRect; + hasBoundingRectBug = boundingRect.height !== observerRect.height || + boundingRect.top !== observerRect.top || + boundingRect.width !== observerRect.width || + boundingRect.bottom !== observerRect.bottom || + boundingRect.left !== observerRect.left || + boundingRect.right !== observerRect.right; + } + return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect; +} + +export default getRectFromEntry; -- cgit From 0324f807f4f7b557bb0c38f0dbb4cfd98490957d Mon Sep 17 00:00:00 2001 From: Jeroen Date: Sat, 8 Jul 2017 17:17:02 +0200 Subject: Update and improvement Dutch language strings (#4117) * Update * Update --- app/javascript/mastodon/locales/nl.json | 10 ++-- config/locales/nl.yml | 102 +++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 26 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 38ca6518a..05a9e3a12 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -10,7 +10,7 @@ "account.media": "Media", "account.mention": "Vermeld @{name}", "account.mute": "Negeer @{name}", - "account.posts": "Berichten", + "account.posts": "Toots", "account.report": "Rapporteer @{name}", "account.requested": "Wacht op goedkeuring", "account.unblock": "Deblokkeer @{name}", @@ -22,7 +22,7 @@ "column.community": "Lokale tijdlijn", "column.favourites": "Favorieten", "column.follow_requests": "Volgverzoeken", - "column.home": "Jouw tijdlijn", + "column.home": "Start", "column.mutes": "Genegeerde gebruikers", "column.notifications": "Meldingen", "column.public": "Globale tijdlijn", @@ -62,7 +62,7 @@ "empty_column.community": "De lokale tijdlijn is leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!", "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.", "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.", - "empty_column.home.inactivity": "Jouw tijdlijn is leeg. Wanneer je een tijdje inactief bent geweest wordt deze snel opnieuw aangemaakt.", + "empty_column.home.inactivity": "Deze tijdlijn is leeg. Wanneer je een tijdje inactief bent geweest wordt deze snel opnieuw aangemaakt.", "empty_column.home.public_timeline": "de globale tijdlijn", "empty_column.notifications": "Je hebt nog geen meldingen. Heb interactie met andere mensen om het gesprek aan te gaan.", "empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere Mastodon-servers om het te vullen.", @@ -109,7 +109,7 @@ "onboarding.done": "Klaar", "onboarding.next": "Volgende", "onboarding.page_five.public_timelines": "De lokale tijdlijn toont openbare toots van iedereen op {domain}. De globale tijdlijn toont openbare toots van iedereen die door gebruikers van {domain} worden gevolgd, dus ook mensen van andere Mastodon-servers. Dit zijn de openbare tijdlijnen en vormen een uitstekende manier om nieuwe mensen te ontdekken.", - "onboarding.page_four.home": "Jouw tijdlijn laat toots zien van mensen die jij volgt.", + "onboarding.page_four.home": "Deze tijdlijn laat toots zien van mensen die jij volgt.", "onboarding.page_four.notifications": "De kolom met meldingen toont alle interacties die je met andere Mastodon-gebruikers hebt.", "onboarding.page_one.federation": "Mastodon is een netwerk van onafhankelijke servers die samen een groot sociaal netwerk vormen.", "onboarding.page_one.handle": "Je bevindt je nu op {domain}, dus is jouw volledige Mastodon-adres {handle}", @@ -162,7 +162,7 @@ "status.unmute_conversation": "Conversatie niet meer negeren", "tabs_bar.compose": "Schrijven", "tabs_bar.federated_timeline": "Globaal", - "tabs_bar.home": "Jouw tijdlijn", + "tabs_bar.home": "Start", "tabs_bar.local_timeline": "Lokaal", "tabs_bar.notifications": "Meldingen", "upload_area.title": "Hierin slepen om te uploaden", diff --git a/config/locales/nl.yml b/config/locales/nl.yml index d9b02e09c..306ce6b1f 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -36,10 +36,33 @@ nl: nothing_here: Hier is niets! people_followed_by: Mensen die %{name} volgt people_who_follow: Mensen die %{name} volgen - posts: Berichten + posts: Toots remote_follow: Extern volgen unfollow: Ontvolgen admin: + reports: + action_taken_by: Actie uitgevoerd door + are_you_sure: Weet je het zeker? + comment: + label: Opmerking + none: Geen + delete: Verwijderen + id: ID + mark_as_resolved: Markeer als opgelost + nsfw: + 'false': Media tonen + 'true': Media verbergen + report: 'Gerapporteerde toot #%{id}' + reported_account: Gerapporteerde account + reported_by: Gerapporteerd door + resolved: Opgelost + silence_account: Account stilzwijgen + status: Toot + suspend_account: Account blokkeren + target: Target + title: Gerapporteerde toots + unresolved: Onopgelost + view: Weergeven settings: contact_information: email: Vul een openbaar gebruikt e-mailadres in @@ -62,24 +85,11 @@ nl: title: Uitgebreide omschrijving Mastodon-server site_title: Naam Mastodon-server title: Server-instellingen - admin.reports: - comment: - label: Opmerking - none: Geen - delete: Verwijderen - id: ID - mark_as_resolved: Markeer als opgelost - report: 'Gerapporteerde toot #%{id}' - reported_account: Gerapporteerde account - reported_by: Gerapporteerd door - resolved: Opgelost - silence_account: Account stilzwijgen - status: Toot - suspend_account: Account blokkeren - target: Target - title: Gerapporteerde toots - unresolved: Onopgelost - view: Weergeven + title: Beheer + admin_mailer: + new_report: + body: "%{reporter} heeft %{target} gerapporteerd" + subject: Nieuwe toots gerapporteerd op %{instance} (#%{id}) application_mailer: settings: 'E-mailvoorkeuren wijzigen: %{link}' signature: Mastodon-meldingen van %{instance} @@ -87,7 +97,9 @@ nl: applications: invalid_url: De opgegevens URL is ongeldig auth: - change_password: Inloggegevens + change_password: Beveiliging + delete_account: Account verwijderen + delete_account_html: Wanneer je jouw account graag wilt verwijderen, kan je dat hier doen. We vragen jou daar om een bevestiging. didnt_get_confirmation: Geen bevestigingsinstructies ontvangen? forgot_password: Wachtwoord vergeten? login: Aanmelden @@ -115,12 +127,23 @@ nl: x_minutes: "%{count}m" x_months: "%{count}ma" x_seconds: "%{count}s" + deletes: + bad_password_msg: Goed geprobeerd hackers! Ongeldig wachtwoord + confirm_password: Voer jouw huidige wachtwoord in om jouw identiteit te bevestigen + description_html: Hierdoor worden alle gegevens van jouw account permanent, onomkeerbaar verwijderd en wordt deze gedeactiveerd. Om toekomstige identiteitsdiefstal te voorkomen, kan op deze server jouw gebruikersnaam niet meer gebruikt worden. + proceed: Account verwijderen + success_msg: Jouw account is succesvol verwijderd + warning_html: We kunnen alleen garanderen dat jouw gegevens op deze server worden verwijderd. Berichten (toots), incl. media, die veel zijn gedeeld laten mogelijk sporen achter. Offline servers en servers die niet meer op jouw updates zijn geabonneerd zullen niet hun databases updaten. + warning_title: Verwijdering gegevens op andere servers errors: + '403': Jij hebt geen toestemming om deze pagina te bekijken. '404': De pagina waarnaar jij op zoek bent bestaat niet. '410': De pagina waarnaar jij op zoek bent bestaat niet meer. '422': content: Veiligheidsverificatie mislukt. Blokkeer je toevallig cookies? title: Veiligheidsverificatie mislukt + '429': Te veel verbindingsaanvragen. + noscript: Schakel JavaScript in om Mastodon te kunnen gebruiken. exports: blocks: Jij blokkeert csv: CSV @@ -141,7 +164,7 @@ nl: unlocked_warning_title: Jouw account is niet besloten generic: changes_saved_msg: Wijzigingen succesvol opgeslagen! - powered_by: mogelijk gemaakt door %{link} + powered_by: wordt mogelijk gemaakt door %{link} save_changes: Wijzigingen opslaan validation_errors: one: Er is iets niet helemaal goed! Bekijk onderstaande fout @@ -189,6 +212,43 @@ nl: missing_resource: Kon vereiste doorverwijzings-URL voor jouw account niet vinden proceed: Ga door om te volgen prompt: 'Jij gaat volgen:' + sessions: + activity: Laatst actief + browser: Webbrowser + browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + firefox: Firefox + generic: Onbekende webbrowser + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: Huidige sessie + description: "%{browser} op %{platform}" + explanation: Dit zijn de webbrowsers die momenteel met jouw Mastodon-account zijn ingelogd. + ip: IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: Onbekend platform + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + title: Sessies settings: authorized_apps: Geautoriseerde apps back: Terug naar Mastodon -- cgit From 852bda3d320563400eaa74f02ef0c6c73cd8180d Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 9 Jul 2017 00:20:53 +0900 Subject: Use srcSet only when know width (#4112) --- app/javascript/mastodon/components/media_gallery.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index 2cb1ce51c..75222e965 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -91,8 +91,10 @@ class Item extends React.PureComponent { const originalUrl = attachment.get('url'); const originalWidth = attachment.getIn(['meta', 'original', 'width']); - const srcSet = `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w`; - const sizes = `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw`; + const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number'; + + const srcSet = hasSize && `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w`; + const sizes = hasSize && `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw`; thumbnail = ( Date: Sun, 9 Jul 2017 00:21:59 +0900 Subject: Don't use preview when image size is unknown (#4113) --- .../mastodon/features/ui/components/image_loader.js | 16 ++++++++++++---- .../mastodon/features/ui/components/media_modal.js | 5 ++++- app/javascript/styles/components.scss | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.js index 52c3a898b..5ea55d1d2 100644 --- a/app/javascript/mastodon/features/ui/components/image_loader.js +++ b/app/javascript/mastodon/features/ui/components/image_loader.js @@ -8,12 +8,14 @@ export default class ImageLoader extends React.PureComponent { alt: PropTypes.string, src: PropTypes.string.isRequired, previewSrc: PropTypes.string.isRequired, - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, + width: PropTypes.number, + height: PropTypes.number, } static defaultProps = { alt: '', + width: null, + height: null, }; state = { @@ -46,8 +48,8 @@ export default class ImageLoader extends React.PureComponent { this.setState({ loading: true, error: false }); Promise.all([ this.loadPreviewCanvas(props), - this.loadOriginalImage(props), - ]) + this.hasSize() && this.loadOriginalImage(props), + ].filter(Boolean)) .then(() => { this.setState({ loading: false, error: false }); this.clearPreviewCanvas(); @@ -106,6 +108,11 @@ export default class ImageLoader extends React.PureComponent { this.removers = []; } + hasSize () { + const { width, height } = this.props; + return typeof width === 'number' && typeof height === 'number'; + } + setCanvasRef = c => { this.canvas = c; } @@ -116,6 +123,7 @@ export default class ImageLoader extends React.PureComponent { const className = classNames('image-loader', { 'image-loader--loading': loading, + 'image-loader--amorphous': !this.hasSize(), }); return ( diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index 8bb81ca01..a5b9dc19f 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -74,7 +74,10 @@ export default class MediaModal extends ImmutablePureComponent { } if (attachment.get('type') === 'image') { - content = ; + const width = attachment.getIn(['meta', 'original', 'width']) || null; + const height = attachment.getIn(['meta', 'original', 'height']) || null; + + content = ; } else if (attachment.get('type') === 'gifv') { content = ; } diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 9b500c7ad..66d2715da 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -1117,6 +1117,20 @@ height: 100%; background-image: none; } + + &.image-loader--amorphous { + position: static; + + .image-loader__preview-canvas { + display: none; + } + + .image-loader__img { + position: static; + width: auto; + height: auto; + } + } } .navigation-bar { -- cgit From 46f5d3a2e95a2db7a8197929a7973259ad067a07 Mon Sep 17 00:00:00 2001 From: unarist Date: Sun, 9 Jul 2017 00:22:24 +0900 Subject: Fix first loading of notifications when the column is pinned (#4114) --- app/javascript/mastodon/features/ui/index.js | 9 +-------- app/javascript/mastodon/features/ui/util/async-components.js | 3 +++ 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 6057d8797..8d784dfe0 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -11,10 +11,8 @@ import { isMobile } from '../../is_mobile'; import { debounce } from 'lodash'; import { uploadCompose } from '../../actions/compose'; import { refreshHomeTimeline } from '../../actions/timelines'; -import { refreshNotifications } from '../../actions/notifications'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; import UploadArea from './components/upload_area'; -import { store } from '../../containers/mastodon'; import ColumnsAreaContainer from './containers/columns_area_container'; import { Compose, @@ -30,7 +28,7 @@ import { Reblogs, Favourites, HashtagTimeline, - Notifications as AsyncNotifications, + Notifications, FollowRequests, GenericNotFound, FavouritedStatuses, @@ -38,11 +36,6 @@ import { Mutes, } from './util/async-components'; -const Notifications = () => AsyncNotifications().then(component => { - store.dispatch(refreshNotifications()); - return component; -}); - // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import '../../components/status'; diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index c9f81136d..56880dd1f 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -1,4 +1,5 @@ import { store } from '../../../containers/mastodon'; +import { refreshNotifications } from '../../../actions/notifications'; import { injectAsyncReducer } from '../../../store/configureStore'; // NOTE: When lazy-loading reducers, make sure to add them @@ -30,6 +31,8 @@ export function Notifications () { ]).then(([component, notificationsReducer]) => { injectAsyncReducer(store, 'notifications', notificationsReducer.default); + store.dispatch(refreshNotifications()); + return component; }); } -- cgit From 91cacb1e8f9b2cb20a7cda2195b91d7d85c494e9 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sat, 8 Jul 2017 18:34:55 +0200 Subject: fix: Rerender Bundle on route change (#4120) --- .../mastodon/features/ui/util/react_router_helpers.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/util/react_router_helpers.js b/app/javascript/mastodon/features/ui/util/react_router_helpers.js index e33a6df6f..ede578e56 100644 --- a/app/javascript/mastodon/features/ui/util/react_router_helpers.js +++ b/app/javascript/mastodon/features/ui/util/react_router_helpers.js @@ -31,13 +31,11 @@ export class WrappedRoute extends React.Component { } renderComponent = ({ match }) => { - this.match = match; // Needed for this.renderBundle - - const { component } = this.props; + const { component, content, multiColumn } = this.props; return ( - {this.renderBundle} + {Component => {content}} ); } @@ -50,12 +48,6 @@ export class WrappedRoute extends React.Component { return ; } - renderBundle = (Component) => { - const { match: { params }, props: { content, multiColumn } } = this; - - return {content}; - } - render () { const { component: Component, content, ...rest } = this.props; -- cgit From 37c832cdf7a307511b27e64174ed1a3e160ec66e Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sun, 9 Jul 2017 12:16:08 +0200 Subject: refactor: Make all reducers sync (#4125) --- app/javascript/mastodon/actions/store.js | 7 ---- app/javascript/mastodon/containers/mastodon.js | 3 +- .../mastodon/features/ui/util/async-components.js | 41 ++-------------------- app/javascript/mastodon/reducers/compose.js | 4 +-- app/javascript/mastodon/reducers/index.js | 15 ++++---- .../mastodon/reducers/media_attachments.js | 4 +-- app/javascript/mastodon/store/configureStore.js | 25 ++----------- app/views/layouts/application.html.haml | 9 ----- 8 files changed, 18 insertions(+), 90 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/store.js b/app/javascript/mastodon/actions/store.js index 08c2810ca..efdb0771a 100644 --- a/app/javascript/mastodon/actions/store.js +++ b/app/javascript/mastodon/actions/store.js @@ -16,10 +16,3 @@ export function hydrateStore(rawState) { state, }; }; - -export function hydrateStoreLazy(name, state) { - return { - type: `${STORE_HYDRATE_LAZY}-${name}`, - state, - }; -}; diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 6e79f9e4f..87ab6023c 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -23,8 +23,7 @@ const { localeData, messages } = getLocale(); addLocaleData(localeData); export const store = configureStore(); -const initialState = JSON.parse(document.getElementById('initial-state').textContent); -export const hydrateAction = hydrateStore(initialState); +const hydrateAction = hydrateStore(JSON.parse(document.getElementById('initial-state').textContent)); store.dispatch(hydrateAction); export default class Mastodon extends React.PureComponent { diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 56880dd1f..55de114b5 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -1,40 +1,13 @@ -import { store } from '../../../containers/mastodon'; -import { refreshNotifications } from '../../../actions/notifications'; -import { injectAsyncReducer } from '../../../store/configureStore'; - -// NOTE: When lazy-loading reducers, make sure to add them -// to application.html.haml (if the component is preloaded there) - export function EmojiPicker () { return import(/* webpackChunkName: "emojione_picker" */'emojione-picker'); } export function Compose () { - return Promise.all([ - import(/* webpackChunkName: "features/compose" */'../../compose'), - import(/* webpackChunkName: "reducers/compose" */'../../../reducers/compose'), - import(/* webpackChunkName: "reducers/media_attachments" */'../../../reducers/media_attachments'), - import(/* webpackChunkName: "reducers/search" */'../../../reducers/search'), - ]).then(([component, composeReducer, mediaAttachmentsReducer, searchReducer]) => { - injectAsyncReducer(store, 'compose', composeReducer.default); - injectAsyncReducer(store, 'media_attachments', mediaAttachmentsReducer.default); - injectAsyncReducer(store, 'search', searchReducer.default); - - return component; - }); + return import(/* webpackChunkName: "features/compose" */'../../compose'); } export function Notifications () { - return Promise.all([ - import(/* webpackChunkName: "features/notifications" */'../../notifications'), - import(/* webpackChunkName: "reducers/notifications" */'../../../reducers/notifications'), - ]).then(([component, notificationsReducer]) => { - injectAsyncReducer(store, 'notifications', notificationsReducer.default); - - store.dispatch(refreshNotifications()); - - return component; - }); + return import(/* webpackChunkName: "features/notifications" */'../../notifications'); } export function HomeTimeline () { @@ -110,15 +83,7 @@ export function MediaModal () { } export function OnboardingModal () { - return Promise.all([ - import(/* webpackChunkName: "modals/onboarding_modal" */'../components/onboarding_modal'), - import(/* webpackChunkName: "reducers/compose" */'../../../reducers/compose'), - import(/* webpackChunkName: "reducers/media_attachments" */'../../../reducers/media_attachments'), - ]).then(([component, composeReducer, mediaAttachmentsReducer]) => { - injectAsyncReducer(store, 'compose', composeReducer.default); - injectAsyncReducer(store, 'media_attachments', mediaAttachmentsReducer.default); - return component; - }); + return import(/* webpackChunkName: "modals/onboarding_modal" */'../components/onboarding_modal'); } export function VideoModal () { diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 09db95e2d..d0b47a85c 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -23,7 +23,7 @@ import { COMPOSE_EMOJI_INSERT, } from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; -import { STORE_HYDRATE_LAZY } from '../actions/store'; +import { STORE_HYDRATE } from '../actions/store'; import Immutable from 'immutable'; import uuid from '../uuid'; @@ -134,7 +134,7 @@ const privacyPreference = (a, b) => { export default function compose(state = initialState, action) { switch(action.type) { - case `${STORE_HYDRATE_LAZY}-compose`: + case STORE_HYDRATE: return clearAll(state.merge(action.state.get('compose'))); case COMPOSE_MOUNT: return state.set('mounted', true); diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index 79062f2f9..919345f16 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -14,6 +14,10 @@ import status_lists from './status_lists'; import cards from './cards'; import reports from './reports'; import contexts from './contexts'; +import compose from './compose'; +import search from './search'; +import media_attachments from './media_attachments'; +import notifications from './notifications'; const reducers = { timelines, @@ -31,13 +35,10 @@ const reducers = { cards, reports, contexts, + compose, + search, + media_attachments, + notifications, }; -export function createReducer(asyncReducers) { - return combineReducers({ - ...reducers, - ...asyncReducers, - }); -} - export default combineReducers(reducers); diff --git a/app/javascript/mastodon/reducers/media_attachments.js b/app/javascript/mastodon/reducers/media_attachments.js index d17d465aa..85bea4f0b 100644 --- a/app/javascript/mastodon/reducers/media_attachments.js +++ b/app/javascript/mastodon/reducers/media_attachments.js @@ -1,4 +1,4 @@ -import { STORE_HYDRATE_LAZY } from '../actions/store'; +import { STORE_HYDRATE } from '../actions/store'; import Immutable from 'immutable'; const initialState = Immutable.Map({ @@ -7,7 +7,7 @@ const initialState = Immutable.Map({ export default function meta(state = initialState, action) { switch(action.type) { - case `${STORE_HYDRATE_LAZY}-media_attachments`: + case STORE_HYDRATE: return state.merge(action.state.get('media_attachments')); default: return state; diff --git a/app/javascript/mastodon/store/configureStore.js b/app/javascript/mastodon/store/configureStore.js index 0fe29f031..1376d4cba 100644 --- a/app/javascript/mastodon/store/configureStore.js +++ b/app/javascript/mastodon/store/configureStore.js @@ -1,36 +1,15 @@ import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; -import appReducer, { createReducer } from '../reducers'; -import { hydrateStoreLazy } from '../actions/store'; -import { hydrateAction } from '../containers/mastodon'; +import appReducer from '../reducers'; import loadingBarMiddleware from '../middleware/loading_bar'; import errorsMiddleware from '../middleware/errors'; import soundsMiddleware from '../middleware/sounds'; export default function configureStore() { - const store = createStore(appReducer, compose(applyMiddleware( + return createStore(appReducer, compose(applyMiddleware( thunk, loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }), errorsMiddleware(), soundsMiddleware() ), window.devToolsExtension ? window.devToolsExtension() : f => f)); - - store.asyncReducers = { }; - - return store; }; - -export function injectAsyncReducer(store, name, asyncReducer) { - if (!store.asyncReducers[name]) { - // Keep track that we injected this reducer - store.asyncReducers[name] = asyncReducer; - - // Add the current reducer to the store - store.replaceReducer(createReducer(store.asyncReducers)); - - // The state this reducer handles defaults to its initial state (stored inside the reducer) - // But that state may be out of date because of the server-side hydration, so we replay - // the hydration action but only for this reducer (all async reducers must listen for this dynamic action) - store.dispatch(hydrateStoreLazy(name, hydrateAction.state)); - } -} diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 68d346859..580d8fb4d 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -22,19 +22,10 @@ = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag 'features/getting_started', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'features/compose', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'reducers/compose', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'reducers/media_attachments', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'reducers/search', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'features/home_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'features/notifications', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'reducers/notifications', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'features/community_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' - = javascript_pack_tag 'features/public_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' -- cgit From ce3a371eeeaae35e49f1938ede5eb6105c764fbc Mon Sep 17 00:00:00 2001 From: unarist Date: Sun, 9 Jul 2017 20:04:30 +0900 Subject: Fix initial loading of pinned Notifications column (#4126) --- app/javascript/mastodon/features/ui/index.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 8d784dfe0..3baf09b93 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -11,6 +11,7 @@ import { isMobile } from '../../is_mobile'; import { debounce } from 'lodash'; import { uploadCompose } from '../../actions/compose'; import { refreshHomeTimeline } from '../../actions/timelines'; +import { refreshNotifications } from '../../actions/notifications'; import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers'; import UploadArea from './components/upload_area'; import ColumnsAreaContainer from './containers/columns_area_container'; @@ -129,6 +130,7 @@ export default class UI extends React.PureComponent { document.addEventListener('dragend', this.handleDragEnd, false); this.props.dispatch(refreshHomeTimeline()); + this.props.dispatch(refreshNotifications()); } componentWillUnmount () { -- cgit From caf938562ef0d0fdb03bf57f15bbab8d76c5b4c0 Mon Sep 17 00:00:00 2001 From: unarist Date: Sun, 9 Jul 2017 21:52:03 +0900 Subject: Avoid async import if the component is previously loaded (#4127) --- app/javascript/mastodon/features/ui/components/bundle.js | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/components/bundle.js b/app/javascript/mastodon/features/ui/components/bundle.js index 3eed446fe..72798f690 100644 --- a/app/javascript/mastodon/features/ui/components/bundle.js +++ b/app/javascript/mastodon/features/ui/components/bundle.js @@ -26,6 +26,8 @@ class Bundle extends React.Component { onFetchFail: noop, } + static cache = {} + state = { mod: undefined, forceRender: false, @@ -58,8 +60,17 @@ class Bundle extends React.Component { this.timeout = setTimeout(() => this.setState({ forceRender: true }), renderDelay); } + if (Bundle.cache[fetchComponent.name]) { + const mod = Bundle.cache[fetchComponent.name]; + + this.setState({ mod: mod.default }); + onFetchSuccess(); + return Promise.resolve(); + } + return fetchComponent() .then((mod) => { + Bundle.cache[fetchComponent.name] = mod; this.setState({ mod: mod.default }); onFetchSuccess(); }) -- cgit From fc4c74660b690038ae48264f9d5b0230df58acc4 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sun, 9 Jul 2017 15:02:26 +0200 Subject: Swipeable views (#4105) * feat: Replace react-swipeable with react-swipeable-views * fix: iOS 9 --- .../mastodon/components/column_header.js | 2 +- .../features/ui/components/column_loading.js | 10 ++++- .../features/ui/components/columns_area.js | 48 +++++++++++++--------- .../mastodon/features/ui/components/media_modal.js | 18 +++++--- .../features/ui/components/onboarding_modal.js | 40 +++++++----------- .../mastodon/features/ui/components/tabs_bar.js | 26 ++++-------- app/javascript/styles/components.scss | 40 +++++++++++++++++- package.json | 2 +- yarn.lock | 46 +++++++++++++++++---- 9 files changed, 150 insertions(+), 82 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index ec9379320..5b2a4d84c 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -10,7 +10,7 @@ export default class ColumnHeader extends React.PureComponent { }; static propTypes = { - title: PropTypes.string.isRequired, + title: PropTypes.node.isRequired, icon: PropTypes.string.isRequired, active: PropTypes.bool, multiColumn: PropTypes.bool, diff --git a/app/javascript/mastodon/features/ui/components/column_loading.js b/app/javascript/mastodon/features/ui/components/column_loading.js index 9bb9c14a1..7ecfaf77a 100644 --- a/app/javascript/mastodon/features/ui/components/column_loading.js +++ b/app/javascript/mastodon/features/ui/components/column_loading.js @@ -1,13 +1,19 @@ import React from 'react'; +import PropTypes from 'prop-types'; import Column from '../../../components/column'; import ColumnHeader from '../../../components/column_header'; -const ColumnLoading = () => ( +const ColumnLoading = ({ title = '', icon = ' ' }) => ( - +
); +ColumnLoading.propTypes = { + title: PropTypes.node, + icon: PropTypes.string, +}; + export default ColumnLoading; diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index 5fa27599f..9ff913774 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -3,8 +3,8 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import ReactSwipeable from 'react-swipeable'; -import { getPreviousLink, getNextLink } from './tabs_bar'; +import ReactSwipeableViews from 'react-swipeable-views'; +import { links, getIndex, getLink } from './tabs_bar'; import BundleContainer from '../containers/bundle_container'; import ColumnLoading from './column_loading'; @@ -32,21 +32,29 @@ export default class ColumnsArea extends ImmutablePureComponent { children: PropTypes.node, }; - handleRightSwipe = () => { - const previousLink = getPreviousLink(this.context.router.history.location.pathname); - - if (previousLink) { - this.context.router.history.push(previousLink); - } + handleSwipe = (index) => { + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + this.context.router.history.push(getLink(index)); + }); + }); } - handleLeftSwipe = () => { - const previousLink = getNextLink(this.context.router.history.location.pathname); + renderView = (link, index) => { + const columnIndex = getIndex(this.context.router.history.location.pathname); + const title = link.props.children[1] && React.cloneElement(link.props.children[1]); + const icon = (link.props.children[0] || link.props.children).props.className.split(' ')[2].split('-')[1]; - if (previousLink) { - this.context.router.history.push(previousLink); - } - }; + const view = (index === columnIndex) ? + React.cloneElement(this.props.children) : + ; + + return ( +
+ {view} +
+ ); + } renderLoading = () => { return ; @@ -59,12 +67,14 @@ export default class ColumnsArea extends ImmutablePureComponent { render () { const { columns, children, singleColumn } = this.props; + const columnIndex = getIndex(this.context.router.history.location.pathname); + if (singleColumn) { - return ( - - {children} - - ); + return columnIndex !== -1 ? ( + + {links.map(this.renderView)} + + ) :
{children}>
; } return ( diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index a5b9dc19f..769e18820 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -1,5 +1,5 @@ import React from 'react'; -import ReactSwipeable from 'react-swipeable'; +import ReactSwipeableViews from 'react-swipeable-views'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import ExtendedVideoPlayer from '../../../components/extended_video_player'; @@ -26,6 +26,10 @@ export default class MediaModal extends ImmutablePureComponent { index: null, }; + handleSwipe = (index) => { + this.setState({ index: (index) % this.props.media.size }); + } + handleNextClick = () => { this.setState({ index: (this.getIndex() + 1) % this.props.media.size }); } @@ -74,10 +78,12 @@ export default class MediaModal extends ImmutablePureComponent { } if (attachment.get('type') === 'image') { - const width = attachment.getIn(['meta', 'original', 'width']) || null; - const height = attachment.getIn(['meta', 'original', 'height']) || null; + content = media.map((image) => { + const width = image.getIn(['meta', 'original', 'width']) || null; + const height = image.getIn(['meta', 'original', 'height']) || null; - content = ; + return ; + }).toArray(); } else if (attachment.get('type') === 'gifv') { content = ; } @@ -88,9 +94,9 @@ export default class MediaModal extends ImmutablePureComponent {
- + {content} - +
{rightNav} diff --git a/app/javascript/mastodon/features/ui/components/onboarding_modal.js b/app/javascript/mastodon/features/ui/components/onboarding_modal.js index b056357a2..189bd8665 100644 --- a/app/javascript/mastodon/features/ui/components/onboarding_modal.js +++ b/app/javascript/mastodon/features/ui/components/onboarding_modal.js @@ -3,11 +3,9 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import ReactSwipeable from 'react-swipeable'; +import ReactSwipeableViews from 'react-swipeable-views'; import classNames from 'classnames'; import Permalink from '../../../components/permalink'; -import TransitionMotion from 'react-motion/lib/TransitionMotion'; -import spring from 'react-motion/lib/spring'; import ComposeForm from '../../compose/components/compose_form'; import Search from '../../compose/components/search'; import NavigationBar from '../../compose/components/navigation_bar'; @@ -227,6 +225,10 @@ export default class OnboardingModal extends React.PureComponent { })); } + handleSwipe = (index) => { + this.setState({ currentIndex: index }); + } + handleKeyUp = ({ key }) => { switch (key) { case 'ArrowLeft': @@ -263,30 +265,18 @@ export default class OnboardingModal extends React.PureComponent { ); - const styles = pages.map((data, i) => ({ - key: `page-${i}`, - data, - style: { - opacity: spring(i === currentIndex ? 1 : 0), - }, - })); - return (
- - {interpolatedStyles => ( - - {interpolatedStyles.map(({ key, data, style }, i) => { - const className = classNames('onboarding-modal__page__wrapper', { - 'onboarding-modal__page__wrapper--active': i === currentIndex, - }); - return ( -
{data}
- ); - })} -
- )} -
+ + {pages.map((page, i) => { + const className = classNames('onboarding-modal__page__wrapper', { + 'onboarding-modal__page__wrapper--active': i === currentIndex, + }); + return ( +
{page}
+ ); + })} +
diff --git a/app/javascript/mastodon/features/ui/components/tabs_bar.js b/app/javascript/mastodon/features/ui/components/tabs_bar.js index c2e6c88b5..b4153ff45 100644 --- a/app/javascript/mastodon/features/ui/components/tabs_bar.js +++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js @@ -2,7 +2,7 @@ import React from 'react'; import NavLink from 'react-router-dom/NavLink'; import { FormattedMessage } from 'react-intl'; -const links = [ +export const links = [ , , , @@ -13,25 +13,13 @@ const links = [ , ]; -export function getPreviousLink (path) { - const index = links.findIndex(link => link.props.to === path); - - if (index > 0) { - return links[index - 1].props.to; - } - - return null; -}; - -export function getNextLink (path) { - const index = links.findIndex(link => link.props.to === path); - - if (index !== -1 && index < links.length - 1) { - return links[index + 1].props.to; - } +export function getIndex (path) { + return links.findIndex(link => link.props.to === path); +} - return null; -}; +export function getLink (index) { + return links[index].props.to; +} export default class TabsBar extends React.Component { diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 66d2715da..397126ec1 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -1266,6 +1266,23 @@ .columns-area { padding: 10px; } + + .react-swipeable-view-container .columns-area { + height: calc(100% - 20px) !important; + } +} + +.react-swipeable-view-container { + &, + .columns-area, + .drawer, + .column { + height: 100%; + } +} + +.react-swipeable-view-container > * { + height: 100%; } .column { @@ -2910,7 +2927,7 @@ button.icon-button.active i.fa-retweet { video { max-width: 80vw; max-height: 80vh; - width: auto; + width: 100%; height: auto; } @@ -2938,7 +2955,26 @@ button.icon-button.active i.fa-retweet { flex-direction: column; } -.onboarding-modal__pager, +.onboarding-modal__pager { + height: 80vh; + width: 80vw; + max-width: 520px; + max-height: 420px; + + .react-swipeable-view-container > div { + width: 100%; + height: 100%; + box-sizing: border-box; + padding: 25px; + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + display: flex; + user-select: text; + } +} + .error-modal__body { height: 80vh; width: 80vw; diff --git a/package.json b/package.json index 2f63d0bbd..94545c47e 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "react-router-dom": "^4.1.1", "react-router-scroll": "ytase/react-router-scroll#build", "react-simple-dropdown": "^3.0.0", - "react-swipeable": "^4.0.1", + "react-swipeable-views": "^0.12.3", "react-textarea-autosize": "^5.0.7", "react-toggle": "^4.0.1", "redis": "^2.7.1", diff --git a/yarn.lock b/yarn.lock index d8ed20343..d4295cb50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1259,7 +1259,7 @@ babel-register@^6.24.1: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@6.x.x, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.5.0, babel-runtime@^6.9.2: +babel-runtime@6.x.x, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.20.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.5.0, babel-runtime@^6.9.2: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" dependencies: @@ -2318,7 +2318,7 @@ doctrine@^2.0.0: esutils "^2.0.2" isarray "^1.0.0" -"dom-helpers@^2.4.0 || ^3.0.0", dom-helpers@^3.0.0: +"dom-helpers@^2.4.0 || ^3.0.0", dom-helpers@^3.0.0, dom-helpers@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a" @@ -3937,7 +3937,7 @@ jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.4: version "1.4.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" -keycode@^2.1.8: +keycode@^2.1.7, keycode@^2.1.8: version "2.1.9" resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.9.tgz#964a23c54e4889405b4861a5c9f0480d45141dfa" @@ -5711,6 +5711,15 @@ react-element-to-jsx-string@^5.0.0: stringify-object "2.4.0" traverse "^0.6.6" +react-event-listener@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.4.5.tgz#e3e895a0970cf14ee8f890113af68197abf3d0b1" + dependencies: + babel-runtime "^6.20.0" + fbjs "^0.8.4" + prop-types "^15.5.4" + warning "^3.0.0" + react-html-attributes@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/react-html-attributes/-/react-html-attributes-1.3.0.tgz#c97896e9cac47ad9c4e6618b835029a826f5d28c" @@ -5875,11 +5884,34 @@ react-style-proptype@^3.0.0: dependencies: prop-types "^15.5.4" -react-swipeable@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-4.0.1.tgz#2cb3a04a52ccebf5361881b30e233dc13ee4b115" +react-swipeable-views-core@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/react-swipeable-views-core/-/react-swipeable-views-core-0.11.1.tgz#61d046799f90725bbf91a0eb3abcab805c774cac" dependencies: - prop-types "^15.5.8" + babel-runtime "^6.23.0" + warning "^3.0.0" + +react-swipeable-views-utils@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/react-swipeable-views-utils/-/react-swipeable-views-utils-0.12.0.tgz#4ff11f20a8da0561f623876d9fd691116e1a6a03" + dependencies: + babel-runtime "^6.23.0" + fbjs "^0.8.4" + keycode "^2.1.7" + prop-types "^15.5.4" + react-event-listener "^0.4.5" + react-swipeable-views-core "^0.11.1" + +react-swipeable-views@^0.12.3: + version "0.12.3" + resolved "https://registry.yarnpkg.com/react-swipeable-views/-/react-swipeable-views-0.12.3.tgz#b0d3f417bcbcd06afda2f8437c15e8360a568744" + dependencies: + babel-runtime "^6.23.0" + dom-helpers "^3.2.1" + prop-types "^15.5.4" + react-swipeable-views-core "^0.11.1" + react-swipeable-views-utils "^0.12.0" + warning "^3.0.0" react-test-renderer@^15.6.1: version "15.6.1" -- cgit From 4122a837fadf8cf59712b5c1790ac0af96bcbc84 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Sun, 9 Jul 2017 18:49:07 +0200 Subject: fix(components/media_modal): Aspect ratio (#4128) * fix(components/media_modal): Aspect ratio * fix: Remove useless style --- app/javascript/mastodon/features/ui/components/image_loader.js | 1 + app/javascript/mastodon/features/ui/components/media_modal.js | 2 +- app/javascript/styles/components.scss | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.js index 5ea55d1d2..aad594380 100644 --- a/app/javascript/mastodon/features/ui/components/image_loader.js +++ b/app/javascript/mastodon/features/ui/components/image_loader.js @@ -133,6 +133,7 @@ export default class ImageLoader extends React.PureComponent { width={width} height={height} ref={this.setCanvasRef} + style={{ opacity: loading ? 1 : 0 }} /> {!loading && ( diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index 769e18820..23f588669 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -94,7 +94,7 @@ export default class MediaModal extends ImmutablePureComponent {
- + {content}
diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 397126ec1..97651b5f4 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -2935,6 +2935,7 @@ button.icon-button.active i.fa-retweet { canvas { display: block; background: url('../images/void.png') repeat; + object-fit: contain; } } -- cgit From 1c6cbdd4e4894cc5c8928fe1d6f45d781049a59c Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Mon, 10 Jul 2017 11:37:10 +0900 Subject: Fix duplication of tag in columns_area.js (#4131) Deleted ">" just a typo. --- app/javascript/mastodon/features/ui/components/columns_area.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index 9ff913774..cbc185a7d 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -74,7 +74,7 @@ export default class ColumnsArea extends ImmutablePureComponent { {links.map(this.renderView)} - ) :
{children}>
; + ) :
{children}
; } return ( -- cgit From 2b9721d1b38319d70bed98e76a0fe1d648780298 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 10 Jul 2017 21:00:32 +0900 Subject: Add setting a always mark media as sensitive (#4136) --- app/controllers/settings/preferences_controller.rb | 1 + app/javascript/mastodon/reducers/compose.js | 7 +++++++ app/lib/user_settings_decorator.rb | 5 +++++ app/models/user.rb | 4 ++++ app/serializers/initial_state_serializer.rb | 1 + app/serializers/rest/credential_account_serializer.rb | 1 + app/views/settings/preferences/show.html.haml | 2 ++ config/locales/simple_form.en.yml | 1 + spec/lib/user_settings_decorator_spec.rb | 7 +++++++ 9 files changed, 29 insertions(+) (limited to 'app/javascript') diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index a15c26031..cac5b0ba8 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -34,6 +34,7 @@ class Settings::PreferencesController < ApplicationController def user_settings_params params.require(:user).permit( :setting_default_privacy, + :setting_default_sensitive, :setting_boost_modal, :setting_delete_modal, :setting_auto_play_gif, diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index d0b47a85c..752377739 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -45,6 +45,7 @@ const initialState = Immutable.Map({ suggestions: Immutable.List(), me: null, default_privacy: 'public', + default_sensitive: false, resetFileKey: Math.floor((Math.random() * 0x10000)), idempotencyKey: null, }); @@ -75,6 +76,8 @@ function clearAll(state) { }; function appendMedia(state, media) { + const prevSize = state.get('media_attachments').size; + return state.withMutations(map => { map.update('media_attachments', list => list.push(media)); map.set('is_uploading', false); @@ -82,6 +85,10 @@ function appendMedia(state, media) { map.update('text', oldText => `${oldText.trim()} ${media.get('text_url')}`); map.set('focusDate', new Date()); map.set('idempotencyKey', uuid()); + + if (prevSize === 0 && state.get('default_sensitive')) { + map.set('sensitive', true); + } }); }; diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index 9c0cb4545..e0e92b19d 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -18,6 +18,7 @@ class UserSettingsDecorator user.settings['notification_emails'] = merged_notification_emails user.settings['interactions'] = merged_interactions user.settings['default_privacy'] = default_privacy_preference + user.settings['default_sensitive'] = default_sensitive_preference user.settings['boost_modal'] = boost_modal_preference user.settings['delete_modal'] = delete_modal_preference user.settings['auto_play_gif'] = auto_play_gif_preference @@ -36,6 +37,10 @@ class UserSettingsDecorator settings['setting_default_privacy'] end + def default_sensitive_preference + boolean_cast_setting 'setting_default_sensitive' + end + def boost_modal_preference boolean_cast_setting 'setting_boost_modal' end diff --git a/app/models/user.rb b/app/models/user.rb index e2bb3d0ed..c80115a08 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -79,6 +79,10 @@ class User < ApplicationRecord settings.default_privacy || (account.locked? ? 'private' : 'public') end + def setting_default_sensitive + settings.default_sensitive + end + def setting_boost_modal settings.boost_modal end diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 84f9e23a6..49ff9e377 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -23,6 +23,7 @@ class InitialStateSerializer < ActiveModel::Serializer { me: object.current_account.id, default_privacy: object.current_account.user.setting_default_privacy, + default_sensitive: object.current_account.user.setting_default_sensitive, } end diff --git a/app/serializers/rest/credential_account_serializer.rb b/app/serializers/rest/credential_account_serializer.rb index 094b831c9..870d8b71f 100644 --- a/app/serializers/rest/credential_account_serializer.rb +++ b/app/serializers/rest/credential_account_serializer.rb @@ -7,6 +7,7 @@ class REST::CredentialAccountSerializer < REST::AccountSerializer user = object.user { privacy: user.setting_default_privacy, + sensitive: user.setting_default_sensitive, note: object.note, } end diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 26fbfdf82..56a261ab6 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -24,6 +24,8 @@ = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), content_tag(:span, I18n.t("statuses.visibilities.#{visibility}_long"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' + = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label + .fields-group = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff| = ff.input :follow, as: :boolean, wrapper: :with_label diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index d8d3b8a6f..fc5ab5ec8 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -37,6 +37,7 @@ en: setting_auto_play_gif: Auto-play animated GIFs setting_boost_modal: Show confirmation dialog before boosting setting_default_privacy: Post privacy + setting_default_sensitive: Always mark media as sensitive setting_delete_modal: Show confirmation dialog before deleting a toot setting_system_font_ui: Use system's default font severity: Severity diff --git a/spec/lib/user_settings_decorator_spec.rb b/spec/lib/user_settings_decorator_spec.rb index e1ba56d97..a67487779 100644 --- a/spec/lib/user_settings_decorator_spec.rb +++ b/spec/lib/user_settings_decorator_spec.rb @@ -28,6 +28,13 @@ describe UserSettingsDecorator do expect(user.settings['default_privacy']).to eq 'public' end + it 'updates the user settings value for sensitive' do + values = { 'setting_default_sensitive' => '1' } + + settings.update(values) + expect(user.settings['default_sensitive']).to eq true + end + it 'updates the user settings value for boost modal' do values = { 'setting_boost_modal' => '1' } -- cgit From 63baab088d734a982d21a5df538db554091188ad Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 10 Jul 2017 21:02:18 +0900 Subject: Fix regular expression for RFC 5646 (regression from #3604) (#4133) --- app/javascript/mastodon/locales/ar.json | 7 ++- app/javascript/mastodon/locales/bg.json | 7 ++- app/javascript/mastodon/locales/ca.json | 7 ++- app/javascript/mastodon/locales/de.json | 7 ++- .../mastodon/locales/defaultMessages.json | 55 +++++++++++++--------- app/javascript/mastodon/locales/en.json | 7 ++- app/javascript/mastodon/locales/eo.json | 7 ++- app/javascript/mastodon/locales/es.json | 7 ++- app/javascript/mastodon/locales/fa.json | 7 ++- app/javascript/mastodon/locales/fi.json | 7 ++- app/javascript/mastodon/locales/fr.json | 7 ++- app/javascript/mastodon/locales/he.json | 7 ++- app/javascript/mastodon/locales/hr.json | 7 ++- app/javascript/mastodon/locales/hu.json | 7 ++- app/javascript/mastodon/locales/id.json | 7 ++- app/javascript/mastodon/locales/io.json | 7 ++- app/javascript/mastodon/locales/it.json | 7 ++- app/javascript/mastodon/locales/ja.json | 7 ++- app/javascript/mastodon/locales/ko.json | 7 ++- app/javascript/mastodon/locales/nl.json | 7 ++- app/javascript/mastodon/locales/no.json | 7 ++- app/javascript/mastodon/locales/oc.json | 7 ++- app/javascript/mastodon/locales/pl.json | 7 ++- app/javascript/mastodon/locales/pt-BR.json | 9 ++-- app/javascript/mastodon/locales/pt.json | 7 ++- app/javascript/mastodon/locales/ru.json | 7 ++- app/javascript/mastodon/locales/th.json | 7 ++- app/javascript/mastodon/locales/tr.json | 7 ++- app/javascript/mastodon/locales/uk.json | 7 ++- .../mastodon/locales/whitelist_zh-HK.json | 1 - app/javascript/mastodon/locales/zh-CN.json | 9 ++-- app/javascript/mastodon/locales/zh-HK.json | 9 ++-- app/javascript/mastodon/locales/zh-TW.json | 9 ++-- config/webpack/translationRunner.js | 2 +- 34 files changed, 221 insertions(+), 62 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index c13bc73d3..6992e7e0f 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -18,6 +18,12 @@ "account.unfollow": "إلغاء المتابعة", "account.unmute": "إلغاء الكتم عن @{name}", "boost_modal.combo": "يمكنك ضغط {combo} لتخطّي هذه في المرّة القادمة", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "الحسابات المحجوبة", "column.community": "الخيط العام المحلي", "column.favourites": "المفضلة", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "لا تقم بإدراجه على الخيوط العامة", "privacy.unlisted.short": "غير مدرج", "reply_indicator.cancel": "إلغاء", - "report.heading": "تقرير جديد", "report.placeholder": "تعليقات إضافية", "report.submit": "إرسال", "report.target": "إبلاغ", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 3b6f228c6..7a56e1446 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -18,6 +18,12 @@ "account.unfollow": "Не следвай", "account.unmute": "Unmute @{name}", "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", "column.favourites": "Favourites", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Отказ", - "report.heading": "New report", "report.placeholder": "Additional comments", "report.submit": "Submit", "report.target": "Reporting", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 8e8c95d56..b2673915a 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -18,6 +18,12 @@ "account.unfollow": "Deixar de seguir", "account.unmute": "Treure silenci de @{name}", "boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Usuaris bloquejats", "column.community": "Línia de temps local", "column.favourites": "Favorits", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "No publicar en línies de temps públiques", "privacy.unlisted.short": "No llistat", "reply_indicator.cancel": "Cancel·lar", - "report.heading": "Nou informe", "report.placeholder": "Comentaris addicionals", "report.submit": "Enviar", "report.target": "Informes", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 55499c0a3..4b62403c3 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -18,6 +18,12 @@ "account.unfollow": "Entfolgen", "account.unmute": "@{name} nicht mehr stummschalten", "boost_modal.combo": "Du kannst {combo} drücken, um dies beim nächsten Mal zu überspringen", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blockierte Benutzer", "column.community": "Lokale Zeitleiste", "column.favourites": "Favoriten", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Nicht in öffentlichen Zeitleisten anzeigen", "privacy.unlisted.short": "Nicht gelistet", "reply_indicator.cancel": "Abbrechen", - "report.heading": "Neue Meldung", "report.placeholder": "Zusätzliche Kommentare", "report.submit": "Absenden", "report.target": "Melden", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index ccf2e6303..88f0f9c30 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -928,27 +928,6 @@ ], "path": "app/javascript/mastodon/features/public_timeline/index.json" }, - { - "descriptors": [ - { - "defaultMessage": "New report", - "id": "report.heading" - }, - { - "defaultMessage": "Additional comments", - "id": "report.placeholder" - }, - { - "defaultMessage": "Submit", - "id": "report.submit" - }, - { - "defaultMessage": "Reporting", - "id": "report.target" - } - ], - "path": "app/javascript/mastodon/features/report/index.json" - }, { "descriptors": [ { @@ -1008,6 +987,40 @@ ], "path": "app/javascript/mastodon/features/ui/components/boost_modal.json" }, + { + "descriptors": [ + { + "defaultMessage": "Network error", + "id": "bundle_column_error.title" + }, + { + "defaultMessage": "Something went wrong while loading this component.", + "id": "bundle_column_error.body" + }, + { + "defaultMessage": "Try again", + "id": "bundle_column_error.retry" + } + ], + "path": "app/javascript/mastodon/features/ui/components/bundle_column_error.json" + }, + { + "descriptors": [ + { + "defaultMessage": "Something went wrong while loading this component.", + "id": "bundle_modal_error.message" + }, + { + "defaultMessage": "Try again", + "id": "bundle_modal_error.retry" + }, + { + "defaultMessage": "Close", + "id": "bundle_modal_error.close" + } + ], + "path": "app/javascript/mastodon/features/ui/components/bundle_modal_error.json" + }, { "descriptors": [ { diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 253db7110..778f33269 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -18,6 +18,12 @@ "account.unfollow": "Unfollow", "account.unmute": "Unmute @{name}", "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", "column.favourites": "Favourites", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Do not post to public timelines", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Cancel", - "report.heading": "Report {target}", "report.placeholder": "Additional comments", "report.submit": "Submit", "report.target": "Reporting {target}", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 330fe831d..2648a6840 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -18,6 +18,12 @@ "account.unfollow": "Malsekvi", "account.unmute": "Unmute @{name}", "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Loka tempolinio", "column.favourites": "Favourites", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Rezigni", - "report.heading": "New report", "report.placeholder": "Additional comments", "report.submit": "Submit", "report.target": "Reporting", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 6469aa6f2..c42930380 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -18,6 +18,12 @@ "account.unfollow": "Dejar de seguir", "account.unmute": "Unmute @{name}", "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Usuarios bloqueados", "column.community": "Historia local", "column.favourites": "Favoritos", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "No mostrar en la historia federada", "privacy.unlisted.short": "Sin federar", "reply_indicator.cancel": "Cancelar", - "report.heading": "New report", "report.placeholder": "Additional comments", "report.submit": "Submit", "report.target": "Reporting", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 3835caab1..c9f1888b5 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -18,6 +18,12 @@ "account.unfollow": "پایان پیگیری", "account.unmute": "باصدا کردن @{name}", "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "کاربران مسدودشده", "column.community": "نوشته‌های محلی", "column.favourites": "پسندیده‌ها", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "عمومی، ولی فهرست نکن", "privacy.unlisted.short": "فهرست‌نشده", "reply_indicator.cancel": "لغو", - "report.heading": "گزارش تازه", "report.placeholder": "توضیح اضافه", "report.submit": "بفرست", "report.target": "گزارش‌دادن", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index dae911799..b836d2f5d 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -18,6 +18,12 @@ "account.unfollow": "Lopeta seuraaminen", "account.unmute": "Unmute @{name}", "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Paikallinen aikajana", "column.favourites": "Favourites", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Peruuta", - "report.heading": "New report", "report.placeholder": "Additional comments", "report.submit": "Submit", "report.target": "Reporting", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index cb7e1b5a7..eaa01638c 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -18,6 +18,12 @@ "account.unfollow": "Ne plus suivre", "account.unmute": "Ne plus masquer", "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Comptes bloqués", "column.community": "Fil public local", "column.favourites": "Favoris", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Ne pas afficher dans les fils publics", "privacy.unlisted.short": "Non-listé", "reply_indicator.cancel": "Annuler", - "report.heading": "Nouveau signalement", "report.placeholder": "Commentaires additionnels", "report.submit": "Envoyer", "report.target": "Signalement", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index db3d00394..98c7ea021 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -18,6 +18,12 @@ "account.unfollow": "הפסקת מעקב", "account.unmute": "הפסקת השתקת @{name}", "boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "חסימות", "column.community": "ציר זמן מקומי", "column.favourites": "חיבובים", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "לא יופיע בפידים הציבוריים המשותפים", "privacy.unlisted.short": "לא לפיד הכללי", "reply_indicator.cancel": "ביטול", - "report.heading": "דווח חדש", "report.placeholder": "הערות נוספות", "report.submit": "שליחה", "report.target": "דיווח", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index f85eb8a3f..fdf5c11c0 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -18,6 +18,12 @@ "account.unfollow": "Prestani slijediti", "account.unmute": "Poništi utišavanje @{name}", "boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blokirani korisnici", "column.community": "Lokalni timeline", "column.favourites": "Favoriti", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Ne prikazuj u javnim timelineovima", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Otkaži", - "report.heading": "Nova prijava", "report.placeholder": "Dodatni komentari", "report.submit": "Pošalji", "report.target": "Prijavljivanje", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 350410c4b..baf762c8d 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -18,6 +18,12 @@ "account.unfollow": "Követés abbahagyása", "account.unmute": "Unmute @{name}", "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", "column.favourites": "Favourites", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Do not show in public timelines", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Mégsem", - "report.heading": "New report", "report.placeholder": "Additional comments", "report.submit": "Submit", "report.target": "Reporting", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 6e9bc5ba9..6f6d688e9 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -18,6 +18,12 @@ "account.unfollow": "Berhenti mengikuti", "account.unmute": "Berhenti membisukan @{name}", "boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Pengguna diblokir", "column.community": "Linimasa Lokal", "column.favourites": "Favorit", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Tidak ditampilkan di linimasa publik", "privacy.unlisted.short": "Tak Terdaftar", "reply_indicator.cancel": "Batal", - "report.heading": "Laporan baru", "report.placeholder": "Komentar tambahan", "report.submit": "Kirim", "report.target": "Melaporkan", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 005dd4f56..25e0adc8a 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -18,6 +18,12 @@ "account.unfollow": "Ne plus sequar", "account.unmute": "Ne plus celar @{name}", "boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blokusita uzeri", "column.community": "Lokala tempolineo", "column.favourites": "Favorati", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Ne montrar en publika tempolinei", "privacy.unlisted.short": "Ne enlistigota", "reply_indicator.cancel": "Nihiligar", - "report.heading": "Nova denunco", "report.placeholder": "Plusa komenti", "report.submit": "Sendar", "report.target": "Denuncante", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 4a5b218e8..4881b0f08 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -18,6 +18,12 @@ "account.unfollow": "Non seguire", "account.unmute": "Non silenziare @{name}", "boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Utenti bloccati", "column.community": "Timeline locale", "column.favourites": "Apprezzati", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Non mostrare sulla timeline pubblica", "privacy.unlisted.short": "Non elencato", "reply_indicator.cancel": "Annulla", - "report.heading": "Nuova segnalazione", "report.placeholder": "Commenti aggiuntivi", "report.submit": "Invia", "report.target": "Invio la segnalazione", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index cb8074b5d..a133e6330 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -18,6 +18,12 @@ "account.unfollow": "フォロー解除", "account.unmute": "ミュート解除", "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "ブロックしたユーザー", "column.community": "ローカルタイムライン", "column.favourites": "お気に入り", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "公開TLで表示しない", "privacy.unlisted.short": "未収載", "reply_indicator.cancel": "キャンセル", - "report.heading": "新規通報", "report.placeholder": "コメント", "report.submit": "通報する", "report.target": "問題のユーザー", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index e88d4a531..5e1aaac85 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -18,6 +18,12 @@ "account.unfollow": "팔로우 해제", "account.unmute": "뮤트 해제", "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "차단 중인 사용자", "column.community": "로컬 타임라인", "column.favourites": "즐겨찾기", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "공개 타임라인에 표시하지 않음", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "취소", - "report.heading": "신고", "report.placeholder": "코멘트", "report.submit": "신고하기", "report.target": "문제가 된 사용자", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 05a9e3a12..479d157f3 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -18,6 +18,12 @@ "account.unfollow": "Ontvolgen", "account.unmute": "@{name} niet meer negeren", "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Geblokkeerde gebruikers", "column.community": "Lokale tijdlijn", "column.favourites": "Favorieten", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Niet op openbare tijdlijnen tonen", "privacy.unlisted.short": "Minder openbaar", "reply_indicator.cancel": "Annuleren", - "report.heading": "Rapporteren", "report.placeholder": "Extra opmerkingen", "report.submit": "Verzenden", "report.target": "Rapporteren van", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index a3c956279..4bbf14938 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -18,6 +18,12 @@ "account.unfollow": "Avfølg", "account.unmute": "Avdemp @{name}", "boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blokkerte brukere", "column.community": "Lokal tidslinje", "column.favourites": "Likt", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Ikke vis i offentlige tidslinjer", "privacy.unlisted.short": "Uoppført", "reply_indicator.cancel": "Avbryt", - "report.heading": "Ny rapport", "report.placeholder": "Tilleggskommentarer", "report.submit": "Send inn", "report.target": "Rapporterer", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 5f226bc70..2c119ef41 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -18,6 +18,12 @@ "account.unfollow": "Quitar de sègre", "account.unmute": "Quitar de rescondre @{name}", "boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Personas blocadas", "column.community": "Flux d’actualitat public local", "column.favourites": "Favorits", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Mostrar pas dins los fluxes publics", "privacy.unlisted.short": "Pas-listat", "reply_indicator.cancel": "Anullar", - "report.heading": "Senhalar {target}", "report.placeholder": "Comentaris addicionals", "report.submit": "Mandar", "report.target": "Senhalar {target}", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 2a69824ee..b547a1737 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -18,6 +18,12 @@ "account.unfollow": "Przestań obserwować", "account.unmute": "Cofnij wyciszenie @{name}", "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Zablokowani użytkownicy", "column.community": "Lokalna oś czasu", "column.favourites": "Ulubione", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Niewidoczne na publicznych osiach czasu", "privacy.unlisted.short": "Niewidoczne", "reply_indicator.cancel": "Anuluj", - "report.heading": "Zgłoś {target}", "report.placeholder": "Dodatkowe komentarze", "report.submit": "Wyślij", "report.target": "Zgłaszanie {target}", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 5e5834a0e..b199a39ce 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -18,6 +18,12 @@ "account.unfollow": "Deixar de seguir", "account.unmute": "Não silenciar @{name}", "boost_modal.combo": "Pode clicar {combo} para não voltar a ver", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Utilizadores Bloqueados", "column.community": "Local", "column.favourites": "Favoritos", @@ -72,7 +78,6 @@ "getting_started.faq": "FAQ", "getting_started.heading": "Primeiros passos", "getting_started.open_source_notice": "Mastodon é software de fonte aberta. Podes contribuir ou repostar problemas no GitHub do projecto: {github}.", - "getting_started.support": "{faq} • {userguide} • {apps}", "getting_started.userguide": "User Guide", "home.column_settings.advanced": "Avançado", "home.column_settings.basic": "Básico", @@ -107,7 +112,6 @@ "notifications.column_settings.reblog": "Partilhas:", "notifications.column_settings.show": "Mostrar nas colunas", "notifications.column_settings.sound": "Reproduzir som", - "notifications.settings": "Parâmetros da listagem de Notificações", "onboarding.done": "Done", "onboarding.next": "Next", "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.", @@ -138,7 +142,6 @@ "privacy.unlisted.long": "Não publicar nos feeds públicos", "privacy.unlisted.short": "Não listar", "reply_indicator.cancel": "Cancelar", - "report.heading": "Nova denúncia", "report.placeholder": "Comentários adicionais", "report.submit": "Enviar", "report.target": "Denunciar", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 4ebfe0c60..b199a39ce 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -18,6 +18,12 @@ "account.unfollow": "Deixar de seguir", "account.unmute": "Não silenciar @{name}", "boost_modal.combo": "Pode clicar {combo} para não voltar a ver", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Utilizadores Bloqueados", "column.community": "Local", "column.favourites": "Favoritos", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Não publicar nos feeds públicos", "privacy.unlisted.short": "Não listar", "reply_indicator.cancel": "Cancelar", - "report.heading": "Nova denúncia", "report.placeholder": "Comentários adicionais", "report.submit": "Enviar", "report.target": "Denunciar", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index f561f0151..f9f48a48d 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -18,6 +18,12 @@ "account.unfollow": "Отписаться", "account.unmute": "Снять глушение", "boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Список блокировки", "column.community": "Локальная лента", "column.favourites": "Понравившееся", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Не показывать в лентах", "privacy.unlisted.short": "Скрытый", "reply_indicator.cancel": "Отмена", - "report.heading": "Новая жалоба", "report.placeholder": "Комментарий", "report.submit": "Отправить", "report.target": "Жалуемся на", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 608d911e9..8a39beacb 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -18,6 +18,12 @@ "account.unfollow": "Unfollow", "account.unmute": "Unmute @{name}", "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", "column.favourites": "Favourites", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Do not post to public timelines", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Cancel", - "report.heading": "New report", "report.placeholder": "Additional comments", "report.submit": "Submit", "report.target": "Reporting", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 7512971f5..203e4a09e 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -18,6 +18,12 @@ "account.unfollow": "Takipten vazgeç", "account.unmute": "Sesi aç @{name}", "boost_modal.combo": "Bir dahaki sefere {combo} tuşuna basabilirsiniz", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Engellenen kullanıcılar", "column.community": "Yerel zaman tüneli", "column.favourites": "Favoriler", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Herkese açık zaman tüneline gönderme", "privacy.unlisted.short": "Listelenmemiş", "reply_indicator.cancel": "İptal", - "report.heading": "Yeni rapor", "report.placeholder": "Ek yorumlar", "report.submit": "Gönder", "report.target": "Raporlama", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index a117c854b..c0f4a8dbb 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -18,6 +18,12 @@ "account.unfollow": "Відписатися", "account.unmute": "Зняти глушення", "boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "Заблоковані користувачі", "column.community": "Локальна стрічка", "column.favourites": "Вподобане", @@ -136,7 +142,6 @@ "privacy.unlisted.long": "Не показувати у публічних стрічках", "privacy.unlisted.short": "Прихований", "reply_indicator.cancel": "Відмінити", - "report.heading": "Нова скарга", "report.placeholder": "Додаткові коментарі", "report.submit": "Відправити", "report.target": "Скаржимося на", diff --git a/app/javascript/mastodon/locales/whitelist_zh-HK.json b/app/javascript/mastodon/locales/whitelist_zh-HK.json index d5fddc3bc..0d4f101c7 100644 --- a/app/javascript/mastodon/locales/whitelist_zh-HK.json +++ b/app/javascript/mastodon/locales/whitelist_zh-HK.json @@ -1,3 +1,2 @@ [ - "getting_started.support" ] diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 68648f2dd..998e1c8da 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -18,6 +18,12 @@ "account.unfollow": "取消关注", "account.unmute": "取消 @{name} 的静音", "boost_modal.combo": "如你想在下次路过时显示,请按{combo},", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "屏蔽用户", "column.community": "本站时间轴", "column.favourites": "赞过的嘟文", @@ -72,7 +78,6 @@ "getting_started.faq": "FAQ", "getting_started.heading": "开始使用", "getting_started.open_source_notice": "Mastodon 是一个开放源码的软件。你可以在官方 GitHub ({github}) 贡献或者回报问题。", - "getting_started.support": "{faq} • {userguide} • {apps}", "getting_started.userguide": "User Guide", "home.column_settings.advanced": "高端", "home.column_settings.basic": "基本", @@ -107,7 +112,6 @@ "notifications.column_settings.reblog": "你的嘟文被转嘟:", "notifications.column_settings.show": "在通知栏显示", "notifications.column_settings.sound": "播放音效", - "notifications.settings": "字段设置", "onboarding.done": "出发!", "onboarding.next": "下一步", "onboarding.page_five.public_timelines": "本站时间轴显示来自 {domain} 的所有人的公共嘟文。 跨站公共时间轴显示 {domain} 上的各位关注的来自所有Mastodon服务器实例上的人发表的公共嘟文。这些就是寻人好去处的公共时间轴啦。", @@ -138,7 +142,6 @@ "privacy.unlisted.long": "公开,但不在公共时间轴显示", "privacy.unlisted.short": "公开", "reply_indicator.cancel": "取消", - "report.heading": "举报", "report.placeholder": "额外消息", "report.submit": "提交", "report.target": "Reporting", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index a9844a625..1079d5429 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -18,6 +18,12 @@ "account.unfollow": "取消關注", "account.unmute": "取消 @{name} 的靜音", "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "封鎖用戶", "column.community": "本站時間軸", "column.favourites": "喜歡的文章", @@ -72,7 +78,6 @@ "getting_started.faq": "常見問題", "getting_started.heading": "開始使用", "getting_started.open_source_notice": "Mastodon(萬象)是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。", - "getting_started.support": "{faq} • {userguide} • {apps}", "getting_started.userguide": "使用指南", "home.column_settings.advanced": "進階", "home.column_settings.basic": "基本", @@ -107,7 +112,6 @@ "notifications.column_settings.reblog": "轉推你的文章:", "notifications.column_settings.show": "在通知欄顯示", "notifications.column_settings.sound": "播放音效", - "notifications.settings": "欄位設定", "onboarding.done": "開始使用", "onboarding.next": "繼續", "onboarding.page_five.public_timelines": "「本站時間軸」顯示在 {domain} 各用戶的公開文章。「跨站時間軸」顯示在 {domain} 各人關注的所有用戶(包括其他服務站)的公開文章。這些都是「公共時間軸」,是認識新朋友的好地方。", @@ -138,7 +142,6 @@ "privacy.unlisted.long": "公開,但不在公共時間軸顯示", "privacy.unlisted.short": "公開", "reply_indicator.cancel": "取消", - "report.heading": "舉報", "report.placeholder": "額外訊息", "report.submit": "提交", "report.target": "舉報", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 5497becf0..6240b8879 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -18,6 +18,12 @@ "account.unfollow": "取消關注", "account.unmute": "不再消音 @{name}", "boost_modal.combo": "下次你可以按 {combo} 來跳過", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", "column.blocks": "封鎖的使用者", "column.community": "本地時間軸", "column.favourites": "最愛", @@ -72,7 +78,6 @@ "getting_started.faq": "FAQ", "getting_started.heading": "馬上開始", "getting_started.open_source_notice": "Mastodon 是開源軟體。你可以在 GitHub {github} 上做出貢獻或是回報問題。", - "getting_started.support": "{faq} • {userguide} • {apps}", "getting_started.userguide": "使用者指南", "home.column_settings.advanced": "進階", "home.column_settings.basic": "基本", @@ -107,7 +112,6 @@ "notifications.column_settings.reblog": "轉推:", "notifications.column_settings.show": "顯示在欄位中", "notifications.column_settings.sound": "播放音效", - "notifications.settings": "欄位設定", "onboarding.done": "完成", "onboarding.next": "下一步", "onboarding.page_five.public_timelines": "本地時間軸顯示 {domain} 上所有人的公開貼文。聯盟時間軸顯示 {domain} 上所有人關注的公開貼文。這就是公開時間軸,發現新朋友的好地方。", @@ -138,7 +142,6 @@ "privacy.unlisted.long": "不要貼到公開時間軸", "privacy.unlisted.short": "不列出來", "reply_indicator.cancel": "取消", - "report.heading": "新的通報", "report.placeholder": "更多訊息", "report.submit": "送出", "report.target": "通報中", diff --git a/config/webpack/translationRunner.js b/config/webpack/translationRunner.js index 097099b48..d616c7839 100644 --- a/config/webpack/translationRunner.js +++ b/config/webpack/translationRunner.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); const { default: manageTranslations } = require('react-intl-translations-manager'); -const RFC5646_REGEXP = /^[a-z]{2,3}(?:|[A-Z]+)$/; +const RFC5646_REGEXP = /^[a-z]{2,3}(?:|-[A-Z]+)$/; const rootDirectory = path.resolve(__dirname, '..', '..'); const translationsDirectory = path.resolve(rootDirectory, 'app', 'javascript', 'mastodon', 'locales'); -- cgit From 31490e0d6ce5f967146da7f7bafa73a1ecb65fb9 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 10 Jul 2017 23:32:17 +0900 Subject: Add Japanese translations (#4140) * Add Japanese translations for #3879 * Add Japanese translations for #4033 * Add Japanese translations for #4136 --- app/javascript/mastodon/locales/ja.json | 12 ++++++------ config/locales/simple_form.ja.yml | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index a133e6330..f62072852 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -18,12 +18,12 @@ "account.unfollow": "フォロー解除", "account.unmute": "ミュート解除", "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。", - "bundle_column_error.body": "Something went wrong while loading this component.", - "bundle_column_error.retry": "Try again", - "bundle_column_error.title": "Network error", - "bundle_modal_error.close": "Close", - "bundle_modal_error.message": "Something went wrong while loading this component.", - "bundle_modal_error.retry": "Try again", + "bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。", + "bundle_column_error.retry": "再試行", + "bundle_column_error.title": "ネットワークエラー", + "bundle_modal_error.close": "閉じる", + "bundle_modal_error.message": "コンポーネントの読み込み中に問題が発生しました。", + "bundle_modal_error.retry": "再試行", "column.blocks": "ブロックしたユーザー", "column.community": "ローカルタイムライン", "column.favourites": "お気に入り", diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index b9f11d7b3..9342398a8 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -34,7 +34,9 @@ ja: setting_auto_play_gif: アニメーションGIFを自動再生する setting_boost_modal: ブーストする前に確認ダイアログを表示する setting_default_privacy: 投稿の公開範囲 + setting_default_sensitive: メディアを常に閲覧注意としてマークする setting_delete_modal: トゥートを削除する前に確認ダイアログを表示する + setting_system_font_ui: システムのデフォルトフォントを使う severity: 重大性 type: インポートする項目 username: ユーザー名 -- cgit From 7f9a353b94f628396c445ae07ed1508c91b93fcb Mon Sep 17 00:00:00 2001 From: m4sk1n Date: Mon, 10 Jul 2017 18:04:06 +0200 Subject: i18n: @63baab0 (pl) (#4141) --- app/javascript/mastodon/locales/pl.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index b547a1737..ac63ec40f 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -18,12 +18,12 @@ "account.unfollow": "Przestań obserwować", "account.unmute": "Cofnij wyciszenie @{name}", "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem", - "bundle_column_error.body": "Something went wrong while loading this component.", - "bundle_column_error.retry": "Try again", - "bundle_column_error.title": "Network error", - "bundle_modal_error.close": "Close", - "bundle_modal_error.message": "Something went wrong while loading this component.", - "bundle_modal_error.retry": "Try again", + "bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.", + "bundle_column_error.retry": "Spróbuj ponownie", + "bundle_column_error.title": "Błąd sieci", + "bundle_modal_error.close": "Zamknij", + "bundle_modal_error.message": "Coś poszło nie tak podczas ładowania tego składnika.", + "bundle_modal_error.retry": "Spróbuj ponownie", "column.blocks": "Zablokowani użytkownicy", "column.community": "Lokalna oś czasu", "column.favourites": "Ulubione", -- cgit From 7a889a8e125a03e109b225cd83b0abcbdc76d95b Mon Sep 17 00:00:00 2001 From: STJrInuyasha Date: Mon, 10 Jul 2017 09:05:06 -0700 Subject: Remote following success page (#4129) * Added a success page to remote following Includes follow-through links to web (the old redirect target) and back to the remote user's profile * Use Account.new in spec instead of a fake with only id (fixes spec) * Fabricate(:account) over Account.new * Remove self from the success text (and all HTML with it) --- app/controllers/authorize_follows_controller.rb | 2 +- app/javascript/styles/forms.scss | 9 +++++++++ app/views/authorize_follows/success.html.haml | 16 ++++++++++++++++ config/locales/en.yml | 6 ++++++ spec/controllers/authorize_follows_controller_spec.rb | 4 ++-- 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 app/views/authorize_follows/success.html.haml (limited to 'app/javascript') diff --git a/app/controllers/authorize_follows_controller.rb b/app/controllers/authorize_follows_controller.rb index da4ef022a..dccd1c209 100644 --- a/app/controllers/authorize_follows_controller.rb +++ b/app/controllers/authorize_follows_controller.rb @@ -15,7 +15,7 @@ class AuthorizeFollowsController < ApplicationController if @account.nil? render :error else - redirect_to web_url("accounts/#{@account.id}") + render :success end rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError render :error diff --git a/app/javascript/styles/forms.scss b/app/javascript/styles/forms.scss index 7a181f36b..414dc4fe8 100644 --- a/app/javascript/styles/forms.scss +++ b/app/javascript/styles/forms.scss @@ -375,3 +375,12 @@ code { width: 50%; } } + +.post-follow-actions { + text-align: center; + color: $ui-primary-color; + + div { + margin-bottom: 4px; + } +} diff --git a/app/views/authorize_follows/success.html.haml b/app/views/authorize_follows/success.html.haml new file mode 100644 index 000000000..f0b495689 --- /dev/null +++ b/app/views/authorize_follows/success.html.haml @@ -0,0 +1,16 @@ +- content_for :page_title do + = t('authorize_follow.title', acct: @account.acct) + +.form-container + .follow-prompt + - if @account.locked? + %h2= t('authorize_follow.follow_request') + - else + %h2= t('authorize_follow.following') + + = render 'card', account: @account + + .post-follow-actions + %div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@account.id}"), class: 'button button--block' + %div= link_to t('authorize_follow.post_follow.return'), @account.url, class: 'button button--block' + %div= t('authorize_follow.post_follow.close') diff --git a/config/locales/en.yml b/config/locales/en.yml index 60e192491..8bb893d1c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -221,6 +221,12 @@ en: authorize_follow: error: Unfortunately, there was an error looking up the remote account follow: Follow + following: 'Success! You are now following:' + follow_request: 'You have sent a follow request to:' + post_follow: + web: Go to web + return: Return to the user's profile + close: Or, you can just close this window. prompt_html: 'You (%{self}) have requested to follow:' title: Follow %{acct} datetime: diff --git a/spec/controllers/authorize_follows_controller_spec.rb b/spec/controllers/authorize_follows_controller_spec.rb index b801aa661..26e46a23c 100644 --- a/spec/controllers/authorize_follows_controller_spec.rb +++ b/spec/controllers/authorize_follows_controller_spec.rb @@ -94,7 +94,7 @@ describe AuthorizeFollowsController do end it 'follows account when found' do - target_account = double(id: '123') + target_account = Fabricate(:account) result_account = double(target_account: target_account) service = double allow(FollowService).to receive(:new).and_return(service) @@ -103,7 +103,7 @@ describe AuthorizeFollowsController do post :create, params: { acct: 'acct:user@hostname' } expect(service).to have_received(:call).with(account, 'user@hostname') - expect(response).to redirect_to(web_url('accounts/123')) + expect(response).to render_template(:success) end end end -- cgit From cc68d1945b42459a787e0ce216e8f49393eeb197 Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Tue, 11 Jul 2017 01:00:14 +0200 Subject: refactor: Rewrite immutablejs import statements using destructuring (#4147) --- app/javascript/mastodon/actions/notifications.js | 4 +-- app/javascript/mastodon/actions/store.js | 6 ++-- app/javascript/mastodon/actions/timelines.js | 10 +++---- .../mastodon/features/account_timeline/index.js | 4 +-- .../mastodon/features/notifications/index.js | 4 +-- .../containers/status_check_box_container.js | 4 +-- .../features/ui/components/onboarding_modal.js | 4 +-- .../features/ui/components/report_modal.js | 4 +-- .../ui/containers/status_list_container.js | 6 ++-- app/javascript/mastodon/reducers/accounts.js | 6 ++-- .../mastodon/reducers/accounts_counters.js | 8 +++--- app/javascript/mastodon/reducers/alerts.js | 6 ++-- app/javascript/mastodon/reducers/cards.js | 6 ++-- app/javascript/mastodon/reducers/compose.js | 18 ++++++------ app/javascript/mastodon/reducers/contexts.js | 18 ++++++------ .../mastodon/reducers/media_attachments.js | 4 +-- app/javascript/mastodon/reducers/meta.js | 4 +-- app/javascript/mastodon/reducers/notifications.js | 14 +++++----- app/javascript/mastodon/reducers/relationships.js | 6 ++-- app/javascript/mastodon/reducers/reports.js | 16 +++++------ app/javascript/mastodon/reducers/search.js | 16 +++++------ app/javascript/mastodon/reducers/settings.js | 30 ++++++++++---------- app/javascript/mastodon/reducers/status_lists.js | 10 +++---- app/javascript/mastodon/reducers/statuses.js | 6 ++-- app/javascript/mastodon/reducers/timelines.js | 24 ++++++++-------- app/javascript/mastodon/reducers/user_lists.js | 32 +++++++++++----------- app/javascript/mastodon/selectors/index.js | 6 ++-- spec/javascript/components/display_name.test.js | 6 ++-- 28 files changed, 141 insertions(+), 141 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index cda636139..c7d248122 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -1,5 +1,5 @@ import api, { getLinks } from '../api'; -import Immutable from 'immutable'; +import { List as ImmutableList } from 'immutable'; import IntlMessageFormat from 'intl-messageformat'; import { fetchRelationships } from './accounts'; import { defineMessages } from 'react-intl'; @@ -124,7 +124,7 @@ export function refreshNotificationsFail(error, skipLoading) { export function expandNotifications() { return (dispatch, getState) => { - const items = getState().getIn(['notifications', 'items'], Immutable.List()); + const items = getState().getIn(['notifications', 'items'], ImmutableList()); if (getState().getIn(['notifications', 'isLoading']) || items.size === 0) { return; diff --git a/app/javascript/mastodon/actions/store.js b/app/javascript/mastodon/actions/store.js index efdb0771a..0597d265e 100644 --- a/app/javascript/mastodon/actions/store.js +++ b/app/javascript/mastodon/actions/store.js @@ -1,11 +1,11 @@ -import Immutable from 'immutable'; +import { Iterable, fromJS } from 'immutable'; export const STORE_HYDRATE = 'STORE_HYDRATE'; export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY'; const convertState = rawState => - Immutable.fromJS(rawState, (k, v) => - Immutable.Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x => + fromJS(rawState, (k, v) => + Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x => Number.isNaN(x * 1) ? x : x * 1)); export function hydrateStore(rawState) { diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index cb4410eba..dd14cb1cd 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -1,5 +1,5 @@ import api, { getLinks } from '../api'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; export const TIMELINE_UPDATE = 'TIMELINE_UPDATE'; export const TIMELINE_DELETE = 'TIMELINE_DELETE'; @@ -66,13 +66,13 @@ export function refreshTimelineRequest(timeline, skipLoading) { export function refreshTimeline(timelineId, path, params = {}) { return function (dispatch, getState) { - const timeline = getState().getIn(['timelines', timelineId], Immutable.Map()); + const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); if (timeline.get('isLoading') || timeline.get('online')) { return; } - const ids = timeline.get('items', Immutable.List()); + const ids = timeline.get('items', ImmutableList()); const newestId = ids.size > 0 ? ids.first() : null; let skipLoading = timeline.get('loaded'); @@ -111,8 +111,8 @@ export function refreshTimelineFail(timeline, error, skipLoading) { export function expandTimeline(timelineId, path, params = {}) { return (dispatch, getState) => { - const timeline = getState().getIn(['timelines', timelineId], Immutable.Map()); - const ids = timeline.get('items', Immutable.List()); + const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); + const ids = timeline.get('items', ImmutableList()); if (timeline.get('isLoading') || ids.size === 0) { return; diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js index 955d0000e..3c8b63114 100644 --- a/app/javascript/mastodon/features/account_timeline/index.js +++ b/app/javascript/mastodon/features/account_timeline/index.js @@ -9,11 +9,11 @@ import LoadingIndicator from '../../components/loading_indicator'; import Column from '../ui/components/column'; import HeaderContainer from './containers/header_container'; import ColumnBackButton from '../../components/column_back_button'; -import Immutable from 'immutable'; +import { List as ImmutableList } from 'immutable'; import ImmutablePureComponent from 'react-immutable-pure-component'; const mapStateToProps = (state, props) => ({ - statusIds: state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'items'], Immutable.List()), + statusIds: state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'items'], ImmutableList()), isLoading: state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'isLoading']), hasMore: !!state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'next']), me: state.getIn(['meta', 'me']), diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 2f545fa4a..c5853d3ba 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -11,7 +11,7 @@ import { ScrollContainer } from 'react-router-scroll'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import ColumnSettingsContainer from './containers/column_settings_container'; import { createSelector } from 'reselect'; -import Immutable from 'immutable'; +import { List as ImmutableList } from 'immutable'; import LoadMore from '../../components/load_more'; import { debounce } from 'lodash'; @@ -20,7 +20,7 @@ const messages = defineMessages({ }); const getNotifications = createSelector([ - state => Immutable.List(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()), + state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()), state => state.getIn(['notifications', 'items']), ], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type')))); diff --git a/app/javascript/mastodon/features/report/containers/status_check_box_container.js b/app/javascript/mastodon/features/report/containers/status_check_box_container.js index 8997718a2..48cd0319b 100644 --- a/app/javascript/mastodon/features/report/containers/status_check_box_container.js +++ b/app/javascript/mastodon/features/report/containers/status_check_box_container.js @@ -1,11 +1,11 @@ import { connect } from 'react-redux'; import StatusCheckBox from '../components/status_check_box'; import { toggleStatusReport } from '../../../actions/reports'; -import Immutable from 'immutable'; +import { Set as ImmutableSet } from 'immutable'; const mapStateToProps = (state, { id }) => ({ status: state.getIn(['statuses', id]), - checked: state.getIn(['reports', 'new', 'status_ids'], Immutable.Set()).includes(id), + checked: state.getIn(['reports', 'new', 'status_ids'], ImmutableSet()).includes(id), }); const mapDispatchToProps = (dispatch, { id }) => ({ diff --git a/app/javascript/mastodon/features/ui/components/onboarding_modal.js b/app/javascript/mastodon/features/ui/components/onboarding_modal.js index 189bd8665..3d59785e2 100644 --- a/app/javascript/mastodon/features/ui/components/onboarding_modal.js +++ b/app/javascript/mastodon/features/ui/components/onboarding_modal.js @@ -10,7 +10,7 @@ import ComposeForm from '../../compose/components/compose_form'; import Search from '../../compose/components/search'; import NavigationBar from '../../compose/components/navigation_bar'; import ColumnHeader from './column_header'; -import Immutable from 'immutable'; +import { List as ImmutableList } from 'immutable'; const noop = () => { }; @@ -48,7 +48,7 @@ const PageTwo = ({ me }) => (
{ isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']), account: getAccount(state, accountId), comment: state.getIn(['reports', 'new', 'comment']), - statusIds: Immutable.OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])), + statusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])), }; }; diff --git a/app/javascript/mastodon/features/ui/containers/status_list_container.js b/app/javascript/mastodon/features/ui/containers/status_list_container.js index 45ad6209b..1b2e1056a 100644 --- a/app/javascript/mastodon/features/ui/containers/status_list_container.js +++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js @@ -1,13 +1,13 @@ import { connect } from 'react-redux'; import StatusList from '../../../components/status_list'; import { scrollTopTimeline } from '../../../actions/timelines'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { createSelector } from 'reselect'; import { debounce } from 'lodash'; const makeGetStatusIds = () => createSelector([ - (state, { type }) => state.getIn(['settings', type], Immutable.Map()), - (state, { type }) => state.getIn(['timelines', type, 'items'], Immutable.List()), + (state, { type }) => state.getIn(['settings', type], ImmutableMap()), + (state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()), (state) => state.get('statuses'), (state) => state.getIn(['meta', 'me']), ], (columnSettings, statusIds, statuses, me) => statusIds.filter(id => { diff --git a/app/javascript/mastodon/reducers/accounts.js b/app/javascript/mastodon/reducers/accounts.js index 7b7074317..4d7c3adc9 100644 --- a/app/javascript/mastodon/reducers/accounts.js +++ b/app/javascript/mastodon/reducers/accounts.js @@ -44,7 +44,7 @@ import { FAVOURITED_STATUSES_EXPAND_SUCCESS, } from '../actions/favourites'; import { STORE_HYDRATE } from '../actions/store'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, fromJS } from 'immutable'; const normalizeAccount = (state, account) => { account = { ...account }; @@ -53,7 +53,7 @@ const normalizeAccount = (state, account) => { delete account.following_count; delete account.statuses_count; - return state.set(account.id, Immutable.fromJS(account)); + return state.set(account.id, fromJS(account)); }; const normalizeAccounts = (state, accounts) => { @@ -82,7 +82,7 @@ const normalizeAccountsFromStatuses = (state, statuses) => { return state; }; -const initialState = Immutable.Map(); +const initialState = ImmutableMap(); export default function accounts(state = initialState, action) { switch(action.type) { diff --git a/app/javascript/mastodon/reducers/accounts_counters.js b/app/javascript/mastodon/reducers/accounts_counters.js index eb8a4f83d..4423e1b50 100644 --- a/app/javascript/mastodon/reducers/accounts_counters.js +++ b/app/javascript/mastodon/reducers/accounts_counters.js @@ -46,9 +46,9 @@ import { FAVOURITED_STATUSES_EXPAND_SUCCESS, } from '../actions/favourites'; import { STORE_HYDRATE } from '../actions/store'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, fromJS } from 'immutable'; -const normalizeAccount = (state, account) => state.set(account.id, Immutable.fromJS({ +const normalizeAccount = (state, account) => state.set(account.id, fromJS({ followers_count: account.followers_count, following_count: account.following_count, statuses_count: account.statuses_count, @@ -80,12 +80,12 @@ const normalizeAccountsFromStatuses = (state, statuses) => { return state; }; -const initialState = Immutable.Map(); +const initialState = ImmutableMap(); export default function accountsCounters(state = initialState, action) { switch(action.type) { case STORE_HYDRATE: - return state.merge(action.state.get('accounts').map(item => Immutable.fromJS({ + return state.merge(action.state.get('accounts').map(item => fromJS({ followers_count: item.get('followers_count'), following_count: item.get('following_count'), statuses_count: item.get('statuses_count'), diff --git a/app/javascript/mastodon/reducers/alerts.js b/app/javascript/mastodon/reducers/alerts.js index aaea9775f..089d920c3 100644 --- a/app/javascript/mastodon/reducers/alerts.js +++ b/app/javascript/mastodon/reducers/alerts.js @@ -3,14 +3,14 @@ import { ALERT_DISMISS, ALERT_CLEAR, } from '../actions/alerts'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; -const initialState = Immutable.List([]); +const initialState = ImmutableList([]); export default function alerts(state = initialState, action) { switch(action.type) { case ALERT_SHOW: - return state.push(Immutable.Map({ + return state.push(ImmutableMap({ key: state.size > 0 ? state.last().get('key') + 1 : 0, title: action.title, message: action.message, diff --git a/app/javascript/mastodon/reducers/cards.js b/app/javascript/mastodon/reducers/cards.js index 3c9395011..4d86b0d7e 100644 --- a/app/javascript/mastodon/reducers/cards.js +++ b/app/javascript/mastodon/reducers/cards.js @@ -1,13 +1,13 @@ import { STATUS_CARD_FETCH_SUCCESS } from '../actions/cards'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, fromJS } from 'immutable'; -const initialState = Immutable.Map(); +const initialState = ImmutableMap(); export default function cards(state = initialState, action) { switch(action.type) { case STATUS_CARD_FETCH_SUCCESS: - return state.set(action.id, Immutable.fromJS(action.card)); + return state.set(action.id, fromJS(action.card)); default: return state; } diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 752377739..a92b5aa23 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -24,10 +24,10 @@ import { } from '../actions/compose'; import { TIMELINE_DELETE } from '../actions/timelines'; import { STORE_HYDRATE } from '../actions/store'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable'; import uuid from '../uuid'; -const initialState = Immutable.Map({ +const initialState = ImmutableMap({ mounted: false, sensitive: false, spoiler: false, @@ -40,9 +40,9 @@ const initialState = Immutable.Map({ is_submitting: false, is_uploading: false, progress: 0, - media_attachments: Immutable.List(), + media_attachments: ImmutableList(), suggestion_token: null, - suggestions: Immutable.List(), + suggestions: ImmutableList(), me: null, default_privacy: 'public', default_sensitive: false, @@ -51,7 +51,7 @@ const initialState = Immutable.Map({ }); function statusToTextMentions(state, status) { - let set = Immutable.OrderedSet([]); + let set = ImmutableOrderedSet([]); let me = state.get('me'); if (status.getIn(['account', 'id']) !== me) { @@ -111,7 +111,7 @@ const insertSuggestion = (state, position, token, completion) => { return state.withMutations(map => { map.update('text', oldText => `${oldText.slice(0, position)}${completion} ${oldText.slice(position + token.length)}`); map.set('suggestion_token', null); - map.update('suggestions', Immutable.List(), list => list.clear()); + map.update('suggestions', ImmutableList(), list => list.clear()); map.set('focusDate', new Date()); map.set('idempotencyKey', uuid()); }); @@ -206,7 +206,7 @@ export default function compose(state = initialState, action) { map.set('is_uploading', true); }); case COMPOSE_UPLOAD_SUCCESS: - return appendMedia(state, Immutable.fromJS(action.media)); + return appendMedia(state, fromJS(action.media)); case COMPOSE_UPLOAD_FAIL: return state.set('is_uploading', false); case COMPOSE_UPLOAD_UNDO: @@ -219,9 +219,9 @@ export default function compose(state = initialState, action) { .set('focusDate', new Date()) .set('idempotencyKey', uuid()); case COMPOSE_SUGGESTIONS_CLEAR: - return state.update('suggestions', Immutable.List(), list => list.clear()).set('suggestion_token', null); + return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null); case COMPOSE_SUGGESTIONS_READY: - return state.set('suggestions', Immutable.List(action.accounts.map(item => item.id))).set('suggestion_token', action.token); + return state.set('suggestions', ImmutableList(action.accounts.map(item => item.id))).set('suggestion_token', action.token); case COMPOSE_SUGGESTION_SELECT: return insertSuggestion(state, action.position, action.token, action.completion); case TIMELINE_DELETE: diff --git a/app/javascript/mastodon/reducers/contexts.js b/app/javascript/mastodon/reducers/contexts.js index 8a24f5f7a..9bfc09aa7 100644 --- a/app/javascript/mastodon/reducers/contexts.js +++ b/app/javascript/mastodon/reducers/contexts.js @@ -1,10 +1,10 @@ import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses'; import { TIMELINE_DELETE } from '../actions/timelines'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; -const initialState = Immutable.Map({ - ancestors: Immutable.Map(), - descendants: Immutable.Map(), +const initialState = ImmutableMap({ + ancestors: ImmutableMap(), + descendants: ImmutableMap(), }); const normalizeContext = (state, id, ancestors, descendants) => { @@ -18,12 +18,12 @@ const normalizeContext = (state, id, ancestors, descendants) => { }; const deleteFromContexts = (state, id) => { - state.getIn(['descendants', id], Immutable.List()).forEach(descendantId => { - state = state.updateIn(['ancestors', descendantId], Immutable.List(), list => list.filterNot(itemId => itemId === id)); + state.getIn(['descendants', id], ImmutableList()).forEach(descendantId => { + state = state.updateIn(['ancestors', descendantId], ImmutableList(), list => list.filterNot(itemId => itemId === id)); }); - state.getIn(['ancestors', id], Immutable.List()).forEach(ancestorId => { - state = state.updateIn(['descendants', ancestorId], Immutable.List(), list => list.filterNot(itemId => itemId === id)); + state.getIn(['ancestors', id], ImmutableList()).forEach(ancestorId => { + state = state.updateIn(['descendants', ancestorId], ImmutableList(), list => list.filterNot(itemId => itemId === id)); }); state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]); @@ -34,7 +34,7 @@ const deleteFromContexts = (state, id) => { export default function contexts(state = initialState, action) { switch(action.type) { case CONTEXT_FETCH_SUCCESS: - return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants)); + return normalizeContext(state, action.id, fromJS(action.ancestors), fromJS(action.descendants)); case TIMELINE_DELETE: return deleteFromContexts(state, action.id); default: diff --git a/app/javascript/mastodon/reducers/media_attachments.js b/app/javascript/mastodon/reducers/media_attachments.js index 85bea4f0b..24119f628 100644 --- a/app/javascript/mastodon/reducers/media_attachments.js +++ b/app/javascript/mastodon/reducers/media_attachments.js @@ -1,7 +1,7 @@ import { STORE_HYDRATE } from '../actions/store'; -import Immutable from 'immutable'; +import { Map as ImmutableMap } from 'immutable'; -const initialState = Immutable.Map({ +const initialState = ImmutableMap({ accept_content_types: [], }); diff --git a/app/javascript/mastodon/reducers/meta.js b/app/javascript/mastodon/reducers/meta.js index 1551228ec..119ef9d8f 100644 --- a/app/javascript/mastodon/reducers/meta.js +++ b/app/javascript/mastodon/reducers/meta.js @@ -1,7 +1,7 @@ import { STORE_HYDRATE } from '../actions/store'; -import Immutable from 'immutable'; +import { Map as ImmutableMap } from 'immutable'; -const initialState = Immutable.Map({ +const initialState = ImmutableMap({ streaming_api_base_url: null, access_token: null, me: null, diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index 0c1cf5b0f..0063d24e4 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -11,10 +11,10 @@ import { } from '../actions/notifications'; import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts'; import { TIMELINE_DELETE } from '../actions/timelines'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; -const initialState = Immutable.Map({ - items: Immutable.List(), +const initialState = ImmutableMap({ + items: ImmutableList(), next: null, top: true, unread: 0, @@ -22,7 +22,7 @@ const initialState = Immutable.Map({ isLoading: true, }); -const notificationToMap = notification => Immutable.Map({ +const notificationToMap = notification => ImmutableMap({ id: notification.id, type: notification.type, account: notification.account.id, @@ -46,7 +46,7 @@ const normalizeNotification = (state, notification) => { }; const normalizeNotifications = (state, notifications, next) => { - let items = Immutable.List(); + let items = ImmutableList(); const loaded = state.get('loaded'); notifications.forEach((n, i) => { @@ -64,7 +64,7 @@ const normalizeNotifications = (state, notifications, next) => { }; const appendNormalizedNotifications = (state, notifications, next) => { - let items = Immutable.List(); + let items = ImmutableList(); notifications.forEach((n, i) => { items = items.set(i, notificationToMap(n)); @@ -110,7 +110,7 @@ export default function notifications(state = initialState, action) { case ACCOUNT_BLOCK_SUCCESS: return filterNotifications(state, action.relationship); case NOTIFICATIONS_CLEAR: - return state.set('items', Immutable.List()).set('next', null); + return state.set('items', ImmutableList()).set('next', null); case TIMELINE_DELETE: return deleteByStatus(state, action.id); default: diff --git a/app/javascript/mastodon/reducers/relationships.js b/app/javascript/mastodon/reducers/relationships.js index b6607860c..c7b04a668 100644 --- a/app/javascript/mastodon/reducers/relationships.js +++ b/app/javascript/mastodon/reducers/relationships.js @@ -11,9 +11,9 @@ import { DOMAIN_BLOCK_SUCCESS, DOMAIN_UNBLOCK_SUCCESS, } from '../actions/domain_blocks'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, fromJS } from 'immutable'; -const normalizeRelationship = (state, relationship) => state.set(relationship.id, Immutable.fromJS(relationship)); +const normalizeRelationship = (state, relationship) => state.set(relationship.id, fromJS(relationship)); const normalizeRelationships = (state, relationships) => { relationships.forEach(relationship => { @@ -23,7 +23,7 @@ const normalizeRelationships = (state, relationships) => { return state; }; -const initialState = Immutable.Map(); +const initialState = ImmutableMap(); export default function relationships(state = initialState, action) { switch(action.type) { diff --git a/app/javascript/mastodon/reducers/reports.js b/app/javascript/mastodon/reducers/reports.js index ad35eaa05..283c5b6f5 100644 --- a/app/javascript/mastodon/reducers/reports.js +++ b/app/javascript/mastodon/reducers/reports.js @@ -7,13 +7,13 @@ import { REPORT_STATUS_TOGGLE, REPORT_COMMENT_CHANGE, } from '../actions/reports'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable'; -const initialState = Immutable.Map({ - new: Immutable.Map({ +const initialState = ImmutableMap({ + new: ImmutableMap({ isSubmitting: false, account_id: null, - status_ids: Immutable.Set(), + status_ids: ImmutableSet(), comment: '', }), }); @@ -26,14 +26,14 @@ export default function reports(state = initialState, action) { map.setIn(['new', 'account_id'], action.account.get('id')); if (state.getIn(['new', 'account_id']) !== action.account.get('id')) { - map.setIn(['new', 'status_ids'], action.status ? Immutable.Set([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : Immutable.Set()); + map.setIn(['new', 'status_ids'], action.status ? ImmutableSet([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : ImmutableSet()); map.setIn(['new', 'comment'], ''); } else { - map.updateIn(['new', 'status_ids'], Immutable.Set(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id')))); + map.updateIn(['new', 'status_ids'], ImmutableSet(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id')))); } }); case REPORT_STATUS_TOGGLE: - return state.updateIn(['new', 'status_ids'], Immutable.Set(), set => { + return state.updateIn(['new', 'status_ids'], ImmutableSet(), set => { if (action.checked) { return set.add(action.statusId); } @@ -50,7 +50,7 @@ export default function reports(state = initialState, action) { case REPORT_SUBMIT_SUCCESS: return state.withMutations(map => { map.setIn(['new', 'account_id'], null); - map.setIn(['new', 'status_ids'], Immutable.Set()); + map.setIn(['new', 'status_ids'], ImmutableSet()); map.setIn(['new', 'comment'], ''); map.setIn(['new', 'isSubmitting'], false); }); diff --git a/app/javascript/mastodon/reducers/search.js b/app/javascript/mastodon/reducers/search.js index 0a3adac05..08d90e4e8 100644 --- a/app/javascript/mastodon/reducers/search.js +++ b/app/javascript/mastodon/reducers/search.js @@ -5,13 +5,13 @@ import { SEARCH_SHOW, } from '../actions/search'; import { COMPOSE_MENTION, COMPOSE_REPLY } from '../actions/compose'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; -const initialState = Immutable.Map({ +const initialState = ImmutableMap({ value: '', submitted: false, hidden: false, - results: Immutable.Map(), + results: ImmutableMap(), }); export default function search(state = initialState, action) { @@ -21,7 +21,7 @@ export default function search(state = initialState, action) { case SEARCH_CLEAR: return state.withMutations(map => { map.set('value', ''); - map.set('results', Immutable.Map()); + map.set('results', ImmutableMap()); map.set('submitted', false); map.set('hidden', false); }); @@ -31,10 +31,10 @@ export default function search(state = initialState, action) { case COMPOSE_MENTION: return state.set('hidden', true); case SEARCH_FETCH_SUCCESS: - return state.set('results', Immutable.Map({ - accounts: Immutable.List(action.results.accounts.map(item => item.id)), - statuses: Immutable.List(action.results.statuses.map(item => item.id)), - hashtags: Immutable.List(action.results.hashtags), + return state.set('results', ImmutableMap({ + accounts: ImmutableList(action.results.accounts.map(item => item.id)), + statuses: ImmutableList(action.results.statuses.map(item => item.id)), + hashtags: ImmutableList(action.results.hashtags), })).set('submitted', true); default: return state; diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index ddad7a4fc..dd2d76ec0 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -1,39 +1,39 @@ import { SETTING_CHANGE } from '../actions/settings'; import { COLUMN_ADD, COLUMN_REMOVE, COLUMN_MOVE } from '../actions/columns'; import { STORE_HYDRATE } from '../actions/store'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, fromJS } from 'immutable'; import uuid from '../uuid'; -const initialState = Immutable.Map({ +const initialState = ImmutableMap({ onboarded: false, - home: Immutable.Map({ - shows: Immutable.Map({ + home: ImmutableMap({ + shows: ImmutableMap({ reblog: true, reply: true, }), - regex: Immutable.Map({ + regex: ImmutableMap({ body: '', }), }), - notifications: Immutable.Map({ - alerts: Immutable.Map({ + notifications: ImmutableMap({ + alerts: ImmutableMap({ follow: true, favourite: true, reblog: true, mention: true, }), - shows: Immutable.Map({ + shows: ImmutableMap({ follow: true, favourite: true, reblog: true, mention: true, }), - sounds: Immutable.Map({ + sounds: ImmutableMap({ follow: true, favourite: true, reblog: true, @@ -41,20 +41,20 @@ const initialState = Immutable.Map({ }), }), - community: Immutable.Map({ - regex: Immutable.Map({ + community: ImmutableMap({ + regex: ImmutableMap({ body: '', }), }), - public: Immutable.Map({ - regex: Immutable.Map({ + public: ImmutableMap({ + regex: ImmutableMap({ body: '', }), }), }); -const defaultColumns = Immutable.fromJS([ +const defaultColumns = fromJS([ { id: 'COMPOSE', uuid: uuid(), params: {} }, { id: 'HOME', uuid: uuid(), params: {} }, { id: 'NOTIFICATIONS', uuid: uuid(), params: {} }, @@ -82,7 +82,7 @@ export default function settings(state = initialState, action) { case SETTING_CHANGE: return state.setIn(action.key, action.value); case COLUMN_ADD: - return state.update('columns', list => list.push(Immutable.fromJS({ id: action.id, uuid: uuid(), params: action.params }))); + return state.update('columns', list => list.push(fromJS({ id: action.id, uuid: uuid(), params: action.params }))); case COLUMN_REMOVE: return state.update('columns', list => list.filterNot(item => item.get('uuid') === action.uuid)); case COLUMN_MOVE: diff --git a/app/javascript/mastodon/reducers/status_lists.js b/app/javascript/mastodon/reducers/status_lists.js index 7d00f6d30..bbc973302 100644 --- a/app/javascript/mastodon/reducers/status_lists.js +++ b/app/javascript/mastodon/reducers/status_lists.js @@ -2,13 +2,13 @@ import { FAVOURITED_STATUSES_FETCH_SUCCESS, FAVOURITED_STATUSES_EXPAND_SUCCESS, } from '../actions/favourites'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; -const initialState = Immutable.Map({ - favourites: Immutable.Map({ +const initialState = ImmutableMap({ + favourites: ImmutableMap({ next: null, loaded: false, - items: Immutable.List(), + items: ImmutableList(), }), }); @@ -16,7 +16,7 @@ const normalizeList = (state, listType, statuses, next) => { return state.update(listType, listMap => listMap.withMutations(map => { map.set('next', next); map.set('loaded', true); - map.set('items', Immutable.List(statuses.map(item => item.id))); + map.set('items', ImmutableList(statuses.map(item => item.id))); })); }; diff --git a/app/javascript/mastodon/reducers/statuses.js b/app/javascript/mastodon/reducers/statuses.js index 691135ff7..b1b1d0988 100644 --- a/app/javascript/mastodon/reducers/statuses.js +++ b/app/javascript/mastodon/reducers/statuses.js @@ -33,7 +33,7 @@ import { FAVOURITED_STATUSES_EXPAND_SUCCESS, } from '../actions/favourites'; import { SEARCH_FETCH_SUCCESS } from '../actions/search'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, fromJS } from 'immutable'; const normalizeStatus = (state, status) => { if (!status) { @@ -51,7 +51,7 @@ const normalizeStatus = (state, status) => { const searchContent = [status.spoiler_text, status.content].join(' ').replace(/
/g, '\n').replace(/<\/p>

/g, '\n\n'); normalStatus.search_index = new DOMParser().parseFromString(searchContent, 'text/html').documentElement.textContent; - return state.update(status.id, Immutable.Map(), map => map.mergeDeep(Immutable.fromJS(normalStatus))); + return state.update(status.id, ImmutableMap(), map => map.mergeDeep(fromJS(normalStatus))); }; const normalizeStatuses = (state, statuses) => { @@ -82,7 +82,7 @@ const filterStatuses = (state, relationship) => { return state; }; -const initialState = Immutable.Map(); +const initialState = ImmutableMap(); export default function statuses(state = initialState, action) { switch(action.type) { diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js index 1b738a16a..065e89f96 100644 --- a/app/javascript/mastodon/reducers/timelines.js +++ b/app/javascript/mastodon/reducers/timelines.js @@ -15,25 +15,25 @@ import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS, } from '../actions/accounts'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; -const initialState = Immutable.Map(); +const initialState = ImmutableMap(); -const initialTimeline = Immutable.Map({ +const initialTimeline = ImmutableMap({ unread: 0, online: false, top: true, loaded: false, isLoading: false, next: false, - items: Immutable.List(), + items: ImmutableList(), }); const normalizeTimeline = (state, timeline, statuses, next) => { - const ids = Immutable.List(statuses.map(status => status.get('id'))); + const ids = ImmutableList(statuses.map(status => status.get('id'))); const wasLoaded = state.getIn([timeline, 'loaded']); const hadNext = state.getIn([timeline, 'next']); - const oldIds = state.getIn([timeline, 'items'], Immutable.List()); + const oldIds = state.getIn([timeline, 'items'], ImmutableList()); return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { mMap.set('loaded', true); @@ -44,8 +44,8 @@ const normalizeTimeline = (state, timeline, statuses, next) => { }; const appendNormalizedTimeline = (state, timeline, statuses, next) => { - const ids = Immutable.List(statuses.map(status => status.get('id'))); - const oldIds = state.getIn([timeline, 'items'], Immutable.List()); + const ids = ImmutableList(statuses.map(status => status.get('id'))); + const oldIds = state.getIn([timeline, 'items'], ImmutableList()); return state.update(timeline, initialTimeline, map => map.withMutations(mMap => { mMap.set('isLoading', false); @@ -56,7 +56,7 @@ const appendNormalizedTimeline = (state, timeline, statuses, next) => { const updateTimeline = (state, timeline, status, references) => { const top = state.getIn([timeline, 'top']); - const ids = state.getIn([timeline, 'items'], Immutable.List()); + const ids = state.getIn([timeline, 'items'], ImmutableList()); const includesId = ids.includes(status.get('id')); const unread = state.getIn([timeline, 'unread'], 0); @@ -124,11 +124,11 @@ export default function timelines(state = initialState, action) { case TIMELINE_EXPAND_FAIL: return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false)); case TIMELINE_REFRESH_SUCCESS: - return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.next); + return normalizeTimeline(state, action.timeline, fromJS(action.statuses), action.next); case TIMELINE_EXPAND_SUCCESS: - return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.next); + return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next); case TIMELINE_UPDATE: - return updateTimeline(state, action.timeline, Immutable.fromJS(action.status), action.references); + return updateTimeline(state, action.timeline, fromJS(action.status), action.references); case TIMELINE_DELETE: return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf); case ACCOUNT_BLOCK_SUCCESS: diff --git a/app/javascript/mastodon/reducers/user_lists.js b/app/javascript/mastodon/reducers/user_lists.js index 83bf1be1b..8db18c5dc 100644 --- a/app/javascript/mastodon/reducers/user_lists.js +++ b/app/javascript/mastodon/reducers/user_lists.js @@ -20,22 +20,22 @@ import { MUTES_FETCH_SUCCESS, MUTES_EXPAND_SUCCESS, } from '../actions/mutes'; -import Immutable from 'immutable'; +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; -const initialState = Immutable.Map({ - followers: Immutable.Map(), - following: Immutable.Map(), - reblogged_by: Immutable.Map(), - favourited_by: Immutable.Map(), - follow_requests: Immutable.Map(), - blocks: Immutable.Map(), - mutes: Immutable.Map(), +const initialState = ImmutableMap({ + followers: ImmutableMap(), + following: ImmutableMap(), + reblogged_by: ImmutableMap(), + favourited_by: ImmutableMap(), + follow_requests: ImmutableMap(), + blocks: ImmutableMap(), + mutes: ImmutableMap(), }); const normalizeList = (state, type, id, accounts, next) => { - return state.setIn([type, id], Immutable.Map({ + return state.setIn([type, id], ImmutableMap({ next, - items: Immutable.List(accounts.map(item => item.id)), + items: ImmutableList(accounts.map(item => item.id)), })); }; @@ -56,22 +56,22 @@ export default function userLists(state = initialState, action) { case FOLLOWING_EXPAND_SUCCESS: return appendToList(state, 'following', action.id, action.accounts, action.next); case REBLOGS_FETCH_SUCCESS: - return state.setIn(['reblogged_by', action.id], Immutable.List(action.accounts.map(item => item.id))); + return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id))); case FAVOURITES_FETCH_SUCCESS: - return state.setIn(['favourited_by', action.id], Immutable.List(action.accounts.map(item => item.id))); + return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id))); case FOLLOW_REQUESTS_FETCH_SUCCESS: - return state.setIn(['follow_requests', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next); + return state.setIn(['follow_requests', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next); case FOLLOW_REQUESTS_EXPAND_SUCCESS: return state.updateIn(['follow_requests', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next); case FOLLOW_REQUEST_AUTHORIZE_SUCCESS: case FOLLOW_REQUEST_REJECT_SUCCESS: return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id)); case BLOCKS_FETCH_SUCCESS: - return state.setIn(['blocks', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next); + return state.setIn(['blocks', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next); case BLOCKS_EXPAND_SUCCESS: return state.updateIn(['blocks', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next); case MUTES_FETCH_SUCCESS: - return state.setIn(['mutes', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next); + return state.setIn(['mutes', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next); case MUTES_EXPAND_SUCCESS: return state.updateIn(['mutes', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next); default: diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js index 07d9a2629..d26d1b727 100644 --- a/app/javascript/mastodon/selectors/index.js +++ b/app/javascript/mastodon/selectors/index.js @@ -1,5 +1,5 @@ import { createSelector } from 'reselect'; -import Immutable from 'immutable'; +import { List as ImmutableList } from 'immutable'; const getAccountBase = (state, id) => state.getIn(['accounts', id], null); const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null); @@ -73,10 +73,10 @@ export const makeGetNotification = () => { }; export const getAccountGallery = createSelector([ - (state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], Immutable.List()), + (state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()), state => state.get('statuses'), ], (statusIds, statuses) => { - let medias = Immutable.List(); + let medias = ImmutableList(); statusIds.forEach(statusId => { const status = statuses.get(statusId); diff --git a/spec/javascript/components/display_name.test.js b/spec/javascript/components/display_name.test.js index d6dc7edc0..ad9288d4d 100644 --- a/spec/javascript/components/display_name.test.js +++ b/spec/javascript/components/display_name.test.js @@ -1,12 +1,12 @@ import { expect } from 'chai'; import { render } from 'enzyme'; -import Immutable from 'immutable'; +import { fromJS } from 'immutable'; import React from 'react'; import DisplayName from '../../../app/javascript/mastodon/components/display_name'; describe('', () => { it('renders display name + account name', () => { - const account = Immutable.fromJS({ + const account = fromJS({ username: 'bar', acct: 'bar@baz', display_name: 'Foo', @@ -16,7 +16,7 @@ describe('', () => { }); it('renders the username + account name if display name is empty', () => { - const account = Immutable.fromJS({ + const account = fromJS({ username: 'bar', acct: 'bar@baz', display_name: '', -- cgit From 31366334cb186974fe97234e05fe36d19d33841b Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Tue, 11 Jul 2017 21:36:27 +0900 Subject: Drawer tab according to column (#4135) * Add notifications link to drawer * Remove local and public timeline tab in drawer * Add home --- app/javascript/mastodon/features/compose/index.js | 20 ++++++++++++++++++-- app/javascript/mastodon/locales/defaultMessages.json | 8 ++++++++ app/javascript/styles/components.scss | 3 +-- 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index 747fe4216..6aa5de96c 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -2,6 +2,7 @@ import React from 'react'; import ComposeFormContainer from './containers/compose_form_container'; import NavigationContainer from './containers/navigation_container'; import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import { connect } from 'react-redux'; import { mountCompose, unmountCompose } from '../../actions/compose'; import Link from 'react-router-dom/Link'; @@ -13,6 +14,8 @@ import SearchResultsContainer from './containers/search_results_container'; const messages = defineMessages({ start: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, + home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, + notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' }, public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' }, community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, @@ -20,6 +23,7 @@ const messages = defineMessages({ }); const mapStateToProps = state => ({ + columns: state.getIn(['settings', 'columns']), showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']), }); @@ -29,6 +33,7 @@ export default class Compose extends React.PureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, + columns: ImmutablePropTypes.list.isRequired, multiColumn: PropTypes.bool, showSearch: PropTypes.bool, intl: PropTypes.object.isRequired, @@ -48,11 +53,22 @@ export default class Compose extends React.PureComponent { let header = ''; if (multiColumn) { + const { columns } = this.props; header = (

diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 88f0f9c30..7c1522299 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -635,6 +635,14 @@ "defaultMessage": "Getting started", "id": "getting_started.heading" }, + { + "defaultMessage": "Home", + "id": "tabs_bar.home" + }, + { + "defaultMessage": "Notifications", + "id": "tabs_bar.notifications" + }, { "defaultMessage": "Federated timeline", "id": "navigation_bar.public_timeline" diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 97651b5f4..def69d250 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -1317,8 +1317,7 @@ .drawer__tab { display: block; flex: 1 1 auto; - padding: 15px; - padding-bottom: 13px; + padding: 15px 5px 13px; color: $ui-primary-color; text-decoration: none; text-align: center; -- cgit From 8784bd79d0053cb15775eb078f45e6aca7775d77 Mon Sep 17 00:00:00 2001 From: "Akihiko Odaki (@fn_aki@pawoo.net)" Date: Tue, 11 Jul 2017 22:15:42 +0900 Subject: Require stylesheets in common.js (#4152) Require stylesheets in common.js because stylesheets are shared by the entry points. --- app/javascript/mastodon/main.js | 4 ---- app/javascript/packs/common.js | 5 +++++ app/views/layouts/application.html.haml | 2 +- app/views/layouts/embedded.html.haml | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.js index aca64c075..d7ffa8ea6 100644 --- a/app/javascript/mastodon/main.js +++ b/app/javascript/mastodon/main.js @@ -1,9 +1,5 @@ const perf = require('./performance'); -// import default stylesheet with variables -require('font-awesome/css/font-awesome.css'); -require('mastodon-application-style'); - function onDomContentLoaded(callback) { if (document.readyState !== 'loading') { callback(); diff --git a/app/javascript/packs/common.js b/app/javascript/packs/common.js index 9d63d8f98..a0cb91ae4 100644 --- a/app/javascript/packs/common.js +++ b/app/javascript/packs/common.js @@ -1,2 +1,7 @@ import { start } from 'rails-ujs'; + +// import default stylesheet with variables +require('font-awesome/css/font-awesome.css'); +require('mastodon-application-style'); + start(); diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 580d8fb4d..ef97fb127 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -18,7 +18,7 @@ = ' - ' = title - = stylesheet_pack_tag 'application', media: 'all' + = stylesheet_pack_tag 'common', media: 'all' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag 'features/getting_started', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script' diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index 5680c1ff9..4826f32f7 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -2,7 +2,7 @@ %html{ lang: I18n.locale } %head %meta{ charset: 'utf-8' }/ - = stylesheet_pack_tag 'application', media: 'all' + = stylesheet_pack_tag 'common', media: 'all' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' -- cgit From e19eefe219c46ea9f763d0279029f03c5cf4554f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 11 Jul 2017 15:27:59 +0200 Subject: Redesign the landing page, mount public timeline on it (#4122) * Redesign the landing page, mount public timeline on it * Adjust the standalone mounted component to the lacking of router * Adjust auth layout pages to new design * Fix tests * Standalone public timeline polling every 5 seconds * Remove now obsolete translations * Add responsive design for new landing page * Address reviews * Add floating clouds behind frontpage form * Use access token from public page when available * Fix mentions and hashtags links, cursor on status content in standalone mode * Add footer link to source code * Fix errors on pages that don't embed the component, use classnames * Fix tests * Change anonymous autoPlayGif default to false * When gif autoplay is disabled, hover to play * Add option to hide the timeline preview * Slightly improve alt layout * Add elephant friend to new frontpage * Display "back to mastodon" in place of "login" when logged in on frontpage * Change polling time to 3s --- app/controllers/about_controller.rb | 13 +- app/controllers/admin/settings_controller.rb | 9 +- app/controllers/home_controller.rb | 16 +- .../fonts/montserrat/Montserrat-Medium.ttf | Bin 0 -> 192488 bytes app/javascript/images/cloud2.png | Bin 0 -> 4973 bytes app/javascript/images/cloud3.png | Bin 0 -> 5860 bytes app/javascript/images/cloud4.png | Bin 0 -> 5273 bytes app/javascript/images/elephant-fren.png | Bin 0 -> 40859 bytes app/javascript/images/logo.svg | 2 +- .../mastodon/components/dropdown_menu.js | 19 +- .../mastodon/components/media_gallery.js | 38 +- app/javascript/mastodon/components/permalink.js | 4 +- app/javascript/mastodon/components/status.js | 8 +- .../mastodon/components/status_action_bar.js | 11 +- .../mastodon/components/status_content.js | 17 +- app/javascript/mastodon/components/video_player.js | 22 +- .../mastodon/containers/timeline_container.js | 39 ++ .../features/standalone/public_timeline/index.js | 76 ++++ app/javascript/packs/public.js | 10 + app/javascript/styles/about.scss | 448 ++++++++++++++++++--- app/javascript/styles/basics.scss | 7 +- app/javascript/styles/boost.scss | 4 + app/javascript/styles/components.scss | 32 +- app/javascript/styles/containers.scss | 48 +-- app/javascript/styles/fonts/montserrat.scss | 8 + app/javascript/styles/forms.scss | 39 +- app/presenters/instance_presenter.rb | 1 + app/serializers/initial_state_serializer.rb | 35 +- app/views/about/_features.html.haml | 25 ++ app/views/about/_registration.html.haml | 20 +- app/views/about/show.html.haml | 120 +++--- app/views/admin/settings/edit.html.haml | 43 +- app/views/auth/registrations/new.html.haml | 6 +- app/views/layouts/auth.html.haml | 3 +- config/locales/ar.yml | 13 +- config/locales/bg.yml | 13 +- config/locales/ca.yml | 17 +- config/locales/de.yml | 13 - config/locales/en.yml | 43 +- config/locales/eo.yml | 23 +- config/locales/es.yml | 13 +- config/locales/fa.yml | 13 - config/locales/fi.yml | 13 +- config/locales/fr.yml | 15 +- config/locales/he.yml | 13 - config/locales/hr.yml | 13 +- config/locales/id.yml | 13 - config/locales/io.yml | 13 - config/locales/it.yml | 13 +- config/locales/ja.yml | 15 +- config/locales/ko.yml | 19 +- config/locales/nl.yml | 21 +- config/locales/no.yml | 13 - config/locales/oc.yml | 17 +- config/locales/pl.yml | 19 +- config/locales/pt-BR.yml | 13 - config/locales/pt.yml | 13 - config/locales/ru.yml | 13 - config/locales/th.yml | 13 - config/locales/tr.yml | 13 - config/locales/uk.yml | 13 - config/locales/zh-CN.yml | 13 - config/locales/zh-HK.yml | 13 - config/locales/zh-TW.yml | 13 - config/settings.yml | 1 + lib/tasks/mastodon.rake | 8 +- spec/requests/localization_spec.rb | 8 +- spec/views/about/show.html.haml_spec.rb | 9 +- 68 files changed, 956 insertions(+), 655 deletions(-) create mode 100644 app/javascript/fonts/montserrat/Montserrat-Medium.ttf create mode 100644 app/javascript/images/cloud2.png create mode 100644 app/javascript/images/cloud3.png create mode 100644 app/javascript/images/cloud4.png create mode 100644 app/javascript/images/elephant-fren.png create mode 100644 app/javascript/mastodon/containers/timeline_container.js create mode 100644 app/javascript/mastodon/features/standalone/public_timeline/index.js create mode 100644 app/views/about/_features.html.haml (limited to 'app/javascript') diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index c0addbecc..47690e81e 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -4,7 +4,10 @@ class AboutController < ApplicationController before_action :set_body_classes before_action :set_instance_presenter, only: [:show, :more, :terms] - def show; end + def show + serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) + @initial_state_json = serializable_resource.to_json + end def more; end @@ -15,6 +18,7 @@ class AboutController < ApplicationController def new_user User.new.tap(&:build_account) end + helper_method :new_user def set_instance_presenter @@ -24,4 +28,11 @@ class AboutController < ApplicationController def set_body_classes @body_classes = 'about-body' end + + def initial_state_params + { + settings: {}, + token: current_session&.token, + } + end end diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb index f27a1f4d4..29b590d7a 100644 --- a/app/controllers/admin/settings_controller.rb +++ b/app/controllers/admin/settings_controller.rb @@ -11,8 +11,15 @@ module Admin site_terms open_registrations closed_registrations_message + open_deletion + timeline_preview + ).freeze + + BOOLEAN_SETTINGS = %w( + open_registrations + open_deletion + timeline_preview ).freeze - BOOLEAN_SETTINGS = %w(open_registrations).freeze def edit @settings = Setting.all_as_records diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 218da6906..8a8b9ec76 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -15,12 +15,16 @@ class HomeController < ApplicationController end def set_initial_state_json - state = InitialStatePresenter.new(settings: Web::Setting.find_by(user: current_user)&.data || {}, - current_account: current_account, - token: current_session.token, - admin: Account.find_local(Setting.site_contact_username)) - - serializable_resource = ActiveModelSerializers::SerializableResource.new(state, serializer: InitialStateSerializer) + serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer) @initial_state_json = serializable_resource.to_json end + + def initial_state_params + { + settings: Web::Setting.find_by(user: current_user)&.data || {}, + current_account: current_account, + token: current_session.token, + admin: Account.find_local(Setting.site_contact_username), + } + end end diff --git a/app/javascript/fonts/montserrat/Montserrat-Medium.ttf b/app/javascript/fonts/montserrat/Montserrat-Medium.ttf new file mode 100644 index 000000000..88d70b89c Binary files /dev/null and b/app/javascript/fonts/montserrat/Montserrat-Medium.ttf differ diff --git a/app/javascript/images/cloud2.png b/app/javascript/images/cloud2.png new file mode 100644 index 000000000..f325ca6de Binary files /dev/null and b/app/javascript/images/cloud2.png differ diff --git a/app/javascript/images/cloud3.png b/app/javascript/images/cloud3.png new file mode 100644 index 000000000..ab194d0b8 Binary files /dev/null and b/app/javascript/images/cloud3.png differ diff --git a/app/javascript/images/cloud4.png b/app/javascript/images/cloud4.png new file mode 100644 index 000000000..98323f5a2 Binary files /dev/null and b/app/javascript/images/cloud4.png differ diff --git a/app/javascript/images/elephant-fren.png b/app/javascript/images/elephant-fren.png new file mode 100644 index 000000000..3b64edf08 Binary files /dev/null and b/app/javascript/images/elephant-fren.png differ diff --git a/app/javascript/images/logo.svg b/app/javascript/images/logo.svg index c233db842..16cb3a944 100644 --- a/app/javascript/images/logo.svg +++ b/app/javascript/images/logo.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js index 12e1b44fa..98323b069 100644 --- a/app/javascript/mastodon/components/dropdown_menu.js +++ b/app/javascript/mastodon/components/dropdown_menu.js @@ -14,6 +14,7 @@ export default class DropdownMenu extends React.PureComponent { size: PropTypes.number.isRequired, direction: PropTypes.string, ariaLabel: PropTypes.string, + disabled: PropTypes.bool, }; static defaultProps = { @@ -68,9 +69,19 @@ export default class DropdownMenu extends React.PureComponent { } render () { - const { icon, items, size, direction, ariaLabel } = this.props; - const { expanded } = this.state; + const { icon, items, size, direction, ariaLabel, disabled } = this.props; + const { expanded } = this.state; const directionClass = (direction === 'left') ? 'dropdown__left' : 'dropdown__right'; + const iconStyle = { fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }; + const iconClassname = `fa fa-fw fa-${icon} dropdown__icon`; + + if (disabled) { + return ( +
+ +
+ ); + } const dropdownItems = expanded && (