Merge branch 'develop' into 'captcha_reload_on_click'

# Conflicts:
#   app/soapbox/locales/defaultMessages.json
merge-requests/97/merge
Curtis 4 years ago
commit c28cc228a2

@ -1,4 +1,3 @@
NODE_ENV=development NODE_ENV=development
# BACKEND_URL="https://example.com" # BACKEND_URL="https://example.com"
# PATRON_URL="https://patron.example.com"
# PROXY_HTTPS_INSECURE=false # PROXY_HTTPS_INSECURE=false

@ -124,7 +124,7 @@ For https, be sure to also set `PROXY_HTTPS_INSECURE=true`.
Allows using an HTTPS backend if set to `true`. Allows using an HTTPS backend if set to `true`.
This is needed if `BACKEND_URL` or `PATRON_URL` are set to an `https://` value. This is needed if `BACKEND_URL` is set to an `https://` value.
[More info](https://stackoverflow.com/a/48624590/8811886). [More info](https://stackoverflow.com/a/48624590/8811886).
**Default:** `false` **Default:** `false`

@ -5,7 +5,7 @@ export const PATRON_FUNDING_FETCH_FAIL = 'PATRON_FUNDING_FETCH_FAIL';
export function fetchFunding() { export function fetchFunding() {
return (dispatch, getState) => { return (dispatch, getState) => {
api(getState).get('/patron/v1/funding').then(response => { api(getState).get('/api/patron/v1/instance').then(response => {
dispatch(importFetchedFunding(response.data)); dispatch(importFetchedFunding(response.data));
}).catch(error => { }).catch(error => {
dispatch(fetchFundingFail(error)); dispatch(fetchFundingFail(error));

@ -11,6 +11,7 @@ import { decode } from 'blurhash';
import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio'; import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio';
import { Map as ImmutableMap } from 'immutable'; import { Map as ImmutableMap } from 'immutable';
import { getSettings } from 'soapbox/actions/settings'; import { getSettings } from 'soapbox/actions/settings';
import Icon from 'soapbox/components/icon';
import StillImage from 'soapbox/components/still_image'; import StillImage from 'soapbox/components/still_image';
const messages = defineMessages({ const messages = defineMessages({
@ -192,6 +193,24 @@ class Item extends React.PureComponent {
<span className='media-gallery__gifv__label'>GIF</span> <span className='media-gallery__gifv__label'>GIF</span>
</div> </div>
); );
} else if (attachment.get('type') === 'audio') {
const remoteURL = attachment.get('remote_url');
const originalUrl = attachment.get('url');
const fileExtensionLastIndex = remoteURL.lastIndexOf('.');
const fileExtension = remoteURL.substr(fileExtensionLastIndex + 1).toUpperCase();
thumbnail = (
<a
className={classNames('media-gallery__item-thumbnail')}
href={attachment.get('remote_url') || originalUrl}
onClick={this.handleClick}
target='_blank'
alt={attachment.get('description')}
title={attachment.get('description')}
>
<span className='media-gallery__item__icons'><Icon id='volume-up' /></span>
<span className='media-gallery__file-extension__label'>{fileExtension}</span>
</a>
);
} }
return ( return (

@ -39,11 +39,13 @@ const messages = defineMessages({
const mapStateToProps = state => { const mapStateToProps = state => {
const me = state.get('me'); const me = state.get('me');
const getAccount = makeGetAccount(); const getAccount = makeGetAccount();
const patronEnabled = state.getIn(['soapbox', 'extensions', 'patron', 'enabled']);
const patronUrl = state.getIn(['soapbox', 'extensions', 'patron', 'baseUrl']);
return { return {
account: getAccount(state, me), account: getAccount(state, me),
sidebarOpen: state.get('sidebar').sidebarOpen, sidebarOpen: state.get('sidebar').sidebarOpen,
hasPatron: state.getIn(['soapbox', 'extensions', 'patron']), patronUrl: patronEnabled && patronUrl,
isStaff: isStaff(state.getIn(['accounts', me])), isStaff: isStaff(state.getIn(['accounts', me])),
}; };
}; };
@ -75,7 +77,7 @@ class SidebarMenu extends ImmutablePureComponent {
} }
render() { render() {
const { sidebarOpen, onClose, intl, account, onClickLogOut, hasPatron, isStaff } = this.props; const { sidebarOpen, onClose, intl, account, onClickLogOut, patronUrl, isStaff } = this.props;
if (!account) return null; if (!account) return null;
const acct = account.get('acct'); const acct = account.get('acct');
@ -127,11 +129,11 @@ class SidebarMenu extends ImmutablePureComponent {
<Icon id='envelope' /> <Icon id='envelope' />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.messages)}</span> <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.messages)}</span>
</NavLink> </NavLink>
{hasPatron ? {patronUrl ?
<NavLink className='sidebar-menu-item' to='/donate' onClick={onClose}> <a className='sidebar-menu-item' href={patronUrl} onClick={onClose}>
<Icon id='dollar' /> <Icon id='dollar' />
<span className='sidebar-menu-item__title'>{intl.formatMessage(messages.donate)}</span> <span className='sidebar-menu-item__title'>{intl.formatMessage(messages.donate)}</span>
</NavLink> </a>
: ''} : ''}
<NavLink className='sidebar-menu-item' to='/lists' onClick={onClose}> <NavLink className='sidebar-menu-item' to='/lists' onClick={onClose}>
<Icon id='list' /> <Icon id='list' />

@ -12,7 +12,7 @@ import AttachmentList from './attachment_list';
import Card from '../features/status/components/card'; import Card from '../features/status/components/card';
import { injectIntl, FormattedMessage } from 'react-intl'; import { injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { MediaGallery, Video } from '../features/ui/util/async-components'; import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
@ -73,6 +73,7 @@ class Status extends ImmutablePureComponent {
onPin: PropTypes.func, onPin: PropTypes.func,
onOpenMedia: PropTypes.func, onOpenMedia: PropTypes.func,
onOpenVideo: PropTypes.func, onOpenVideo: PropTypes.func,
onOpenAudio: PropTypes.func,
onBlock: PropTypes.func, onBlock: PropTypes.func,
onEmbed: PropTypes.func, onEmbed: PropTypes.func,
onHeightChange: PropTypes.func, onHeightChange: PropTypes.func,
@ -194,10 +195,18 @@ class Status extends ImmutablePureComponent {
return <div className='media-spoiler-video' style={{ height: '110px' }} />; return <div className='media-spoiler-video' style={{ height: '110px' }} />;
} }
renderLoadingAudioPlayer() {
return <div className='media-spoiler-audio' style={{ height: '110px' }} />;
}
handleOpenVideo = (media, startTime) => { handleOpenVideo = (media, startTime) => {
this.props.onOpenVideo(media, startTime); this.props.onOpenVideo(media, startTime);
} }
handleOpenAudio = (media, startTime) => {
this.props.OnOpenAudio(media, startTime);
}
handleHotkeyReply = e => { handleHotkeyReply = e => {
e.preventDefault(); e.preventDefault();
this.props.onReply(this._properStatus(), this.context.router.history); this.props.onReply(this._properStatus(), this.context.router.history);
@ -356,6 +365,24 @@ class Status extends ImmutablePureComponent {
)} )}
</Bundle> </Bundle>
); );
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio' && status.get('media_attachments').size === 1) {
const audio = status.getIn(['media_attachments', 0]);
media = (
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
{Component => (
<Component
src={audio.get('url')}
alt={audio.get('description')}
inline
sensitive={status.get('sensitive')}
cacheWidth={this.props.cacheMediaWidth}
visible={this.state.showMedia}
onOpenAudio={this.handleOpenAudio}
/>
)}
</Bundle>
);
} else { } else {
media = ( media = (
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}> <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>

@ -146,6 +146,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(openModal('VIDEO', { media, time })); dispatch(openModal('VIDEO', { media, time }));
}, },
onOpenAudio(media, time) {
dispatch(openModal('AUDIO', { media, time }));
},
onBlock(status) { onBlock(status) {
const account = status.get('account'); const account = status.get('account');
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {

@ -146,6 +146,16 @@ class MediaItem extends ImmutablePureComponent {
<span className='media-gallery__gifv__label'>GIF</span> <span className='media-gallery__gifv__label'>GIF</span>
</div> </div>
); );
} else if (attachment.get('type') === 'audio') {
const remoteURL = attachment.get('remote_url');
const fileExtensionLastIndex = remoteURL.lastIndexOf('.');
const fileExtension = remoteURL.substr(fileExtensionLastIndex + 1).toUpperCase();
thumbnail = (
<div className='media-gallery__item-thumbnail'>
<span className='media-gallery__item__icons'><Icon id='volume-up' /></span>
<span className='media-gallery__file-extension__label'>{fileExtension}</span>
</div>
);
} }
if (!visible) { if (!visible) {

@ -0,0 +1,380 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { throttle } from 'lodash';
import classNames from 'classnames';
import Icon from 'soapbox/components/icon';
import { getSettings } from 'soapbox/actions/settings';
const messages = defineMessages({
play: { id: 'audio.play', defaultMessage: 'Play' },
pause: { id: 'audio.pause', defaultMessage: 'Pause' },
mute: { id: 'audio.mute', defaultMessage: 'Mute' },
unmute: { id: 'audio.unmute', defaultMessage: 'Unmute' },
hide: { id: 'audio.hide', defaultMessage: 'Hide audio' },
expand: { id: 'audio.expand', defaultMessage: 'Expand audio' },
close: { id: 'audio.close', defaultMessage: 'Close audio' },
});
const formatTime = secondsNum => {
let hours = Math.floor(secondsNum / 3600);
let minutes = Math.floor((secondsNum - (hours * 3600)) / 60);
let seconds = secondsNum - (hours * 3600) - (minutes * 60);
if (hours < 10) hours = '0' + hours;
if (minutes < 10 && hours >= 1) minutes = '0' + minutes;
if (seconds < 10) seconds = '0' + seconds;
return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
};
export const findElementPosition = el => {
let box;
if (el.getBoundingClientRect && el.parentNode) {
box = el.getBoundingClientRect();
}
if (!box) {
return {
left: 0,
top: 0,
};
}
const docEl = document.documentElement;
const body = document.body;
const clientLeft = docEl.clientLeft || body.clientLeft || 0;
const scrollLeft = window.pageXOffset || body.scrollLeft;
const left = (box.left + scrollLeft) - clientLeft;
const clientTop = docEl.clientTop || body.clientTop || 0;
const scrollTop = window.pageYOffset || body.scrollTop;
const top = (box.top + scrollTop) - clientTop;
return {
left: Math.round(left),
top: Math.round(top),
};
};
export const getPointerPosition = (el, event) => {
const position = {};
const box = findElementPosition(el);
const boxW = el.offsetWidth;
const boxH = el.offsetHeight;
const boxY = box.top;
const boxX = box.left;
let pageY = event.pageY;
let pageX = event.pageX;
if (event.changedTouches) {
pageX = event.changedTouches[0].pageX;
pageY = event.changedTouches[0].pageY;
}
position.y = Math.max(0, Math.min(1, (pageY - boxY) / boxH));
position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
return position;
};
const mapStateToProps = state => ({
displayMedia: getSettings(state).get('displayMedia'),
});
export default @connect(mapStateToProps)
@injectIntl
class Audio extends React.PureComponent {
static propTypes = {
src: PropTypes.string.isRequired,
alt: PropTypes.string,
sensitive: PropTypes.bool,
startTime: PropTypes.number,
detailed: PropTypes.bool,
inline: PropTypes.bool,
cacheWidth: PropTypes.func,
visible: PropTypes.bool,
onToggleVisibility: PropTypes.func,
intl: PropTypes.object.isRequired,
link: PropTypes.node,
displayMedia: PropTypes.string,
expandSpoilers: PropTypes.bool,
};
state = {
currentTime: 0,
duration: 0,
volume: 0.5,
paused: true,
dragging: false,
muted: false,
revealed: this.props.visible !== undefined ? this.props.visible : (this.props.displayMedia !== 'hide_all' && !this.props.sensitive || this.props.displayMedia === 'show_all'),
};
// hard coded in components.scss
// any way to get ::before values programatically?
volWidth = 50;
volOffset = 85;
volHandleOffset = v => {
const offset = v * this.volWidth + this.volOffset;
return (offset > 125) ? 125 : offset;
}
setPlayerRef = c => {
this.player = c;
if (c) {
if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth);
this.setState({
containerWidth: c.offsetWidth,
});
}
}
setAudioRef = c => {
this.audio = c;
if (this.audio) {
this.setState({ volume: this.audio.volume, muted: this.audio.muted });
}
}
setSeekRef = c => {
this.seek = c;
}
setVolumeRef = c => {
this.volume = c;
}
handleClickRoot = e => e.stopPropagation();
handlePlay = () => {
this.setState({ paused: false });
}
handlePause = () => {
this.setState({ paused: true });
}
handleTimeUpdate = () => {
this.setState({
currentTime: Math.floor(this.audio.currentTime),
duration: Math.floor(this.audio.duration),
});
}
handleVolumeMouseDown = e => {
document.addEventListener('mousemove', this.handleMouseVolSlide, true);
document.addEventListener('mouseup', this.handleVolumeMouseUp, true);
document.addEventListener('touchmove', this.handleMouseVolSlide, true);
document.addEventListener('touchend', this.handleVolumeMouseUp, true);
this.handleMouseVolSlide(e);
e.preventDefault();
e.stopPropagation();
}
handleVolumeMouseUp = () => {
document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
}
handleMouseVolSlide = throttle(e => {
const rect = this.volume.getBoundingClientRect();
const x = (e.clientX - rect.left) / this.volWidth; //x position within the element.
if(!isNaN(x)) {
var slideamt = x;
if(x > 1) {
slideamt = 1;
} else if(x < 0) {
slideamt = 0;
}
this.audio.volume = slideamt;
this.setState({ volume: slideamt });
}
}, 60);
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.audio.currentTime = currentTime;
this.setState({ currentTime });
}
}, 60);
togglePlay = () => {
if (this.state.paused) {
this.audio.play();
} else {
this.audio.pause();
}
}
toggleMute = () => {
this.audio.muted = !this.audio.muted;
this.setState({ muted: this.audio.muted });
}
toggleWarning = () => {
this.setState({ revealed: !this.state.revealed });
}
handleLoadedData = () => {
if (this.props.startTime) {
this.audio.currentTime = this.props.startTime;
this.audio.play();
}
}
handleProgress = () => {
if (this.audio.buffered.length > 0) {
this.setState({ buffer: this.audio.buffered.end(0) / this.audio.duration * 100 });
}
}
handleVolumeChange = () => {
this.setState({ volume: this.audio.volume, muted: this.audio.muted });
}
getPreload = () => {
const { startTime, detailed } = this.props;
const { dragging } = this.state;
if (startTime || dragging) {
return 'auto';
} else if (detailed) {
return 'metadata';
} else {
return 'none';
}
}
render() {
const { src, inline, intl, alt, detailed, sensitive, link } = this.props;
const { currentTime, duration, volume, buffer, dragging, paused, muted, revealed } = this.state;
const progress = (currentTime / duration) * 100;
const volumeWidth = (muted) ? 0 : volume * this.volWidth;
const volumeHandleLoc = (muted) ? this.volHandleOffset(0) : this.volHandleOffset(volume);
const playerStyle = {};
let warning;
if (sensitive) {
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
} else {
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
}
return (
<div
role='menuitem'
className={classNames('audio-player', { detailed: detailed, inline: inline, warning_visible: !revealed })}
style={playerStyle}
ref={this.setPlayerRef}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onClick={this.handleClickRoot}
tabIndex={0}
>
<audio
ref={this.setAudioRef}
src={src}
// preload={this.getPreload()}
role='button'
tabIndex='0'
aria-label={alt}
title={alt}
volume={volume}
onClick={this.togglePlay}
onPlay={this.handlePlay}
onPause={this.handlePause}
onTimeUpdate={this.handleTimeUpdate}
onLoadedData={this.handleLoadedData}
onProgress={this.handleProgress}
onVolumeChange={this.handleVolumeChange}
/>
<div className={classNames('audio-player__spoiler-warning', { 'spoiler-button--hidden': revealed })}>
<span className='audio-player__spoiler-warning__label'><Icon id='warning' fixedWidth /> {warning}</span>
<button aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleWarning}><Icon id='times' fixedWidth /></button>
</div>
<div className={classNames('audio-player__controls')}>
<div className='audio-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
<div className='audio-player__seek__buffer' style={{ width: `${buffer}%` }} />
<div className='audio-player__seek__progress' style={{ width: `${progress}%` }} />
<span
className={classNames('audio-player__seek__handle', { active: dragging })}
tabIndex='0'
style={{ left: `${progress}%` }}
/>
</div>
<div className='audio-player__buttons-bar'>
<div className='audio-player__buttons left'>
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
<div className='audio-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
<div className='audio-player__volume__current' style={{ width: `${volumeWidth}px` }} />
<span
className={classNames('audio-player__volume__handle')}
tabIndex='0'
style={{ left: `${volumeHandleLoc}px` }}
/>
</div>
<span>
<span className='audio-player__time-current'>{formatTime(currentTime)}</span>
<span className='audio-player__time-sep'>/</span>
<span className='audio-player__time-total'>{formatTime(duration)}</span>
</span>
{link && <span className='audio-player__link'>{link}</span>}
</div>
</div>
</div>
</div>
);
}
}

@ -7,7 +7,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
const messages = defineMessages({ const messages = defineMessages({
upload: { id: 'upload_button.label', defaultMessage: 'Add media (JPEG, PNG, GIF, WebM, MP4, MOV)' }, upload: { id: 'upload_button.label', defaultMessage: 'Add media attachment' },
}); });
const makeMapStateToProps = () => { const makeMapStateToProps = () => {

@ -4,7 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
import noop from 'lodash/noop'; import noop from 'lodash/noop';
import StatusContent from '../../../components/status_content'; import StatusContent from '../../../components/status_content';
import { MediaGallery, Video } from '../../ui/util/async-components'; import { MediaGallery, Video, Audio } from '../../ui/util/async-components';
import Bundle from '../../ui/components/bundle'; import Bundle from '../../ui/components/bundle';
export default class StatusCheckBox extends React.PureComponent { export default class StatusCheckBox extends React.PureComponent {
@ -48,6 +48,22 @@ export default class StatusCheckBox extends React.PureComponent {
)} )}
</Bundle> </Bundle>
); );
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
const audio = status.getIn(['media_attachments', 0]);
media = (
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
{Component => (
<Component
src={audio.get('url')}
alt={audio.get('description')}
inline
sensitive={status.get('sensitive')}
onOpenAudio={noop}
/>
)}
</Bundle>
);
} else { } else {
media = ( media = (
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} > <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >

@ -10,6 +10,7 @@ import { FormattedDate, FormattedNumber } from 'react-intl';
import Card from './card'; import Card from './card';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import Video from '../../video'; import Video from '../../video';
import Audio from '../../audio';
import scheduleIdleTask from '../../ui/util/schedule_idle_task'; import scheduleIdleTask from '../../ui/util/schedule_idle_task';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'soapbox/components/icon'; import Icon from 'soapbox/components/icon';
@ -120,6 +121,19 @@ export default class DetailedStatus extends ImmutablePureComponent {
onToggleVisibility={this.props.onToggleMediaVisibility} onToggleVisibility={this.props.onToggleMediaVisibility}
/> />
); );
} else if (status.getIn(['media_attachments', 0, 'type']) === 'audio' && status.get('media_attachments').size === 1) {
const audio = status.getIn(['media_attachments', 0]);
media = (
<Audio
src={audio.get('url')}
alt={audio.get('description')}
inline
sensitive={status.get('sensitive')}
visible={this.props.showMedia}
onToggleVisibility={this.props.onToggleMediaVisibility}
/>
);
} else { } else {
media = ( media = (
<MediaGallery <MediaGallery

@ -5,6 +5,16 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import ProgressBar from '../../../components/progress_bar'; import ProgressBar from '../../../components/progress_bar';
import { fetchFunding } from 'soapbox/actions/patron'; import { fetchFunding } from 'soapbox/actions/patron';
const moneyFormat = amount => (
new Intl
.NumberFormat('en-US', {
style: 'currency',
currency: 'usd',
notation: 'compact',
})
.format(amount/100)
);
class FundingPanel extends ImmutablePureComponent { class FundingPanel extends ImmutablePureComponent {
componentDidMount() { componentDidMount() {
@ -12,21 +22,22 @@ class FundingPanel extends ImmutablePureComponent {
} }
render() { render() {
const { funding } = this.props; const { funding, patronUrl } = this.props;
if (!funding) { if (!funding) {
return null; return null;
} }
const amount = funding.getIn(['funding', 'amount']);
const goal = funding.getIn(['goals', '0', 'amount']); const goal = funding.getIn(['goals', '0', 'amount']);
const goal_text = funding.getIn(['goals', '0', 'text']); const goal_text = funding.getIn(['goals', '0', 'text']);
const goal_reached = funding.get('amount') >= goal; const goal_reached = amount >= goal;
let ratio_text; let ratio_text;
if (goal_reached) { if (goal_reached) {
ratio_text = <><strong>${Math.floor(goal/100)}</strong> per month<span className='funding-panel__reached'>&mdash; reached!</span></>; ratio_text = <><strong>{moneyFormat(goal)}</strong> per month<span className='funding-panel__reached'>&mdash; reached!</span></>;
} else { } else {
ratio_text = <><strong>${Math.floor(funding.get('amount')/100)} out of ${Math.floor(goal/100)}</strong> per month</>; ratio_text = <><strong>{moneyFormat(amount)} out of {moneyFormat(goal)}</strong> per month</>;
} }
return ( return (
@ -41,11 +52,11 @@ class FundingPanel extends ImmutablePureComponent {
<div className='funding-panel__ratio'> <div className='funding-panel__ratio'>
{ratio_text} {ratio_text}
</div> </div>
<ProgressBar progress={funding.get('amount')/goal} /> <ProgressBar progress={amount/goal} />
<div className='funding-panel__description'> <div className='funding-panel__description'>
{goal_text} {goal_text}
</div> </div>
<a className='button' href='/donate'>Donate</a> {patronUrl && <a className='button' href={patronUrl}>Donate</a>}
</div> </div>
</div> </div>
); );
@ -56,6 +67,7 @@ class FundingPanel extends ImmutablePureComponent {
const mapStateToProps = state => { const mapStateToProps = state => {
return { return {
funding: state.getIn(['patron', 'funding']), funding: state.getIn(['patron', 'funding']),
patronUrl: state.getIn(['soapbox', 'extensions', 'patron', 'baseUrl']),
}; };
}; };

@ -3,6 +3,7 @@ import ReactSwipeableViews from 'react-swipeable-views';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Video from 'soapbox/features/video'; import Video from 'soapbox/features/video';
import Audio from 'soapbox/features/audio';
import ExtendedVideoPlayer from 'soapbox/components/extended_video_player'; import ExtendedVideoPlayer from 'soapbox/components/extended_video_player';
import classNames from 'classnames'; import classNames from 'classnames';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@ -138,9 +139,18 @@ class MediaModal extends ImmutablePureComponent {
}); });
} }
const isMultiMedia = media.map((image) => {
if (image.get('type') !== 'image') {
return true;
}
return false;
}).toArray();
const content = media.map((image) => { const content = media.map((image) => {
const width = image.getIn(['meta', 'original', 'width']) || null; const width = image.getIn(['meta', 'original', 'width']) || null;
const height = image.getIn(['meta', 'original', 'height']) || null; const height = image.getIn(['meta', 'original', 'height']) || null;
const link = (status && <a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a>);
if (image.get('type') === 'image') { if (image.get('type') === 'image') {
return ( return (
@ -167,6 +177,20 @@ class MediaModal extends ImmutablePureComponent {
startTime={time || 0} startTime={time || 0}
onCloseVideo={onClose} onCloseVideo={onClose}
detailed detailed
link={link}
alt={image.get('description')}
key={image.get('url')}
/>
);
} else if (image.get('type') === 'audio') {
const { time } = this.props;
return (
<Audio
src={image.get('url')}
startTime={time || 0}
detailed
link={link}
alt={image.get('description')} alt={image.get('description')}
key={image.get('url')} key={image.get('url')}
/> />
@ -178,6 +202,7 @@ class MediaModal extends ImmutablePureComponent {
muted muted
controls={false} controls={false}
width={width} width={width}
link={link}
height={height} height={height}
key={image.get('preview_url')} key={image.get('preview_url')}
alt={image.get('description')} alt={image.get('description')}
@ -230,7 +255,7 @@ class MediaModal extends ImmutablePureComponent {
{leftNav} {leftNav}
{rightNav} {rightNav}
{status && ( {(status && !isMultiMedia[index]) && (
<div className={classNames('media-modal__meta', { 'media-modal__meta--shifted': media.size > 1 })}> <div className={classNames('media-modal__meta', { 'media-modal__meta--shifted': media.size > 1 })}>
<a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> <a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a>
</div> </div>

@ -142,6 +142,10 @@ export function Video() {
return import(/* webpackChunkName: "features/video" */'../../video'); return import(/* webpackChunkName: "features/video" */'../../video');
} }
export function Audio() {
return import(/* webpackChunkName: "features/audio" */'../../audio');
}
export function EmbedModal() { export function EmbedModal() {
return import(/* webpackChunkName: "modals/embed_modal" */'../components/embed_modal'); return import(/* webpackChunkName: "modals/embed_modal" */'../components/embed_modal');
} }

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "لقد طرأ هناك خطأ غير متوقّع.", "alert.unexpected.message": "لقد طرأ هناك خطأ غير متوقّع.",
"alert.unexpected.title": "المعذرة!", "alert.unexpected.title": "المعذرة!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "يمكنك/ي ضغط {combo} لتخطّي هذه في المرّة القادمة", "boost_modal.combo": "يمكنك/ي ضغط {combo} لتخطّي هذه في المرّة القادمة",
"bundle_column_error.body": "لقد وقع هناك خطأ أثناء عملية تحميل هذا العنصر.", "bundle_column_error.body": "لقد وقع هناك خطأ أثناء عملية تحميل هذا العنصر.",
"bundle_column_error.retry": "إعادة المحاولة", "bundle_column_error.retry": "إعادة المحاولة",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Asocedió un fallu inesperáu.", "alert.unexpected.message": "Asocedió un fallu inesperáu.",
"alert.unexpected.title": "¡Ups!", "alert.unexpected.title": "¡Ups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Pues primir {combo} pa saltar esto la próxima vegada", "boost_modal.combo": "Pues primir {combo} pa saltar esto la próxima vegada",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You can press {combo} to skip this next time", "boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Опитай отново", "bundle_column_error.retry": "Опитай отново",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "অপ্রত্যাশিত একটি সমস্যা হয়েছে।", "alert.unexpected.message": "অপ্রত্যাশিত একটি সমস্যা হয়েছে।",
"alert.unexpected.title": "ওহো!", "alert.unexpected.title": "ওহো!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "পরেরবার আপনি {combo} চাপ দিলে এটার শেষে চলে যেতে পারবেন", "boost_modal.combo": "পরেরবার আপনি {combo} চাপ দিলে এটার শেষে চলে যেতে পারবেন",
"bundle_column_error.body": "এই অংশটি দেখতে যেয়ে কোনো সমস্যা হয়েছে।", "bundle_column_error.body": "এই অংশটি দেখতে যেয়ে কোনো সমস্যা হয়েছে।",
"bundle_column_error.retry": "আবার চেষ্টা করুন", "bundle_column_error.retry": "আবার চেষ্টা করুন",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.", "alert.unexpected.message": "Ur fazi dic'hortozet zo degouezhet.",
"alert.unexpected.title": "C'hem !", "alert.unexpected.title": "C'hem !",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You can press {combo} to skip this next time", "boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Klask endro", "bundle_column_error.retry": "Klask endro",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "S'ha produït un error inesperat.", "alert.unexpected.message": "S'ha produït un error inesperat.",
"alert.unexpected.title": "Vaja!", "alert.unexpected.title": "Vaja!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop", "boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop",
"bundle_column_error.body": "S'ha produït un error en carregar aquest component.", "bundle_column_error.body": "S'ha produït un error en carregar aquest component.",
"bundle_column_error.retry": "Torna-ho a provar", "bundle_column_error.retry": "Torna-ho a provar",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.", "alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.",
"alert.unexpected.title": "Uups!", "alert.unexpected.title": "Uups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta", "boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta",
"bundle_column_error.body": "C'hè statu un prublemu caricandu st'elementu.", "bundle_column_error.body": "C'hè statu un prublemu caricandu st'elementu.",
"bundle_column_error.retry": "Pruvà torna", "bundle_column_error.retry": "Pruvà torna",

@ -43,6 +43,13 @@
"account_gallery.none": "Žádná média k zobrazení.", "account_gallery.none": "Žádná média k zobrazení.",
"alert.unexpected.message": "Objevila se neočekávaná chyba.", "alert.unexpected.message": "Objevila se neočekávaná chyba.",
"alert.unexpected.title": "Jejda!", "alert.unexpected.title": "Jejda!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Příště můžete pro přeskočení stisknout {combo}", "boost_modal.combo": "Příště můžete pro přeskočení stisknout {combo}",
"bundle_column_error.body": "Při načítání tohoto komponentu se něco pokazilo.", "bundle_column_error.body": "Při načítání tohoto komponentu se něco pokazilo.",
"bundle_column_error.retry": "Zkuste to znovu", "bundle_column_error.retry": "Zkuste to znovu",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Digwyddodd gwall annisgwyl.", "alert.unexpected.message": "Digwyddodd gwall annisgwyl.",
"alert.unexpected.title": "Wps!", "alert.unexpected.title": "Wps!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa", "boost_modal.combo": "Mae modd gwasgu {combo} er mwyn sgipio hyn tro nesa",
"bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.", "bundle_column_error.body": "Aeth rhywbeth o'i le tra'n llwytho'r elfen hon.",
"bundle_column_error.retry": "Ceisiwch eto", "bundle_column_error.retry": "Ceisiwch eto",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Der opstod en uventet fejl.", "alert.unexpected.message": "Der opstod en uventet fejl.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Du kan trykke {combo} for at springe dette over næste gang", "boost_modal.combo": "Du kan trykke {combo} for at springe dette over næste gang",
"bundle_column_error.body": "Noget gik galt under indlæsningen af dette komponent.", "bundle_column_error.body": "Noget gik galt under indlæsningen af dette komponent.",
"bundle_column_error.retry": "Prøv igen", "bundle_column_error.retry": "Prøv igen",

@ -43,6 +43,13 @@
"account_gallery.none": "Keine Medien vorhanden.", "account_gallery.none": "Keine Medien vorhanden.",
"alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.", "alert.unexpected.message": "Ein unerwarteter Fehler ist aufgetreten.",
"alert.unexpected.title": "Hoppla!", "alert.unexpected.title": "Hoppla!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Drücke {combo}, um dieses Fenster zu überspringen", "boost_modal.combo": "Drücke {combo}, um dieses Fenster zu überspringen",
"bundle_column_error.body": "Etwas ist beim Laden schiefgelaufen.", "bundle_column_error.body": "Etwas ist beim Laden schiefgelaufen.",
"bundle_column_error.retry": "Erneut versuchen", "bundle_column_error.retry": "Erneut versuchen",

@ -867,11 +867,43 @@
{ {
"descriptors": [ "descriptors": [
{ {
"defaultMessage": "Click the image to get a new captcha", "defaultMessage": "Play",
"id": "registration.captcha.hint" "id": "audio.play"
},
{
"defaultMessage": "Pause",
"id": "audio.pause"
},
{
"defaultMessage": "Mute",
"id": "audio.mute"
},
{
"defaultMessage": "Unmute",
"id": "audio.unmute"
},
{
"defaultMessage": "Hide audio",
"id": "audio.hide"
},
{
"defaultMessage": "Expand audio",
"id": "audio.expand"
},
{
"defaultMessage": "Close audio",
"id": "audio.close"
},
{
"defaultMessage": "Sensitive content",
"id": "status.sensitive_warning"
},
{
"defaultMessage": "Media hidden",
"id": "status.media_hidden"
} }
], ],
"path": "app/soapbox/features/auth_login/components/captcha.json" "path": "app/soapbox/features/audio/index.json"
}, },
{ {
"descriptors": [ "descriptors": [
@ -1226,7 +1258,7 @@
{ {
"descriptors": [ "descriptors": [
{ {
"defaultMessage": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)", "defaultMessage": "Add media attachment",
"id": "upload_button.label" "id": "upload_button.label"
} }
], ],

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Προέκυψε απροσδόκητο σφάλμα.", "alert.unexpected.message": "Προέκυψε απροσδόκητο σφάλμα.",
"alert.unexpected.title": "Εεπ!", "alert.unexpected.title": "Εεπ!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Μπορείς να πατήσεις {combo} για να το προσπεράσεις αυτό την επόμενη φορά", "boost_modal.combo": "Μπορείς να πατήσεις {combo} για να το προσπεράσεις αυτό την επόμενη φορά",
"bundle_column_error.body": "Κάτι πήγε στραβά ενώ φορτωνόταν αυτό το στοιχείο.", "bundle_column_error.body": "Κάτι πήγε στραβά ενώ φορτωνόταν αυτό το στοιχείο.",
"bundle_column_error.retry": "Δοκίμασε ξανά", "bundle_column_error.retry": "Δοκίμασε ξανά",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You can press {combo} to skip this next time", "boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",
@ -451,7 +458,7 @@
"unauthorized_modal.text": "You need to be logged in to do that.", "unauthorized_modal.text": "You need to be logged in to do that.",
"unauthorized_modal.title": "Sign up for {site_title}", "unauthorized_modal.title": "Sign up for {site_title}",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)", "upload_button.label": "Add media attachment",
"upload_error.limit": "File upload limit exceeded.", "upload_error.limit": "File upload limit exceeded.",
"upload_error.poll": "File upload not allowed with polls.", "upload_error.poll": "File upload not allowed with polls.",
"upload_form.description": "Describe for the visually impaired", "upload_form.description": "Describe for the visually impaired",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Neatendita eraro okazis.", "alert.unexpected.message": "Neatendita eraro okazis.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje", "boost_modal.combo": "Vi povas premi {combo} por preterpasi sekvafoje",
"bundle_column_error.body": "Io misfunkciis en la ŝargado de ĉi tiu elemento.", "bundle_column_error.body": "Io misfunkciis en la ŝargado de ĉi tiu elemento.",
"bundle_column_error.retry": "Bonvolu reprovi", "bundle_column_error.retry": "Bonvolu reprovi",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Ocurrió un error inesperado.", "alert.unexpected.message": "Ocurrió un error inesperado.",
"alert.unexpected.title": "¡Epa!", "alert.unexpected.title": "¡Epa!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Podés hacer clic en {combo} para saltar esto la próxima vez", "boost_modal.combo": "Podés hacer clic en {combo} para saltar esto la próxima vez",
"bundle_column_error.body": "Algo salió mal al cargar este componente.", "bundle_column_error.body": "Algo salió mal al cargar este componente.",
"bundle_column_error.retry": "Intentá de nuevo", "bundle_column_error.retry": "Intentá de nuevo",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Hubo un error inesperado.", "alert.unexpected.message": "Hubo un error inesperado.",
"alert.unexpected.title": "¡Ups!", "alert.unexpected.title": "¡Ups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez", "boost_modal.combo": "Puedes hacer clic en {combo} para saltar este aviso la próxima vez",
"bundle_column_error.body": "Algo salió mal al cargar este componente.", "bundle_column_error.body": "Algo salió mal al cargar este componente.",
"bundle_column_error.retry": "Inténtalo de nuevo", "bundle_column_error.retry": "Inténtalo de nuevo",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Tekkis ootamatu viga.", "alert.unexpected.message": "Tekkis ootamatu viga.",
"alert.unexpected.title": "Oih!", "alert.unexpected.title": "Oih!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Saad vajutada {combo}, et see järgmine kord vahele jätta", "boost_modal.combo": "Saad vajutada {combo}, et see järgmine kord vahele jätta",
"bundle_column_error.body": "Mindagi läks valesti selle komponendi laadimisel.", "bundle_column_error.body": "Mindagi läks valesti selle komponendi laadimisel.",
"bundle_column_error.retry": "Proovi uuesti", "bundle_column_error.retry": "Proovi uuesti",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Ustekabeko errore bat gertatu da.", "alert.unexpected.message": "Ustekabeko errore bat gertatu da.",
"alert.unexpected.title": "Ene!", "alert.unexpected.title": "Ene!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "{combo} sakatu dezakezu hurrengoan hau saltatzeko", "boost_modal.combo": "{combo} sakatu dezakezu hurrengoan hau saltatzeko",
"bundle_column_error.body": "Zerbait okerra gertatu da osagai hau kargatzean.", "bundle_column_error.body": "Zerbait okerra gertatu da osagai hau kargatzean.",
"bundle_column_error.retry": "Saiatu berriro", "bundle_column_error.retry": "Saiatu berriro",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "خطای پیش‌بینی‌نشده‌ای رخ داد.", "alert.unexpected.message": "خطای پیش‌بینی‌نشده‌ای رخ داد.",
"alert.unexpected.title": "ای وای!", "alert.unexpected.title": "ای وای!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید", "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
"bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.", "bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.",
"bundle_column_error.retry": "تلاش دوباره", "bundle_column_error.retry": "تلاش دوباره",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Tapahtui odottamaton virhe.", "alert.unexpected.message": "Tapahtui odottamaton virhe.",
"alert.unexpected.title": "Hups!", "alert.unexpected.title": "Hups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}", "boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}",
"bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.", "bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.",
"bundle_column_error.retry": "Yritä uudestaan", "bundle_column_error.retry": "Yritä uudestaan",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Une erreur inattendue sest produite.", "alert.unexpected.message": "Une erreur inattendue sest produite.",
"alert.unexpected.title": "Oups!", "alert.unexpected.title": "Oups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci, la prochaine fois", "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci, la prochaine fois",
"bundle_column_error.body": "Une erreur sest produite lors du chargement de ce composant.", "bundle_column_error.body": "Une erreur sest produite lors du chargement de ce composant.",
"bundle_column_error.retry": "Réessayer", "bundle_column_error.retry": "Réessayer",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You can press {combo} to skip this next time", "boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Aconteceu un fallo non agardado.", "alert.unexpected.message": "Aconteceu un fallo non agardado.",
"alert.unexpected.title": "Vaia!", "alert.unexpected.title": "Vaia!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Pulse {combo} para saltar esto a próxima vez", "boost_modal.combo": "Pulse {combo} para saltar esto a próxima vez",
"bundle_column_error.body": "Houbo un fallo mentras se cargaba este compoñente.", "bundle_column_error.body": "Houbo un fallo mentras se cargaba este compoñente.",
"bundle_column_error.retry": "Inténteo de novo", "bundle_column_error.retry": "Inténteo de novo",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "אירעה שגיאה בלתי צפויה.", "alert.unexpected.message": "אירעה שגיאה בלתי צפויה.",
"alert.unexpected.title": "אופס!", "alert.unexpected.title": "אופס!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה", "boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה",
"bundle_column_error.body": "משהו השתבש בעת הצגת הרכיב הזה.", "bundle_column_error.body": "משהו השתבש בעת הצגת הרכיב הזה.",
"bundle_column_error.retry": "לנסות שוב", "bundle_column_error.retry": "לנסות שוב",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You can press {combo} to skip this next time", "boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put", "boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Váratlan hiba történt.", "alert.unexpected.message": "Váratlan hiba történt.",
"alert.unexpected.title": "Hoppá!", "alert.unexpected.title": "Hoppá!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Hogy átugord ezt következő alkalommal, használd {combo}", "boost_modal.combo": "Hogy átugord ezt következő alkalommal, használd {combo}",
"bundle_column_error.body": "Hiba történt a komponens betöltése közben.", "bundle_column_error.body": "Hiba történt a komponens betöltése közben.",
"bundle_column_error.retry": "Próbáld újra", "bundle_column_error.retry": "Próbáld újra",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Վա՜յ", "alert.unexpected.title": "Վա՜յ",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Կարող ես սեղմել {combo}՝ սա հաջորդ անգամ բաց թողնելու համար", "boost_modal.combo": "Կարող ես սեղմել {combo}՝ սա հաջորդ անգամ բաց թողնելու համար",
"bundle_column_error.body": "Այս բաղադրիչը բեռնելու ընթացքում ինչ֊որ բան խափանվեց։", "bundle_column_error.body": "Այս բաղադրիչը բեռնելու ընթացքում ինչ֊որ բան խափանվեց։",
"bundle_column_error.retry": "Կրկին փորձել", "bundle_column_error.retry": "Կրկին փորձել",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Terjadi kesalahan yang tidak terduga.", "alert.unexpected.message": "Terjadi kesalahan yang tidak terduga.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini", "boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini",
"bundle_column_error.body": "Kesalahan terjadi saat memuat komponen ini.", "bundle_column_error.body": "Kesalahan terjadi saat memuat komponen ini.",
"bundle_column_error.retry": "Coba lagi", "bundle_column_error.retry": "Coba lagi",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo", "boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Si è verificato un errore inatteso.", "alert.unexpected.message": "Si è verificato un errore inatteso.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta", "boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta",
"bundle_column_error.body": "E' avvenuto un errore durante il caricamento di questo componente.", "bundle_column_error.body": "E' avvenuto un errore durante il caricamento di questo componente.",
"bundle_column_error.retry": "Riprova", "bundle_column_error.retry": "Riprova",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "不明なエラーが発生しました。", "alert.unexpected.message": "不明なエラーが発生しました。",
"alert.unexpected.title": "エラー!", "alert.unexpected.title": "エラー!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "次からは{combo}を押せばスキップできます", "boost_modal.combo": "次からは{combo}を押せばスキップできます",
"bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。", "bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。",
"bundle_column_error.retry": "再試行", "bundle_column_error.retry": "再試行",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "წარმოიშვა მოულოდნელი შეცდომა.", "alert.unexpected.message": "წარმოიშვა მოულოდნელი შეცდომა.",
"alert.unexpected.title": "უპს!", "alert.unexpected.title": "უპს!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "შეგიძლიათ დააჭიროთ {combo}-ს რათა შემდეგ ჯერზე გამოტოვოთ ეს", "boost_modal.combo": "შეგიძლიათ დააჭიროთ {combo}-ს რათა შემდეგ ჯერზე გამოტოვოთ ეს",
"bundle_column_error.body": "ამ კომპონენტის ჩატვირთვისას რაღაც აირია.", "bundle_column_error.body": "ამ კომპონენტის ჩატვირთვისას რაღაც აირია.",
"bundle_column_error.retry": "სცადეთ კიდევ ერთხელ", "bundle_column_error.retry": "სცადეთ კიდევ ერთხელ",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Бір нәрсе дұрыс болмады.", "alert.unexpected.message": "Бір нәрсе дұрыс болмады.",
"alert.unexpected.title": "Өй!", "alert.unexpected.title": "Өй!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Келесіде өткізіп жіберу үшін басыңыз {combo}", "boost_modal.combo": "Келесіде өткізіп жіберу үшін басыңыз {combo}",
"bundle_column_error.body": "Бұл компонентті жүктеген кезде бір қате пайда болды.", "bundle_column_error.body": "Бұл компонентті жүктеген кезде бір қате пайда болды.",
"bundle_column_error.retry": "Қайтадан көріңіз", "bundle_column_error.retry": "Қайтадан көріңіз",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "예측하지 못한 에러가 발생했습니다.", "alert.unexpected.message": "예측하지 못한 에러가 발생했습니다.",
"alert.unexpected.title": "앗!", "alert.unexpected.title": "앗!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "{combo}를 누르면 다음부터 이 과정을 건너뛸 수 있습니다", "boost_modal.combo": "{combo}를 누르면 다음부터 이 과정을 건너뛸 수 있습니다",
"bundle_column_error.body": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.", "bundle_column_error.body": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
"bundle_column_error.retry": "다시 시도", "bundle_column_error.retry": "다시 시도",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You can press {combo} to skip this next time", "boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Negaidīta kļūda.", "alert.unexpected.message": "Negaidīta kļūda.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Nospied {combo} lai izlaistu šo nākamreiz", "boost_modal.combo": "Nospied {combo} lai izlaistu šo nākamreiz",
"bundle_column_error.body": "Kaut kas nogāja greizi ielādējot šo komponenti.", "bundle_column_error.body": "Kaut kas nogāja greizi ielādējot šo komponenti.",
"bundle_column_error.retry": "Mēģini vēlreiz", "bundle_column_error.retry": "Mēģini vēlreiz",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Неочекувана грешка.", "alert.unexpected.message": "Неочекувана грешка.",
"alert.unexpected.title": "Упс!", "alert.unexpected.title": "Упс!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Кликни {combo} за да го прескокниш ова нареден пат", "boost_modal.combo": "Кликни {combo} за да го прескокниш ова нареден пат",
"bundle_column_error.body": "Се случи проблем при вчитувањето.", "bundle_column_error.body": "Се случи проблем при вчитувањето.",
"bundle_column_error.retry": "Обидете се повторно", "bundle_column_error.retry": "Обидете се повторно",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You can press {combo} to skip this next time", "boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.body": "Something went wrong while loading this component.", "bundle_column_error.body": "Something went wrong while loading this component.",
"bundle_column_error.retry": "Try again", "bundle_column_error.retry": "Try again",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Er deed zich een onverwachte fout voor", "alert.unexpected.message": "Er deed zich een onverwachte fout voor",
"alert.unexpected.title": "Oeps!", "alert.unexpected.title": "Oeps!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan", "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan",
"bundle_column_error.body": "Tijdens het laden van dit onderdeel is er iets fout gegaan.", "bundle_column_error.body": "Tijdens het laden van dit onderdeel is er iets fout gegaan.",
"bundle_column_error.retry": "Opnieuw proberen", "bundle_column_error.retry": "Opnieuw proberen",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Eit uforventa problem har hendt.", "alert.unexpected.message": "Eit uforventa problem har hendt.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Du kan trykke {combo} for å hoppe over dette neste gong", "boost_modal.combo": "Du kan trykke {combo} for å hoppe over dette neste gong",
"bundle_column_error.body": "Noko gikk gale mens komponent ble nedlasta.", "bundle_column_error.body": "Noko gikk gale mens komponent ble nedlasta.",
"bundle_column_error.retry": "Prøv igjen", "bundle_column_error.retry": "Prøv igjen",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang", "boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang",
"bundle_column_error.body": "Noe gikk galt mens denne komponenten lastet.", "bundle_column_error.body": "Noe gikk galt mens denne komponenten lastet.",
"bundle_column_error.retry": "Prøv igjen", "bundle_column_error.retry": "Prøv igjen",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Una error ses producha.", "alert.unexpected.message": "Una error ses producha.",
"alert.unexpected.title": "Ops!", "alert.unexpected.title": "Ops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven", "boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven",
"bundle_column_error.body": "Quicòm a fach mèuca pendent lo cargament daqueste compausant.", "bundle_column_error.body": "Quicòm a fach mèuca pendent lo cargament daqueste compausant.",
"bundle_column_error.retry": "Tornar ensajar", "bundle_column_error.retry": "Tornar ensajar",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Wystąpił nieoczekiwany błąd.", "alert.unexpected.message": "Wystąpił nieoczekiwany błąd.",
"alert.unexpected.title": "O nie!", "alert.unexpected.title": "O nie!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem", "boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem",
"bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.", "bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.",
"bundle_column_error.retry": "Spróbuj ponownie", "bundle_column_error.retry": "Spróbuj ponownie",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Um erro inesperado ocorreu.", "alert.unexpected.message": "Um erro inesperado ocorreu.",
"alert.unexpected.title": "Eita!", "alert.unexpected.title": "Eita!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Você pode pressionar {combo} para ignorar este diálogo na próxima vez", "boost_modal.combo": "Você pode pressionar {combo} para ignorar este diálogo na próxima vez",
"bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.", "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.",
"bundle_column_error.retry": "Tente novamente", "bundle_column_error.retry": "Tente novamente",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Ocorreu um erro inesperado.", "alert.unexpected.message": "Ocorreu um erro inesperado.",
"alert.unexpected.title": "Bolas!", "alert.unexpected.title": "Bolas!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Pode clicar {combo} para não voltar a ver", "boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
"bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.", "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.",
"bundle_column_error.retry": "Tente de novo", "bundle_column_error.retry": "Tente de novo",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "A apărut o eroare neașteptată.", "alert.unexpected.message": "A apărut o eroare neașteptată.",
"alert.unexpected.title": "Hopa!", "alert.unexpected.title": "Hopa!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Poți apăsa {combo} pentru a omite asta data viitoare", "boost_modal.combo": "Poți apăsa {combo} pentru a omite asta data viitoare",
"bundle_column_error.body": "Ceva nu a funcționat la încărcarea acestui component.", "bundle_column_error.body": "Ceva nu a funcționat la încărcarea acestui component.",
"bundle_column_error.retry": "Încearcă din nou", "bundle_column_error.retry": "Încearcă din nou",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Что-то пошло не так.", "alert.unexpected.message": "Что-то пошло не так.",
"alert.unexpected.title": "Ой!", "alert.unexpected.title": "Ой!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз", "boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз",
"bundle_column_error.body": "Что-то пошло не так при загрузке этого компонента.", "bundle_column_error.body": "Что-то пошло не так при загрузке этого компонента.",
"bundle_column_error.retry": "Попробовать снова", "bundle_column_error.retry": "Попробовать снова",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Vyskytla sa nečakaná chyba.", "alert.unexpected.message": "Vyskytla sa nečakaná chyba.",
"alert.unexpected.title": "Ups!", "alert.unexpected.title": "Ups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Nabudúce môžeš kliknúť {combo} pre preskočenie", "boost_modal.combo": "Nabudúce môžeš kliknúť {combo} pre preskočenie",
"bundle_column_error.body": "Pri načítaní tohto prvku nastala nejaká chyba.", "bundle_column_error.body": "Pri načítaní tohto prvku nastala nejaká chyba.",
"bundle_column_error.retry": "Skús to znova", "bundle_column_error.retry": "Skús to znova",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Zgodila se je nepričakovana napaka.", "alert.unexpected.message": "Zgodila se je nepričakovana napaka.",
"alert.unexpected.title": "Uups!", "alert.unexpected.title": "Uups!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Če želite preskočiti to, lahko pritisnete {combo}", "boost_modal.combo": "Če želite preskočiti to, lahko pritisnete {combo}",
"bundle_column_error.body": "Med nalaganjem te komponente je prišlo do napake.", "bundle_column_error.body": "Med nalaganjem te komponente je prišlo do napake.",
"bundle_column_error.retry": "Poskusi ponovno", "bundle_column_error.retry": "Poskusi ponovno",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Ndodhi një gabim të papritur.", "alert.unexpected.message": "Ndodhi një gabim të papritur.",
"alert.unexpected.title": "Hëm!", "alert.unexpected.title": "Hëm!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Mund të shtypni {combo}, që të anashkalohet kjo herës tjetër", "boost_modal.combo": "Mund të shtypni {combo}, që të anashkalohet kjo herës tjetër",
"bundle_column_error.body": "Diç shkoi ters teksa ngarkohej ky përbërës.", "bundle_column_error.body": "Diç shkoi ters teksa ngarkohej ky përbërës.",
"bundle_column_error.retry": "Riprovoni", "bundle_column_error.retry": "Riprovoni",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.message": "An unexpected error occurred.",
"alert.unexpected.title": "Oops!", "alert.unexpected.title": "Oops!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put", "boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put",
"bundle_column_error.body": "Nešto je pošlo po zlu prilikom učitavanja ove komponente.", "bundle_column_error.body": "Nešto je pošlo po zlu prilikom učitavanja ove komponente.",
"bundle_column_error.retry": "Pokušajte ponovo", "bundle_column_error.retry": "Pokušajte ponovo",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Појавила се неочекивана грешка.", "alert.unexpected.message": "Појавила се неочекивана грешка.",
"alert.unexpected.title": "Упс!", "alert.unexpected.title": "Упс!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут", "boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут",
"bundle_column_error.body": "Нешто је пошло по злу приликом учитавања ове компоненте.", "bundle_column_error.body": "Нешто је пошло по злу приликом учитавања ове компоненте.",
"bundle_column_error.retry": "Покушајте поново", "bundle_column_error.retry": "Покушајте поново",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Ett oväntat fel uppstod.", "alert.unexpected.message": "Ett oväntat fel uppstod.",
"alert.unexpected.title": "Hoppsan!", "alert.unexpected.title": "Hoppsan!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Du kan trycka {combo} för att slippa denna nästa gång", "boost_modal.combo": "Du kan trycka {combo} för att slippa denna nästa gång",
"bundle_column_error.body": "Något gick fel när du laddade denna komponent.", "bundle_column_error.body": "Något gick fel när du laddade denna komponent.",
"bundle_column_error.retry": "Försök igen", "bundle_column_error.retry": "Försök igen",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "எதிர் பாராத பிழை ஏற்பட்டு விட்டது.", "alert.unexpected.message": "எதிர் பாராத பிழை ஏற்பட்டு விட்டது.",
"alert.unexpected.title": "அச்சச்சோ!", "alert.unexpected.title": "அச்சச்சோ!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "நீங்கள் அழுத்தவும் {combo} அடுத்த முறை தவிர்க்கவும்", "boost_modal.combo": "நீங்கள் அழுத்தவும் {combo} அடுத்த முறை தவிர்க்கவும்",
"bundle_column_error.body": "இந்த கூறுகளை ஏற்றும்போது ஏதோ தவறு ஏற்பட்டது.", "bundle_column_error.body": "இந்த கூறுகளை ஏற்றும்போது ஏதோ தவறு ஏற்பட்டது.",
"bundle_column_error.retry": "மீண்டும் முயற்சி செய்", "bundle_column_error.retry": "மீண்டும் முயற்சி செய்",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "అనుకోని తప్పు జరిగినది.", "alert.unexpected.message": "అనుకోని తప్పు జరిగినది.",
"alert.unexpected.title": "అయ్యో!", "alert.unexpected.title": "అయ్యో!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "మీరు తదుపరిసారి దీనిని దాటవేయడానికి {combo} నొక్కవచ్చు", "boost_modal.combo": "మీరు తదుపరిసారి దీనిని దాటవేయడానికి {combo} నొక్కవచ్చు",
"bundle_column_error.body": "ఈ భాగం లోడ్ అవుతున్నప్పుడు ఏదో తప్పు జరిగింది.", "bundle_column_error.body": "ఈ భాగం లోడ్ అవుతున్నప్పుడు ఏదో తప్పు జరిగింది.",
"bundle_column_error.retry": "మళ్ళీ ప్రయత్నించండి", "bundle_column_error.retry": "మళ్ళీ ప్రయత్నించండి",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "เกิดข้อผิดพลาดที่ไม่คาดคิด", "alert.unexpected.message": "เกิดข้อผิดพลาดที่ไม่คาดคิด",
"alert.unexpected.title": "อุปส์!", "alert.unexpected.title": "อุปส์!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "คุณสามารถกด {combo} เพื่อข้ามสิ่งนี้ในครั้งถัดไป", "boost_modal.combo": "คุณสามารถกด {combo} เพื่อข้ามสิ่งนี้ในครั้งถัดไป",
"bundle_column_error.body": "มีบางอย่างผิดพลาดขณะโหลดส่วนประกอบนี้", "bundle_column_error.body": "มีบางอย่างผิดพลาดขณะโหลดส่วนประกอบนี้",
"bundle_column_error.retry": "ลองอีกครั้ง", "bundle_column_error.retry": "ลองอีกครั้ง",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Beklenmedik bir hata oluştu.", "alert.unexpected.message": "Beklenmedik bir hata oluştu.",
"alert.unexpected.title": "Hay aksi!", "alert.unexpected.title": "Hay aksi!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Bir daha ki sefere {combo} tuşuna basabilirsiniz", "boost_modal.combo": "Bir daha ki sefere {combo} tuşuna basabilirsiniz",
"bundle_column_error.body": "Bu bileşen yüklenirken bir şeyler ters gitti.", "bundle_column_error.body": "Bu bileşen yüklenirken bir şeyler ters gitti.",
"bundle_column_error.retry": "Tekrar deneyin", "bundle_column_error.retry": "Tekrar deneyin",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "Трапилась неочікувана помилка.", "alert.unexpected.message": "Трапилась неочікувана помилка.",
"alert.unexpected.title": "Ой!", "alert.unexpected.title": "Ой!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу", "boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу",
"bundle_column_error.body": "Щось пішло не так під час завантаження компоненту.", "bundle_column_error.body": "Щось пішло не так під час завантаження компоненту.",
"bundle_column_error.retry": "Спробуйте ще раз", "bundle_column_error.retry": "Спробуйте ще раз",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "发生了意外错误。", "alert.unexpected.message": "发生了意外错误。",
"alert.unexpected.title": "哎呀!", "alert.unexpected.title": "哎呀!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "下次按住 {combo} 即可跳过此提示", "boost_modal.combo": "下次按住 {combo} 即可跳过此提示",
"bundle_column_error.body": "载入这个组件时发生了错误。", "bundle_column_error.body": "载入这个组件时发生了错误。",
"bundle_column_error.retry": "重试", "bundle_column_error.retry": "重试",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "發生不可預期的錯誤。", "alert.unexpected.message": "發生不可預期的錯誤。",
"alert.unexpected.title": "噢!", "alert.unexpected.title": "噢!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "如你想在下次路過這顯示,請按{combo}", "boost_modal.combo": "如你想在下次路過這顯示,請按{combo}",
"bundle_column_error.body": "加載本組件出錯。", "bundle_column_error.body": "加載本組件出錯。",
"bundle_column_error.retry": "重試", "bundle_column_error.retry": "重試",

@ -43,6 +43,13 @@
"account_gallery.none": "No media to show.", "account_gallery.none": "No media to show.",
"alert.unexpected.message": "發生了非預期的錯誤。", "alert.unexpected.message": "發生了非預期的錯誤。",
"alert.unexpected.title": "哎呀!", "alert.unexpected.title": "哎呀!",
"audio.close": "Close audio",
"audio.expand": "Expand audio",
"audio.hide": "Hide audio",
"audio.mute": "Mute",
"audio.pause": "Pause",
"audio.play": "Play",
"audio.unmute": "Unmute",
"boost_modal.combo": "下次您可以按 {combo} 跳過", "boost_modal.combo": "下次您可以按 {combo} 跳過",
"bundle_column_error.body": "載入此元件時發生錯誤。", "bundle_column_error.body": "載入此元件時發生錯誤。",
"bundle_column_error.retry": "重試", "bundle_column_error.retry": "重試",

@ -16,7 +16,7 @@ const mapStateToProps = state => {
const me = state.get('me'); const me = state.get('me');
return { return {
account: state.getIn(['accounts', me]), account: state.getIn(['accounts', me]),
hasPatron: state.getIn(['soapbox', 'extensions', 'patron']), hasPatron: state.getIn(['soapbox', 'extensions', 'patron', 'enabled']),
features: getFeatures(state.get('instance')), features: getFeatures(state.get('instance')),
}; };
}; };

@ -14,6 +14,8 @@ describe('media_attachments reducer', () => {
'.mp4', '.mp4',
'.m4v', '.m4v',
'.mov', '.mov',
'.mp3',
'.ogg',
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/gif', 'image/gif',
@ -21,6 +23,9 @@ describe('media_attachments reducer', () => {
'video/webm', 'video/webm',
'video/mp4', 'video/mp4',
'video/quicktime', 'video/quicktime',
'audio/mp3',
'audio/mpeg',
'audio/ogg',
]), ]),
})); }));
}); });

@ -16,6 +16,8 @@ const initialState = ImmutableMap({
'.mp4', '.mp4',
'.m4v', '.m4v',
'.mov', '.mov',
'.mp3',
'.ogg',
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
'image/gif', 'image/gif',
@ -23,6 +25,9 @@ const initialState = ImmutableMap({
'video/webm', 'video/webm',
'video/mp4', 'video/mp4',
'video/quicktime', 'video/quicktime',
'audio/mp3',
'audio/mpeg',
'audio/ogg',
]), ]),
}); });

@ -399,6 +399,10 @@ a .account__avatar {
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: 1; z-index: 1;
&.still-image {
position: absolute;
}
} }
} }

@ -69,3 +69,4 @@
@import 'components/media-spoiler'; @import 'components/media-spoiler';
@import 'components/error-boundary'; @import 'components/error-boundary';
@import 'components/video-player'; @import 'components/video-player';
@import 'components/audio-player';

@ -0,0 +1,397 @@
.audio-error-cover {
align-items: center;
background: var(--background-color);
color: var(--primary-text-color);
cursor: pointer;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
margin-top: 8px;
position: relative;
text-align: center;
z-index: 100;
}
.status__audio-player {
background: var(--background-color);
box-sizing: border-box;
cursor: default; /* May not be needed */
margin-top: 8px;
overflow: hidden;
position: relative;
}
.status__audio-player-audio {
height: 100%;
object-fit: cover;
position: relative;
top: 50%;
transform: translateY(-50%);
width: 100%;
z-index: 1;
}
.status__audio-player-expand,
.status__audio-player-mute {
color: var(--primary-text-color);
opacity: 0.8;
position: absolute;
right: 4px;
text-shadow: 0 1px 1px $base-shadow-color, 1px 0 1px $base-shadow-color;
}
.status__audio-player-expand {
bottom: 4px;
z-index: 100;
}
.status__audio-player-mute {
top: 4px;
z-index: 5;
}
.detailed,
.fullscreen {
.audio-player__volume__current,
.audio-player__volume::before {
bottom: 27px;
}
.audio-player__volume__handle {
bottom: 23px;
}
}
.audio-player {
overflow: hidden;
position: relative;
background: $base-shadow-color;
max-width: 100%;
border-radius: 4px;
height: 57px;
&.warning_visible {
height: 92px;
}
&:focus {
outline: 0;
}
audio {
max-width: 100vw;
max-height: 80vh;
min-height: 120px;
object-fit: contain;
z-index: 1;
}
&.fullscreen {
width: 100% !important;
height: 100% !important;
margin: 0;
audio {
max-width: 100% !important;
max-height: 100% !important;
width: 100% !important;
height: 100% !important;
}
}
&.inline {
audio {
object-fit: contain;
position: relative;
}
}
&__controls {
position: absolute;
z-index: 2;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
background: linear-gradient(0deg, rgba($base-shadow-color, 0.85) 0, rgba($base-shadow-color, 0.45) 60%, transparent);
padding: 0 15px;
opacity: 1;
transition: opacity .1s ease;
}
&.inactive {
min-height: 57px;
audio,
.audio-player__controls {
visibility: hidden;
}
}
&__spoiler {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 4;
border: 0;
background: var(--background-color);
color: var(--primary-text-color--faint);
transition: none;
pointer-events: auto;
&:hover,
&:active,
&:focus {
color: var(--primary-text-color);
}
&__title {
display: block;
font-size: 14px;
}
&__subtitle {
display: block;
font-size: 11px;
font-weight: 500;
}
}
&__buttons-bar {
display: flex;
justify-content: space-between;
padding-bottom: 10px;
}
&__spoiler-warning {
font-size: 16px;
white-space: nowrap;
overlow: hidden;
text-overflow: ellipsis;
background: hsl( var(--brand-color_h), var(--brand-color_s), 20% );
padding: 5px;
&__label {
color: white;
}
button {
background: transparent;
font-size: 16px;
border: 0;
color: rgba(#ffffff, 0.75);
position: absolute;
right: 0;
padding-right: 5px;
i {
margin-right: 0;
}
&:active,
&:hover,
&:focus {
color: #ffffff;
}
}
}
&__buttons {
font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&.left {
button {
padding-left: 0;
}
}
button {
background: transparent;
padding: 2px 10px;
font-size: 16px;
border: 0;
color: rgba(#ffffff, 0.75);
&:active,
&:hover,
&:focus {
color: #ffffff;
}
}
}
&__time-sep,
&__time-total,
&__time-current {
font-size: 14px;
font-weight: 500;
}
&__time-current {
color: #ffffff;
margin-left: 60px;
}
&__time-sep {
display: inline-block;
margin: 0 6px;
}
&__time-sep,
&__time-total {
color: #ffffff;
}
&__volume {
cursor: pointer;
height: 24px;
display: inline;
&::before {
content: "";
width: 50px;
background: rgba(#ffffff, 0.35);
border-radius: 4px;
display: block;
position: absolute;
height: 4px;
left: 85px;
bottom: 20px;
}
&__current {
display: block;
position: absolute;
height: 4px;
border-radius: 4px;
left: 85px;
bottom: 20px;
background: var(--brand-color);
}
&__handle {
position: absolute;
z-index: 3;
border-radius: 50%;
width: 12px;
height: 12px;
bottom: 16px;
left: 70px;
transition: opacity .1s ease;
background: var(--brand-color);
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
pointer-events: none;
}
}
&__link {
padding: 2px 10px;
a {
text-decoration: none;
font-size: 14px;
font-weight: 500;
color: #ffffff;
&:hover,
&:active,
&:focus {
text-decoration: underline;
}
}
}
&__seek {
cursor: pointer;
height: 24px;
position: relative;
&::before {
content: "";
width: 100%;
background: rgba(#ffffff, 0.35);
border-radius: 4px;
display: block;
position: absolute;
height: 4px;
top: 10px;
}
&__progress,
&__buffer {
display: block;
position: absolute;
height: 4px;
border-radius: 4px;
top: 10px;
background: var(--brand-color);
}
&__buffer {
background: rgba(#ffffff, 0.2);
}
&__handle {
position: absolute;
z-index: 3;
opacity: 1;
border-radius: 50%;
width: 12px;
height: 12px;
top: 6px;
margin-left: -6px;
transition: opacity .1s ease;
background: var(--brand-color);
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
pointer-events: none;
}
&:hover {
.audio-player__seek__handle {
opacity: 1;
}
}
}
&.detailed {
width: 100vmin;
height: 80px;
.audio-player__buttons {
button {
padding-top: 10px;
padding-bottom: 10px;
}
}
}
}
.media-spoiler-audio {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
margin-top: 8px;
position: relative;
border: 0;
display: block;
}
.media-spoiler-audio-play-icon {
border-radius: 100px;
color: var(--primary-text-color--faint);
font-size: 36px;
left: 50%;
padding: 5px;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}

@ -284,7 +284,7 @@
.compose-form__upload-thumbnail { .compose-form__upload-thumbnail {
border-radius: 4px; border-radius: 4px;
background-position: center; background-position: center;
background-size: cover; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
height: 140px; height: 140px;
width: 100%; width: 100%;

@ -38,7 +38,8 @@
} }
} }
.video-player { .video-player,
.audio-player {
margin-top: 8px; margin-top: 8px;
} }
} }

@ -16,6 +16,14 @@
position: relative; position: relative;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
&__icons {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 100px;
}
} }
.media-gallery__item-thumbnail { .media-gallery__item-thumbnail {
@ -31,6 +39,10 @@
.still-image { .still-image {
height: 100%; height: 100%;
width: 100%; width: 100%;
img {
object-fit: contain;
}
} }
.still-image--play-on-hover::before { .still-image--play-on-hover::before {
@ -108,7 +120,8 @@
position: absolute; position: absolute;
} }
.media-gallery__gifv__label { .media-gallery__gifv__label,
.media-gallery__file-extension__label {
display: block; display: block;
position: absolute; position: absolute;
color: var(--primary-text-color); color: var(--primary-text-color);

@ -38,7 +38,8 @@
overflow-y: hidden; overflow-y: hidden;
} }
.video-modal { .video-modal,
.audio-modal {
max-width: 100vw; max-width: 100vw;
max-height: 100vh; max-height: 100vh;
position: relative; position: relative;
@ -49,12 +50,16 @@
height: 100%; height: 100%;
position: relative; position: relative;
.audio-player.detailed,
.extended-video-player { .extended-video-player {
width: 100%;
height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
}
.extended-video-player {
width: 100%;
height: 100%;
video { video {
max-width: $media-modal-media-max-width; max-width: $media-modal-media-max-width;

@ -78,7 +78,8 @@
opacity: 1; opacity: 1;
animation: fade 150ms linear; animation: fade 150ms linear;
.video-player { .video-player,
.audio-player {
margin-top: 8px; margin-top: 8px;
} }
@ -176,7 +177,8 @@
white-space: normal; white-space: normal;
} }
.video-player { .video-player,
.audio-player {
margin-top: 8px; margin-top: 8px;
max-width: 250px; max-width: 250px;
} }
@ -453,7 +455,8 @@ a.status-card {
margin: 0; margin: 0;
} }
.status-card-video { .status-card-video,
.status-card-audio {
iframe { iframe {
width: 100%; width: 100%;
height: 100%; height: 100%;

@ -11,6 +11,10 @@
width: 100%; width: 100%;
background: var(--brand-color--faint); background: var(--brand-color--faint);
.still-image {
height: 100%;
}
img { img {
display: block; display: block;
height: 100%; height: 100%;

@ -1,6 +1,8 @@
# Customizing Soapbox # Customizing Soapbox
If you haven't already, [install Soapbox](https://soapbox.pub/). If you haven't already, [install Soapbox](https://soapbox.pub/). But before you install soapbox, you should consider how Soapbox is installed, by default.
Soapbox, by default, is installed to replace the default Pleroma front end. By extension, the Pleroma Masto front end continues to be available at the `/web` sub-URL, which you can reference, if you'd like, in the `promoPanel` section of `soapbox.json`
There are two main places Soapbox gets its configuration: There are two main places Soapbox gets its configuration:
@ -97,3 +99,9 @@ Simply rename `about.example` to `about`, or create your own.
The `soapbox.json` file navlinks section's default URL values are pointing to the above file location, when the `about.example` folder is renamed to `about` The `soapbox.json` file navlinks section's default URL values are pointing to the above file location, when the `about.example` folder is renamed to `about`
These four template files have placeholders in them, e.g. "Your_Instance", that should be edited to match your Soapbox instance configuration, and will be meaningless to your users until you edit them. These four template files have placeholders in them, e.g. "Your_Instance", that should be edited to match your Soapbox instance configuration, and will be meaningless to your users until you edit them.
## Alternate Soapbox URL Root Location
If you want to install Soapbox at an alternate URL, allowing you to potentially run more than 2 front ends on a Pleroma server, you can consider deploying the Nginx config created by @a1batross, available [here](https://git.mentality.rip/a1batross/soapbox-nginx-config/src/branch/master/soapbox.nginx)
Tech support is limited for this level of customization

@ -12,9 +12,6 @@
"url": "https://blog.example.com" "url": "https://blog.example.com"
}] }]
}, },
"extensions": {
"patron": false
},
"defaultSettings": { "defaultSettings": {
"autoPlayGif": false, "autoPlayGif": false,
"themeMode": "light" "themeMode": "light"

@ -9,7 +9,6 @@ const { settings, output } = require('./configuration');
const watchOptions = {}; const watchOptions = {};
const backendUrl = process.env.BACKEND_URL || 'http://localhost:4000'; const backendUrl = process.env.BACKEND_URL || 'http://localhost:4000';
const patronUrl = process.env.PATRON_URL || 'http://localhost:5000';
const secureProxy = !(process.env.PROXY_HTTPS_INSECURE === 'true'); const secureProxy = !(process.env.PROXY_HTTPS_INSECURE === 'true');
const backendEndpoints = [ const backendEndpoints = [
@ -22,7 +21,6 @@ const backendEndpoints = [
'/.well-known/webfinger', '/.well-known/webfinger',
'/static', '/static',
'/emoji', '/emoji',
'/patron',
]; ];
const makeProxyConfig = () => { const makeProxyConfig = () => {
@ -33,10 +31,6 @@ const makeProxyConfig = () => {
secure: secureProxy, secure: secureProxy,
}; };
}); });
proxyConfig['/patron'] = {
target: patronUrl,
secure: secureProxy,
};
return proxyConfig; return proxyConfig;
}; };

Loading…
Cancel
Save