diff options
Diffstat (limited to 'app/javascript')
24 files changed, 109 insertions, 78 deletions
diff --git a/app/javascript/core/admin.js b/app/javascript/core/admin.js index 3175ff560..ac1b2f95f 100644 --- a/app/javascript/core/admin.js +++ b/app/javascript/core/admin.js @@ -194,7 +194,7 @@ ready(() => { } document.querySelector('a#add-instance-button')?.addEventListener('click', (e) => { - const domain = document.getElementById('by_domain')?.value; + const domain = document.querySelector('input[type="text"]#by_domain')?.value; if (domain) { const url = new URL(event.target.href); diff --git a/app/javascript/flavours/glitch/components/icon_button.js b/app/javascript/flavours/glitch/components/icon_button.js index 41a95e92f..2485f0f48 100644 --- a/app/javascript/flavours/glitch/components/icon_button.js +++ b/app/javascript/flavours/glitch/components/icon_button.js @@ -30,6 +30,7 @@ export default class IconButton extends React.PureComponent { counter: PropTypes.number, obfuscateCount: PropTypes.bool, href: PropTypes.string, + ariaHidden: PropTypes.bool, }; static defaultProps = { @@ -39,6 +40,7 @@ export default class IconButton extends React.PureComponent { animate: false, overlay: false, tabIndex: '0', + ariaHidden: false, }; state = { @@ -115,6 +117,7 @@ export default class IconButton extends React.PureComponent { counter, obfuscateCount, href, + ariaHidden, } = this.props; const { @@ -155,6 +158,7 @@ export default class IconButton extends React.PureComponent { <button aria-label={title} aria-expanded={expanded} + aria-hidden={ariaHidden} title={title} className={classes} onClick={this.handleClick} diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js index ac0d05926..23e279589 100644 --- a/app/javascript/flavours/glitch/components/media_gallery.js +++ b/app/javascript/flavours/glitch/components/media_gallery.js @@ -376,7 +376,7 @@ class MediaGallery extends React.PureComponent { </button> ); } else if (visible) { - spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} />; + spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />; } else { spoilerButton = ( <button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'> diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 5d3a6eb46..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' }, @@ -37,9 +37,10 @@ const messages = defineMessages({ unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' }, 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' }, - copy: { id: 'status.copy', defaultMessage: 'Copy link to status' }, - hide: { id: 'status.hide', defaultMessage: 'Hide toot' }, + 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}' }, filter: { id: 'status.filter', defaultMessage: 'Filter this post' }, openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' }, @@ -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/extra_polyfills.js b/app/javascript/flavours/glitch/extra_polyfills.js index 0d45c23b0..6e8004f07 100644 --- a/app/javascript/flavours/glitch/extra_polyfills.js +++ b/app/javascript/flavours/glitch/extra_polyfills.js @@ -1,6 +1,3 @@ import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'; import 'intersection-observer'; import 'requestidlecallback'; -import objectFitImages from 'object-fit-images'; - -objectFitImages(); 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/compose/components/compose_form.js b/app/javascript/flavours/glitch/features/compose/components/compose_form.js index abdd247a0..0462c7c4b 100644 --- a/app/javascript/flavours/glitch/features/compose/components/compose_form.js +++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.js @@ -310,7 +310,7 @@ class ComposeForm extends ImmutablePureComponent { <ReplyIndicatorContainer /> - <div className={`spoiler-input ${spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}> + <div className={`spoiler-input ${spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}> <AutosuggestInput placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={spoilerText} 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/load_polyfills.js b/app/javascript/flavours/glitch/load_polyfills.js index cc5bcd18f..f5a897f75 100644 --- a/app/javascript/flavours/glitch/load_polyfills.js +++ b/app/javascript/flavours/glitch/load_polyfills.js @@ -23,15 +23,14 @@ function loadPolyfills() { ); // Latest version of Firefox and Safari do not have IntersectionObserver. - // Edge does not have requestIdleCallback and object-fit CSS property. + // Edge does not have requestIdleCallback. // This avoids shipping them all the polyfills. const needsExtraPolyfills = !( window.AbortController && window.IntersectionObserver && window.IntersectionObserverEntry && 'isIntersecting' in IntersectionObserverEntry.prototype && - window.requestIdleCallback && - 'object-fit' in (new Image()).style + window.requestIdleCallback ); return Promise.all([ 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; diff --git a/app/javascript/flavours/glitch/styles/polls.scss b/app/javascript/flavours/glitch/styles/polls.scss index 0847c8f4c..43924829d 100644 --- a/app/javascript/flavours/glitch/styles/polls.scss +++ b/app/javascript/flavours/glitch/styles/polls.scss @@ -289,10 +289,10 @@ color: $dark-text-color; &__chart { - background: rgba(darken($ui-primary-color, 14%), 0.2); + background: rgba(darken($ui-primary-color, 14%), 0.7); &.leading { - background: rgba($ui-highlight-color, 0.2); + background: rgba($ui-highlight-color, 0.5); } } } diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js index 49858f2e2..b7daf82a4 100644 --- a/app/javascript/mastodon/components/icon_button.js +++ b/app/javascript/mastodon/components/icon_button.js @@ -27,6 +27,7 @@ export default class IconButton extends React.PureComponent { counter: PropTypes.number, obfuscateCount: PropTypes.bool, href: PropTypes.string, + ariaHidden: PropTypes.bool, }; static defaultProps = { @@ -36,6 +37,7 @@ export default class IconButton extends React.PureComponent { animate: false, overlay: false, tabIndex: '0', + ariaHidden: false, }; state = { @@ -102,6 +104,7 @@ export default class IconButton extends React.PureComponent { counter, obfuscateCount, href, + ariaHidden, } = this.props; const { @@ -142,6 +145,7 @@ export default class IconButton extends React.PureComponent { type='button' aria-label={title} aria-expanded={expanded} + aria-hidden={ariaHidden} title={title} className={classes} onClick={this.handleClick} diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index bf7982cea..e4a8be338 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -345,7 +345,7 @@ class MediaGallery extends React.PureComponent { </button> ); } else if (visible) { - spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} />; + spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />; } else { spoilerButton = ( <button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'> diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index 40c86afdf..00fc94358 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -8,7 +8,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { me } from '../initial_state'; import classNames from 'classnames'; -import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions'; +import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, @@ -37,9 +37,10 @@ const messages = defineMessages({ unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' }, 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' }, - copy: { id: 'status.copy', defaultMessage: 'Copy link to status' }, - hide: { id: 'status.hide', defaultMessage: 'Hide toot' }, + 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' }, blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' }, unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' }, unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, @@ -232,7 +233,7 @@ class StatusActionBar extends ImmutablePureComponent { render () { const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props; - const { signedIn } = this.context.identity; + const { signedIn, permissions } = this.context.identity; const anonymousAccess = !signedIn; const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); @@ -312,10 +313,16 @@ class StatusActionBar extends ImmutablePureComponent { } } - if ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); - menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` }); - menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }); + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` }); + menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }); + } + if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) { + const domain = account.get('acct').split('@')[1]; + menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` }); + } } } diff --git a/app/javascript/mastodon/extra_polyfills.js b/app/javascript/mastodon/extra_polyfills.js index 395f1ed05..6e8004f07 100644 --- a/app/javascript/mastodon/extra_polyfills.js +++ b/app/javascript/mastodon/extra_polyfills.js @@ -1,6 +1,3 @@ import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'; import 'intersection-observer'; import 'requestidlecallback'; -import objectFitImages from 'object-fit-images'; - -objectFitImages(); diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js index dddbf4dd4..2481e4783 100644 --- a/app/javascript/mastodon/features/account/components/header.js +++ b/app/javascript/mastodon/features/account/components/header.js @@ -15,7 +15,7 @@ import { NavLink } from 'react-router-dom'; import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container'; import AccountNoteContainer from '../containers/account_note_container'; import FollowRequestNoteContainer from '../containers/follow_request_note_container'; -import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions'; +import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { Helmet } from 'react-helmet'; const messages = defineMessages({ @@ -53,6 +53,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}' }, languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' }, openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' }, }); @@ -163,7 +164,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; @@ -288,9 +289,14 @@ class Header extends ImmutablePureComponent { } } - if (account.get('id') !== me && (this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + if ((account.get('id') !== me && (permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); - menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` }); + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${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/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index abd3ba2f7..ebdd55d33 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -226,7 +226,7 @@ class ComposeForm extends ImmutablePureComponent { <ReplyIndicatorContainer /> - <div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}> + <div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}> <AutosuggestInput placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={this.props.spoilerText} diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index c1242754c..46ee9f6c1 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -7,7 +7,7 @@ import DropdownMenuContainer from '../../../containers/dropdown_menu_container'; import { defineMessages, injectIntl } from 'react-intl'; import { me } from '../../../initial_state'; import classNames from 'classnames'; -import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions'; +import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/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' }, blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' }, unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' }, @@ -243,10 +244,16 @@ class ActionBar extends React.PureComponent { } } - if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) { menu.push(null); - menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` }); - menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }); + if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) { + menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` }); + menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` }); + } + if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) { + const domain = account.get('acct').split('@')[1]; + menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` }); + } } } diff --git a/app/javascript/mastodon/load_polyfills.js b/app/javascript/mastodon/load_polyfills.js index cc5bcd18f..f5a897f75 100644 --- a/app/javascript/mastodon/load_polyfills.js +++ b/app/javascript/mastodon/load_polyfills.js @@ -23,15 +23,14 @@ function loadPolyfills() { ); // Latest version of Firefox and Safari do not have IntersectionObserver. - // Edge does not have requestIdleCallback and object-fit CSS property. + // Edge does not have requestIdleCallback. // This avoids shipping them all the polyfills. const needsExtraPolyfills = !( window.AbortController && window.IntersectionObserver && window.IntersectionObserverEntry && 'isIntersecting' in IntersectionObserverEntry.prototype && - window.requestIdleCallback && - 'object-fit' in (new Image()).style + window.requestIdleCallback ); return Promise.all([ diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 8958b1ec0..d0fa1022c 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -563,7 +563,7 @@ "status.favourite": "Favourite", "status.filter": "Filter this post", "status.filtered": "Filtered", - "status.hide": "Hide toot", + "status.hide": "Hide post", "status.history.created": "{name} created {date}", "status.history.edited": "{name} edited {date}", "status.load_more": "Load more", diff --git a/app/javascript/mastodon/permissions.js b/app/javascript/mastodon/permissions.js index 752ddd6c5..9ea149e5f 100644 --- a/app/javascript/mastodon/permissions.js +++ b/app/javascript/mastodon/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; diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 5e7fedf48..4250cf2b6 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -1216,7 +1216,7 @@ a.name-tag, path:first-child { fill: rgba($highlight-text-color, 0.25) !important; - fill-opacity: 100% !important; + fill-opacity: 1 !important; } path:last-child { diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 713144f7d..23c29260b 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4268,7 +4268,7 @@ a.status-card.compact:hover { } @keyframes heartbeat { - from { + 0% { transform: scale(1); animation-timing-function: ease-out; } @@ -7343,7 +7343,7 @@ noscript { path:first-child { fill: rgba($highlight-text-color, 0.25) !important; - fill-opacity: 100% !important; + fill-opacity: 1 !important; } path:last-child { diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss index f553c5501..6812d5462 100644 --- a/app/javascript/styles/mastodon/polls.scss +++ b/app/javascript/styles/mastodon/polls.scss @@ -279,10 +279,10 @@ color: $dark-text-color; &__chart { - background: rgba(darken($ui-primary-color, 14%), 0.2); + background: rgba(darken($ui-primary-color, 14%), 0.7); &.leading { - background: rgba($ui-highlight-color, 0.2); + background: rgba($ui-highlight-color, 0.5); } } } |