Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FR-287] Message Editing Support #61

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Content/Embed/EmbedVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ interface EmbedVideoProps
extends Required<Pick<APIEmbedVideo, "width" | "height">> {
thumbnail?: APIEmbedThumbnail["url"];
url: APIEmbedVideo["url"] | undefined;
proxyUrl: APIEmbedVideo["proxy_url"] | undefined;
proxyUrl: APIEmbedVideo["proxy_url"] | undefined | null;
}

function EmbedVideo(props: EmbedVideoProps) {
if (props.proxyUrl !== undefined)
if (props.proxyUrl)
return (
<ThumbnailWrapper
thumbnail={props.thumbnail}
Expand Down
2 changes: 1 addition & 1 deletion src/Message/Reactions/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const Reaction = styled.withConfig({
display: "flex",
flexDirection: "row",
alignItems: "center",
padding: `${theme.space.small} ${theme.space.medium}`,
padding: `${theme.space.xs} ${theme.space.medium}`,
borderRadius: 8,
cursor: "not-allowed",
backgroundColor: theme.colors.backgroundSecondary,
Expand Down
21 changes: 20 additions & 1 deletion src/Message/style/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {
styled,
theme,
} from "../../Stitches/stitches.config";
import { Link } from "../../markdown/render/elements";
import SvgFromUrl from "../../SvgFromUrl";
import { Link } from "../../markdown/render/elements";

export const SmallTimestamp = styled.withConfig({
displayName: "small-timestamp",
Expand Down Expand Up @@ -96,6 +96,25 @@ export const MessageHeaderBase = styled.withConfig({
flexWrap: "wrap",
});

export const MessageEditor = styled.withConfig({
displayName: "message-editor",
componentId: commonComponentId,
})("input", {
paddingTop: theme.space.xl,
paddingBottom: theme.space.xl,
paddingLeft: theme.space.xxl,
paddingRight: theme.space.large,
backgroundColor: theme.colors.primaryOpacity10,
// backgroundColor: theme.colors.primaryOpacity10,
tylergeorges marked this conversation as resolved.
Show resolved Hide resolved
outline: "none",
borderRadius: 8,
border: "none",
color: theme.colors.primaryOpacity80,
fontWeight: 400,
lineHeight: "1.375rem",
width: "100%",
});

export const AutomodHeaderText = styled.withConfig({
displayName: "automod-header-text",
componentId: commonComponentId,
Expand Down
50 changes: 50 additions & 0 deletions src/Message/variants/EditMessageInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { useConfig } from "../../core/ConfigContext";
import type { ChatMessage } from "../../types";

import * as Styles from "../style/message";

interface EditMessageInputProps {
// isFirstMessage?: boolean;
tylergeorges marked this conversation as resolved.
Show resolved Hide resolved
message: ChatMessage;
// isHovered?: boolean;
// noThreadButton?: boolean;
// isEditing?:boolean;
// isContextMenuInteraction?: boolean;
// hideTimestamp?: boolean;
// overrides?: {
// userMentioned?: boolean;
// };
}

function EditMessageInput(props: EditMessageInputProps) {
const { handleMessageEditSubmit } = useConfig();

function submitMessageCallback(content: string) {
if (!handleMessageEditSubmit || !content) return;

handleMessageEditSubmit({
...props.message,
content: content,
edited_timestamp: new Date().getMilliseconds().toString(),
});
}

function onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
const target = e.target as HTMLInputElement;

if (e.key === "Enter") {
submitMessageCallback(target.value);
}
}

return (
<Styles.MessageEditor
autoCorrect={"false"}
tylergeorges marked this conversation as resolved.
Show resolved Hide resolved
onKeyDown={onKeyDown}
defaultValue={props.message.content}
/>
);
}

export default EditMessageInput;
46 changes: 28 additions & 18 deletions src/Message/variants/NormalMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import React, { memo, useMemo } from "react";
import MessageAuthor from "../MessageAuthor";
import Content from "../../Content";
import Moment from "moment";
import Tooltip from "../../Tooltip";
import type { GetAvatarOptions } from "../../utils/getAvatar";
import getAvatar from "../../utils/getAvatar";
import LargeTimestamp from "../LargeTimestamp";
import ChatTag from "../../ChatTag";
import * as Styles from "../style/message";
import type {
APIMessageInteraction,
APIRole,
APIUser,
Snowflake,
} from "discord-api-types/v10";
import { MessageType } from "discord-api-types/v10";
import Moment from "moment";
import React, { memo, useMemo } from "react";
import ChatTag from "../../ChatTag";
import Content from "../../Content";
import Tooltip from "../../Tooltip";
import { useConfig } from "../../core/ConfigContext";
import getDisplayName from "../../utils/getDisplayName";
import type { ChatMessage } from "../../types";
import type { GetAvatarOptions } from "../../utils/getAvatar";
import getAvatar from "../../utils/getAvatar";
import getDisplayName from "../../utils/getDisplayName";
import LargeTimestamp from "../LargeTimestamp";
import MessageAuthor from "../MessageAuthor";
import * as Styles from "../style/message";

interface ReplyInfoProps {
channelId: Snowflake;
Expand Down Expand Up @@ -163,8 +163,6 @@ const ReplyInfo = memo((props: ReplyInfoProps) => {

ReplyInfo.displayName = "ReplyInfo";

// type Message = Omit<MessageData, "referencedMessage"> & Partial<MessageData>;

interface MessageProps {
isFirstMessage?: boolean;
message: ChatMessage;
Expand All @@ -181,7 +179,14 @@ function NormalMessage(props: MessageProps) {
const shouldShowReply =
props.message.type === MessageType.Reply ||
Boolean(props.message.interaction);
const { currentUser, resolveChannel } = useConfig();

const {
currentUser,
resolveChannel,
editingMessageId,
EditMessageComponent,
} = useConfig();

const channel = resolveChannel(props.message.channel_id);
const guildId =
channel !== null && "guild_id" in channel ? channel.guild_id : null;
Expand Down Expand Up @@ -220,6 +225,7 @@ function NormalMessage(props: MessageProps) {
isContextMenuInteraction={props.isContextMenuInteraction}
/>
)}

<Styles.MessageHeaderBase>
<MessageAuthor
guildId={guildId}
Expand All @@ -231,10 +237,14 @@ function NormalMessage(props: MessageProps) {
<LargeTimestamp timestamp={props.message.timestamp} />
)}
</Styles.MessageHeaderBase>
<Content
message={props.message}
noThreadButton={props.noThreadButton}
/>
{editingMessageId === props.message.id && EditMessageComponent ? (
<EditMessageComponent message={props.message} />
) : (
<Content
message={props.message}
noThreadButton={props.noThreadButton}
/>
)}
</Styles.Message>
);

Expand Down
19 changes: 11 additions & 8 deletions src/core/ConfigContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { ReactElement } from "react";
import { createContext, useContext } from "react";
import type {
APIChannel,
APIEmbedImage,
Expand All @@ -9,11 +7,13 @@ import type {
APIUser,
Snowflake,
} from "discord-api-types/v10";
import type { SvgConfig } from "./svgs";
import type { Tag } from "../ChatTag/style";
import type { APIAttachment } from "discord-api-types/v10";
import type { UserAvatar } from "../utils/getAvatar";
import type { ReactElement } from "react";
import { createContext, useContext } from "react";
import type { Tag } from "../ChatTag/style";
import type { ChatMessage } from "../types";
import type { UserAvatar } from "../utils/getAvatar";
import type { SvgConfig } from "./svgs";

export type PartialSvgConfig = Partial<SvgConfig>;

Expand All @@ -29,9 +29,9 @@ export interface ChatBadgeProps {
}

export enum MessageTypeResponse {
InAppError,
ConsoleError,
None,
InAppError = 0,
ConsoleError = 1,
None = 2,
}

export type Config<SvgConfig extends PartialSvgConfig> = {
Expand All @@ -50,6 +50,7 @@ export type Config<SvgConfig extends PartialSvgConfig> = {
avatarUrlOverride?(user: APIUser): UserAvatar | null;
themeOverrideClassName?: string;
unknownMessageTypeResponse?: MessageTypeResponse;
editingMessageId?: string;

// Click handlers
currentUser(): APIUser | null;
Expand All @@ -62,6 +63,8 @@ export type Config<SvgConfig extends PartialSvgConfig> = {
attachmentImageOnClick?(image: APIAttachment): void;
embedImageOnClick?(image: APIEmbedImage): void;
externalLinkOpenRequested?(url: string): void;
handleMessageEditSubmit?(message: ChatMessage): void;
EditMessageComponent?: (props: { message: ChatMessage }) => JSX.Element;
tylergeorges marked this conversation as resolved.
Show resolved Hide resolved
};

export const ConfigContext = createContext<Config<PartialSvgConfig>>({
Expand Down
56 changes: 56 additions & 0 deletions src/stories/Normal.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,62 @@ VideoAttachment.args = {
],
};

export const Editing: StoryFn<typeof MessageGroup> = Template.bind({});
Editing.args = {
showButtons: true,
messages: [
{
id: "1101275906716213331",
type: 0,
content:
"Small update: We needed to roll this back ~~for 24 hours~~ to patch some security issues. It'll be back real soon. Update: we don't want to re-roll it out on a friday afternoon, so thisll be back next week.",
channel_id: "697138785317814292",
author: {
id: "132819036282159104",
username: "mrquine",
global_name: "Mr. Quine",
avatar: "3a30ffeeeb354950804d77ded94162d3",
discriminator: "0001",
public_flags: 4457220,
},
attachments: [],
embeds: [],
mentions: [],
mention_roles: [],
pinned: false,
mention_everyone: false,
tts: false,
timestamp: "2023-04-27T22:37:16.878000+00:00",
edited_timestamp: "2023-04-28T21:00:43.827000+00:00",
flags: 0,
components: [],
message_reference: {
channel_id: "697138785317814292",
guild_id: "613425648685547541",
message_id: "1101188115344920607",
},
reactions: [
{
emoji: {
id: null,
name: "👍",
},
count: 234,
me: false,
},
{
emoji: {
id: "1085363933579329656",
name: "App_Broom",
},
count: 185,
me: false,
},
],
},
],
};

export const Reply: StoryFn<typeof MessageGroup> = Template.bind({});
Reply.args = {
messages: [
Expand Down
Loading