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
ac7121c2
Commit
ac7121c2
authored
Jan 10, 2025
by
johnnyjoy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: update archived page
parent
5d40f389
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
82 additions
and
152 deletions
+82
-152
MemoActionMenu.tsx
web/src/components/MemoActionMenu.tsx
+62
-50
MemoReactionListView.tsx
web/src/components/MemoReactionListView.tsx
+3
-1
MemoView.tsx
web/src/components/MemoView.tsx
+9
-42
ReactionView.tsx
web/src/components/ReactionView.tsx
+4
-2
Archived.tsx
web/src/pages/Archived.tsx
+4
-57
No files found.
web/src/components/MemoActionMenu.tsx
View file @
ac7121c2
...
...
@@ -24,17 +24,32 @@ import { useTranslate } from "@/utils/i18n";
interface
Props
{
memo
:
Memo
;
readonly
?:
boolean
;
className
?:
string
;
hiddenActions
?:
(
"edit"
|
"archive"
|
"delete"
|
"share"
|
"pin"
|
"remove_completed_task_list"
)[];
onEdit
?:
()
=>
void
;
}
const
checkHasCompletedTaskList
=
(
memo
:
Memo
)
=>
{
for
(
const
node
of
memo
.
nodes
)
{
if
(
node
.
type
===
NodeType
.
LIST
&&
node
.
listNode
?.
children
&&
node
.
listNode
?.
children
?.
length
>
0
)
{
for
(
let
j
=
0
;
j
<
node
.
listNode
.
children
.
length
;
j
++
)
{
if
(
node
.
listNode
.
children
[
j
].
type
===
NodeType
.
TASK_LIST_ITEM
&&
node
.
listNode
.
children
[
j
].
taskListItemNode
?.
complete
)
{
return
true
;
}
}
}
}
return
false
;
};
const
MemoActionMenu
=
(
props
:
Props
)
=>
{
const
{
memo
,
hiddenActions
}
=
props
;
const
{
memo
,
readonly
}
=
props
;
const
t
=
useTranslate
();
const
location
=
useLocation
();
const
navigateTo
=
useNavigateTo
();
const
memoStore
=
useMemoStore
();
const
isArchived
=
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
;
const
hasCompletedTaskList
=
checkHasCompletedTaskList
(
memo
);
const
isInMemoDetailPage
=
location
.
pathname
.
startsWith
(
`/m/
${
memo
.
uid
}
`
);
const
handleTogglePinMemoBtnClick
=
async
()
=>
{
...
...
@@ -69,26 +84,17 @@ const MemoActionMenu = (props: Props) => {
};
const
handleToggleMemoStatusClick
=
async
()
=>
{
const
status
=
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
?
RowStatus
.
ACTIVE
:
RowStatus
.
ARCHIVED
;
const
message
=
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
?
t
(
"message.restored-successfully"
)
:
t
(
"message.archived-successfully"
);
try
{
if
(
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
)
{
await
memoStore
.
updateMemo
(
{
name
:
memo
.
name
,
rowStatus
:
RowStatus
.
ACTIVE
,
},
[
"row_status"
],
);
toast
(
t
(
"message.restored-successfully"
));
}
else
{
await
memoStore
.
updateMemo
(
{
name
:
memo
.
name
,
rowStatus
:
RowStatus
.
ARCHIVED
,
},
[
"row_status"
],
);
toast
.
success
(
t
(
"message.archived-successfully"
));
}
await
memoStore
.
updateMemo
(
{
name
:
memo
.
name
,
rowStatus
:
status
,
},
[
"row_status"
],
);
toast
(
message
);
}
catch
(
error
:
any
)
{
toast
.
error
(
error
.
details
);
console
.
error
(
error
);
...
...
@@ -155,38 +161,44 @@ const MemoActionMenu = (props: Props) => {
</
span
>
</
MenuButton
>
<
Menu
className=
"text-sm"
size=
"sm"
placement=
"bottom-end"
>
{
!
hiddenActions
?.
includes
(
"pin"
)
&&
(
<
MenuItem
onClick=
{
handleTogglePinMemoBtnClick
}
>
{
memo
.
pinned
?
<
BookmarkMinusIcon
className=
"w-4 h-auto"
/>
:
<
BookmarkPlusIcon
className=
"w-4 h-auto"
/>
}
{
memo
.
pinned
?
t
(
"common.unpin"
)
:
t
(
"common.pin"
)
}
</
MenuItem
>
{
!
readonly
&&
!
isArchived
&&
(
<>
<
MenuItem
onClick=
{
handleTogglePinMemoBtnClick
}
>
{
memo
.
pinned
?
<
BookmarkMinusIcon
className=
"w-4 h-auto"
/>
:
<
BookmarkPlusIcon
className=
"w-4 h-auto"
/>
}
{
memo
.
pinned
?
t
(
"common.unpin"
)
:
t
(
"common.pin"
)
}
</
MenuItem
>
<
MenuItem
onClick=
{
handleEditMemoClick
}
>
<
Edit3Icon
className=
"w-4 h-auto"
/>
{
t
(
"common.edit"
)
}
</
MenuItem
>
</>
)
}
{
!
hiddenActions
?.
includes
(
"edit"
)
&&
props
.
onEdit
&&
(
<
MenuItem
onClick=
{
handleEditMemoClick
}
>
<
Edit3Icon
className=
"w-4 h-auto"
/>
{
t
(
"common.edit"
)
}
</
MenuItem
>
)
}
{
!
hiddenActions
?.
includes
(
"share"
)
&&
(
<
MenuItem
onClick=
{
handleCopyLink
}
>
<
CopyIcon
className=
"w-4 h-auto"
/>
{
t
(
"memo.copy-link"
)
}
</
MenuItem
>
)
}
<
MenuItem
color=
"warning"
onClick=
{
handleToggleMemoStatusClick
}
>
{
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
?
<
ArchiveRestoreIcon
className=
"w-4 h-auto"
/>
:
<
ArchiveIcon
className=
"w-4 h-auto"
/>
}
{
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
?
t
(
"common.restore"
)
:
t
(
"common.archive"
)
}
<
MenuItem
onClick=
{
handleCopyLink
}
>
<
CopyIcon
className=
"w-4 h-auto"
/>
{
t
(
"memo.copy-link"
)
}
</
MenuItem
>
{
!
hiddenActions
?.
includes
(
"remove_completed_task_list"
)
&&
(
<
MenuItem
color=
"danger"
onClick=
{
handleRemoveCompletedTaskListItemsClick
}
>
<
SquareCheckIcon
className=
"w-4 h-auto"
/>
{
t
(
"memo.remove-completed-task-list-items"
)
}
</
MenuItem
>
{
!
readonly
&&
(
<>
{
!
isArchived
&&
hasCompletedTaskList
&&
(
<
MenuItem
color=
"danger"
onClick=
{
handleRemoveCompletedTaskListItemsClick
}
>
<
SquareCheckIcon
className=
"w-4 h-auto"
/>
{
t
(
"memo.remove-completed-task-list-items"
)
}
</
MenuItem
>
)
}
<
MenuItem
color=
"warning"
onClick=
{
handleToggleMemoStatusClick
}
>
{
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
?
(
<
ArchiveRestoreIcon
className=
"w-4 h-auto"
/>
)
:
(
<
ArchiveIcon
className=
"w-4 h-auto"
/>
)
}
{
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
?
t
(
"common.restore"
)
:
t
(
"common.archive"
)
}
</
MenuItem
>
<
MenuItem
color=
"danger"
onClick=
{
handleDeleteMemoClick
}
>
<
TrashIcon
className=
"w-4 h-auto"
/>
{
t
(
"common.delete"
)
}
</
MenuItem
>
</>
)
}
<
MenuItem
color=
"danger"
onClick=
{
handleDeleteMemoClick
}
>
<
TrashIcon
className=
"w-4 h-auto"
/>
{
t
(
"common.delete"
)
}
</
MenuItem
>
</
Menu
>
</
Dropdown
>
);
...
...
web/src/components/MemoReactionListView.tsx
View file @
ac7121c2
...
...
@@ -2,6 +2,7 @@ import { uniq } from "lodash-es";
import
{
memo
,
useEffect
,
useState
}
from
"react"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useUserStore
}
from
"@/store/v1"
;
import
{
RowStatus
}
from
"@/types/proto/api/v1/common"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
Reaction
}
from
"@/types/proto/api/v1/reaction_service"
;
import
{
User
}
from
"@/types/proto/api/v1/user_service"
;
...
...
@@ -18,6 +19,7 @@ const MemoReactionListView = (props: Props) => {
const
currentUser
=
useCurrentUser
();
const
userStore
=
useUserStore
();
const
[
reactionGroup
,
setReactionGroup
]
=
useState
<
Map
<
string
,
User
[]
>>
(
new
Map
());
const
readonly
=
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
;
useEffect
(()
=>
{
(
async
()
=>
{
...
...
@@ -38,7 +40,7 @@ const MemoReactionListView = (props: Props) => {
{
Array
.
from
(
reactionGroup
).
map
(([
reactionType
,
users
])
=>
{
return
<
ReactionView
key=
{
`${reactionType.toString()} ${users.length}`
}
memo=
{
memo
}
reactionType=
{
reactionType
}
users=
{
users
}
/>;
})
}
{
currentUser
&&
<
ReactionSelector
memo=
{
memo
}
/>
}
{
!
readonly
&&
currentUser
&&
<
ReactionSelector
memo=
{
memo
}
/>
}
</
div
>
)
);
...
...
web/src/components/MemoView.tsx
View file @
ac7121c2
...
...
@@ -7,7 +7,7 @@ import useAsyncEffect from "@/hooks/useAsyncEffect";
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useUserStore
,
useWorkspaceSettingStore
,
useMemoStore
}
from
"@/store/v1"
;
import
{
NodeType
}
from
"@/types/proto/api/v1/markdown_service
"
;
import
{
RowStatus
}
from
"@/types/proto/api/v1/common
"
;
import
{
MemoRelation_Type
}
from
"@/types/proto/api/v1/memo_relation_service"
;
import
{
Memo
,
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
WorkspaceMemoRelatedSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
...
...
@@ -29,7 +29,6 @@ import VisibilityIcon from "./VisibilityIcon";
interface
Props
{
memo
:
Memo
;
displayTimeFormat
?:
"auto"
|
"time"
;
compact
?:
boolean
;
showCreator
?:
boolean
;
showVisibility
?:
boolean
;
...
...
@@ -59,6 +58,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
(
relation
)
=>
relation
.
type
===
MemoRelation_Type
.
COMMENT
&&
relation
.
relatedMemo
?.
name
===
memo
.
name
,
).
length
;
const
relativeTimeFormat
=
Date
.
now
()
-
memo
.
displayTime
!
.
getTime
()
>
1000
*
60
*
60
*
24
?
"datetime"
:
"auto"
;
const
isArchived
=
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
;
const
readonly
=
memo
.
creator
!==
user
?.
name
&&
!
isSuperUser
(
user
);
const
isInMemoDetailPage
=
location
.
pathname
.
startsWith
(
`/m/
${
memo
.
uid
}
`
);
const
parentPage
=
props
.
parentPage
||
location
.
pathname
;
...
...
@@ -115,42 +115,11 @@ const MemoView: React.FC<Props> = (props: Props) => {
}
};
const
displayTime
=
props
.
displayTimeFormat
===
"time"
?
(
memo
.
displayTime
?.
toLocaleTimeString
()
)
:
(
<
relative
-
time
datetime=
{
memo
.
displayTime
?.
toISOString
()
}
format=
{
relativeTimeFormat
}
></
relative
-
time
>
);
const
handleHiddenActions
=
()
=>
{
const
hiddenActions
:
(
"edit"
|
"archive"
|
"delete"
|
"share"
|
"pin"
|
"remove_completed_task_list"
)[]
=
[];
if
(
!
props
.
showPinned
)
{
hiddenActions
.
push
(
"pin"
);
}
// check if the content has done tasks
let
hasCompletedTaskList
=
false
;
const
newNodes
=
JSON
.
parse
(
JSON
.
stringify
(
memo
.
nodes
));
for
(
let
i
=
0
;
i
<
newNodes
.
length
;
i
++
)
{
if
(
hasCompletedTaskList
)
{
break
;
}
if
(
newNodes
[
i
].
type
===
NodeType
.
LIST
&&
newNodes
[
i
].
listNode
?.
children
?.
length
>
0
)
{
for
(
let
j
=
0
;
j
<
newNodes
[
i
].
listNode
.
children
.
length
;
j
++
)
{
if
(
newNodes
[
i
].
listNode
.
children
[
j
].
type
===
NodeType
.
TASK_LIST_ITEM
&&
newNodes
[
i
].
listNode
.
children
[
j
].
taskListItemNode
?.
complete
)
{
hasCompletedTaskList
=
true
;
break
;
}
}
}
}
if
(
!
hasCompletedTaskList
)
{
hiddenActions
.
push
(
"remove_completed_task_list"
);
}
return
hiddenActions
;
};
const
displayTime
=
isArchived
?
(
memo
.
displayTime
?.
toLocaleString
()
)
:
(
<
relative
-
time
datetime=
{
memo
.
displayTime
?.
toISOString
()
}
format=
{
relativeTimeFormat
}
></
relative
-
time
>
);
return
(
<
div
...
...
@@ -213,7 +182,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
</
span
>
</
Tooltip
>
)
}
{
currentUser
&&
<
ReactionSelector
className=
"border-none w-auto h-auto"
memo=
{
memo
}
/>
}
{
currentUser
&&
!
isArchived
&&
<
ReactionSelector
className=
"border-none w-auto h-auto"
memo=
{
memo
}
/>
}
</
div
>
{
!
isInMemoDetailPage
&&
(
workspaceMemoRelatedSetting
.
enableComment
||
commentAmount
>
0
)
&&
(
<
Link
...
...
@@ -238,9 +207,7 @@ const MemoView: React.FC<Props> = (props: Props) => {
</
span
>
</
Tooltip
>
)
}
{
!
readonly
&&
(
<
MemoActionMenu
className=
"-ml-1"
memo=
{
memo
}
hiddenActions=
{
handleHiddenActions
()
}
onEdit=
{
()
=>
setShowEditor
(
true
)
}
/>
)
}
<
MemoActionMenu
className=
"-ml-1"
memo=
{
memo
}
readonly=
{
readonly
}
onEdit=
{
()
=>
setShowEditor
(
true
)
}
/>
</
div
>
</
div
>
<
MemoContent
...
...
web/src/components/ReactionView.tsx
View file @
ac7121c2
...
...
@@ -3,6 +3,7 @@ import clsx from "clsx";
import
{
memoServiceClient
}
from
"@/grpcweb"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
{
RowStatus
}
from
"@/types/proto/api/v1/common"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
User
}
from
"@/types/proto/api/v1/user_service"
;
...
...
@@ -32,9 +33,10 @@ const ReactionView = (props: Props) => {
const
currentUser
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
hasReaction
=
users
.
some
((
user
)
=>
currentUser
&&
user
.
username
===
currentUser
.
username
);
const
readonly
=
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
;
const
handleReactionClick
=
async
()
=>
{
if
(
!
currentUser
)
{
if
(
!
currentUser
||
readonly
)
{
return
;
}
...
...
@@ -68,7 +70,7 @@ const ReactionView = (props: Props) => {
className=
{
clsx
(
"h-7 border px-2 py-0.5 rounded-full flex flex-row justify-center items-center gap-1 dark:border-zinc-700"
,
"text-sm text-gray-600 dark:text-gray-400"
,
currentUser
&&
"cursor-pointer"
,
currentUser
&&
!
readonly
&&
"cursor-pointer"
,
hasReaction
&&
"bg-blue-100 border-blue-200 dark:bg-zinc-900"
,
)
}
onClick=
{
handleReactionClick
}
...
...
web/src/pages/Archived.tsx
View file @
ac7121c2
import
{
Tooltip
}
from
"@mui/joy"
;
import
dayjs
from
"dayjs"
;
import
{
ArchiveIcon
,
ArchiveRestoreIcon
,
TrashIcon
}
from
"lucide-react"
;
import
{
ClientError
}
from
"nice-grpc-web"
;
import
{
ArchiveIcon
}
from
"lucide-react"
;
import
{
useMemo
}
from
"react"
;
import
toast
from
"react-hot-toast"
;
import
MemoContent
from
"@/components/MemoContent"
;
import
MemoFilters
from
"@/components/MemoFilters"
;
import
MemoView
from
"@/components/MemoView"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
PagedMemoList
from
"@/components/PagedMemoList"
;
import
SearchBar
from
"@/components/SearchBar"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useMemoFilterStore
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemoFilterStore
}
from
"@/store/v1"
;
import
{
RowStatus
}
from
"@/types/proto/api/v1/common"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -18,7 +15,6 @@ import { useTranslate } from "@/utils/i18n";
const
Archived
=
()
=>
{
const
t
=
useTranslate
();
const
user
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
memoFilterStore
=
useMemoFilterStore
();
const
memoListFilter
=
useMemo
(()
=>
{
...
...
@@ -44,29 +40,6 @@ const Archived = () => {
return
filters
.
join
(
" && "
);
},
[
user
,
memoFilterStore
.
filters
]);
const
handleDeleteMemoClick
=
async
(
memo
:
Memo
)
=>
{
const
confirmed
=
window
.
confirm
(
t
(
"memo.delete-confirm"
));
if
(
confirmed
)
{
await
memoStore
.
deleteMemo
(
memo
.
name
);
}
};
const
handleRestoreMemoClick
=
async
(
memo
:
Memo
)
=>
{
try
{
await
memoStore
.
updateMemo
(
{
name
:
memo
.
name
,
rowStatus
:
RowStatus
.
ACTIVE
,
},
[
"row_status"
],
);
toast
(
t
(
"message.restored-successfully"
));
}
catch
(
error
:
unknown
)
{
console
.
error
(
error
);
toast
.
error
((
error
as
ClientError
).
details
);
}
};
return
(
<
section
className=
"@container w-full max-w-5xl min-h-full flex flex-col justify-start items-center sm:pt-3 md:pt-6 pb-8"
>
<
MobileHeader
/>
...
...
@@ -83,33 +56,7 @@ const Archived = () => {
</
div
>
<
MemoFilters
/>
<
PagedMemoList
renderer=
{
(
memo
:
Memo
)
=>
(
<
div
key=
{
memo
.
name
}
className=
"relative flex flex-col justify-start items-start w-full p-4 pt-3 mb-2 bg-white dark:bg-zinc-800 rounded-lg"
>
<
div
className=
"w-full mb-1 flex flex-row justify-between items-center"
>
<
div
className=
"w-full max-w-[calc(100%-20px)] flex flex-row justify-start items-center mr-1"
>
<
div
className=
"text-sm leading-6 text-gray-400 select-none"
>
<
relative
-
time
datetime=
{
memo
.
displayTime
?.
toISOString
()
}
></
relative
-
time
>
</
div
>
</
div
>
<
div
className=
"flex flex-row justify-end items-center gap-x-2"
>
<
Tooltip
title=
{
t
(
"common.restore"
)
}
placement=
"top"
>
<
button
onClick=
{
()
=>
handleRestoreMemoClick
(
memo
)
}
>
<
ArchiveRestoreIcon
className=
"w-4 h-auto cursor-pointer text-gray-500 dark:text-gray-400"
/>
</
button
>
</
Tooltip
>
<
Tooltip
title=
{
t
(
"common.delete"
)
}
placement=
"top"
>
<
button
onClick=
{
()
=>
handleDeleteMemoClick
(
memo
)
}
className=
"text-gray-500 dark:text-gray-400"
>
<
TrashIcon
className=
"w-4 h-auto cursor-pointer"
/>
</
button
>
</
Tooltip
>
</
div
>
</
div
>
<
MemoContent
key=
{
`${memo.name}-${memo.displayTime}`
}
memoName=
{
memo
.
name
}
nodes=
{
memo
.
nodes
}
readonly=
{
true
}
/>
</
div
>
)
}
renderer=
{
(
memo
:
Memo
)
=>
<
MemoView
key=
{
`${memo.name}-${memo.updateTime}`
}
memo=
{
memo
}
showVisibility
compact
/>
}
listSort=
{
(
memos
:
Memo
[])
=>
memos
.
filter
((
memo
)
=>
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
)
...
...
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