* feat: mention user * fix: trigger api call on every letter typed * fix: this allows command menu to re-render upon props change, shouldComponentUpdate prevented re-rendering when necessary * fix: add node * fix: mention node styling * fix: Caret not visible after inserting mention * fix: apply mentionRule * fix: label is to be obtained from content, not attrs * feat: add mentions table and model * fix: typo * fix: make all mention nodes visible in shared doc * feat: parse mention ids from doc text * feat: MentionsProcessor * feat: documents.publish tests * feat: tests for MentionsProcessor * feat: schedule notifs for mentions * fix: get rid of Mention model * fix: put actor id and mention id in raw md * Revert "fix: put actor id and mention id in raw md" This reverts commit 3bb8a22e3c560971dccad6d2f82266256bcb2d96. * Revert "Revert "fix: put actor id and mention id in raw md"" This reverts commit 3c5b36c40cebf147663908cf27d0dce6488adfad. * fix: review * fix: no need of set * fix: show avatar * fix: get rid of eventName * fix: font-weight * fix: prioritize mention notifs * fix: store id in md * fix: no need of prepending m * fix: fetchPage * fix: Avatars incorrect color * fix: remove scanRE * fix: test * fix: include alphabet other than latin * lockfile * fix: regex should test for letters, marks and digits --------- Co-authored-by: Tom Moor <tom.moor@gmail.com>
114 lines
2.8 KiB
TypeScript
114 lines
2.8 KiB
TypeScript
import * as React from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { v4 } from "uuid";
|
|
import { MenuItem } from "@shared/editor/types";
|
|
import { MentionType } from "@shared/types";
|
|
import User from "~/models/User";
|
|
import Avatar from "~/components/Avatar";
|
|
import Flex from "~/components/Flex";
|
|
import useRequest from "~/hooks/useRequest";
|
|
import useStores from "~/hooks/useStores";
|
|
import CommandMenu, { Props } from "./CommandMenu";
|
|
import MentionMenuItem from "./MentionMenuItem";
|
|
|
|
interface MentionItem extends MenuItem {
|
|
name: string;
|
|
user: User;
|
|
appendSpace: boolean;
|
|
attrs: {
|
|
id: string;
|
|
type: MentionType;
|
|
modelId: string;
|
|
label: string;
|
|
actorId?: string;
|
|
};
|
|
}
|
|
|
|
type MentionMenuProps = Omit<
|
|
Props<MentionItem>,
|
|
"renderMenuItem" | "items" | "onLinkToolbarOpen" | "embeds" | "onClearSearch"
|
|
>;
|
|
|
|
function MentionMenu({ search, ...rest }: MentionMenuProps) {
|
|
const [items, setItems] = React.useState<MentionItem[]>([]);
|
|
const { t } = useTranslation();
|
|
const { users, auth } = useStores();
|
|
const { data, request } = useRequest(
|
|
React.useCallback(() => users.fetchPage({ query: search }), [users, search])
|
|
);
|
|
|
|
React.useEffect(() => {
|
|
request();
|
|
}, [request]);
|
|
|
|
React.useEffect(() => {
|
|
if (data) {
|
|
setItems(
|
|
data.map((user) => ({
|
|
name: "mention",
|
|
user,
|
|
title: user.name,
|
|
appendSpace: true,
|
|
attrs: {
|
|
id: v4(),
|
|
type: MentionType.User,
|
|
modelId: user.id,
|
|
actorId: auth.user?.id,
|
|
label: user.name,
|
|
},
|
|
}))
|
|
);
|
|
}
|
|
}, [auth.user?.id, data]);
|
|
|
|
const clearSearch = () => {
|
|
const { state, dispatch } = rest.view;
|
|
|
|
// clear search input
|
|
dispatch(
|
|
state.tr.insertText(
|
|
"",
|
|
state.selection.$from.pos - (search ?? "").length - 1,
|
|
state.selection.to
|
|
)
|
|
);
|
|
};
|
|
|
|
const containerId = "mention-menu-container";
|
|
return (
|
|
<CommandMenu
|
|
{...rest}
|
|
id={containerId}
|
|
filterable={false}
|
|
onClearSearch={clearSearch}
|
|
search={search}
|
|
renderMenuItem={(item, _index, options) => (
|
|
<MentionMenuItem
|
|
onClick={options.onClick}
|
|
selected={options.selected}
|
|
title={item.title}
|
|
label={item.attrs.label}
|
|
containerId={containerId}
|
|
icon={
|
|
<Flex
|
|
align="center"
|
|
justify="center"
|
|
style={{ width: 24, height: 24 }}
|
|
>
|
|
<Avatar
|
|
model={item.user}
|
|
showBorder={false}
|
|
alt={t("Profile picture")}
|
|
size={16}
|
|
/>
|
|
</Flex>
|
|
}
|
|
/>
|
|
)}
|
|
items={items}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export default MentionMenu;
|