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
9b5b7b1e
Commit
9b5b7b1e
authored
Jul 23, 2025
by
johnnyjoy
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'main' of
https://github.com/usememos/memos
parents
c5767bf9
2c7eb233
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
86 additions
and
31 deletions
+86
-31
activity_service.go
server/router/api/v1/activity_service.go
+3
-0
MemoCommentMessage.tsx
web/src/components/Inbox/MemoCommentMessage.tsx
+71
-29
de.json
web/src/locales/de.json
+2
-1
en.json
web/src/locales/en.json
+2
-1
user.ts
web/src/store/user.ts
+8
-0
No files found.
server/router/api/v1/activity_service.go
View file @
9b5b7b1e
...
...
@@ -108,6 +108,9 @@ func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payl
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get memo: %v"
,
err
)
}
if
memo
==
nil
{
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"memo does not exist"
)
}
relatedMemo
,
err
:=
s
.
Store
.
GetMemo
(
ctx
,
&
store
.
FindMemo
{
ID
:
&
payload
.
MemoComment
.
RelatedMemoId
,
ExcludeContent
:
true
,
...
...
web/src/components/Inbox/MemoCommentMessage.tsx
View file @
9b5b7b1e
import
{
InboxIcon
,
LoaderIcon
,
MessageCircleIcon
}
from
"lucide-react"
;
import
{
InboxIcon
,
LoaderIcon
,
MessageCircleIcon
,
TrashIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useState
}
from
"react"
;
import
toast
from
"react-hot-toast"
;
...
...
@@ -24,24 +24,32 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
const
[
relatedMemo
,
setRelatedMemo
]
=
useState
<
Memo
|
undefined
>
(
undefined
);
const
[
sender
,
setSender
]
=
useState
<
User
|
undefined
>
(
undefined
);
const
[
initialized
,
setInitialized
]
=
useState
<
boolean
>
(
false
);
const
[
hasError
,
setHasError
]
=
useState
<
boolean
>
(
false
);
useAsyncEffect
(
async
()
=>
{
if
(
!
inbox
.
activityId
)
{
return
;
}
const
activity
=
await
activityServiceClient
.
getActivity
({
name
:
`
${
activityNamePrefix
}${
inbox
.
activityId
}
`
,
});
if
(
activity
.
payload
?.
memoComment
)
{
const
memoCommentPayload
=
activity
.
payload
.
memoComment
;
const
memo
=
await
memoStore
.
getOrFetchMemoByName
(
memoCommentPayload
.
relatedMemo
,
{
skipStore
:
true
,
try
{
const
activity
=
await
activityServiceClient
.
getActivity
({
name
:
`
${
activityNamePrefix
}${
inbox
.
activityId
}
`
,
});
setRelatedMemo
(
memo
);
const
sender
=
await
userStore
.
getOrFetchUserByName
(
inbox
.
sender
);
setSender
(
sender
);
setInitialized
(
true
);
if
(
activity
.
payload
?.
memoComment
)
{
const
memoCommentPayload
=
activity
.
payload
.
memoComment
;
const
memo
=
await
memoStore
.
getOrFetchMemoByName
(
memoCommentPayload
.
relatedMemo
,
{
skipStore
:
true
,
});
setRelatedMemo
(
memo
);
const
sender
=
await
userStore
.
getOrFetchUserByName
(
inbox
.
sender
);
setSender
(
sender
);
setInitialized
(
true
);
}
}
catch
(
error
)
{
console
.
error
(
"Failed to fetch activity:"
,
error
);
setHasError
(
true
);
return
;
}
},
[
inbox
.
activityId
]);
...
...
@@ -69,6 +77,51 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
}
};
const
handleDeleteMessage
=
async
()
=>
{
await
userStore
.
deleteInbox
(
inbox
.
name
);
toast
.
success
(
t
(
"message.deleted-successfully"
));
};
const
deleteButton
=
()
=>
(
<>
<
div
>
<
TooltipProvider
>
<
Tooltip
>
<
TooltipTrigger
>
<
TrashIcon
className=
"w-4 h-auto cursor-pointer text-muted-foreground hover:text-primary"
onClick=
{
()
=>
handleDeleteMessage
()
}
/>
</
TooltipTrigger
>
<
TooltipContent
>
<
p
>
{
t
(
"common.delete"
)
}
</
p
>
</
TooltipContent
>
</
Tooltip
>
</
TooltipProvider
>
</
div
>
</>
);
const
archiveButton
=
()
=>
(
<>
<
div
>
<
TooltipProvider
>
<
Tooltip
>
<
TooltipTrigger
>
<
InboxIcon
className=
"w-4 h-auto cursor-pointer text-muted-foreground hover:text-primary"
onClick=
{
()
=>
handleArchiveMessage
()
}
/>
</
TooltipTrigger
>
<
TooltipContent
>
<
p
>
{
t
(
"common.archive"
)
}
</
p
>
</
TooltipContent
>
</
Tooltip
>
</
TooltipProvider
>
</
div
>
</>
);
return
(
<
div
className=
"w-full flex flex-row justify-start items-start gap-3"
>
<
div
...
...
@@ -100,23 +153,7 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
<>
<
div
className=
"w-full flex flex-row justify-between items-center"
>
<
span
className=
"text-sm text-muted-foreground"
>
{
inbox
.
createTime
?.
toLocaleString
()
}
</
span
>
<
div
>
{
inbox
.
status
===
Inbox_Status
.
UNREAD
&&
(
<
TooltipProvider
>
<
Tooltip
>
<
TooltipTrigger
>
<
InboxIcon
className=
"w-4 h-auto cursor-pointer text-muted-foreground hover:text-primary"
onClick=
{
()
=>
handleArchiveMessage
()
}
/>
</
TooltipTrigger
>
<
TooltipContent
>
<
p
>
{
t
(
"common.archive"
)
}
</
p
>
</
TooltipContent
>
</
Tooltip
>
</
TooltipProvider
>
)
}
</
div
>
{
inbox
.
status
===
Inbox_Status
.
UNREAD
?
archiveButton
()
:
deleteButton
()
}
</
div
>
<
p
className=
"text-base leading-tight cursor-pointer text-muted-foreground hover:underline hover:text-primary"
...
...
@@ -129,6 +166,11 @@ const MemoCommentMessage = observer(({ inbox }: Props) => {
})
}
</
p
>
</>
)
:
hasError
?
(
<
div
className=
"w-full flex flex-row justify-between items-center"
>
<
span
className=
"text-sm text-muted-foreground"
>
{
t
(
"inbox.failed-to-load"
)
}
</
span
>
{
deleteButton
()
}
</
div
>
)
:
(
<
div
className=
"w-full flex flex-row justify-center items-center my-2"
>
<
LoaderIcon
className=
"animate-spin text-muted-foreground"
/>
...
...
web/src/locales/de.json
View file @
9b5b7b1e
...
...
@@ -107,7 +107,8 @@
},
"inbox"
:
{
"memo-comment"
:
"{{user}} hat einen Kommentar zu {{memo}} hinterlassen."
,
"version-update"
:
"Die neue Version {{version}} ist jetzt verfügbar!"
"version-update"
:
"Die neue Version {{version}} ist jetzt verfügbar!"
,
"failed-to-load"
:
"Fehler beim Laden des Eintrags"
},
"markdown"
:
{
"checkbox"
:
"Checkbox"
,
...
...
web/src/locales/en.json
View file @
9b5b7b1e
...
...
@@ -123,7 +123,8 @@
},
"inbox"
:
{
"memo-comment"
:
"{{user}} has a comment on your {{memo}}."
,
"version-update"
:
"New version {{version}} is available now!"
"version-update"
:
"New version {{version}} is available now!"
,
"failed-to-load"
:
"Failed to load inbox item"
},
"markdown"
:
{
"checkbox"
:
"Checkbox"
,
...
...
web/src/store/user.ts
View file @
9b5b7b1e
...
...
@@ -189,6 +189,13 @@ const userStore = (() => {
return
updatedInbox
;
};
const
deleteInbox
=
async
(
name
:
string
)
=>
{
await
inboxServiceClient
.
deleteInbox
({
name
});
state
.
setPartial
({
inboxes
:
state
.
inboxes
.
filter
((
i
)
=>
i
.
name
!==
name
),
});
};
const
fetchUserStats
=
async
(
user
?:
string
)
=>
{
const
userStatsByName
:
Record
<
string
,
UserStats
>
=
{};
if
(
!
user
)
{
...
...
@@ -224,6 +231,7 @@ const userStore = (() => {
fetchShortcuts
,
fetchInboxes
,
updateInbox
,
deleteInbox
,
fetchUserStats
,
setStatsStateId
,
};
...
...
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