diff --git a/Gemfile b/Gemfile
index 1cf1ee379b..70295bc8d1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -113,7 +113,7 @@ group :production, :test do
end
group :test do
- gem 'capybara', '~> 3.27'
+ gem 'capybara', '~> 3.28'
gem 'climate_control', '~> 0.2'
gem 'faker', '~> 1.9'
gem 'microformats', '~> 4.1'
@@ -133,7 +133,7 @@ group :development do
gem 'letter_opener', '~> 1.7'
gem 'letter_opener_web', '~> 1.3'
gem 'memory_profiler'
- gem 'rubocop', '~> 0.73', require: false
+ gem 'rubocop', '~> 0.74', require: false
gem 'rubocop-rails', '~> 2.2', require: false
gem 'brakeman', '~> 4.6', require: false
gem 'bundler-audit', '~> 0.6', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 4faca35e97..5a096ba061 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -150,7 +150,7 @@ GEM
sshkit (~> 1.3)
capistrano-yarn (2.0.2)
capistrano (~> 3.0)
- capybara (3.27.0)
+ capybara (3.28.0)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
@@ -209,9 +209,9 @@ GEM
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.1.0)
railties (>= 5)
- dotenv (2.7.4)
- dotenv-rails (2.7.4)
- dotenv (= 2.7.4)
+ dotenv (2.7.5)
+ dotenv-rails (2.7.5)
+ dotenv (= 2.7.5)
railties (>= 3.2, < 6.1)
elasticsearch (6.0.2)
elasticsearch-api (= 6.0.2)
@@ -274,7 +274,7 @@ GEM
railties (>= 4.0.1)
hamster (3.0.0)
concurrent-ruby (~> 1.0)
- hashdiff (0.4.0)
+ hashdiff (1.0.0)
hashie (3.6.0)
heapy (0.1.4)
highline (2.0.1)
@@ -478,7 +478,7 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
- rails-html-sanitizer (1.0.4)
+ rails-html-sanitizer (1.1.0)
loofah (~> 2.2, >= 2.2.2)
rails-i18n (5.1.3)
i18n (>= 0.7, < 2)
@@ -492,7 +492,7 @@ GEM
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
rainbow (3.0.0)
- rake (12.3.2)
+ rake (12.3.3)
rdf (3.0.12)
hamster (~> 3.0)
link_header (~> 0.0, >= 0.0.8)
@@ -548,7 +548,7 @@ GEM
rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0)
rspec-support (3.8.0)
- rubocop (0.73.0)
+ rubocop (0.74.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.6)
@@ -645,7 +645,7 @@ GEM
uniform_notifier (1.12.1)
warden (1.2.8)
rack (>= 2.0.6)
- webmock (3.6.0)
+ webmock (3.6.2)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -684,7 +684,7 @@ DEPENDENCIES
capistrano-rails (~> 1.4)
capistrano-rbenv (~> 2.1)
capistrano-yarn (~> 2.0)
- capybara (~> 3.27)
+ capybara (~> 3.28)
charlock_holmes (~> 0.7.6)
chewy (~> 5.0)
cld3 (~> 3.2.4)
@@ -766,7 +766,7 @@ DEPENDENCIES
rqrcode (~> 0.10)
rspec-rails (~> 3.8)
rspec-sidekiq (~> 3.0)
- rubocop (~> 0.73)
+ rubocop (~> 0.74)
rubocop-rails (~> 2.2)
sanitize (~> 5.0)
sidekiq (~> 5.2)
diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb
index 53f7f1e278..50bace2178 100644
--- a/app/controllers/well_known/webfinger_controller.rb
+++ b/app/controllers/well_known/webfinger_controller.rb
@@ -9,17 +9,8 @@ module WellKnown
def show
@account = Account.find_local!(username_from_resource)
- respond_to do |format|
- format.any(:json, :html) do
- render json: @account, serializer: WebfingerSerializer, content_type: 'application/jrd+json'
- end
-
- format.xml do
- render content_type: 'application/xrd+xml'
- end
- end
-
expires_in 3.days, public: true
+ render json: @account, serializer: WebfingerSerializer, content_type: 'application/jrd+json'
rescue ActiveRecord::RecordNotFound
head 404
end
diff --git a/app/javascript/mastodon/components/column.js b/app/javascript/mastodon/components/column.js
index d453874636..55e3bfd5e0 100644
--- a/app/javascript/mastodon/components/column.js
+++ b/app/javascript/mastodon/components/column.js
@@ -8,10 +8,11 @@ export default class Column extends React.PureComponent {
static propTypes = {
children: PropTypes.node,
label: PropTypes.string,
+ bindToDocument: PropTypes.bool,
};
scrollTop () {
- const scrollable = this.node.querySelector('.scrollable');
+ const scrollable = this.props.bindToDocument ? document.scrollingElement : this.node.querySelector('.scrollable');
if (!scrollable) {
return;
@@ -33,11 +34,19 @@ export default class Column extends React.PureComponent {
}
componentDidMount () {
- this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false);
+ if (this.props.bindToDocument) {
+ document.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false);
+ } else {
+ this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false);
+ }
}
componentWillUnmount () {
- this.node.removeEventListener('wheel', this.handleWheel);
+ if (this.props.bindToDocument) {
+ document.removeEventListener('wheel', this.handleWheel);
+ } else {
+ this.node.removeEventListener('wheel', this.handleWheel);
+ }
}
render () {
diff --git a/app/javascript/mastodon/components/column_back_button.js b/app/javascript/mastodon/components/column_back_button.js
index f41045787e..cc0e5c07c3 100644
--- a/app/javascript/mastodon/components/column_back_button.js
+++ b/app/javascript/mastodon/components/column_back_button.js
@@ -2,6 +2,7 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import Icon from 'mastodon/components/icon';
+import { createPortal } from 'react-dom';
export default class ColumnBackButton extends React.PureComponent {
@@ -9,6 +10,10 @@ export default class ColumnBackButton extends React.PureComponent {
router: PropTypes.object,
};
+ static propTypes = {
+ multiColumn: PropTypes.bool,
+ };
+
handleClick = () => {
if (window.history && window.history.length === 1) {
this.context.router.history.push('/');
@@ -18,12 +23,20 @@ export default class ColumnBackButton extends React.PureComponent {
}
render () {
- return (
+ const { multiColumn } = this.props;
+
+ const component = (
);
+
+ if (multiColumn) {
+ return component;
+ } else {
+ return createPortal(component, document.getElementById('tabs-bar__portal'));
+ }
}
}
diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js
index f33c689e7c..89c5fe723f 100644
--- a/app/javascript/mastodon/components/column_header.js
+++ b/app/javascript/mastodon/components/column_header.js
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { createPortal } from 'react-dom';
import classNames from 'classnames';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
import Icon from 'mastodon/components/icon';
@@ -28,6 +29,7 @@ class ColumnHeader extends React.PureComponent {
showBackButton: PropTypes.bool,
children: PropTypes.node,
pinned: PropTypes.bool,
+ placeholder: PropTypes.bool,
onPin: PropTypes.func,
onMove: PropTypes.func,
onClick: PropTypes.func,
@@ -79,7 +81,7 @@ class ColumnHeader extends React.PureComponent {
}
render () {
- const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage } } = this.props;
+ const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder } = this.props;
const { collapsed, animating } = this.state;
const wrapperClassName = classNames('column-header__wrapper', {
@@ -146,7 +148,7 @@ class ColumnHeader extends React.PureComponent {
const hasTitle = icon && title;
- return (
+ const component = (
{hasTitle && (
@@ -172,6 +174,12 @@ class ColumnHeader extends React.PureComponent {
);
+
+ if (multiColumn || placeholder) {
+ return component;
+ } else {
+ return createPortal(component, document.getElementById('tabs-bar__portal'));
+ }
}
}
diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js
index 5d6a53e18e..f1a665d8ff 100644
--- a/app/javascript/mastodon/features/account_gallery/index.js
+++ b/app/javascript/mastodon/features/account_gallery/index.js
@@ -56,6 +56,7 @@ class AccountGallery extends ImmutablePureComponent {
isLoading: PropTypes.bool,
hasMore: PropTypes.bool,
isAccount: PropTypes.bool,
+ multiColumn: PropTypes.bool,
};
state = {
@@ -116,7 +117,7 @@ class AccountGallery extends ImmutablePureComponent {
}
render () {
- const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount } = this.props;
+ const { attachments, shouldUpdateScroll, isLoading, hasMore, isAccount, multiColumn } = this.props;
const { width } = this.state;
if (!isAccount) {
@@ -143,7 +144,7 @@ class AccountGallery extends ImmutablePureComponent {
return (
-
+
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index 9914b7e654..69bab1e86e 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -100,7 +100,7 @@ class AccountTimeline extends ImmutablePureComponent {
return (
-
+
}
diff --git a/app/javascript/mastodon/features/blocks/index.js b/app/javascript/mastodon/features/blocks/index.js
index 8fb0f051b3..051431ed29 100644
--- a/app/javascript/mastodon/features/blocks/index.js
+++ b/app/javascript/mastodon/features/blocks/index.js
@@ -57,7 +57,7 @@ class Blocks extends ImmutablePureComponent {
const emptyMessage = ;
return (
-
+
+
+
);
diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js
index d202f3bfd9..5ce7957600 100644
--- a/app/javascript/mastodon/features/direct_timeline/index.js
+++ b/app/javascript/mastodon/features/direct_timeline/index.js
@@ -75,7 +75,7 @@ class DirectTimeline extends React.PureComponent {
const pinned = !!columnId;
return (
-
+
;
return (
-
+
;
return (
-
+
-
+
;
return (
-
+
-
+
-
+
+
{multiColumn &&