diff options
author | Starfall <us@starfall.systems> | 2022-07-25 18:53:31 -0500 |
---|---|---|
committer | Starfall <us@starfall.systems> | 2022-07-25 18:53:31 -0500 |
commit | 5b9419060d79eda85c40a12c567dd0e1e44a7ecb (patch) | |
tree | f5e21930844f7c11ae40b9097a78a32916ba5dba /app/javascript/flavours/glitch/features | |
parent | a137fecf94d25a03ef7224843c1afd0c30f428e6 (diff) | |
parent | 3a7c641dd4db1d67b172f731518b472d58dd2262 (diff) |
Merge remote-tracking branch 'glitch/main'
Diffstat (limited to 'app/javascript/flavours/glitch/features')
14 files changed, 360 insertions, 61 deletions
diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js index 45aba53f7..53170b7a6 100644 --- a/app/javascript/flavours/glitch/features/account/components/header.js +++ b/app/javascript/flavours/glitch/features/account/components/header.js @@ -3,7 +3,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { autoPlayGif, me, isStaff } from 'flavours/glitch/util/initial_state'; +import { autoPlayGif, me } from 'flavours/glitch/util/initial_state'; import { preferencesLink, profileLink, accountAdminLink } from 'flavours/glitch/util/backend_links'; import classNames from 'classnames'; import Icon from 'flavours/glitch/components/icon'; @@ -13,6 +13,7 @@ import Button from 'flavours/glitch/components/button'; import { NavLink } from 'react-router-dom'; import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container'; import AccountNoteContainer from '../containers/account_note_container'; +import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions'; const messages = defineMessages({ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, @@ -64,6 +65,10 @@ const dateFormatOptions = { export default @injectIntl class Header extends ImmutablePureComponent { + static contextTypes = { + identity: PropTypes.object, + }; + static propTypes = { account: ImmutablePropTypes.map, identity_props: ImmutablePropTypes.list, @@ -244,7 +249,7 @@ class Header extends ImmutablePureComponent { } } - if (account.get('id') !== me && isStaff && accountAdminLink) { + if (account.get('id') !== me && (this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) { menu.push(null); menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: accountAdminLink(account.get('id')) }); } 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 202d96676..7107c9db3 100644 --- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js +++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js @@ -132,6 +132,8 @@ class Conversation extends ImmutablePureComponent { } handleShowMore = () => { + this.props.onToggleHidden(this.props.lastStatus); + if (this.props.lastStatus.get('spoiler_text')) { this.setExpansion(!this.state.isExpanded); } @@ -143,12 +145,13 @@ class Conversation extends ImmutablePureComponent { render () { const { accounts, lastStatus, unread, scrollKey, intl } = this.props; - const { isExpanded } = this.state; if (lastStatus === null) { return null; } + const isExpanded = this.props.settings.getIn(['content_warnings', 'shared_state']) ? !lastStatus.get('hidden') : this.state.isExpanded; + const menu = [ { text: intl.formatMessage(messages.open), action: this.handleClick }, null, diff --git a/app/javascript/flavours/glitch/features/direct_timeline/containers/conversation_container.js b/app/javascript/flavours/glitch/features/direct_timeline/containers/conversation_container.js index b15ce9f0f..f5e5946e3 100644 --- a/app/javascript/flavours/glitch/features/direct_timeline/containers/conversation_container.js +++ b/app/javascript/flavours/glitch/features/direct_timeline/containers/conversation_container.js @@ -23,6 +23,7 @@ const mapStateToProps = () => { accounts: conversation.get('accounts').map(accountId => state.getIn(['accounts', accountId], null)), unread: conversation.get('unread'), lastStatus: lastStatusId && getStatus(state, { id: lastStatusId }), + settings: state.get('local_settings'), }; }; }; diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.js index 2490b6e2d..ffa4e3409 100644 --- a/app/javascript/flavours/glitch/features/local_settings/page/index.js +++ b/app/javascript/flavours/glitch/features/local_settings/page/index.js @@ -303,38 +303,59 @@ class LocalSettingsPage extends React.PureComponent { ({ intl, onChange, settings }) => ( <div className='glitch local-settings__page content_warnings'> <h1><FormattedMessage id='settings.content_warnings' defaultMessage='Content warnings' /></h1> - <DeprecatedLocalSettingsPageItem - id='mastodon-settings--content_warnings-auto_unfold' - value={expandSpoilers} + <LocalSettingsPageItem + settings={settings} + item={['content_warnings', 'shared_state']} + id='mastodon-settings--content_warnings-shared_state' + onChange={onChange} > - <FormattedMessage id='settings.enable_content_warnings_auto_unfold' defaultMessage='Automatically unfold content-warnings' /> - <span className='hint'> - <FormattedMessage - id='settings.deprecated_setting' - defaultMessage="This setting is now controlled from Mastodon's {settings_page_link}" - values={{ - settings_page_link: ( - <a href={preferenceLink('user_setting_expand_spoilers')}> - <FormattedMessage - id='settings.shared_settings_link' - defaultMessage='user preferences' - /> - </a> - ) - }} - /> - </span> - </DeprecatedLocalSettingsPageItem> + <FormattedMessage id='settings.content_warnings_shared_state' defaultMessage='Show/hide content of all copies at once' /> + <span className='hint'><FormattedMessage id='settings.content_warnings_shared_state_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW' /></span> + </LocalSettingsPageItem> <LocalSettingsPageItem settings={settings} - item={['content_warnings', 'filter']} - id='mastodon-settings--content_warnings-auto_unfold' + item={['content_warnings', 'media_outside']} + id='mastodon-settings--content_warnings-media_outside' onChange={onChange} - placeholder={intl.formatMessage(messages.regexp)} - disabled={!expandSpoilers} > - <FormattedMessage id='settings.content_warnings_filter' defaultMessage='Content warnings to not automatically unfold:' /> + <FormattedMessage id='settings.content_warnings_media_outside' defaultMessage='Display media attachments outside content warnings' /> + <span className='hint'><FormattedMessage id='settings.content_warnings_media_outside_hint' defaultMessage='Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments' /></span> </LocalSettingsPageItem> + <section> + <h2><FormattedMessage id='settings.content_warnings_unfold_opts' defaultMessage='Auto-unfolding options' /></h2> + <DeprecatedLocalSettingsPageItem + id='mastodon-settings--content_warnings-auto_unfold' + value={expandSpoilers} + > + <FormattedMessage id='settings.enable_content_warnings_auto_unfold' defaultMessage='Automatically unfold content-warnings' /> + <span className='hint'> + <FormattedMessage + id='settings.deprecated_setting' + defaultMessage="This setting is now controlled from Mastodon's {settings_page_link}" + values={{ + settings_page_link: ( + <a href={preferenceLink('user_setting_expand_spoilers')}> + <FormattedMessage + id='settings.shared_settings_link' + defaultMessage='user preferences' + /> + </a> + ) + }} + /> + </span> + </DeprecatedLocalSettingsPageItem> + <LocalSettingsPageItem + settings={settings} + item={['content_warnings', 'filter']} + id='mastodon-settings--content_warnings-auto_unfold' + onChange={onChange} + placeholder={intl.formatMessage(messages.regexp)} + disabled={!expandSpoilers} + > + <FormattedMessage id='settings.content_warnings_filter' defaultMessage='Content warnings to not automatically unfold:' /> + </LocalSettingsPageItem> + </section> </div> ), ({ intl, onChange, settings }) => ( @@ -366,6 +387,7 @@ class LocalSettingsPage extends React.PureComponent { onChange={onChange} > <FormattedMessage id='settings.enable_collapsed' defaultMessage='Enable collapsed toots' /> + <span className='hint'><FormattedMessage id='settings.enable_collapsed_hint' defaultMessage='Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature' /></span> </LocalSettingsPageItem> <LocalSettingsPageItem settings={settings} @@ -457,6 +479,7 @@ class LocalSettingsPage extends React.PureComponent { dependsOn={[['collapsed', 'enabled']]} > <FormattedMessage id='settings.image_backgrounds_media' defaultMessage='Preview collapsed toot media' /> + <span className='hint'><FormattedMessage id='settings.image_backgrounds_media_hint' defaultMessage='If the post has any media attachment, use the first one as a background' /></span> </LocalSettingsPageItem> </section> </div> diff --git a/app/javascript/flavours/glitch/features/notifications/components/admin_report.js b/app/javascript/flavours/glitch/features/notifications/components/admin_report.js new file mode 100644 index 000000000..80beeb9da --- /dev/null +++ b/app/javascript/flavours/glitch/features/notifications/components/admin_report.js @@ -0,0 +1,108 @@ +// Package imports. +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { HotKeys } from 'react-hotkeys'; +import classNames from 'classnames'; + +// Our imports. +import Permalink from 'flavours/glitch/components/permalink'; +import AccountContainer from 'flavours/glitch/containers/account_container'; +import NotificationOverlayContainer from '../containers/overlay_container'; +import Icon from 'flavours/glitch/components/icon'; +import Report from './report'; + +const messages = defineMessages({ + adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' }, +}); + +export default class AdminReport extends ImmutablePureComponent { + + static propTypes = { + hidden: PropTypes.bool, + id: PropTypes.string.isRequired, + account: ImmutablePropTypes.map.isRequired, + notification: ImmutablePropTypes.map.isRequired, + unread: PropTypes.bool, + report: ImmutablePropTypes.map.isRequired, + }; + + handleMoveUp = () => { + const { notification, onMoveUp } = this.props; + onMoveUp(notification.get('id')); + } + + handleMoveDown = () => { + const { notification, onMoveDown } = this.props; + onMoveDown(notification.get('id')); + } + + handleOpen = () => { + this.handleOpenProfile(); + } + + handleOpenProfile = () => { + const { notification } = this.props; + this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`); + } + + handleMention = e => { + e.preventDefault(); + + const { notification, onMention } = this.props; + onMention(notification.get('account'), this.context.router.history); + } + + getHandlers () { + return { + moveUp: this.handleMoveUp, + moveDown: this.handleMoveDown, + open: this.handleOpen, + openProfile: this.handleOpenProfile, + mention: this.handleMention, + reply: this.handleMention, + }; + } + + render () { + const { intl, account, notification, unread, report } = this.props; + + // Links to the display name. + const displayName = account.get('display_name_html') || account.get('username'); + const link = ( + <bdi><Permalink + className='notification__display-name' + href={account.get('url')} + title={account.get('acct')} + to={`/@${account.get('acct')}`} + dangerouslySetInnerHTML={{ __html: displayName }} + /></bdi> + ); + + const targetAccount = report.get('target_account'); + const targetDisplayNameHtml = { __html: targetAccount.get('display_name_html') }; + const targetLink = <bdi><Permalink className='notification__display-name' href={targetAccount.get('url')} title={targetAccount.get('acct')} to={`/@${targetAccount.get('acct')}`} dangerouslySetInnerHTML={targetDisplayNameHtml} /></bdi>; + + return ( + <HotKeys handlers={this.getHandlers()}> + <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex='0'> + <div className='notification__message'> + <div className='notification__favourite-icon-wrapper'> + <Icon id='flag' fixedWidth /> + </div> + + <span title={notification.get('created_at')}> + <FormattedMessage id='notification.admin.report' defaultMessage='{name} reported {target}' values={{ name: link, target: targetLink }} /> + </span> + </div> + + <Report account={account} report={notification.get('report')} hidden={this.props.hidden} /> + <NotificationOverlayContainer notification={notification} /> + </div> + </HotKeys> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/notifications/components/column_settings.js b/app/javascript/flavours/glitch/features/notifications/components/column_settings.js index 0be2a7e13..42ab9de35 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/column_settings.js +++ b/app/javascript/flavours/glitch/features/notifications/components/column_settings.js @@ -6,10 +6,14 @@ import ClearColumnButton from './clear_column_button'; import GrantPermissionButton from './grant_permission_button'; import SettingToggle from './setting_toggle'; import PillBarButton from './pill_bar_button'; -import { isStaff } from 'flavours/glitch/util/initial_state'; +import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'flavours/glitch/permissions'; export default class ColumnSettings extends React.PureComponent { + static contextTypes = { + identity: PropTypes.object, + }; + static propTypes = { settings: ImmutablePropTypes.map.isRequired, pushSettings: ImmutablePropTypes.map.isRequired, @@ -167,7 +171,7 @@ export default class ColumnSettings extends React.PureComponent { </div> </div> - {isStaff && ( + {(this.context.identity.permissions & PERMISSION_MANAGE_USERS === PERMISSION_MANAGE_USERS) && ( <div role='group' aria-labelledby='notifications-admin-sign-up'> <span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></span> @@ -179,6 +183,19 @@ export default class ColumnSettings extends React.PureComponent { </div> </div> )} + + {(this.context.identity.permissions & PERMISSION_MANAGE_REPORTS === PERMISSION_MANAGE_REPORTS) && ( + <div role='group' aria-labelledby='notifications-admin-report'> + <span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></span> + + <div className='column-settings__pillbar'> + <PillBarButton disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'admin.report']} onChange={onChange} label={alertStr} /> + {showPushSettings && <PillBarButton prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'admin.report']} onChange={this.onPushChange} label={pushStr} />} + <PillBarButton prefix='notifications' settings={settings} settingPath={['shows', 'admin.report']} onChange={onChange} label={showStr} /> + <PillBarButton prefix='notifications' settings={settings} settingPath={['sounds', 'admin.report']} onChange={onChange} label={soundStr} /> + </div> + </div> + )} </div> ); } diff --git a/app/javascript/flavours/glitch/features/notifications/components/notification.js b/app/javascript/flavours/glitch/features/notifications/components/notification.js index e0cd3c7a6..d676a4207 100644 --- a/app/javascript/flavours/glitch/features/notifications/components/notification.js +++ b/app/javascript/flavours/glitch/features/notifications/components/notification.js @@ -9,6 +9,7 @@ import StatusContainer from 'flavours/glitch/containers/status_container'; import NotificationFollow from './follow'; import NotificationFollowRequestContainer from '../containers/follow_request_container'; import NotificationAdminSignup from './admin_signup'; +import NotificationAdminReportContainer from '../containers/admin_report_container'; export default class Notification extends ImmutablePureComponent { @@ -77,6 +78,19 @@ export default class Notification extends ImmutablePureComponent { unread={this.props.unread} /> ); + case 'admin.report': + return ( + <NotificationAdminReportContainer + hidden={hidden} + id={notification.get('id')} + account={notification.get('account')} + notification={notification} + onMoveDown={onMoveDown} + onMoveUp={onMoveUp} + onMention={onMention} + unread={this.props.unread} + /> + ); case 'mention': return ( <StatusContainer diff --git a/app/javascript/flavours/glitch/features/notifications/components/report.js b/app/javascript/flavours/glitch/features/notifications/components/report.js new file mode 100644 index 000000000..46a307250 --- /dev/null +++ b/app/javascript/flavours/glitch/features/notifications/components/report.js @@ -0,0 +1,62 @@ +import React, { Fragment } from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import AvatarOverlay from 'flavours/glitch/components/avatar_overlay'; +import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp'; + +const messages = defineMessages({ + openReport: { id: 'report_notification.open', defaultMessage: 'Open report' }, + other: { id: 'report_notification.categories.other', defaultMessage: 'Other' }, + spam: { id: 'report_notification.categories.spam', defaultMessage: 'Spam' }, + violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' }, +}); + +export default @injectIntl +class Report extends ImmutablePureComponent { + + static propTypes = { + account: ImmutablePropTypes.map.isRequired, + report: ImmutablePropTypes.map.isRequired, + hidden: PropTypes.bool, + intl: PropTypes.object.isRequired, + }; + + render () { + const { intl, hidden, report, account } = this.props; + + if (!report) { + return null; + } + + if (hidden) { + return ( + <Fragment> + {report.get('id')} + </Fragment> + ); + } + + return ( + <div className='notification__report'> + <div className='notification__report__avatar'> + <AvatarOverlay account={report.get('target_account')} friend={account} /> + </div> + + <div className='notification__report__details'> + <div> + <RelativeTimestamp timestamp={report.get('created_at')} short={false} /> · <FormattedMessage id='report_notification.attached_statuses' defaultMessage='{count, plural, one {{count} post} other {{count} posts}} attached' values={{ count: report.get('status_ids').size }} /> + <br /> + <strong>{intl.formatMessage(messages[report.get('category')])}</strong> + </div> + + <div className='notification__report__actions'> + <a href={`/admin/reports/${report.get('id')}`} className='button' target='_blank' rel='noopener noreferrer'>{intl.formatMessage(messages.openReport)}</a> + </div> + </div> + </div> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/notifications/containers/admin_report_container.js b/app/javascript/flavours/glitch/features/notifications/containers/admin_report_container.js new file mode 100644 index 000000000..4198afce8 --- /dev/null +++ b/app/javascript/flavours/glitch/features/notifications/containers/admin_report_container.js @@ -0,0 +1,13 @@ +import { connect } from 'react-redux'; +import { makeGetReport } from 'flavours/glitch/selectors'; +import AdminReport from '../components/admin_report'; + +const mapStateToProps = (state, { notification }) => { + const getReport = makeGetReport(); + + return { + report: notification.get('report') ? getReport(state, notification.get('report'), notification.getIn(['report', 'target_account', 'id'])) : null, + }; +}; + +export default connect(mapStateToProps)(AdminReport); diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js index a67a045da..ef0f0f2b7 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.js +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js @@ -4,9 +4,10 @@ import IconButton from 'flavours/glitch/components/icon_button'; import ImmutablePropTypes from 'react-immutable-proptypes'; import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container'; import { defineMessages, injectIntl } from 'react-intl'; -import { me, isStaff } from 'flavours/glitch/util/initial_state'; +import { me } from 'flavours/glitch/util/initial_state'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/util/backend_links'; import classNames from 'classnames'; +import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -41,6 +42,7 @@ class ActionBar extends React.PureComponent { static contextTypes = { router: PropTypes.object, + identity: PropTypes.object, }; static propTypes = { @@ -182,7 +184,7 @@ class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick }); menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport }); - if (isStaff && (accountAdminLink || statusAdminLink)) { + if ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) { menu.push(null); if (accountAdminLink !== undefined) { menu.push({ diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js index f4e6c24c5..301a2add6 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -122,14 +122,27 @@ class DetailedStatus extends ImmutablePureComponent { return null; } - let media = []; - let mediaIcons = []; let applicationLink = ''; let reblogLink = ''; let reblogIcon = 'retweet'; let favouriteLink = ''; let edited = ''; + // Depending on user settings, some media are considered as parts of the + // contents (affected by CW) while other will be displayed outside of the + // CW. + let contentMedia = []; + let contentMediaIcons = []; + let extraMedia = []; + let extraMediaIcons = []; + let media = contentMedia; + let mediaIcons = contentMediaIcons; + + if (settings.getIn(['content_warnings', 'media_outside'])) { + media = extraMedia; + mediaIcons = extraMediaIcons; + } + if (this.props.measureHeight) { outerStyle.height = `${this.state.height}px`; } @@ -199,8 +212,8 @@ class DetailedStatus extends ImmutablePureComponent { } if (status.get('poll')) { - media.push(<PollContainer pollId={status.get('poll')} />); - mediaIcons.push('tasks'); + contentMedia.push(<PollContainer pollId={status.get('poll')} />); + contentMediaIcons.push('tasks'); } if (status.get('application')) { @@ -282,8 +295,9 @@ class DetailedStatus extends ImmutablePureComponent { <StatusContent status={status} - media={media} - mediaIcons={mediaIcons} + media={contentMedia} + extraMedia={extraMedia} + mediaIcons={contentMediaIcons} expanded={expanded} collapsed={false} onExpandedToggle={onToggleHidden} diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 653fabeae..9c86d54db 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -26,7 +26,14 @@ import { directCompose, } from 'flavours/glitch/actions/compose'; import { changeLocalSetting } from 'flavours/glitch/actions/local_settings'; -import { muteStatus, unmuteStatus, deleteStatus, editStatus } from 'flavours/glitch/actions/statuses'; +import { + muteStatus, + unmuteStatus, + deleteStatus, + editStatus, + hideStatus, + revealStatus +} from 'flavours/glitch/actions/statuses'; import { initMuteModal } from 'flavours/glitch/actions/mutes'; import { initBlockModal } from 'flavours/glitch/actions/blocks'; import { initReport } from 'flavours/glitch/actions/reports'; @@ -215,11 +222,19 @@ class Status extends ImmutablePureComponent { return updated ? update : null; } - handleExpandedToggle = () => { - if (this.props.status.get('spoiler_text')) { + handleToggleHidden = () => { + const { status } = this.props; + + if (this.props.settings.getIn(['content_warnings', 'shared_state'])) { + if (status.get('hidden')) { + this.props.dispatch(revealStatus(status.get('id'))); + } else { + this.props.dispatch(hideStatus(status.get('id'))); + } + } else if (this.props.status.get('spoiler_text')) { this.setExpansion(!this.state.isExpanded); } - }; + } handleToggleMediaVisibility = () => { this.setState({ showMedia: !this.state.showMedia }); @@ -354,7 +369,19 @@ class Status extends ImmutablePureComponent { } handleToggleAll = () => { - const { isExpanded } = this.state; + const { status, ancestorsIds, descendantsIds, settings } = this.props; + const statusIds = [status.get('id')].concat(ancestorsIds.toJS(), descendantsIds.toJS()); + let { isExpanded } = this.state; + + if (settings.getIn(['content_warnings', 'shared_state'])) + isExpanded = !status.get('hidden'); + + if (!isExpanded) { + this.props.dispatch(revealStatus(statusIds)); + } else { + this.props.dispatch(hideStatus(statusIds)); + } + this.setState({ isExpanded: !isExpanded, threadExpanded: !isExpanded }); } @@ -513,9 +540,8 @@ class Status extends ImmutablePureComponent { render () { let ancestors, descendants; - const { setExpansion } = this; const { status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, usingPiP } = this.props; - const { fullscreen, isExpanded } = this.state; + const { fullscreen } = this.state; if (status === null) { return ( @@ -526,6 +552,8 @@ class Status extends ImmutablePureComponent { ); } + const isExpanded = settings.getIn(['content_warnings', 'shared_state']) ? !status.get('hidden') : this.state.isExpanded; + if (ancestorsIds && ancestorsIds.size > 0) { ancestors = <div>{this.renderChildren(ancestorsIds)}</div>; } @@ -543,7 +571,7 @@ class Status extends ImmutablePureComponent { bookmark: this.handleHotkeyBookmark, mention: this.handleHotkeyMention, openProfile: this.handleHotkeyOpenProfile, - toggleSpoiler: this.handleExpandedToggle, + toggleSpoiler: this.handleToggleHidden, toggleSensitive: this.handleHotkeyToggleSensitive, openMedia: this.handleHotkeyOpenMedia, }; @@ -574,7 +602,7 @@ class Status extends ImmutablePureComponent { onOpenVideo={this.handleOpenVideo} onOpenMedia={this.handleOpenMedia} expanded={isExpanded} - onToggleHidden={this.handleExpandedToggle} + onToggleHidden={this.handleToggleHidden} domain={domain} showMedia={this.state.showMedia} onToggleMediaVisibility={this.handleToggleMediaVisibility} diff --git a/app/javascript/flavours/glitch/features/ui/components/image_loader.js b/app/javascript/flavours/glitch/features/ui/components/image_loader.js index c6f16a792..dfa0efe49 100644 --- a/app/javascript/flavours/glitch/features/ui/components/image_loader.js +++ b/app/javascript/flavours/glitch/features/ui/components/image_loader.js @@ -1,10 +1,10 @@ -import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React, { PureComponent } from 'react'; import { LoadingBar } from 'react-redux-loading-bar'; import ZoomableImage from './zoomable_image'; -export default class ImageLoader extends React.PureComponent { +export default class ImageLoader extends PureComponent { static propTypes = { alt: PropTypes.string, @@ -43,7 +43,7 @@ export default class ImageLoader extends React.PureComponent { this.loadImage(this.props); } - componentWillReceiveProps (nextProps) { + UNSAFE_componentWillReceiveProps (nextProps) { if (this.props.src !== nextProps.src) { this.loadImage(nextProps); } @@ -139,14 +139,18 @@ export default class ImageLoader extends React.PureComponent { return ( <div className={className}> - <LoadingBar loading={loading ? 1 : 0} className='loading-bar' style={{ width: this.state.width || width }} /> {loading ? ( - <canvas - className='image-loader__preview-canvas' - ref={this.setCanvasRef} - width={width} - height={height} - /> + <> + <div className='loading-bar__container' style={{ width: this.state.width || width }}> + <LoadingBar className='loading-bar' loading={1} /> + </div> + <canvas + className='image-loader__preview-canvas' + ref={this.setCanvasRef} + width={width} + height={height} + /> + </> ) : ( <ZoomableImage alt={alt} diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js index 5d566e516..040e967f2 100644 --- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js +++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js @@ -3,10 +3,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import { Link } from 'react-router-dom'; -import { invitesEnabled, limitedFederationMode, version, repository, source_url } from 'flavours/glitch/util/initial_state'; +import { limitedFederationMode, version, repository, source_url } from 'flavours/glitch/util/initial_state'; import { signOutLink, securityLink } from 'flavours/glitch/util/backend_links'; import { logOut } from 'flavours/glitch/util/log_out'; import { openModal } from 'flavours/glitch/actions/modal'; +import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions'; const messages = defineMessages({ logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' }, @@ -28,6 +29,10 @@ export default @injectIntl @connect(null, mapDispatchToProps) class LinkFooter extends React.PureComponent { + static contextTypes = { + identity: PropTypes.object, + }; + static propTypes = { onLogout: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, @@ -46,7 +51,7 @@ class LinkFooter extends React.PureComponent { return ( <div className='getting-started__footer'> <ul> - {invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>} + {((this.context.identity.permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>} {!!securityLink && <li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>} {!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>} <li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li> |