Skip to content

Commit

Permalink
ui: add comment ui (pinterest#1257)
Browse files Browse the repository at this point in the history
* ui: add ui

* update;

* update

* update

* update

* refactor const

* fix

* fix

* update
  • Loading branch information
meowcodes committed Jun 23, 2023
1 parent a72bd53 commit ce2701e
Show file tree
Hide file tree
Showing 12 changed files with 721 additions and 2 deletions.
3 changes: 3 additions & 0 deletions querybook/webapp/components/DataDoc/DataDocCellControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { copy, sleep, titleize } from 'lib/utils';
import { getShortcutSymbols, KeyMap } from 'lib/utils/keyboard';
import { AsyncButton } from 'ui/AsyncButton/AsyncButton';
import { SoftButton } from 'ui/Button/Button';
import { CommentButton } from 'ui/Comment/CommentButton';
import { Dropdown } from 'ui/Dropdown/Dropdown';
import { IListMenuItem, ListMenu } from 'ui/Menu/ListMenu';

Expand Down Expand Up @@ -144,6 +145,8 @@ export const DataDocCellControl: React.FunctionComponent<IProps> = ({
icon: 'Clipboard',
});
}

rightButtons.push(<CommentButton key="comment" />);
}

if (isEditable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,11 @@
right: 42px;
}
}

.execution-selector-section,
.StatementExecutionPicker {
* {
white-space: nowrap;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const QueryExecutionDuration: React.FunctionComponent<IProps> = ({
}

return (
<StyledText className="flex-row" color="light">
<StyledText className="flex-row mr8" color="light">
{durationDOM}
<span className="mh4">by</span>
<UserName uid={queryExecution.uid} />
Expand Down
7 changes: 6 additions & 1 deletion querybook/webapp/components/UserBadge/UserName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { IUserInfo } from 'const/user';
import { useUser } from 'hooks/redux/useUser';
import { titleize } from 'lib/utils';
import { StyledText } from 'ui/StyledText/StyledText';

import { ICommonUserLoaderProps } from './types';

Expand All @@ -20,7 +21,11 @@ export const UserNameComponent: React.FunctionComponent<
? titleize(userInfo.fullname) || userInfo.username
: '[No Name]';

return <span>{fullName}</span>;
return (
<StyledText className="UserName" cursor="default">
{fullName}
</StyledText>
);
};

export const UserName: React.FunctionComponent<ICommonUserLoaderProps> = ({
Expand Down
19 changes: 19 additions & 0 deletions querybook/webapp/const/comment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as DraftJs from 'draft-js';

export interface IComment {
id: number;
// TODO: clean this
text: string | DraftJs.ContentState;
uid: number;
created_at: number;
updated_at: number;

child_comments?: IComment[];

reactions: IReaction[];
}

export interface IReaction {
reaction: string;
uid: number;
}
73 changes: 73 additions & 0 deletions querybook/webapp/ui/Comment/AddReactionButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as React from 'react';

import { TooltipDirection } from 'const/tooltip';
import { IconButton } from 'ui/Button/IconButton';
import { Popover, PopoverLayout } from 'ui/Popover/Popover';

interface IProps {
uid: number;
popoverLayout?: PopoverLayout;
tooltipPos?: TooltipDirection;
}
interface IEmojiListProps {
onClick: (emoji: string) => void;
}

const emojis = ['👍', '👀', '🙂', '😍', '🙁', '❌', '🤌', '🌟', '🔥', '🩵'];

const EmojiList: React.FunctionComponent<IEmojiListProps> = ({ onClick }) => (
<div className="EmojiList flex-row">
{emojis.map((emoji) => (
<div
key={emoji}
className="Emoji mh8"
onClick={() => onClick(emoji)}
>
{emoji}
</div>
))}
</div>
);

export const AddReactionButton: React.FunctionComponent<IProps> = ({
uid,
popoverLayout = ['bottom', 'right'],
tooltipPos = 'down',
}) => {
const addReactionButtonRef = React.useRef<HTMLAnchorElement>();

const [showEmojis, setShowEmojis] = React.useState(false);

const handleEmojiClick = React.useCallback(
(emoji: string) => {
// TODO: make this work (with backend)
console.log(emoji, 'added by', uid);
},
[uid]
);

return (
<div className="AddReactionButton">
<IconButton
icon="Plus"
invertCircle
size={18}
tooltip="React"
tooltipPos={tooltipPos}
ref={addReactionButtonRef}
onClick={() => setShowEmojis(true)}
/>
{showEmojis ? (
<Popover
onHide={() => setShowEmojis(false)}
anchor={addReactionButtonRef.current}
layout={popoverLayout}
hideArrow
noPadding
>
<EmojiList onClick={handleEmojiClick} />
</Popover>
) : null}
</div>
);
};
82 changes: 82 additions & 0 deletions querybook/webapp/ui/Comment/Comment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as DraftJS from 'draft-js';
import * as React from 'react';
import { useSelector } from 'react-redux';

import { UserAvatar } from 'components/UserBadge/UserAvatar';
import { UserName } from 'components/UserBadge/UserName';
import { IComment } from 'const/comment';
import { fromNow } from 'lib/utils/datetime';
import { IStoreState } from 'redux/store/types';
import { IconButton } from 'ui/Button/IconButton';
import { RichTextEditor } from 'ui/RichTextEditor/RichTextEditor';
import { StyledText } from 'ui/StyledText/StyledText';

import { AddReactionButton } from './AddReactionButton';
import { Reactions } from './Reactions';

interface IProps {
comment: IComment;
editComment: (text: DraftJS.ContentState) => void;
isBeingEdited: boolean;
}

export const Comment: React.FunctionComponent<IProps> = ({
comment,
editComment,
isBeingEdited,
}) => {
const userInfo = useSelector((state: IStoreState) => state.user.myUserInfo);
const { text, uid, created_at: createdAt, reactions } = comment;
const textContentState = React.useMemo(
() => DraftJS.ContentState.createFromText(text as string),
[text]
);

return (
<div className="Comment">
<div className="Comment-top horizontal-space-between">
<div className="Comment-top-left flex-row">
<UserAvatar uid={uid} tiny />
<UserName uid={uid} />
<StyledText size="xsmall" color="lightest" cursor="default">
{fromNow(createdAt)}
</StyledText>
</div>
<div className="Comment-top-right flex-row">
{isBeingEdited ? (
<StyledText
className="Editing-text mr4"
color="accent"
weight="bold"
>
editing
</StyledText>
) : null}
<div className="Comment-top-right-buttons flex-row">
{uid === userInfo.uid && !isBeingEdited ? (
<div className="Comment-edit">
<IconButton
icon="Edit"
invertCircle
size={18}
tooltip="Edit Comment"
tooltipPos="left"
onClick={() =>
editComment(textContentState)
}
/>
</div>
) : null}
<div className="mh8">
<AddReactionButton uid={userInfo.uid} />
</div>
</div>
</div>
</div>
<div className="Comment-text mt4">
<RichTextEditor value={textContentState} readOnly={true} />
{reactions.length ? <Reactions reactions={reactions} /> : null}
</div>
</div>
);
};
34 changes: 34 additions & 0 deletions querybook/webapp/ui/Comment/CommentButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';

import { SoftButton } from 'ui/Button/Button';
import { Popover } from 'ui/Popover/Popover';

import { Comments } from './Comments';

export const CommentButton: React.FunctionComponent = () => {
const [showComments, setShowComments] = React.useState(false);
const commentButtonRef = React.useRef<HTMLAnchorElement>();
return (
<>
<SoftButton
className="block-crud-button"
onClick={() => setShowComments((curr) => !curr)}
icon="MessageSquare"
aria-label="Comments"
data-balloon-pos="left"
ref={commentButtonRef}
/>
{showComments ? (
<Popover
onHide={() => setShowComments(false)}
anchor={commentButtonRef.current}
layout={['bottom', 'right']}
hideArrow
noPadding
>
<Comments />
</Popover>
) : null}
</>
);
};
Loading

0 comments on commit ce2701e

Please sign in to comment.