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
914c0620
Commit
914c0620
authored
Jan 03, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: add statistics view
parent
138b69e3
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
98 additions
and
260 deletions
+98
-260
HomeSidebar.tsx
web/src/components/HomeSidebar.tsx
+7
-6
HomeSidebarDrawer.tsx
web/src/components/HomeSidebarDrawer.tsx
+1
-1
MemoCreationHeatMap.tsx
web/src/components/MemoCreationHeatMap.tsx
+0
-156
MemoResourceListView.tsx
web/src/components/MemoResourceListView.tsx
+4
-5
PersonalStatistics.tsx
web/src/components/PersonalStatistics.tsx
+69
-0
SearchBar.tsx
web/src/components/SearchBar.tsx
+7
-8
TagList.tsx
web/src/components/TagList.tsx
+7
-7
TimelineMemo.tsx
web/src/components/TimelineMemo.tsx
+0
-3
usage-heat-map.less
web/src/less/usage-heat-map.less
+0
-73
Home.tsx
web/src/pages/Home.tsx
+3
-1
No files found.
web/src/components/HomeSidebar.tsx
View file @
914c0620
import
MemoCreationHeatMap
from
"./MemoCreationHeatMap"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
PersonalStatistics
from
"./PersonalStatistics"
;
import
SearchBar
from
"./SearchBar"
;
import
TagList
from
"./TagList"
;
const
HomeSidebar
=
()
=>
{
const
currentUser
=
useCurrentUser
();
return
(
<
aside
className=
"relative w-full pr-2 h-full max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start py-4 sm:pt-6"
>
<
div
className=
"px-4 pr-8 mb-4 w-full"
>
<
SearchBar
/>
</
div
>
<
MemoCreationHeatMap
/>
<
aside
className=
"relative w-full px-4 h-full max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start py-4 sm:pt-6"
>
<
SearchBar
/>
<
PersonalStatistics
user=
{
currentUser
}
/>
<
TagList
/>
</
aside
>
);
...
...
web/src/components/HomeSidebarDrawer.tsx
View file @
914c0620
...
...
@@ -26,7 +26,7 @@ const HomeSidebarDrawer = () => {
<
Icon
.
Search
className=
"w-5 h-auto dark:text-gray-200"
/>
</
IconButton
>
<
Drawer
anchor=
"right"
size=
"sm"
open=
{
open
}
onClose=
{
toggleDrawer
(
false
)
}
>
<
div
className=
"w-full px-
4
"
>
<
div
className=
"w-full px-
1
"
>
<
HomeSidebar
/>
</
div
>
</
Drawer
>
...
...
web/src/components/MemoCreationHeatMap.tsx
deleted
100644 → 0
View file @
138b69e3
import
{
useCallback
,
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
memoServiceClient
}
from
"@/grpcweb"
;
import
{
DAILY_TIMESTAMP
}
from
"@/helpers/consts"
;
import
{
getDateStampByDate
,
getDateString
,
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
*
as
utils
from
"@/helpers/utils"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useGlobalStore
}
from
"@/store/module"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
{
useTranslate
,
Translations
}
from
"@/utils/i18n"
;
import
"@/less/usage-heat-map.less"
;
interface
DailyUsageStat
{
timestamp
:
number
;
count
:
number
;
}
const
tableConfig
=
{
width
:
10
,
height
:
7
,
};
const
getInitialCreationStats
=
(
usedDaysAmount
:
number
,
beginDayTimestamp
:
number
):
DailyUsageStat
[]
=>
{
const
initialUsageStat
:
DailyUsageStat
[]
=
[];
for
(
let
i
=
1
;
i
<=
usedDaysAmount
;
i
++
)
{
initialUsageStat
.
push
({
timestamp
:
beginDayTimestamp
+
DAILY_TIMESTAMP
*
i
,
count
:
0
,
});
}
return
initialUsageStat
;
};
const
MemoCreationHeatMap
=
()
=>
{
const
t
=
useTranslate
();
const
navigateTo
=
useNavigateTo
();
const
user
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
todayTimeStamp
=
getDateStampByDate
(
Date
.
now
());
const
weekDay
=
new
Date
(
todayTimeStamp
).
getDay
();
const
weekFromMonday
=
[
"zh-Hans"
,
"ko"
].
includes
(
useGlobalStore
().
state
.
locale
);
const
dayTips
=
weekFromMonday
?
[
"mon"
,
""
,
"wed"
,
""
,
"fri"
,
""
,
"sun"
]
:
[
"sun"
,
""
,
"tue"
,
""
,
"thu"
,
""
,
"sat"
];
const
todayDay
=
weekFromMonday
?
(
weekDay
==
0
?
7
:
weekDay
)
:
weekDay
+
1
;
const
nullCell
=
new
Array
(
7
-
todayDay
).
fill
(
0
);
const
usedDaysAmount
=
(
tableConfig
.
width
-
1
)
*
tableConfig
.
height
+
todayDay
;
const
beginDayTimestamp
=
todayTimeStamp
-
usedDaysAmount
*
DAILY_TIMESTAMP
;
const
[
memoAmount
,
setMemoAmount
]
=
useState
(
0
);
const
[
creationStatus
,
setCreationStatus
]
=
useState
<
DailyUsageStat
[]
>
(
getInitialCreationStats
(
usedDaysAmount
,
beginDayTimestamp
));
const
containerElRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
memos
=
Object
.
values
(
memoStore
.
getState
().
memoMapById
);
const
createdDays
=
Math
.
ceil
((
Date
.
now
()
-
getTimeStampByDate
(
user
.
createTime
))
/
1000
/
3600
/
24
);
useEffect
(()
=>
{
if
(
memos
.
length
===
0
)
{
return
;
}
(
async
()
=>
{
const
{
memoCreationStats
}
=
await
memoServiceClient
.
getUserMemosStats
({
name
:
user
.
name
,
});
const
tempStats
=
getInitialCreationStats
(
usedDaysAmount
,
beginDayTimestamp
);
Object
.
entries
(
memoCreationStats
).
forEach
(([
k
,
v
])
=>
{
const
dayIndex
=
Math
.
floor
((
getDateStampByDate
(
k
)
-
beginDayTimestamp
)
/
DAILY_TIMESTAMP
)
-
1
;
if
(
tempStats
[
dayIndex
])
{
tempStats
[
dayIndex
].
count
=
v
;
}
});
setCreationStatus
(
tempStats
);
setMemoAmount
(
Object
.
values
(
memoCreationStats
).
reduce
((
acc
,
cur
)
=>
acc
+
cur
,
0
));
})();
},
[
memos
.
length
,
user
.
name
]);
const
handleUsageStatItemMouseEnter
=
useCallback
((
event
:
React
.
MouseEvent
,
item
:
DailyUsageStat
)
=>
{
const
tempDiv
=
document
.
createElement
(
"div"
);
tempDiv
.
className
=
"usage-detail-container pop-up"
;
const
bounding
=
utils
.
getElementBounding
(
event
.
target
as
HTMLElement
);
tempDiv
.
style
.
left
=
bounding
.
left
+
"px"
;
tempDiv
.
style
.
top
=
bounding
.
top
-
2
+
"px"
;
const
tMemoOnOpts
=
{
amount
:
item
.
count
,
date
:
getDateString
(
item
.
timestamp
as
number
)
};
tempDiv
.
innerHTML
=
item
.
count
===
1
?
t
(
"heatmap.memo-on"
,
tMemoOnOpts
)
:
t
(
"heatmap.memos-on"
,
tMemoOnOpts
);
document
.
body
.
appendChild
(
tempDiv
);
if
(
tempDiv
.
offsetLeft
-
tempDiv
.
clientWidth
/
2
<
0
)
{
tempDiv
.
style
.
left
=
bounding
.
left
+
tempDiv
.
clientWidth
*
0.4
+
"px"
;
tempDiv
.
className
+=
" offset-left"
;
}
},
[]);
const
handleUsageStatItemMouseLeave
=
useCallback
(()
=>
{
document
.
body
.
querySelectorAll
(
"div.usage-detail-container.pop-up"
).
forEach
((
node
)
=>
node
.
remove
());
},
[]);
const
handleUsageStatItemClick
=
useCallback
((
item
:
DailyUsageStat
)
=>
{
navigateTo
(
`/timeline?timestamp=
${
item
.
timestamp
}
`
);
},
[]);
// This interpolation is not being used because of the current styling,
// but it can improve translation quality by giving it a more meaningful context
const
tMemoInOpts
=
{
amount
:
memoAmount
,
period
:
""
,
date
:
""
};
return
(
<>
<
div
className=
"usage-heat-map-wrapper"
ref=
{
containerElRef
}
>
<
div
className=
"usage-heat-map"
>
{}
{
creationStatus
.
map
((
v
,
i
)
=>
{
const
count
=
v
.
count
;
const
colorLevel
=
count
<=
0
?
""
:
count
<=
1
?
"stat-day-l1-bg"
:
count
<=
2
?
"stat-day-l2-bg"
:
count
<=
4
?
"stat-day-l3-bg"
:
"stat-day-l4-bg"
;
return
(
<
div
className=
"stat-wrapper"
key=
{
i
}
onMouseEnter=
{
(
e
)
=>
handleUsageStatItemMouseEnter
(
e
,
v
)
}
onMouseLeave=
{
handleUsageStatItemMouseLeave
}
onClick=
{
()
=>
handleUsageStatItemClick
(
v
)
}
>
<
span
className=
{
`stat-container ${colorLevel} ${todayTimeStamp === v.timestamp ? "today" : ""}`
}
></
span
>
</
div
>
);
})
}
{
nullCell
.
map
((
_
,
i
)
=>
(
<
div
className=
"stat-wrapper"
key=
{
i
}
>
<
span
className=
"stat-container null"
></
span
>
</
div
>
))
}
</
div
>
<
div
className=
"day-tip-text-container"
>
{
dayTips
.
map
((
v
,
i
)
=>
(
<
span
className=
"tip-text"
key=
{
i
}
>
{
v
&&
t
((
"days."
+
v
)
as
Translations
)
}
</
span
>
))
}
</
div
>
</
div
>
<
p
className=
"w-full pl-4 text-xs -mt-2 mb-3 text-gray-400 dark:text-zinc-400"
>
<
span
className=
"font-medium text-gray-500 dark:text-zinc-300 number"
>
{
memoAmount
}
</
span
>
{
memoAmount
===
1
?
t
(
"heatmap.memo-in"
,
tMemoInOpts
)
:
t
(
"heatmap.memos-in"
,
tMemoInOpts
)
}{
" "
}
<
span
className=
"font-medium text-gray-500 dark:text-zinc-300"
>
{
createdDays
}
</
span
>
{
createdDays
===
1
?
t
(
"heatmap.day"
,
tMemoInOpts
)
:
t
(
"heatmap.days"
,
tMemoInOpts
)
}
</
p
>
</>
);
};
export
default
MemoCreationHeatMap
;
web/src/components/MemoResourceListView.tsx
View file @
914c0620
...
...
@@ -31,6 +31,7 @@ const MemoResourceListView = ({ resourceList = [] }: { resourceList: Resource[]
const
MediaCard
=
({
resource
,
thumbnail
}:
{
resource
:
Resource
;
thumbnail
?:
boolean
})
=>
{
const
type
=
getResourceType
(
resource
);
const
url
=
getResourceUrl
(
resource
);
if
(
type
===
"image/*"
)
{
return
(
<
img
...
...
@@ -40,9 +41,7 @@ const MemoResourceListView = ({ resourceList = [] }: { resourceList: Resource[]
decoding=
"async"
/>
);
}
if
(
type
===
"video/*"
)
{
}
else
if
(
type
===
"video/*"
)
{
return
(
<
video
className=
"cursor-pointer w-full h-full object-contain bg-zinc-100 dark:bg-zinc-800"
...
...
@@ -52,9 +51,9 @@ const MemoResourceListView = ({ resourceList = [] }: { resourceList: Resource[]
controls
/>
);
}
else
{
return
<></>;
}
return
<></>;
};
const
MediaList
=
({
resources
=
[]
}:
{
resources
:
Resource
[]
})
=>
{
...
...
web/src/components/PersonalStatistics.tsx
0 → 100644
View file @
914c0620
import
{
useEffect
,
useState
}
from
"react"
;
import
{
memoServiceClient
}
from
"@/grpcweb"
;
import
{
useTagStore
}
from
"@/store/module"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
{
User
}
from
"@/types/proto/api/v2/user_service"
;
import
Icon
from
"./Icon"
;
interface
Props
{
user
:
User
;
}
const
PersonalStatistics
=
(
props
:
Props
)
=>
{
const
{
user
}
=
props
;
const
tagStore
=
useTagStore
();
const
memoStore
=
useMemoStore
();
const
[
memoAmount
,
setMemoAmount
]
=
useState
(
0
);
const
[
isRequesting
,
setIsRequesting
]
=
useState
(
false
);
const
days
=
Math
.
ceil
((
Date
.
now
()
-
user
.
createTime
!
.
getTime
())
/
86400000
);
const
memos
=
Object
.
values
(
memoStore
.
getState
().
memoMapById
);
const
tags
=
tagStore
.
state
.
tags
.
length
;
useEffect
(()
=>
{
if
(
memos
.
length
===
0
)
{
return
;
}
(
async
()
=>
{
setIsRequesting
(
true
);
const
{
memoCreationStats
}
=
await
memoServiceClient
.
getUserMemosStats
({
name
:
user
.
name
,
});
setIsRequesting
(
false
);
setMemoAmount
(
Object
.
values
(
memoCreationStats
).
reduce
((
acc
,
cur
)
=>
acc
+
cur
,
0
));
})();
},
[
memos
.
length
,
user
.
name
]);
return
(
<
div
className=
"w-full border mt-2 py-2 px-3 rounded-md space-y-0.5 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800"
>
<
p
className=
"text-sm font-medium text-gray-500"
>
Statistics
</
p
>
<
div
className=
"w-full flex justify-between items-center"
>
<
div
className=
"w-full flex justify-start items-center text-gray-500"
>
<
Icon
.
CalendarDays
className=
"w-4 h-auto mr-1"
/>
<
span
className=
"block text-base sm:text-sm"
>
Days
</
span
>
</
div
>
<
span
className=
"text-gray-500 font-mono"
>
{
days
}
</
span
>
</
div
>
<
div
className=
"w-full flex justify-between items-center"
>
<
div
className=
"w-full flex justify-start items-center text-gray-500"
>
<
Icon
.
PencilLine
className=
"w-4 h-auto mr-1"
/>
<
span
className=
"block text-base sm:text-sm"
>
Memos
</
span
>
</
div
>
{
isRequesting
?
(
<
Icon
.
Loader
className=
"animate-spin w-4 h-auto text-gray-400"
/>
)
:
(
<
span
className=
"text-gray-500 font-mono"
>
{
memoAmount
}
</
span
>
)
}
</
div
>
<
div
className=
"w-full flex justify-between items-center"
>
<
div
className=
"w-full flex justify-start items-center text-gray-500"
>
<
Icon
.
Hash
className=
"w-4 h-auto mr-1"
/>
<
span
className=
"block text-base sm:text-sm"
>
Tags
</
span
>
</
div
>
<
span
className=
"text-gray-500 font-mono"
>
{
tags
}
</
span
>
</
div
>
</
div
>
);
};
export
default
PersonalStatistics
;
web/src/components/SearchBar.tsx
View file @
914c0620
import
{
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
Input
}
from
"@mui/joy"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
useDebounce
from
"react-use/lib/useDebounce"
;
import
{
useFilterStore
}
from
"@/store/module"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -8,7 +9,6 @@ const SearchBar = () => {
const
t
=
useTranslate
();
const
filterStore
=
useFilterStore
();
const
[
queryText
,
setQueryText
]
=
useState
(
""
);
const
inputRef
=
useRef
<
HTMLInputElement
>
(
null
);
useEffect
(()
=>
{
const
text
=
filterStore
.
getState
().
text
;
...
...
@@ -28,13 +28,12 @@ const SearchBar = () => {
};
return
(
<
div
className=
"w-full h-9 flex flex-row justify-start items-center
py-2 px-3 rounded-md bg-gray-200 dark:bg-zinc-700
"
>
<
I
con
.
Search
className=
"w-4 h-auto opacity-30 dark:text-gray-200"
/>
<
input
className=
"flex ml-2 w-24 grow text-sm outline-none bg-transparent dark:text-gray-200
"
type=
"text"
<
div
className=
"w-full h-9 flex flex-row justify-start items-center"
>
<
I
nput
className=
"w-full !shadow-none !border-gray-200 dark:!border-zinc-800"
size=
"md
"
startDecorator=
{
<
Icon
.
Search
className=
"w-4 h-auto opacity-30"
/>
}
placeholder=
{
t
(
"memo.search-placeholder"
)
}
ref=
{
inputRef
}
value=
{
queryText
}
onChange=
{
handleTextQueryInput
}
/>
...
...
web/src/components/TagList.tsx
View file @
914c0620
...
...
@@ -70,8 +70,8 @@ const TagList = () => {
},
[
tagsText
]);
return
(
<
div
className=
"flex flex-col justify-start items-start w-full mt-
2
h-auto shrink-0 flex-nowrap hide-scrollbar"
>
<
div
className=
"flex flex-row justify-start items-center w-full
px-4
"
>
<
div
className=
"flex flex-col justify-start items-start w-full mt-
3 px-1
h-auto shrink-0 flex-nowrap hide-scrollbar"
>
<
div
className=
"flex flex-row justify-start items-center w-full"
>
<
span
className=
"text-sm leading-6 font-mono text-gray-400"
>
{
t
(
"common.tags"
)
}
</
span
>
<
button
onClick=
{
()
=>
showCreateTagDialog
()
}
...
...
@@ -80,7 +80,7 @@ const TagList = () => {
<
Icon
.
Plus
className=
"w-4 h-4 text-gray-400"
/>
</
button
>
</
div
>
<
div
className=
"flex flex-col justify-start items-start relative w-full h-auto flex-nowrap
mt-2 mb-2
"
>
<
div
className=
"flex flex-col justify-start items-start relative w-full h-auto flex-nowrap"
>
{
tags
.
map
((
t
,
idx
)
=>
(
<
TagItemContainer
key=
{
t
.
text
+
"-"
+
idx
}
tag=
{
t
}
tagQuery=
{
filter
.
tag
}
/>
))
}
...
...
@@ -117,15 +117,15 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
return
(
<>
<
div
className=
"relative group flex flex-row justify-between items-center w-full h-
10 py-0 px-4 mt-px first:mt-1 rounded-lg text-base cursor-pointer select-none shrink-0 hover:opacity-6
0"
className=
"relative group flex flex-row justify-between items-center w-full h-
8 py-0 mt-px first:mt-1 rounded-lg text-base sm:text-sm cursor-pointer select-none shrink-0 hover:opacity-8
0"
onClick=
{
handleTagClick
}
>
<
div
className=
{
`flex flex-row justify-start items-center truncate shrink leading-5 mr-1 text-
black dark:text-gray-2
00 ${
className=
{
`flex flex-row justify-start items-center truncate shrink leading-5 mr-1 text-
gray-600 dark:text-gray-4
00 ${
isActive && "text-green-600"
}`
}
>
<
span
className=
"block w-4 shrink-0"
>
#
</
span
>
<
Icon
.
Hash
className=
"w-4 h-auto shrink-0 opacity-60 mr-1"
/
>
<
span
className=
"truncate"
>
{
tag
.
key
}
</
span
>
</
div
>
<
div
className=
"flex flex-row justify-end items-center"
>
...
...
@@ -141,7 +141,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
</
div
>
{
hasSubTags
?
(
<
div
className=
{
`w-[calc(100%-
1rem)] flex flex-col justify-start items-start h-auto ml-4 pl-1
border-l-2 border-l-gray-200 dark:border-l-gray-400 ${
className=
{
`w-[calc(100%-
0.5rem)] flex flex-col justify-start items-start h-auto ml-2 pl-2
border-l-2 border-l-gray-200 dark:border-l-gray-400 ${
!showSubTags && "!hidden"
}`
}
>
...
...
web/src/components/TimelineMemo.tsx
View file @
914c0620
import
Icon
from
"@/components/Icon"
;
import
MemoContent
from
"@/components/MemoContent"
;
import
MemoResourceListView
from
"@/components/MemoResourceListView"
;
import
{
getTimeString
}
from
"@/helpers/datetime"
;
...
...
@@ -18,8 +17,6 @@ const TimelineMemo = (props: Props) => {
<
div
className=
"relative w-full flex flex-col justify-start items-start"
>
<
div
className=
"w-full flex flex-row justify-start items-center mt-0.5 mb-1 text-sm font-mono text-gray-500 dark:text-gray-400"
>
<
span
className=
"opacity-80"
>
{
getTimeString
(
memo
.
displayTime
)
}
</
span
>
<
Icon
.
Dot
className=
"w-5 h-auto opacity-60"
/>
<
span
className=
"opacity-60"
>
#
{
memo
.
id
}
</
span
>
</
div
>
<
MemoContent
nodes=
{
memo
.
nodes
}
/>
<
MemoResourceListView
resourceList=
{
memo
.
resources
}
/>
...
...
web/src/less/usage-heat-map.less
deleted
100644 → 0
View file @
138b69e3
.usage-heat-map-wrapper {
@apply flex flex-row justify-start items-center flex-nowrap w-full h-32 pl-4 pb-3 shrink-0;
> .usage-heat-map {
@apply w-full h-full grid grid-rows-7 grid-cols-10 grid-flow-col;
> .stat-wrapper {
> .stat-container {
@apply block rounded bg-gray-200 dark:bg-zinc-700;
width: 14px;
height: 14px;
&.stat-day-l1-bg {
@apply bg-green-400 dark:bg-green-800;
}
&.stat-day-l2-bg {
@apply bg-green-500 dark:bg-green-700;
}
&.stat-day-l3-bg {
@apply bg-green-600 dark:bg-green-600;
}
&.stat-day-l4-bg {
@apply bg-green-700 dark:bg-green-500;
}
&.today {
@apply border border-black dark:border-gray-400;
}
&.null {
@apply opacity-40;
}
}
}
}
> .day-tip-text-container {
@apply w-8 h-full grid grid-rows-7;
> .tip-text {
@apply pl-1 w-full h-full text-left font-mono text-gray-400;
font-size: 10px;
}
}
}
.usage-detail-container {
@apply fixed left-0 top-0 ml-2 -mt-9 p-2 z-100 -translate-x-1/2 select-none text-white text-xs rounded whitespace-nowrap;
background-color: rgba(0, 0, 0, 0.8);
> .date-text {
@apply text-gray-300;
}
&.offset-left {
&::before {
left: calc(10% - 5px);
}
}
&::before {
content: "";
position: absolute;
bottom: -4px;
left: calc(50% - 5px);
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid rgba(0, 0, 0, 0.8);
}
}
web/src/pages/Home.tsx
View file @
914c0620
...
...
@@ -13,6 +13,7 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
{
useFilterStore
}
from
"@/store/module"
;
import
{
useMemoList
,
useMemoStore
}
from
"@/store/v1"
;
import
{
RowStatus
}
from
"@/types/proto/api/v2/common"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
Home
=
()
=>
{
...
...
@@ -26,6 +27,7 @@ const Home = () => {
const
[
isComplete
,
setIsComplete
]
=
useState
(
false
);
const
{
tag
:
tagQuery
,
text
:
textQuery
}
=
filterStore
.
state
;
const
sortedMemos
=
memoList
.
value
.
filter
((
memo
)
=>
memo
.
rowStatus
===
RowStatus
.
ACTIVE
)
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
))
.
sort
((
a
,
b
)
=>
Number
(
b
.
pinned
)
-
Number
(
a
.
pinned
));
...
...
@@ -62,7 +64,7 @@ const Home = () => {
<
MobileHeader
>
{
!
md
&&
<
HomeSidebarDrawer
/>
}
</
MobileHeader
>
<
div
className=
"w-full px-4 sm:px-6 md:pr-2"
>
<
MemoEditor
className=
"mb-2"
cacheKey=
"home-memo-editor"
/>
<
div
className=
"flex flex-col justify-start items-start w-full max-w-full
overflow-y-scroll pb-28 hide-scrollbar
"
>
<
div
className=
"flex flex-col justify-start items-start w-full max-w-full
pb-28
"
>
<
MemoFilter
/>
{
sortedMemos
.
map
((
memo
)
=>
(
<
MemoView
key=
{
`${memo.id}-${memo.updateTime}`
}
memo=
{
memo
}
showVisibility
showPinnedStyle
showParent
/>
...
...
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