diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss index 5f465259f1..2b4ba85925 100644 --- a/app/javascript/flavours/glitch/styles/components/accounts.scss +++ b/app/javascript/flavours/glitch/styles/components/accounts.scss @@ -451,10 +451,12 @@ border-bottom: 1px solid lighten($ui-base-color, 8%); cursor: default; display: flex; + flex-shrink: 0; button { background: darken($ui-base-color, 4%); border: 0; + margin: 0; } button, diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.js index 46d972251c..d640033ebb 100644 --- a/app/javascript/mastodon/features/public_timeline/index.js +++ b/app/javascript/mastodon/features/public_timeline/index.js @@ -100,13 +100,6 @@ class PublicTimeline extends React.PureComponent { dispatch(expandPublicTimeline({ maxId, onlyMedia })); } - handleSettingChanged = (key, checked) => { - const { columnId } = this.props; - if (!columnId && key[0] === 'other' && key[1] === 'onlyMedia') { - this.context.router.history.replace(`/timelines/public${checked ? '/media' : ''}`); - } - } - render () { const { intl, shouldUpdateScroll, columnId, hasUnread, multiColumn, onlyMedia } = this.props; const pinned = !!columnId; @@ -123,7 +116,7 @@ class PublicTimeline extends React.PureComponent { pinned={pinned} multiColumn={multiColumn} > - + - - diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 1015aba1bc..ebf43bf50b 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -1369,6 +1369,79 @@ ], "path": "app/javascript/mastodon/features/home_timeline/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "First steps", + "id": "introduction.welcome.headline" + }, + { + "defaultMessage": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", + "id": "introduction.welcome.text" + }, + { + "defaultMessage": "Let's go!", + "id": "introduction.welcome.action" + }, + { + "defaultMessage": "Home", + "id": "introduction.federation.home.headline" + }, + { + "defaultMessage": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "id": "introduction.federation.home.text" + }, + { + "defaultMessage": "Local", + "id": "introduction.federation.local.headline" + }, + { + "defaultMessage": "Public posts from people on the same server as you will appear in the local timeline.", + "id": "introduction.federation.local.text" + }, + { + "defaultMessage": "Federated", + "id": "introduction.federation.federated.headline" + }, + { + "defaultMessage": "Public posts from other servers of the fediverse will appear in the federated timeline.", + "id": "introduction.federation.federated.text" + }, + { + "defaultMessage": "Next", + "id": "introduction.federation.action" + }, + { + "defaultMessage": "Reply", + "id": "introduction.interactions.reply.headline" + }, + { + "defaultMessage": "You can reply to other people's and your own toots, which will chain them together in a conversation.", + "id": "introduction.interactions.reply.text" + }, + { + "defaultMessage": "Boost", + "id": "introduction.interactions.reblog.headline" + }, + { + "defaultMessage": "You can share other people's toots with your followers by boosting them.", + "id": "introduction.interactions.reblog.text" + }, + { + "defaultMessage": "Favourite", + "id": "introduction.interactions.favourite.headline" + }, + { + "defaultMessage": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "id": "introduction.interactions.favourite.text" + }, + { + "defaultMessage": "Finish tutorial!", + "id": "introduction.interactions.action" + } + ], + "path": "app/javascript/mastodon/features/introduction/index.json" + }, { "descriptors": [ { @@ -1612,6 +1685,14 @@ }, { "descriptors": [ + { + "defaultMessage": "Show", + "id": "notifications.column_settings.filter_bar.show" + }, + { + "defaultMessage": "Display all categories", + "id": "notifications.column_settings.filter_bar.advanced" + }, { "defaultMessage": "Desktop notifications", "id": "notifications.column_settings.alert" @@ -1628,6 +1709,10 @@ "defaultMessage": "Push notifications", "id": "notifications.column_settings.push" }, + { + "defaultMessage": "Quick filter bar", + "id": "notifications.column_settings.filter_bar.category" + }, { "defaultMessage": "New followers:", "id": "notifications.column_settings.follow" @@ -1647,6 +1732,31 @@ ], "path": "app/javascript/mastodon/features/notifications/components/column_settings.json" }, + { + "descriptors": [ + { + "defaultMessage": "Mentions", + "id": "notifications.filter.mentions" + }, + { + "defaultMessage": "Favourites", + "id": "notifications.filter.favourites" + }, + { + "defaultMessage": "Boosts", + "id": "notifications.filter.boosts" + }, + { + "defaultMessage": "Follows", + "id": "notifications.filter.follows" + }, + { + "defaultMessage": "All", + "id": "notifications.filter.all" + } + ], + "path": "app/javascript/mastodon/features/notifications/components/filter_bar.json" + }, { "descriptors": [ { diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d20ac1f881..e9b5fad0d7 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -153,6 +153,23 @@ "home.column_settings.basic": "Basic", "home.column_settings.show_reblogs": "Show boosts", "home.column_settings.show_replies": "Show replies", + "introduction.federation.action": "Next", + "introduction.federation.federated.headline": "Federated", + "introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.", + "introduction.federation.home.headline": "Home", + "introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!", + "introduction.federation.local.headline": "Local", + "introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.", + "introduction.interactions.action": "Finish tutorial!", + "introduction.interactions.favourite.headline": "Favourite", + "introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.", + "introduction.interactions.reblog.headline": "Boost", + "introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.", + "introduction.interactions.reply.headline": "Reply", + "introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.", + "introduction.welcome.action": "Let's go!", + "introduction.welcome.headline": "First steps", + "introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.", "keyboard_shortcuts.back": "to navigate back", "keyboard_shortcuts.blocked": "to open blocked users list", "keyboard_shortcuts.boost": "to boost", @@ -228,22 +245,22 @@ "notification.reblog": "{name} boosted your status", "notifications.clear": "Clear notifications", "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", - "notifications.filter.all": "All", - "notifications.filter.mentions": "Mentions", - "notifications.filter.favourites": "Favourites", - "notifications.filter.boosts": "Boosts", - "notifications.filter.follows": "Follows", - "notifications.column_settings.filter_bar.category": "Quick filter bar", - "notifications.column_settings.filter_bar.show": "Show", - "notifications.column_settings.filter_bar.advanced": "Display all categories", "notifications.column_settings.alert": "Desktop notifications", "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.filter_bar.advanced": "Display all categories", + "notifications.column_settings.filter_bar.category": "Quick filter bar", + "notifications.column_settings.filter_bar.show": "Show", "notifications.column_settings.follow": "New followers:", "notifications.column_settings.mention": "Mentions:", "notifications.column_settings.push": "Push notifications", "notifications.column_settings.reblog": "Boosts:", "notifications.column_settings.show": "Show in column", "notifications.column_settings.sound": "Play sound", + "notifications.filter.all": "All", + "notifications.filter.boosts": "Boosts", + "notifications.filter.favourites": "Favourites", + "notifications.filter.follows": "Follows", + "notifications.filter.mentions": "Mentions", "notifications.group": "{count} notifications", "onboarding.done": "Done", "onboarding.next": "Next", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 4e88622019..a93ee68fd3 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -17,7 +17,7 @@ "account.follows_you": "フォローされています", "account.hide_reblogs": "@{name}さんからのブーストを非表示", "account.link_verified_on": "このリンクの所有権は{date}に確認されました", - "account.locked_info": "このアカウントは承認制に設定されています。フォローするには所有者の確認が必要です。", + "account.locked_info": "このアカウントは承認制アカウントです。相手が確認するまでフォローは完了しません。", "account.media": "メディア", "account.mention": "@{name}さんにトゥート", "account.moved_to": "{name}さんは引っ越しました:", @@ -153,6 +153,23 @@ "home.column_settings.basic": "基本設定", "home.column_settings.show_reblogs": "ブースト表示", "home.column_settings.show_replies": "返信表示", + "introduction.federation.action": "次へ", + "introduction.federation.federated.headline": "連合タイムライン", + "introduction.federation.federated.text": "Fediverseの他のサーバーからの公開投稿は連合タイムラインに表示されます。", + "introduction.federation.home.headline": "ホームタイムライン", + "introduction.federation.home.text": "フォローしている人々の投稿はホームタイムラインに表示されます。どこのサーバーの誰でもフォローできます!", + "introduction.federation.local.headline": "ローカルタイムライン", + "introduction.federation.local.text": "同じサーバーにいる人々の公開投稿はローカルタイムラインに表示されます。", + "introduction.interactions.action": "はじめよう!", + "introduction.interactions.favourite.headline": "お気に入り", + "introduction.interactions.favourite.text": "お気に入り登録することで後から見られるよう保存したり、「好き」を相手に伝えたりできます。", + "introduction.interactions.reblog.headline": "ブースト", + "introduction.interactions.reblog.text": "ブーストすることでフォロワーにそのトゥートを共有できます。", + "introduction.interactions.reply.headline": "返信", + "introduction.interactions.reply.text": "自身や人々のトゥートに返信することで、一連の会話に繋げることができます。", + "introduction.welcome.action": "はじめる!", + "introduction.welcome.headline": "はじめに", + "introduction.welcome.text": "Fediverseの世界へようこそ!あと少しでメッセージを配信したり、さまざまなサーバーを越えた友達と話せるようになります。ところでここ{domain}は特別なサーバーです…あなたのプロフィールを持つ主体のサーバーですので、名前を覚えておきましょう。", "keyboard_shortcuts.back": "戻る", "keyboard_shortcuts.blocked": "ブロックしたユーザーのリストを開く", "keyboard_shortcuts.boost": "ブースト", @@ -230,12 +247,20 @@ "notifications.clear_confirmation": "本当に通知を消去しますか?", "notifications.column_settings.alert": "デスクトップ通知", "notifications.column_settings.favourite": "お気に入り:", + "notifications.column_settings.filter_bar.advanced": "すべてのカテゴリを表示", + "notifications.column_settings.filter_bar.category": "クイックフィルターバー", + "notifications.column_settings.filter_bar.show": "表示", "notifications.column_settings.follow": "新しいフォロワー:", "notifications.column_settings.mention": "返信:", "notifications.column_settings.push": "プッシュ通知", "notifications.column_settings.reblog": "ブースト:", "notifications.column_settings.show": "カラムに表示", "notifications.column_settings.sound": "通知音を再生", + "notifications.filter.all": "すべて", + "notifications.filter.boosts": "ブースト", + "notifications.filter.favourites": "お気に入り", + "notifications.filter.follows": "フォロー", + "notifications.filter.mentions": "返信", "notifications.group": "{count} 件の通知", "onboarding.done": "完了", "onboarding.next": "次へ", diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 61e330a262..eaf25e7872 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4806,10 +4806,12 @@ a.status-card.compact:hover { border-bottom: 1px solid lighten($ui-base-color, 8%); cursor: default; display: flex; + flex-shrink: 0; button { background: darken($ui-base-color, 4%); border: 0; + margin: 0; } button, diff --git a/config/locales/en.yml b/config/locales/en.yml index d6f071c789..735a88efdd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -104,6 +104,7 @@ en: followers: Followers followers_url: Followers URL follows: Follows + header: Header inbox_url: Inbox URL ip: IP location: @@ -134,6 +135,7 @@ en: push_subscription_expires: PuSH subscription expires redownload: Refresh avatar remove_avatar: Remove avatar + remove_header: Remove header resend_confirmation: already_confirmed: This user is already confirmed send: Resend confirmation email diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 21e4236a82..5879755e96 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -104,6 +104,7 @@ ja: followers: フォロワー数 followers_url: Followers URL follows: フォロー数 + header: ヘッダー inbox_url: Inbox URL ip: IP location: @@ -115,10 +116,10 @@ ja: media_attachments: 添付されたメディア memorialize: 追悼アカウント化 moderation: - active: 有効 + active: アクティブ all: すべて - silenced: サイレンス中 - suspended: 停止中 + silenced: サイレンス済み + suspended: 停止済み title: モデレーション moderation_notes: モデレーションメモ most_recent_activity: 直近の活動 @@ -134,6 +135,7 @@ ja: push_subscription_expires: PuSH購読期限 redownload: アバターの更新 remove_avatar: アイコンを削除 + remove_header: ヘッダーを削除 resend_confirmation: already_confirmed: メールアドレスは確認済みです send: 確認メールを再送 @@ -229,6 +231,7 @@ ja: config: 構成 feature_deletions: アカウント削除 feature_invites: 招待リンク + feature_profile_directory: ディレクトリ feature_registrations: 新規登録 feature_relay: 連合リレー features: 機能 @@ -376,6 +379,9 @@ ja: preview_sensitive_media: desc_html: 他のウェブサイトにリンクを貼った際、メディアが閲覧注意としてマークされていてもサムネイルが表示されます title: OpenGraphによるプレビューで閲覧注意のメディアも表示する + profile_directory: + desc_html: ユーザーが見つかりやすくできるようになります + title: ディレクトリを有効にする registrations: closed_message: desc_html: 新規登録を停止しているときにフロントページに表示されます。HTMLタグが使えます @@ -529,6 +535,7 @@ ja: warning_title: 共有されたコンテンツについて directories: directory: ディレクトリ + explanation: 興味のある人を見つけよう explore_mastodon: "%{title}を探索" people: one: "%{count} 人" diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 07ae66c794..b64974ffd3 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -19,6 +19,7 @@ ja: password: 少なくとも8文字は入力してください phrase: トゥートの大文字小文字や閲覧注意に関係なく一致 scopes: アプリの API に許可するアクセス権を選択してください。最上位のスコープを選択する場合、個々のスコープを選択する必要はありません。 + setting_aggregate_reblogs: 最近ブーストされたトゥートが新たにブーストされても表示しません (設定後受信したものにのみ影響) setting_default_language: トゥートの言語は自動的に検出されますが、必ずしも正確とは限りません setting_display_media_default: 閲覧注意としてマークされたメディアは隠す setting_display_media_hide_all: 全てのメディアを常に隠す diff --git a/spec/policies/account_moderation_note_policy_spec.rb b/spec/policies/account_moderation_note_policy_spec.rb new file mode 100644 index 0000000000..bb7af94e45 --- /dev/null +++ b/spec/policies/account_moderation_note_policy_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'pundit/rspec' + +RSpec.describe AccountModerationNotePolicy do + let(:subject) { described_class } + let(:admin) { Fabricate(:user, admin: true).account } + let(:john) { Fabricate(:user).account } + + permissions :create? do + context 'staff' do + it 'grants to create' do + expect(subject).to permit(admin, AccountModerationNotePolicy) + end + end + + context 'not staff' do + it 'denies to create' do + expect(subject).to_not permit(john, AccountModerationNotePolicy) + end + end + end + + permissions :destroy? do + let(:account_moderation_note) do + Fabricate(:account_moderation_note, + account: john, + target_account: Fabricate(:account)) + end + + context 'admin' do + it 'grants to destroy' do + expect(subject).to permit(admin, AccountModerationNotePolicy) + end + end + + context 'owner' do + it 'grants to destroy' do + expect(subject).to permit(john, account_moderation_note) + end + end + + context 'neither admin nor owner' do + let(:kevin) { Fabricate(:user).account } + + it 'denies to destroy' do + expect(subject).to_not permit(kevin, account_moderation_note) + end + end + end +end diff --git a/spec/policies/account_policy_spec.rb b/spec/policies/account_policy_spec.rb new file mode 100644 index 0000000000..6648b0888b --- /dev/null +++ b/spec/policies/account_policy_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'pundit/rspec' + +RSpec.describe AccountPolicy do + let(:subject) { described_class } + let(:admin) { Fabricate(:user, admin: true).account } + let(:john) { Fabricate(:user).account } + + permissions :index?, :show?, :unsuspend?, :unsilence?, :remove_avatar?, :remove_header? do + context 'staff' do + it 'permits' do + expect(subject).to permit(admin) + end + end + + context 'not staff' do + it 'denies' do + expect(subject).to_not permit(john) + end + end + end + + permissions :redownload?, :subscribe?, :unsubscribe? do + context 'admin' do + it 'permits' do + expect(subject).to permit(admin) + end + end + + context 'not admin' do + it 'denies' do + expect(subject).to_not permit(john) + end + end + end + + permissions :suspend?, :silence? do + let(:staff) { Fabricate(:user, admin: true).account } + + context 'staff' do + context 'record is staff' do + it 'denies' do + expect(subject).to_not permit(admin, staff) + end + end + + context 'record is not staff' do + it 'permits' do + expect(subject).to permit(admin, john) + end + end + end + + context 'not staff' do + it 'denies' do + expect(subject).to_not permit(john, Account) + end + end + end + + permissions :memorialize? do + let(:other_admin) { Fabricate(:user, admin: true).account } + + context 'admin' do + context 'record is admin' do + it 'denies' do + expect(subject).to_not permit(admin, other_admin) + end + end + + context 'record is not admin' do + it 'permits' do + expect(subject).to permit(admin, john) + end + end + end + + context 'not admin' do + it 'denies' do + expect(subject).to_not permit(john, Account) + end + end + end +end diff --git a/spec/policies/status_policy_spec.rb b/spec/policies/status_policy_spec.rb index 837fa9cee8..8bce29cad2 100644 --- a/spec/policies/status_policy_spec.rb +++ b/spec/policies/status_policy_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rails_helper' require 'pundit/rspec' @@ -118,4 +120,30 @@ RSpec.describe StatusPolicy, type: :model do expect(subject).to_not permit(nil, status) end end + + permissions :favourite? do + it 'grants access when viewer is not blocked' do + follow = Fabricate(:follow) + status.account = follow.target_account + + expect(subject).to permit(follow.account, status) + end + + it 'denies when viewer is blocked' do + block = Fabricate(:block) + status.account = block.target_account + + expect(subject).to_not permit(block.account, status) + end + end + + permissions :index?, :update? do + it 'grants access if staff' do + expect(subject).to permit(admin.account) + end + + it 'denies access unless staff' do + expect(subject).to_not permit(alice) + end + end end