From 151b80bc9b269996a6876615e96c8881d0ba7fb9 Mon Sep 17 00:00:00 2001 From: Pavel Prichodko <14926950+prichodko@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:37:59 +0200 Subject: [PATCH] feat(react): implement message reactions --- .../src/components/reaction-popover/index.tsx | 89 +++++----- .../src/protocol/use-messages.tsx | 72 ++++++++ .../chat/components/chat-message/actions.tsx | 7 +- .../chat/components/chat-message/index.tsx | 10 +- .../components/chat-message/reactions.tsx | 159 ++++++++---------- packages/status-react/src/routes/index.tsx | 55 ++++++ 6 files changed, 259 insertions(+), 133 deletions(-) create mode 100644 packages/status-react/src/routes/index.tsx diff --git a/packages/status-react/src/components/reaction-popover/index.tsx b/packages/status-react/src/components/reaction-popover/index.tsx index b89933cd..1dd3e768 100644 --- a/packages/status-react/src/components/reaction-popover/index.tsx +++ b/packages/status-react/src/components/reaction-popover/index.tsx @@ -3,63 +3,64 @@ import React from 'react' import { styled } from '~/src/styles/config' import { Flex, Image, Popover, PopoverTrigger } from '~/src/system' +import type { Reaction, Reactions } from '~/src/protocol/use-messages' + interface Props { children: React.ReactElement - onClick: (emoji: string) => void + reactions: Reactions + onClick: (reaction: Reaction) => void open?: boolean onOpenChange?: (open: boolean) => void } +export const emojis: Record = { + heart: { + symbol: '❤️', + url: 'https://twemoji.maxcdn.com/v/latest/svg/2764.svg', + }, + 'thumbs-up': { + symbol: '👍️', + url: 'https://twemoji.maxcdn.com/v/latest/svg/1f44d.svg', + }, + 'thumbs-down': { + symbol: '👎️', + url: 'https://twemoji.maxcdn.com/v/latest/svg/1f44e.svg', + }, + smile: { + symbol: '😆', + url: 'https://twemoji.maxcdn.com/v/latest/svg/1f606.svg', + }, + sad: { + symbol: '😭', + url: 'https://twemoji.maxcdn.com/v/latest/svg/1f62d.svg', + }, + angry: { + symbol: '😡', + url: 'https://twemoji.maxcdn.com/v/latest/svg/1f621.svg', + }, +} + export const ReactionPopover = (props: Props) => { - const { children, onClick, ...popoverProps } = props + const { reactions, children, onClick, ...popoverProps } = props return ( {children} - - - - - - + {Object.entries(reactions).map(([reaction, value]) => { + const emoji = emojis[reaction as Reaction] + return ( + + ) + })} diff --git a/packages/status-react/src/protocol/use-messages.tsx b/packages/status-react/src/protocol/use-messages.tsx index fa059433..19972002 100644 --- a/packages/status-react/src/protocol/use-messages.tsx +++ b/packages/status-react/src/protocol/use-messages.tsx @@ -1,3 +1,18 @@ +export type Reaction = + | 'heart' + | 'thumbs-up' + | 'thumbs-down' + | 'smile' + | 'sad' + | 'angry' + +export type Reactions = { + [key in Reaction]: { + count: number + me: boolean + } +} + interface BaseMessage { id: string type: 'text' | 'image' | 'image-text' @@ -9,6 +24,7 @@ interface BaseMessage { pinned: boolean mention: boolean reply?: TextReply | ImageReply | ImageTextReply + reactions: Reactions } interface TextMessage extends BaseMessage { @@ -68,6 +84,14 @@ export const useMessages = (): Message[] => { owner: false, pinned: true, mention: false, + reactions: { + heart: { count: 0, me: false }, + 'thumbs-up': { count: 0, me: false }, + 'thumbs-down': { count: 0, me: false }, + smile: { count: 0, me: false }, + sad: { count: 0, me: false }, + angry: { count: 0, me: false }, + }, reply: { contact: { name: 'Leila Joyner', @@ -90,6 +114,14 @@ export const useMessages = (): Message[] => { owner: false, pinned: false, mention: false, + reactions: { + heart: { count: 0, me: false }, + 'thumbs-up': { count: 0, me: false }, + 'thumbs-down': { count: 0, me: false }, + smile: { count: 0, me: false }, + sad: { count: 0, me: false }, + angry: { count: 0, me: false }, + }, reply: { contact: { name: 'Leila Joyner', @@ -113,6 +145,14 @@ export const useMessages = (): Message[] => { owner: false, pinned: false, mention: true, + reactions: { + heart: { count: 1, me: false }, + 'thumbs-up': { count: 1, me: false }, + 'thumbs-down': { count: 3, me: true }, + smile: { count: 0, me: false }, + sad: { count: 0, me: false }, + angry: { count: 0, me: false }, + }, reply: { contact: { name: 'Leila Joyner', @@ -137,6 +177,14 @@ export const useMessages = (): Message[] => { owner: false, pinned: false, mention: false, + reactions: { + heart: { count: 0, me: false }, + 'thumbs-up': { count: 0, me: false }, + 'thumbs-down': { count: 0, me: false }, + smile: { count: 0, me: false }, + sad: { count: 0, me: false }, + angry: { count: 0, me: false }, + }, }, { id: '5', @@ -150,6 +198,14 @@ export const useMessages = (): Message[] => { owner: true, pinned: false, mention: false, + reactions: { + heart: { count: 0, me: false }, + 'thumbs-up': { count: 0, me: false }, + 'thumbs-down': { count: 0, me: false }, + smile: { count: 0, me: false }, + sad: { count: 1, me: false }, + angry: { count: 1, me: true }, + }, reply: { contact: { name: 'Leila Joyner', @@ -174,6 +230,14 @@ export const useMessages = (): Message[] => { owner: false, pinned: false, mention: false, + reactions: { + heart: { count: 0, me: false }, + 'thumbs-up': { count: 10, me: true }, + 'thumbs-down': { count: 3, me: false }, + smile: { count: 0, me: false }, + sad: { count: 0, me: false }, + angry: { count: 0, me: false }, + }, }, { id: '5', @@ -187,6 +251,14 @@ export const useMessages = (): Message[] => { owner: true, pinned: false, mention: false, + reactions: { + heart: { count: 0, me: false }, + 'thumbs-up': { count: 0, me: false }, + 'thumbs-down': { count: 0, me: false }, + smile: { count: 0, me: false }, + sad: { count: 0, me: false }, + angry: { count: 0, me: false }, + }, }, ] } diff --git a/packages/status-react/src/routes/chat/components/chat-message/actions.tsx b/packages/status-react/src/routes/chat/components/chat-message/actions.tsx index 00154edd..898db3c7 100644 --- a/packages/status-react/src/routes/chat/components/chat-message/actions.tsx +++ b/packages/status-react/src/routes/chat/components/chat-message/actions.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React from 'react' import { ReactionPopover } from '~/src/components/reaction-popover' import { PencilIcon } from '~/src/icons/pencil-icon' @@ -15,6 +15,8 @@ import { Tooltip, } from '~/src/system' +import type { Reactions } from '~/src/protocol/use-messages' + interface Props { owner: boolean pinned: boolean @@ -22,6 +24,7 @@ interface Props { onEditClick: () => void reacting: boolean onReactingChange: (reacting: boolean) => void + reactions: Reactions } export const Actions = (props: Props) => { @@ -32,11 +35,13 @@ export const Actions = (props: Props) => { onEditClick, reacting, onReactingChange, + reactions, } = props return ( { diff --git a/packages/status-react/src/routes/chat/components/chat-message/index.tsx b/packages/status-react/src/routes/chat/components/chat-message/index.tsx index e4f73108..7dd6bcab 100644 --- a/packages/status-react/src/routes/chat/components/chat-message/index.tsx +++ b/packages/status-react/src/routes/chat/components/chat-message/index.tsx @@ -21,7 +21,7 @@ import { import { ChatInput } from '../chat-input' import { Actions } from './actions' import { MessageReply } from './message-reply' -import { Reactions } from './reactions' +import { MessageReactions } from './reactions' import type { Message } from '~/src/protocol/use-messages' @@ -53,7 +53,7 @@ interface Props { export const ChatMessage = (props: Props) => { const { message } = props - const { type, contact, owner, mention, pinned, reply } = message + const { type, contact, owner, mention, pinned, reply, reactions } = message const [editing, setEditing] = useState(false) const [reacting, setReacting] = useState(false) @@ -198,7 +198,10 @@ export const ChatMessage = (props: Props) => { {renderMessage()} - + @@ -209,6 +212,7 @@ export const ChatMessage = (props: Props) => { onReplyClick={handleReplyClick} reacting={reacting} onReactingChange={setReacting} + reactions={reactions} /> diff --git a/packages/status-react/src/routes/chat/components/chat-message/reactions.tsx b/packages/status-react/src/routes/chat/components/chat-message/reactions.tsx index 3d5e9a7b..f058ae72 100644 --- a/packages/status-react/src/routes/chat/components/chat-message/reactions.tsx +++ b/packages/status-react/src/routes/chat/components/chat-message/reactions.tsx @@ -1,115 +1,104 @@ import React from 'react' -import { ReactionPopover } from '~/src/components/reaction-popover' +import { emojis, ReactionPopover } from '~/src/components/reaction-popover' import { ReactionIcon } from '~/src/icons/reaction-icon' +import { Reaction } from '~/src/protocol/use-messages' import { styled } from '~/src/styles/config' -import { Flex, Image } from '~/src/system' +import { Flex, Image, Text } from '~/src/system' + +import type { Reactions } from '~/src/protocol/use-messages' interface Props { - onClick: (reaction: string) => void + reactions: Reactions + onClick: (reaction: Reaction) => void } -export const Reactions = (props: Props) => { - const { onClick } = props +export const MessageReactions = (props: Props) => { + const { reactions, onClick } = props + + const hasReaction = Object.values(reactions).some( + reaction => reaction.count !== 0 + ) + + if (hasReaction === false) { + return null + } return ( - - - - - - - - { - console.log(emoji) - }} - > - + ))} + + + + + ) } +const AddReactionButton = styled('button', { + color: '$gray-1', + width: 16, + height: 16, + + "&[aria-expanded='true']": { + color: '$primary-1', + }, +}) + +interface ReactionProps { + emoji: { + url: string + symbol: string + } + reaction: Props['reactions']['smile'] + onClick: VoidFunction +} + +const Reaction = (props: ReactionProps) => { + const { emoji, reaction, onClick } = props + + if (reaction.count === 0) { + return null + } + + return ( + + ) +} + const Button = styled('button', { - padding: 2, + padding: '0px 8px 0px 3px', boxShadow: '0px 4px 12px rgba(0, 34, 51, 0.08)', background: '$accent-8', borderRadius: '2px 10px 10px 10px', minWidth: 36, height: 20, display: 'inline-flex', + gap: 4, alignItems: 'center', variants: { active: { - true: {}, + true: { + border: '1px solid $primary-1', + background: '$primary-3', + }, }, }, }) diff --git a/packages/status-react/src/routes/index.tsx b/packages/status-react/src/routes/index.tsx new file mode 100644 index 00000000..40f8a8a8 --- /dev/null +++ b/packages/status-react/src/routes/index.tsx @@ -0,0 +1,55 @@ +import React from 'react' + +import { BrowserRouter, Route, Routes } from 'react-router-dom' + +import { AppProvider } from '~/src/contexts/app-context' +import { DialogProvider } from '~/src/contexts/dialog-context' +import { styled } from '~/src/styles/config' + +import { MainSidebar } from '../components/main-sidebar' +import { Box } from '../system' +import { Chat } from './chat' +import { NewChat } from './new-chat' + +import type { Config } from '~/src/types/config' + +type Props = Config + +export const Community = (props: Props) => { + const { + // theme, + // environment, + // publicKey, + router: Router = BrowserRouter, + } = props + + const { options } = props + + return ( + + + + + + {options.enableMembers && } + + } /> + } /> + + + + + + + ) +} + +export type { Props as CommunityProps } + +const Wrapper = styled('div', { + position: 'relative', + width: '100%', + height: '100%', + display: 'flex', + alignItems: 'stretch', +})