diff options
Diffstat (limited to 'app/javascript/flavours/glitch/components')
4 files changed, 72 insertions, 64 deletions
diff --git a/app/javascript/flavours/glitch/components/error_boundary.js b/app/javascript/flavours/glitch/components/error_boundary.js index 142a0c21a..dd21f2930 100644 --- a/app/javascript/flavours/glitch/components/error_boundary.js +++ b/app/javascript/flavours/glitch/components/error_boundary.js @@ -50,43 +50,43 @@ export default class ErrorBoundary extends React.PureComponent { <h1><FormattedMessage id='web_app_crash.title' defaultMessage="We're sorry, but something went wrong with the Mastodon app." /></h1> <p> <FormattedMessage id='web_app_crash.content' defaultMessage='You could try any of the following:' /> - <ul> - <li> - <FormattedMessage - id='web_app_crash.report_issue' - defaultMessage='Report a bug in the {issuetracker}' - values={{ issuetracker: <a href='https://github.com/glitch-soc/mastodon/issues' rel='noopener' target='_blank'><FormattedMessage id='web_app_crash.issue_tracker' defaultMessage='issue tracker' /></a> }} - /> - { debugInfo !== '' && ( - <details> - <summary><FormattedMessage id='web_app_crash.debug_info' defaultMessage='Debug information' /></summary> - <textarea - className='web_app_crash-stacktrace' - value={debugInfo} - rows='10' - readOnly - /> - </details> - )} - </li> + </p> + <ul> + <li> + <FormattedMessage + id='web_app_crash.report_issue' + defaultMessage='Report a bug in the {issuetracker}' + values={{ issuetracker: <a href='https://github.com/glitch-soc/mastodon/issues' rel='noopener' target='_blank'><FormattedMessage id='web_app_crash.issue_tracker' defaultMessage='issue tracker' /></a> }} + /> + { debugInfo !== '' && ( + <details> + <summary><FormattedMessage id='web_app_crash.debug_info' defaultMessage='Debug information' /></summary> + <textarea + className='web_app_crash-stacktrace' + value={debugInfo} + rows='10' + readOnly + /> + </details> + )} + </li> + <li> + <FormattedMessage + id='web_app_crash.reload_page' + defaultMessage='{reload} the current page' + values={{ reload: <a href='#' onClick={this.handleReload}><FormattedMessage id='web_app_crash.reload' defaultMessage='Reload' /></a> }} + /> + </li> + { preferencesLink !== undefined && ( <li> <FormattedMessage - id='web_app_crash.reload_page' - defaultMessage='{reload} the current page' - values={{ reload: <a href='#' onClick={this.handleReload}><FormattedMessage id='web_app_crash.reload' defaultMessage='Reload' /></a> }} + id='web_app_crash.change_your_settings' + defaultMessage='Change your {settings}' + values={{ settings: <a href={preferencesLink}><FormattedMessage id='web_app_crash.settings' defaultMessage='settings' /></a> }} /> </li> - { preferencesLink !== undefined && ( - <li> - <FormattedMessage - id='web_app_crash.change_your_settings' - defaultMessage='Change your {settings}' - values={{ settings: <a href={preferencesLink}><FormattedMessage id='web_app_crash.settings' defaultMessage='settings' /></a> }} - /> - </li> - )} - </ul> - </p> + )} + </ul> </div> </div> ); diff --git a/app/javascript/flavours/glitch/components/intersection_observer_article.js b/app/javascript/flavours/glitch/components/intersection_observer_article.js index 900c98638..03b3700df 100644 --- a/app/javascript/flavours/glitch/components/intersection_observer_article.js +++ b/app/javascript/flavours/glitch/components/intersection_observer_article.js @@ -1,10 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ImmutablePureComponent from 'react-immutable-pure-component'; import scheduleIdleTask from 'flavours/glitch/util/schedule_idle_task'; import getRectFromEntry from 'flavours/glitch/util/get_rect_from_entry'; -export default class IntersectionObserverArticle extends ImmutablePureComponent { +// Diff these props in the "unrendered" state +const updateOnPropsForUnrendered = ['id', 'index', 'listLength', 'cachedHeight']; + +export default class IntersectionObserverArticle extends React.Component { static propTypes = { intersectionObserverWrapper: PropTypes.object.isRequired, @@ -22,20 +24,21 @@ export default class IntersectionObserverArticle extends ImmutablePureComponent } shouldComponentUpdate (nextProps, nextState) { - if (!nextState.isIntersecting && nextState.isHidden) { - // It's only if we're not intersecting (i.e. offscreen) and isHidden is true - // that either "isIntersecting" or "isHidden" matter, and then they're - // the only things that matter (and updated ARIA attributes). - return this.state.isIntersecting || !this.state.isHidden || nextProps.listLength !== this.props.listLength; - } else if (nextState.isIntersecting && !this.state.isIntersecting) { - // If we're going from a non-intersecting state to an intersecting state, - // (i.e. offscreen to onscreen), then we definitely need to re-render + const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight); + const willBeUnrendered = !nextState.isIntersecting && (nextState.isHidden || nextProps.cachedHeight); + if (!!isUnrendered !== !!willBeUnrendered) { + // If we're going from rendered to unrendered (or vice versa) then update return true; } - // Otherwise, diff based on "updateOnProps" and "updateOnStates" - return super.shouldComponentUpdate(nextProps, nextState); + // If we are and remain hidden, diff based on props + if (isUnrendered) { + return !updateOnPropsForUnrendered.every(prop => nextProps[prop] === this.props[prop]); + } + // Else, assume the children have changed + return true; } + componentDidMount () { const { intersectionObserverWrapper, id } = this.props; @@ -119,7 +122,7 @@ export default class IntersectionObserverArticle extends ImmutablePureComponent data-id={id} tabIndex='0' style={style}> - {children && React.cloneElement(children, { hidden: !isIntersecting && (isHidden || cachedHeight) })} + {children && React.cloneElement(children, { hidden: !isIntersecting && (isHidden || !!cachedHeight) })} </article> ); } diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index ed2623ebb..022ae6de8 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -55,8 +55,8 @@ export const defaultMediaVisibility = (status, settings) => { return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all'); } -@injectIntl -export default class Status extends ImmutablePureComponent { +export default @injectIntl +class Status extends ImmutablePureComponent { static contextTypes = { router: PropTypes.object, @@ -105,6 +105,7 @@ export default class Status extends ImmutablePureComponent { showMedia: undefined, statusId: undefined, revealBehindCW: undefined, + showCard: false, } // Avoid checking props that are functions (and whose equality will always @@ -255,28 +256,32 @@ export default class Status extends ImmutablePureComponent { this.setState({ autoCollapsed: true }); } - this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card') && this.props.settings.get('inline_preview_cards'); + // Hack to fix timeline jumps when a preview card is fetched + this.setState({ + showCard: !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card') && this.props.settings.get('inline_preview_cards'), + }); } + // Hack to fix timeline jumps on second rendering when auto-collapsing + // or on subsequent rendering when a preview card has been fetched getSnapshotBeforeUpdate (prevProps, prevState) { - if (this.props.getScrollPosition) { + if (!this.props.getScrollPosition) return null; + + const { muted, hidden, status, settings } = this.props; + + const doShowCard = !muted && !hidden && status && status.get('card') && settings.get('inline_preview_cards'); + if (this.state.autoCollapsed || (doShowCard && !this.state.showCard)) { + if (doShowCard) this.setState({ showCard: true }); + if (this.state.autoCollapsed) this.setState({ autoCollapsed: false }); return this.props.getScrollPosition(); } else { return null; } } - // Hack to fix timeline jumps on second rendering when auto-collapsing componentDidUpdate (prevProps, prevState, snapshot) { - const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card') && this.props.settings.get('inline_preview_cards'); - if (this.state.autoCollapsed || (doShowCard && !this.didShowCard)) { - if (doShowCard) this.didShowCard = true; - if (this.state.autoCollapsed) this.setState({ autoCollapsed: false }); - if (snapshot !== null && this.props.updateScrollBottom) { - if (this.node.offsetTop < snapshot.top) { - this.props.updateScrollBottom(snapshot.height - snapshot.top); - } - } + if (snapshot !== null && this.props.updateScrollBottom && this.node.offsetTop < snapshot.top) { + this.props.updateScrollBottom(snapshot.height - snapshot.top); } } diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 85bc4a976..c424fbde1 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -284,11 +284,11 @@ export default class StatusActionBar extends ImmutablePureComponent { <div className='status__action-bar'> {replyButton} {!directMessage && [ - <IconButton className='status__action-bar-button' disabled={reblogDisabled} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogDisabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(reblogMessage)} icon={reblogIcon} onClick={this.handleReblogClick} />, - <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />, + <IconButton key='reblog-button' className='status__action-bar-button' disabled={reblogDisabled} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogDisabled ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(reblogMessage)} icon={reblogIcon} onClick={this.handleReblogClick} />, + <IconButton key='favourite-button' className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />, shareButton, - <IconButton className='status__action-bar-button bookmark-icon' disabled={anonymousAccess} active={status.get('bookmarked')} pressed={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />, - <div className='status__action-bar-dropdown'> + <IconButton key='bookmark-button' className='status__action-bar-button bookmark-icon' disabled={anonymousAccess} active={status.get('bookmarked')} pressed={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />, + <div key='dropdown-button' className='status__action-bar-dropdown'> <DropdownMenuContainer disabled={anonymousAccess} status={status} items={menu} icon='ellipsis-h' size={18} direction='right' ariaLabel={intl.formatMessage(messages.more)} /> </div>, ]} |