Commit ceac7c85 authored by Sander Snel's avatar Sander Snel

Merge remote-tracking branch 'upstream/master'

parents ddb01fbc ddcf78d8
......@@ -3,7 +3,7 @@ version: 2
aliases:
- &defaults
docker:
- image: circleci/ruby:2.6-buster-node
- image: circleci/ruby:2.7-buster-node
environment: &ruby_environment
BUNDLE_APP_CONFIG: ./.bundle/
DB_HOST: localhost
......@@ -98,8 +98,15 @@ jobs:
<<: *defaults
<<: *install_steps
install-ruby2.7:
<<: *defaults
<<: *install_ruby_dependencies
install-ruby2.6:
<<: *defaults
docker:
- image: circleci/ruby:2.6-buster-node
environment: *ruby_environment
<<: *install_ruby_dependencies
install-ruby2.5:
......@@ -128,6 +135,36 @@ jobs:
- ./mastodon/public/assets
- ./mastodon/public/packs-test/
test-migrations:
<<: *defaults
docker:
- image: circleci/ruby:2.7-buster-node
environment: *ruby_environment
- image: circleci/postgres:10.6-alpine
environment:
POSTGRES_USER: root
- image: circleci/redis:5-alpine
steps:
- *attach_workspace
- *install_system_dependencies
- run:
name: Create database
command: ./bin/rails parallel:create
- run:
name: Run migrations
command: ./bin/rails parallel:migrate
test-ruby2.7:
<<: *defaults
docker:
- image: circleci/ruby:2.7-buster-node
environment: *ruby_environment
- image: circleci/postgres:10.6-alpine
environment:
POSTGRES_USER: root
- image: circleci/redis:5-alpine
<<: *test_steps
test-ruby2.6:
<<: *defaults
docker:
......@@ -184,20 +221,31 @@ workflows:
build-and-test:
jobs:
- install
- install-ruby2.7:
requires:
- install
- install-ruby2.6:
requires:
- install
- install-ruby2.7
- install-ruby2.5:
requires:
- install
- install-ruby2.6
- install-ruby2.7
- install-ruby2.4:
requires:
- install
- install-ruby2.6
- install-ruby2.7
- build:
requires:
- install-ruby2.6
- install-ruby2.7
- test-migrations:
requires:
- install-ruby2.7
- test-ruby2.7:
requires:
- install-ruby2.7
- build
- test-ruby2.6:
requires:
- install-ruby2.6
......@@ -215,4 +263,4 @@ workflows:
- install
- check-i18n:
requires:
- install-ruby2.6
- install-ruby2.7
# frozen_string_literal: true
source 'https://rubygems.org'
ruby '>= 2.4.0', '< 2.7.0'
ruby '>= 2.4.0', '< 3.0.0'
gem 'pkg-config', '~> 1.4'
......@@ -10,6 +10,9 @@ gem 'rails', '~> 5.2.4'
gem 'sprockets', '~> 3.7.2'
gem 'thor', '~> 0.20'
gem 'thwait', '~> 0.1.0'
gem 'e2mmap', '~> 0.1.0'
gem 'hamlit-rails', '~> 0.2'
gem 'pg', '~> 1.2'
gem 'makara', '~> 0.4'
......@@ -54,7 +57,7 @@ gem 'redis-namespace', '~> 1.7'
gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b'
gem 'html2text'
gem 'htmlentities', '~> 4.3'
gem 'http', '~> 3.3'
gem 'http', '~> 4.3'
gem 'http_accept_language', '~> 2.1'
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2', submodules: true
gem 'httplog', '~> 1.3'
......@@ -66,7 +69,6 @@ gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b
gem 'nokogiri', '~> 1.10'
gem 'nsa', '~> 0.2'
gem 'oj', '~> 3.10'
gem 'ostatus2', '~> 2.0'
gem 'ox', '~> 2.11'
gem 'parslet'
gem 'parallel', '~> 1.19'
......@@ -79,7 +81,7 @@ gem 'rails-i18n', '~> 5.1'
gem 'rails-settings-cached', '~> 0.6'
gem 'redis', '~> 4.1', require: ['redis', 'redis/connection/hiredis']
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 0.10'
gem 'rqrcode', '~> 1.1'
gem 'ruby-progressbar', '~> 1.10'
gem 'sanitize', '~> 5.1'
gem 'sidekiq', '~> 5.2'
......@@ -100,7 +102,7 @@ gem 'webpush'
gem 'json-ld', git: 'https://github.com/ruby-rdf/json-ld.git', ref: 'e742697a0906e74e8bb777ef98137bc3955d981d'
gem 'json-ld-preloaded', '~> 3.0'
gem 'rdf-normalize', '~> 0.3'
gem 'rdf-normalize', '~> 0.4'
gem 'redcarpet', '~> 3.4'
......
......@@ -128,7 +128,7 @@ GEM
rack (>= 0.9.0)
binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
blurhash (0.1.3)
blurhash (0.1.4)
ffi (~> 1.10.0)
bootsnap (1.4.5)
msgpack (~> 1.0)
......@@ -173,7 +173,7 @@ GEM
activesupport (>= 4.0)
elasticsearch (>= 2.0.0)
elasticsearch-dsl
chunky_png (1.3.10)
chunky_png (1.3.11)
cld3 (3.2.6)
ffi (>= 1.1.0, < 1.12.0)
climate_control (0.2.0)
......@@ -216,7 +216,7 @@ GEM
discard (1.1.0)
activerecord (>= 4.2, < 7)
docile (1.3.2)
domain_name (0.5.20180417)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.2.3)
railties (>= 5)
......@@ -224,6 +224,7 @@ GEM
dotenv-rails (2.7.5)
dotenv (= 2.7.5)
railties (>= 3.2, < 6.1)
e2mmap (0.1.0)
elasticsearch (7.3.0)
elasticsearch-api (= 7.3.0)
elasticsearch-transport (= 7.3.0)
......@@ -247,6 +248,9 @@ GEM
fast_blank (1.0.0)
fastimage (2.1.7)
ffi (1.10.0)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
fog-core (2.1.0)
builder
excon (~> 0.58)
......@@ -270,9 +274,9 @@ GEM
ffi (~> 1.0)
globalid (0.4.2)
activesupport (>= 4.2.0)
goldfinger (2.1.0)
goldfinger (2.1.1)
addressable (~> 2.5)
http (~> 3.0)
http (~> 4.0)
nokogiri (~> 1.8)
oj (~> 3.0)
hamlit (2.11.0)
......@@ -295,14 +299,16 @@ GEM
html2text (0.2.1)
nokogiri (~> 1.6)
htmlentities (4.3.4)
http (3.3.0)
http (4.3.0)
addressable (~> 2.3)
http-cookie (~> 1.0)
http-form_data (~> 2.0)
http_parser.rb (~> 0.6.0)
http-form_data (~> 2.2)
http-parser (~> 1.2.0)
http-cookie (1.0.3)
domain_name (~> 0.5)
http-form_data (2.1.1)
http-form_data (2.2.0)
http-parser (1.2.1)
ffi-compiler (>= 1.0, < 2.0)
http_accept_language (2.1.1)
httplog (1.3.3)
rack (>= 1.0)
......@@ -324,7 +330,7 @@ GEM
iso-639 (0.2.8)
jaro_winkler (1.5.4)
jmespath (1.4.0)
json (2.2.0)
json (2.3.0)
json-canonicalization (0.1.0)
json-ld-preloaded (3.0.6)
json-ld (~> 3.0)
......@@ -411,10 +417,6 @@ GEM
omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.7)
orm_adapter (0.5.0)
ostatus2 (2.0.3)
addressable (~> 2.5)
http (~> 3.0)
nokogiri (~> 1.8)
ox (2.11.0)
paperclip (6.0.0)
activemodel (>= 4.2.0)
......@@ -428,7 +430,7 @@ GEM
parallel (1.19.1)
parallel_tests (2.30.0)
parallel
parser (2.6.5.0)
parser (2.7.0.2)
ast (~> 2.4.0)
parslet (1.8.2)
pastel (0.7.3)
......@@ -460,7 +462,7 @@ GEM
pundit (2.1.0)
activesupport (>= 3.0.0)
raabro (1.1.6)
rack (2.0.8)
rack (2.1.1)
rack-attack (6.2.2)
rack (>= 1.0, < 3)
rack-cors (1.1.1)
......@@ -506,11 +508,11 @@ GEM
thor (>= 0.19.0, < 2.0)
rainbow (3.0.0)
rake (13.0.1)
rdf (3.0.13)
rdf (3.1.0)
hamster (~> 3.0)
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.3.3)
rdf (>= 2.2, < 4.0)
rdf-normalize (0.4.0)
rdf (~> 3.1)
redcarpet (3.4.0)
redis (4.1.3)
redis-actionpack (5.0.2)
......@@ -539,8 +541,10 @@ GEM
railties (>= 5.0)
rotp (2.1.2)
rpam2 (4.0.2)
rqrcode (0.10.1)
rqrcode (1.1.2)
chunky_png (~> 1.0)
rqrcode_core (~> 0.1)
rqrcode_core (0.1.1)
rspec-core (3.9.0)
rspec-support (~> 3.9.0)
rspec-expectations (3.9.0)
......@@ -568,7 +572,7 @@ GEM
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
rubocop-rails (2.4.0)
rubocop-rails (2.4.1)
rack (>= 1.1)
rubocop (>= 0.72.0)
ruby-progressbar (1.10.1)
......@@ -632,6 +636,7 @@ GEM
climate_control (>= 0.0.3, < 1.0)
thor (0.20.3)
thread_safe (0.3.6)
thwait (0.1.0)
tilt (2.0.10)
tty-color (0.5.0)
tty-command (0.9.0)
......@@ -712,6 +717,7 @@ DEPENDENCIES
discard (~> 1.1)
doorkeeper (~> 5.2)
dotenv-rails (~> 2.7)
e2mmap (~> 0.1.0)
fabrication (~> 2.21)
faker (~> 2.10)
fast_blank (~> 1.0)
......@@ -725,7 +731,7 @@ DEPENDENCIES
hiredis (~> 0.6)
html2text
htmlentities (~> 4.3)
http (~> 3.3)
http (~> 4.3)
http_accept_language (~> 2.1)
http_parser.rb (~> 0.6)!
httplog (~> 1.3)
......@@ -752,7 +758,6 @@ DEPENDENCIES
omniauth (~> 1.9)
omniauth-cas (~> 1.1)
omniauth-saml (~> 1.10)
ostatus2 (~> 2.0)
ox (~> 2.11)
paperclip (~> 6.0)
paperclip-av-transcoder (~> 0.6)
......@@ -775,12 +780,12 @@ DEPENDENCIES
rails-controller-testing (~> 1.0)
rails-i18n (~> 5.1)
rails-settings-cached (~> 0.6)
rdf-normalize (~> 0.3)
rdf-normalize (~> 0.4)
redcarpet (~> 3.4)
redis (~> 4.1)
redis-namespace (~> 1.7)
redis-rails (~> 5.0)
rqrcode (~> 0.10)
rqrcode (~> 1.1)
rspec-rails (~> 3.9)
rspec-sidekiq (~> 3.0)
rubocop (~> 0.78)
......@@ -801,6 +806,7 @@ DEPENDENCIES
streamio-ffmpeg (~> 3.0)
strong_migrations (~> 0.5)
thor (~> 0.20)
thwait (~> 0.1.0)
tty-command (~> 0.9)
tty-prompt (~> 0.20)
twitter-text (~> 1.14)
......
......@@ -3,7 +3,7 @@
module AccountsHelper
def display_name(account, **options)
if options[:custom_emojify]
Formatter.instance.format_display_name(account, options)
Formatter.instance.format_display_name(account, **options)
else
account.display_name.presence || account.username
end
......
......@@ -13,13 +13,13 @@ module RoutingHelper
end
def full_asset_url(source, **options)
source = ActionController::Base.helpers.asset_url(source, options) unless use_storage?
source = ActionController::Base.helpers.asset_url(source, **options) unless use_storage?
URI.join(root_url, source).to_s
end
def full_pack_url(source, **options)
full_asset_url(asset_pack_path(source, options))
full_asset_url(asset_pack_path(source, **options))
end
private
......
......@@ -186,17 +186,22 @@ $content-width: 840px;
padding-bottom: 40px;
border-bottom: 1px solid lighten($ui-base-color, 8%);
margin-bottom: 40px;
margin: -15px -15px 40px 0;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
& > * {
margin-top: 15px;
margin-right: 15px;
}
&-actions {
display: inline-flex;
& > * {
& > :not(:first-child) {
margin-left: 5px;
}
}
......@@ -894,3 +899,7 @@ a.name-tag,
color: $primary-text-color;
}
}
.center-text {
text-align: center;
}
......@@ -37,15 +37,14 @@ class Audio extends React.PureComponent {
volume: 0.5,
};
// hard coded in components.scss
// any way to get ::before values programatically?
volWidth = 50;
// Hard coded in components.scss
// Any way to get ::before values programatically?
volWidth = 50;
volOffset = 70;
volHandleOffset = v => {
const offset = v * this.volWidth + this.volOffset;
return (offset > 110) ? 110 : offset;
}
......@@ -61,6 +60,8 @@ class Audio extends React.PureComponent {
if (this.waveform) {
this._updateWaveform();
}
window.addEventListener('scroll', this.handleScroll);
}
componentDidUpdate (prevProps) {
......@@ -70,6 +71,8 @@ class Audio extends React.PureComponent {
}
componentWillUnmount () {
window.removeEventListener('scroll', this.handleScroll);
if (this.wavesurfer) {
this.wavesurfer.destroy();
this.wavesurfer = null;
......@@ -128,16 +131,15 @@ class Audio extends React.PureComponent {
this.loaded = true;
}
this.wavesurfer.play();
this.setState({ paused: false });
this.setState({ paused: false }, () => this.wavesurfer.play());
} else {
this.wavesurfer.pause();
this.setState({ paused: true });
this.setState({ paused: true }, () => this.wavesurfer.pause());
}
}
toggleMute = () => {
this.wavesurfer.setMute(!this.state.muted);
const muted = !this.state.muted;
this.setState({ muted }, () => this.wavesurfer.setMute(muted));
}
handleVolumeMouseDown = e => {
......@@ -176,6 +178,19 @@ class Audio extends React.PureComponent {
}
}, 60);
handleScroll = throttle(() => {
if (!this.waveform || !this.wavesurfer) {
return;
}
const { top, height } = this.waveform.getBoundingClientRect();
const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0);
if (!this.state.paused && !inView) {
this.setState({ paused: true }, () => this.wavesurfer.pause());
}
}, 150, { trailing: true })
render () {
const { height, intl, alt, editable } = this.props;
const { paused, muted, volume, currentTime } = this.state;
......
......@@ -124,12 +124,14 @@ class Video extends React.PureComponent {
revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
};
// hard coded in components.scss
// any way to get ::before values programatically?
volWidth = 50;
// Hard-coded in components.scss
// Any way to get ::before values programatically?
volWidth = 50;
volOffset = 70;
volHandleOffset = v => {
const offset = v * this.volWidth + this.volOffset;
return (offset > 110) ? 110 : offset;
}
......@@ -138,6 +140,7 @@ class Video extends React.PureComponent {
if (c) {
if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth);
this.setState({
containerWidth: c.offsetWidth,
});
......@@ -205,12 +208,14 @@ class Video extends React.PureComponent {
const x = (e.clientX - rect.left) / this.volWidth; //x position within the element.
if(!isNaN(x)) {
var slideamt = x;
let slideamt = x;
if(x > 1) {
slideamt = 1;
} else if(x < 0) {
slideamt = 0;
}
this.video.volume = slideamt;
this.setState({ volume: slideamt });
}
......@@ -252,9 +257,9 @@ class Video extends React.PureComponent {
togglePlay = () => {
if (this.state.paused) {
this.video.play();
this.setState({ paused: false }, () => this.video.play());
} else {
this.video.pause();
this.setState({ paused: true }, () => this.video.pause());
}
}
......@@ -272,12 +277,16 @@ class Video extends React.PureComponent {
document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
window.addEventListener('scroll', this.handleScroll);
if (this.props.blurhash) {
this._decode();
}
}
componentWillUnmount () {
window.removeEventListener('scroll', this.handleScroll);
document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true);
document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
......@@ -294,6 +303,7 @@ class Video extends React.PureComponent {
if (prevState.revealed && !this.state.revealed && this.video) {
this.video.pause();
}
if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
this._decode();
}
......@@ -313,6 +323,19 @@ class Video extends React.PureComponent {
}
}
handleScroll = throttle(() => {
if (!this.video) {
return;
}
const { top, height } = this.video.getBoundingClientRect();
const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0);
if (!this.state.paused && !inView) {
this.setState({ paused: true }, () => this.video.pause());
}
}, 150, { trailing: true })
handleFullscreenChange = () => {
this.setState({ fullscreen: isFullscreen() });
}
......@@ -326,8 +349,11 @@ class Video extends React.PureComponent {
}
toggleMute = () => {
this.video.muted = !this.video.muted;
this.setState({ muted: this.video.muted });
const muted = !this.video.muted;
this.setState({ muted }, () => {
this.video.muted = muted;
});
}
toggleReveal = () => {
......@@ -430,7 +456,6 @@ class Video extends React.PureComponent {
src={src}
poster={preview}
preload={preload}
loop
role='button'
tabIndex='0'
aria-label={alt}
......@@ -495,13 +520,8 @@ class Video extends React.PureComponent {
{(!onCloseVideo && !editable) && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
{(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>}
{onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>}
<button type='button' aria-label={intl.formatMessage(messages.download)}>
<a className='video-player__download__icon' href={this.props.src} download>
<Icon id={'download'} fixedWidth />
</a>
</button>