Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
f78b2774
Commit
f78b2774
authored
Jul 23, 2025
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: simplify add memo relation
parent
9b5b7b1e
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
22 additions
and
90 deletions
+22
-90
AddMemoRelationPopover.tsx
...onents/MemoEditor/ActionButton/AddMemoRelationPopover.tsx
+21
-89
index.tsx
web/src/components/MemoEditor/Editor/index.tsx
+1
-1
No files found.
web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx
View file @
f78b2774
import
{
uniqBy
}
from
"lodash-es"
;
import
{
LinkIcon
,
X
}
from
"lucide-react"
;
import
React
,
{
useContext
,
useState
}
from
"react"
;
import
{
LinkIcon
}
from
"lucide-react"
;
import
{
useContext
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
useDebounce
from
"react-use/lib/useDebounce"
;
import
{
Badge
}
from
"@/components/ui/badge"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
Checkbox
}
from
"@/components/ui/checkbox"
;
import
{
Input
}
from
"@/components/ui/input"
;
import
{
Popover
,
PopoverContent
,
PopoverTrigger
}
from
"@/components/ui/popover"
;
import
{
memoServiceClient
}
from
"@/grpcweb"
;
import
{
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
extract
Memo
IdFromName
}
from
"@/store/common"
;
import
{
extract
User
IdFromName
}
from
"@/store/common"
;
import
{
Memo
,
MemoRelation_Memo
,
MemoRelation_Type
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
EditorRefActions
}
from
"../Editor"
;
import
{
MemoEditorContext
}
from
"../types"
;
interface
Props
{
editorRef
:
React
.
RefObject
<
EditorRefActions
>
;
}
const
AddMemoRelationPopover
=
(
props
:
Props
)
=>
{
const
{
editorRef
}
=
props
;
const
AddMemoRelationPopover
=
()
=>
{
const
t
=
useTranslate
();
const
context
=
useContext
(
MemoEditorContext
);
const
user
=
useCurrentUser
();
const
[
searchText
,
setSearchText
]
=
useState
<
string
>
(
""
);
const
[
isFetching
,
setIsFetching
]
=
useState
<
boolean
>
(
true
);
const
[
fetchedMemos
,
setFetchedMemos
]
=
useState
<
Memo
[]
>
([]);
const
[
selectedMemos
,
setSelectedMemos
]
=
useState
<
Memo
[]
>
([]);
const
[
embedded
,
setEmbedded
]
=
useState
<
boolean
>
(
false
);
const
[
popoverOpen
,
setPopoverOpen
]
=
useState
<
boolean
>
(
false
);
const
filteredMemos
=
fetchedMemos
.
filter
(
(
memo
)
=>
!
selectedMemos
.
includes
(
memo
)
&&
memo
.
name
!==
context
.
memoName
&&
!
context
.
relationList
.
some
((
relation
)
=>
relation
.
relatedMemo
?.
name
===
memo
.
name
),
(
memo
)
=>
memo
.
name
!==
context
.
memoName
&&
!
context
.
relationList
.
some
((
relation
)
=>
relation
.
relatedMemo
?.
name
===
memo
.
name
),
);
useDebounce
(
...
...
@@ -46,10 +33,7 @@ const AddMemoRelationPopover = (props: Props) => {
setIsFetching
(
true
);
try
{
const
conditions
=
[];
// Extract user ID from user name (format: users/{user_id})
const
userId
=
user
.
name
.
replace
(
"users/"
,
""
);
conditions
.
push
(
`creator_id ==
${
userId
}
`
);
const
conditions
=
[
`creator_id ==
${
extractUserIdFromName
(
user
.
name
)}
`
];
if
(
searchText
)
{
conditions
.
push
(
`content.contains("
${
searchText
}
")`
);
}
...
...
@@ -92,42 +76,20 @@ const AddMemoRelationPopover = (props: Props) => {
);
};
const
addMemoRelations
=
async
()
=>
{
// If embedded mode is enabled, embed the memo instead of creating a relation.
if
(
embedded
)
{
if
(
!
editorRef
.
current
)
{
toast
.
error
(
t
(
"message.failed-to-embed-memo"
));
return
;
}
const
cursorPosition
=
editorRef
.
current
.
getCursorPosition
();
const
prevValue
=
editorRef
.
current
.
getContent
().
slice
(
0
,
cursorPosition
);
if
(
prevValue
!==
""
&&
!
prevValue
.
endsWith
(
"
\n
"
))
{
editorRef
.
current
.
insertText
(
"
\n
"
);
}
for
(
const
memo
of
selectedMemos
)
{
editorRef
.
current
.
insertText
(
`![[memos/
${
extractMemoIdFromName
(
memo
.
name
)}
]]\n`
);
}
setTimeout
(()
=>
{
editorRef
.
current
?.
scrollToCursor
();
editorRef
.
current
?.
focus
();
});
}
else
{
context
.
setRelationList
(
uniqBy
(
[
...
selectedMemos
.
map
((
memo
)
=>
({
memo
:
MemoRelation_Memo
.
fromPartial
({
name
:
memo
.
name
}),
relatedMemo
:
MemoRelation_Memo
.
fromPartial
({
name
:
memo
.
name
}),
type
:
MemoRelation_Type
.
REFERENCE
,
})),
...
context
.
relationList
,
].
filter
((
relation
)
=>
relation
.
relatedMemo
!==
context
.
memoName
),
"relatedMemo"
,
),
);
}
setSelectedMemos
([]);
const
addMemoRelations
=
async
(
memo
:
Memo
)
=>
{
context
.
setRelationList
(
uniqBy
(
[
{
memo
:
MemoRelation_Memo
.
fromPartial
({
name
:
memo
.
name
}),
relatedMemo
:
MemoRelation_Memo
.
fromPartial
({
name
:
memo
.
name
}),
type
:
MemoRelation_Type
.
REFERENCE
,
},
...
context
.
relationList
,
].
filter
((
relation
)
=>
relation
.
relatedMemo
!==
context
.
memoName
),
"relatedMemo"
,
),
);
setPopoverOpen
(
false
);
};
...
...
@@ -140,24 +102,6 @@ const AddMemoRelationPopover = (props: Props) => {
</
PopoverTrigger
>
<
PopoverContent
align=
"center"
>
<
div
className=
"w-[16rem] p-1 flex flex-col justify-start items-start"
>
{
/* Selected memos display */
}
{
selectedMemos
.
length
>
0
&&
(
<
div
className=
"w-full mb-2 flex flex-wrap gap-1"
>
{
selectedMemos
.
map
((
memo
)
=>
(
<
Badge
key=
{
memo
.
name
}
variant=
"outline"
className=
"max-w-full flex items-center gap-1 p-2"
>
<
div
className=
"flex-1 min-w-0"
>
<
p
className=
"text-xs text-muted-foreground select-none"
>
{
memo
.
displayTime
?.
toLocaleString
()
}
</
p
>
<
span
className=
"text-sm leading-5 truncate block"
>
{
memo
.
content
}
</
span
>
</
div
>
<
X
className=
"w-3 h-3 cursor-pointer hover:text-destructive flex-shrink-0"
onClick=
{
()
=>
setSelectedMemos
((
memos
)
=>
memos
.
filter
((
m
)
=>
m
.
name
!==
memo
.
name
))
}
/>
</
Badge
>
))
}
</
div
>
)
}
{
/* Search and selection interface */
}
<
div
className=
"w-full"
>
<
Input
...
...
@@ -177,7 +121,7 @@ const AddMemoRelationPopover = (props: Props) => {
key=
{
memo
.
name
}
className=
"relative flex cursor-pointer items-start gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground"
onClick=
{
()
=>
{
setSelectedMemos
((
prev
)
=>
[...
prev
,
memo
]
);
addMemoRelations
(
memo
);
}
}
>
<
div
className=
"w-full flex flex-col justify-start items-start"
>
...
...
@@ -191,18 +135,6 @@ const AddMemoRelationPopover = (props: Props) => {
)
}
</
div
>
</
div
>
<
div
className=
"mt-2 w-full flex flex-row justify-end items-center gap-2"
>
<
div
className=
"flex items-center space-x-2"
>
<
Checkbox
id=
"embed-checkbox"
checked=
{
embedded
}
onCheckedChange=
{
(
checked
)
=>
setEmbedded
(
checked
===
true
)
}
/>
<
label
htmlFor=
"embed-checkbox"
className=
"text-sm"
>
Embed
</
label
>
</
div
>
<
Button
onClick=
{
addMemoRelations
}
disabled=
{
selectedMemos
.
length
===
0
}
>
{
t
(
"common.add"
)
}
</
Button
>
</
div
>
</
div
>
</
PopoverContent
>
</
Popover
>
...
...
web/src/components/MemoEditor/Editor/index.tsx
View file @
f78b2774
...
...
@@ -216,7 +216,7 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef<
<
div
className=
{
cn
(
"flex flex-col justify-start items-start relative w-full h-auto max-h-[50vh] bg-inherit"
,
className
)
}
>
<
textarea
className=
"w-full h-full my-1 text-base resize-none overflow-x-hidden overflow-y-auto bg-transparent outline-none placeholder:opacity-70 whitespace-pre-wrap break-words"
rows=
{
2
}
rows=
{
1
}
placeholder=
{
placeholder
}
ref=
{
editorRef
}
onPaste=
{
onPaste
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment