Skip to content

Commit

Permalink
CW-mention-streams
Browse files Browse the repository at this point in the history
Added creation of discussion
  • Loading branch information
MeyerPV committed Dec 11, 2024
1 parent 5106e0b commit 77f5551
Show file tree
Hide file tree
Showing 20 changed files with 127 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,7 @@ export default function ChatComponent({
user={user}
commonId={commonId}
circleVisibility={discussion?.circleVisibility}
onInternalLinkClick={onInternalLinkClick}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from "@/shared/ui-kit";
import { BaseTextEditorHandles } from "@/shared/ui-kit/TextEditor/BaseTextEditor";
import { EmojiCount } from "@/shared/ui-kit/TextEditor/utils";
import { emptyFunction } from "@/shared/utils";
import { emptyFunction, InternalLinkData } from "@/shared/utils";
import styles from "./ChatInput.module.scss";

interface ChatInputProps {
Expand All @@ -44,6 +44,7 @@ interface ChatInputProps {
circleVisibility?: string[];
user?: User | null;
commonId?: string;
onInternalLinkClick?: (data: InternalLinkData) => void;
}

export const ChatInput = React.memo(
Expand All @@ -70,6 +71,7 @@ export const ChatInput = React.memo(
circleVisibility,
user,
commonId,
onInternalLinkClick,
} = props;

if (shouldHideChatInput) {
Expand Down Expand Up @@ -131,6 +133,7 @@ export const ChatInput = React.memo(
circleVisibility={circleVisibility}
user={user}
commonId={commonId}
onInternalLinkClick={onInternalLinkClick}
/>
<button
className={styles.sendIcon}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { FC } from "react";
import classNames from "classnames";
import styles from "../../ChatMessage.module.scss";
import { InternalLinkData, parseMessageLink } from "@/shared/utils";

interface DiscussionLinkProps {
link: string;
title: string;
mentionTextClassName?: string;
onInternalLinkClick?: (data: InternalLinkData) => void;
}

export const DiscussionLink: FC<DiscussionLinkProps> = (props) => {
const { title, link, mentionTextClassName, onInternalLinkClick } =
props;


const handleInternalLinkClick = () => {
if (onInternalLinkClick && link) {
const data = parseMessageLink(link);
data && onInternalLinkClick(data);
}
};

return (
<>
<span
className={classNames(styles.mentionText, mentionTextClassName)}
onClick={handleInternalLinkClick}
>
@{title}
</span>
</>
);
};

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./DiscussionLink";
1 change: 1 addition & 0 deletions src/shared/components/Chat/ChatMessage/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./MessageLinkPreview";
export * from "./Time";
export * from "./UserMention";
export * from "./StreamMention";
export * from "./DiscussionLink";
export * from "./InternalLink";
export * from "./ReactWithEmoji";
export * from "./Reactions";
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import textEditorElementsStyles from "@/shared/ui-kit/TextEditor/shared/TextEdit
import { EmojiElement } from "@/shared/ui-kit/TextEditor/types";
import { isRtlWithNoMentions } from "@/shared/ui-kit/TextEditor/utils";
import { InternalLinkData } from "@/shared/utils";
import { CheckboxItem, StreamMention, UserMention } from "../components";
import { CheckboxItem, StreamMention, UserMention, DiscussionLink } from "../components";
import { Text, TextData } from "../types";
import { generateInternalLink } from "./generateInternalLink";
import { getTextFromSystemMessage } from "./getTextFromSystemMessage";
Expand Down Expand Up @@ -111,6 +111,15 @@ const getTextFromDescendant = async ({
onStreamMentionClick={onStreamMentionClick}
/>
);
case ElementType.DiscussionLink:
return (
<DiscussionLink
link={descendant.link}
title={descendant.title}
mentionTextClassName={mentionTextClassName}
onInternalLinkClick={onInternalLinkClick}
/>
);
case ElementType.Emoji:
return (
<ChatEmoji
Expand Down
9 changes: 6 additions & 3 deletions src/shared/ui-kit/TextEditor/BaseTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { AI_PRO_USER, AI_USER, FeatureFlags } from "@/shared/constants";
import { KeyboardKeys } from "@/shared/constants/keyboardKeys";
import { useFeatureFlag } from "@/shared/hooks";
import { Discussion, User } from "@/shared/models";
import { generateDiscussionShareLink, getUserName, isMobile, isRtlText } from "@/shared/utils";
import { generateDiscussionShareLink, getUserName, InternalLinkData, isMobile, isRtlText } from "@/shared/utils";
import {
Editor,
MentionDropdown,
Expand All @@ -49,6 +49,7 @@ import {
checkIsCheckboxCreationText,
toggleCheckboxItem,
checkIsEmptyCheckboxCreationText,
insertDiscussionLink,
} from "./utils";
import styles from "./BaseTextEditor.module.scss";

Expand Down Expand Up @@ -85,6 +86,7 @@ export interface TextEditorProps {
circleVisibility?: string[];
user?: User | null;
commonId?: string;
onInternalLinkClick?: (data: InternalLinkData) => void;
}

const INITIAL_SEARCH_VALUE = {
Expand Down Expand Up @@ -124,6 +126,7 @@ const BaseTextEditor = forwardRef<BaseTextEditorHandles, TextEditorProps>(
circleVisibility,
user,
commonId,
onInternalLinkClick,
} = props;
const editor = useMemo(
() =>
Expand Down Expand Up @@ -512,10 +515,10 @@ const BaseTextEditor = forwardRef<BaseTextEditorHandles, TextEditorProps>(
setShouldFocusTarget(false);
isNewMentionCreated.current = true;
}}
onCreateDiscussion={(createdDiscussionCommonId: string, discussionId: string) => {
onCreateDiscussion={(createdDiscussionCommonId: string, discussionId: string, title: string) => {
Transforms.select(editor, target);
const link = generateDiscussionShareLink(createdDiscussionCommonId, discussionId);
Transforms.insertText(editor, link);
insertDiscussionLink(editor, title, link, onInternalLinkClick);
setTarget(null);
setShouldFocusTarget(false);
isNewMentionCreated.current = true;
Expand Down
21 changes: 20 additions & 1 deletion src/shared/ui-kit/TextEditor/components/Element/Element.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ const StreamMention = ({ attributes, element, className, children }) => {
);
};

const DiscussionLink = ({ attributes, element, className, children }) => {
return (
<span
{...attributes}
contentEditable={false}
data-cy={`discussion-link-${element.title.replace(" ", "-")}`}
className={className}
onClick={element.onInternalLinkClick}
>
@{element.title}
{children}
</span>
);
};

const Element: FC<RenderElementProps & { styles?: EditorElementStyles }> = (
props,
) => {
Expand Down Expand Up @@ -89,6 +104,10 @@ const Element: FC<RenderElementProps & { styles?: EditorElementStyles }> = (
{children}
</Link>
);
case ElementType.DiscussionLink:
return (
<DiscussionLink {...props} className={classNames(styles.mention, elementStyles?.mention)} />
);
case ElementType.Mention: {
return (
<Mention
Expand All @@ -103,7 +122,7 @@ const Element: FC<RenderElementProps & { styles?: EditorElementStyles }> = (
{...props}
className={classNames(styles.mention, elementStyles?.mention)}
/>
)
);
}
case ElementType.Emoji: {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const MENTION_TAG = "@";
export interface MentionDropdownProps {
onClick: (user: User) => void;
onClickDiscussion: (discussion: Discussion) => void;
onCreateDiscussion: (createdDiscussionCommonId: string, discussionId: string) => void;
onCreateDiscussion: (createdDiscussionCommonId: string, discussionId: string, title: string) => void;
onClose: () => void;
users?: User[];
discussions?: Discussion[];
Expand Down Expand Up @@ -215,13 +215,14 @@ const MentionDropdown: FC<MentionDropdownProps> = (props) => {
const userName = getUserName(user);
const userId = user.uid;
const firstMessage = generateFirstMessage({ userName, userId });
const title = searchText.slice(1);
const optimisticFeedItem = generateOptimisticFeedItem({
userId,
commonId,
type: CommonFeedType.OptimisticDiscussion,
circleVisibility: circleVisibility ?? [],
discussionId,
title: searchText,
title,
content: firstMessage,
lastMessageContent: {
ownerId: userId,
Expand All @@ -242,7 +243,7 @@ const MentionDropdown: FC<MentionDropdownProps> = (props) => {
commonActions.createDiscussion.request({
payload: {
id: discussionId,
title: searchText,
title,
message: firstMessage,
ownerId: userId,
commonId,
Expand All @@ -253,7 +254,7 @@ const MentionDropdown: FC<MentionDropdownProps> = (props) => {
}),
);

onCreateDiscussion(commonId, discussionId);
onCreateDiscussion(commonId, discussionId, title);
};

const getRef = (element) => listRefs.current.push(element);
Expand Down
2 changes: 2 additions & 0 deletions src/shared/ui-kit/TextEditor/constants/elementType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum ElementType {
Link = "link",
Mention = "mention",
StreamMention = "StreamMention",
DiscussionLink = "DiscussionLink",
NumberedList = "numbered-list",
BulletedList = "bulleted-list",
ListItem = "list-item",
Expand All @@ -21,5 +22,6 @@ export const INLINE_TYPES = [
ElementType.Link,
ElementType.Mention,
ElementType.StreamMention,
ElementType.DiscussionLink,
ElementType.Emoji,
];
4 changes: 2 additions & 2 deletions src/shared/ui-kit/TextEditor/hofs/withMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ export const withMentions = (editor: Editor): Editor => {
checkIsInlineType(element.type) || isInline(element);

editor.isVoid = (element) => {
return ((element.type as ElementType) === ElementType.Mention || (element.type as ElementType) === ElementType.StreamMention)
return ((element.type as ElementType) === ElementType.Mention || (element.type as ElementType) === ElementType.StreamMention || (element.type as ElementType) === ElementType.DiscussionLink)
? true
: isVoid(element);
};

editor.markableVoid = (element) => {
return (
((element.type as ElementType) === ElementType.Mention || (element.type as ElementType) === ElementType.StreamMention) ||
((element.type as ElementType) === ElementType.Mention || (element.type as ElementType) === ElementType.StreamMention || (element.type as ElementType) === ElementType.DiscussionLink) ||
markableVoid(element)
);
};
Expand Down
11 changes: 10 additions & 1 deletion src/shared/ui-kit/TextEditor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { HistoryEditor } from "slate-history";
import { ReactEditor } from "slate-react";
import { Skin } from "@emoji-mart/data";
import { ElementType, FormatType } from "./constants";
import { InternalLinkData } from "@/shared/utils";

export type TextEditorValue = Descendant[];

Expand Down Expand Up @@ -59,6 +60,13 @@ export interface MentionElement extends BaseElement<CustomText> {
userId: string;
}

export interface DiscussionLinkElement extends BaseElement<CustomText> {
type: ElementType.DiscussionLink;
title: string;
link: string;
onInternalLinkClick?: (data: InternalLinkData) => void;
}

export interface StreamMentionElement extends BaseElement<CustomText> {
type: ElementType.StreamMention;
title: string;
Expand Down Expand Up @@ -99,4 +107,5 @@ export type CustomElement =
| MentionElement
| StreamMentionElement
| EmojiElement
| CheckboxItemElement;
| CheckboxItemElement
| DiscussionLinkElement;
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const checkIsTextEditorValueEmpty = (
const firstChild = firstElement.children[0];
const secondChild = firstElement.children[1];

if (Element.isElementType(secondChild, ElementType.Mention) || Element.isElementType(secondChild, ElementType.StreamMention)) {
if (Element.isElementType(secondChild, ElementType.Mention) || Element.isElementType(secondChild, ElementType.StreamMention) || Element.isElementType(secondChild, ElementType.DiscussionLink)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const countTextEditorEmojiElements = (
emojiCount = emojiCount + 1;
} else if (children?.text !== "") {
hasText = true;
} else if (Element.isElementType(children, ElementType.Mention)) {
} else if (Element.isElementType(children, ElementType.Mention) || Element.isElementType(children, ElementType.StreamMention) || Element.isElementType(children, ElementType.DiscussionLink)) {
hasText = true;
}
});
Expand Down
1 change: 1 addition & 0 deletions src/shared/ui-kit/TextEditor/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export * from "./insertEmoji";
export * from "./insertMention";
export * from "./insertStreamMention";
export * from "./isRtlWithNoMentions";
export * from "./insertDiscussionLink";
18 changes: 18 additions & 0 deletions src/shared/ui-kit/TextEditor/utils/insertDiscussionLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Transforms } from "slate";
import { ReactEditor } from "slate-react";
import { ElementType } from "../constants";
import { DiscussionLinkElement } from "../types";

export const insertDiscussionLink = (editor, title, link, onInternalLinkClick) => {
const discussionLink: DiscussionLinkElement = {
type: ElementType.DiscussionLink,
title: `${title} `,
link,
onInternalLinkClick,
children: [{ text: "" }],
};
Transforms.insertNodes(editor, discussionLink);
Transforms.move(editor);

ReactEditor.focus(editor);
};
2 changes: 1 addition & 1 deletion src/shared/ui-kit/TextEditor/utils/isRtlWithNoMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const isRtlWithNoMentions = (
const parsedText = typeof text === "string" ? JSON.parse(text) : text;
const textWithNoMentions = JSON.stringify(
parsedText[0].children?.filter(
(item) => item.type !== ElementType.Mention,
(item) => (item.type !== ElementType.Mention || item.type !== ElementType.StreamMention || item.type !== ElementType.DiscussionLink),
),
);
return isRtlText(textWithNoMentions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const removeTextEditorEmptyEndLinesValues = (
firstChild?.text !== "" ||
Element.isElementType(secondChild, ElementType.Mention) ||
Element.isElementType(secondChild, ElementType.StreamMention) ||
Element.isElementType(secondChild, ElementType.DiscussionLink) ||
Element.isElementType(secondChild, ElementType.Emoji)
) {
endOfTextIndex = index;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const serializeDescendant = (descendant: Descendant): string => {
return `${combinedChildren}\n`;
case ElementType.Mention:
return `@${descendant.displayName}`;
case ElementType.StreamMention:
return `@${descendant.title}`;
case ElementType.DiscussionLink:
return `@${descendant.title}`;
default:
return descendant.text || "";
}
Expand Down
4 changes: 2 additions & 2 deletions src/store/states/common/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,8 @@ export const reducer = createReducer<CommonState, Action>(initialState)

// Sort feedItems by updatedAt in descending order
feedItems.sort((a, b) => {
const dateA = a?.feedItem?.updatedAt.toDate().getTime();
const dateB = b?.feedItem?.updatedAt.toDate().getTime();
const dateA = a?.feedItem?.updatedAt?.toDate().getTime();
const dateB = b?.feedItem?.updatedAt?.toDate().getTime();
return dateB - dateA; // Sort in descending order
});

Expand Down

0 comments on commit 77f5551

Please sign in to comment.