From d61a6271c68ecca1745f2683d25ec58573dd2819 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Sun, 9 Jun 2019 12:07:23 +0200 Subject: Add DM conversations mode similar to upstream --- .../flavours/glitch/components/avatar_composite.js | 104 +++++++++++++++++++++ .../flavours/glitch/components/display_name.js | 44 ++++++++- .../flavours/glitch/components/status.js | 23 ++++- .../glitch/components/status_action_bar.js | 20 ++-- .../flavours/glitch/components/status_header.js | 82 ++++++++++------ .../flavours/glitch/components/status_icons.js | 6 +- 6 files changed, 229 insertions(+), 50 deletions(-) create mode 100644 app/javascript/flavours/glitch/components/avatar_composite.js (limited to 'app/javascript/flavours/glitch/components') diff --git a/app/javascript/flavours/glitch/components/avatar_composite.js b/app/javascript/flavours/glitch/components/avatar_composite.js new file mode 100644 index 000000000..c52df043a --- /dev/null +++ b/app/javascript/flavours/glitch/components/avatar_composite.js @@ -0,0 +1,104 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { autoPlayGif } from 'flavours/glitch/util/initial_state'; + +export default class AvatarComposite extends React.PureComponent { + + static propTypes = { + accounts: ImmutablePropTypes.list.isRequired, + animate: PropTypes.bool, + size: PropTypes.number.isRequired, + }; + + static defaultProps = { + animate: autoPlayGif, + }; + + renderItem (account, size, index) { + const { animate } = this.props; + + let width = 50; + let height = 100; + let top = 'auto'; + let left = 'auto'; + let bottom = 'auto'; + let right = 'auto'; + + if (size === 1) { + width = 100; + } + + if (size === 4 || (size === 3 && index > 0)) { + height = 50; + } + + if (size === 2) { + if (index === 0) { + right = '2px'; + } else { + left = '2px'; + } + } else if (size === 3) { + if (index === 0) { + right = '2px'; + } else if (index > 0) { + left = '2px'; + } + + if (index === 1) { + bottom = '2px'; + } else if (index > 1) { + top = '2px'; + } + } else if (size === 4) { + if (index === 0 || index === 2) { + right = '2px'; + } + + if (index === 1 || index === 3) { + left = '2px'; + } + + if (index < 2) { + bottom = '2px'; + } else { + top = '2px'; + } + } + + const style = { + left: left, + top: top, + right: right, + bottom: bottom, + width: `${width}%`, + height: `${height}%`, + backgroundSize: 'cover', + backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`, + }; + + return ( + this.props.onAccountClick(account.get('id'), e)} + title={`@${account.get('acct')}`} + key={account.get('id')} + > +
+ + ); + } + + render() { + const { accounts, size } = this.props; + + return ( +
+ {accounts.take(4).map((account, i) => this.renderItem(account, accounts.size, i))} +
+ ); + } + +} diff --git a/app/javascript/flavours/glitch/components/display_name.js b/app/javascript/flavours/glitch/components/display_name.js index a26cff049..7f6ef5a5d 100644 --- a/app/javascript/flavours/glitch/components/display_name.js +++ b/app/javascript/flavours/glitch/components/display_name.js @@ -10,24 +10,56 @@ export default function DisplayName ({ className, inline, localDomain, + others, + onAccountClick, }) { const computedClass = classNames('display-name', { inline }, className); if (!account) return null; + let displayName, suffix; + let acct = account.get('acct'); + if (acct.indexOf('@') === -1 && localDomain) { acct = `${acct}@${localDomain}`; } - // The result. - return account ? ( + if (others && others.size > 0) { + displayName = others.take(2).map(a => ( + onAccountClick(a.get('id'), e)} + title={`@${a.get('acct')}`} + > + + + + + )).reduce((prev, cur) => [prev, ', ', cur]); + + if (others.size - 2 > 0) { + displayName.push(` +${others.size - 2}`); + } + + suffix = ( + onAccountClick(account.get('id'), e)}> + @{acct} + + ); + } else { + displayName = ; + suffix = @{acct}; + } + + return ( - + {displayName} {inline ? ' ' : null} - @{acct} + {suffix} - ) : null; + ); } // Props. @@ -36,4 +68,6 @@ DisplayName.propTypes = { className: PropTypes.string, inline: PropTypes.bool, localDomain: PropTypes.string, + others: ImmutablePropTypes.list, + handleClick: PropTypes.func, }; diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 7014cab17..4b9364ae5 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -66,6 +66,7 @@ export default class Status extends ImmutablePureComponent { containerId: PropTypes.string, id: PropTypes.string, status: ImmutablePropTypes.map, + otherAccounts: ImmutablePropTypes.list, account: ImmutablePropTypes.map, onReply: PropTypes.func, onFavourite: PropTypes.func, @@ -83,6 +84,7 @@ export default class Status extends ImmutablePureComponent { muted: PropTypes.bool, collapse: PropTypes.bool, hidden: PropTypes.bool, + unread: PropTypes.bool, prepend: PropTypes.string, withDismiss: PropTypes.bool, onMoveUp: PropTypes.func, @@ -93,6 +95,7 @@ export default class Status extends ImmutablePureComponent { intl: PropTypes.object.isRequired, cacheMediaWidth: PropTypes.func, cachedMediaWidth: PropTypes.number, + onClick: PropTypes.func, }; state = { @@ -321,17 +324,21 @@ export default class Status extends ImmutablePureComponent { const { status } = this.props; const { isCollapsed } = this.state; if (!router) return; - if (destination === undefined) { - destination = `/statuses/${ - status.getIn(['reblog', 'id'], status.get('id')) - }`; - } + if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) { if (isCollapsed) this.setCollapsed(false); else if (e.shiftKey) { this.setCollapsed(true); document.getSelection().removeAllRanges(); + } else if (this.props.onClick) { + this.props.onClick(); + return; } else { + if (destination === undefined) { + destination = `/statuses/${ + status.getIn(['reblog', 'id'], status.get('id')) + }`; + } let state = {...router.history.location.state}; state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1; router.history.push(destination, state); @@ -441,6 +448,7 @@ export default class Status extends ImmutablePureComponent { intl, status, account, + otherAccounts, settings, collapsed, muted, @@ -450,6 +458,7 @@ export default class Status extends ImmutablePureComponent { onOpenMedia, notification, hidden, + unread, featured, ...other } = this.props; @@ -617,6 +626,7 @@ export default class Status extends ImmutablePureComponent { collapsed: isCollapsed, 'has-background': isCollapsed && background, 'status__wrapper-reply': !!status.get('in_reply_to_id'), + read: unread === false, muted, }, 'focusable'); @@ -647,6 +657,7 @@ export default class Status extends ImmutablePureComponent { friend={account} collapsed={isCollapsed} parseClick={parseClick} + otherAccounts={otherAccounts} /> ) : null} @@ -656,6 +667,7 @@ export default class Status extends ImmutablePureComponent { collapsible={settings.getIn(['collapsed', 'enabled'])} collapsed={isCollapsed} setCollapsed={setCollapsed} + directMessage={!!otherAccounts} /> ) : null} {notification ? ( diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js index 4c398fd19..85bc4a976 100644 --- a/app/javascript/flavours/glitch/components/status_action_bar.js +++ b/app/javascript/flavours/glitch/components/status_action_bar.js @@ -71,6 +71,7 @@ export default class StatusActionBar extends ImmutablePureComponent { onBookmark: PropTypes.func, withDismiss: PropTypes.bool, showReplyCount: PropTypes.bool, + directMessage: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -191,7 +192,7 @@ export default class StatusActionBar extends ImmutablePureComponent { } render () { - const { status, intl, withDismiss, showReplyCount } = this.props; + const { status, intl, withDismiss, showReplyCount, directMessage } = this.props; const mutingConversation = status.get('muted'); const anonymousAccess = !me; @@ -282,14 +283,15 @@ export default class StatusActionBar extends ImmutablePureComponent { return (
{replyButton} - - - {shareButton} - - -
- -
+ {!directMessage && [ + , + , + shareButton, + , +
+ +
, + ]}
diff --git a/app/javascript/flavours/glitch/components/status_header.js b/app/javascript/flavours/glitch/components/status_header.js index f9321904c..23cff286a 100644 --- a/app/javascript/flavours/glitch/components/status_header.js +++ b/app/javascript/flavours/glitch/components/status_header.js @@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; // Mastodon imports. import Avatar from './avatar'; import AvatarOverlay from './avatar_overlay'; +import AvatarComposite from './avatar_composite'; import DisplayName from './display_name'; export default class StatusHeader extends React.PureComponent { @@ -14,12 +15,18 @@ export default class StatusHeader extends React.PureComponent { status: ImmutablePropTypes.map.isRequired, friend: ImmutablePropTypes.map, parseClick: PropTypes.func.isRequired, + otherAccounts: ImmutablePropTypes.list, }; // Handles clicks on account name/image + handleClick = (id, e) => { + const { parseClick } = this.props; + parseClick(e, `/accounts/${id}`); + } + handleAccountClick = (e) => { - const { status, parseClick } = this.props; - parseClick(e, `/accounts/${status.getIn(['account', 'id'])}`); + const { status } = this.props; + this.handleClick(status.getIn(['account', 'id']), e); } // Rendering. @@ -27,36 +34,55 @@ export default class StatusHeader extends React.PureComponent { const { status, friend, + otherAccounts, } = this.props; const account = status.get('account'); - return ( - - ); + let statusAvatar; + if (otherAccounts && otherAccounts.size > 0) { + statusAvatar = ; + } else if (friend === undefined || friend === null) { + statusAvatar = ; + } else { + statusAvatar = ; + } + + if (!otherAccounts) { + return ( + + ); + } else { + // This is a DM conversation + return ( +
+ + {statusAvatar} + + + + + +
+ ); + } } } diff --git a/app/javascript/flavours/glitch/components/status_icons.js b/app/javascript/flavours/glitch/components/status_icons.js index c9747650f..4a2c62881 100644 --- a/app/javascript/flavours/glitch/components/status_icons.js +++ b/app/javascript/flavours/glitch/components/status_icons.js @@ -22,6 +22,7 @@ export default class StatusIcons extends React.PureComponent { mediaIcon: PropTypes.string, collapsible: PropTypes.bool, collapsed: PropTypes.bool, + directMessage: PropTypes.bool, setCollapsed: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, }; @@ -42,6 +43,7 @@ export default class StatusIcons extends React.PureComponent { mediaIcon, collapsible, collapsed, + directMessage, intl, } = this.props; @@ -59,9 +61,7 @@ export default class StatusIcons extends React.PureComponent { aria-hidden='true' /> ) : null} - {( - - )} + {!directMessage && } {collapsible ? (