Skip to content

Commit bc1cf12

Browse files
committed
feat: handle EditMessageInput submit
1 parent 1f0a83f commit bc1cf12

File tree

6 files changed

+330
-52
lines changed

6 files changed

+330
-52
lines changed

src/Message/style/message.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
styled,
44
theme,
55
} from "../../Stitches/stitches.config";
6-
import { Link } from "../../markdown/render/elements";
76
import SvgFromUrl from "../../SvgFromUrl";
7+
import { Link } from "../../markdown/render/elements";
88

99
export const SmallTimestamp = styled.withConfig({
1010
displayName: "small-timestamp",
@@ -104,7 +104,7 @@ export const MessageEditor = styled.withConfig({
104104
paddingBottom: theme.space.xl,
105105
paddingLeft: theme.space.xxl,
106106
paddingRight: theme.space.large,
107-
backgroundColor: "rgb(56, 58, 64)",
107+
backgroundColor: theme.colors.primaryOpacity10,
108108
// backgroundColor: theme.colors.primaryOpacity10,
109109
outline: "none",
110110
borderRadius: 8,
@@ -113,7 +113,6 @@ export const MessageEditor = styled.withConfig({
113113
fontWeight: 400,
114114
lineHeight: "1.375rem",
115115
width: "100%",
116-
whiteSpace: "pre-wrap",
117116
});
118117

119118
export const AutomodHeaderText = styled.withConfig({
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import type {
2+
APIMessageInteraction,
3+
APIRole,
4+
APIUser,
5+
Snowflake,
6+
} from "discord-api-types/v10";
7+
import { MessageType } from "discord-api-types/v10";
8+
import Moment from "moment";
9+
import React, { memo, useMemo, useRef } from "react";
10+
import ChatTag from "../../ChatTag";
11+
import Content from "../../Content";
12+
import Tooltip from "../../Tooltip";
13+
import { useConfig } from "../../core/ConfigContext";
14+
import type { ChatMessage } from "../../types";
15+
import type { GetAvatarOptions } from "../../utils/getAvatar";
16+
import getAvatar from "../../utils/getAvatar";
17+
import getDisplayName from "../../utils/getDisplayName";
18+
import LargeTimestamp from "../LargeTimestamp";
19+
import MessageAuthor from "../MessageAuthor";
20+
import * as Styles from "../style/message";
21+
22+
interface ReplyInfoProps {
23+
channelId: Snowflake;
24+
referencedMessage: ChatMessage["referenced_message"];
25+
mentioned?: boolean;
26+
interaction: APIMessageInteraction | undefined;
27+
isContextMenuInteraction?: boolean;
28+
}
29+
30+
function getMiniAvatarUrl(user: APIUser) {
31+
const getAvatarSettings: GetAvatarOptions = {
32+
size: 16,
33+
};
34+
35+
return getAvatar(user, getAvatarSettings);
36+
}
37+
38+
const FLAG_CROSSPOST = 1 << 1;
39+
40+
const ReplyInfo = memo((props: ReplyInfoProps) => {
41+
const user = props.interaction
42+
? props.interaction.user
43+
: props.referencedMessage?.author;
44+
45+
const { resolveRole, resolveChannel, resolveMember, avatarUrlOverride } =
46+
useConfig();
47+
const miniUserName = useMemo(() => {
48+
if (!props.interaction && !props.referencedMessage) return null;
49+
50+
if (user === undefined) return null;
51+
52+
if (!resolveChannel) return getDisplayName(user);
53+
54+
const channel = resolveChannel(props.channelId);
55+
if (
56+
!channel ||
57+
!("guild_id" in channel) ||
58+
!channel.guild_id ||
59+
!props.referencedMessage
60+
)
61+
return getDisplayName(user);
62+
63+
const guildMember = resolveMember(
64+
props.referencedMessage.author,
65+
channel.guild_id
66+
);
67+
68+
if (!guildMember) return getDisplayName(user);
69+
70+
return guildMember.nick ?? getDisplayName(guildMember.user as APIUser);
71+
}, [props.referencedMessage, props.interaction, resolveChannel]);
72+
73+
const miniAvatarUrl = useMemo(
74+
() =>
75+
user === undefined
76+
? null
77+
: avatarUrlOverride?.(user) ?? getMiniAvatarUrl(user),
78+
[props.referencedMessage, props.interaction]
79+
);
80+
81+
const miniUserNameColorHex = useMemo(() => {
82+
if (!props.referencedMessage) return undefined;
83+
84+
const channel = resolveChannel(props.referencedMessage.channel_id);
85+
if (!channel || !("guild_id" in channel) || !channel.guild_id)
86+
return undefined;
87+
88+
const guildMember = resolveMember(
89+
props.referencedMessage.author,
90+
channel.guild_id
91+
);
92+
93+
if (!guildMember) return undefined;
94+
95+
const [role] = guildMember.roles
96+
.map((id) => resolveRole(id))
97+
.filter(
98+
(role): role is APIRole =>
99+
role !== null && role !== undefined && role.color !== 0
100+
)
101+
.sort((a, b) => b.position - a.position);
102+
103+
const color = role?.color;
104+
if (!color) return undefined;
105+
106+
return color > 0 ? `#${color.toString(16).padStart(6, "0")}` : undefined;
107+
}, [resolveRole]);
108+
109+
const unknownReply = !props.referencedMessage && !props.interaction;
110+
111+
return (
112+
<Styles.ReplyInfo>
113+
<Styles.ReplySpine />
114+
{unknownReply ? (
115+
<>
116+
<Styles.UnknownReplyIcon
117+
width={12}
118+
height={12}
119+
svg="IconUnknownReply"
120+
/>
121+
<Styles.UnknownReply>
122+
Original message was deleted or is unknown.
123+
</Styles.UnknownReply>
124+
</>
125+
) : (
126+
<Styles.ReplyUser>
127+
{miniAvatarUrl && (
128+
<Styles.MiniUserAvatar src={miniAvatarUrl.stillAvatarUrl} />
129+
)}
130+
{props.referencedMessage && (
131+
<ChatTag
132+
author={props.referencedMessage.author}
133+
crossPost={Boolean(
134+
(props.referencedMessage.flags ?? 0) & FLAG_CROSSPOST
135+
)}
136+
referenceGuild={
137+
props.referencedMessage.message_reference?.guild_id
138+
}
139+
/>
140+
)}
141+
<Styles.MiniUserName style={{ color: miniUserNameColorHex }}>
142+
{props.mentioned && "@"}
143+
{miniUserName}
144+
</Styles.MiniUserName>
145+
</Styles.ReplyUser>
146+
)}
147+
{props.referencedMessage ? (
148+
<Content message={props.referencedMessage} isReplyContent={true} />
149+
) : (
150+
props.interaction && (
151+
<Styles.SlashCommand>
152+
used{" "}
153+
<Styles.SlashCommandText>
154+
{!props.isContextMenuInteraction ? "/" : ""}
155+
{props.interaction.name}
156+
</Styles.SlashCommandText>
157+
</Styles.SlashCommand>
158+
)
159+
)}
160+
</Styles.ReplyInfo>
161+
);
162+
});
163+
164+
ReplyInfo.displayName = "ReplyInfo";
165+
166+
// type Message = Omit<MessageData, "referencedMessage"> & Partial<MessageData>;
167+
168+
interface EditMessageInputProps {
169+
// isFirstMessage?: boolean;
170+
message: ChatMessage;
171+
// isHovered?: boolean;
172+
// noThreadButton?: boolean;
173+
// isEditing?:boolean;
174+
// isContextMenuInteraction?: boolean;
175+
// hideTimestamp?: boolean;
176+
// overrides?: {
177+
// userMentioned?: boolean;
178+
// };
179+
}
180+
181+
function EditMessageInput(props: EditMessageInputProps) {
182+
const { handleMessageEditSubmit } = useConfig();
183+
184+
const submitMessageCallback = (content: string) => {
185+
if (!handleMessageEditSubmit || !content) return;
186+
187+
handleMessageEditSubmit({
188+
...props.message,
189+
content: content,
190+
edited_timestamp: new Date().getMilliseconds().toString(),
191+
});
192+
};
193+
194+
return (
195+
<Styles.MessageEditor
196+
autoCorrect={"false"}
197+
onKeyDown={(e) => {
198+
if (e.key === "Enter") {
199+
submitMessageCallback(e.target.value);
200+
}
201+
}}
202+
defaultValue={props.message.content}
203+
/>
204+
);
205+
}
206+
207+
export default EditMessageInput;

src/Message/variants/NormalMessage.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
import React, { memo, useMemo } from "react";
2-
import MessageAuthor from "../MessageAuthor";
3-
import Content from "../../Content";
4-
import Moment from "moment";
5-
import Tooltip from "../../Tooltip";
6-
import type { GetAvatarOptions } from "../../utils/getAvatar";
7-
import getAvatar from "../../utils/getAvatar";
8-
import LargeTimestamp from "../LargeTimestamp";
9-
import ChatTag from "../../ChatTag";
10-
import * as Styles from "../style/message";
111
import type {
122
APIMessageInteraction,
133
APIRole,
144
APIUser,
155
Snowflake,
166
} from "discord-api-types/v10";
177
import { MessageType } from "discord-api-types/v10";
8+
import Moment from "moment";
9+
import React, { memo, useMemo } from "react";
10+
import ChatTag from "../../ChatTag";
11+
import Content from "../../Content";
12+
import Tooltip from "../../Tooltip";
1813
import { useConfig } from "../../core/ConfigContext";
19-
import getDisplayName from "../../utils/getDisplayName";
2014
import type { ChatMessage } from "../../types";
15+
import type { GetAvatarOptions } from "../../utils/getAvatar";
16+
import getAvatar from "../../utils/getAvatar";
17+
import getDisplayName from "../../utils/getDisplayName";
18+
import LargeTimestamp from "../LargeTimestamp";
19+
import MessageAuthor from "../MessageAuthor";
20+
import * as Styles from "../style/message";
2121

2222
interface ReplyInfoProps {
2323
channelId: Snowflake;
@@ -170,6 +170,7 @@ interface MessageProps {
170170
message: ChatMessage;
171171
isHovered?: boolean;
172172
noThreadButton?: boolean;
173+
isEditing?: boolean;
173174
isContextMenuInteraction?: boolean;
174175
hideTimestamp?: boolean;
175176
overrides?: {
@@ -181,7 +182,9 @@ function NormalMessage(props: MessageProps) {
181182
const shouldShowReply =
182183
props.message.type === MessageType.Reply ||
183184
Boolean(props.message.interaction);
184-
const { currentUser, resolveChannel } = useConfig();
185+
186+
const { currentUser, resolveChannel, EditMessageComponent } = useConfig();
187+
185188
const channel = resolveChannel(props.message.channel_id);
186189
const guildId =
187190
channel !== null && "guild_id" in channel ? channel.guild_id : null;
@@ -220,6 +223,7 @@ function NormalMessage(props: MessageProps) {
220223
isContextMenuInteraction={props.isContextMenuInteraction}
221224
/>
222225
)}
226+
223227
<Styles.MessageHeaderBase>
224228
<MessageAuthor
225229
guildId={guildId}
@@ -231,10 +235,13 @@ function NormalMessage(props: MessageProps) {
231235
<LargeTimestamp timestamp={props.message.timestamp} />
232236
)}
233237
</Styles.MessageHeaderBase>
234-
<Content
238+
{EditMessageComponent ? (
239+
<EditMessageComponent message={props.message} />
240+
) : null}
241+
{/* <Content
235242
message={props.message}
236243
noThreadButton={props.noThreadButton}
237-
/>
244+
/> */}
238245
</Styles.Message>
239246
);
240247

src/core/ConfigContext.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type { ReactElement } from "react";
2-
import { createContext, useContext } from "react";
31
import type {
42
APIChannel,
53
APIEmbedImage,
@@ -9,11 +7,14 @@ import type {
97
APIUser,
108
Snowflake,
119
} from "discord-api-types/v10";
12-
import type { SvgConfig } from "./svgs";
13-
import type { Tag } from "../ChatTag/style";
1410
import type { APIAttachment } from "discord-api-types/v10";
15-
import type { UserAvatar } from "../utils/getAvatar";
11+
import type { ReactElement } from "react";
12+
import { createContext, useContext } from "react";
13+
import EditMessageInput from "src/Message/variants/EditMessageInput";
14+
import type { Tag } from "../ChatTag/style";
1615
import type { ChatMessage } from "../types";
16+
import type { UserAvatar } from "../utils/getAvatar";
17+
import type { SvgConfig } from "./svgs";
1718

1819
export type PartialSvgConfig = Partial<SvgConfig>;
1920

@@ -29,9 +30,9 @@ export interface ChatBadgeProps {
2930
}
3031

3132
export enum MessageTypeResponse {
32-
InAppError,
33-
ConsoleError,
34-
None,
33+
InAppError = 0,
34+
ConsoleError = 1,
35+
None = 2,
3536
}
3637

3738
export type Config<SvgConfig extends PartialSvgConfig> = {
@@ -62,6 +63,8 @@ export type Config<SvgConfig extends PartialSvgConfig> = {
6263
attachmentImageOnClick?(image: APIAttachment): void;
6364
embedImageOnClick?(image: APIEmbedImage): void;
6465
externalLinkOpenRequested?(url: string): void;
66+
handleMessageEditSubmit?(message: ChatMessage): void;
67+
EditMessageComponent?:( (props: { message: ChatMessage }) => JSX.Element);
6568
};
6669

6770
export const ConfigContext = createContext<Config<PartialSvgConfig>>({
@@ -76,6 +79,7 @@ export const ConfigContext = createContext<Config<PartialSvgConfig>>({
7679
still: "",
7780
animated: "",
7881
},
82+
EditMessageComponent: EditMessageInput,
7983
});
8084

8185
export function useConfig() {

0 commit comments

Comments
 (0)