From 7a0f781aa9aad505dbde6a2bf3aa5048638c2de0 Mon Sep 17 00:00:00 2001 From: Spanky <2788886+SpankyWorks@users.noreply.github.com> Date: Sat, 18 Aug 2018 12:40:35 -0500 Subject: [PATCH 01/10] Fix ctrl+enter not submitting toot when text cursor is composing image description (#8273) --- .../mastodon/features/compose/components/upload.js | 13 +++++++++++++ .../features/compose/containers/upload_container.js | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js index bfa2b47271a..3d09217dc30 100644 --- a/app/javascript/mastodon/features/compose/components/upload.js +++ b/app/javascript/mastodon/features/compose/components/upload.js @@ -20,6 +20,7 @@ export default class Upload extends ImmutablePureComponent { onUndo: PropTypes.func.isRequired, onDescriptionChange: PropTypes.func.isRequired, onOpenFocalPoint: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, }; state = { @@ -28,6 +29,17 @@ export default class Upload extends ImmutablePureComponent { dirtyDescription: null, }; + handleKeyDown = (e) => { + if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { + this.handleSubmit(); + } + } + + handleSubmit = () => { + this.handleInputBlur(); + this.props.onSubmit(); + } + handleUndoClick = () => { this.props.onUndo(this.props.media.get('id')); } @@ -93,6 +105,7 @@ export default class Upload extends ImmutablePureComponent { onFocus={this.handleInputFocus} onChange={this.handleInputChange} onBlur={this.handleInputBlur} + onKeyDown={this.handleKeyDown} /> diff --git a/app/javascript/mastodon/features/compose/containers/upload_container.js b/app/javascript/mastodon/features/compose/containers/upload_container.js index d6b57e5ffbb..9f3aab4bcdd 100644 --- a/app/javascript/mastodon/features/compose/containers/upload_container.js +++ b/app/javascript/mastodon/features/compose/containers/upload_container.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import Upload from '../components/upload'; import { undoUploadCompose, changeUploadCompose } from '../../../actions/compose'; import { openModal } from '../../../actions/modal'; +import { submitCompose } from '../../../actions/compose'; const mapStateToProps = (state, { id }) => ({ media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id), @@ -21,6 +22,10 @@ const mapDispatchToProps = dispatch => ({ dispatch(openModal('FOCAL_POINT', { id })); }, + onSubmit () { + dispatch(submitCompose()); + }, + }); export default connect(mapStateToProps, mapDispatchToProps)(Upload); From abc5548cca42dd87926c7c3d4b42b59a7e306eb8 Mon Sep 17 00:00:00 2001 From: ThibG Date: Sat, 18 Aug 2018 19:42:13 +0200 Subject: [PATCH 02/10] Do not process outgoing mentions to suspended accounts (#8272) --- app/services/process_mentions_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index 2ed6698cf28..b4641c4b4ab 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -25,7 +25,7 @@ class ProcessMentionsService < BaseService end end - next match if mention_undeliverable?(mentioned_account) + next match if mention_undeliverable?(mentioned_account) || mentioned_account&.suspended mentions << mentioned_account.mentions.where(status: status).first_or_create(status: status) From c6eab9e0aa06928e644cfe8445c0787e5515cd4f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 18 Aug 2018 21:06:12 +0200 Subject: [PATCH 03/10] Add Makara dependency to support read-replicas (#8216) Not active by default unless config/database.yml is edited --- Gemfile | 1 + Gemfile.lock | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index cb34ae2ec15..31c3c808619 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,7 @@ gem 'rails', '~> 5.2.1' gem 'hamlit-rails', '~> 0.2' gem 'pg', '~> 1.0' +gem 'makara', '~> 0.4' gem 'pghero', '~> 2.1' gem 'dotenv-rails', '~> 2.2', '< 2.3' diff --git a/Gemfile.lock b/Gemfile.lock index ba7bdaa7b8f..71f2f6c7df9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -322,6 +322,8 @@ GEM nokogiri (>= 1.5.9) mail (2.7.0) mini_mime (>= 0.1.1) + makara (0.4.0) + activerecord (>= 3.0.0) marcel (0.3.2) mimemagic (~> 0.3.2) mario-redis-lock (1.2.1) @@ -697,6 +699,7 @@ DEPENDENCIES letter_opener_web (~> 1.3) link_header (~> 0.0) lograge (~> 0.10) + makara (~> 0.4) mario-redis-lock (~> 1.2) memory_profiler microformats (~> 4.0) From 9dd5639f90e7a256863a2b1fc199390def8bdb14 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 19 Aug 2018 00:58:53 +0200 Subject: [PATCH 04/10] Add admin function to deactivate all invites (#8279) Fix #8261 --- app/controllers/admin/invites_controller.rb | 6 +++++ app/policies/invite_policy.rb | 4 +++ app/views/admin/invites/index.html.haml | 28 +++++++++++++-------- config/locales/en.yml | 1 + config/routes.rb | 7 +++++- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/app/controllers/admin/invites_controller.rb b/app/controllers/admin/invites_controller.rb index faccaa7c899..44a8eec77b2 100644 --- a/app/controllers/admin/invites_controller.rb +++ b/app/controllers/admin/invites_controller.rb @@ -30,6 +30,12 @@ module Admin redirect_to admin_invites_path end + def deactivate_all + authorize :invite, :deactivate_all? + Invite.available.in_batches.update_all(expires_at: Time.now.utc) + redirect_to admin_invites_path + end + private def resource_params diff --git a/app/policies/invite_policy.rb b/app/policies/invite_policy.rb index a2a65f934d9..14236f78b8c 100644 --- a/app/policies/invite_policy.rb +++ b/app/policies/invite_policy.rb @@ -9,6 +9,10 @@ class InvitePolicy < ApplicationPolicy min_required_role? end + def deactivate_all? + admin? + end + def destroy? owner? || (Setting.min_invite_role == 'admin' ? admin? : staff?) end diff --git a/app/views/admin/invites/index.html.haml b/app/views/admin/invites/index.html.haml index 944a6047141..42159e9f367 100644 --- a/app/views/admin/invites/index.html.haml +++ b/app/views/admin/invites/index.html.haml @@ -9,22 +9,28 @@ %li= filter_link_to t('admin.invites.filter.available'), available: 1, expired: nil %li= filter_link_to t('admin.invites.filter.expired'), available: nil, expired: 1 +%hr.spacer/ + - if policy(:invite).create? %p= t('invites.prompt') = render 'invites/form' - %hr/ + %hr.spacer/ -%table.table - %thead - %tr - %th - %th= t('invites.table.uses') - %th= t('invites.table.expires_at') - %th - %th - %tbody - = render @invites +.table-wrapper + %table.table + %thead + %tr + %th + %th= t('invites.table.uses') + %th= t('invites.table.expires_at') + %th + %th + %tbody + = render @invites = paginate @invites + +- if policy(:invite).deactivate_all? + = link_to t('admin.invites.deactivate_all'), deactivate_all_admin_invites_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' diff --git a/config/locales/en.yml b/config/locales/en.yml index 66ab8f10a1f..65fc7b78e07 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -281,6 +281,7 @@ en: search: Search title: Known instances invites: + deactivate_all: Deactivate all filter: all: All available: Available diff --git a/config/routes.rb b/config/routes.rb index fd3b5fe4ba1..a8716aae593 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,7 +134,12 @@ Rails.application.routes.draw do resources :email_domain_blocks, only: [:index, :new, :create, :destroy] resources :action_logs, only: [:index] resource :settings, only: [:edit, :update] - resources :invites, only: [:index, :create, :destroy] + + resources :invites, only: [:index, :create, :destroy] do + collection do + post :deactivate_all + end + end resources :relays, only: [:index, :new, :create, :destroy] do member do From 0fc0980de1d8b9fd94da5aa4ce5f222f57649eff Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 19 Aug 2018 01:17:44 +0200 Subject: [PATCH 05/10] Link to mobile apps page (#8278) Fix #8269 --- app/javascript/mastodon/features/getting_started/index.js | 1 + app/views/layouts/public.html.haml | 2 +- config/locales/en.yml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 074ab01c8be..95af8997eb8 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -139,6 +139,7 @@ export default class GettingStarted extends ImmutablePureComponent { {multiColumn &&
  • ·
  • }
  • ·
  • ·
  • +
  • ·
  • ·
  • ·
  • ·
  • diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml index f9d808bede8..ca9dac8bb03 100644 --- a/app/views/layouts/public.html.haml +++ b/app/views/layouts/public.html.haml @@ -45,6 +45,6 @@ %h4= t 'footer.more' %ul %li= link_to t('about.source_code'), Mastodon::Version.source_url - %li= link_to 'joinmastodon.org', 'https://joinmastodon.org' + %li= link_to t('about.apps'), 'https://joinmastodon.org/apps' = render template: 'layouts/application' diff --git a/config/locales/en.yml b/config/locales/en.yml index 65fc7b78e07..7809b8e68fc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -6,6 +6,7 @@ en: about_this: About administered_by: 'Administered by:' api: API + apps: Mobile apps closed_registrations: Registrations are currently closed on this instance. However! You can find a different instance to make an account on and get access to the very same network from there. contact: Contact contact_missing: Not set From f13afa1ee92ceb40ce2e44f6457bd6e75b80adb5 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 19 Aug 2018 02:01:49 +0200 Subject: [PATCH 06/10] Do not use WHERE NOT IN in CopyStatusStats migration (#8281) Fix #8275 As the batch operation progresses, the statuses_stats table grows, and the WHERE NOT IN subquery becomes more expensive --- db/migrate/20180812173710_copy_status_stats.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20180812173710_copy_status_stats.rb b/db/migrate/20180812173710_copy_status_stats.rb index 0c5907c301c..850aa9c13ee 100644 --- a/db/migrate/20180812173710_copy_status_stats.rb +++ b/db/migrate/20180812173710_copy_status_stats.rb @@ -3,7 +3,7 @@ class CopyStatusStats < ActiveRecord::Migration[5.2] def up safety_assured do - Status.where.not(id: StatusStat.select('status_id')).select('id').find_in_batches do |statuses| + Status.unscoped.select('id').find_in_batches(batch_size: 5_000) do |statuses| execute <<-SQL.squish INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at) SELECT id, reblogs_count, favourites_count, created_at, updated_at From 025fbb8285cdedf495d9e4d015db611b0c622cbb Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 19 Aug 2018 03:17:01 +0200 Subject: [PATCH 07/10] Show compose form on delete & redraft when in mobile layout (#8277) Fix #8274 --- app/javascript/mastodon/actions/statuses.js | 6 +++++- app/javascript/mastodon/components/status_action_bar.js | 4 ++-- app/javascript/mastodon/containers/status_container.js | 6 +++--- .../mastodon/features/status/components/action_bar.js | 4 ++-- app/javascript/mastodon/features/status/index.js | 6 +++--- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 3e1e5f27096..8d5e72beca3 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -140,7 +140,7 @@ export function redraft(status) { }; }; -export function deleteStatus(id, withRedraft = false) { +export function deleteStatus(id, router, withRedraft = false) { return (dispatch, getState) => { const status = getState().getIn(['statuses', id]); @@ -153,6 +153,10 @@ export function deleteStatus(id, withRedraft = false) { if (withRedraft) { dispatch(redraft(status)); + + if (!getState().getIn(['compose', 'mounted'])) { + router.push('/statuses/new'); + } } }).catch(error => { dispatch(deleteStatusFail(id, error)); diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index c799d4e9860..6d44a4b4511 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -96,11 +96,11 @@ export default class StatusActionBar extends ImmutablePureComponent { } handleDeleteClick = () => { - this.props.onDelete(this.props.status); + this.props.onDelete(this.props.status, this.context.router.history); } handleRedraftClick = () => { - this.props.onDelete(this.props.status, true); + this.props.onDelete(this.props.status, this.context.router.history, true); } handlePinClick = () => { diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index eb6329fdcdd..ed375c3e523 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -93,14 +93,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ })); }, - onDelete (status, withRedraft = false) { + onDelete (status, history, withRedraft = false) { if (!deleteModal) { - dispatch(deleteStatus(status.get('id'), withRedraft)); + dispatch(deleteStatus(status.get('id'), history, withRedraft)); } else { dispatch(openModal('CONFIRM', { message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage), confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm), - onConfirm: () => dispatch(deleteStatus(status.get('id'), withRedraft)), + onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)), })); } }, diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index 54149966817..f5977c02cd7 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -65,11 +65,11 @@ export default class ActionBar extends React.PureComponent { } handleDeleteClick = () => { - this.props.onDelete(this.props.status); + this.props.onDelete(this.props.status, this.context.router.history); } handleRedraftClick = () => { - this.props.onDelete(this.props.status, true); + this.props.onDelete(this.props.status, this.context.router.history, true); } handleDirectClick = () => { diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 0ffeaa4dc11..e506733b407 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -174,16 +174,16 @@ export default class Status extends ImmutablePureComponent { } } - handleDeleteClick = (status, withRedraft = false) => { + handleDeleteClick = (status, history, withRedraft = false) => { const { dispatch, intl } = this.props; if (!deleteModal) { - dispatch(deleteStatus(status.get('id'), withRedraft)); + dispatch(deleteStatus(status.get('id'), history, withRedraft)); } else { dispatch(openModal('CONFIRM', { message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage), confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm), - onConfirm: () => dispatch(deleteStatus(status.get('id'), withRedraft)), + onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)), })); } } From 58ffe3f7c3d73f02d646869a5ed500fa7ff846d6 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Sun, 19 Aug 2018 10:20:37 +0900 Subject: [PATCH 08/10] Revert "Upgrade Doorkeeper to 4.4.1 (#8197)" (#8231) * Revert "Upgrade Doorkeeper to 4.4.1 (#8197)" This reverts commit 464daffdf9a37e9a773d224a162fad022890d463. * Except migration files from revert commit --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 31c3c808619..5a6afe9ad5f 100644 --- a/Gemfile +++ b/Gemfile @@ -42,7 +42,7 @@ gem 'omniauth-cas', '~> 1.1' gem 'omniauth-saml', '~> 1.10' gem 'omniauth', '~> 1.2' -gem 'doorkeeper', '~> 4.4' +gem 'doorkeeper', '~> 4.2', '< 4.3' gem 'fast_blank', '~> 1.0' gem 'fastimage' gem 'goldfinger', '~> 2.1' diff --git a/Gemfile.lock b/Gemfile.lock index 71f2f6c7df9..32851c67bf7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -181,7 +181,7 @@ GEM docile (1.3.0) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) - doorkeeper (4.4.1) + doorkeeper (4.2.6) railties (>= 4.2) dotenv (2.2.2) dotenv-rails (2.2.2) @@ -672,7 +672,7 @@ DEPENDENCIES devise (~> 4.4) devise-two-factor (~> 3.0) devise_pam_authenticatable2 (~> 9.1) - doorkeeper (~> 4.4) + doorkeeper (~> 4.2, < 4.3) dotenv-rails (~> 2.2, < 2.3) fabrication (~> 2.20) faker (~> 1.8) From 19b07ba260a64b300a81890119af0970df0eb994 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 19 Aug 2018 03:28:43 +0200 Subject: [PATCH 09/10] Make unfollow button light up red when hovered (#8286) Fix #8284 --- app/helpers/stream_entries_helper.rb | 2 +- app/javascript/styles/mastodon/components.scss | 11 +++++++++++ app/javascript/styles/mastodon/stream_entries.scss | 12 ++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index 121644263dc..9ded69436b1 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -19,7 +19,7 @@ module StreamEntriesHelper safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('settings.edit_profile')]) end elsif current_account.following?(account) || current_account.requested?(account) - link_to account_unfollow_path(account), class: 'button logo-button', data: { method: :post } do + link_to account_unfollow_path(account), class: 'button logo-button button--destructive', data: { method: :post } do safe_join([render(file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')), t('accounts.unfollow')]) end else diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index cfd8e5ad484..7c58828fd69 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -35,6 +35,17 @@ transition: all 200ms ease-out; } + &--destructive { + transition: none; + + &:active, + &:focus, + &:hover { + background-color: $error-red; + transition: none; + } + } + &:disabled { background-color: $ui-primary-color; cursor: default; diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss index 5aa809f76b9..14306c8bdf1 100644 --- a/app/javascript/styles/mastodon/stream_entries.scss +++ b/app/javascript/styles/mastodon/stream_entries.scss @@ -110,6 +110,18 @@ } } + &.button--destructive { + &:active, + &:focus, + &:hover { + background: $error-red; + + svg path:last-child { + fill: $error-red; + } + } + } + @media screen and (max-width: $no-gap-breakpoint) { svg { display: none; From 59c68c1a74d5398b9c31489744ff8eca82e2ce50 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 19 Aug 2018 03:50:34 +0200 Subject: [PATCH 10/10] Reduce user active duration from 7 days to 2 days (#8282) To minimize fanout work and redis home feed storage space when there are lots of recent sign-ups --- app/models/user.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index c820c553a7f..a2cf2565fd5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -42,7 +42,14 @@ class User < ApplicationRecord include Settings::Extend include Omniauthable - ACTIVE_DURATION = 7.days + # The home and list feeds will be stored in Redis for this amount + # of time, and status fan-out to followers will include only people + # within this time frame. Lowering the duration may improve performance + # if lots of people sign up, but not a lot of them check their feed + # every day. Raising the duration reduces the amount of expensive + # RegenerationWorker jobs that need to be run when those people come + # to check their feed + ACTIVE_DURATION = ENV.fetch('USER_ACTIVE_DAYS', 7).to_i.days devise :two_factor_authenticatable, otp_secret_encryption_key: Rails.configuration.x.otp_secret