From b11ac88692ad7a8765b0b15e6d7a882d171ffe81 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 18 Jul 2017 07:19:02 +0900 Subject: Require any modules after loading polyfill in entry points (#4231) app/javascript/mastodon/main.js delayed the execution of modules, but other entry points didn't. That leads to failure in executing modules, which requires those polyfills. Strictly enforce the rule to require any modules after loading polyfill in entry points. --- app/javascript/mastodon/main.js | 11 +++--- app/javascript/packs/about.js | 8 ++--- app/javascript/packs/application.js | 5 +-- app/javascript/packs/public.js | 67 ++++++++++++++++++------------------- 4 files changed, 45 insertions(+), 46 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.js index cc422c109..a7fc22a00 100644 --- a/app/javascript/mastodon/main.js +++ b/app/javascript/mastodon/main.js @@ -1,12 +1,14 @@ +import * as OfflinePluginRuntime from 'offline-plugin/runtime'; +import * as WebPushSubscription from './web_push_subscription'; +import Mastodon from 'mastodon/containers/mastodon'; +import React from 'react'; +import ReactDOM from 'react-dom'; import ready from './ready'; const perf = require('./performance'); function main() { perf.start('main()'); - const Mastodon = require('mastodon/containers/mastodon').default; - const React = require('react'); - const ReactDOM = require('react-dom'); if (window.history && history.replaceState) { const { pathname, search, hash } = window.location; @@ -23,9 +25,6 @@ function main() { ReactDOM.render(, mountNode); if (process.env.NODE_ENV === 'production') { // avoid offline in dev mode because it's harder to debug - const OfflinePluginRuntime = require('offline-plugin/runtime'); - const WebPushSubscription = require('./web_push_subscription'); - OfflinePluginRuntime.install(); WebPushSubscription.register(); } diff --git a/app/javascript/packs/about.js b/app/javascript/packs/about.js index 7b8ab5e5d..6705377c1 100644 --- a/app/javascript/packs/about.js +++ b/app/javascript/packs/about.js @@ -1,12 +1,11 @@ -import TimelineContainer from '../mastodon/containers/timeline_container'; -import React from 'react'; -import ReactDOM from 'react-dom'; import loadPolyfills from '../mastodon/load_polyfills'; -import ready from '../mastodon/ready'; require.context('../images/', true); function loaded() { + const TimelineContainer = require('../mastodon/containers/timeline_container').default; + const React = require('react'); + const ReactDOM = require('react-dom'); const mountNode = document.getElementById('mastodon-timeline'); if (mountNode !== null) { @@ -16,6 +15,7 @@ function loaded() { } function main() { + const ready = require('../mastodon/ready').default; ready(loaded); } diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 63c5d6272..116632dea 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -1,6 +1,7 @@ -import main from '../mastodon/main'; import loadPolyfills from '../mastodon/load_polyfills'; -loadPolyfills().then(main).catch(e => { +loadPolyfills().then(() => { + require('../mastodon/main').default(); +}).catch(e => { console.error(e); }); diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index 371e0f445..ce79836d6 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -1,44 +1,43 @@ -import { length } from 'stringz'; -import IntlRelativeFormat from 'intl-relativeformat'; -import { delegate } from 'rails-ujs'; -import emojify from '../mastodon/emoji'; -import { getLocale } from '../mastodon/locales'; import loadPolyfills from '../mastodon/load_polyfills'; -import ready from '../mastodon/ready'; -const { localeData } = getLocale(); -localeData.forEach(IntlRelativeFormat.__addLocaleData); +function main() { + const { length } = require('stringz'); + const IntlRelativeFormat = require('intl-relativeformat').default; + const { delegate } = require('rails-ujs'); + const emojify = require('../mastodon/emoji').default; + const { getLocale } = require('../mastodon/locales'); + const ready = require('../mastodon/ready').default; -function loaded() { - const locale = document.documentElement.lang; - const dateTimeFormat = new Intl.DateTimeFormat(locale, { - year: 'numeric', - month: 'long', - day: 'numeric', - hour: 'numeric', - minute: 'numeric', - }); - const relativeFormat = new IntlRelativeFormat(locale); + const { localeData } = getLocale(); + localeData.forEach(IntlRelativeFormat.__addLocaleData); - [].forEach.call(document.querySelectorAll('.emojify'), (content) => { - content.innerHTML = emojify(content.innerHTML); - }); + ready(() => { + const locale = document.documentElement.lang; + const dateTimeFormat = new Intl.DateTimeFormat(locale, { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + }); + const relativeFormat = new IntlRelativeFormat(locale); - [].forEach.call(document.querySelectorAll('time.formatted'), (content) => { - const datetime = new Date(content.getAttribute('datetime')); - const formattedDate = dateTimeFormat.format(datetime); - content.title = formattedDate; - content.textContent = formattedDate; - }); + [].forEach.call(document.querySelectorAll('.emojify'), (content) => { + content.innerHTML = emojify(content.innerHTML); + }); - [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => { - const datetime = new Date(content.getAttribute('datetime')); - content.textContent = relativeFormat.format(datetime);; - }); -} + [].forEach.call(document.querySelectorAll('time.formatted'), (content) => { + const datetime = new Date(content.getAttribute('datetime')); + const formattedDate = dateTimeFormat.format(datetime); + content.title = formattedDate; + content.textContent = formattedDate; + }); -function main() { - ready(loaded); + [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => { + const datetime = new Date(content.getAttribute('datetime')); + content.textContent = relativeFormat.format(datetime);; + }); + }); delegate(document, '.video-player video', 'click', ({ target }) => { if (target.paused) { -- cgit From 719ab720a7286c4968f1b42e1be385f5580de13e Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Tue, 18 Jul 2017 00:19:17 +0200 Subject: feat(push-notifications): Open link in current tab if possible (#4228) * fix(push-notification): Open link in current tab if possible * feat(sw): Skip waiting and claim clients --- app/javascript/mastodon/service_worker/entry.js | 9 +++++++++ .../service_worker/web_push_notifications.js | 20 +++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/service_worker/entry.js b/app/javascript/mastodon/service_worker/entry.js index 364b67066..eea4cfc3c 100644 --- a/app/javascript/mastodon/service_worker/entry.js +++ b/app/javascript/mastodon/service_worker/entry.js @@ -1 +1,10 @@ import './web_push_notifications'; + +// Cause a new version of a registered Service Worker to replace an existing one +// that is already installed, and replace the currently active worker on open pages. +self.addEventListener('install', function(event) { + event.waitUntil(self.skipWaiting()); +}); +self.addEventListener('activate', function(event) { + event.waitUntil(self.clients.claim()); +}); diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js index 1708aa9f7..4a8a57767 100644 --- a/app/javascript/mastodon/service_worker/web_push_notifications.js +++ b/app/javascript/mastodon/service_worker/web_push_notifications.js @@ -50,6 +50,24 @@ const makeRequest = (notification, action) => credentials: 'include', }); +const openUrl = url => + self.clients.matchAll({ type: 'window' }).then(clientList => { + if (clientList.length !== 0 && 'navigate' in clientList[0]) { // Chrome 42-48 does not support navigate + const webClients = clientList + .filter(client => /\/web\//.test(client.url)) + .sort(client => client !== 'visible'); + + const visibleClient = clientList.find(client => client.visibilityState === 'visible'); + const focusedClient = clientList.find(client => client.focused); + + const client = webClients[0] || visibleClient || focusedClient || clientList[0]; + + return client.navigate(url).then(client => client.focus()); + } else { + return self.clients.openWindow(url); + } + }); + const removeActionFromNotification = (notification, action) => { const actions = notification.actions.filter(act => act.action !== action.action); @@ -75,7 +93,7 @@ const handleNotificationClick = (event) => { } } else { event.notification.close(); - resolve(self.clients.openWindow(event.notification.data.url)); + resolve(openUrl(event.notification.data.url)); } }); -- cgit From f5382ec08504a06c005e8f40738596c5c5c837de Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Tue, 18 Jul 2017 22:20:38 +0900 Subject: Exclude self toots from regular expression filter (#4245) * Exclude self toots from regular expression filter * refactor --- .../ui/containers/status_list_container.js | 43 ++++++++++++---------- 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'app/javascript') 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 1b2e1056a..ff29bfdd4 100644 --- a/app/javascript/mastodon/features/ui/containers/status_list_container.js +++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js @@ -10,31 +10,36 @@ const makeGetStatusIds = () => createSelector([ (state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()), (state) => state.get('statuses'), (state) => state.getIn(['meta', 'me']), -], (columnSettings, statusIds, statuses, me) => statusIds.filter(id => { - const statusForId = statuses.get(id); - let showStatus = true; +], (columnSettings, statusIds, statuses, me) => { + const rawRegex = columnSettings.getIn(['regex', 'body'], '').trim(); + let regex = null; - if (columnSettings.getIn(['shows', 'reblog']) === false) { - showStatus = showStatus && statusForId.get('reblog') === null; + try { + regex = rawRegex && new RegExp(rawRegex, 'i'); + } catch (e) { + // Bad regex, don't affect filters } - if (columnSettings.getIn(['shows', 'reply']) === false) { - showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me); - } + return statusIds.filter(id => { + const statusForId = statuses.get(id); + let showStatus = true; - if (columnSettings.getIn(['regex', 'body'], '').trim().length > 0) { - try { - if (showStatus) { - const regex = new RegExp(columnSettings.getIn(['regex', 'body']).trim(), 'i'); - showStatus = !regex.test(statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'search_index']) : statusForId.get('search_index')); - } - } catch(e) { - // Bad regex, don't affect filters + if (columnSettings.getIn(['shows', 'reblog']) === false) { + showStatus = showStatus && statusForId.get('reblog') === null; + } + + if (columnSettings.getIn(['shows', 'reply']) === false) { + showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me); } - } - return showStatus; -})); + if (showStatus && regex && statusForId.get('account') !== me) { + const searchIndex = statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'search_index']) : statusForId.get('search_index'); + showStatus = !regex.test(searchIndex); + } + + return showStatus; + }); +}); const makeMapStateToProps = () => { const getStatusIds = makeGetStatusIds(); -- cgit From afa52e4d636f8fdc4b21b90e4f006d8ea434b4e9 Mon Sep 17 00:00:00 2001 From: abcang Date: Tue, 18 Jul 2017 22:24:57 +0900 Subject: Fixed issue that the NSFW image is not hidden on detail page (#4244) --- app/javascript/mastodon/components/media_gallery.js | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index 89a358e38..92d7d494e 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -183,6 +183,12 @@ export default class MediaGallery extends React.PureComponent { visible: !this.props.sensitive, }; + componentWillReceiveProps (nextProps) { + if (nextProps.sensitive !== this.props.sensitive) { + this.setState({ visible: !nextProps.sensitive }); + } + } + handleOpen = () => { this.setState({ visible: !this.state.visible }); } -- cgit From 4d42a389540690b32886f2a38af1f86aee617d27 Mon Sep 17 00:00:00 2001 From: abcang Date: Tue, 18 Jul 2017 23:38:22 +0900 Subject: Improve admin page (#4121) * Improve admin page * Fix test * Add spec * Improve select style --- .../admin/reported_statuses_controller.rb | 15 ++- app/controllers/admin/reports_controller.rb | 4 +- app/controllers/admin/statuses_controller.rb | 69 +++++++++++++ app/javascript/packs/admin.js | 40 ++++++++ app/javascript/styles/admin.scss | 45 ++++++++- app/models/form/status_batch.rb | 39 ++++++++ app/views/admin/accounts/show.html.haml | 4 +- app/views/admin/reports/show.html.haml | 34 +++++-- app/views/admin/statuses/index.html.haml | 47 +++++++++ config/locales/en.yml | 15 +++ config/locales/ja.yml | 15 +++ config/routes.rb | 3 +- .../admin/reported_statuses_controller_spec.rb | 38 +++++++- spec/controllers/admin/statuses_controller_spec.rb | 107 +++++++++++++++++++++ spec/models/form/status_batch_spec.rb | 52 ++++++++++ 15 files changed, 508 insertions(+), 19 deletions(-) create mode 100644 app/controllers/admin/statuses_controller.rb create mode 100644 app/javascript/packs/admin.js create mode 100644 app/models/form/status_batch.rb create mode 100644 app/views/admin/statuses/index.html.haml create mode 100644 spec/controllers/admin/statuses_controller_spec.rb create mode 100644 spec/models/form/status_batch_spec.rb (limited to 'app/javascript') diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb index 32434d30f..5a31adecf 100644 --- a/app/controllers/admin/reported_statuses_controller.rb +++ b/app/controllers/admin/reported_statuses_controller.rb @@ -5,7 +5,14 @@ module Admin include Authorization before_action :set_report - before_action :set_status + before_action :set_status, only: [:update, :destroy] + + def create + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + + redirect_to admin_report_path(@report) + end def update @status.update(status_params) @@ -15,7 +22,7 @@ module Admin def destroy authorize @status, :destroy? RemovalWorker.perform_async(@status.id) - redirect_to admin_report_path(@report) + render json: @status end private @@ -24,6 +31,10 @@ module Admin params.require(:status).permit(:sensitive) end + def form_status_batch_params + params.require(:form_status_batch).permit(:action, status_ids: []) + end + def set_report @report = Report.find(params[:report_id]) end diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 2d8c3c820..226467739 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -8,7 +8,9 @@ module Admin @reports = filtered_reports.page(params[:page]) end - def show; end + def show + @form = Form::StatusBatch.new + end def update process_report diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb new file mode 100644 index 000000000..50712f0dd --- /dev/null +++ b/app/controllers/admin/statuses_controller.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Admin + class StatusesController < BaseController + include Authorization + + helper_method :current_params + + before_action :set_account + before_action :set_status, only: [:update, :destroy] + + PAR_PAGE = 20 + + def index + @statuses = @account.statuses + if params[:media] + account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct + @statuses.merge!(Status.where(id: account_media_status_ids)) + end + @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PAR_PAGE) + + @form = Form::StatusBatch.new + end + + def create + @form = Form::StatusBatch.new(form_status_batch_params) + flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save + + redirect_to admin_account_statuses_path(@account.id, current_params) + end + + def update + @status.update(status_params) + redirect_to admin_account_statuses_path(@account.id, current_params) + end + + def destroy + authorize @status, :destroy? + RemovalWorker.perform_async(@status.id) + render json: @status + end + + private + + def status_params + params.require(:status).permit(:sensitive) + end + + def form_status_batch_params + params.require(:form_status_batch).permit(:action, status_ids: []) + end + + def set_status + @status = @account.statuses.find(params[:id]) + end + + def set_account + @account = Account.find(params[:account_id]) + end + + def current_params + page = (params[:page] || 1).to_i + { + media: params[:media], + page: page > 1 && page, + }.select { |_, value| value.present? } + end + end +end diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js new file mode 100644 index 000000000..993827db5 --- /dev/null +++ b/app/javascript/packs/admin.js @@ -0,0 +1,40 @@ +import { delegate } from 'rails-ujs'; + +function handleDeleteStatus(event) { + const [data] = event.detail; + const element = document.querySelector(`[data-id="${data.id}"]`); + if (element) { + element.parentNode.removeChild(element); + } +} + +[].forEach.call(document.querySelectorAll('.trash-button'), (content) => { + content.addEventListener('ajax:success', handleDeleteStatus); +}); + +const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]'; + +delegate(document, '#batch_checkbox_all', 'change', ({ target }) => { + [].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => { + content.checked = target.checked; + }); +}); + +delegate(document, batchCheckboxClassName, 'change', () => { + const checkAllElement = document.querySelector('#batch_checkbox_all'); + if (checkAllElement) { + checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); + } +}); + +delegate(document, '.media-spoiler-show-button', 'click', () => { + [].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => { + content.classList.add('media-spoiler-wrapper__visible'); + }); +}); + +delegate(document, '.media-spoiler-hide-button', 'click', () => { + [].forEach.call(document.querySelectorAll('.activity-stream .media-spoiler-wrapper'), (content) => { + content.classList.remove('media-spoiler-wrapper__visible'); + }); +}); diff --git a/app/javascript/styles/admin.scss b/app/javascript/styles/admin.scss index 3bc713566..4c3bbdfc5 100644 --- a/app/javascript/styles/admin.scss +++ b/app/javascript/styles/admin.scss @@ -253,7 +253,8 @@ } } -.report-status { +.report-status, +.account-status { display: flex; margin-bottom: 10px; @@ -263,7 +264,8 @@ } } -.report-status__actions { +.report-status__actions, +.account-status__actions { flex: 0 0 auto; display: flex; flex-direction: column; @@ -275,3 +277,42 @@ margin-bottom: 10px; } } + +.batch-form-box { + display: flex; + margin-bottom: 10px; + + #form_status_batch_action { + margin-right: 5px; + font-size: 14px; + } + + .media-spoiler-toggle-buttons { + margin-left: auto; + + .button { + overflow: visible; + } + } +} + +.batch-checkbox, +.batch-checkbox-all { + display: flex; + align-items: center; + margin-right: 5px; +} + +.back-link { + margin-bottom: 10px; + font-size: 14px; + + a { + color: $classic-highlight-color; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} diff --git a/app/models/form/status_batch.rb b/app/models/form/status_batch.rb new file mode 100644 index 000000000..a97b4aa28 --- /dev/null +++ b/app/models/form/status_batch.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class Form::StatusBatch + include ActiveModel::Model + + attr_accessor :status_ids, :action + + ACTION_TYPE = %w(nsfw_on nsfw_off delete).freeze + + def save + case action + when 'nsfw_on', 'nsfw_off' + change_sensitive(action == 'nsfw_on') + when 'delete' + delete_statuses + end + end + + private + + def change_sensitive(sensitive) + media_attached_status_ids = MediaAttachment.where(status_id: status_ids).pluck(:status_id) + ApplicationRecord.transaction do + Status.where(id: media_attached_status_ids).find_each do |status| + status.update!(sensitive: sensitive) + end + end + true + rescue ActiveRecord::RecordInvalid + false + end + + def delete_statuses + Status.where(id: status_ids).find_each do |status| + RemovalWorker.perform_async(status.id) + end + true + end +end diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index d91ba9c78..5ad1fd6ee 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -53,11 +53,11 @@ %td= @account.followers_count %tr %th= t('admin.accounts.statuses') - %td= @account.statuses_count + %td= link_to @account.statuses_count, admin_account_statuses_path(@account.id) %tr %th= t('admin.accounts.media_attachments') %td - = @account.media_attachments.count + = link_to @account.media_attachments.count, admin_account_statuses_path(@account.id, { media: true }) = surround '(', ')' do = number_to_human_size @account.media_attachments.sum('file_file_size') %tr diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 44486cb42..5747cc274 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -1,3 +1,6 @@ +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + - content_for :page_title do = t('admin.reports.report', id: @report.id) @@ -19,16 +22,27 @@ - unless @report.statuses.empty? %hr/ - - @report.statuses.each do |status| - .report-status - .activity-stream.activity-stream-headless - .entry= render 'stream_entries/simple_status', status: status - .report-status__actions - - unless status.media_attachments.empty? - = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :patch, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do - = fa_icon status.sensitive? ? 'eye' : 'eye-slash' - = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') } do - = fa_icon 'trash' + = form_for(@form, url: admin_report_reported_statuses_path(@report.id)) do |f| + .batch-form-box + .batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]} + = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button' + .media-spoiler-toggle-buttons + .media-spoiler-show-button.button= t('admin.statuses.media.show') + .media-spoiler-hide-button.button= t('admin.statuses.media.hide') + - @report.statuses.each do |status| + .report-status{ data: { id: status.id } } + .batch-checkbox + = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id + .activity-stream.activity-stream-headless + .entry= render 'stream_entries/simple_status', status: status + .report-status__actions + - unless status.media_attachments.empty? + = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :put, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do + = fa_icon status.sensitive? ? 'eye' : 'eye-slash' + = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do + = fa_icon 'trash' %hr/ diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml new file mode 100644 index 000000000..fe2581527 --- /dev/null +++ b/app/views/admin/statuses/index.html.haml @@ -0,0 +1,47 @@ +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + +- content_for :page_title do + = t('admin.statuses.title') + +.back-link + = link_to admin_account_path(@account.id) do + %i.fa.fa-chevron-left.fa-fw + = t('admin.statuses.back_to_account') + +.filters + .filter-subset + %strong= t('admin.statuses.media.title') + %ul + %li= link_to t('admin.statuses.no_media'), admin_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected' + %li= link_to t('admin.statuses.with_media'), admin_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected' + +- if @statuses.empty? + .accounts-grid + = render 'accounts/nothing_here' +- else + = form_for(@form, url: admin_account_statuses_path(@account.id)) do |f| + = hidden_field_tag :page, params[:page] + = hidden_field_tag :media, params[:media] + .batch-form-box + .batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]} + = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button' + .media-spoiler-toggle-buttons + .media-spoiler-show-button.button= t('admin.statuses.media.show') + .media-spoiler-hide-button.button= t('admin.statuses.media.hide') + - @statuses.each do |status| + .account-status{ data: { id: status.id } } + .batch-checkbox + = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id + .activity-stream.activity-stream-headless + .entry= render 'stream_entries/simple_status', status: status + .account-status__actions + - unless status.media_attachments.empty? + = link_to admin_account_status_path(@account.id, status, current_params.merge(status: { sensitive: !status.sensitive })), method: :patch, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do + = fa_icon status.sensitive? ? 'eye' : 'eye-slash' + = link_to admin_account_status_path(@account.id, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do + = fa_icon 'trash' + += paginate @statuses diff --git a/config/locales/en.yml b/config/locales/en.yml index be1f15e25..4cb536223 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -185,6 +185,21 @@ en: desc_html: Display public timeline on landing page title: Timeline preview title: Site Settings + statuses: + back_to_account: Back to account page + batch: + delete: Delete + nsfw_off: NSFW OFF + nsfw_on: NSFW ON + execute: Execute + failed_to_execute: Failed to execute + media: + hide: Hide media + show: Show media + title: Media + no_media: No media + with_media: With media + title: Account statuses subscriptions: callback_url: Callback URL confirmed: Confirmed diff --git a/config/locales/ja.yml b/config/locales/ja.yml index fda87526d..2897e864f 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -171,6 +171,21 @@ ja: desc_html: ランディングページに公開タイムラインを表示します title: タイムラインプレビュー title: サイト設定 + statuses: + back_to_account: アカウントページに戻る + batch: + delete: 削除 + nsfw_off: NSFW オフ + nsfw_on: NSFW オン + execute: 実行 + failed_to_execute: 実行に失敗しました + media: + hide: メディアを隠す + show: メディアを表示 + title: メディア + no_media: メディアなし + with_media: メディアあり + title: トゥート一覧 subscriptions: callback_url: コールバックURL confirmed: 確認済み diff --git a/config/routes.rb b/config/routes.rb index dda3534eb..60234a9e6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -89,7 +89,7 @@ Rails.application.routes.draw do resources :instances, only: [:index] resources :reports, only: [:index, :show, :update] do - resources :reported_statuses, only: [:update, :destroy] + resources :reported_statuses, only: [:create, :update, :destroy] end resources :accounts, only: [:index, :show] do @@ -103,6 +103,7 @@ Rails.application.routes.draw do resource :silence, only: [:create, :destroy] resource :suspension, only: [:create, :destroy] resource :confirmation, only: [:create] + resources :statuses, only: [:index, :create, :update, :destroy] end resources :users, only: [] do diff --git a/spec/controllers/admin/reported_statuses_controller_spec.rb b/spec/controllers/admin/reported_statuses_controller_spec.rb index 80c69e8d1..297807d41 100644 --- a/spec/controllers/admin/reported_statuses_controller_spec.rb +++ b/spec/controllers/admin/reported_statuses_controller_spec.rb @@ -11,6 +11,42 @@ describe Admin::ReportedStatusesController do sign_in user, scope: :user end + describe 'POST #create' do + subject do + -> { post :create, params: { report_id: report, form_status_batch: { action: action, status_ids: status_ids } } } + end + + let(:action) { 'nsfw_on' } + let(:status_ids) { [status.id] } + let(:status) { Fabricate(:status, sensitive: !sensitive) } + let(:sensitive) { true } + let!(:media_attachment) { Fabricate(:media_attachment, status: status) } + + context 'updates sensitive column to true' do + it 'updates sensitive column' do + is_expected.to change { + status.reload.sensitive + }.from(false).to(true) + end + end + + context 'updates sensitive column to false' do + let(:action) { 'nsfw_off' } + let(:sensitive) { false } + + it 'updates sensitive column' do + is_expected.to change { + status.reload.sensitive + }.from(true).to(false) + end + end + + it 'redirects to report page' do + subject.call + expect(response).to redirect_to(admin_report_path(report)) + end + end + describe 'PATCH #update' do subject do -> { patch :update, params: { report_id: report, id: status, status: { sensitive: sensitive } } } @@ -48,7 +84,7 @@ describe Admin::ReportedStatusesController do allow(RemovalWorker).to receive(:perform_async) delete :destroy, params: { report_id: report, id: status } - expect(response).to redirect_to(admin_report_path(report)) + expect(response).to have_http_status(:success) expect(RemovalWorker). to have_received(:perform_async).with(status.id) end diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb new file mode 100644 index 000000000..1515e299b --- /dev/null +++ b/spec/controllers/admin/statuses_controller_spec.rb @@ -0,0 +1,107 @@ +require 'rails_helper' + +describe Admin::StatusesController do + render_views + + let(:user) { Fabricate(:user, admin: true) } + let(:account) { Fabricate(:account) } + let!(:status) { Fabricate(:status, account: account) } + let(:media_attached_status) { Fabricate(:status, account: account, sensitive: !sensitive) } + let!(:media_attachment) { Fabricate(:media_attachment, account: account, status: media_attached_status) } + let(:sensitive) { true } + + before do + sign_in user, scope: :user + end + + describe 'GET #index' do + it 'returns http success with no media' do + get :index, params: { account_id: account.id } + + statuses = assigns(:statuses).to_a + expect(statuses.size).to eq 2 + expect(response).to have_http_status(:success) + end + + it 'returns http success with media' do + get :index, params: { account_id: account.id , media: true } + + statuses = assigns(:statuses).to_a + expect(statuses.size).to eq 1 + expect(response).to have_http_status(:success) + end + end + + describe 'POST #create' do + subject do + -> { post :create, params: { account_id: account.id, form_status_batch: { action: action, status_ids: status_ids } } } + end + + let(:action) { 'nsfw_on' } + let(:status_ids) { [media_attached_status.id] } + + context 'updates sensitive column to true' do + it 'updates sensitive column' do + is_expected.to change { + media_attached_status.reload.sensitive + }.from(false).to(true) + end + end + + context 'updates sensitive column to false' do + let(:action) { 'nsfw_off' } + let(:sensitive) { false } + + it 'updates sensitive column' do + is_expected.to change { + media_attached_status.reload.sensitive + }.from(true).to(false) + end + end + + it 'redirects to account statuses page' do + subject.call + expect(response).to redirect_to(admin_account_statuses_path(account.id)) + end + end + + describe 'PATCH #update' do + subject do + -> { patch :update, params: { account_id: account.id, id: media_attached_status, status: { sensitive: sensitive } } } + end + + context 'updates sensitive column to true' do + it 'updates sensitive column' do + is_expected.to change { + media_attached_status.reload.sensitive + }.from(false).to(true) + end + end + + context 'updates sensitive column to false' do + let(:sensitive) { false } + + it 'updates sensitive column' do + is_expected.to change { + media_attached_status.reload.sensitive + }.from(true).to(false) + end + end + + it 'redirects to account statuses page' do + subject.call + expect(response).to redirect_to(admin_account_statuses_path(account.id)) + end + end + + describe 'DELETE #destroy' do + it 'removes a status' do + allow(RemovalWorker).to receive(:perform_async) + + delete :destroy, params: { account_id: account.id, id: status } + expect(response).to have_http_status(:success) + expect(RemovalWorker). + to have_received(:perform_async).with(status.id) + end + end +end diff --git a/spec/models/form/status_batch_spec.rb b/spec/models/form/status_batch_spec.rb new file mode 100644 index 000000000..00c790a11 --- /dev/null +++ b/spec/models/form/status_batch_spec.rb @@ -0,0 +1,52 @@ +require 'rails_helper' + +describe Form::StatusBatch do + let(:form) { Form::StatusBatch.new(action: action, status_ids: status_ids) } + let(:status) { Fabricate(:status) } + + describe 'with nsfw action' do + let(:status_ids) { [status.id, nonsensitive_status.id, sensitive_status.id] } + let(:nonsensitive_status) { Fabricate(:status, sensitive: false) } + let(:sensitive_status) { Fabricate(:status, sensitive: true) } + let!(:shown_media_attachment) { Fabricate(:media_attachment, status: nonsensitive_status) } + let!(:hidden_media_attachment) { Fabricate(:media_attachment, status: sensitive_status) } + + context 'nsfw_on' do + let(:action) { 'nsfw_on' } + + it { expect(form.save).to be true } + it { expect { form.save }.to change { nonsensitive_status.reload.sensitive }.from(false).to(true) } + it { expect { form.save }.not_to change { sensitive_status.reload.sensitive } } + it { expect { form.save }.not_to change { status.reload.sensitive } } + end + + context 'nsfw_off' do + let(:action) { 'nsfw_off' } + + it { expect(form.save).to be true } + it { expect { form.save }.to change { sensitive_status.reload.sensitive }.from(true).to(false) } + it { expect { form.save }.not_to change { nonsensitive_status.reload.sensitive } } + it { expect { form.save }.not_to change { status.reload.sensitive } } + end + end + + describe 'with delete action' do + let(:status_ids) { [status.id] } + let(:action) { 'delete' } + let!(:another_status) { Fabricate(:status) } + + before do + allow(RemovalWorker).to receive(:perform_async) + end + + it 'call RemovalWorker' do + form.save + expect(RemovalWorker).to have_received(:perform_async).with(status.id) + end + + it 'do not call RemovalWorker' do + form.save + expect(RemovalWorker).not_to have_received(:perform_async).with(another_status.id) + end + end +end -- cgit From 3267e4a7851b57bef7d16da4b7c66764f63d4416 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Wed, 19 Jul 2017 00:14:43 +0900 Subject: Add unfollow modal (optional) (#4246) * Add unfollow modal * unfollowing someone * remove unnecessary prop --- app/controllers/settings/preferences_controller.rb | 1 + .../mastodon/containers/account_container.js | 24 +++++++++++++++++++--- .../containers/header_container.js | 14 ++++++++++++- .../mastodon/features/ui/components/modal_root.js | 2 +- app/javascript/mastodon/locales/ar.json | 2 ++ app/javascript/mastodon/locales/bg.json | 2 ++ app/javascript/mastodon/locales/ca.json | 2 ++ app/javascript/mastodon/locales/de.json | 2 ++ .../mastodon/locales/defaultMessages.json | 21 +++++++++++++++++++ app/javascript/mastodon/locales/en.json | 2 ++ app/javascript/mastodon/locales/eo.json | 2 ++ app/javascript/mastodon/locales/es.json | 2 ++ app/javascript/mastodon/locales/fa.json | 2 ++ app/javascript/mastodon/locales/fi.json | 2 ++ app/javascript/mastodon/locales/fr.json | 2 ++ app/javascript/mastodon/locales/he.json | 2 ++ app/javascript/mastodon/locales/hr.json | 2 ++ app/javascript/mastodon/locales/hu.json | 2 ++ app/javascript/mastodon/locales/id.json | 2 ++ app/javascript/mastodon/locales/io.json | 2 ++ app/javascript/mastodon/locales/it.json | 2 ++ app/javascript/mastodon/locales/ja.json | 2 ++ app/javascript/mastodon/locales/ko.json | 2 ++ app/javascript/mastodon/locales/nl.json | 2 ++ app/javascript/mastodon/locales/no.json | 2 ++ app/javascript/mastodon/locales/oc.json | 2 ++ app/javascript/mastodon/locales/pl.json | 2 ++ app/javascript/mastodon/locales/pt-BR.json | 2 ++ app/javascript/mastodon/locales/pt.json | 2 ++ app/javascript/mastodon/locales/ru.json | 2 ++ app/javascript/mastodon/locales/th.json | 2 ++ app/javascript/mastodon/locales/tr.json | 2 ++ app/javascript/mastodon/locales/uk.json | 2 ++ app/javascript/mastodon/locales/zh-CN.json | 2 ++ app/javascript/mastodon/locales/zh-HK.json | 2 ++ app/javascript/mastodon/locales/zh-TW.json | 2 ++ app/lib/user_settings_decorator.rb | 5 +++++ app/models/user.rb | 4 ++++ app/serializers/initial_state_serializer.rb | 1 + app/views/settings/preferences/show.html.haml | 1 + config/locales/simple_form.en.yml | 1 + spec/lib/user_settings_decorator_spec.rb | 7 +++++++ spec/models/user_spec.rb | 8 ++++++++ 43 files changed, 146 insertions(+), 5 deletions(-) (limited to 'app/javascript') diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index a3f5a008b..f107f2b16 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -35,6 +35,7 @@ class Settings::PreferencesController < ApplicationController params.require(:user).permit( :setting_default_privacy, :setting_default_sensitive, + :setting_unfollow_modal, :setting_boost_modal, :setting_delete_modal, :setting_auto_play_gif, diff --git a/app/javascript/mastodon/containers/account_container.js b/app/javascript/mastodon/containers/account_container.js index 1426bcaa4..ca1efd0e5 100644 --- a/app/javascript/mastodon/containers/account_container.js +++ b/app/javascript/mastodon/containers/account_container.js @@ -1,4 +1,6 @@ +import React from 'react'; import { connect } from 'react-redux'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { makeGetAccount } from '../selectors'; import Account from '../components/account'; import { @@ -9,6 +11,11 @@ import { muteAccount, unmuteAccount, } from '../actions/accounts'; +import { openModal } from '../actions/modal'; + +const messages = defineMessages({ + unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' }, +}); const makeMapStateToProps = () => { const getAccount = makeGetAccount(); @@ -16,15 +23,25 @@ const makeMapStateToProps = () => { const mapStateToProps = (state, props) => ({ account: getAccount(state, props.id), me: state.getIn(['meta', 'me']), + unfollowModal: state.getIn(['meta', 'unfollow_modal']), }); return mapStateToProps; }; -const mapDispatchToProps = (dispatch) => ({ +const mapDispatchToProps = (dispatch, { intl }) => ({ + onFollow (account) { if (account.getIn(['relationship', 'following'])) { - dispatch(unfollowAccount(account.get('id'))); + if (this.unfollowModal) { + dispatch(openModal('CONFIRM', { + message: @{account.get('acct')} }} />, + confirm: intl.formatMessage(messages.unfollowConfirm), + onConfirm: () => dispatch(unfollowAccount(account.get('id'))), + })); + } else { + dispatch(unfollowAccount(account.get('id'))); + } } else { dispatch(followAccount(account.get('id'))); } @@ -45,6 +62,7 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(muteAccount(account.get('id'))); } }, + }); -export default connect(makeMapStateToProps, mapDispatchToProps)(Account); +export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Account)); diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.js index 19dd64699..baa81bbc2 100644 --- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js +++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js @@ -17,6 +17,7 @@ import { blockDomain, unblockDomain } from '../../../actions/domain_blocks'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; const messages = defineMessages({ + unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' }, blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, muteConfirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' }, blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' }, @@ -28,15 +29,25 @@ const makeMapStateToProps = () => { const mapStateToProps = (state, { accountId }) => ({ account: getAccount(state, Number(accountId)), me: state.getIn(['meta', 'me']), + unfollowModal: state.getIn(['meta', 'unfollow_modal']), }); return mapStateToProps; }; const mapDispatchToProps = (dispatch, { intl }) => ({ + onFollow (account) { if (account.getIn(['relationship', 'following'])) { - dispatch(unfollowAccount(account.get('id'))); + if (this.unfollowModal) { + dispatch(openModal('CONFIRM', { + message: @{account.get('acct')} }} />, + confirm: intl.formatMessage(messages.unfollowConfirm), + onConfirm: () => dispatch(unfollowAccount(account.get('id'))), + })); + } else { + dispatch(unfollowAccount(account.get('id'))); + } } else { dispatch(followAccount(account.get('id'))); } @@ -85,6 +96,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onUnblockDomain (domain, accountId) { dispatch(unblockDomain(domain, accountId)); }, + }); export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header)); diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index 4240871a7..f303088d7 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -85,7 +85,7 @@ export default class ModalRoot extends React.PureComponent { > {interpolatedStyles =>
- {interpolatedStyles.map(({ key, data: { type }, style }) => ( + {interpolatedStyles.map(({ key, data: { type, props }, style }) => (
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 7b890ce64..89ddb2d15 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "أكتم", "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "الأنشطة", "emoji_button.flags": "الأعلام", "emoji_button.food": "الطعام والشراب", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 0cf6bf3ac..3dba91b82 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 1e44d6fa5..54f2e5e22 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Estàs realment, realment segur que vols bloquejar totalment {domain}? En la majoria dels casos bloquejar o silenciar és suficient i preferible.", "confirmations.mute.confirm": "Silenciar", "confirmations.mute.message": "Estàs segur que vols silenciar {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activitat", "emoji_button.flags": "Flags", "emoji_button.food": "Menjar i Beure", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index f73011e73..a041e6655 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index aaa558c0e..bf462a537 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -228,6 +228,19 @@ ], "path": "app/javascript/mastodon/components/video_player.json" }, + { + "descriptors": [ + { + "defaultMessage": "Unfollow", + "id": "confirmations.unfollow.confirm" + }, + { + "defaultMessage": "Are you sure you want to unfollow {name}?", + "id": "confirmations.unfollow.message" + } + ], + "path": "app/javascript/mastodon/containers/account_container.json" + }, { "descriptors": [ { @@ -268,6 +281,10 @@ }, { "descriptors": [ + { + "defaultMessage": "Unfollow", + "id": "confirmations.unfollow.confirm" + }, { "defaultMessage": "Block", "id": "confirmations.block.confirm" @@ -280,6 +297,10 @@ "defaultMessage": "Hide entire domain", "id": "confirmations.domain_block.confirm" }, + { + "defaultMessage": "Are you sure you want to unfollow {name}?", + "id": "confirmations.unfollow.message" + }, { "defaultMessage": "Are you sure you want to block {name}?", "id": "confirmations.block.message" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 15afe2309..fe2bd4cb4 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 4f9e26c25..029cef883 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 64ba78716..36ad66ace 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 306937cc2..113daef77 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "آیا جدی جدی می‌خواهید کل دامین {domain} را مسدود کنید؟ بیشتر وقت‌ها مسدودکردن یا بی‌صداکردن چند حساب کاربری خاص کافی است و توصیه می‌شود.", "confirmations.mute.confirm": "بی‌صدا کن", "confirmations.mute.message": "آیا واقعاً می‌خواهید {name} را بی‌صدا کنید؟", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "فعالیت", "emoji_button.flags": "پرچم‌ها", "emoji_button.food": "غذا و نوشیدنی", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 1b17fb155..da9e5d0f2 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index b6605295b..c3e743259 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -55,6 +55,8 @@ "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 masquages ciblés sont suffisants et préférables.", "confirmations.mute.confirm": "Masquer", "confirmations.mute.message": "Confirmez vous le masquage de {name} ?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activités", "emoji_button.flags": "Drapeaux", "emoji_button.food": "Boire et manger", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 8b63bd26b..c8dc4fe8d 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "באמת באמת לחסום את כל קהילת {domain}? ברב המקרים השתקות נבחרות של מספר משתמשים מסויימים צריכה להספיק.", "confirmations.mute.confirm": "להשתיק", "confirmations.mute.message": "להשתיק את {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "פעילות", "emoji_button.flags": "דגלים", "emoji_button.food": "אוכל ושתיה", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index 165e3088f..fd669b2b2 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Jesi li zaista, zaista siguran da želiš blokirati sve sa {domain}? U većini slučajeva nekoliko ciljanih blokiranja ili utišavanja je dostatno i poželjnije.", "confirmations.mute.confirm": "Utišaj", "confirmations.mute.message": "Jesi li siguran da želiš utišati {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Aktivnost", "emoji_button.flags": "Zastave", "emoji_button.food": "Hrana & Piće", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 71dcce505..b3672cb7a 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 0c21877d8..8a17262fe 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Bisukan", "confirmations.mute.message": "Apa anda yakin ingin membisukan {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Aktivitas", "emoji_button.flags": "Bendera", "emoji_button.food": "Makanan & Minuman", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 788d09f34..154ca66ce 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 9176bfaaf..6cb274bae 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index a686cdc03..c62e36482 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "本当に{domain}全体を非表示にしますか? 多くの場合は個別にブロックやミュートするだけで充分であり、また好ましいです。", "confirmations.mute.confirm": "ミュート", "confirmations.mute.message": "本当に{name}をミュートしますか?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "活動", "emoji_button.flags": "国旗", "emoji_button.food": "食べ物", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 0b47cc990..fbd0098d7 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "정말로 {domain} 전체를 숨기시겠습니까? 대부분의 경우 개별 차단이나 뮤트로 충분합니다.", "confirmations.mute.confirm": "뮤트", "confirmations.mute.message": "정말로 {name}를 뮤트하시겠습니까?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "활동", "emoji_button.flags": "국기", "emoji_button.food": "음식", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index cf6a8bd31..f7b0bbf68 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Weet je het echt, echt zeker dat je alles van {domain} wil negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en gewenst.", "confirmations.mute.confirm": "Negeren", "confirmations.mute.message": "Weet je zeker dat je {name} wilt negeren?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activiteiten", "emoji_button.flags": "Vlaggen", "emoji_button.food": "Eten en drinken", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 1f4082d7b..98f59f774 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Er du sikker på at du vil skjule hele domenet {domain}? I de fleste tilfeller er det bedre med målrettet blokkering eller demping.", "confirmations.mute.confirm": "Demp", "confirmations.mute.message": "Er du sikker på at du vil dempe {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Aktivitet", "emoji_button.flags": "Flagg", "emoji_button.food": "Mat og drikke", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index dc6dd5e32..ca094c18a 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Sètz segur segur de voler blocar complètament {domain} ? De còps cal pas que blocar o rescondre unas personas solament.", "confirmations.mute.confirm": "Metre en silenci", "confirmations.mute.message": "Sètz segur de voler metre en silenci {name} ?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activitat", "emoji_button.flags": "Drapèus", "emoji_button.food": "Beure e manjar", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 233d61995..683f589b1 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Czy na pewno chcesz zablokować całą domenę {domain}? Zwykle lepszym rozwiązaniem jest blokada lub wyciszenie kilku użytkowników.", "confirmations.mute.confirm": "Wycisz", "confirmations.mute.message": "Czy na pewno chcesz wyciszyć {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Aktywność", "emoji_button.flags": "Flagi", "emoji_button.food": "Żywność i napoje", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index cf2b911f2..3944e33e9 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index cf2b911f2..3944e33e9 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 942a13ede..cffc285f4 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Вы на самом деле уверены, что хотите блокировать весь {domain}? В большинстве случаев нескольких отдельных блокировок или глушений достаточно.", "confirmations.mute.confirm": "Заглушить", "confirmations.mute.message": "Вы уверены, что хотите заглушить {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Занятия", "emoji_button.flags": "Флаги", "emoji_button.food": "Еда и напитки", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index e9e96c14f..63bed6d8c 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Mute", "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Activity", "emoji_button.flags": "Flags", "emoji_button.food": "Food & Drink", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index adfa79cd9..5bd308e95 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "Sessize al", "confirmations.mute.message": "{name} kullanıcısını sessize almak istiyor musunuz?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Aktivite", "emoji_button.flags": "Bayraklar", "emoji_button.food": "Yiyecek ve İçecek", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 435067281..e1611505d 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Ви точно, точно впевнені, що хочете заблокувати весь домен {domain}? У більшості випадків для нормальної роботи краще заблокувати/заглушити лише деяких користувачів.", "confirmations.mute.confirm": "Заглушити", "confirmations.mute.message": "Ви впевнені, що хочете заглушити {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "Заняття", "emoji_button.flags": "Прапори", "emoji_button.food": "Їжа та напої", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 0f2c1fcec..18bf872e5 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "静音", "confirmations.mute.message": "想好了,真的要静音 {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "活动", "emoji_button.flags": "旗帜", "emoji_button.food": "食物和饮料", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index c0b4cfce9..a461085c7 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", "confirmations.mute.confirm": "靜音", "confirmations.mute.message": "你確定要將{name}靜音嗎?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "活動", "emoji_button.flags": "旗幟", "emoji_button.food": "飲飲食食", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 772cc691c..d766fb394 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -55,6 +55,8 @@ "confirmations.domain_block.message": "你真的真的確定要封鎖整個 {domain} ?多數情況下,比較推薦封鎖或消音幾個特定目標就好。", "confirmations.mute.confirm": "消音", "confirmations.mute.message": "你確定要消音 {name} ?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", "emoji_button.activity": "活動", "emoji_button.flags": "旗幟", "emoji_button.food": "食物與飲料", diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index c5da18029..62046ed72 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -19,6 +19,7 @@ class UserSettingsDecorator user.settings['interactions'] = merged_interactions user.settings['default_privacy'] = default_privacy_preference user.settings['default_sensitive'] = default_sensitive_preference + user.settings['unfollow_modal'] = unfollow_modal_preference user.settings['boost_modal'] = boost_modal_preference user.settings['delete_modal'] = delete_modal_preference user.settings['auto_play_gif'] = auto_play_gif_preference @@ -42,6 +43,10 @@ class UserSettingsDecorator boolean_cast_setting 'setting_default_sensitive' end + def unfollow_modal_preference + boolean_cast_setting 'setting_unfollow_modal' + 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 becf0018f..25dc25864 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -83,6 +83,10 @@ class User < ApplicationRecord settings.default_sensitive end + def setting_unfollow_modal + settings.unfollow_modal + 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 704d29a57..0191948b1 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -15,6 +15,7 @@ class InitialStateSerializer < ActiveModel::Serializer if object.current_account store[:me] = object.current_account.id + store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal store[:boost_modal] = object.current_account.user.setting_boost_modal store[:delete_modal] = object.current_account.user.setting_delete_modal store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 3b5d90942..fae6090c8 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -44,6 +44,7 @@ = f.input :setting_noindex, as: :boolean, wrapper: :with_label .fields-group + = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label = f.input :setting_delete_modal, as: :boolean, wrapper: :with_label diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 476ccc773..536bb06e1 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -42,6 +42,7 @@ en: 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 + setting_unfollow_modal: Show confirmation dialog before unfollowing someone setting_noindex: Opt-out of search engine indexing severity: Severity type: Import type diff --git a/spec/lib/user_settings_decorator_spec.rb b/spec/lib/user_settings_decorator_spec.rb index a67487779..6fbf6536b 100644 --- a/spec/lib/user_settings_decorator_spec.rb +++ b/spec/lib/user_settings_decorator_spec.rb @@ -35,6 +35,13 @@ describe UserSettingsDecorator do expect(user.settings['default_sensitive']).to eq true end + it 'updates the user settings value for unfollow modal' do + values = { 'setting_unfollow_modal' => '0' } + + settings.update(values) + expect(user.settings['unfollow_modal']).to eq false + end + it 'updates the user settings value for boost modal' do values = { 'setting_boost_modal' => '1' } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2019ec0f6..ef45818b9 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -219,6 +219,14 @@ RSpec.describe User, type: :model do end end + describe '#setting_unfollow_modal' do + it 'returns unfollow modal setting' do + user = Fabricate(:user) + user.settings[:unfollow_modal] = true + expect(user.setting_unfollow_modal).to eq true + end + end + describe '#setting_delete_modal' do it 'returns delete modal setting' do user = Fabricate(:user) -- cgit From 0ccd47f413fe249c2c848596789b83bdd228a58b Mon Sep 17 00:00:00 2001 From: lindwurm Date: Wed, 19 Jul 2017 01:42:59 +0900 Subject: Update Japanese translations for new landing page (#4159) Signed-off-by: lindwurm --- app/javascript/mastodon/locales/ja.json | 2 +- config/locales/ja.yml | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index c62e36482..f58395d10 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -151,7 +151,7 @@ "report.target": "問題のユーザー", "search.placeholder": "検索", "search_results.total": "{count, number}件の結果", - "standalone.public_title": "A look inside...", + "standalone.public_title": "連合タイムライン", "status.cannot_reblog": "この投稿はブーストできません", "status.delete": "削除", "status.favourite": "お気に入り", diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 2897e864f..fc5a98d09 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1,15 +1,28 @@ --- ja: about: - about_mastodon: Mastodon は自由でオープンソースなソーシャルネットワークです。商用プラットフォームの代替となる分散型を採用し、あなたのやりとりが一つの会社によって独占されるのを防ぎます。信頼できるインスタンスを選択してください — どのインスタンスを選んでも、誰とでもやりとりすることができます。 だれでも自分の Mastodon インスタンスを作ることができ、シームレスにソーシャルネットワークに参加できます。 + about_mastodon_html: Mastodon は、オープンなウェブプロトコルを採用した、自由でオープンソースなソーシャルネットワークです。電子メールのような分散型の仕組みを採っています。 about_this: このインスタンスについて business_email: 'ビジネスメールアドレス:' - closed_registrations: 現在このインスタンスでの新規登録は受け付けていません。 + closed_registrations: 現在このインスタンスでの新規登録は受け付けていません。しかし、他のインスタンスにアカウントを作成しても全く同じネットワークに参加することができます。 contact: 連絡先 description_headline: "%{domain} とは?" domain_count_after: 個のインスタンス domain_count_before: 接続中 + features: + humane_approach_body: 他の SNS の失敗から学び、Mastodon はソーシャルメディアが誤った使い方をされることの無いように倫理的な設計を目指しています。 + humane_approach_title: より思いやりのある設計 + not_a_product_body: Mastodon は営利的な SNS ではありません。広告や、データの収集・解析は無く、またユーザーの囲い込みもありません。 + not_a_product_title: あなたは人間であり、商品ではありません + real_conversation_body: 好きなように書ける500文字までの投稿や、文章やメディアの内容に警告をつけられる機能で、思い通りに自分自身を表現することができます。 + real_conversation_title: 本当のコミュニケーションのために + within_reach_body: デベロッパーフレンドリーな API により実現された、iOS や Android、その他様々なプラットフォームのためのアプリでどこでも友人とやりとりできます。 + within_reach_title: いつでも身近に + find_another_instance: 他のインスタンスを探す + generic_description: "%{domain} は、Mastodon インスタンスの一つです。" get_started: 参加する + hosted_on: Mastodon hosted on %{domain} + learn_more: もっと詳しく links: リンク other_instances: 他のインスタンス source_code: ソースコード @@ -19,6 +32,7 @@ ja: user_count_after: 人 user_count_before: ユーザー数 version: バージョン + what_is_mastodon: Mastodon とは? accounts: follow: フォロー followers: フォロワー @@ -205,9 +219,10 @@ ja: applications: invalid_url: URLが無効です auth: + agreement_html: 登録すると 利用規約プライバシーポリシー に同意したことになります。 change_password: セキュリティ delete_account: アカウントの削除 - delete_account_html: アカウントを削除したい場合、こちらから手続きが行えます。削除前には確認画面があります。 + delete_account_html: アカウントを削除したい場合、こちら から手続きが行えます。削除する前に、確認画面があります。 didnt_get_confirmation: 確認メールを受信できませんか? forgot_password: パスワードをお忘れですか? login: ログイン -- cgit From 0b4006fc47dcd3d57ffdd0b95c95ad4c40a4918f Mon Sep 17 00:00:00 2001 From: lindwurm Date: Wed, 19 Jul 2017 01:43:19 +0900 Subject: Add Japanese translation for #4246 (#4249) Signed-off-by: lindwurm --- app/javascript/mastodon/locales/ja.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/javascript') diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index f58395d10..b3943f646 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -55,8 +55,8 @@ "confirmations.domain_block.message": "本当に{domain}全体を非表示にしますか? 多くの場合は個別にブロックやミュートするだけで充分であり、また好ましいです。", "confirmations.mute.confirm": "ミュート", "confirmations.mute.message": "本当に{name}をミュートしますか?", - "confirmations.unfollow.confirm": "Unfollow", - "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "confirmations.unfollow.confirm": "フォロー解除", + "confirmations.unfollow.message": "本当に{name}のフォローを解除しますか?", "emoji_button.activity": "活動", "emoji_button.flags": "国旗", "emoji_button.food": "食べ物", -- cgit