From 749c360ba06cef2c2ff1f0592c6a774b584909aa Mon Sep 17 00:00:00 2001 From: Claire Date: Tue, 20 Dec 2022 12:37:14 +0100 Subject: Reuse upstream's “Posts and replies” as column heading (#2023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream does not have a column heading for “Posts and replies”, but the text itself exists in a similar context, so re-use that translatable string so that we can use upstream's translations. --- app/javascript/flavours/glitch/features/status/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index e190652b0..c716e4f0f 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -67,7 +67,7 @@ const messages = defineMessages({ detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' }, replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' }, replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' }, - tootHeading: { id: 'column.toot', defaultMessage: 'Toots and replies' }, + tootHeading: { id: 'account.posts_with_replies', defaultMessage: 'Posts and replies' }, }); const makeMapStateToProps = () => { -- cgit From 7e9d5bdbb9125197afb5b3df69ce49a79451d697 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 5 Jan 2023 13:32:29 +0100 Subject: [Glitch] Fix unnecessary re-rendering of various components when typing in web UI (#2063) Port 9620ee90be7e04b3616ce4b851abb63dbba7af7f to glitch-soc Signed-off-by: Claire Signed-off-by: Claire Co-authored-by: Eugen Rochko --- app/javascript/flavours/glitch/components/status.js | 8 ++++---- .../flavours/glitch/containers/status_container.js | 9 +++------ .../features/status/components/detailed_status.js | 9 ++++++--- .../flavours/glitch/features/status/index.js | 19 ++++++++++++------- app/javascript/flavours/glitch/selectors/index.js | 12 +++++++++++- 5 files changed, 36 insertions(+), 21 deletions(-) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 4041b4819..409ec0adc 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -102,7 +102,7 @@ class Status extends ImmutablePureComponent { scrollKey: PropTypes.string, deployPictureInPicture: PropTypes.func, settings: ImmutablePropTypes.map.isRequired, - pictureInPicture: PropTypes.shape({ + pictureInPicture: ImmutablePropTypes.contains({ inUse: PropTypes.bool, available: PropTypes.bool, }), @@ -603,7 +603,7 @@ class Status extends ImmutablePureComponent { attachments = status.get('media_attachments'); - if (pictureInPicture.inUse) { + if (pictureInPicture.get('inUse')) { media.push(); mediaIcons.push('video-camera'); } else if (attachments.size > 0) { @@ -631,7 +631,7 @@ class Status extends ImmutablePureComponent { width={this.props.cachedMediaWidth} height={110} cacheWidth={this.props.cacheMediaWidth} - deployPictureInPicture={pictureInPicture.available ? this.handleDeployPictureInPicture : undefined} + deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined} sensitive={status.get('sensitive')} blurhash={attachment.get('blurhash')} visible={this.state.showMedia} @@ -660,7 +660,7 @@ class Status extends ImmutablePureComponent { onOpenVideo={this.handleOpenVideo} width={this.props.cachedMediaWidth} cacheWidth={this.props.cacheMediaWidth} - deployPictureInPicture={pictureInPicture.available ? this.handleDeployPictureInPicture : undefined} + deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined} visible={this.state.showMedia} onToggleVisibility={this.handleToggleMediaVisibility} />)} diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js index 947573fc7..645919ebe 100644 --- a/app/javascript/flavours/glitch/containers/status_container.js +++ b/app/javascript/flavours/glitch/containers/status_container.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import Status from 'flavours/glitch/components/status'; import { List as ImmutableList } from 'immutable'; -import { makeGetStatus } from 'flavours/glitch/selectors'; +import { makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors'; import { replyCompose, mentionCompose, @@ -60,6 +60,7 @@ const messages = defineMessages({ const makeMapStateToProps = () => { const getStatus = makeGetStatus(); + const getPictureInPicture = makeGetPictureInPicture(); const mapStateToProps = (state, props) => { @@ -83,11 +84,7 @@ const makeMapStateToProps = () => { account: account || props.account, settings: state.get('local_settings'), prepend: prepend || props.prepend, - - pictureInPicture: { - inUse: state.getIn(['meta', 'layout']) !== 'mobile' && state.get('picture_in_picture').statusId === props.id, - available: state.getIn(['meta', 'layout']) !== 'mobile', - }, + pictureInPicture: getPictureInPicture(state, props), }; }; 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 7d2c2aace..907fc3f1c 100644 --- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js +++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js @@ -41,7 +41,10 @@ class DetailedStatus extends ImmutablePureComponent { domain: PropTypes.string.isRequired, compact: PropTypes.bool, showMedia: PropTypes.bool, - usingPiP: PropTypes.bool, + pictureInPicture: ImmutablePropTypes.contains({ + inUse: PropTypes.bool, + available: PropTypes.bool, + }), onToggleMediaVisibility: PropTypes.func, intl: PropTypes.object.isRequired, }; @@ -120,7 +123,7 @@ class DetailedStatus extends ImmutablePureComponent { render () { const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status; - const { expanded, onToggleHidden, settings, usingPiP, intl } = this.props; + const { expanded, onToggleHidden, settings, pictureInPicture, intl } = this.props; const outerStyle = { boxSizing: 'border-box' }; const { compact } = this.props; @@ -153,7 +156,7 @@ class DetailedStatus extends ImmutablePureComponent { outerStyle.height = `${this.state.height}px`; } - if (usingPiP) { + if (pictureInPicture.get('inUse')) { media.push(); mediaIcons.push('video-camera'); } else if (status.get('media_attachments').size > 0) { diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index c716e4f0f..c22e7f0bd 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -41,7 +41,7 @@ import { initMuteModal } from 'flavours/glitch/actions/mutes'; import { initBlockModal } from 'flavours/glitch/actions/blocks'; import { initReport } from 'flavours/glitch/actions/reports'; import { initBoostModal } from 'flavours/glitch/actions/boosts'; -import { makeGetStatus } from 'flavours/glitch/selectors'; +import { makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors'; import ScrollContainer from 'flavours/glitch/containers/scroll_container'; import ColumnBackButton from 'flavours/glitch/components/column_back_button'; import ColumnHeader from '../../components/column_header'; @@ -72,6 +72,7 @@ const messages = defineMessages({ const makeMapStateToProps = () => { const getStatus = makeGetStatus(); + const getPictureInPicture = makeGetPictureInPicture(); const getAncestorsIds = createSelector([ (_, { id }) => id, @@ -129,11 +130,12 @@ const makeMapStateToProps = () => { const mapStateToProps = (state, props) => { const status = getStatus(state, { id: props.params.statusId }); - let ancestorsIds = Immutable.List(); + + let ancestorsIds = Immutable.List(); let descendantsIds = Immutable.List(); if (status) { - ancestorsIds = getAncestorsIds(state, { id: status.get('in_reply_to_id') }); + ancestorsIds = getAncestorsIds(state, { id: status.get('in_reply_to_id') }); descendantsIds = getDescendantsIds(state, { id: status.get('id') }); } @@ -145,7 +147,7 @@ const makeMapStateToProps = () => { settings: state.get('local_settings'), askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0, domain: state.getIn(['meta', 'domain']), - usingPiP: state.get('picture_in_picture').statusId === props.params.statusId, + pictureInPicture: getPictureInPicture(state, { id: props.params.statusId }), }; }; @@ -190,7 +192,10 @@ class Status extends ImmutablePureComponent { askReplyConfirmation: PropTypes.bool, multiColumn: PropTypes.bool, domain: PropTypes.string.isRequired, - usingPiP: PropTypes.bool, + pictureInPicture: ImmutablePropTypes.contains({ + inUse: PropTypes.bool, + available: PropTypes.bool, + }), }; state = { @@ -604,7 +609,7 @@ class Status extends ImmutablePureComponent { render () { let ancestors, descendants; - const { isLoading, status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, usingPiP } = this.props; + const { isLoading, status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props; const { fullscreen } = this.state; if (isLoading) { @@ -682,7 +687,7 @@ class Status extends ImmutablePureComponent { domain={domain} showMedia={this.state.showMedia} onToggleMediaVisibility={this.handleToggleMediaVisibility} - usingPiP={usingPiP} + pictureInPicture={pictureInPicture} /> { ); }; +export const makeGetPictureInPicture = () => { + return createSelector([ + (state, { id }) => state.get('picture_in_picture').statusId === id, + (state) => state.getIn(['meta', 'layout']) !== 'mobile', + ], (inUse, available) => ImmutableMap({ + inUse: inUse && available, + available, + })); +}; + const getAlertsBase = state => state.get('alerts'); export const getAlerts = createSelector([getAlertsBase], (base) => { -- cgit From c4649ae9d4ef34cc0bad9d5df0da8e8b6c729522 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 5 Jan 2023 14:03:46 +0100 Subject: [Glitch] Add dropdown menu item to open admin interface for remote domains Port 18d00055f4f7e28a9a4cc81d3159072e9beb779d to glitch-soc Signed-off-by: Claire --- .../glitch/components/status_action_bar.js | 26 ++++++++++++---------- .../glitch/features/account/components/header.js | 14 ++++++++---- .../features/status/components/action_bar.js | 25 +++++++++++---------- app/javascript/flavours/glitch/permissions.js | 7 +++--- 4 files changed, 41 insertions(+), 31 deletions(-) (limited to 'app/javascript/flavours/glitch/features/status') diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 7a08e7a59..baf61000a 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -9,7 +9,7 @@ import { me } from 'flavours/glitch/initial_state'; import RelativeTimestamp from './relative_timestamp'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; import classNames from 'classnames'; -import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions'; +import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -38,6 +38,7 @@ const messages = defineMessages({ embed: { id: 'status.embed', defaultMessage: 'Embed' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' }, + admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' }, copy: { id: 'status.copy', defaultMessage: 'Copy link to post' }, hide: { id: 'status.hide', defaultMessage: 'Hide post' }, edited: { id: 'status.edited', defaultMessage: 'Edited {date}' }, @@ -197,6 +198,7 @@ class StatusActionBar extends ImmutablePureComponent { render () { const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props; + const { permissions } = this.context.identity; const anonymousAccess = !me; const mutingConversation = status.get('muted'); @@ -252,19 +254,19 @@ class StatusActionBar extends ImmutablePureComponent { 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 ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) { + if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); - if (accountAdminLink !== undefined) { - menu.push({ - text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), - href: accountAdminLink(status.getIn(['account', 'id'])), - }); + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + if (accountAdminLink !== undefined) { + menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) }); + } + if (statusAdminLink !== undefined) { + menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) }); + } } - if (statusAdminLink !== undefined) { - menu.push({ - text: intl.formatMessage(messages.admin_status), - href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')), - }); + if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) { + const domain = status.getIn(['account', 'acct']).split('@')[1]; + menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` }); } } } diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js index b0fb527c7..ec4a192bc 100644 --- a/app/javascript/flavours/glitch/features/account/components/header.js +++ b/app/javascript/flavours/glitch/features/account/components/header.js @@ -14,7 +14,7 @@ import { NavLink } from 'react-router-dom'; import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container'; import AccountNoteContainer from '../containers/account_note_container'; import FollowRequestNoteContainer from '../containers/follow_request_note_container'; -import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions'; +import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; import { Helmet } from 'react-helmet'; const messages = defineMessages({ @@ -52,6 +52,7 @@ const messages = defineMessages({ unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' }, add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, + admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' }, add_account_note: { id: 'account.add_account_note', defaultMessage: 'Add note for @{name}' }, languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' }, openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' }, @@ -155,7 +156,7 @@ class Header extends ImmutablePureComponent { render () { const { account, hidden, intl, domain } = this.props; - const { signedIn } = this.context.identity; + const { signedIn, permissions } = this.context.identity; if (!account) { return null; @@ -291,9 +292,14 @@ class Header extends ImmutablePureComponent { } } - if (account.get('id') !== me && (this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) { + if (account.get('id') !== me && ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); - menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: accountAdminLink(account.get('id')) }); + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) { + menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: accountAdminLink(account.get('id')) }); + } + if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) { + menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: remoteDomain }), href: `/admin/instances/${remoteDomain}` }); + } } const content = { __html: account.get('note_emojified') }; 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 b6f8a9877..73913dd49 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.js +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js @@ -7,7 +7,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import { me } from 'flavours/glitch/initial_state'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; import classNames from 'classnames'; -import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions'; +import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -34,6 +34,7 @@ const messages = defineMessages({ embed: { id: 'status.embed', defaultMessage: 'Embed' }, admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' }, admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' }, + admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' }, copy: { id: 'status.copy', defaultMessage: 'Copy link to status' }, openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' }, }); @@ -177,19 +178,19 @@ 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 ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) { + if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); - if (accountAdminLink !== undefined) { - menu.push({ - text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), - href: accountAdminLink(status.getIn(['account', 'id'])), - }); + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + if (accountAdminLink !== undefined) { + menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) }); + } + if (statusAdminLink !== undefined) { + menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) }); + } } - if (statusAdminLink !== undefined) { - menu.push({ - text: intl.formatMessage(messages.admin_status), - href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')), - }); + if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) { + const domain = status.getIn(['account', 'acct']).split('@')[1]; + menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` }); } } } diff --git a/app/javascript/flavours/glitch/permissions.js b/app/javascript/flavours/glitch/permissions.js index 752ddd6c5..9ea149e5f 100644 --- a/app/javascript/flavours/glitch/permissions.js +++ b/app/javascript/flavours/glitch/permissions.js @@ -1,3 +1,4 @@ -export const PERMISSION_INVITE_USERS = 0x0000000000010000; -export const PERMISSION_MANAGE_USERS = 0x0000000000000400; -export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010; +export const PERMISSION_INVITE_USERS = 0x0000000000010000; +export const PERMISSION_MANAGE_USERS = 0x0000000000000400; +export const PERMISSION_MANAGE_FEDERATION = 0x0000000000000020; +export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010; -- cgit