Browse Source

Merge pt1

Main bits here are:

 - retaining the "no alt text badge" and the custom class "media-missing-description" which lets admins apply custom CSS to media that doesn't have alt text
 - agreeing with the upstream merge removing "noreferrer" from the
   thumbnail link because that is now set by the admin in the UI: 425311e1d9
dariusk-working/4_4_0
Darius Kazemi 4 months ago
parent
commit
0a74823f8d
  1. 6
      app/javascript/mastodon/actions/lists.js
  2. 181
      app/javascript/mastodon/components/account.jsx
  3. 13
      app/javascript/mastodon/components/avatar.tsx
  4. 29
      app/javascript/mastodon/components/media_gallery.jsx

6
app/javascript/mastodon/actions/lists.js

@ -4,7 +4,6 @@ export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
export const LIST_FETCH_FAIL = 'LIST_FETCH_FAIL';
<<<<<<< HEAD
export const LISTS_FETCH_REQUEST = 'LISTS_FETCH_REQUEST';
export const LISTS_FETCH_SUCCESS = 'LISTS_FETCH_SUCCESS';
export const LISTS_FETCH_FAIL = 'LISTS_FETCH_FAIL';
@ -22,8 +21,6 @@ export const LIST_UPDATE_REQUEST = 'LIST_UPDATE_REQUEST';
export const LIST_UPDATE_SUCCESS = 'LIST_UPDATE_SUCCESS';
export const LIST_UPDATE_FAIL = 'LIST_UPDATE_FAIL';
=======
>>>>>>> v4.4.0
export const LIST_DELETE_REQUEST = 'LIST_DELETE_REQUEST';
export const LIST_DELETE_SUCCESS = 'LIST_DELETE_SUCCESS';
export const LIST_DELETE_FAIL = 'LIST_DELETE_FAIL';
@ -58,7 +55,6 @@ export const fetchListFail = (id, error) => ({
error,
});
<<<<<<< HEAD
export const fetchLists = () => (dispatch) => {
dispatch(fetchListsRequest());
@ -170,8 +166,6 @@ export const resetListEditor = () => ({
type: LIST_EDITOR_RESET,
});
=======
>>>>>>> v4.4.0
export const deleteList = id => (dispatch) => {
dispatch(deleteListRequest(id));

181
app/javascript/mastodon/components/account.jsx

@ -1,181 +0,0 @@
import PropTypes from 'prop-types';
import { useCallback } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import { EmptyAccount } from 'mastodon/components/empty_account';
import { Permalink } from 'mastodon/components/permalink';
import { ShortNumber } from 'mastodon/components/short_number';
import { VerifiedBadge } from 'mastodon/components/verified_badge';
import DropdownMenuContainer from '../containers/dropdown_menu_container';
import { me } from '../initial_state';
import { Avatar } from './avatar';
import { Button } from './button';
import { FollowersCounter } from './counters';
import { DisplayName } from './display_name';
import { RelativeTimestamp } from './relative_timestamp';
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Withdraw follow request' },
unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' },
unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' },
mute_notifications: { id: 'account.mute_notifications_short', defaultMessage: 'Mute notifications' },
unmute_notifications: { id: 'account.unmute_notifications_short', defaultMessage: 'Unmute notifications' },
mute: { id: 'account.mute_short', defaultMessage: 'Mute' },
block: { id: 'account.block_short', defaultMessage: 'Block' },
more: { id: 'status.more', defaultMessage: 'More' },
});
const Account = ({ size = 46, account, onFollow, onBlock, onMute, onMuteNotifications, hidden, minimal, defaultAction, withBio }) => {
const intl = useIntl();
const handleFollow = useCallback(() => {
onFollow(account);
}, [onFollow, account]);
const handleBlock = useCallback(() => {
onBlock(account);
}, [onBlock, account]);
const handleMute = useCallback(() => {
onMute(account);
}, [onMute, account]);
const handleMuteNotifications = useCallback(() => {
onMuteNotifications(account, true);
}, [onMuteNotifications, account]);
const handleUnmuteNotifications = useCallback(() => {
onMuteNotifications(account, false);
}, [onMuteNotifications, account]);
if (!account) {
return <EmptyAccount size={size} minimal={minimal} />;
}
if (hidden) {
return (
<>
{account.get('display_name')}
{account.get('username')}
</>
);
}
let buttons;
if (account.get('id') !== me && account.get('relationship', null) !== null) {
const following = account.getIn(['relationship', 'following']);
const requested = account.getIn(['relationship', 'requested']);
const blocking = account.getIn(['relationship', 'blocking']);
const muting = account.getIn(['relationship', 'muting']);
if (requested) {
buttons = <Button text={intl.formatMessage(messages.cancel_follow_request)} onClick={handleFollow} />;
} else if (blocking) {
buttons = <Button text={intl.formatMessage(messages.unblock)} onClick={handleBlock} />;
} else if (muting) {
let menu;
if (account.getIn(['relationship', 'muting_notifications'])) {
menu = [{ text: intl.formatMessage(messages.unmute_notifications), action: handleUnmuteNotifications }];
} else {
menu = [{ text: intl.formatMessage(messages.mute_notifications), action: handleMuteNotifications }];
}
buttons = (
<>
<DropdownMenuContainer
items={menu}
icon='ellipsis-h'
iconComponent={MoreHorizIcon}
direction='right'
title={intl.formatMessage(messages.more)}
/>
<Button text={intl.formatMessage(messages.unmute)} onClick={handleMute} />
</>
);
} else if (defaultAction === 'mute') {
buttons = <Button text={intl.formatMessage(messages.mute)} onClick={handleMute} />;
} else if (defaultAction === 'block') {
buttons = <Button text={intl.formatMessage(messages.block)} onClick={handleBlock} />;
} else if (!account.get('suspended') && !account.get('moved') || following) {
buttons = <Button text={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={handleFollow} />;
}
}
let muteTimeRemaining;
if (account.get('mute_expires_at')) {
muteTimeRemaining = <>· <RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></>;
}
let verification;
const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
if (firstVerifiedField) {
verification = <VerifiedBadge link={firstVerifiedField.get('value')} />;
}
return (
<div className={classNames('account', { 'account--minimal': minimal })}>
<div className='account__wrapper'>
<Permalink key={account.get('id')} className='account__display-name' title={account.get('acct')} href={account.get('url')} to={`/@${account.get('acct')}`} data-hover-card-account={account.get('id')}>
<div className='account__avatar-wrapper'>
<Avatar account={account} size={size} />
</div>
<div className='account__contents'>
<DisplayName account={account} />
{!minimal && (
<div className='account__details'>
<ShortNumber value={account.get('followers_count')} renderer={FollowersCounter} /> {verification} {muteTimeRemaining}
</div>
)}
</div>
</Permalink>
{!minimal && (
<div className='account__relationship'>
{buttons}
</div>
)}
</div>
{withBio && (account.get('note').length > 0 ? (
<div
className='account__note translate'
dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
/>
) : (
<div className='account__note account__note--missing'><FormattedMessage id='account.no_bio' defaultMessage='No description provided.' /></div>
))}
</div>
);
};
Account.propTypes = {
size: PropTypes.number,
account: ImmutablePropTypes.record,
onFollow: PropTypes.func,
onBlock: PropTypes.func,
onMute: PropTypes.func,
onMuteNotifications: PropTypes.func,
hidden: PropTypes.bool,
minimal: PropTypes.bool,
defaultAction: PropTypes.string,
withBio: PropTypes.bool,
};
export default Account;

13
app/javascript/mastodon/components/avatar.tsx

@ -1,16 +1,9 @@
import { useState, useCallback } from 'react';
<<<<<<< HEAD
import classNames from 'classnames';
import { useHovering } from 'mastodon/../hooks/useHovering';
=======
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { useHovering } from 'mastodon/hooks/useHovering';
>>>>>>> v4.4.0
import { autoPlayGif } from 'mastodon/initial_state';
import type { Account } from 'mastodon/models/account';
@ -59,15 +52,9 @@ export const Avatar: React.FC<Props> = ({
setError(true);
}, [setError]);
<<<<<<< HEAD
return (
<div
className={classNames('account__avatar', {
=======
const avatar = (
<div
className={classNames(className, 'account__avatar', {
>>>>>>> v4.4.0
'account__avatar--inline': inline,
'account__avatar--loading': loading,
})}

29
app/javascript/mastodon/components/media_gallery.jsx

@ -84,15 +84,11 @@ class Item extends PureComponent {
this.setState({ loaded: true });
};
<<<<<<< HEAD
render() {
=======
handleImageError = () => {
this.setState({ error: true });
};
render () {
>>>>>>> v4.4.0
const { attachment, lang, index, size, standalone, displayWidth, visible } = this.props;
let badges = [], thumbnail;
@ -108,31 +104,18 @@ class Item extends PureComponent {
height = 50;
}
<<<<<<< HEAD
const hasMediaDescription = attachment.get('description')?.length > 0;
if (hasMediaDescription) {
badges.push(<AltTextBadge key='alt' description={attachment.get('description')} />);
} else {
badges.push(<NoAltTextBadge key='no-alt' />);
}
=======
>>>>>>> v4.4.0
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
if (description?.length > 0) {
badges.push(<AltTextBadge key='alt' description={description} />);
} else {
badges.push(<NoAltTextBadge key='no-alt' />);
}
if (attachment.get('type') === 'unknown') {
return (
<<<<<<< HEAD
<div className={classNames('media-gallery__item', { standalone, 'media-gallery__item--tall': height === 100, 'media-gallery__item--wide': width === 100, 'media-missing-description': !hasMediaDescription })} key={attachment.get('id')}>
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={description} lang={lang} target='_blank' rel='noopener noreferrer'>
=======
<div className={classNames('media-gallery__item', { standalone, 'media-gallery__item--tall': height === 100, 'media-gallery__item--wide': width === 100 })} key={attachment.get('id')}>
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={description} lang={lang} target='_blank' rel='noopener'>
>>>>>>> v4.4.0
<Blurhash
hash={attachment.get('blurhash')}
className='media-gallery__preview'
@ -344,15 +327,7 @@ class MediaGallery extends PureComponent {
<div className={`media-gallery media-gallery--layout-${size}`} style={style} ref={this.handleRef}>
{children}
<<<<<<< HEAD
{(!visible || uncached) && (
<div className={classNames('spoiler-button', { 'spoiler-button--click-thru': uncached })}>
{spoilerButton}
</div>
)}
=======
{(!visible || uncached) && <SpoilerButton uncached={uncached} sensitive={sensitive} onClick={this.handleOpen} matchedFilters={matchedFilters} />}
>>>>>>> v4.4.0
{(visible && !uncached) && (
<div className='media-gallery__actions'>

Loading…
Cancel
Save