diff options
Diffstat (limited to 'app/javascript/flavours')
11 files changed, 272 insertions, 88 deletions
diff --git a/app/javascript/flavours/glitch/components/admin/Counter.js b/app/javascript/flavours/glitch/components/admin/Counter.js index 2bc9ce482..ecb242950 100644 --- a/app/javascript/flavours/glitch/components/admin/Counter.js +++ b/app/javascript/flavours/glitch/components/admin/Counter.js @@ -68,12 +68,12 @@ export default class Counter extends React.PureComponent { ); } else { const measure = data[0]; - const percentChange = percIncrease(measure.previous_total * 1, measure.total * 1); + const percentChange = measure.previous_total && percIncrease(measure.previous_total * 1, measure.total * 1); content = ( <React.Fragment> - <span className='sparkline__value__total'><FormattedNumber value={measure.total} /></span> - <span className={classNames('sparkline__value__change', { positive: percentChange > 0, negative: percentChange < 0 })}>{percentChange > 0 && '+'}<FormattedNumber value={percentChange} style='percent' /></span> + <span className='sparkline__value__total'>{measure.human_value || <FormattedNumber value={measure.total} />}</span> + {measure.previous_total && (<span className={classNames('sparkline__value__change', { positive: percentChange > 0, negative: percentChange < 0 })}>{percentChange > 0 && '+'}<FormattedNumber value={percentChange} style='percent' /></span>)} </React.Fragment> ); } diff --git a/app/javascript/flavours/glitch/components/media_attachments.js b/app/javascript/flavours/glitch/components/media_attachments.js new file mode 100644 index 000000000..c8d133f09 --- /dev/null +++ b/app/javascript/flavours/glitch/components/media_attachments.js @@ -0,0 +1,119 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { MediaGallery, Video, Audio } from 'flavours/glitch/util/async-components'; +import Bundle from 'flavours/glitch/features/ui/components/bundle'; +import noop from 'lodash/noop'; + +export default class MediaAttachments extends ImmutablePureComponent { + + static propTypes = { + status: ImmutablePropTypes.map.isRequired, + height: PropTypes.number, + width: PropTypes.number, + revealed: PropTypes.bool, + }; + + static defaultProps = { + height: 110, + width: 239, + }; + + updateOnProps = [ + 'status', + ]; + + renderLoadingMediaGallery = () => { + const { height, width } = this.props; + + return ( + <div className='media-gallery' style={{ height, width }} /> + ); + } + + renderLoadingVideoPlayer = () => { + const { height, width } = this.props; + + return ( + <div className='video-player' style={{ height, width }} /> + ); + } + + renderLoadingAudioPlayer = () => { + const { height, width } = this.props; + + return ( + <div className='audio-player' style={{ height, width }} /> + ); + } + + render () { + const { status, width, height, revealed } = this.props; + const mediaAttachments = status.get('media_attachments'); + + if (mediaAttachments.size === 0) { + return null; + } + + if (mediaAttachments.getIn([0, 'type']) === 'audio') { + const audio = mediaAttachments.get(0); + + return ( + <Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} > + {Component => ( + <Component + src={audio.get('url')} + alt={audio.get('description')} + width={width} + height={height} + poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])} + backgroundColor={audio.getIn(['meta', 'colors', 'background'])} + foregroundColor={audio.getIn(['meta', 'colors', 'foreground'])} + accentColor={audio.getIn(['meta', 'colors', 'accent'])} + duration={audio.getIn(['meta', 'original', 'duration'], 0)} + /> + )} + </Bundle> + ); + } else if (mediaAttachments.getIn([0, 'type']) === 'video') { + const video = mediaAttachments.get(0); + + return ( + <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > + {Component => ( + <Component + preview={video.get('preview_url')} + frameRate={video.getIn(['meta', 'original', 'frame_rate'])} + blurhash={video.get('blurhash')} + src={video.get('url')} + alt={video.get('description')} + width={width} + height={height} + inline + sensitive={status.get('sensitive')} + revealed={revealed} + onOpenVideo={noop} + /> + )} + </Bundle> + ); + } else { + return ( + <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} > + {Component => ( + <Component + media={mediaAttachments} + sensitive={status.get('sensitive')} + defaultWidth={width} + revealed={revealed} + height={height} + onOpenMedia={noop} + /> + )} + </Bundle> + ); + } + } + +} diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js b/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js index 142118cef..ede8907e5 100644 --- a/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js +++ b/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js @@ -40,7 +40,17 @@ class ColumnSettings extends React.PureComponent { } }; - onSelect = mode => value => this.props.onChange(['tags', mode], value); + onSelect = mode => value => { + const oldValue = this.tags(mode); + + // Prevent changes that add more than 4 tags, but allow removing + // tags that were already added before + if ((value.length > 4) && !(value < oldValue)) { + return; + } + + this.props.onChange(['tags', mode], value); + }; onToggle = () => { if (this.state.open && this.hasTags()) { diff --git a/app/javascript/flavours/glitch/features/report/components/status_check_box.js b/app/javascript/flavours/glitch/features/report/components/status_check_box.js index adb5e77a7..76bf0eb85 100644 --- a/app/javascript/flavours/glitch/features/report/components/status_check_box.js +++ b/app/javascript/flavours/glitch/features/report/components/status_check_box.js @@ -1,14 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import noop from 'lodash/noop'; import StatusContent from 'flavours/glitch/components/status_content'; -import { MediaGallery, Video } from 'flavours/glitch/util/async-components'; -import Bundle from 'flavours/glitch/features/ui/components/bundle'; import Avatar from 'flavours/glitch/components/avatar'; import DisplayName from 'flavours/glitch/components/display_name'; import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp'; import Option from './option'; +import MediaAttachments from 'flavours/glitch/components/media_attachments'; export default class StatusCheckBox extends React.PureComponent { @@ -27,53 +25,10 @@ export default class StatusCheckBox extends React.PureComponent { render () { const { status, checked } = this.props; - let media = null; - if (status.get('reblog')) { return null; } - if (status.get('media_attachments').size > 0) { - if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) { - - } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { - const video = status.getIn(['media_attachments', 0]); - - media = ( - <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > - {Component => ( - <Component - preview={video.get('preview_url')} - blurhash={video.get('blurhash')} - src={video.get('url')} - alt={video.get('description')} - width={239} - height={110} - inline - sensitive={status.get('sensitive')} - revealed={false} - onOpenVideo={noop} - /> - )} - </Bundle> - ); - } else { - media = ( - <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} > - {Component => ( - <Component - media={status.get('media_attachments')} - sensitive={status.get('sensitive')} - revealed={false} - height={110} - onOpenMedia={noop} - /> - )} - </Bundle> - ); - } - } - const labelComponent = ( <div className='status-check-box__status poll__option__text'> <div className='detailed-status__display-name'> @@ -84,7 +39,7 @@ export default class StatusCheckBox extends React.PureComponent { <div><DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /></div> </div> - <StatusContent status={status} media={media} /> + <StatusContent status={status} media={<MediaAttachments status={status} revealed={false} />} /> </div> ); diff --git a/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.js b/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.js index 198443221..8fd528da0 100644 --- a/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.js @@ -9,6 +9,7 @@ import escapeTextContentForBrowser from 'escape-html'; import InlineAccount from 'flavours/glitch/components/inline_account'; import IconButton from 'flavours/glitch/components/icon_button'; import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp'; +import MediaAttachments from 'flavours/glitch/components/media_attachments'; const mapStateToProps = (state, { statusId }) => ({ versions: state.getIn(['history', statusId, 'items']), @@ -70,6 +71,25 @@ class CompareHistoryModal extends React.PureComponent { )} <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} /> + + {!!currentVersion.get('poll') && ( + <div className='poll'> + <ul> + {currentVersion.getIn(['poll', 'options']).map(option => ( + <li key={option.get('title')}> + <span className='poll__input disabled' /> + + <span + className='poll__option__text translate' + dangerouslySetInnerHTML={{ __html: emojify(escapeTextContentForBrowser(option.get('title')), emojiMap) }} + /> + </li> + ))} + </ul> + </div> + )} + + <MediaAttachments status={currentVersion} /> </div> </div> </div> diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss index 0873ac300..40cd899b3 100644 --- a/app/javascript/flavours/glitch/styles/admin.scss +++ b/app/javascript/flavours/glitch/styles/admin.scss @@ -367,6 +367,21 @@ body, } } + .positive-hint, + .negative-hint, + .neutral-hint { + a { + color: inherit; + text-decoration: underline; + + &:focus, + &:hover, + &:active { + text-decoration: none; + } + } + } + .positive-hint { color: $valid-value-color; font-weight: 500; @@ -1612,3 +1627,38 @@ a.sparkline { } } } + +.availability-indicator { + display: flex; + align-items: center; + margin-bottom: 30px; + font-size: 14px; + line-height: 21px; + + &__hint { + padding: 0 15px; + } + + &__graphic { + display: flex; + margin: 0 -2px; + + &__item { + display: block; + flex: 0 0 auto; + width: 4px; + height: 21px; + background: lighten($ui-base-color, 8%); + margin: 0 2px; + border-radius: 2px; + + &.positive { + background: $valid-value-color; + } + + &.negative { + background: $error-value-color; + } + } + } +} diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 7364eba91..016e31c63 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -1563,41 +1563,6 @@ button.icon-button.active i.fa-retweet { filter: none; } -.compare-history-modal { - .report-modal__target { - border-bottom: 1px solid $ui-secondary-color; - } - - &__container { - padding: 30px; - pointer-events: all; - } - - .status__content { - color: $inverted-text-color; - font-size: 19px; - line-height: 24px; - - .emojione { - width: 24px; - height: 24px; - margin: -1px 0 0; - } - - a { - color: $highlight-text-color; - } - - hr { - height: 0.25rem; - padding: 0; - background-color: $ui-secondary-color; - border: 0; - margin: 20px 0; - } - } -} - .loading-bar { background-color: $ui-highlight-color; height: 3px; diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss index ae1afc320..7e6918356 100644 --- a/app/javascript/flavours/glitch/styles/components/modal.scss +++ b/app/javascript/flavours/glitch/styles/components/modal.scss @@ -1041,6 +1041,47 @@ } } +.compare-history-modal { + .report-modal__target { + border-bottom: 1px solid $ui-secondary-color; + } + + &__container { + padding: 30px; + pointer-events: all; + } + + .status__content { + color: $inverted-text-color; + font-size: 19px; + line-height: 24px; + + .emojione { + width: 24px; + height: 24px; + margin: -1px 0 0; + } + + a { + color: $highlight-text-color; + } + + hr { + height: 0.25rem; + padding: 0; + background-color: $ui-secondary-color; + border: 0; + margin: 20px 0; + } + } + + .media-gallery, + .audio-player, + .video-player { + margin-top: 15px; + } +} + .embed-modal { width: auto; max-width: 80vw; diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index a49299af1..ad6c24ea6 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -538,7 +538,7 @@ .media-gallery, .audio-player, .video-player { - margin-top: 8px; + margin-top: 15px; max-width: 250px; } diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss index 8f5309f2b..87b83b563 100644 --- a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss +++ b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss @@ -229,15 +229,21 @@ .mute-modal, .block-modal, .report-modal, +.report-dialog-modal, .embed-modal, .error-modal, .onboarding-modal, .report-modal__comment .setting-text__wrapper, -.report-modal__comment .setting-text { +.report-modal__comment .setting-text, +.report-dialog-modal__textarea { background: $white; border: 1px solid lighten($ui-base-color, 8%); } +.report-dialog-modal .dialog-option .poll__input { + color: $white; +} + .report-modal__comment { border-right-color: lighten($ui-base-color, 8%); } diff --git a/app/javascript/flavours/glitch/styles/tables.scss b/app/javascript/flavours/glitch/styles/tables.scss index 8b5933b7b..598c67034 100644 --- a/app/javascript/flavours/glitch/styles/tables.scss +++ b/app/javascript/flavours/glitch/styles/tables.scss @@ -65,6 +65,24 @@ } } + &.horizontal-table { + border-collapse: collapse; + border-style: hidden; + + & > tbody > tr > th, + & > tbody > tr > td { + padding: 11px 10px; + background: transparent; + border: 1px solid lighten($ui-base-color, 8%); + color: $secondary-text-color; + } + + & > tbody > tr > th { + color: $darker-text-color; + font-weight: 600; + } + } + &.batch-table { & > thead > tr > th { background: $ui-base-color; |