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
aeb1e5fe
Commit
aeb1e5fe
authored
Feb 12, 2026
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
perf(web): eliminate redundant fetch when opening inline memo editor
parent
69485eec
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
43 additions
and
88 deletions
+43
-88
useMemoInit.ts
web/src/components/MemoEditor/hooks/useMemoInit.ts
+25
-54
index.tsx
web/src/components/MemoEditor/index.tsx
+9
-20
memoService.ts
web/src/components/MemoEditor/services/memoService.ts
+3
-8
components.ts
web/src/components/MemoEditor/types/components.ts
+2
-1
MemoView.tsx
web/src/components/MemoView/MemoView.tsx
+4
-5
No files found.
web/src/components/MemoEditor/hooks/useMemoInit.ts
View file @
aeb1e5fe
import
{
useQueryClient
}
from
"@tanstack/react-query"
;
import
{
useEffect
,
useRef
}
from
"react"
;
import
{
memoKeys
}
from
"@/hooks/useMemoQueries"
;
import
type
{
Visibility
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
type
{
Memo
,
Visibility
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
type
{
EditorRefActions
}
from
"../Editor"
;
import
{
cacheService
,
memoService
}
from
"../services"
;
import
{
useEditorContext
}
from
"../state"
;
export
const
useMemoInit
=
(
editorRef
:
React
.
RefObject
<
EditorRefActions
|
null
>
,
memoName
:
string
|
undefined
,
cacheKey
:
string
|
undefined
,
username
:
string
,
autoFocus
?:
boolean
,
defaultVisibility
?:
Visibility
,
)
=>
{
interface
UseMemoInitOptions
{
editorRef
:
React
.
RefObject
<
EditorRefActions
|
null
>
;
memo
?:
Memo
;
cacheKey
?:
string
;
username
:
string
;
autoFocus
?:
boolean
;
defaultVisibility
?:
Visibility
;
}
export
const
useMemoInit
=
({
editorRef
,
memo
,
cacheKey
,
username
,
autoFocus
,
defaultVisibility
}:
UseMemoInitOptions
)
=>
{
const
{
actions
,
dispatch
}
=
useEditorContext
();
const
queryClient
=
useQueryClient
();
const
initializedRef
=
useRef
(
false
);
useEffect
(()
=>
{
if
(
initializedRef
.
current
)
return
;
initializedRef
.
current
=
true
;
const
init
=
async
()
=>
{
dispatch
(
actions
.
setLoading
(
"loading"
,
true
));
try
{
if
(
memoName
)
{
// Force refetch from server to prevent stale data issues
// See: https://github.com/usememos/memos/issues/5470
await
queryClient
.
invalidateQueries
({
queryKey
:
memoKeys
.
detail
(
memoName
)
});
// Load existing memo
const
loadedState
=
await
memoService
.
load
(
memoName
);
dispatch
(
actions
.
initMemo
({
content
:
loadedState
.
content
,
metadata
:
loadedState
.
metadata
,
timestamps
:
loadedState
.
timestamps
,
}),
);
}
else
{
// Load from cache for new memo
const
cachedContent
=
cacheService
.
load
(
cacheService
.
key
(
username
,
cacheKey
));
if
(
cachedContent
)
{
dispatch
(
actions
.
updateContent
(
cachedContent
));
}
// Apply default visibility for new memos
if
(
defaultVisibility
!==
undefined
)
{
dispatch
(
actions
.
setMetadata
({
visibility
:
defaultVisibility
}));
}
}
}
catch
(
error
)
{
console
.
error
(
"Failed to initialize editor:"
,
error
);
}
finally
{
dispatch
(
actions
.
setLoading
(
"loading"
,
false
));
if
(
autoFocus
)
{
setTimeout
(()
=>
{
editorRef
.
current
?.
focus
();
},
100
);
}
if
(
memo
)
{
dispatch
(
actions
.
initMemo
(
memoService
.
fromMemo
(
memo
)));
}
else
{
const
cachedContent
=
cacheService
.
load
(
cacheService
.
key
(
username
,
cacheKey
));
if
(
cachedContent
)
{
dispatch
(
actions
.
updateContent
(
cachedContent
));
}
if
(
defaultVisibility
!==
undefined
)
{
dispatch
(
actions
.
setMetadata
({
visibility
:
defaultVisibility
}));
}
}
;
}
init
();
},
[
memoName
,
cacheKey
,
username
,
autoFocus
,
defaultVisibility
,
actions
,
dispatch
,
editorRef
,
queryClient
]);
if
(
autoFocus
)
{
setTimeout
(()
=>
editorRef
.
current
?.
focus
(),
100
);
}
},
[
memo
,
cacheKey
,
username
,
autoFocus
,
defaultVisibility
,
actions
,
dispatch
,
editorRef
]);
};
web/src/components/MemoEditor/index.tsx
View file @
aeb1e5fe
...
...
@@ -17,29 +17,16 @@ import { cacheService, errorService, memoService, validationService } from "./se
import
{
EditorProvider
,
useEditorContext
}
from
"./state"
;
import
type
{
MemoEditorProps
}
from
"./types"
;
const
MemoEditor
=
(
props
:
MemoEditorProps
)
=>
{
const
{
className
,
cacheKey
,
memoName
,
parentMemoName
,
autoFocus
,
placeholder
,
onConfirm
,
onCancel
}
=
props
;
return
(
<
EditorProvider
>
<
MemoEditorImpl
className=
{
className
}
cacheKey=
{
cacheKey
}
memoName=
{
memoName
}
parentMemoName=
{
parentMemoName
}
autoFocus=
{
autoFocus
}
placeholder=
{
placeholder
}
onConfirm=
{
onConfirm
}
onCancel=
{
onCancel
}
/>
</
EditorProvider
>
);
};
const
MemoEditor
=
(
props
:
MemoEditorProps
)
=>
(
<
EditorProvider
>
<
MemoEditorImpl
{
...
props
}
/>
</
EditorProvider
>
);
const
MemoEditorImpl
:
React
.
FC
<
MemoEditorProps
>
=
({
className
,
cacheKey
,
memo
Name
,
memo
,
parentMemoName
,
autoFocus
,
placeholder
,
...
...
@@ -53,10 +40,12 @@ const MemoEditorImpl: React.FC<MemoEditorProps> = ({
const
{
state
,
actions
,
dispatch
}
=
useEditorContext
();
const
{
userGeneralSetting
}
=
useAuth
();
const
memoName
=
memo
?.
name
;
// Get default visibility from user settings
const
defaultVisibility
=
userGeneralSetting
?.
memoVisibility
?
convertVisibilityFromString
(
userGeneralSetting
.
memoVisibility
)
:
undefined
;
useMemoInit
(
editorRef
,
memoName
,
cacheKey
,
currentUser
?.
name
??
""
,
autoFocus
,
defaultVisibility
);
useMemoInit
(
{
editorRef
,
memo
,
cacheKey
,
username
:
currentUser
?.
name
??
""
,
autoFocus
,
defaultVisibility
}
);
// Auto-save content to localStorage
useAutoSave
(
state
.
content
,
currentUser
?.
name
??
""
,
cacheKey
);
...
...
web/src/components/MemoEditor/services/memoService.ts
View file @
aeb1e5fe
...
...
@@ -122,9 +122,8 @@ export const memoService = {
return
{
memoName
:
memo
.
name
,
hasChanges
:
true
};
},
async
load
(
memoName
:
string
):
Promise
<
EditorState
>
{
const
memo
=
await
memoServiceClient
.
getMemo
({
name
:
memoName
});
/** Build editor state from an already-loaded Memo entity (no network request). */
fromMemo
(
memo
:
Memo
):
EditorState
{
return
{
content
:
memo
.
content
,
metadata
:
{
...
...
@@ -135,11 +134,7 @@ export const memoService = {
},
ui
:
{
isFocusMode
:
false
,
isLoading
:
{
saving
:
false
,
uploading
:
false
,
loading
:
false
,
},
isLoading
:
{
saving
:
false
,
uploading
:
false
,
loading
:
false
},
isDragging
:
false
,
isComposing
:
false
,
},
...
...
web/src/components/MemoEditor/types/components.ts
View file @
aeb1e5fe
...
...
@@ -8,7 +8,8 @@ export interface MemoEditorProps {
className
?:
string
;
cacheKey
?:
string
;
placeholder
?:
string
;
memoName
?:
string
;
/** Existing memo to edit. When provided, the editor initializes from it without fetching. */
memo
?:
Memo
;
parentMemoName
?:
string
;
autoFocus
?:
boolean
;
onConfirm
?:
(
memoName
:
string
)
=>
void
;
...
...
web/src/components/MemoView/MemoView.tsx
View file @
aeb1e5fe
...
...
@@ -31,8 +31,7 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
const
{
previewState
,
openPreview
,
setPreviewOpen
}
=
useImagePreview
();
const
{
unpinMemo
}
=
useMemoActions
(
memoData
,
isArchived
);
const
handleEditorConfirm
=
()
=>
setShowEditor
(
false
);
const
handleEditorCancel
=
()
=>
setShowEditor
(
false
);
const
closeEditor
=
()
=>
setShowEditor
(
false
);
const
openEditor
=
()
=>
setShowEditor
(
true
);
const
{
handleGotoMemoDetailPage
,
handleMemoContentClick
,
handleMemoContentDoubleClick
}
=
useMemoHandlers
({
...
...
@@ -63,9 +62,9 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
autoFocus
className=
"mb-2"
cacheKey=
{
`inline-memo-editor-${memoData.name}`
}
memo
Name=
{
memoData
.
name
}
onConfirm=
{
handleEditorConfirm
}
onCancel=
{
handleEditorCancel
}
memo
=
{
memoData
}
onConfirm=
{
closeEditor
}
onCancel=
{
closeEditor
}
/>
);
}
...
...
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