Merge pull request #2625 from ClearlyClaire/glitch-soc/ports/copy-button-on-profile
Add prominent share/copy button on profiles in web UIpull/2627/head
commit
5c0a64a975
|
@ -0,0 +1,43 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useState, useCallback } from 'react';
|
||||||
|
|
||||||
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
import ContentCopyIcon from '@/material-icons/400-24px/content_copy.svg?react';
|
||||||
|
import { showAlert } from 'flavours/glitch/actions/alerts';
|
||||||
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
copied: { id: 'copy_icon_button.copied', defaultMessage: 'Copied to clipboard' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CopyIconButton = ({ title, value, className }) => {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
navigator.clipboard.writeText(value);
|
||||||
|
setCopied(true);
|
||||||
|
dispatch(showAlert({ message: messages.copied }));
|
||||||
|
setTimeout(() => setCopied(false), 700);
|
||||||
|
}, [setCopied, value, dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
className={classNames(className, copied ? 'copied' : 'copyable')}
|
||||||
|
title={title}
|
||||||
|
onClick={handleClick}
|
||||||
|
iconComponent={ContentCopyIcon}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CopyIconButton.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
value: PropTypes.string,
|
||||||
|
className: PropTypes.string,
|
||||||
|
};
|
|
@ -14,9 +14,11 @@ import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react';
|
import NotificationsIcon from '@/material-icons/400-24px/notifications.svg?react';
|
||||||
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
|
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
|
||||||
|
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
|
||||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||||
import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge';
|
import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge';
|
||||||
import { Button } from 'flavours/glitch/components/button';
|
import { Button } from 'flavours/glitch/components/button';
|
||||||
|
import { CopyIconButton } from 'flavours/glitch/components/copy_icon_button';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||||
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
||||||
|
@ -44,6 +46,7 @@ const messages = defineMessages({
|
||||||
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
||||||
report: { id: 'account.report', defaultMessage: 'Report @{name}' },
|
report: { id: 'account.report', defaultMessage: 'Report @{name}' },
|
||||||
share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
|
share: { id: 'account.share', defaultMessage: 'Share @{name}\'s profile' },
|
||||||
|
copy: { id: 'account.copy', defaultMessage: 'Copy link to profile' },
|
||||||
media: { id: 'account.media', defaultMessage: 'Media' },
|
media: { id: 'account.media', defaultMessage: 'Media' },
|
||||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
||||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||||
|
@ -175,10 +178,9 @@ class Header extends ImmutablePureComponent {
|
||||||
const isRemote = account.get('acct') !== account.get('username');
|
const isRemote = account.get('acct') !== account.get('username');
|
||||||
const remoteDomain = isRemote ? account.get('acct').split('@')[1] : null;
|
const remoteDomain = isRemote ? account.get('acct').split('@')[1] : null;
|
||||||
|
|
||||||
|
let actionBtn, bellBtn, lockedIcon, shareBtn;
|
||||||
|
|
||||||
let info = [];
|
let info = [];
|
||||||
let actionBtn = '';
|
|
||||||
let bellBtn = '';
|
|
||||||
let lockedIcon = '';
|
|
||||||
let menu = [];
|
let menu = [];
|
||||||
|
|
||||||
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
|
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
|
||||||
|
@ -197,6 +199,12 @@ class Header extends ImmutablePureComponent {
|
||||||
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsActiveIcon : NotificationsIcon} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsActiveIcon : NotificationsIcon} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('share' in navigator) {
|
||||||
|
shareBtn = <IconButton className='optional' iconComponent={ShareIcon} title={intl.formatMessage(messages.share, { name: account.get('username') })} onClick={this.handleShare} />;
|
||||||
|
} else {
|
||||||
|
shareBtn = <CopyIconButton className='optional' title={intl.formatMessage(messages.copy)} value={account.get('url')} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (me !== account.get('id')) {
|
if (me !== account.get('id')) {
|
||||||
if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded
|
if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded
|
||||||
actionBtn = '';
|
actionBtn = '';
|
||||||
|
@ -234,11 +242,6 @@ class Header extends ImmutablePureComponent {
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('share' in navigator && !account.get('suspended')) {
|
|
||||||
menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
|
|
||||||
menu.push(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account.get('id') === me) {
|
if (account.get('id') === me) {
|
||||||
if (profileLink) menu.push({ text: intl.formatMessage(messages.edit_profile), href: profileLink });
|
if (profileLink) menu.push({ text: intl.formatMessage(messages.edit_profile), href: profileLink });
|
||||||
if (preferencesLink) menu.push({ text: intl.formatMessage(messages.preferences), href: preferencesLink });
|
if (preferencesLink) menu.push({ text: intl.formatMessage(messages.preferences), href: preferencesLink });
|
||||||
|
@ -349,6 +352,7 @@ class Header extends ImmutablePureComponent {
|
||||||
<>
|
<>
|
||||||
{actionBtn}
|
{actionBtn}
|
||||||
{bellBtn}
|
{bellBtn}
|
||||||
|
{shareBtn}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -1240,6 +1240,17 @@ body > [data-popper-placement] {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.copyable {
|
||||||
|
transition: all 300ms linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.copied {
|
||||||
|
border-color: $valid-value-color;
|
||||||
|
color: $valid-value-color;
|
||||||
|
transition: none;
|
||||||
|
background-color: rgba($valid-value-color, 0.15);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.domain__wrapper {
|
.domain__wrapper {
|
||||||
|
@ -7965,6 +7976,7 @@ noscript {
|
||||||
|
|
||||||
.account__header {
|
.account__header {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
container: account-header / inline-size;
|
||||||
|
|
||||||
&.inactive {
|
&.inactive {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
@ -8061,6 +8073,16 @@ noscript {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.copied {
|
||||||
|
border-color: $valid-value-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container account-header (max-width: 372px) {
|
||||||
|
.optional {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue