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
1d99dad4
Commit
1d99dad4
authored
May 08, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: update timeline page
parent
33133ea1
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
118 additions
and
229 deletions
+118
-229
ActivityCalendar.tsx
web/src/components/ActivityCalendar.tsx
+10
-5
ChangeMemoCreatedTsDialog.tsx
web/src/components/ChangeMemoCreatedTsDialog.tsx
+0
-105
TagsSection.tsx
web/src/components/HomeSidebar/TagsSection.tsx
+6
-2
MemoView.tsx
web/src/components/MemoView.tsx
+3
-8
Archived.tsx
web/src/pages/Archived.tsx
+9
-6
Explore.tsx
web/src/pages/Explore.tsx
+9
-6
Home.tsx
web/src/pages/Home.tsx
+9
-6
Timeline.tsx
web/src/pages/Timeline.tsx
+63
-76
UserProfile.tsx
web/src/pages/UserProfile.tsx
+9
-6
tag.ts
web/src/store/v1/tag.ts
+0
-9
No files found.
web/src/components/ActivityCalendar.tsx
View file @
1d99dad4
...
...
@@ -6,6 +6,7 @@ import { useTranslate } from "@/utils/i18n";
interface
Props
{
// Format: 2021-1
month
:
string
;
selectedDate
:
string
;
data
:
Record
<
string
,
number
>
;
onClick
?:
(
date
:
string
)
=>
void
;
}
...
...
@@ -28,8 +29,8 @@ const getCellAdditionalStyles = (count: number, maxCount: number) => {
const
ActivityCalendar
=
(
props
:
Props
)
=>
{
const
t
=
useTranslate
();
const
{
month
:
monthStr
,
data
,
onClick
}
=
props
;
const
year
=
new
Date
(
monthStr
).
get
UTC
FullYear
();
const
month
=
new
Date
(
monthStr
).
get
UTC
Month
()
+
1
;
const
year
=
new
Date
(
monthStr
).
getFullYear
();
const
month
=
new
Date
(
monthStr
).
getMonth
()
+
1
;
const
dayInMonth
=
new
Date
(
year
,
month
,
0
).
getDate
();
const
firstDay
=
new
Date
(
year
,
month
-
1
,
1
).
getDay
();
const
lastDay
=
new
Date
(
year
,
month
-
1
,
dayInMonth
).
getDay
();
...
...
@@ -55,15 +56,19 @@ const ActivityCalendar = (props: Props) => {
const
count
=
data
[
date
]
||
0
;
const
isToday
=
new
Date
().
toDateString
()
===
new
Date
(
date
).
toDateString
();
const
tooltipText
=
count
?
t
(
"memo.count-memos-in-date"
,
{
count
:
count
,
date
:
date
})
:
date
;
const
isSelected
=
new
Date
(
props
.
selectedDate
).
toDateString
()
===
new
Date
(
date
).
toDateString
();
return
day
?
(
<
Tooltip
className=
"shrink-0"
key=
{
`${date}-${index}`
}
title=
{
tooltipText
}
placement=
"top"
arrow
>
<
div
className=
{
clsx
(
"w-4 h-4 text-[9px] rounded-md flex justify-center items-center border
border-transparent
"
,
"w-4 h-4 text-[9px] rounded-md flex justify-center items-center border"
,
getCellAdditionalStyles
(
count
,
maxCount
),
isToday
&&
"border-gray-600 dark:!border-gray-500"
,
isToday
&&
"border-gray-600 dark:border-zinc-300"
,
isSelected
&&
"font-bold border-gray-600 dark:border-zinc-300"
,
!
isToday
&&
!
isSelected
&&
"border-transparent"
,
count
>
0
&&
"cursor-pointer"
,
)
}
onClick=
{
()
=>
count
&&
onClick
&&
onClick
(
date
)
}
onClick=
{
()
=>
count
&&
onClick
&&
onClick
(
new
Date
(
date
).
toDateString
()
)
}
>
{
day
}
</
div
>
...
...
web/src/components/ChangeMemoCreatedTsDialog.tsx
deleted
100644 → 0
View file @
33133ea1
import
{
Button
,
IconButton
,
Input
}
from
"@mui/joy"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
getNormalizedTimeString
}
from
"@/helpers/datetime"
;
import
{
MemoNamePrefix
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
Icon
from
"./Icon"
;
interface
Props
extends
DialogProps
{
memoId
:
number
;
}
const
ChangeMemoCreatedTsDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
t
=
useTranslate
();
const
{
destroy
,
memoId
}
=
props
;
const
memoStore
=
useMemoStore
();
const
[
createdAt
,
setCreatedAt
]
=
useState
(
""
);
const
maxDatetimeValue
=
getNormalizedTimeString
();
useEffect
(()
=>
{
memoStore
.
getOrFetchMemoByName
(
`
${
MemoNamePrefix
}${
memoId
}
`
).
then
((
memo
)
=>
{
if
(
memo
)
{
const
datetime
=
getNormalizedTimeString
(
memo
.
createTime
);
setCreatedAt
(
datetime
);
}
else
{
toast
.
error
(
t
(
"message.memo-not-found"
));
destroy
();
}
});
},
[]);
const
handleCloseBtnClick
=
()
=>
{
destroy
();
};
const
handleDatetimeInputChange
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
datetime
=
e
.
target
.
value
as
string
;
setCreatedAt
(
datetime
);
};
const
handleSaveBtnClick
=
async
()
=>
{
try
{
await
memoStore
.
updateMemo
(
{
name
:
`
${
MemoNamePrefix
}${
memoId
}
`
,
createTime
:
new
Date
(
createdAt
),
},
[
"created_ts"
],
);
toast
.
success
(
"Updated memo created time successfully."
);
handleCloseBtnClick
();
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
toast
.
error
(
error
.
response
.
data
.
message
);
}
};
return
(
<>
<
div
className=
"dialog-header-container"
>
<
p
className=
"title-text"
>
{
t
(
"message.change-memo-created-time"
)
}
</
p
>
<
IconButton
size=
"sm"
onClick=
{
handleCloseBtnClick
}
>
<
Icon
.
X
className=
"w-5 h-auto"
/>
</
IconButton
>
</
div
>
<
div
className=
"flex flex-col justify-start items-start !w-72 max-w-full"
>
<
Input
className=
"w-full"
type=
"datetime-local"
value=
{
createdAt
}
slotProps=
{
{
input
:
{
max
:
maxDatetimeValue
,
},
}
}
onChange=
{
handleDatetimeInputChange
}
/>
<
div
className=
"flex flex-row justify-end items-center mt-4 w-full gap-x-2"
>
<
Button
color=
"neutral"
variant=
"plain"
onClick=
{
handleCloseBtnClick
}
>
{
t
(
"common.cancel"
)
}
</
Button
>
<
Button
color=
"primary"
onClick=
{
handleSaveBtnClick
}
>
{
t
(
"common.save"
)
}
</
Button
>
</
div
>
</
div
>
</>
);
};
function
showChangeMemoCreatedTsDialog
(
memoId
:
number
)
{
generateDialog
(
{
className
:
"change-memo-created-ts-dialog"
,
dialogName
:
"change-memo-created-ts-dialog"
,
},
ChangeMemoCreatedTsDialog
,
{
memoId
,
},
);
}
export
default
showChangeMemoCreatedTsDialog
;
web/src/components/HomeSidebar/TagsSection.tsx
View file @
1d99dad4
...
...
@@ -94,8 +94,12 @@ const TagContainer: React.FC<TagContainerProps> = (props: TagContainerProps) =>
style
:
"danger"
,
dialogName
:
"delete-tag-dialog"
,
onConfirm
:
async
()
=>
{
await
tagStore
.
deleteTag
(
tag
);
tagStore
.
fetchTags
({
skipCache
:
true
});
await
memoServiceClient
.
deleteMemoTag
({
parent
:
"memos/-"
,
tag
:
tag
,
});
await
tagStore
.
fetchTags
({
skipCache
:
true
});
toast
.
success
(
t
(
"message.deleted-successfully"
));
},
});
};
...
...
web/src/components/MemoView.tsx
View file @
1d99dad4
...
...
@@ -4,12 +4,11 @@ import { memo, useCallback, useEffect, useRef, useState } from "react";
import
{
Link
,
useLocation
}
from
"react-router-dom"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
extractMemoIdFromName
,
useUserStore
}
from
"@/store/v1"
;
import
{
useUserStore
}
from
"@/store/v1"
;
import
{
MemoRelation_Type
}
from
"@/types/proto/api/v1/memo_relation_service"
;
import
{
Memo
,
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
convertVisibilityToString
}
from
"@/utils/memo"
;
import
showChangeMemoCreatedTsDialog
from
"./ChangeMemoCreatedTsDialog"
;
import
Icon
from
"./Icon"
;
import
MemoActionMenu
from
"./MemoActionMenu"
;
import
MemoContent
from
"./MemoContent"
;
...
...
@@ -56,12 +55,8 @@ const MemoView: React.FC<Props> = (props: Props) => {
})();
},
[]);
const
handleGotoMemoDetailPage
=
(
event
:
React
.
MouseEvent
<
HTMLDivElement
>
)
=>
{
if
(
event
.
altKey
)
{
showChangeMemoCreatedTsDialog
(
extractMemoIdFromName
(
memo
.
name
));
}
else
{
navigateTo
(
`/m/
${
memo
.
uid
}
`
);
}
const
handleGotoMemoDetailPage
=
()
=>
{
navigateTo
(
`/m/
${
memo
.
uid
}
`
);
};
const
handleMemoContentClick
=
useCallback
(
async
(
e
:
React
.
MouseEvent
)
=>
{
...
...
web/src/pages/Archived.tsx
View file @
1d99dad4
...
...
@@ -31,9 +31,14 @@ const Archived = () => {
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
));
useEffect
(()
=>
{
setIsRequesting
(
true
);
nextPageTokenRef
.
current
=
undefined
;
memoList
.
reset
();
fetchMemos
();
setTimeout
(
async
()
=>
{
memoList
.
reset
();
const
nextPageToken
=
await
fetchMemos
();
nextPageTokenRef
.
current
=
nextPageToken
;
setIsRequesting
(
false
);
});
},
[
tagQuery
,
textQuery
]);
const
fetchMemos
=
async
()
=>
{
...
...
@@ -48,14 +53,12 @@ const Archived = () => {
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
setIsRequesting
(
true
);
const
data
=
await
memoStore
.
fetchMemos
({
const
{
nextPageToken
}
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
pageToken
:
nextPageTokenRef
.
current
,
});
setIsRequesting
(
false
);
nextPageTokenRef
.
current
=
data
.
nextPageToken
;
return
nextPageToken
;
};
const
handleDeleteMemoClick
=
async
(
memo
:
Memo
)
=>
{
...
...
web/src/pages/Explore.tsx
View file @
1d99dad4
...
...
@@ -27,9 +27,14 @@ const Explore = () => {
const
sortedMemos
=
memoList
.
value
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
));
useEffect
(()
=>
{
setIsRequesting
(
true
);
nextPageTokenRef
.
current
=
undefined
;
memoList
.
reset
();
fetchMemos
();
setTimeout
(
async
()
=>
{
memoList
.
reset
();
const
nextPageToken
=
await
fetchMemos
();
nextPageTokenRef
.
current
=
nextPageToken
;
setIsRequesting
(
false
);
});
},
[
tagQuery
,
textQuery
]);
const
fetchMemos
=
async
()
=>
{
...
...
@@ -44,14 +49,12 @@ const Explore = () => {
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
setIsRequesting
(
true
);
const
data
=
await
memoStore
.
fetchMemos
({
const
{
nextPageToken
}
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
pageToken
:
nextPageTokenRef
.
current
,
});
setIsRequesting
(
false
);
nextPageTokenRef
.
current
=
data
.
nextPageToken
;
return
nextPageToken
;
};
return
(
...
...
web/src/pages/Home.tsx
View file @
1d99dad4
...
...
@@ -33,9 +33,14 @@ const Home = () => {
.
sort
((
a
,
b
)
=>
Number
(
b
.
pinned
)
-
Number
(
a
.
pinned
));
useEffect
(()
=>
{
setIsRequesting
(
true
);
nextPageTokenRef
.
current
=
undefined
;
memoList
.
reset
();
fetchMemos
();
setTimeout
(
async
()
=>
{
memoList
.
reset
();
const
nextPageToken
=
await
fetchMemos
();
nextPageTokenRef
.
current
=
nextPageToken
;
setIsRequesting
(
false
);
});
},
[
tagQuery
,
textQuery
]);
const
fetchMemos
=
async
()
=>
{
...
...
@@ -50,14 +55,12 @@ const Home = () => {
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
setIsRequesting
(
true
);
const
data
=
await
memoStore
.
fetchMemos
({
const
{
nextPageToken
}
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
pageToken
:
nextPageTokenRef
.
current
,
});
setIsRequesting
(
false
);
nextPageTokenRef
.
current
=
data
.
nextPageToken
;
return
nextPageToken
;
};
const
handleEditPrevious
=
useCallback
(()
=>
{
...
...
web/src/pages/Timeline.tsx
View file @
1d99dad4
import
{
Button
,
Divider
,
IconButton
}
from
"@mui/joy"
;
import
{
Button
,
IconButton
}
from
"@mui/joy"
;
import
clsx
from
"clsx"
;
import
{
Fragment
,
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
useEffect
,
useRef
,
useState
}
from
"react"
;
import
ActivityCalendar
from
"@/components/ActivityCalendar"
;
import
Empty
from
"@/components/Empty"
;
import
Icon
from
"@/components/Icon"
;
...
...
@@ -11,58 +11,38 @@ import MobileHeader from "@/components/MobileHeader";
import
{
TimelineSidebar
,
TimelineSidebarDrawer
}
from
"@/components/TimelineSidebar"
;
import
{
memoServiceClient
}
from
"@/grpcweb"
;
import
{
DAILY_TIMESTAMP
,
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
{
get
NormalizedTimeString
,
get
TimeStampByDate
}
from
"@/helpers/datetime"
;
import
{
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useFilterWithUrlParams
from
"@/hooks/useFilterWithUrlParams"
;
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
i18n
from
"@/i18n"
;
import
{
useMemoList
,
useMemoStore
}
from
"@/store/v1"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
interface
GroupedByMonthItem
{
// Format: 2021-1
month
:
string
;
data
:
Record
<
string
,
number
>
;
memos
:
Memo
[];
}
const
groupByMonth
=
(
dateCountMap
:
Record
<
string
,
number
>
,
memos
:
Memo
[]):
GroupedByMonthItem
[]
=>
{
const
groupedByMonth
:
GroupedByMonthItem
[]
=
[];
Object
.
entries
(
dateCountMap
).
forEach
(([
date
,
count
])
=>
{
const
month
=
date
.
split
(
"-"
).
slice
(
0
,
2
).
join
(
"-"
);
const
existingMonth
=
groupedByMonth
.
find
((
group
)
=>
group
.
month
===
month
);
if
(
existingMonth
)
{
existingMonth
.
data
[
date
]
=
count
;
}
else
{
const
monthMemos
=
memos
.
filter
((
memo
)
=>
getNormalizedTimeString
(
memo
.
displayTime
).
startsWith
(
month
));
groupedByMonth
.
push
({
month
,
data
:
{
[
date
]:
count
},
memos
:
monthMemos
});
}
});
return
groupedByMonth
.
filter
((
group
)
=>
group
.
memos
.
length
>
0
).
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
month
)
-
getTimeStampByDate
(
a
.
month
));
};
const
Timeline
=
()
=>
{
const
t
=
useTranslate
();
const
{
md
}
=
useResponsiveWidth
();
const
user
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
memoList
=
useMemoList
();
const
{
tag
:
tagQuery
,
text
:
textQuery
}
=
useFilterWithUrlParams
();
const
[
activityStats
,
setActivityStats
]
=
useState
<
Record
<
string
,
number
>>
({});
const
[
selectedDa
y
,
setSelectedDay
]
=
useState
<
string
|
undefined
>
(
);
const
[
selectedDa
teString
,
setSelectedDateString
]
=
useState
<
string
>
(
new
Date
().
toDateString
()
);
const
[
isRequesting
,
setIsRequesting
]
=
useState
(
true
);
const
nextPageTokenRef
=
useRef
<
string
|
undefined
>
(
undefined
);
const
{
tag
:
tagQuery
,
text
:
textQuery
}
=
useFilterWithUrlParams
();
const
sortedMemos
=
memoList
.
value
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
));
const
groupedByMonth
=
groupByMonth
(
activityStats
,
sortedMemos
);
const
sortedMemos
=
memoList
.
value
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
a
.
displayTime
)
-
getTimeStampByDate
(
b
.
displayTime
));
const
monthString
=
new
Date
(
selectedDateString
).
getFullYear
()
+
"-"
+
(
new
Date
(
selectedDateString
).
getMonth
()
+
1
);
useEffect
(()
=>
{
setIsRequesting
(
true
);
nextPageTokenRef
.
current
=
undefined
;
memoList
.
reset
();
fetchMemos
();
},
[
selectedDay
,
tagQuery
,
textQuery
]);
setTimeout
(
async
()
=>
{
memoList
.
reset
();
const
nextPageToken
=
await
fetchMemos
();
nextPageTokenRef
.
current
=
nextPageToken
;
setIsRequesting
(
false
);
});
},
[
selectedDateString
,
tagQuery
,
textQuery
]);
useEffect
(()
=>
{
(
async
()
=>
{
...
...
@@ -82,7 +62,15 @@ const Timeline = () => {
timezone
:
Intl
.
DateTimeFormat
().
resolvedOptions
().
timeZone
,
filter
:
filters
.
join
(
" && "
),
});
setActivityStats
(
stats
);
setActivityStats
(
Object
.
fromEntries
(
Object
.
entries
(
stats
).
filter
(([
date
])
=>
{
const
d
=
new
Date
(
date
);
return
`
${
d
.
getFullYear
()}
-
${
d
.
getMonth
()
+
1
}
`
===
monthString
;
}),
),
);
})();
},
[
sortedMemos
.
length
]);
...
...
@@ -98,20 +86,18 @@ const Timeline = () => {
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
if
(
selectedDa
y
)
{
const
selectedDateStamp
=
getTimeStampByDate
(
selectedDa
y
)
+
new
Date
().
getTimezoneOffset
()
*
60
*
1000
;
if
(
selectedDa
teString
)
{
const
selectedDateStamp
=
getTimeStampByDate
(
selectedDa
teString
)
;
filters
.
push
(
...[
`display_time_after ==
${
selectedDateStamp
/
1000
}
`
,
`display_time_before ==
${(
selectedDateStamp
+
DAILY_TIMESTAMP
)
/
1000
}
`
],
);
}
setIsRequesting
(
true
);
const
data
=
await
memoStore
.
fetchMemos
({
const
{
nextPageToken
}
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
pageToken
:
nextPageTokenRef
.
current
,
});
setIsRequesting
(
false
);
nextPageTokenRef
.
current
=
data
.
nextPageToken
;
return
nextPageToken
;
};
const
handleNewMemo
=
()
=>
{
...
...
@@ -132,7 +118,7 @@ const Timeline = () => {
<
div
>
<
div
className=
"py-1 flex flex-row justify-start items-center select-none opacity-80"
onClick=
{
()
=>
setSelectedDa
y
(
undefined
)
}
onClick=
{
()
=>
setSelectedDa
teString
(
new
Date
().
toDateString
()
)
}
>
<
Icon
.
GanttChartSquare
className=
"w-6 h-auto mr-1 opacity-80"
/>
<
span
className=
"text-lg"
>
{
t
(
"timeline.title"
)
}
</
span
>
...
...
@@ -145,43 +131,44 @@ const Timeline = () => {
</
div
>
</
div
>
<
div
className=
"w-full h-auto flex flex-col justify-start items-start"
>
<
MemoFilter
className=
"p
x-2 my-4
"
/>
<
MemoFilter
className=
"p
-2 my-2 rounded-lg dark:bg-zinc-900
"
/>
{
groupedByMonth
.
map
((
group
,
index
)
=>
(
<
Fragment
key=
{
group
.
month
}
>
<
div
className=
{
clsx
(
"flex flex-col justify-start items-start w-full mt-2 last:mb-4"
)
}
>
<
div
className=
{
clsx
(
"flex shrink-0 flex-row w-full pl-1 mt-2 mb-2"
)
}
>
<
div
className=
{
clsx
(
"w-full flex flex-col"
)
}
>
<
span
className=
"font-medium text-3xl leading-tight mb-1"
>
{
new
Date
(
group
.
month
).
toLocaleString
(
i18n
.
language
,
{
month
:
"short"
,
timeZone
:
"UTC"
})
}
</
span
>
<
span
className=
"opacity-60"
>
{
new
Date
(
group
.
month
).
getUTCFullYear
()
}
</
span
>
</
div
>
<
ActivityCalendar
month=
{
group
.
month
}
data=
{
group
.
data
}
onClick=
{
(
date
)
=>
setSelectedDay
(
date
)
}
/>
</
div
>
<
div
className=
{
clsx
(
"flex flex-col justify-start items-start w-full mt-2 last:mb-4"
)
}
>
<
div
className=
{
clsx
(
"flex shrink-0 flex-row w-full pl-1 mt-2 mb-2"
)
}
>
<
div
className=
{
clsx
(
"w-full flex flex-col"
)
}
>
<
span
className=
"font-medium text-3xl sm:text-4xl"
>
{
new
Date
(
selectedDateString
).
toLocaleDateString
(
i18n
.
language
,
{
month
:
"short"
,
day
:
"numeric"
})
}
</
span
>
<
span
className=
"opacity-60 text-lg"
>
{
new
Date
(
monthString
).
getFullYear
()
}
</
span
>
</
div
>
<
ActivityCalendar
month=
{
monthString
}
selectedDate=
{
selectedDateString
}
data=
{
activityStats
}
onClick=
{
(
date
)
=>
setSelectedDateString
(
date
)
}
/>
</
div
>
<
div
className=
{
clsx
(
"w-full flex flex-col justify-start items-start"
)
}
>
{
group
.
memos
.
map
((
memo
,
index
)
=>
(
<
div
key=
{
`${memo.name}-${memo.displayTime}`
}
className=
{
clsx
(
"relative w-full flex flex-col justify-start items-start pl-4 sm:pl-10 pt-0"
)
}
>
<
MemoView
className=
"!border max-w-full !border-gray-100 dark:!border-zinc-700"
memo=
{
memo
}
/>
<
div
className=
"absolute -left-2 sm:left-2 top-4 h-full"
>
{
index
!==
group
.
memos
.
length
-
1
&&
(
<
div
className=
"absolute top-2 left-[7px] h-full w-0.5 bg-gray-200 dark:bg-gray-700 block"
></
div
>
)
}
<
div
className=
"border-4 rounded-full border-white relative dark:border-zinc-800"
>
<
Icon
.
Circle
className=
"w-2 h-auto bg-gray-200 text-gray-200 dark:bg-gray-700 dark:text-gray-700 rounded-full"
/>
</
div
>
</
div
>
<
div
className=
{
clsx
(
"w-full flex flex-col justify-start items-start"
)
}
>
{
sortedMemos
.
map
((
memo
,
index
)
=>
(
<
div
key=
{
`${memo.name}-${memo.displayTime}`
}
className=
{
clsx
(
"relative w-full flex flex-col justify-start items-start pl-4 sm:pl-10 pt-0"
)
}
>
<
MemoView
className=
"!border max-w-full !border-gray-100 dark:!border-zinc-700"
memo=
{
memo
}
/>
<
div
className=
"absolute -left-2 sm:left-2 top-4 h-full"
>
{
index
!==
sortedMemos
.
length
-
1
&&
(
<
div
className=
"absolute top-2 left-[7px] h-full w-0.5 bg-gray-200 dark:bg-gray-700 block"
></
div
>
)
}
<
div
className=
"border-4 rounded-full border-white relative dark:border-zinc-800"
>
<
Icon
.
Circle
className=
"w-2 h-auto bg-gray-200 text-gray-200 dark:bg-gray-700 dark:text-gray-700 rounded-full"
/>
</
div
>
))
}
</
div
>
</
div
>
</
div
>
{
index
!==
groupedByMonth
.
length
-
1
&&
<
Divider
className=
"w-full !my-4 md:!mb-8 !bg-gray-100 dark:!bg-zinc-700"
/>
}
</
Fragment
>
))
}
))
}
</
div
>
</
div
>
{
isRequesting
?
(
<
div
className=
"flex flex-row justify-center items-center w-full my-4 text-gray-400"
>
<
Icon
.
Loader
className=
"w-4 h-auto animate-spin mr-1"
/>
...
...
web/src/pages/UserProfile.tsx
View file @
1d99dad4
...
...
@@ -59,9 +59,14 @@ const UserProfile = () => {
return
;
}
setIsRequesting
(
true
);
nextPageTokenRef
.
current
=
undefined
;
memoList
.
reset
();
fetchMemos
();
setTimeout
(
async
()
=>
{
memoList
.
reset
();
const
nextPageToken
=
await
fetchMemos
();
nextPageTokenRef
.
current
=
nextPageToken
;
setIsRequesting
(
false
);
});
},
[
user
,
tagQuery
,
textQuery
]);
const
fetchMemos
=
async
()
=>
{
...
...
@@ -80,14 +85,12 @@ const UserProfile = () => {
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
setIsRequesting
(
true
);
const
data
=
await
memoStore
.
fetchMemos
({
const
{
nextPageToken
}
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
pageToken
:
nextPageTokenRef
.
current
,
});
setIsRequesting
(
false
);
nextPageTokenRef
.
current
=
data
.
nextPageToken
;
return
nextPageToken
;
};
const
handleCopyProfileLink
=
()
=>
{
...
...
web/src/store/v1/tag.ts
View file @
1d99dad4
...
...
@@ -28,14 +28,5 @@ export const useTagStore = create(
const
{
tagAmounts
}
=
await
memoServiceClient
.
listMemoTags
({
parent
:
"memos/-"
});
set
({
tagAmounts
});
},
deleteTag
:
async
(
tagName
:
string
)
=>
{
await
memoServiceClient
.
deleteMemoTag
({
parent
:
"memos/-"
,
tag
:
tagName
,
});
const
{
tagAmounts
}
=
get
();
delete
tagAmounts
[
tagName
];
set
({
tagAmounts
});
},
})),
);
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