From 6c158a9e2fc998d1e647e16ee07266dbdc903ca6 Mon Sep 17 00:00:00 2001 From: Maksym <109230509+TheMaksym@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:12:40 -0500 Subject: [PATCH 1/4] Whitespace in Memo References Whitespace works with Memo References with this code. --- CreateMemoRelationDialog.tsx | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 CreateMemoRelationDialog.tsx diff --git a/CreateMemoRelationDialog.tsx b/CreateMemoRelationDialog.tsx new file mode 100644 index 000000000..ff35910bf --- /dev/null +++ b/CreateMemoRelationDialog.tsx @@ -0,0 +1,172 @@ +import { Autocomplete, AutocompleteOption, Button, Checkbox, Chip, IconButton } from "@mui/joy"; +import React, { useState, useEffect } from "react"; +import { toast } from "react-hot-toast"; +import { memoServiceClient } from "@/grpcweb"; +import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts"; +import { getDateTimeString } from "@/helpers/datetime"; +import useCurrentUser from "@/hooks/useCurrentUser"; +import { Memo } from "@/types/proto/api/v2/memo_service"; +import { useTranslate } from "@/utils/i18n"; +import { generateDialog } from "./Dialog"; +import Icon from "./Icon"; + +interface Props extends DialogProps { + onConfirm: (memoIdList: number[], embedded?: boolean) => void; +} + +const CreateMemoRelationDialog: React.FC = (props: Props) => { + const { destroy, onConfirm } = props; + const t = useTranslate(); + const user = useCurrentUser(); + const [searchText, setSearchText] = useState(""); + const [debouncedSearchText, setDebouncedSearchText] = useState(""); + const [isFetching, setIsFetching] = useState(true); + const [fetchedMemos, setFetchedMemos] = useState([]); + const [selectedMemos, setSelectedMemos] = useState([]); + const [embedded, setEmbedded] = useState(false); + const filteredMemos = fetchedMemos.filter((memo) => !selectedMemos.includes(memo)); + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedSearchText(searchText.trim()); + }, 300); + + return () => { + clearTimeout(handler); + }; + }, [searchText]); + + useEffect(() => { + const fetchMemos = async () => { + setIsFetching(true); + try { + const filters = [`creator == "${user.name}"`, `row_status == "NORMAL"`]; + if (debouncedSearchText) { + filters.push(`content_search == [${JSON.stringify(debouncedSearchText)}]`); + } + const { memos } = await memoServiceClient.listMemos({ + pageSize: DEFAULT_MEMO_LIMIT, + filter: filters.length > 0 ? filters.join(" && ") : undefined, + }); + setFetchedMemos(memos); + } catch (error: any) { + console.error(error); + toast.error(error.response.data.message); + } + setIsFetching(false); + }; + + if (debouncedSearchText) fetchMemos(); + }, [debouncedSearchText]); + + const getHighlightedContent = (content: string) => { + const index = content.toLowerCase().indexOf(searchText.toLowerCase()); + if (index === -1) { + return content; + } + let before = content.slice(0, index); + if (before.length > 20) { + before = "..." + before.slice(before.length - 20); + } + const highlighted = content.slice(index, index + searchText.length); + let after = content.slice(index + searchText.length); + if (after.length > 20) { + after = after.slice(0, 20) + "..."; + } + + return ( + <> + {before} + {highlighted} + {after} + + ); + }; + + const handleCloseDialog = () => { + destroy(); + }; + + const handleConfirmBtnClick = async () => { + onConfirm( + selectedMemos.map((memo) => memo.id), + embedded, + ); + destroy(); + }; + + return ( + <> +
+

{"Add references"}

+ destroy()}> + + +
+
+ setSearchText(value)} + getOptionKey={(option) => option.name} + getOptionLabel={(option) => option.content} + isOptionEqualToValue={(option, value) => option.id === value.id} + renderOption={(props, option) => ( + +
+

{getDateTimeString(option.displayTime)}

+

+ {searchText ? getHighlightedContent(option.content) : option.content} +

+
+
+ )} + renderTags={(memos) => + memos.map((memo) => ( + +
+

{getDateTimeString(memo.displayTime)}

+ {memo.content} +
+
+ )) + } + onChange={(_, value) => setSelectedMemos(value)} + /> +
+ setEmbedded(e.target.checked)} /> +
+
+ + +
+
+ + ); +}; + +function showCreateMemoRelationDialog(props: Omit) { + generateDialog( + { + className: "create-memo-relation-dialog", + dialogName: "create-memo-relation-dialog", + }, + CreateMemoRelationDialog, + props, + ); +} + +export default showCreateMemoRelationDialog; \ No newline at end of file From 9f1207aa43550a4953c2ebe8623105fe01baa515 Mon Sep 17 00:00:00 2001 From: Maksym <109230509+TheMaksym@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:14:23 -0500 Subject: [PATCH 2/4] Delete CreateMemoRelationDialog.tsx --- CreateMemoRelationDialog.tsx | 172 ----------------------------------- 1 file changed, 172 deletions(-) delete mode 100644 CreateMemoRelationDialog.tsx diff --git a/CreateMemoRelationDialog.tsx b/CreateMemoRelationDialog.tsx deleted file mode 100644 index ff35910bf..000000000 --- a/CreateMemoRelationDialog.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { Autocomplete, AutocompleteOption, Button, Checkbox, Chip, IconButton } from "@mui/joy"; -import React, { useState, useEffect } from "react"; -import { toast } from "react-hot-toast"; -import { memoServiceClient } from "@/grpcweb"; -import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts"; -import { getDateTimeString } from "@/helpers/datetime"; -import useCurrentUser from "@/hooks/useCurrentUser"; -import { Memo } from "@/types/proto/api/v2/memo_service"; -import { useTranslate } from "@/utils/i18n"; -import { generateDialog } from "./Dialog"; -import Icon from "./Icon"; - -interface Props extends DialogProps { - onConfirm: (memoIdList: number[], embedded?: boolean) => void; -} - -const CreateMemoRelationDialog: React.FC = (props: Props) => { - const { destroy, onConfirm } = props; - const t = useTranslate(); - const user = useCurrentUser(); - const [searchText, setSearchText] = useState(""); - const [debouncedSearchText, setDebouncedSearchText] = useState(""); - const [isFetching, setIsFetching] = useState(true); - const [fetchedMemos, setFetchedMemos] = useState([]); - const [selectedMemos, setSelectedMemos] = useState([]); - const [embedded, setEmbedded] = useState(false); - const filteredMemos = fetchedMemos.filter((memo) => !selectedMemos.includes(memo)); - - useEffect(() => { - const handler = setTimeout(() => { - setDebouncedSearchText(searchText.trim()); - }, 300); - - return () => { - clearTimeout(handler); - }; - }, [searchText]); - - useEffect(() => { - const fetchMemos = async () => { - setIsFetching(true); - try { - const filters = [`creator == "${user.name}"`, `row_status == "NORMAL"`]; - if (debouncedSearchText) { - filters.push(`content_search == [${JSON.stringify(debouncedSearchText)}]`); - } - const { memos } = await memoServiceClient.listMemos({ - pageSize: DEFAULT_MEMO_LIMIT, - filter: filters.length > 0 ? filters.join(" && ") : undefined, - }); - setFetchedMemos(memos); - } catch (error: any) { - console.error(error); - toast.error(error.response.data.message); - } - setIsFetching(false); - }; - - if (debouncedSearchText) fetchMemos(); - }, [debouncedSearchText]); - - const getHighlightedContent = (content: string) => { - const index = content.toLowerCase().indexOf(searchText.toLowerCase()); - if (index === -1) { - return content; - } - let before = content.slice(0, index); - if (before.length > 20) { - before = "..." + before.slice(before.length - 20); - } - const highlighted = content.slice(index, index + searchText.length); - let after = content.slice(index + searchText.length); - if (after.length > 20) { - after = after.slice(0, 20) + "..."; - } - - return ( - <> - {before} - {highlighted} - {after} - - ); - }; - - const handleCloseDialog = () => { - destroy(); - }; - - const handleConfirmBtnClick = async () => { - onConfirm( - selectedMemos.map((memo) => memo.id), - embedded, - ); - destroy(); - }; - - return ( - <> -
-

{"Add references"}

- destroy()}> - - -
-
- setSearchText(value)} - getOptionKey={(option) => option.name} - getOptionLabel={(option) => option.content} - isOptionEqualToValue={(option, value) => option.id === value.id} - renderOption={(props, option) => ( - -
-

{getDateTimeString(option.displayTime)}

-

- {searchText ? getHighlightedContent(option.content) : option.content} -

-
-
- )} - renderTags={(memos) => - memos.map((memo) => ( - -
-

{getDateTimeString(memo.displayTime)}

- {memo.content} -
-
- )) - } - onChange={(_, value) => setSelectedMemos(value)} - /> -
- setEmbedded(e.target.checked)} /> -
-
- - -
-
- - ); -}; - -function showCreateMemoRelationDialog(props: Omit) { - generateDialog( - { - className: "create-memo-relation-dialog", - dialogName: "create-memo-relation-dialog", - }, - CreateMemoRelationDialog, - props, - ); -} - -export default showCreateMemoRelationDialog; \ No newline at end of file From 188acf7083bae71b1235ade6663010736549ddd9 Mon Sep 17 00:00:00 2001 From: Maksym <109230509+TheMaksym@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:14:59 -0500 Subject: [PATCH 3/4] Delete web/src/components/CreateMemoRelationDialog.tsx --- .../components/CreateMemoRelationDialog.tsx | 162 ------------------ 1 file changed, 162 deletions(-) delete mode 100644 web/src/components/CreateMemoRelationDialog.tsx diff --git a/web/src/components/CreateMemoRelationDialog.tsx b/web/src/components/CreateMemoRelationDialog.tsx deleted file mode 100644 index aaddd8e86..000000000 --- a/web/src/components/CreateMemoRelationDialog.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { Autocomplete, AutocompleteOption, Button, Checkbox, Chip, IconButton } from "@mui/joy"; -import React, { useState } from "react"; -import { toast } from "react-hot-toast"; -import useDebounce from "react-use/lib/useDebounce"; -import { memoServiceClient } from "@/grpcweb"; -import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts"; -import { getDateTimeString } from "@/helpers/datetime"; -import useCurrentUser from "@/hooks/useCurrentUser"; -import { Memo } from "@/types/proto/api/v2/memo_service"; -import { useTranslate } from "@/utils/i18n"; -import { generateDialog } from "./Dialog"; -import Icon from "./Icon"; - -interface Props extends DialogProps { - onConfirm: (memoIdList: number[], embedded?: boolean) => void; -} - -const CreateMemoRelationDialog: React.FC = (props: Props) => { - const { destroy, onConfirm } = props; - const t = useTranslate(); - const user = useCurrentUser(); - const [searchText, setSearchText] = useState(""); - const [isFetching, setIsFetching] = useState(true); - const [fetchedMemos, setFetchedMemos] = useState([]); - const [selectedMemos, setSelectedMemos] = useState([]); - const [embedded, setEmbedded] = useState(false); - const filteredMemos = fetchedMemos.filter((memo) => !selectedMemos.includes(memo)); - - useDebounce( - async () => { - setIsFetching(true); - try { - const filters = [`creator == "${user.name}"`, `row_status == "NORMAL"`]; - if (searchText) { - filters.push(`content_search == [${JSON.stringify(searchText)}]`); - } - const { memos } = await memoServiceClient.listMemos({ - pageSize: DEFAULT_MEMO_LIMIT, - filter: filters.length > 0 ? filters.join(" && ") : undefined, - }); - setFetchedMemos(memos); - } catch (error: any) { - console.error(error); - toast.error(error.response.data.message); - } - setIsFetching(false); - }, - 300, - [searchText], - ); - - const getHighlightedContent = (content: string) => { - const index = content.toLowerCase().indexOf(searchText.toLowerCase()); - if (index === -1) { - return content; - } - let before = content.slice(0, index); - if (before.length > 20) { - before = "..." + before.slice(before.length - 20); - } - const highlighted = content.slice(index, index + searchText.length); - let after = content.slice(index + searchText.length); - if (after.length > 20) { - after = after.slice(0, 20) + "..."; - } - - return ( - <> - {before} - {highlighted} - {after} - - ); - }; - - const handleCloseDialog = () => { - destroy(); - }; - - const handleConfirmBtnClick = async () => { - onConfirm( - selectedMemos.map((memo) => memo.id), - embedded, - ); - destroy(); - }; - - return ( - <> -
-

{"Add references"}

- destroy()}> - - -
-
- setSearchText(value.trim())} - getOptionKey={(option) => option.name} - getOptionLabel={(option) => option.content} - isOptionEqualToValue={(option, value) => option.id === value.id} - renderOption={(props, option) => ( - -
-

{getDateTimeString(option.displayTime)}

-

- {searchText ? getHighlightedContent(option.content) : option.content} -

-
-
- )} - renderTags={(memos) => - memos.map((memo) => ( - -
-

{getDateTimeString(memo.displayTime)}

- {memo.content} -
-
- )) - } - onChange={(_, value) => setSelectedMemos(value)} - /> -
- setEmbedded(e.target.checked)} /> -
-
- - -
-
- - ); -}; - -function showCreateMemoRelationDialog(props: Omit) { - generateDialog( - { - className: "create-memo-relation-dialog", - dialogName: "create-memo-relation-dialog", - }, - CreateMemoRelationDialog, - props, - ); -} - -export default showCreateMemoRelationDialog; From 2ae0428ba82da7356a94cb80d5ef8b6a9a1017c4 Mon Sep 17 00:00:00 2001 From: Maksym <109230509+TheMaksym@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:16:32 -0500 Subject: [PATCH 4/4] Allowing Whitespace in References --- .../components/CreateMemoRelationDialog.tsx | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 web/src/components/CreateMemoRelationDialog.tsx diff --git a/web/src/components/CreateMemoRelationDialog.tsx b/web/src/components/CreateMemoRelationDialog.tsx new file mode 100644 index 000000000..ff35910bf --- /dev/null +++ b/web/src/components/CreateMemoRelationDialog.tsx @@ -0,0 +1,172 @@ +import { Autocomplete, AutocompleteOption, Button, Checkbox, Chip, IconButton } from "@mui/joy"; +import React, { useState, useEffect } from "react"; +import { toast } from "react-hot-toast"; +import { memoServiceClient } from "@/grpcweb"; +import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts"; +import { getDateTimeString } from "@/helpers/datetime"; +import useCurrentUser from "@/hooks/useCurrentUser"; +import { Memo } from "@/types/proto/api/v2/memo_service"; +import { useTranslate } from "@/utils/i18n"; +import { generateDialog } from "./Dialog"; +import Icon from "./Icon"; + +interface Props extends DialogProps { + onConfirm: (memoIdList: number[], embedded?: boolean) => void; +} + +const CreateMemoRelationDialog: React.FC = (props: Props) => { + const { destroy, onConfirm } = props; + const t = useTranslate(); + const user = useCurrentUser(); + const [searchText, setSearchText] = useState(""); + const [debouncedSearchText, setDebouncedSearchText] = useState(""); + const [isFetching, setIsFetching] = useState(true); + const [fetchedMemos, setFetchedMemos] = useState([]); + const [selectedMemos, setSelectedMemos] = useState([]); + const [embedded, setEmbedded] = useState(false); + const filteredMemos = fetchedMemos.filter((memo) => !selectedMemos.includes(memo)); + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedSearchText(searchText.trim()); + }, 300); + + return () => { + clearTimeout(handler); + }; + }, [searchText]); + + useEffect(() => { + const fetchMemos = async () => { + setIsFetching(true); + try { + const filters = [`creator == "${user.name}"`, `row_status == "NORMAL"`]; + if (debouncedSearchText) { + filters.push(`content_search == [${JSON.stringify(debouncedSearchText)}]`); + } + const { memos } = await memoServiceClient.listMemos({ + pageSize: DEFAULT_MEMO_LIMIT, + filter: filters.length > 0 ? filters.join(" && ") : undefined, + }); + setFetchedMemos(memos); + } catch (error: any) { + console.error(error); + toast.error(error.response.data.message); + } + setIsFetching(false); + }; + + if (debouncedSearchText) fetchMemos(); + }, [debouncedSearchText]); + + const getHighlightedContent = (content: string) => { + const index = content.toLowerCase().indexOf(searchText.toLowerCase()); + if (index === -1) { + return content; + } + let before = content.slice(0, index); + if (before.length > 20) { + before = "..." + before.slice(before.length - 20); + } + const highlighted = content.slice(index, index + searchText.length); + let after = content.slice(index + searchText.length); + if (after.length > 20) { + after = after.slice(0, 20) + "..."; + } + + return ( + <> + {before} + {highlighted} + {after} + + ); + }; + + const handleCloseDialog = () => { + destroy(); + }; + + const handleConfirmBtnClick = async () => { + onConfirm( + selectedMemos.map((memo) => memo.id), + embedded, + ); + destroy(); + }; + + return ( + <> +
+

{"Add references"}

+ destroy()}> + + +
+
+ setSearchText(value)} + getOptionKey={(option) => option.name} + getOptionLabel={(option) => option.content} + isOptionEqualToValue={(option, value) => option.id === value.id} + renderOption={(props, option) => ( + +
+

{getDateTimeString(option.displayTime)}

+

+ {searchText ? getHighlightedContent(option.content) : option.content} +

+
+
+ )} + renderTags={(memos) => + memos.map((memo) => ( + +
+

{getDateTimeString(memo.displayTime)}

+ {memo.content} +
+
+ )) + } + onChange={(_, value) => setSelectedMemos(value)} + /> +
+ setEmbedded(e.target.checked)} /> +
+
+ + +
+
+ + ); +}; + +function showCreateMemoRelationDialog(props: Omit) { + generateDialog( + { + className: "create-memo-relation-dialog", + dialogName: "create-memo-relation-dialog", + }, + CreateMemoRelationDialog, + props, + ); +} + +export default showCreateMemoRelationDialog; \ No newline at end of file