Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit 40df03a

Browse files
authored
Merge pull request #1600 from ecency/feature/reply-to-public-message
Chats: public messages reply
2 parents 5bf2b05 + 4b3ac0d commit 40df03a

File tree

8 files changed

+102
-30
lines changed

8 files changed

+102
-30
lines changed

src/common/features/chats/components/chat-channel-messages.tsx

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import useDebounce from "react-use/lib/useDebounce";
2323
import { useCommunityCache } from "../../../core";
2424
import { ROLES } from "../../../store/communities";
2525
import { ForwardMessageDialog } from "./forward-message-dialog";
26-
import { UilMessage } from "@iconscout/react-unicons";
26+
import { UilCommentAltMessage, UilMessage } from "@iconscout/react-unicons";
27+
import { usePersistentReplyToMessage } from "../hooks";
2728

2829
interface Props {
2930
publicMessages: PublicMessage[];
@@ -40,6 +41,7 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }:
4041
const [currentInteractingMessageId, setCurrentInteractingMessageId] = useState<string>();
4142
const [needFetchNextPage, setNeedFetchNextPage] = useState(false);
4243
const [forwardingMessage, setForwardingMessage] = useState<Message>();
44+
const [_, setReply] = usePersistentReplyToMessage(currentChannel);
4345

4446
const { data: community } = useCommunityCache(currentChannel?.communityName);
4547
const { data: joinedCommunityTeamKeys, isSuccess: isJoinedCommunityTeamKeysFetched } =
@@ -107,11 +109,7 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }:
107109
type={message.creator !== publicKey ? "receiver" : "sender"}
108110
message={message}
109111
isSameUser={checkContiguousMessage(message, i, publicMessages)}
110-
onContextMenu={() => {
111-
if (isCommunityTeamMember) {
112-
setCurrentInteractingMessageId(message.id);
113-
}
114-
}}
112+
onContextMenu={() => setCurrentInteractingMessageId(message.id)}
115113
onAppear={() =>
116114
setTimeout(
117115
() =>
@@ -137,18 +135,35 @@ export function ChatsChannelMessages({ publicMessages, currentChannel, isPage }:
137135
onClick={() => setForwardingMessage(message)}
138136
/>
139137
<DropdownItemWithIcon
140-
icon={isHideMessageLoading ? <Spinner className="w-3.5 h-3.5" /> : hideSvg}
141-
label={_t("chat.hide-message")}
142-
onClick={() => hideMessage({ messageId: message.id, status: 0 })}
138+
icon={<UilCommentAltMessage />}
139+
label={_t("chat.reply")}
140+
onClick={() => setReply(message)}
143141
/>
144-
{message.creator !== publicKey && (
145-
<DropdownItemWithIcon
146-
icon={
147-
isUserMutingLoading ? <Spinner className="w-3.5 h-3.5" /> : removeUserSvg
148-
}
149-
label={_t("chat.block-author")}
150-
onClick={() => muteUserInChannel({ pubkey: message.creator, status: 0 })}
151-
/>
142+
{isCommunityTeamMember && (
143+
<>
144+
<DropdownItemWithIcon
145+
icon={
146+
isHideMessageLoading ? <Spinner className="w-3.5 h-3.5" /> : hideSvg
147+
}
148+
label={_t("chat.hide-message")}
149+
onClick={() => hideMessage({ messageId: message.id, status: 0 })}
150+
/>
151+
{message.creator !== publicKey && (
152+
<DropdownItemWithIcon
153+
icon={
154+
isUserMutingLoading ? (
155+
<Spinner className="w-3.5 h-3.5" />
156+
) : (
157+
removeUserSvg
158+
)
159+
}
160+
label={_t("chat.block-author")}
161+
onClick={() =>
162+
muteUserInChannel({ pubkey: message.creator, status: 0 })
163+
}
164+
/>
165+
)}
166+
</>
152167
)}
153168
</DropdownMenu>
154169
</Dropdown>

src/common/features/chats/components/chat-input/chat-input.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import Gallery from "../../../../components/gallery";
2525
import useWindowSize from "react-use/lib/useWindowSize";
2626
import "./_chats.scss";
2727
import { useMappedStore } from "../../../../store/use-mapped-store";
28-
import { ChatReplyDirectMessage } from "../reply-to-messages";
28+
import { ChatReplyDirectMessage, ChatReplyPublicMessage } from "../reply-to-messages";
2929
import { useChatInputSubmit } from "./hooks";
3030

3131
interface Props {
@@ -104,6 +104,7 @@ export function ChatInput({ currentChannel, currentContact }: Props) {
104104
) : (
105105
<>
106106
{currentContact && <ChatReplyDirectMessage currentContact={currentContact} />}
107+
{currentChannel && <ChatReplyPublicMessage currentChannel={currentChannel} />}
107108
{showGifPicker && (
108109
<GifPicker
109110
rootRef={gifPickerRef}

src/common/features/chats/components/chat-message-item/chat-message-replied-label.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import { classNameObject } from "../../../../helper/class-name-object";
2-
import React from "react";
3-
import { DirectContact, Message } from "@ecency/ns-query";
2+
import React, { useMemo } from "react";
3+
import { Message, useNostrGetUserProfileQuery } from "@ecency/ns-query";
44
import { useFocusOnMessageById } from "./hooks";
55

66
interface Props {
77
message: Message;
8-
currentContact?: DirectContact;
98
type: "receiver" | "sender";
109
}
1110

12-
export function ChatMessageRepliedLabel({ message, currentContact, type }: Props) {
11+
export function ChatMessageRepliedLabel({ message, type }: Props) {
1312
const { focus } = useFocusOnMessageById(message.parentMessage?.id);
1413

15-
return message.parentMessage && currentContact ? (
14+
const { data: nostrUserProfiles } = useNostrGetUserProfileQuery(message?.creator);
15+
const profile = useMemo(
16+
() => nostrUserProfiles?.find((p) => p.creator === message?.creator),
17+
[nostrUserProfiles, message?.creator]
18+
);
19+
20+
return message.parentMessage ? (
1621
<div
1722
className={classNameObject({
1823
"rounded-b-xl py-1 px-3 mb-1.5 truncate cursor-pointer": true,
@@ -22,7 +27,7 @@ export function ChatMessageRepliedLabel({ message, currentContact, type }: Props
2227
})}
2328
onClick={() => focus()}
2429
>
25-
<div className="text-xs font-semibold">{currentContact?.name}</div>
30+
<div className="text-xs font-semibold">{profile?.name}</div>
2631
<div className="text-xs">{message.parentMessage.content}</div>
2732
</div>
2833
) : (

src/common/features/chats/components/chat-message-item/index.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,7 @@ export function ChatMessageItem({
153153
isEmoji={isEmoji}
154154
/>
155155
<ChatMessageForwardingLabel message={message} type={type} />
156-
<ChatMessageRepliedLabel
157-
message={message}
158-
type={type}
159-
currentContact={currentContact}
160-
/>
156+
<ChatMessageRepliedLabel message={message} type={type} />
161157
<div
162158
className="sender-message-content [&>img]:rounded-xl"
163159
dangerouslySetInnerHTML={{ __html: renderedPreview }}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.chat-reply-to-message {
2+
display: grid;
3+
grid-template-columns: min-content 1fr min-content;
4+
align-items: center;
5+
6+
&-text {
7+
@apply truncate;
8+
}
9+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { _t } from "../../../../i18n";
2+
import { Button } from "@ui/button";
3+
import { UilMessage, UilMultiply } from "@iconscout/react-unicons";
4+
import React, { useMemo } from "react";
5+
import { usePersistentReplyToMessage } from "../../hooks";
6+
import { Channel, useNostrGetUserProfileQuery } from "@ecency/ns-query";
7+
import { ChatReplyToMessageLayout } from "./chat-reply-to-message-layout";
8+
9+
interface Props {
10+
currentChannel?: Channel;
11+
}
12+
13+
export function ChatReplyPublicMessage({ currentChannel }: Props) {
14+
const [reply, _, clearReply] = usePersistentReplyToMessage(currentChannel);
15+
16+
const { data: nostrUserProfiles } = useNostrGetUserProfileQuery(reply?.creator);
17+
const profile = useMemo(
18+
() => nostrUserProfiles?.find((p) => p.creator === reply?.creator),
19+
[nostrUserProfiles, reply?.creator]
20+
);
21+
22+
return reply ? (
23+
<ChatReplyToMessageLayout>
24+
<div className="chat-reply-to-message w-full gap-4 px-3 py-2">
25+
<UilMessage className="text-blue-dark-sky" />
26+
<div className="chat-reply-to-message-text text-sm flex flex-col border-l-[0.25rem] border-blue-dark-sky pl-2">
27+
<div className="text-xs text-blue-dark-sky">
28+
{_t("chat.reply-to", { account: profile?.name })}
29+
</div>
30+
<div>{reply.content}</div>
31+
</div>
32+
<Button
33+
appearance="gray-link"
34+
size="sm"
35+
noPadding={true}
36+
icon={<UilMultiply />}
37+
onClick={() => clearReply()}
38+
/>
39+
</div>
40+
</ChatReplyToMessageLayout>
41+
) : (
42+
<></>
43+
);
44+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React, { HTMLProps } from "react";
2+
import "./_chat-reply-message.scss";
23

34
export function ChatReplyToMessageLayout(props: HTMLProps<HTMLDivElement>) {
45
return (
56
<div
67
{...props}
7-
className="bg-white bg-opacity-50 backdrop-blur border-t border-[--border-color] z-10 left-[-0.5rem] overflow-x-auto w-[calc(100%+0.5rem)] absolute bottom-[100%] flex gap-4"
8+
className="bg-white border-t border-[--border-color] z-10 left-[-0.5rem] overflow-x-auto w-[calc(100%+0.5rem)] absolute bottom-[100%] flex gap-4"
89
/>
910
);
1011
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./chat-reply-direct-message";
2+
export * from "./chat-reply-public-message";

0 commit comments

Comments
 (0)