diff options
Diffstat (limited to 'app/javascript/flavours')
21 files changed, 247 insertions, 30 deletions
diff --git a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js index bd3baa5c3..6d07ec48c 100644 --- a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js +++ b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js @@ -118,6 +118,7 @@ export default class MediaItem extends ImmutablePureComponent { ); } else if (['gifv', 'video'].indexOf(attachment.get('type')) !== -1) { const autoPlay = !isIOS() && autoPlayGif; + const label = attachment.get('type') === 'video' ? <Icon id='play' /> : 'GIF'; thumbnail = ( <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}> @@ -133,7 +134,8 @@ export default class MediaItem extends ImmutablePureComponent { loop muted /> - <span className='media-gallery__gifv__label'>GIF</span> + + <span className='media-gallery__gifv__label'>{label}</span> </div> ); } diff --git a/app/javascript/flavours/glitch/features/account_gallery/index.js b/app/javascript/flavours/glitch/features/account_gallery/index.js index 597196567..ff39764bb 100644 --- a/app/javascript/flavours/glitch/features/account_gallery/index.js +++ b/app/javascript/flavours/glitch/features/account_gallery/index.js @@ -111,8 +111,10 @@ class AccountGallery extends ImmutablePureComponent { } handleOpenMedia = attachment => { - if (['video', 'audio'].includes(attachment.get('type'))) { + if (attachment.get('type') === 'video') { this.props.dispatch(openModal('VIDEO', { media: attachment, status: attachment.get('status') })); + } else if (attachment.get('type') === 'audio') { + this.props.dispatch(openModal('AUDIO', { media: attachment, status: attachment.get('status') })); } else { const media = attachment.getIn(['status', 'media_attachments']); const index = media.findIndex(x => x.get('id') === attachment.get('id')); diff --git a/app/javascript/flavours/glitch/features/compose/components/search.js b/app/javascript/flavours/glitch/features/compose/components/search.js index 29d195ecd..12d839637 100644 --- a/app/javascript/flavours/glitch/features/compose/components/search.js +++ b/app/javascript/flavours/glitch/features/compose/components/search.js @@ -73,12 +73,17 @@ class Search extends React.PureComponent { onShow: PropTypes.func.isRequired, openInRoute: PropTypes.bool, intl: PropTypes.object.isRequired, + singleColumn: PropTypes.bool, }; state = { expanded: false, }; + setRef = c => { + this.searchForm = c; + } + handleChange = (e) => { const { onChange } = this.props; if (onChange) { @@ -103,10 +108,14 @@ class Search extends React.PureComponent { } handleFocus = () => { - const { onShow } = this.props; this.setState({ expanded: true }); - if (onShow) { - onShow(); + this.props.onShow(); + + if (this.searchForm && !this.props.singleColumn) { + const { left, right } = this.searchForm.getBoundingClientRect(); + if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) { + this.searchForm.scrollIntoView(); + } } } @@ -135,6 +144,7 @@ class Search extends React.PureComponent { <label> <span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span> <input + ref={this.setRef} className='search__input' type='text' placeholder={intl.formatMessage(messages.placeholder)} diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js index 17487b202..a80fa824b 100644 --- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js +++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js @@ -11,6 +11,7 @@ import Permalink from 'flavours/glitch/components/permalink'; import IconButton from 'flavours/glitch/components/icon_button'; import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp'; import { HotKeys } from 'react-hotkeys'; +import { autoPlayGif } from 'flavours/glitch/util/initial_state'; const messages = defineMessages({ more: { id: 'status.more', defaultMessage: 'More' }, @@ -37,6 +38,7 @@ class Conversation extends ImmutablePureComponent { onMoveUp: PropTypes.func, onMoveDown: PropTypes.func, markRead: PropTypes.func.isRequired, + delete: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, }; @@ -63,6 +65,43 @@ class Conversation extends ImmutablePureComponent { } } + _updateEmojis () { + const node = this.namesNode; + + if (!node || autoPlayGif) { + return; + } + + const emojis = node.querySelectorAll('.custom-emoji'); + + for (var i = 0; i < emojis.length; i++) { + let emoji = emojis[i]; + if (emoji.classList.contains('status-emoji')) { + continue; + } + emoji.classList.add('status-emoji'); + + emoji.addEventListener('mouseenter', this.handleEmojiMouseEnter, false); + emoji.addEventListener('mouseleave', this.handleEmojiMouseLeave, false); + } + } + + componentDidMount () { + this._updateEmojis(); + } + + componentDidUpdate () { + this._updateEmojis(); + } + + handleEmojiMouseEnter = ({ target }) => { + target.src = target.getAttribute('data-original'); + } + + handleEmojiMouseLeave = ({ target }) => { + target.src = target.getAttribute('data-static'); + } + handleClick = () => { if (!this.context.router) { return; @@ -111,6 +150,10 @@ class Conversation extends ImmutablePureComponent { this.setState({ isExpanded: value }); } + setNamesRef = (c) => { + this.namesNode = c; + } + render () { const { accounts, lastStatus, unread, intl } = this.props; const { isExpanded } = this.state; @@ -161,7 +204,7 @@ class Conversation extends ImmutablePureComponent { <RelativeTimestamp timestamp={lastStatus.get('created_at')} /> </div> - <div className='conversation__content__names'> + <div className='conversation__content__names' ref={this.setNamesRef}> <FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} /> </div> </div> diff --git a/app/javascript/flavours/glitch/features/ui/components/audio_modal.js b/app/javascript/flavours/glitch/features/ui/components/audio_modal.js new file mode 100644 index 000000000..08fbddc91 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/audio_modal.js @@ -0,0 +1,53 @@ +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import Audio from 'flavours/glitch/features/audio'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; +import Icon from 'flavours/glitch/components/icon'; + +export default class AudioModal extends ImmutablePureComponent { + + static propTypes = { + media: ImmutablePropTypes.map.isRequired, + status: ImmutablePropTypes.map, + onClose: PropTypes.func.isRequired, + }; + + static contextTypes = { + router: PropTypes.object, + }; + + handleStatusClick = e => { + if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { + e.preventDefault(); + this.context.router.history.push(`/statuses/${this.props.status.get('id')}`); + } + } + + render () { + const { media, status } = this.props; + + return ( + <div className='modal-root__modal audio-modal'> + <div className='audio-modal__container'> + <Audio + src={media.get('url')} + alt={media.get('description')} + duration={media.getIn(['meta', 'original', 'duration'], 0)} + height={135} + preload + /> + </div> + + {status && ( + <div className={classNames('media-modal__meta')}> + <a href={status.get('url')} onClick={this.handleStatusClick}><Icon id='comments' /> <FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> + </div> + )} + </div> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/components/embed_modal.js b/app/javascript/flavours/glitch/features/ui/components/embed_modal.js index 47c1c7925..b6f5e628d 100644 --- a/app/javascript/flavours/glitch/features/ui/components/embed_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/embed_modal.js @@ -1,8 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { FormattedMessage, injectIntl } from 'react-intl'; +import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; import api from 'flavours/glitch/util/api'; +import IconButton from 'flavours/glitch/components/icon_button'; + +const messages = defineMessages({ + close: { id: 'lightbox.close', defaultMessage: 'Close' }, +}); export default @injectIntl class EmbedModal extends ImmutablePureComponent { @@ -50,13 +55,17 @@ class EmbedModal extends ImmutablePureComponent { } render () { + const { intl, onClose } = this.props; const { oembed } = this.state; return ( - <div className='modal-root__modal embed-modal'> - <h4><FormattedMessage id='status.embed' defaultMessage='Embed' /></h4> + <div className='modal-root__modal report-modal embed-modal'> + <div className='report-modal__target'> + <IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} /> + <FormattedMessage id='status.embed' defaultMessage='Embed' /> + </div> - <div className='embed-modal__container'> + <div className='report-modal__container embed-modal__container' style={{ display: 'block' }}> <p className='hint'> <FormattedMessage id='embed.instructions' defaultMessage='Embed this status on your website by copying the code below.' /> </p> diff --git a/app/javascript/flavours/glitch/features/ui/components/media_modal.js b/app/javascript/flavours/glitch/features/ui/components/media_modal.js index 5d60c8a08..d61c69f69 100644 --- a/app/javascript/flavours/glitch/features/ui/components/media_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/media_modal.js @@ -208,7 +208,7 @@ class MediaModal extends ImmutablePureComponent { {status && ( <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}><Icon id='comments' /> <FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> </div> )} diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js index 0941ce9c8..117ce4c55 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -9,6 +9,7 @@ import MediaModal from './media_modal'; import VideoModal from './video_modal'; import BoostModal from './boost_modal'; import FavouriteModal from './favourite_modal'; +import AudioModal from './audio_modal'; import DoodleModal from './doodle_modal'; import ConfirmationModal from './confirmation_modal'; import FocalPointModal from './focal_point_modal'; @@ -28,6 +29,7 @@ const MODAL_COMPONENTS = { 'MEDIA': () => Promise.resolve({ default: MediaModal }), 'ONBOARDING': OnboardingModal, 'VIDEO': () => Promise.resolve({ default: VideoModal }), + 'AUDIO': () => Promise.resolve({ default: AudioModal }), 'BOOST': () => Promise.resolve({ default: BoostModal }), 'FAVOURITE': () => Promise.resolve({ default: FavouriteModal }), 'DOODLE': () => Promise.resolve({ default: DoodleModal }), diff --git a/app/javascript/flavours/glitch/features/ui/components/mute_modal.js b/app/javascript/flavours/glitch/features/ui/components/mute_modal.js index dec6413c3..2aab82751 100644 --- a/app/javascript/flavours/glitch/features/ui/components/mute_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/mute_modal.js @@ -82,7 +82,7 @@ class MuteModal extends React.PureComponent { <p className='mute-modal__explanation'> <FormattedMessage id='confirmations.mute.explanation' - defaultMessage='This will hide posts from them and posts mentioning them, but it will still allow them to see your posts follow you.' + defaultMessage='This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.' /> </p> <div className='setting-toggle'> diff --git a/app/javascript/flavours/glitch/features/ui/components/video_modal.js b/app/javascript/flavours/glitch/features/ui/components/video_modal.js index 3f742c260..ef69f60f4 100644 --- a/app/javascript/flavours/glitch/features/ui/components/video_modal.js +++ b/app/javascript/flavours/glitch/features/ui/components/video_modal.js @@ -4,6 +4,8 @@ import PropTypes from 'prop-types'; import Video from 'flavours/glitch/features/video'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; +import Icon from 'mastodon/components/icon'; export default class VideoModal extends ImmutablePureComponent { @@ -28,22 +30,25 @@ export default class VideoModal extends ImmutablePureComponent { render () { const { media, status, time, onClose } = this.props; - const link = status && <a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a>; - return ( <div className='modal-root__modal video-modal'> - <div> + <div className='video-modal__container'> <Video preview={media.get('preview_url')} blurhash={media.get('blurhash')} src={media.get('url')} startTime={time} onCloseVideo={onClose} - link={link} detailed alt={media.get('description')} /> </div> + + {status && ( + <div className={classNames('media-modal__meta')}> + <a href={status.get('url')} onClick={this.handleStatusClick}><Icon id='comments' /> <FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a> + </div> + )} </div> ); } diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.js index 6cc1b1cde..c201cd93d 100644 --- a/app/javascript/flavours/glitch/features/ui/index.js +++ b/app/javascript/flavours/glitch/features/ui/index.js @@ -320,7 +320,7 @@ class UI extends React.Component { } dataTransferIsText = (dataTransfer) => { - return (dataTransfer && Array.from(dataTransfer.types).includes('text/plain') && dataTransfer.items.length === 1); + return (dataTransfer && Array.from(dataTransfer.types).filter((type) => type === 'text/plain').length === 1); } closeUploadModal = () => { diff --git a/app/javascript/flavours/glitch/reducers/conversations.js b/app/javascript/flavours/glitch/reducers/conversations.js index 8fcc2cc79..fba0308bc 100644 --- a/app/javascript/flavours/glitch/reducers/conversations.js +++ b/app/javascript/flavours/glitch/reducers/conversations.js @@ -7,6 +7,7 @@ import { CONVERSATIONS_FETCH_FAIL, CONVERSATIONS_UPDATE, CONVERSATIONS_READ, + CONVERSATIONS_DELETE_SUCCESS, } from '../actions/conversations'; import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'flavours/glitch/actions/accounts'; import { DOMAIN_BLOCK_SUCCESS } from 'flavours/glitch/actions/domain_blocks'; @@ -107,6 +108,8 @@ export default function conversations(state = initialState, action) { return filterConversations(state, [action.relationship.id]); case DOMAIN_BLOCK_SUCCESS: return filterConversations(state, action.accounts); + case CONVERSATIONS_DELETE_SUCCESS: + return state.update('items', list => list.filterNot(item => item.get('id') === action.id)); default: return state; } diff --git a/app/javascript/flavours/glitch/styles/about.scss b/app/javascript/flavours/glitch/styles/about.scss index 7c129674d..a38ca99b4 100644 --- a/app/javascript/flavours/glitch/styles/about.scss +++ b/app/javascript/flavours/glitch/styles/about.scss @@ -145,8 +145,6 @@ $small-breakpoint: 960px; thead tr, tbody tr { - break-after: auto; - break-inside: avoid; border-bottom: 1px solid lighten($ui-base-color, 4%); font-size: 1em; line-height: 1.625; @@ -167,12 +165,25 @@ $small-breakpoint: 960px; padding: 8px; align-self: start; align-items: start; + word-break: break-all; &.nowrap { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; width: 25%; + position: relative; + + &::before { + content: ' '; + visibility: hidden; + } + + span { + position: absolute; + left: 8px; + right: 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } } } diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 848ef78df..6f0d4c0be 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -1454,7 +1454,6 @@ flex: 1 1 auto; padding: 10px 5px; padding-right: 15px; - word-break: break-all; overflow: hidden; &__info { @@ -1477,8 +1476,8 @@ overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px; - flex-basis: 170px; - flex-shrink: 1000; + flex-basis: 90px; + flex-grow: 1; a { color: $primary-text-color; @@ -1525,6 +1524,10 @@ noscript { text-decoration: none; } } + + a { + word-break: break-word; + } } } diff --git a/app/javascript/flavours/glitch/styles/components/media.scss b/app/javascript/flavours/glitch/styles/components/media.scss index 85982d938..366759847 100644 --- a/app/javascript/flavours/glitch/styles/components/media.scss +++ b/app/javascript/flavours/glitch/styles/components/media.scss @@ -179,10 +179,13 @@ position: absolute; } -.video-modal { +.video-modal__container { max-width: 100vw; max-height: 100vh; - position: relative; +} + +.audio-modal__container { + width: 50vw; } .media-modal { @@ -282,6 +285,7 @@ } a { + pointer-events: auto; text-decoration: none; font-weight: 500; color: $ui-secondary-color; @@ -339,6 +343,7 @@ background: darken($ui-base-color, 8%); border-radius: 4px; padding-bottom: 44px; + direction: ltr; &.editable { border-radius: 0; @@ -386,6 +391,7 @@ max-width: 100%; border-radius: 4px; box-sizing: border-box; + direction: ltr; &.editable { border-radius: 0; diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss index 4f3e5babf..716796af9 100644 --- a/app/javascript/flavours/glitch/styles/components/modal.scss +++ b/app/javascript/flavours/glitch/styles/components/modal.scss @@ -736,6 +736,7 @@ &:focus, &:active { color: darken($lighter-text-color, 4%); + background-color: transparent; } } @@ -804,6 +805,7 @@ } .embed-modal { + width: auto; max-width: 80vw; max-height: 80vh; @@ -834,6 +836,7 @@ font-size: 14px; margin: 0; margin-bottom: 15px; + border-radius: 4px; &::-moz-focus-inner { border: 0; @@ -859,6 +862,7 @@ max-width: 100%; overflow: hidden; border: 0; + border-radius: 4px; } } } diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss index ae89ac0a8..ded423afa 100644 --- a/app/javascript/flavours/glitch/styles/components/status.scss +++ b/app/javascript/flavours/glitch/styles/components/status.scss @@ -640,6 +640,10 @@ a.status__display-name, color: inherit; } +.detailed-status .button.logo-button { + margin-bottom: 15px; +} + .detailed-status__display-name { color: $secondary-text-color; display: block; diff --git a/app/javascript/flavours/glitch/styles/containers.scss b/app/javascript/flavours/glitch/styles/containers.scss index 6a48ff354..e2f291b69 100644 --- a/app/javascript/flavours/glitch/styles/containers.scss +++ b/app/javascript/flavours/glitch/styles/containers.scss @@ -414,6 +414,20 @@ } } + .directory__card { + border-radius: 4px; + + @media screen and (max-width: $no-gap-breakpoint) { + border-radius: 0; + } + } + + .page-header { + @media screen and (max-width: $no-gap-breakpoint) { + border-bottom: 0; + } + } + .public-account-header { overflow: hidden; margin-bottom: 10px; diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss index 5c7fa87da..3b4ffdf3c 100644 --- a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss +++ b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss @@ -230,8 +230,19 @@ .report-modal, .embed-modal, .error-modal, -.onboarding-modal { - background: $ui-base-color; +.onboarding-modal, +.report-modal__comment .setting-text__wrapper, +.report-modal__comment .setting-text { + background: $white; + border: 1px solid lighten($ui-base-color, 8%); +} + +.report-modal__comment { + border-right-color: lighten($ui-base-color, 8%); +} + +.report-modal__container { + border-top-color: lighten($ui-base-color, 8%); } .boost-modal__action-bar, diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/variables.scss b/app/javascript/flavours/glitch/styles/mastodon-light/variables.scss index 1b060b58d..312f5e314 100644 --- a/app/javascript/flavours/glitch/styles/mastodon-light/variables.scss +++ b/app/javascript/flavours/glitch/styles/mastodon-light/variables.scss @@ -18,6 +18,8 @@ $darker-text-color: $classic-base-color !default; $dark-text-color: #444b5d; $action-button-color: #606984; +$success-green: lighten(#3c754d, 8%); + $base-overlay-background: $white !default; $inverted-text-color: $black !default; diff --git a/app/javascript/flavours/glitch/styles/rtl.scss b/app/javascript/flavours/glitch/styles/rtl.scss index 8a275d82f..2375bac90 100644 --- a/app/javascript/flavours/glitch/styles/rtl.scss +++ b/app/javascript/flavours/glitch/styles/rtl.scss @@ -7,6 +7,34 @@ body.rtl { padding-right: 15px; } + .radio-button__input { + margin-right: 0; + margin-left: 10px; + } + + .directory__card__bar .display-name { + margin-left: 0; + margin-right: 15px; + } + + .display-name { + text-align: right; + } + + .notification__message { + margin-left: 0; + margin-right: 68px; + } + + .drawer__inner__mastodon > img { + transform: scaleX(-1); + } + + .notification__favourite-icon-wrapper { + left: auto; + right: -26px; + } + .landing-page__logo { margin-right: 0; margin-left: 20px; @@ -152,7 +180,6 @@ body.rtl { } .status__action-bar { - &__counter { margin-right: 0; margin-left: 11px; @@ -345,6 +372,12 @@ body.rtl { } } + .columns-area--mobile .column, + .columns-area--mobile .drawer { + padding-left: 0; + padding-right: 0; + } + .public-layout { .header { .nav-button { |