From ac8a788370012656cde08b2aea2bd927f0748422 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 19 Jun 2020 19:18:47 +0200 Subject: [PATCH 01/19] Fix functional user requirements in whitelist mode (#14093) Fixes #14092 --- app/controllers/accounts_controller.rb | 2 +- app/controllers/api/base_controller.rb | 2 +- app/controllers/directories_controller.rb | 2 +- app/controllers/follower_accounts_controller.rb | 2 +- app/controllers/following_accounts_controller.rb | 2 +- app/controllers/media_controller.rb | 2 +- app/controllers/remote_interaction_controller.rb | 2 +- app/controllers/statuses_controller.rb | 2 +- app/controllers/tags_controller.rb | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 62c862d36..db77b628c 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -11,7 +11,7 @@ class AccountsController < ApplicationController before_action :set_body_classes skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) } - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? def show respond_to do |format| diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 153ade253..045e7dd26 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -7,7 +7,7 @@ class Api::BaseController < ApplicationController include RateLimitHeaders skip_before_action :store_current_location - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access? before_action :set_cache_headers diff --git a/app/controllers/directories_controller.rb b/app/controllers/directories_controller.rb index 750c835dd..f198ad5ba 100644 --- a/app/controllers/directories_controller.rb +++ b/app/controllers/directories_controller.rb @@ -9,7 +9,7 @@ class DirectoriesController < ApplicationController before_action :set_tag, only: :show before_action :set_accounts - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? def index render :index diff --git a/app/controllers/follower_accounts_controller.rb b/app/controllers/follower_accounts_controller.rb index 14e22dd1e..ab0749963 100644 --- a/app/controllers/follower_accounts_controller.rb +++ b/app/controllers/follower_accounts_controller.rb @@ -8,7 +8,7 @@ class FollowerAccountsController < ApplicationController before_action :set_cache_headers skip_around_action :set_locale, if: -> { request.format == :json } - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? def index respond_to do |format| diff --git a/app/controllers/following_accounts_controller.rb b/app/controllers/following_accounts_controller.rb index 95849ffb9..918bdac0a 100644 --- a/app/controllers/following_accounts_controller.rb +++ b/app/controllers/following_accounts_controller.rb @@ -8,7 +8,7 @@ class FollowingAccountsController < ApplicationController before_action :set_cache_headers skip_around_action :set_locale, if: -> { request.format == :json } - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? def index respond_to do |format| diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index 1d166d6e7..ce015dd1b 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -4,7 +4,7 @@ class MediaController < ApplicationController include Authorization skip_before_action :store_current_location - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? before_action :authenticate_user!, if: :whitelist_mode? before_action :set_media_attachment diff --git a/app/controllers/remote_interaction_controller.rb b/app/controllers/remote_interaction_controller.rb index 3b9202a5c..6c29a2b9f 100644 --- a/app/controllers/remote_interaction_controller.rb +++ b/app/controllers/remote_interaction_controller.rb @@ -10,7 +10,7 @@ class RemoteInteractionController < ApplicationController before_action :set_status before_action :set_body_classes - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? def new @remote_follow = RemoteFollow.new(session_params) diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 67a6cc2ec..17ddd31fb 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -19,7 +19,7 @@ class StatusesController < ApplicationController before_action :set_autoplay, only: :embed skip_around_action :set_locale, if: -> { request.format == :json } - skip_before_action :require_functional!, only: [:show, :embed] + skip_before_action :require_functional!, only: [:show, :embed], unless: :whitelist_mode? content_security_policy only: :embed do |p| p.frame_ancestors(false) diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 7e86d4f1a..234a0c411 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -15,7 +15,7 @@ class TagsController < ApplicationController before_action :set_body_classes before_action :set_instance_presenter - skip_before_action :require_functional! + skip_before_action :require_functional!, unless: :whitelist_mode? def show respond_to do |format| From a279acc73071b3fb2f8381fc5051de5b70e879ef Mon Sep 17 00:00:00 2001 From: fuyu <54523771+mfmfuyu@users.noreply.github.com> Date: Sat, 20 Jun 2020 20:30:13 +0900 Subject: [PATCH 02/19] Fix not working I18n on 2FA and Sign in token page (#14087) --- app/controllers/concerns/localized.rb | 4 ++-- .../concerns/sign_in_token_authentication_concern.rb | 8 +++++--- .../concerns/two_factor_authentication_concern.rb | 8 +++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/controllers/concerns/localized.rb b/app/controllers/concerns/localized.rb index d1384ed56..fe1142f34 100644 --- a/app/controllers/concerns/localized.rb +++ b/app/controllers/concerns/localized.rb @@ -7,8 +7,6 @@ module Localized around_action :set_locale end - private - def set_locale locale = current_user.locale if respond_to?(:user_signed_in?) && user_signed_in? locale ||= session[:locale] ||= default_locale @@ -19,6 +17,8 @@ module Localized end end + private + def default_locale if ENV['DEFAULT_LOCALE'].present? I18n.default_locale diff --git a/app/controllers/concerns/sign_in_token_authentication_concern.rb b/app/controllers/concerns/sign_in_token_authentication_concern.rb index a177aacaf..91f813acc 100644 --- a/app/controllers/concerns/sign_in_token_authentication_concern.rb +++ b/app/controllers/concerns/sign_in_token_authentication_concern.rb @@ -42,8 +42,10 @@ module SignInTokenAuthenticationConcern UserMailer.sign_in_token(user, request.remote_ip, request.user_agent, Time.now.utc.to_s).deliver_later! end - session[:attempt_user_id] = user.id - @body_classes = 'lighter' - render :sign_in_token + set_locale do + session[:attempt_user_id] = user.id + @body_classes = 'lighter' + render :sign_in_token + end end end diff --git a/app/controllers/concerns/two_factor_authentication_concern.rb b/app/controllers/concerns/two_factor_authentication_concern.rb index cdd8d14af..daafe56f4 100644 --- a/app/controllers/concerns/two_factor_authentication_concern.rb +++ b/app/controllers/concerns/two_factor_authentication_concern.rb @@ -40,8 +40,10 @@ module TwoFactorAuthenticationConcern end def prompt_for_two_factor(user) - session[:attempt_user_id] = user.id - @body_classes = 'lighter' - render :two_factor + set_locale do + session[:attempt_user_id] = user.id + @body_classes = 'lighter' + render :two_factor + end end end From cb3c6d17804da67d3c0ff233a9ca1c1902f6926f Mon Sep 17 00:00:00 2001 From: fuyu <54523771+mfmfuyu@users.noreply.github.com> Date: Sat, 20 Jun 2020 20:30:27 +0900 Subject: [PATCH 03/19] Fix unnecessary gap under of video modal (#14098) --- app/javascript/styles/mastodon/components.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index acd4b93b5..79ae5874e 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5361,6 +5361,7 @@ a.status-card.compact:hover { } video { + display: block; max-width: 100vw; max-height: 80vh; z-index: 1; From 434a6d0b15ff413c6e4d7e0c3763af6429ad25b6 Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Sat, 20 Jun 2020 20:30:40 +0900 Subject: [PATCH 04/19] Fix modifier key to keep the EmojiPicker on macOS (#14096) --- .../features/compose/components/emoji_picker_dropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js index a6186010b..360a7af6a 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js @@ -203,7 +203,7 @@ class EmojiPickerMenu extends React.PureComponent { if (!emoji.native) { emoji.native = emoji.colons; } - if (!event.ctrlKey) { + if (!(event.ctrlKey || event.metaKey)) { this.props.onClose(); } this.props.onPick(emoji); From d22931454e343712c0b058a84dbdb82b15411557 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 08:25:05 +0900 Subject: [PATCH 05/19] Bump caniuse-lite from 1.0.30001078 to 1.0.30001084 (#14083) Bumps [caniuse-lite](https://github.com/ben-eb/caniuse-lite) from 1.0.30001078 to 1.0.30001084. - [Release notes](https://github.com/ben-eb/caniuse-lite/releases) - [Changelog](https://github.com/ben-eb/caniuse-lite/blob/master/CHANGELOG.md) - [Commits](https://github.com/ben-eb/caniuse-lite/compare/v1.0.30001078...v1.0.30001084) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 97e468b72..a2d3d800f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2722,9 +2722,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001061: - version "1.0.30001078" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001078.tgz#e1b6e2ae327b6a1ec11f65ec7a0dde1e7093074c" - integrity sha512-sF12qXe9VMm32IEf/+NDvmTpwJaaU7N1igpiH2FdI4DyABJSsOqG3ZAcFvszLkoLoo1y6VJLMYivukUAxaMASw== + version "1.0.30001084" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001084.tgz#00e471931eaefbeef54f46aa2203914d3c165669" + integrity sha512-ftdc5oGmhEbLUuMZ/Qp3mOpzfZLCxPYKcvGv6v2dJJ+8EdqcvZRbAGOiLmkM/PV1QGta/uwBs8/nCl6sokDW6w== capture-exit@^2.0.0: version "2.0.0" From 3bf3b4cb225374476ef1337debeb027d5dd4c1cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 08:26:21 +0900 Subject: [PATCH 06/19] Bump electron-to-chromium from 1.3.448 to 1.3.475 (#14068) Bumps [electron-to-chromium](https://github.com/kilian/electron-to-chromium) from 1.3.448 to 1.3.475. - [Release notes](https://github.com/kilian/electron-to-chromium/releases) - [Changelog](https://github.com/Kilian/electron-to-chromium/blob/master/CHANGELOG.md) - [Commits](https://github.com/kilian/electron-to-chromium/compare/v1.3.448...v1.3.475) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a2d3d800f..a172515a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3911,9 +3911,9 @@ ejs@^2.3.4, ejs@^2.6.1: integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== electron-to-chromium@^1.3.413: - version "1.3.448" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.448.tgz#682831ecf3ce505231978f7c795a2813740cae7c" - integrity sha512-WOr3SrZ55lUFYugA6sUu3H3ZoxVIH5o3zTSqYS+2DOJJP4hnHmBiD1w432a2YFW/H2G5FIxE6DB06rv+9dUL5g== + version "1.3.475" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.475.tgz#67688cc82c342f39594a412286e975eda45d8412" + integrity sha512-vcTeLpPm4+ccoYFXnepvkFt0KujdyrBU19KNEO40Pnkhta6mUi2K0Dn7NmpRcNz7BvysnSqeuIYScP003HWuYg== elliptic@^6.0.0, elliptic@^6.5.2: version "6.5.2" From 02fc97928c72fe344f8d20314c33f301754b20c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 08:27:50 +0900 Subject: [PATCH 07/19] Bump nearley from 2.19.3 to 2.19.4 (#14075) Bumps [nearley](https://github.com/hardmath123/nearley) from 2.19.3 to 2.19.4. - [Release notes](https://github.com/hardmath123/nearley/releases) - [Commits](https://github.com/hardmath123/nearley/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a172515a0..3ed06e7ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7468,9 +7468,9 @@ natural-compare@^1.4.0: integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= nearley@^2.7.10: - version "2.19.3" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.3.tgz#ae3b040e27616b5348102c436d1719209476a5a1" - integrity sha512-FpAy1PmTsUpOtgxr23g4jRNvJHYzZEW2PixXeSzksLR/ykPfwKhAodc2+9wQhY+JneWLcvkDw6q7FJIsIdF/aQ== + version "2.19.4" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.4.tgz#7518cbdd7d0e8e08b5f82841b9edb0126239c8b1" + integrity sha512-oqj3m4oqwKsN77pETa9IPvxHHHLW68KrDc2KYoWMUOhDlrNUo7finubwffQMBRnwNCOXc4kRxCZO0Rvx4L6Zrw== dependencies: commander "^2.19.0" moo "^0.5.0" From 25cd4998fe97bf255af1a09e4f59bb6863176af8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 08:28:45 +0900 Subject: [PATCH 08/19] Bump aws-sdk-core from 3.99.2 to 3.100.0 (#14072) Bumps [aws-sdk-core](https://github.com/aws/aws-sdk-ruby) from 3.99.2 to 3.100.0. - [Release notes](https://github.com/aws/aws-sdk-ruby/releases) - [Changelog](https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-core/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-ruby/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index af770f435..1b3846bfb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -93,7 +93,7 @@ GEM cocaine (~> 0.5.3) aws-eventstream (1.1.0) aws-partitions (1.329.0) - aws-sdk-core (3.99.2) + aws-sdk-core (3.100.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.239.0) aws-sigv4 (~> 1.1) From 1b29574a54e47145c36e653dd8e8edf27e157aa0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 08:30:09 +0900 Subject: [PATCH 09/19] Bump sass from 1.26.5 to 1.26.8 (#14078) Bumps [sass](https://github.com/sass/dart-sass) from 1.26.5 to 1.26.8. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/master/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.26.5...1.26.8) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a21429d6c..c0c6844f6 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "requestidlecallback": "^0.3.0", "reselect": "^4.0.0", "rimraf": "^3.0.2", - "sass": "^1.26.5", + "sass": "^1.26.8", "sass-loader": "^8.0.2", "stacktrace-js": "^2.0.2", "stringz": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index 3ed06e7ea..b37b9a902 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9773,10 +9773,10 @@ sass-loader@^8.0.2: schema-utils "^2.6.1" semver "^6.3.0" -sass@^1.26.5: - version "1.26.5" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.5.tgz#2d7aecfbbabfa298567c8f06615b6e24d2d68099" - integrity sha512-FG2swzaZUiX53YzZSjSakzvGtlds0lcbF+URuU9mxOv7WBh7NhXEVDa4kPKN4hN6fC2TkOTOKqiqp6d53N9X5Q== +sass@^1.26.8: + version "1.26.8" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.26.8.tgz#312652530721f9568d4c4000b0db07ec6eb23325" + integrity sha512-yvtzyrKLGiXQu7H12ekXqsfoGT/aTKeMDyVzCB675k1HYuaj0py63i8Uf4SI9CHXj6apDhpfwbUr3gGOjdpu2Q== dependencies: chokidar ">=2.0.0 <4.0.0" From 975c943432796fcfe9685328fdb42e2eb780dcf4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 08:32:02 +0900 Subject: [PATCH 10/19] Bump fast-glob from 3.2.2 to 3.2.4 (#14079) Bumps [fast-glob](https://github.com/mrmlnc/fast-glob) from 3.2.2 to 3.2.4. - [Release notes](https://github.com/mrmlnc/fast-glob/releases) - [Commits](https://github.com/mrmlnc/fast-glob/compare/3.2.2...3.2.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b37b9a902..7972a2a86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4667,9 +4667,9 @@ fast-deep-equal@^3.1.1: integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== fast-glob@^3.1.1, fast-glob@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" - integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== + version "3.2.4" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" + integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" From f111b71d1c302435d8bdc577784b4a12d8e305ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2020 08:32:23 +0900 Subject: [PATCH 11/19] Bump eslint-import-resolver-node from 0.3.3 to 0.3.4 (#14081) Bumps [eslint-import-resolver-node](https://github.com/benmosher/eslint-plugin-import) from 0.3.3 to 0.3.4. - [Release notes](https://github.com/benmosher/eslint-plugin-import/releases) - [Changelog](https://github.com/benmosher/eslint-plugin-import/blob/master/CHANGELOG.md) - [Commits](https://github.com/benmosher/eslint-plugin-import/compare/resolvers/node/v0.3.3...resolvers/node/v0.3.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7972a2a86..61ab69852 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4215,9 +4215,9 @@ escope@^3.6.0: estraverse "^4.1.1" eslint-import-resolver-node@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" - integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== dependencies: debug "^2.6.9" resolve "^1.13.1" From 75a2b8f8153ce3a6496fcaf6eedf9f2bb7c729e6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 21 Jun 2020 02:27:19 +0200 Subject: [PATCH 12/19] Change design of audio players in web UI (#14095) --- app/javascript/mastodon/components/status.js | 3 + .../mastodon/features/audio/index.js | 608 +++++++++++++++--- .../status/components/detailed_status.js | 1 + .../styles/mastodon/components.scss | 54 +- 4 files changed, 560 insertions(+), 106 deletions(-) diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index f99ccd39a..4ed8cbdd9 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -345,9 +345,12 @@ class Status extends ImmutablePureComponent { )} diff --git a/app/javascript/mastodon/features/audio/index.js b/app/javascript/mastodon/features/audio/index.js index baad1c0e5..5f5d85b95 100644 --- a/app/javascript/mastodon/features/audio/index.js +++ b/app/javascript/mastodon/features/audio/index.js @@ -1,11 +1,135 @@ import React from 'react'; import PropTypes from 'prop-types'; -import WaveSurfer from 'wavesurfer.js'; import { defineMessages, injectIntl } from 'react-intl'; import { formatTime } from 'mastodon/features/video'; import Icon from 'mastodon/components/icon'; import classNames from 'classnames'; import { throttle } from 'lodash'; +import { encode, decode } from 'blurhash'; +import { getPointerPosition } from 'mastodon/features/video'; + +const digitCharacters = [ + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + '#', + '$', + '%', + '*', + '+', + ',', + '-', + '.', + ':', + ';', + '=', + '?', + '@', + '[', + ']', + '^', + '_', + '{', + '|', + '}', + '~', +]; + +const decode83 = (str) => { + let value = 0; + let c, digit; + + for (let i = 0; i < str.length; i++) { + c = str[i]; + digit = digitCharacters.indexOf(c); + value = value * 83 + digit; + } + + return value; +}; + +const decodeRGB = int => ({ + r: Math.max(0, (int >> 16)), + g: Math.max(0, (int >> 8) & 255), + b: Math.max(0, (int & 255)), +}); + +const luma = ({ r, g, b }) => 0.2126 * r + 0.7152 * g + 0.0722 * b; + +const adjustColor = ({ r, g, b }, lumaThreshold = 100) => { + let delta; + + if (luma({ r, g, b }) >= lumaThreshold) { + delta = -80; + } else { + delta = 80; + } + + return { + r: r + delta, + g: g + delta, + b: b + delta, + }; +}; const messages = defineMessages({ play: { id: 'video.play', defaultMessage: 'Play' }, @@ -15,26 +139,36 @@ const messages = defineMessages({ download: { id: 'video.download', defaultMessage: 'Download file' }, }); +const TICK_SIZE = 10; +const PADDING = 180; + export default @injectIntl class Audio extends React.PureComponent { static propTypes = { src: PropTypes.string.isRequired, alt: PropTypes.string, + poster: PropTypes.string, duration: PropTypes.number, peaks: PropTypes.arrayOf(PropTypes.number), + width: PropTypes.number, height: PropTypes.number, preload: PropTypes.bool, editable: PropTypes.bool, intl: PropTypes.object.isRequired, + cacheWidth: PropTypes.func, }; state = { + width: this.props.width, currentTime: 0, + buffer: 0, duration: null, paused: true, muted: false, volume: 0.5, + dragging: false, + color: { r: 255, g: 255, b: 255 }, }; // Hard coded in components.scss @@ -48,99 +182,122 @@ class Audio extends React.PureComponent { return (offset > 110) ? 110 : offset; } + setPlayerRef = c => { + this.player = c; + + if (c) { + const width = c.offsetWidth; + const height = width / (16/9); + + if (this.props.cacheWidth) { + this.props.cacheWidth(width); + } + + this.setState({ width, height }); + } + } + + setSeekRef = c => { + this.seek = c; + } + setVolumeRef = c => { this.volume = c; } - setWaveformRef = c => { - this.waveform = c; + setAudioRef = c => { + this.audio = c; + + if (this.audio) { + this.setState({ volume: this.audio.volume, muted: this.audio.muted }); + } + } + + setBlurhashCanvasRef = c => { + this.blurhashCanvas = c; + } + + setCanvasRef = c => { + this.canvas = c; + + if (c) { + this.canvasContext = c.getContext('2d'); + } } componentDidMount () { - if (this.waveform) { - this._updateWaveform(); - } - window.addEventListener('scroll', this.handleScroll); + + const img = new Image(); + img.onload = () => this.handlePosterLoad(img); + img.src = this.props.poster; } - componentDidUpdate (prevProps) { - if (this.waveform && prevProps.src !== this.props.src) { - this._updateWaveform(); + componentDidUpdate (prevProps, prevState) { + if (prevProps.poster !== this.props.poster) { + const img = new Image(); + img.onload = () => this.handlePosterLoad(img); + img.src = this.props.poster; } + + if (prevState.blurhash !== this.state.blurhash) { + const context = this.blurhashCanvas.getContext('2d'); + const pixels = decode(this.state.blurhash, 32, 32); + const outputImageData = new ImageData(pixels, 32, 32); + + context.putImageData(outputImageData, 0, 0); + } + + this._clear(); + this._draw(); } componentWillUnmount () { window.removeEventListener('scroll', this.handleScroll); - - if (this.wavesurfer) { - this.wavesurfer.destroy(); - this.wavesurfer = null; - } - } - - _updateWaveform () { - const { src, height, duration, peaks, preload } = this.props; - - const progressColor = window.getComputedStyle(document.querySelector('.audio-player__progress-placeholder')).getPropertyValue('background-color'); - const waveColor = window.getComputedStyle(document.querySelector('.audio-player__wave-placeholder')).getPropertyValue('background-color'); - - if (this.wavesurfer) { - this.wavesurfer.destroy(); - this.loaded = false; - } - - const wavesurfer = WaveSurfer.create({ - container: this.waveform, - height, - barWidth: 3, - cursorWidth: 0, - progressColor, - waveColor, - backend: 'MediaElement', - interact: preload, - }); - - wavesurfer.setVolume(this.state.volume); - - if (preload) { - wavesurfer.load(src); - this.loaded = true; - } else { - wavesurfer.load(src, peaks, 'none', duration); - this.loaded = false; - } - - wavesurfer.on('ready', () => this.setState({ duration: Math.floor(wavesurfer.getDuration()) })); - wavesurfer.on('audioprocess', () => this.setState({ currentTime: Math.floor(wavesurfer.getCurrentTime()) })); - wavesurfer.on('pause', () => this.setState({ paused: true })); - wavesurfer.on('play', () => this.setState({ paused: false })); - wavesurfer.on('volume', volume => this.setState({ volume })); - wavesurfer.on('mute', muted => this.setState({ muted })); - - this.wavesurfer = wavesurfer; } togglePlay = () => { if (this.state.paused) { - if (!this.props.preload && !this.loaded) { - this.wavesurfer.createBackend(); - this.wavesurfer.createPeakCache(); - this.wavesurfer.load(this.props.src); - this.wavesurfer.toggleInteraction(); - this.wavesurfer.setVolume(this.state.volume); - this.loaded = true; - } - - this.setState({ paused: false }, () => this.wavesurfer.play()); + this.setState({ paused: false }, () => this.audio.play()); } else { - this.setState({ paused: true }, () => this.wavesurfer.pause()); + this.setState({ paused: true }, () => this.audio.pause()); + } + } + + handlePlay = () => { + this.setState({ paused: false }); + + if (this.canvas && !this.audioContext) { + this._initAudioContext(); + } + + if (this.audioContext && this.audioContext.state === 'suspended') { + this.audioContext.resume(); + } + + this._renderCanvas(); + } + + handlePause = () => { + this.setState({ paused: true }); + + if (this.audioContext) { + this.audioContext.suspend(); + } + } + + handleProgress = () => { + if (this.audio.buffered.length > 0) { + this.setState({ buffer: this.audio.buffered.end(0) / this.audio.duration * 100 }); } } toggleMute = () => { const muted = !this.state.muted; - this.setState({ muted }, () => this.wavesurfer.setMute(muted)); + + this.setState({ muted }, () => { + this.audio.muted = muted; + }); } handleVolumeMouseDown = e => { @@ -162,6 +319,48 @@ class Audio extends React.PureComponent { document.removeEventListener('touchend', this.handleVolumeMouseUp, true); } + handleMouseDown = e => { + document.addEventListener('mousemove', this.handleMouseMove, true); + document.addEventListener('mouseup', this.handleMouseUp, true); + document.addEventListener('touchmove', this.handleMouseMove, true); + document.addEventListener('touchend', this.handleMouseUp, true); + + this.setState({ dragging: true }); + this.audio.pause(); + this.handleMouseMove(e); + + e.preventDefault(); + e.stopPropagation(); + } + + handleMouseUp = () => { + document.removeEventListener('mousemove', this.handleMouseMove, true); + document.removeEventListener('mouseup', this.handleMouseUp, true); + document.removeEventListener('touchmove', this.handleMouseMove, true); + document.removeEventListener('touchend', this.handleMouseUp, true); + + this.setState({ dragging: false }); + this.audio.play(); + } + + handleMouseMove = throttle(e => { + const { x } = getPointerPosition(this.seek, e); + const currentTime = Math.floor(this.audio.duration * x); + + if (!isNaN(currentTime)) { + this.setState({ currentTime }, () => { + this.audio.currentTime = currentTime; + }); + } + }, 60); + + handleTimeUpdate = () => { + this.setState({ + currentTime: Math.floor(this.audio.currentTime), + duration: Math.floor(this.audio.duration), + }); + } + handleMouseVolSlide = throttle(e => { const rect = this.volume.getBoundingClientRect(); const x = (e.clientX - rect.left) / this.volWidth; // x position within the element. @@ -175,43 +374,280 @@ class Audio extends React.PureComponent { slideamt = 0; } - this.wavesurfer.setVolume(slideamt); + this.setState({ volume: slideamt }, () => { + this.audio.volume = slideamt; + }); } }, 60); handleScroll = throttle(() => { - if (!this.waveform || !this.wavesurfer) { + if (!this.canvas || !this.audio) { return; } - const { top, height } = this.waveform.getBoundingClientRect(); + const { top, height } = this.canvas.getBoundingClientRect(); const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0); if (!this.state.paused && !inView) { - this.setState({ paused: true }, () => this.wavesurfer.pause()); + this.setState({ paused: true }, () => this.audio.pause()); } - }, 150, { trailing: true }) + }, 150, { trailing: true }); + + _initAudioContext () { + const context = new AudioContext(); + const analyser = context.createAnalyser(); + const source = context.createMediaElementSource(this.audio); + + analyser.smoothingTimeConstant = 0.6; + analyser.fftSize = 2048; + + source.connect(analyser); + source.connect(context.destination); + + this.audioContext = context; + this.analyser = analyser; + } + + handlePosterLoad = image => { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + + canvas.width = image.width; + canvas.height = image.height; + + context.drawImage(image, 0, 0); + + const inputImageData = context.getImageData(0, 0, image.width, image.height); + const blurhash = encode(inputImageData.data, image.width, image.height, 4, 4); + const averageColor = decodeRGB(decode83(blurhash.slice(2, 6))); + + this.setState({ + blurhash, + color: adjustColor(averageColor), + darkText: luma(averageColor) >= 165, + }); + } + + _renderCanvas () { + requestAnimationFrame(() => { + this._clear(); + this._draw(); + + if (!this.state.paused) { + this._renderCanvas(); + } + }); + } + + _clear () { + this.canvasContext.clearRect(0, 0, this.state.width, this.state.height); + } + + _draw () { + this.canvasContext.save(); + + const ticks = this._getTicks(360 * this._getScaleCoefficient(), TICK_SIZE); + + ticks.forEach(tick => { + this._drawTick(tick.x1, tick.y1, tick.x2, tick.y2); + }); + + this.canvasContext.restore(); + } + + _getRadius () { + return parseInt(((this.state.height || this.props.height) - (PADDING * this._getScaleCoefficient()) * 2) / 2); + } + + _getScaleCoefficient () { + return (this.state.height || this.props.height) / 982; + } + + _getTicks (count, size, animationParams = [0, 90]) { + const radius = this._getRadius(); + const ticks = this._getTickPoints(count); + const lesser = 200; + const m = []; + const bufferLength = this.analyser ? this.analyser.frequencyBinCount : 0; + const frequencyData = new Uint8Array(bufferLength); + const allScales = []; + const scaleCoefficient = this._getScaleCoefficient(); + + if (this.analyser) { + this.analyser.getByteFrequencyData(frequencyData); + } + + ticks.forEach((tick, i) => { + const coef = 1 - i / (ticks.length * 2.5); + + let delta = ((frequencyData[i] || 0) - lesser * coef) * scaleCoefficient; + + if (delta < 0) { + delta = 0; + } + + let k; + + if (animationParams[0] <= tick.angle && tick.angle <= animationParams[1]) { + k = radius / (radius - this._getSize(tick.angle, animationParams[0], animationParams[1]) - delta); + } else { + k = radius / (radius - (size + delta)); + } + + const x1 = tick.x * (radius - size); + const y1 = tick.y * (radius - size); + const x2 = x1 * k; + const y2 = y1 * k; + + m.push({ x1, y1, x2, y2 }); + + if (i < 20) { + let scale = delta / (200 * scaleCoefficient); + scale = scale < 1 ? 1 : scale; + allScales.push(scale); + } + }); + + const scale = allScales.reduce((pv, cv) => pv + cv, 0) / allScales.length; + + return m.map(({ x1, y1, x2, y2 }) => ({ + x1: x1, + y1: y1, + x2: x2 * scale, + y2: y2 * scale, + })); + } + + _getSize (angle, l, r) { + const scaleCoefficient = this._getScaleCoefficient(); + const maxTickSize = TICK_SIZE * 9 * scaleCoefficient; + const m = (r - l) / 2; + const x = (angle - l); + + let h; + + if (x === m) { + return maxTickSize; + } + + const d = Math.abs(m - x); + const v = 40 * Math.sqrt(1 / d); + + if (v > maxTickSize) { + h = maxTickSize; + } else { + h = Math.max(TICK_SIZE, v); + } + + return h; + } + + _getTickPoints (count) { + const PI = 360; + const coords = []; + const step = PI / count; + + let rad; + + for(let deg = 0; deg < PI; deg += step) { + rad = deg * Math.PI / (PI / 2); + coords.push({ x: Math.cos(rad), y: -Math.sin(rad), angle: deg }); + } + + return coords; + } + + _drawTick (x1, y1, x2, y2) { + const radius = this._getRadius(); + const cx = parseInt(this.state.width / 2); + const cy = parseInt(radius + (PADDING * this._getScaleCoefficient())); + + const dx1 = parseInt(cx + x1); + const dy1 = parseInt(cy + y1); + const dx2 = parseInt(cx + x2); + const dy2 = parseInt(cy + y2); + + const gradient = this.canvasContext.createLinearGradient(dx1, dy1, dx2, dy2); + + const mainColor = `rgb(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b})`; + const lastColor = `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, 0)`; + + gradient.addColorStop(0, mainColor); + gradient.addColorStop(0.6, mainColor); + gradient.addColorStop(1, lastColor); + + this.canvasContext.beginPath(); + this.canvasContext.strokeStyle = gradient; + this.canvasContext.lineWidth = 2; + this.canvasContext.moveTo(dx1, dy1); + this.canvasContext.lineTo(dx2, dy2); + this.canvasContext.stroke(); + } + + _getColor () { + return `rgb(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b})`; + } render () { - const { height, intl, alt, editable } = this.props; - const { paused, muted, volume, currentTime } = this.state; + const { src, intl, alt, editable } = this.props; + const { paused, muted, volume, currentTime, duration, buffer, darkText, dragging } = this.state; const volumeWidth = muted ? 0 : volume * this.volWidth; const volumeHandleLoc = muted ? this.volHandleOffset(0) : this.volHandleOffset(volume); + const progress = (currentTime / duration) * 100; return ( -
-
-
+
+