diff options
author | beatrix-bitrot <beatrix.bitrot@gmail.com> | 2017-06-27 20:46:13 +0000 |
---|---|---|
committer | beatrix-bitrot <beatrix.bitrot@gmail.com> | 2017-06-27 20:46:13 +0000 |
commit | ddafde942ca53816c19b0ea0cb40bb1b46cf5668 (patch) | |
tree | c0ac2138fe994c4c2a15c23b47d4155f75148945 /app/javascript | |
parent | e6300de1421d28d173658e61601b9e016c3d0a6d (diff) | |
parent | da42bfadb58888e3a18afd66395f0f3edc2fa622 (diff) |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'app/javascript')
26 files changed, 295 insertions, 271 deletions
diff --git a/app/javascript/mastodon/actions/reports.js b/app/javascript/mastodon/actions/reports.js index 9b632be74..b19a07285 100644 --- a/app/javascript/mastodon/actions/reports.js +++ b/app/javascript/mastodon/actions/reports.js @@ -1,4 +1,5 @@ import api from '../api'; +import { openModal, closeModal } from './modal'; export const REPORT_INIT = 'REPORT_INIT'; export const REPORT_CANCEL = 'REPORT_CANCEL'; @@ -11,10 +12,14 @@ export const REPORT_STATUS_TOGGLE = 'REPORT_STATUS_TOGGLE'; export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE'; export function initReport(account, status) { - return { - type: REPORT_INIT, - account, - status, + return dispatch => { + dispatch({ + type: REPORT_INIT, + account, + status, + }); + + dispatch(openModal('REPORT')); }; }; @@ -40,7 +45,10 @@ export function submitReport() { account_id: getState().getIn(['reports', 'new', 'account_id']), status_ids: getState().getIn(['reports', 'new', 'status_ids']), comment: getState().getIn(['reports', 'new', 'comment']), - }).then(response => dispatch(submitReportSuccess(response.data))).catch(error => dispatch(submitReportFail(error))); + }).then(response => { + dispatch(closeModal()); + dispatch(submitReportSuccess(response.data)); + }).catch(error => dispatch(submitReportFail(error))); }; }; diff --git a/app/javascript/mastodon/components/column_collapsable.js b/app/javascript/mastodon/components/column_collapsable.js deleted file mode 100644 index d6b4edb9f..000000000 --- a/app/javascript/mastodon/components/column_collapsable.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -export default class ColumnCollapsable extends React.PureComponent { - - static propTypes = { - icon: PropTypes.string.isRequired, - title: PropTypes.string, - fullHeight: PropTypes.number.isRequired, - children: PropTypes.node, - onCollapse: PropTypes.func, - }; - - state = { - collapsed: true, - animating: false, - }; - - handleToggleCollapsed = () => { - const currentState = this.state.collapsed; - - this.setState({ collapsed: !currentState, animating: true }); - - if (!currentState && this.props.onCollapse) { - this.props.onCollapse(); - } - } - - handleTransitionEnd = () => { - this.setState({ animating: false }); - } - - render () { - const { icon, title, fullHeight, children } = this.props; - const { collapsed, animating } = this.state; - - return ( - <div className={`column-collapsable ${collapsed ? 'collapsed' : ''}`} onTransitionEnd={this.handleTransitionEnd}> - <div role='button' tabIndex='0' title={`${title}`} className='column-collapsable__button column-icon' onClick={this.handleToggleCollapsed}> - <i className={`fa fa-${icon}`} /> - </div> - - <div className='column-collapsable__content' style={{ height: `${fullHeight}px` }}> - {(!collapsed || animating) && children} - </div> - </div> - ); - } - -} diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index a309f74e8..ec9379320 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -132,7 +132,7 @@ export default class ColumnHeader extends React.PureComponent { </div> <div className={collapsibleClassName} onTransitionEnd={this.handleTransitionEnd}> - <div> + <div className='column-header__collapsible-inner'> {(!collapsed || animating) && collapsedContent} </div> </div> diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index 78ff35130..2cb1ce51c 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -85,14 +85,24 @@ class Item extends React.PureComponent { let thumbnail = ''; if (attachment.get('type') === 'image') { + const previewUrl = attachment.get('preview_url'); + const previewWidth = attachment.getIn(['meta', 'small', 'width']); + + const originalUrl = attachment.get('url'); + const originalWidth = attachment.getIn(['meta', 'original', 'width']); + + const srcSet = `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w`; + const sizes = `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw`; + thumbnail = ( - <a // eslint-disable-line jsx-a11y/anchor-has-content + <a className='media-gallery__item-thumbnail' - href={attachment.get('remote_url') || attachment.get('url')} + href={attachment.get('remote_url') || originalUrl} onClick={this.handleClick} target='_blank' - style={{ backgroundImage: `url(${attachment.get('preview_url')})` }} - /> + > + <img src={previewUrl} srcSet={srcSet} sizes={sizes} alt='' /> + </a> ); } else if (attachment.get('type') === 'gifv') { const autoPlay = !isIOS() && this.props.autoPlayGif; diff --git a/app/javascript/mastodon/components/permalink.js b/app/javascript/mastodon/components/permalink.js index 5d3e4738d..0b7d0a65a 100644 --- a/app/javascript/mastodon/components/permalink.js +++ b/app/javascript/mastodon/components/permalink.js @@ -25,7 +25,7 @@ export default class Permalink extends React.PureComponent { const { href, children, className, ...other } = this.props; return ( - <a href={href} onClick={this.handleClick} {...other} className={'permalink ' + className}> + <a href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}> {children} </a> ); diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 0077a9191..9e9e1c3c7 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -149,7 +149,7 @@ class StatusUnextended extends ImmutablePureComponent { saveHeight = () => { if (this.node && this.node.children.length !== 0) { - this.height = this.node.clientHeight; + this.height = this.node.getBoundingClientRect().height; } } diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index a1e1a135a..6693548c7 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -88,7 +88,6 @@ export default class StatusActionBar extends ImmutablePureComponent { handleReport = () => { this.props.onReport(this.props.status); - this.context.router.history.push('/report'); } handleConversationMuteClick = () => { diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js index 7f80e39e8..167a2097e 100644 --- a/app/javascript/mastodon/features/account_timeline/components/header.js +++ b/app/javascript/mastodon/features/account_timeline/components/header.js @@ -38,7 +38,6 @@ export default class Header extends ImmutablePureComponent { handleReport = () => { this.props.onReport(this.props.account); - this.context.router.history.push('/report'); } handleMute = () => { diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index c379c1855..f7eeedc69 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -67,6 +67,12 @@ export default class ComposeForm extends ImmutablePureComponent { } handleSubmit = () => { + if (this.props.text !== this.autosuggestTextarea.textarea.value) { + // Something changed the text inside the textarea (e.g. browser extensions like Grammarly) + // Update the state to match the current text + this.props.onChange(this.autosuggestTextarea.textarea.value); + } + this.props.onSubmit(); } diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js index 137e55089..8cef6a1e4 100644 --- a/app/javascript/mastodon/features/favourited_statuses/index.js +++ b/app/javascript/mastodon/features/favourited_statuses/index.js @@ -1,6 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import LoadingIndicator from '../../components/loading_indicator'; import { fetchFavouritedStatuses, expandFavouritedStatuses } from '../../actions/favourites'; import Column from '../ui/components/column'; @@ -14,7 +15,9 @@ const messages = defineMessages({ }); const mapStateToProps = state => ({ + statusIds: state.getIn(['status_lists', 'favourites', 'items']), loaded: state.getIn(['status_lists', 'favourites', 'loaded']), + me: state.getIn(['meta', 'me']), }); @connect(mapStateToProps) @@ -23,8 +26,10 @@ export default class Favourites extends ImmutablePureComponent { static propTypes = { dispatch: PropTypes.func.isRequired, + statusIds: ImmutablePropTypes.list.isRequired, loaded: PropTypes.bool, intl: PropTypes.object.isRequired, + me: PropTypes.number.isRequired, }; componentWillMount () { diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 1dd1b9a71..ed4b3ad98 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -13,6 +13,7 @@ import ColumnSettingsContainer from './containers/column_settings_container'; import { createSelector } from 'reselect'; import Immutable from 'immutable'; import LoadMore from '../../components/load_more'; +import { debounce } from 'lodash'; const messages = defineMessages({ title: { id: 'column.notifications', defaultMessage: 'Notifications' }, @@ -50,19 +51,27 @@ export default class Notifications extends React.PureComponent { trackScroll: true, }; + dispatchExpandNotifications = debounce(() => { + this.props.dispatch(expandNotifications()); + }, 300, { leading: true }); + + dispatchScrollToTop = debounce((top) => { + this.props.dispatch(scrollTopNotifications(top)); + }, 100); + handleScroll = (e) => { const { scrollTop, scrollHeight, clientHeight } = e.target; const offset = scrollHeight - scrollTop - clientHeight; this._oldScrollPosition = scrollHeight - scrollTop; - if (250 > offset && !this.props.isLoading) { - if (this.props.hasMore) { - this.props.dispatch(expandNotifications()); - } - } else if (scrollTop < 100) { - this.props.dispatch(scrollTopNotifications(true)); + if (250 > offset && this.props.hasMore && !this.props.isLoading) { + this.dispatchExpandNotifications(); + } + + if (scrollTop < 100) { + this.dispatchScrollToTop(true); } else { - this.props.dispatch(scrollTopNotifications(false)); + this.dispatchScrollToTop(false); } } @@ -74,7 +83,7 @@ export default class Notifications extends React.PureComponent { handleLoadMore = (e) => { e.preventDefault(); - this.props.dispatch(expandNotifications()); + this.dispatchExpandNotifications(); } handlePin = () => { diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index 29080529d..5e150842e 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -56,7 +56,6 @@ export default class ActionBar extends React.PureComponent { handleReport = () => { this.props.onReport(this.props.status); - this.context.router.history.push('/report'); } render () { diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.js index 5c3879970..52c3a898b 100644 --- a/app/javascript/mastodon/features/ui/components/image_loader.js +++ b/app/javascript/mastodon/features/ui/components/image_loader.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; export default class ImageLoader extends React.PureComponent { @@ -20,46 +21,121 @@ export default class ImageLoader extends React.PureComponent { error: false, } - componentWillMount() { - this._loadImage(this.props.src); + removers = []; + + get canvasContext() { + if (!this.canvas) { + return null; + } + this._canvasContext = this._canvasContext || this.canvas.getContext('2d'); + return this._canvasContext; + } + + componentDidMount () { + this.loadImage(this.props); + } + + componentWillReceiveProps (nextProps) { + if (this.props.src !== nextProps.src) { + this.loadImage(nextProps); + } } - componentWillReceiveProps(props) { - this._loadImage(props.src); + loadImage (props) { + this.removeEventListeners(); + this.setState({ loading: true, error: false }); + Promise.all([ + this.loadPreviewCanvas(props), + this.loadOriginalImage(props), + ]) + .then(() => { + this.setState({ loading: false, error: false }); + this.clearPreviewCanvas(); + }) + .catch(() => this.setState({ loading: false, error: true })); } - _loadImage(src) { + loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => { const image = new Image(); + const removeEventListeners = () => { + image.removeEventListener('error', handleError); + image.removeEventListener('load', handleLoad); + }; + const handleError = () => { + removeEventListeners(); + reject(); + }; + const handleLoad = () => { + removeEventListeners(); + this.canvasContext.drawImage(image, 0, 0, width, height); + resolve(); + }; + image.addEventListener('error', handleError); + image.addEventListener('load', handleLoad); + image.src = previewSrc; + this.removers.push(removeEventListeners); + }) - image.onerror = () => this.setState({ loading: false, error: true }); - image.onload = () => this.setState({ loading: false, error: false }); + clearPreviewCanvas () { + const { width, height } = this.canvas; + this.canvasContext.clearRect(0, 0, width, height); + } + loadOriginalImage = ({ src }) => new Promise((resolve, reject) => { + const image = new Image(); + const removeEventListeners = () => { + image.removeEventListener('error', handleError); + image.removeEventListener('load', handleLoad); + }; + const handleError = () => { + removeEventListeners(); + reject(); + }; + const handleLoad = () => { + removeEventListeners(); + resolve(); + }; + image.addEventListener('error', handleError); + image.addEventListener('load', handleLoad); image.src = src; + this.removers.push(removeEventListeners); + }); - this.setState({ loading: true }); + removeEventListeners () { + this.removers.forEach(listeners => listeners()); + this.removers = []; } - render() { - const { alt, src, previewSrc, width, height } = this.props; + setCanvasRef = c => { + this.canvas = c; + } + + render () { + const { alt, src, width, height } = this.props; const { loading } = this.state; + const className = classNames('image-loader', { + 'image-loader--loading': loading, + }); + return ( - <div className='image-loader'> - <img - alt={alt} - className='image-loader__img' - src={src} + <div className={className}> + <canvas + className='image-loader__preview-canvas' width={width} height={height} + ref={this.setCanvasRef} /> - {loading && + {!loading && ( <img - alt='' - src={previewSrc} - className='image-loader__preview-img' + alt={alt} + className='image-loader__img' + src={src} + width={width} + height={height} /> - } + )} </div> ); } diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js index 2e4f9876d..48b048eb7 100644 --- a/app/javascript/mastodon/features/ui/components/modal_root.js +++ b/app/javascript/mastodon/features/ui/components/modal_root.js @@ -5,6 +5,7 @@ import OnboardingModal from './onboarding_modal'; import VideoModal from './video_modal'; import BoostModal from './boost_modal'; import ConfirmationModal from './confirmation_modal'; +import ReportModal from './report_modal'; import TransitionMotion from 'react-motion/lib/TransitionMotion'; import spring from 'react-motion/lib/spring'; @@ -14,6 +15,7 @@ const MODAL_COMPONENTS = { 'VIDEO': VideoModal, 'BOOST': BoostModal, 'CONFIRM': ConfirmationModal, + 'REPORT': ReportModal, }; export default class ModalRoot extends React.PureComponent { diff --git a/app/javascript/mastodon/features/ui/components/onboarding_modal.js b/app/javascript/mastodon/features/ui/components/onboarding_modal.js index 4c1c0f7c1..dab5e47ea 100644 --- a/app/javascript/mastodon/features/ui/components/onboarding_modal.js +++ b/app/javascript/mastodon/features/ui/components/onboarding_modal.js @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import ReactSwipeable from 'react-swipeable'; import classNames from 'classnames'; import Permalink from '../../../components/permalink'; import TransitionMotion from 'react-motion/lib/TransitionMotion'; @@ -274,7 +275,7 @@ export default class OnboardingModal extends React.PureComponent { <div className='modal-root__modal onboarding-modal'> <TransitionMotion styles={styles}> {interpolatedStyles => ( - <div className='onboarding-modal__pager'> + <ReactSwipeable onSwipedRight={this.handlePrev} onSwipedLeft={this.handleNext} className='onboarding-modal__pager'> {interpolatedStyles.map(({ key, data, style }, i) => { const className = classNames('onboarding-modal__page__wrapper', { 'onboarding-modal__page__wrapper--active': i === currentIndex, @@ -283,7 +284,7 @@ export default class OnboardingModal extends React.PureComponent { <div key={key} style={style} className={className}>{data}</div> ); })} - </div> + </ReactSwipeable> )} </TransitionMotion> diff --git a/app/javascript/mastodon/features/report/index.js b/app/javascript/mastodon/features/ui/components/report_modal.js index bfb09e193..c989d2c9b 100644 --- a/app/javascript/mastodon/features/report/index.js +++ b/app/javascript/mastodon/features/ui/components/report_modal.js @@ -1,19 +1,17 @@ import React from 'react'; import { connect } from 'react-redux'; -import { changeReportComment, submitReport } from '../../actions/reports'; -import { refreshAccountTimeline } from '../../actions/timelines'; +import { changeReportComment, submitReport } from '../../../actions/reports'; +import { refreshAccountTimeline } from '../../../actions/timelines'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import Column from '../ui/components/column'; -import Button from '../../components/button'; -import { makeGetAccount } from '../../selectors'; +import { makeGetAccount } from '../../../selectors'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; -import StatusCheckBox from './containers/status_check_box_container'; +import StatusCheckBox from '../../report/containers/status_check_box_container'; import Immutable from 'immutable'; -import ColumnBackButtonSlim from '../../components/column_back_button_slim'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import Button from '../../../components/button'; const messages = defineMessages({ - heading: { id: 'report.heading', defaultMessage: 'New report' }, placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' }, submit: { id: 'report.submit', defaultMessage: 'Submit' }, }); @@ -37,11 +35,7 @@ const makeMapStateToProps = () => { @connect(makeMapStateToProps) @injectIntl -export default class Report extends React.PureComponent { - - static contextTypes = { - router: PropTypes.object, - }; +export default class ReportModal extends ImmutablePureComponent { static propTypes = { isSubmitting: PropTypes.bool, @@ -52,17 +46,15 @@ export default class Report extends React.PureComponent { intl: PropTypes.object.isRequired, }; - componentWillMount () { - if (!this.props.account) { - this.context.router.history.replace('/'); - } + handleCommentChange = (e) => { + this.props.dispatch(changeReportComment(e.target.value)); } - componentDidMount () { - if (!this.props.account) { - return; - } + handleSubmit = () => { + this.props.dispatch(submitReport()); + } + componentDidMount () { this.props.dispatch(refreshAccountTimeline(this.props.account.get('id'))); } @@ -72,15 +64,6 @@ export default class Report extends React.PureComponent { } } - handleCommentChange = (e) => { - this.props.dispatch(changeReportComment(e.target.value)); - } - - handleSubmit = () => { - this.props.dispatch(submitReport()); - this.context.router.history.replace('/'); - } - render () { const { account, comment, intl, statusIds, isSubmitting } = this.props; @@ -89,36 +72,33 @@ export default class Report extends React.PureComponent { } return ( - <Column heading={intl.formatMessage(messages.heading)} icon='flag'> - <ColumnBackButtonSlim /> - - <div className='report scrollable'> - <div className='report__target'> - <FormattedMessage id='report.target' defaultMessage='Reporting' /> - <strong>{account.get('acct')}</strong> - </div> + <div className='modal-root__modal report-modal'> + <div className='report-modal__target'> + <FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} /> + </div> - <div className='scrollable report__statuses'> + <div className='report-modal__container'> + <div className='report-modal__statuses'> <div> {statusIds.map(statusId => <StatusCheckBox id={statusId} key={statusId} disabled={isSubmitting} />)} </div> </div> - <div className='report__textarea-wrapper'> + <div className='report-modal__comment'> <textarea - className='report__textarea' + className='setting-text light' placeholder={intl.formatMessage(messages.placeholder)} value={comment} onChange={this.handleCommentChange} disabled={isSubmitting} /> - - <div className='report__submit'> - <div className='report__submit-button'><Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} /></div> - </div> </div> </div> - </Column> + + <div className='report-modal__action-bar'> + <Button disabled={isSubmitting} text={intl.formatMessage(messages.submit)} onClick={this.handleSubmit} /> + </div> + </div> ); } diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index e5915ffe0..4d38c2677 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -15,7 +15,6 @@ import { refreshHomeTimeline } from '../../actions/timelines'; import { refreshNotifications } from '../../actions/notifications'; import UploadArea from './components/upload_area'; import ColumnsAreaContainer from './containers/columns_area_container'; - import Status from '../../features/status'; import GettingStarted from '../../features/getting_started'; import PublicTimeline from '../../features/public_timeline'; @@ -35,7 +34,6 @@ import GenericNotFound from '../../features/generic_not_found'; import FavouritedStatuses from '../../features/favourited_statuses'; import Blocks from '../../features/blocks'; import Mutes from '../../features/mutes'; -import Report from '../../features/report'; // Small wrapper to pass multiColumn to the route components const WrappedSwitch = ({ multiColumn, children }) => ( @@ -222,7 +220,6 @@ export default class UI extends React.PureComponent { <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} /> <WrappedRoute path='/blocks' component={Blocks} content={children} /> <WrappedRoute path='/mutes' component={Mutes} content={children} /> - <WrappedRoute path='/report' component={Report} content={children} /> <WrappedRoute component={GenericNotFound} content={children} /> </WrappedSwitch> diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 45353e9a3..5ab914477 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -1154,6 +1154,23 @@ { "descriptors": [ { + "defaultMessage": "Additional comments", + "id": "report.placeholder" + }, + { + "defaultMessage": "Submit", + "id": "report.submit" + }, + { + "defaultMessage": "Report {target}", + "id": "report.target" + } + ], + "path": "app/javascript/mastodon/features/ui/components/report_modal.json" + }, + { + "descriptors": [ + { "defaultMessage": "Compose", "id": "tabs_bar.compose" }, diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 3b2d198b1..d0c0ca137 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -140,10 +140,10 @@ "privacy.unlisted.long": "Do not post to public timelines", "privacy.unlisted.short": "Unlisted", "reply_indicator.cancel": "Cancel", - "report.heading": "New report", + "report.heading": "Report {target}", "report.placeholder": "Additional comments", "report.submit": "Submit", - "report.target": "Reporting", + "report.target": "Reporting {target}", "search.placeholder": "Search", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "status.cannot_reblog": "This post cannot be boosted", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 52d6b0f87..1a69235c8 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -27,8 +27,8 @@ "column.notifications": "Notifications", "column.public": "Fil public global", "column_back_button.label": "Retour", - "column_header.pin": "Pin", - "column_header.unpin": "Unpin", + "column_header.pin": "Épingler", + "column_header.unpin": "Retirer", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Paramètres", "compose_form.lock_disclaimer": "Votre compte n'est pas {locked}. Tout le monde peut vous suivre et voir vos pouets restreints.", @@ -101,7 +101,7 @@ "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?", "notifications.column_settings.alert": "Notifications locales", "notifications.column_settings.favourite": "Favoris :", - "notifications.column_settings.follow": "Nouveaux abonné⋅e⋅s :", + "notifications.column_settings.follow": "Nouveaux⋅elles abonn⋅é⋅s :", "notifications.column_settings.mention": "Mentions :", "notifications.column_settings.reblog": "Partages :", "notifications.column_settings.show": "Afficher dans la colonne", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 6dcb872df..bf425501f 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -136,10 +136,10 @@ "privacy.unlisted.long": "Niewidoczne na publicznych osiach czasu", "privacy.unlisted.short": "Niewidoczne", "reply_indicator.cancel": "Anuluj", - "report.heading": "Nowe zgłoszenie", + "report.heading": "Zgłoś {target}", "report.placeholder": "Dodatkowe komentarze", "report.submit": "Wyślij", - "report.target": "Zgłaszanie", + "report.target": "Zgłaszanie {target}", "search.placeholder": "Szukaj", "search_results.total": "{count, number} {count, plural, one {wynik} more {wyniki}}", "status.cannot_reblog": "Ten post nie może zostać podbity", diff --git a/app/javascript/styles/admin.scss b/app/javascript/styles/admin.scss index c2bfc10a0..3bc713566 100644 --- a/app/javascript/styles/admin.scss +++ b/app/javascript/styles/admin.scss @@ -129,6 +129,11 @@ color: $ui-primary-color; } } + + .positive-hint { + color: $valid-value-color; + font-weight: 500; + } } .simple_form { diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 85566a653..a7c982cb2 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -58,37 +58,6 @@ position: relative; } -.column-collapsable { - position: relative; - - .column-collapsable__content { - overflow: auto; - transition: 300ms ease; - opacity: 1; - max-height: 70vh; - } - - &.collapsed .column-collapsable__content { - height: 0 !important; - opacity: 0; - } - - .column-collapsable__button { - color: $primary-text-color; - background: lighten($ui-base-color, 8%); - - &:hover { - color: $primary-text-color; - background: lighten($ui-base-color, 8%); - } - } - - &.collapsed .column-collapsable__button { - color: $ui-primary-color; - background: lighten($ui-base-color, 4%); - } -} - .column-icon { background: lighten($ui-base-color, 4%); color: $ui-primary-color; @@ -670,13 +639,15 @@ } .status-check-box { - border-bottom: 1px solid lighten($ui-base-color, 8%); + border-bottom: 1px solid $ui-secondary-color; display: flex; .status__content { - background: lighten($ui-base-color, 4%); flex: 1 1 auto; padding: 10px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } } @@ -1233,20 +1204,22 @@ .image-loader { position: relative; -} -.image-loader__preview-img { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - filter: blur(2px); -} + &.image-loader--loading { + .image-loader__preview-canvas { + filter: blur(2px); + } + } -.media-modal img.image-loader__preview-img { - width: 100%; - height: 100%; + .image-loader__img { + position: absolute; + top: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + background-image: none; + } } .navigation-bar { @@ -1980,6 +1953,17 @@ @include limited-single-column('screen and (max-width: 600px)') { font-size: 16px; } + + &.light { + color: $ui-base-color; + border-bottom: 2px solid lighten($ui-base-color, 27%); + + &:focus, + &:active { + color: $ui-base-color; + border-bottom-color: $ui-highlight-color; + } + } } @import 'boost'; @@ -2231,11 +2215,6 @@ button.icon-button.active i.fa-retweet { transition: max-height 150ms ease-in-out, opacity 300ms linear; opacity: 1; - & > div { - background: lighten($ui-base-color, 8%); - padding: 15px; - } - &.collapsed { max-height: 0; opacity: 0.5; @@ -2246,6 +2225,11 @@ button.icon-button.active i.fa-retweet { } } +.column-header__collapsible-inner { + background: lighten($ui-base-color, 8%); + padding: 15px; +} + .column-header__setting-btn { &:hover { color: lighten($ui-primary-color, 4%); @@ -2437,67 +2421,6 @@ button.icon-button.active i.fa-retweet { vertical-align: middle; } -.report.scrollable { - box-sizing: border-box; - display: flex; - flex-direction: column; - max-height: 100%; -} - -.report__target { - border-bottom: 1px solid lighten($ui-base-color, 4%); - color: $ui-secondary-color; - flex: 0 0 auto; - padding: 10px; - - strong { - display: block; - color: $primary-text-color; - font-weight: 500; - } -} - -.report__statuses { - flex: 1 1 auto; -} - -.report__textarea-wrapper { - flex: 0 0 100px; - padding: 10px; -} - -.report__textarea { - background: transparent; - box-sizing: border-box; - border: 0; - border-bottom: 2px solid $ui-primary-color; - border-radius: 2px 2px 0 0; - color: $primary-text-color; - display: block; - font-family: inherit; - font-size: 14px; - margin-bottom: 10px; - outline: 0; - padding: 7px 4px; - resize: vertical; - width: 100%; - - &:active, - &:focus { - border-bottom-color: $ui-highlight-color; - background: rgba($base-overlay-background, 0.1); - } -} - -.report__submit { - margin-top: 10px; - overflow: hidden; -} - -.report__submit-button { - float: right; -} - .empty-column-indicator { color: lighten($ui-base-color, 20%); background: $ui-base-color; @@ -3086,6 +3009,7 @@ button.icon-button.active i.fa-retweet { position: relative; img, + canvas, video { max-width: 80vw; max-height: 80vh; @@ -3093,7 +3017,8 @@ button.icon-button.active i.fa-retweet { height: auto; } - img { + img, + canvas { display: block; background: url('../images/void.png') repeat; } @@ -3279,6 +3204,7 @@ button.icon-button.active i.fa-retweet { @media screen and (max-width: 400px) { .onboarding-modal__page-one { flex-direction: column; + align-items: normal; } .onboarding-modal__page-one__elephant-friend { @@ -3393,7 +3319,8 @@ button.icon-button.active i.fa-retweet { } .boost-modal, -.confirmation-modal { +.confirmation-modal, +.report-modal { background: lighten($ui-secondary-color, 8%); color: $ui-base-color; border-radius: 8px; @@ -3429,7 +3356,8 @@ button.icon-button.active i.fa-retweet { } .boost-modal__action-bar, -.confirmation-modal__action-bar { +.confirmation-modal__action-bar, +.report-modal__action-bar { display: flex; justify-content: space-between; background: $ui-secondary-color; @@ -3465,6 +3393,23 @@ button.icon-button.active i.fa-retweet { } } +.report-modal__statuses, +.report-modal__comment { + padding: 10px; +} + +.report-modal__statuses { + min-height: 20vh; + overflow-y: auto; + overflow-x: hidden; +} + +.report-modal__comment { + .setting-text { + margin-top: 10px; + } +} + .confirmation-modal__action-bar { .confirmation-modal__cancel-button { background-color: transparent; @@ -3480,7 +3425,8 @@ button.icon-button.active i.fa-retweet { } } -.confirmation-modal__container { +.confirmation-modal__container, +.report-modal__target { padding: 30px; font-size: 16px; text-align: center; @@ -3601,10 +3547,15 @@ button.icon-button.active i.fa-retweet { background-repeat: no-repeat; background-size: cover; cursor: zoom-in; - display: block; - height: 100%; + display: flex; + align-items: center; text-decoration: none; - width: 100%; + height: 100%; + + &, + img { + width: 100%; + } } .media-gallery__gifv { diff --git a/app/javascript/styles/forms.scss b/app/javascript/styles/forms.scss index 059c4a7d8..7a181f36b 100644 --- a/app/javascript/styles/forms.scss +++ b/app/javascript/styles/forms.scss @@ -358,7 +358,6 @@ code { } .user_filtered_languages { - & > label { font-family: inherit; font-size: 16px; diff --git a/app/javascript/styles/lists.scss b/app/javascript/styles/lists.scss index 47805663f..6019cd800 100644 --- a/app/javascript/styles/lists.scss +++ b/app/javascript/styles/lists.scss @@ -10,7 +10,6 @@ .recovery-codes { list-style: none; margin: 0 auto; - text-align: center; li { font-size: 125%; diff --git a/app/javascript/styles/tables.scss b/app/javascript/styles/tables.scss index f7def8cf3..6e54c59c0 100644 --- a/app/javascript/styles/tables.scss +++ b/app/javascript/styles/tables.scss @@ -42,6 +42,18 @@ strong { font-weight: 500; } + + &.inline-table { + td, + th { + padding: 8px 0; + } + + & > tbody > tr:nth-child(odd) > td, + & > tbody > tr:nth-child(odd) > th { + background: transparent; + } + } } samp { |