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
a69e405c
Commit
a69e405c
authored
Mar 03, 2026
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: remove dead code and deduplicate comment amount logic
parent
3a5d3c8f
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
39 additions
and
52 deletions
+39
-52
MemoView.tsx
web/src/components/MemoView/MemoView.tsx
+8
-12
MemoViewContext.tsx
web/src/components/MemoView/MemoViewContext.tsx
+4
-4
MemoCommentListView.tsx
...rc/components/MemoView/components/MemoCommentListView.tsx
+13
-5
useImagePreview.ts
web/src/components/MemoView/hooks/useImagePreview.ts
+0
-2
useMemoActions.ts
web/src/components/MemoView/hooks/useMemoActions.ts
+2
-20
MemoDetail.tsx
web/src/pages/MemoDetail.tsx
+12
-9
No files found.
web/src/components/MemoView/MemoView.tsx
View file @
a69e405c
...
...
@@ -4,18 +4,17 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import
{
useUser
}
from
"@/hooks/useUserQueries"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
State
}
from
"@/types/proto/api/v1/common_pb"
;
import
{
MemoRelation_Type
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
{
isSuperUser
}
from
"@/utils/user"
;
import
MemoEditor
from
"../MemoEditor"
;
import
PreviewImageDialog
from
"../PreviewImageDialog"
;
import
{
MemoBody
,
MemoCommentListView
,
MemoHeader
}
from
"./components"
;
import
{
MEMO_CARD_BASE_CLASSES
}
from
"./constants"
;
import
{
useImagePreview
,
useMemoActions
,
useMemoHandlers
}
from
"./hooks"
;
import
{
MemoViewContext
}
from
"./MemoViewContext"
;
import
{
computeCommentAmount
,
MemoViewContext
}
from
"./MemoViewContext"
;
import
type
{
MemoViewProps
}
from
"./types"
;
const
MemoView
:
React
.
FC
<
MemoViewProps
>
=
(
props
:
MemoViewProps
)
=>
{
const
{
memo
:
memoData
,
className
,
parentPage
:
parentPageProp
}
=
props
;
const
{
memo
:
memoData
,
className
,
parentPage
:
parentPageProp
,
compact
,
showCreator
,
showVisibility
,
showPinned
}
=
props
;
const
cardRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
[
showEditor
,
setShowEditor
]
=
useState
(
false
);
...
...
@@ -31,7 +30,7 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
const
toggleNsfwVisibility
=
()
=>
setShowNSFWContent
((
prev
)
=>
!
prev
);
const
{
previewState
,
openPreview
,
setPreviewOpen
}
=
useImagePreview
();
const
{
unpinMemo
}
=
useMemoActions
(
memoData
,
isArchived
);
const
{
unpinMemo
}
=
useMemoActions
(
memoData
);
const
closeEditor
=
()
=>
setShowEditor
(
false
);
const
openEditor
=
()
=>
setShowEditor
(
true
);
...
...
@@ -46,10 +45,7 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
const
location
=
useLocation
();
const
isInMemoDetailPage
=
location
.
pathname
.
startsWith
(
`/
${
memoData
.
name
}
`
);
const
commentAmount
=
memoData
.
relations
.
filter
(
(
r
)
=>
r
.
type
===
MemoRelation_Type
.
COMMENT
&&
r
.
relatedMemo
?.
name
===
memoData
.
name
,
).
length
;
const
showCommentPreview
=
!
isInMemoDetailPage
&&
commentAmount
>
0
;
const
showCommentPreview
=
!
isInMemoDetailPage
&&
computeCommentAmount
(
memoData
)
>
0
;
const
contextValue
=
useMemo
(
()
=>
({
...
...
@@ -85,16 +81,16 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
tabIndex=
{
readonly
?
-
1
:
0
}
>
<
MemoHeader
showCreator=
{
props
.
showCreator
}
showVisibility=
{
props
.
showVisibility
}
showPinned=
{
props
.
showPinned
}
showCreator=
{
showCreator
}
showVisibility=
{
showVisibility
}
showPinned=
{
showPinned
}
onEdit=
{
openEditor
}
onGotoDetail=
{
handleGotoMemoDetailPage
}
onUnpin=
{
unpinMemo
}
/>
<
MemoBody
compact=
{
props
.
compact
}
compact=
{
compact
}
onContentClick=
{
handleMemoContentClick
}
onContentDoubleClick=
{
handleMemoContentDoubleClick
}
onToggleNsfwVisibility=
{
toggleNsfwVisibility
}
...
...
web/src/components/MemoView/MemoViewContext.tsx
View file @
a69e405c
...
...
@@ -27,15 +27,15 @@ export const useMemoViewContext = (): MemoViewContextValue => {
return
context
;
};
export
const
computeCommentAmount
=
(
memo
:
Memo
):
number
=>
memo
.
relations
.
filter
((
r
)
=>
r
.
type
===
MemoRelation_Type
.
COMMENT
&&
r
.
relatedMemo
?.
name
===
memo
.
name
).
length
;
export
const
useMemoViewDerived
=
()
=>
{
const
{
memo
,
isArchived
,
readonly
}
=
useMemoViewContext
();
const
location
=
useLocation
();
const
isInMemoDetailPage
=
location
.
pathname
.
startsWith
(
`/
${
memo
.
name
}
`
);
const
commentAmount
=
memo
.
relations
.
filter
(
(
relation
)
=>
relation
.
type
===
MemoRelation_Type
.
COMMENT
&&
relation
.
relatedMemo
?.
name
===
memo
.
name
,
).
length
;
const
commentAmount
=
computeCommentAmount
(
memo
);
const
displayTime
=
memo
.
displayTime
?
timestampDate
(
memo
.
displayTime
)
:
undefined
;
const
relativeTimeFormat
:
"datetime"
|
"auto"
=
...
...
web/src/components/MemoView/components/MemoCommentListView.tsx
View file @
a69e405c
import
{
ArrowUpRightIcon
}
from
"lucide-react"
;
import
{
Link
}
from
"react-router-dom"
;
import
{
extractMemoIdFromName
}
from
"@/helpers/resource-names"
;
import
{
useMemoComments
}
from
"@/hooks/useMemoQueries"
;
import
{
useMemoViewContext
,
useMemoViewDerived
}
from
"../MemoViewContext"
;
...
...
@@ -28,11 +29,18 @@ const MemoCommentListView: React.FC = () => {
<
ArrowUpRightIcon
className=
"w-3 h-3"
/>
</
Link
>
</
div
>
{
displayedComments
.
map
((
comment
)
=>
(
<
div
key=
{
comment
.
name
}
className=
"bg-muted/60 rounded-md px-2 py-1 text-xs text-muted-foreground truncate leading-relaxed"
>
{
comment
.
content
}
</
div
>
))
}
{
displayedComments
.
map
((
comment
)
=>
{
const
uid
=
extractMemoIdFromName
(
comment
.
name
);
return
(
<
Link
key=
{
comment
.
name
}
to=
{
`/${memo.name}#${uid}`
}
className=
"bg-muted/60 rounded-md px-2 py-1 text-xs text-muted-foreground truncate leading-relaxed hover:bg-muted transition-colors block"
>
{
comment
.
content
}
</
Link
>
);
})
}
</
div
>
);
};
...
...
web/src/components/MemoView/hooks/useImagePreview.ts
View file @
a69e405c
...
...
@@ -9,7 +9,6 @@ export interface ImagePreviewState {
export
interface
UseImagePreviewReturn
{
previewState
:
ImagePreviewState
;
openPreview
:
(
url
:
string
)
=>
void
;
closePreview
:
()
=>
void
;
setPreviewOpen
:
(
open
:
boolean
)
=>
void
;
}
...
...
@@ -19,7 +18,6 @@ export const useImagePreview = (): UseImagePreviewReturn => {
return
{
previewState
,
openPreview
:
(
url
:
string
)
=>
setPreviewState
({
open
:
true
,
urls
:
[
url
],
index
:
0
}),
closePreview
:
()
=>
setPreviewState
({
open
:
false
,
urls
:
[],
index
:
0
}),
setPreviewOpen
:
(
open
:
boolean
)
=>
setPreviewState
((
prev
)
=>
({
...
prev
,
open
})),
};
};
web/src/components/MemoView/hooks/useMemoActions.ts
View file @
a69e405c
import
toast
from
"react-hot-toast"
;
import
{
useUpdateMemo
}
from
"@/hooks/useMemoQueries"
;
import
{
handleError
}
from
"@/lib/error"
;
import
{
State
}
from
"@/types/proto/api/v1/common_pb"
;
import
type
{
Memo
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
export
const
useMemoActions
=
(
memo
:
Memo
,
isArchived
:
boolean
)
=>
{
const
t
=
useTranslate
();
export
const
useMemoActions
=
(
memo
:
Memo
)
=>
{
const
{
mutateAsync
:
updateMemo
}
=
useUpdateMemo
();
const
archiveMemo
=
async
()
=>
{
if
(
isArchived
)
return
;
try
{
await
updateMemo
({
update
:
{
name
:
memo
.
name
,
state
:
State
.
ARCHIVED
},
updateMask
:
[
"state"
]
});
toast
.
success
(
t
(
"message.archived-successfully"
));
}
catch
(
error
:
unknown
)
{
handleError
(
error
,
toast
.
error
,
{
context
:
"Archive memo"
,
fallbackMessage
:
"Failed to archive memo"
,
});
}
};
const
unpinMemo
=
async
()
=>
{
if
(
!
memo
.
pinned
)
return
;
await
updateMemo
({
update
:
{
name
:
memo
.
name
,
pinned
:
false
},
updateMask
:
[
"pinned"
]
});
};
return
{
archiveMemo
,
unpinMemo
};
return
{
unpinMemo
};
};
web/src/pages/MemoDetail.tsx
View file @
a69e405c
import
{
ConnectError
}
from
"@connectrpc/connect"
;
import
{
ArrowUpLeftFromCircleIcon
,
MessageCircleIcon
}
from
"lucide-react"
;
import
{
useState
}
from
"react"
;
import
{
use
Effect
,
use
State
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
Link
,
useLocation
,
useParams
}
from
"react-router-dom"
;
import
{
MemoDetailSidebar
,
MemoDetailSidebarDrawer
}
from
"@/components/MemoDetailSidebar"
;
...
...
@@ -8,7 +8,7 @@ import MemoEditor from "@/components/MemoEditor";
import
MemoView
from
"@/components/MemoView"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
{
Button
}
from
"@/components/ui/button"
;
import
{
memoNamePrefix
}
from
"@/helpers/resource-names"
;
import
{
extractMemoIdFromName
,
memoNamePrefix
}
from
"@/helpers/resource-names"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useMediaQuery
from
"@/hooks/useMediaQuery"
;
import
{
useMemo
,
useMemoComments
}
from
"@/hooks/useMemoQueries"
;
...
...
@@ -47,6 +47,13 @@ const MemoDetail = () => {
});
const
comments
=
commentsResponse
?.
memos
||
[];
const
{
hash
}
=
useLocation
();
useEffect
(()
=>
{
if
(
!
hash
||
comments
.
length
===
0
)
return
;
const
el
=
document
.
getElementById
(
hash
.
slice
(
1
));
el
?.
scrollIntoView
({
behavior
:
"smooth"
,
block
:
"center"
});
},
[
hash
,
comments
]);
const
showCreateCommentButton
=
currentUser
&&
!
showCommentEditor
;
if
(
isLoading
||
!
memo
)
{
...
...
@@ -135,13 +142,9 @@ const MemoDetail = () => {
</
div
>
)
}
{
comments
.
map
((
comment
)
=>
(
<
MemoView
key=
{
`${comment.name}-${comment.displayTime}`
}
memo=
{
comment
}
parentPage=
{
locationState
?.
from
}
showCreator
compact
/>
<
div
key=
{
`${comment.name}-${comment.displayTime}`
}
id=
{
extractMemoIdFromName
(
comment
.
name
)
}
>
<
MemoView
memo=
{
comment
}
parentPage=
{
locationState
?.
from
}
showCreator
compact
/>
</
div
>
))
}
</
div
>
</
div
>
...
...
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