Merge branch 'reply-popover' into 'develop'

Add popover when trying to reply to Group status

See merge request soapbox-pub/soapbox!2386
develop^2
Chewbacca 2023-03-30 13:23:47 +00:00
commit 01458c3003
5 zmienionych plików z 150 dodań i 14 usunięć

Wyświetl plik

@ -0,0 +1,99 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { Button, Divider, HStack, Popover, Stack, Text } from 'soapbox/components/ui';
import GroupMemberCount from 'soapbox/features/group/components/group-member-count';
import GroupPrivacy from 'soapbox/features/group/components/group-privacy';
import GroupAvatar from '../group-avatar';
import type { Group } from 'soapbox/schemas';
interface IGroupPopoverContainer {
children: React.ReactElement<any, string | React.JSXElementConstructor<any>>
isEnabled: boolean
group: Group
}
const messages = defineMessages({
title: { id: 'group.popover.title', defaultMessage: 'Membership required' },
summary: { id: 'group.popover.summary', defaultMessage: 'You must be a member of the group in order to reply to this status.' },
action: { id: 'group.popover.action', defaultMessage: 'View Group' },
});
const GroupPopover = (props: IGroupPopoverContainer) => {
const { children, group, isEnabled } = props;
const intl = useIntl();
if (!isEnabled) {
return children;
}
return (
<Popover
interaction='click'
referenceElementClassName='cursor-pointer'
content={
<Stack space={4} className='w-80'>
<Stack
className='relative h-60 rounded-lg bg-white dark:border-primary-800 dark:bg-primary-900'
data-testid='group-card'
>
{/* Group Cover Image */}
<Stack grow className='relative basis-1/2 rounded-t-lg bg-primary-100 dark:bg-gray-800'>
{group.header && (
<img
className='absolute inset-0 h-full w-full rounded-t-lg object-cover'
src={group.header}
alt=''
/>
)}
</Stack>
{/* Group Avatar */}
<div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
<GroupAvatar group={group} size={64} withRing />
</div>
{/* Group Info */}
<Stack alignItems='center' justifyContent='end' grow className='basis-1/2 py-4' space={0.5}>
<Text size='lg' weight='bold' dangerouslySetInnerHTML={{ __html: group.display_name_html }} />
<HStack className='text-gray-700 dark:text-gray-600' space={2} wrap>
<GroupPrivacy group={group} />
<GroupMemberCount group={group} />
</HStack>
</Stack>
</Stack>
<Divider />
<Stack space={0.5} className='px-4'>
<Text weight='semibold'>
{intl.formatMessage(messages.title)}
</Text>
<Text theme='muted'>
{intl.formatMessage(messages.summary)}
</Text>
</Stack>
<div className='px-4 pb-4'>
<Link to={`/groups/${group.id}`}>
<Button type='button' theme='secondary' block>
{intl.formatMessage(messages.action)}
</Button>
</Link>
</div>
</Stack>
}
isFlush
children={
<div className='inline-block'>{children}</div>
}
/>
);
};
export default GroupPopover;

Wyświetl plik

@ -24,6 +24,8 @@ import { isLocal, isRemote } from 'soapbox/utils/accounts';
import copy from 'soapbox/utils/copy'; import copy from 'soapbox/utils/copy';
import { getReactForStatus, reduceEmoji } from 'soapbox/utils/emoji-reacts'; import { getReactForStatus, reduceEmoji } from 'soapbox/utils/emoji-reacts';
import GroupPopover from './groups/popover/group-popover';
import type { Menu } from 'soapbox/components/dropdown-menu'; import type { Menu } from 'soapbox/components/dropdown-menu';
import type { Account, Group, Status } from 'soapbox/types/entities'; import type { Account, Group, Status } from 'soapbox/types/entities';
@ -617,14 +619,19 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
grow={space === 'expand'} grow={space === 'expand'}
onClick={e => e.stopPropagation()} onClick={e => e.stopPropagation()}
> >
<StatusActionButton <GroupPopover
title={replyTitle} group={status.group as any}
icon={require('@tabler/icons/message-circle-2.svg')} isEnabled={replyDisabled}
onClick={handleReplyClick} >
count={replyCount} <StatusActionButton
text={withLabels ? intl.formatMessage(messages.reply) : undefined} title={replyTitle}
disabled={replyDisabled} icon={require('@tabler/icons/message-circle-2.svg')}
/> onClick={handleReplyClick}
count={replyCount}
text={withLabels ? intl.formatMessage(messages.reply) : undefined}
disabled={replyDisabled}
/>
</GroupPopover>
{(features.quotePosts && me) ? ( {(features.quotePosts && me) ? (
<DropdownMenu <DropdownMenu

Wyświetl plik

@ -1,18 +1,28 @@
import { import {
arrow, arrow,
autoPlacement,
FloatingArrow, FloatingArrow,
offset, offset,
useClick, useClick,
useDismiss, useDismiss,
useFloating, useFloating,
useHover,
useInteractions, useInteractions,
useTransitionStyles, useTransitionStyles,
} from '@floating-ui/react'; } from '@floating-ui/react';
import clsx from 'clsx';
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
interface IPopover { interface IPopover {
children: React.ReactElement<any, string | React.JSXElementConstructor<any>> children: React.ReactElement<any, string | React.JSXElementConstructor<any>>
/** The content of the popover */
content: React.ReactNode content: React.ReactNode
/** Should we remove padding on the Popover */
isFlush?: boolean
/** Should the popover trigger via click or hover */
interaction?: 'click' | 'hover'
/** Add a class to the reference (trigger) element */
referenceElementClassName?: string
} }
/** /**
@ -22,7 +32,7 @@ interface IPopover {
* of information. * of information.
*/ */
const Popover: React.FC<IPopover> = (props) => { const Popover: React.FC<IPopover> = (props) => {
const { children, content } = props; const { children, content, referenceElementClassName, interaction = 'hover', isFlush = false } = props;
const [isOpen, setIsOpen] = useState<boolean>(false); const [isOpen, setIsOpen] = useState<boolean>(false);
@ -33,6 +43,9 @@ const Popover: React.FC<IPopover> = (props) => {
onOpenChange: setIsOpen, onOpenChange: setIsOpen,
placement: 'top', placement: 'top',
middleware: [ middleware: [
autoPlacement({
allowedPlacements: ['top', 'bottom'],
}),
offset(10), offset(10),
arrow({ arrow({
element: arrowRef, element: arrowRef,
@ -40,8 +53,6 @@ const Popover: React.FC<IPopover> = (props) => {
], ],
}); });
const click = useClick(context);
const dismiss = useDismiss(context);
const { isMounted, styles } = useTransitionStyles(context, { const { isMounted, styles } = useTransitionStyles(context, {
initial: { initial: {
opacity: 0, opacity: 0,
@ -53,8 +64,13 @@ const Popover: React.FC<IPopover> = (props) => {
}, },
}); });
const click = useClick(context, { enabled: interaction === 'click' });
const hover = useHover(context, { enabled: interaction === 'hover' });
const dismiss = useDismiss(context);
const { getReferenceProps, getFloatingProps } = useInteractions([ const { getReferenceProps, getFloatingProps } = useInteractions([
click, click,
hover,
dismiss, dismiss,
]); ]);
@ -63,7 +79,7 @@ const Popover: React.FC<IPopover> = (props) => {
{React.cloneElement(children, { {React.cloneElement(children, {
ref: refs.setReference, ref: refs.setReference,
...getReferenceProps(), ...getReferenceProps(),
className: 'cursor-help', className: clsx(children.props.className, referenceElementClassName),
})} })}
{(isMounted) && ( {(isMounted) && (
@ -75,12 +91,22 @@ const Popover: React.FC<IPopover> = (props) => {
left: x ?? 0, left: x ?? 0,
...styles, ...styles,
}} }}
className='rounded-lg bg-white p-6 shadow-2xl dark:bg-gray-900 dark:ring-2 dark:ring-primary-700' className={
clsx({
'z-40 rounded-lg bg-white shadow-2xl dark:bg-gray-900 dark:ring-2 dark:ring-primary-700': true,
'p-6': !isFlush,
})
}
{...getFloatingProps()} {...getFloatingProps()}
> >
{content} {content}
<FloatingArrow ref={arrowRef} context={context} className='fill-white dark:hidden' /> <FloatingArrow
ref={arrowRef}
context={context}
className='-ml-2 fill-white dark:hidden' /** -ml-2 to fix offcenter arrow */
tipRadius={3}
/>
</div> </div>
)} )}
</> </>

Wyświetl plik

@ -10,6 +10,7 @@ interface IGroupPolicy {
const GroupPrivacy = ({ group }: IGroupPolicy) => ( const GroupPrivacy = ({ group }: IGroupPolicy) => (
<Popover <Popover
referenceElementClassName='cursor-help'
content={ content={
<Stack space={4} alignItems='center' className='w-72'> <Stack space={4} alignItems='center' className='w-72'>
<div className='rounded-full bg-gray-200 p-3 dark:bg-gray-800'> <div className='rounded-full bg-gray-200 p-3 dark:bg-gray-800'>

Wyświetl plik

@ -787,6 +787,9 @@
"group.leave": "Leave Group", "group.leave": "Leave Group",
"group.leave.success": "Left the group", "group.leave.success": "Left the group",
"group.manage": "Manage Group", "group.manage": "Manage Group",
"group.popover.action": "View Group",
"group.popover.summary": "You must be a member of the group in order to reply to this status.",
"group.popover.title": "Membership required",
"group.privacy.locked": "Private", "group.privacy.locked": "Private",
"group.privacy.locked.full": "Private Group", "group.privacy.locked.full": "Private Group",
"group.privacy.locked.info": "Discoverable. Users can join after their request is approved.", "group.privacy.locked.info": "Discoverable. Users can join after their request is approved.",