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:
parent
562d42b09e
commit
a666d93844
|
@ -104,7 +104,7 @@ class Client {
|
|||
}
|
||||
|
||||
public handleWakuMessage = (wakuMessage: WakuMessage): void => {
|
||||
handleWakuMessage(wakuMessage, this, this.community, this.account)
|
||||
handleWakuMessage(wakuMessage, this, this.community)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>(),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue