Compare commits

...

2 Commits

Author SHA1 Message Date
Tom Moor 9943ac07f6 cleanup fetchDocumentUsers 2024-09-15 23:16:55 -04:00
Tom Moor 7a9b66a704 feat: Add ability to tag users out of document 2024-09-15 23:12:25 -04:00
4 changed files with 37 additions and 20 deletions
+31 -1
View File
@@ -2,6 +2,7 @@ import { observer } from "mobx-react";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom";
import { toast } from "sonner";
import { v4 } from "uuid";
import { MenuItem } from "@shared/editor/types";
import { MentionType } from "@shared/types";
@@ -11,6 +12,7 @@ import { Avatar, AvatarSize } from "~/components/Avatar";
import Flex from "~/components/Flex";
import useRequest from "~/hooks/useRequest";
import useStores from "~/hooks/useStores";
import { client } from "~/utils/ApiClient";
import MentionMenuItem from "./MentionMenuItem";
import SuggestionsMenu, {
Props as SuggestionsMenuProps,
@@ -45,7 +47,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
React.useCallback(
() =>
documentId
? users.fetchDocumentUsers({ id: documentId, query: search })
? users.fetchPage({ id: documentId, query: search })
: Promise.resolve([]),
[users, documentId, search]
)
@@ -78,6 +80,33 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
}
}, [auth.currentUserId, loading, data]);
const handleSelect = React.useCallback(
async (item: MentionItem) => {
// Check if the mentioned user has access to the document
const res = await client.post("/documents.users", {
id: documentId,
userId: item.attrs.modelId,
});
if (!res.data.length) {
const user = users.get(item.attrs.modelId);
toast.message(
t(
"{{ userName }} won't by notified as they do not have access to this document",
{
userName: item.attrs.label,
}
),
{
icon: <Avatar model={user} size={AvatarSize.Toast} />,
duration: 10000,
}
);
}
},
[t, users, documentId]
);
// Prevent showing the menu until we have data otherwise it will be positioned
// incorrectly due to the height being unknown.
if (!loaded) {
@@ -91,6 +120,7 @@ function MentionMenu({ search, isActive, ...rest }: Props) {
filterable={false}
trigger="@"
search={search}
onSelect={handleSelect}
renderMenuItem={(item, _index, options) => (
<MentionMenuItem
onClick={options.onClick}
@@ -60,7 +60,10 @@ export type Props<T extends MenuItem = MenuItem> = {
uploadFile?: (file: File) => Promise<string>;
onFileUploadStart?: () => void;
onFileUploadStop?: () => void;
/** Callback when the menu is closed */
onClose: (insertNewLine?: boolean) => void;
/** Optional callback when a suggestion is selected */
onSelect?: (item: MenuItem) => void;
embeds?: EmbedDescriptor[];
renderMenuItem: (
item: T,
@@ -244,6 +247,8 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
const handleClickItem = React.useCallback(
(item) => {
props.onSelect?.(item);
switch (item.name) {
case "image":
return triggerFilePick(
-19
View File
@@ -119,25 +119,6 @@ export default class UsersStore extends Store<User> {
id: user.id,
});
@action
fetchDocumentUsers = async (params: {
id: string;
query?: string;
}): Promise<User[]> => {
try {
const res = await client.post("/documents.users", params);
invariant(res?.data, "User list not available");
let response: User[] = [];
runInAction("DocumentsStore#fetchUsers", () => {
response = res.data.map(this.add);
this.addPolicies(res.policies);
});
return response;
} catch (err) {
return Promise.resolve([]);
}
};
/**
* Returns users that are not in the given document, optionally filtered by a query.
*
@@ -378,6 +378,7 @@
"Replacement": "Replacement",
"Replace": "Replace",
"Replace all": "Replace all",
"{{ userName }} won't by notified as they do not have access to this document": "{{ userName }} won't by notified as they do not have access to this document",
"Profile picture": "Profile picture",
"Add column after": "Add column after",
"Add column before": "Add column before",