Update handling of reactions (#277)

* fix: reactions argument in callback

* replace object with Set in reactions

* update reactions in UI
This commit is contained in:
Pavel 2022-06-15 11:30:43 +02:00 committed by GitHub
parent c50b32546b
commit 83777e9759
No known key found for this signature in database
GPG Key ID: 0EB8D75C775AB6F1
8 changed files with 74 additions and 80 deletions

View File

@ -104,7 +104,7 @@ class Client {
}
public handleWakuMessage = (wakuMessage: WakuMessage): void => {
handleWakuMessage(wakuMessage, this, this.community, this.account)
handleWakuMessage(wakuMessage, this, this.community)
}
}

View File

@ -277,7 +277,7 @@ export class Chat {
public handleEmojiReaction = (
messageId: string,
reaction: EmojiReaction,
isMe: boolean
publicKey: string
) => {
let messageIndex = this.messages.length
while (--messageIndex >= 0) {
@ -295,7 +295,7 @@ export class Chat {
this.messages[messageIndex].reactions = getReactions(
reaction,
this.messages[messageIndex].reactions,
isMe
publicKey
)
this.emitMessages(this.messages)
@ -379,16 +379,30 @@ export class Chat {
public sendReaction = async (
chatId: string,
messageId: string,
reaction: EmojiReaction.Type
reaction: keyof ChatMessage['reactions']
) => {
if (!this.client.account) {
throw new Error('Account not initialized')
}
const message = this.getMessage(messageId)
if (!message) {
throw new Error('Message not found')
}
const retracted = message.reactions[reaction].has(
this.client.account.publicKey
)
const payload = EmojiReaction.encode({
clock: BigInt(Date.now()),
chatId: chatId,
messageType: 'COMMUNITY_CHAT',
grant: new Uint8Array([]),
messageType: 'COMMUNITY_CHAT' as MessageType,
messageId,
retracted: false,
type: reaction,
retracted,
grant: new Uint8Array([]),
})
await this.client.sendWakuMessage(

View File

@ -3,40 +3,25 @@ import type { EmojiReaction } from '../../../protos/emoji-reaction'
type Reaction = Exclude<`${EmojiReaction.Type}`, 'UNKNOWN_EMOJI_REACTION_TYPE'>
export type Reactions = {
[key in Reaction]: {
count: number
me: boolean
}
[key in Reaction]: Set<string>
}
export function getReactions(
reaction: EmojiReaction,
reactions: Reactions,
isMe: boolean
) {
publicKey: string
): Reactions {
const { type, retracted } = reaction
if (type === 'UNKNOWN_EMOJI_REACTION_TYPE') {
return reactions
}
const _reaction = {
count: reactions[type].count,
me: reactions[type].me,
}
if (retracted && _reaction.count !== 0) {
_reaction.count -= 1
if (retracted) {
reactions[type].delete(publicKey)
} else {
_reaction.count += 1
reactions[type].add(publicKey)
}
if (isMe) {
_reaction.me = retracted ? false : true
}
return {
...reactions,
[type]: _reaction,
}
return reactions
}

View File

@ -15,7 +15,6 @@ import { recoverPublicKey } from '../../utils/recover-public-key'
import { getChatUuid } from './get-chat-uuid'
import { mapChatMessage } from './map-chat-message'
import type { Account } from '../../account'
import type { Client } from '../../client'
import type { Community } from './community'
import type { WakuMessage } from 'js-waku'
@ -24,8 +23,7 @@ export function handleWakuMessage(
wakuMessage: WakuMessage,
// state
client: Client,
community: Community,
account?: Account
community: Community
): void {
// decode (layers)
if (!wakuMessage.payload) {
@ -151,11 +149,14 @@ export function handleWakuMessage(
const messageId = decodedPayload.messageId
const chatUuid = getChatUuid(decodedPayload.chatId)
const isMe = account?.publicKey === `0x${bytesToHex(publicKey)}`
community.chats
.get(chatUuid)
?.handleEmojiReaction(messageId, decodedPayload, isMe)
const chat = community.chats.get(chatUuid)
chat?.handleEmojiReaction(
messageId,
decodedPayload,
`0x${bytesToHex(publicKey)}`
)
success = true

View File

@ -6,40 +6,24 @@ export function mapChatMessage(
props: {
messageId: string
chatUuid: string
publicKey: string
}
): ChatMessage {
const { messageId, chatUuid } = props
const { messageId, chatUuid, publicKey } = props
const message = {
...decodedMessage,
messageId,
chatUuid,
pinned: false,
signer: publicKey,
reactions: {
THUMBS_UP: {
count: 0,
me: false,
},
THUMBS_DOWN: {
count: 0,
me: false,
},
LOVE: {
count: 0,
me: false,
},
LAUGH: {
count: 0,
me: false,
},
SAD: {
count: 0,
me: false,
},
ANGRY: {
count: 0,
me: false,
},
THUMBS_UP: new Set<string>(),
THUMBS_DOWN: new Set<string>(),
LOVE: new Set<string>(),
LAUGH: new Set<string>(),
SAD: new Set<string>(),
ANGRY: new Set<string>(),
},
}

View File

@ -1,5 +1,6 @@
import React from 'react'
import { useAccount } from '~/src/protocol'
import { styled } from '~/src/styles/config'
import { Flex, Image, Popover, PopoverTrigger } from '~/src/system'
@ -43,18 +44,22 @@ export const emojis: Record<Reaction, { url: string; symbol: string }> = {
export const ReactionPopover = (props: Props) => {
const { reactions, children, onClick, ...popoverProps } = props
const { account } = useAccount()
return (
<PopoverTrigger {...popoverProps}>
{children}
<Popover side="top" align="center" sideOffset={6}>
<Flex gap={1} css={{ padding: 8 }}>
{Object.entries(emojis).map(([type, emoji]) => {
const reaction = reactions[type]
const value = reactions[type]
const me = account ? value.has('0x' + account.publicKey) : false
return (
<Button
key={type}
onClick={() => onClick(reaction as Reaction)}
active={reaction.me}
onClick={() => onClick(type)}
active={me}
aria-label={`React with ${emoji.symbol}`}
>
<Image width={30} src={emoji.url} alt={emoji.symbol} />

View File

@ -84,7 +84,9 @@ export const ChatMessage = (props: Props) => {
}
const handleReaction = (reaction: Reaction) => {
client.community.chats.get(chatId).sendReaction(chatId, messageId, reaction)
client.community
.getChatById(chatId)
.sendReaction(chatId, messageId, reaction)
}
const handleReplyClick = () => {

View File

@ -2,7 +2,7 @@ import React from 'react'
import { emojis, ReactionPopover } from '~/src/components/reaction-popover'
import { ReactionIcon } from '~/src/icons/reaction-icon'
import { Reaction } from '~/src/protocol/use-messages'
import { useAccount } from '~/src/protocol'
import { styled } from '~/src/styles/config'
import { Flex, Image, Text } from '~/src/system'
@ -16,9 +16,7 @@ interface Props {
export const MessageReactions = (props: Props) => {
const { reactions, onClick } = props
const hasReaction = Object.values(reactions).some(
reaction => reaction.count !== 0
)
const hasReaction = Object.values(reactions).some(value => value.size > 0)
if (hasReaction === false) {
return null
@ -26,12 +24,12 @@ export const MessageReactions = (props: Props) => {
return (
<Flex align="center" css={{ paddingTop: 6 }} gap={1}>
{Object.entries(reactions).map(([reaction, value]) => (
{Object.entries(emojis).map(([type, emoji]) => (
<Reaction
key={reaction}
emoji={emojis[reaction as Reaction]}
reaction={value}
onClick={() => onClick(reaction as Reaction)}
key={type}
emoji={emoji}
value={reactions[type]}
onClick={() => onClick(type)}
/>
))}
@ -59,25 +57,30 @@ interface ReactionProps {
url: string
symbol: string
}
reaction: Props['reactions']['smile']
value: Props['reactions']['HEART']
onClick: VoidFunction
}
const Reaction = (props: ReactionProps) => {
const { emoji, reaction, onClick } = props
const { emoji, value, onClick } = props
if (reaction.count === 0) {
const { account } = useAccount()
const count = value.size
const me = account ? value.has('0x' + account.publicKey) : false
if (value.size === 0) {
return null
}
return (
<Button
onClick={onClick}
active={reaction.me}
aria-label={`${emoji.symbol}, ${reaction.count} reaction, press to react`}
active={me}
aria-label={`${emoji.symbol}, ${count} reaction, press to react`}
>
<Image width={14} src={emoji.url} alt={emoji.symbol} />
<Text size="12">{reaction.count}</Text>
<Text size="12">{count}</Text>
</Button>
)
}