box-ui-elements-mlh
Version:
190 lines (181 loc) • 8.7 kB
Flow
// @flow
import * as React from 'react';
import classNames from 'classnames';
import getProp from 'lodash/get';
import noop from 'lodash/noop';
import TetherComponent from 'react-tether';
import ActivityError from '../common/activity-error';
import ActivityMessage from '../common/activity-message';
import ActivityTimestamp from '../common/activity-timestamp';
import AnnotationActivityLink from './AnnotationActivityLink';
import AnnotationActivityMenu from './AnnotationActivityMenu';
import Avatar from '../Avatar';
import CommentForm from '../comment-form/CommentForm';
import DeleteConfirmation from '../common/delete-confirmation';
import Media from '../../../../components/media';
import messages from './messages';
import SelectableActivityCard from '../SelectableActivityCard';
import UserLink from '../common/user-link';
import { ACTIVITY_TARGETS } from '../../../common/interactionTargets';
import { PLACEHOLDER_USER } from '../../../../constants';
import type { Annotation, AnnotationPermission } from '../../../../common/types/feed';
import type { GetAvatarUrlCallback, GetProfileUrlCallback } from '../../../common/flowTypes';
import type { SelectorItems, User } from '../../../../common/types/core';
import './AnnotationActivity.scss';
type Props = {
currentUser?: User,
getAvatarUrl: GetAvatarUrlCallback,
getMentionWithQuery?: (searchStr: string) => void,
getUserProfileUrl?: GetProfileUrlCallback,
isCurrentVersion: boolean,
item: Annotation,
mentionSelectorContacts?: SelectorItems<User>,
onDelete?: ({ id: string, permissions: AnnotationPermission }) => any,
onEdit?: (id: string, text: string, permissions: AnnotationPermission) => void,
onSelect?: (annotation: Annotation) => any,
};
const AnnotationActivity = ({
currentUser,
item,
getAvatarUrl,
getMentionWithQuery,
getUserProfileUrl,
isCurrentVersion,
mentionSelectorContacts,
onDelete = noop,
onEdit = noop,
onSelect = noop,
}: Props) => {
const [isConfirmingDelete, setIsConfirmingDelete] = React.useState(false);
const [isEditing, setIsEditing] = React.useState(false);
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
const { created_at, created_by, description, error, file_version, id, isPending, permissions = {}, target } = item;
const { can_delete: canDelete, can_edit: canEdit } = permissions;
const isFileVersionUnavailable = file_version === null;
const isCardDisabled = !!error || isConfirmingDelete || isMenuOpen || isEditing || isFileVersionUnavailable;
const isMenuVisible = (canDelete || canEdit) && !isPending;
const handleDelete = (): void => setIsConfirmingDelete(true);
const handleDeleteCancel = (): void => setIsConfirmingDelete(false);
const handleDeleteConfirm = (): void => onDelete({ id, permissions });
const handleEdit = (): void => setIsEditing(true);
const handleFormCancel = (): void => setIsEditing(false);
const handleFormSubmit = ({ text }): void => {
setIsEditing(false);
onEdit(id, text, permissions);
};
const handleMenuClose = (): void => setIsMenuOpen(false);
const handleMenuOpen = (): void => setIsMenuOpen(true);
const handleMouseDown = (event: MouseEvent) => {
if (isCardDisabled) {
return;
}
// Prevents document event handlers from executing because box-annotations relies on
// detecting mouse events on the document outside of annotation targets to determine when to
// deselect annotations
event.stopPropagation();
};
const handleSelect = () => onSelect(item);
const createdAtTimestamp = new Date(created_at).getTime();
const createdByUser = created_by || PLACEHOLDER_USER;
const linkMessage = isCurrentVersion ? messages.annotationActivityPageItem : messages.annotationActivityVersionLink;
const linkValue = isCurrentVersion ? target.location.value : getProp(file_version, 'version_number');
const message = (description && description.message) || '';
const activityLinkMessage = isFileVersionUnavailable
? messages.annotationActivityVersionUnavailable
: { ...linkMessage, values: { number: linkValue } };
const tetherProps = {
attachment: 'top right',
className: 'bcs-AnnotationActivity-deleteConfirmationModal',
constraints: [{ to: 'scrollParent', attachment: 'together' }],
targetAttachment: 'bottom right',
};
return (
<>
<SelectableActivityCard
className="bcs-AnnotationActivity"
data-resin-feature="annotations"
data-resin-iscurrent={isCurrentVersion}
data-resin-itemid={id}
data-resin-target="annotationButton"
isDisabled={isCardDisabled}
onMouseDown={handleMouseDown}
onSelect={handleSelect}
>
<Media
className={classNames('bcs-AnnotationActivity-media', {
'bcs-is-pending': isPending || error,
})}
>
<Media.Figure>
<Avatar getAvatarUrl={getAvatarUrl} user={createdByUser} />
</Media.Figure>
<Media.Body>
<div className="bcs-AnnotationActivity-headline">
<UserLink
data-resin-target={ACTIVITY_TARGETS.PROFILE}
getUserProfileUrl={getUserProfileUrl}
id={createdByUser.id}
name={createdByUser.name}
/>
</div>
<div className="bcs-AnnotationActivity-timestamp">
<ActivityTimestamp date={createdAtTimestamp} />
<AnnotationActivityLink
className="bcs-AnnotationActivity-link"
data-resin-target="annotationLink"
id={id}
isDisabled={isFileVersionUnavailable}
message={activityLinkMessage}
onClick={handleSelect}
/>
</div>
{isEditing && currentUser ? (
<CommentForm
className="bcs-AnnotationActivity-editor"
entityId={id}
getAvatarUrl={getAvatarUrl}
getMentionWithQuery={getMentionWithQuery}
isEditing={isEditing}
isOpen={isEditing}
mentionSelectorContacts={mentionSelectorContacts}
onCancel={handleFormCancel}
updateComment={handleFormSubmit}
user={currentUser}
tagged_message={message}
/>
) : (
<ActivityMessage id={id} tagged_message={message} getUserProfileUrl={getUserProfileUrl} />
)}
</Media.Body>
</Media>
{/* $FlowFixMe */}
{error ? <ActivityError {...error} /> : null}
</SelectableActivityCard>
<TetherComponent {...tetherProps}>
{isMenuVisible && (
<AnnotationActivityMenu
canDelete={canDelete}
canEdit={canEdit}
className="bcs-AnnotationActivity-menu"
id={id}
isDisabled={isConfirmingDelete}
onDelete={handleDelete}
onEdit={handleEdit}
onMenuClose={handleMenuClose}
onMenuOpen={handleMenuOpen}
/>
)}
{isConfirmingDelete && (
<DeleteConfirmation
data-resin-component={ACTIVITY_TARGETS.ANNOTATION_OPTIONS}
isOpen={isConfirmingDelete}
message={messages.annotationActivityDeletePrompt}
onDeleteCancel={handleDeleteCancel}
onDeleteConfirm={handleDeleteConfirm}
/>
)}
</TetherComponent>
</>
);
};
export default AnnotationActivity;