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
cd38ec93
Commit
cd38ec93
authored
Jul 25, 2024
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: implement memo filters
parent
b3b4aa9d
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
175 additions
and
141 deletions
+175
-141
Tag.tsx
web/src/components/MemoContent/Tag.tsx
+9
-6
MemoFilters.tsx
web/src/components/MemoFilters.tsx
+40
-0
RenameTagDialog.tsx
web/src/components/RenameTagDialog.tsx
+0
-3
SearchBar.tsx
web/src/components/SearchBar.tsx
+19
-19
TagTree.tsx
web/src/components/TagTree.tsx
+12
-11
UserStatisticsView.tsx
web/src/components/UserStatisticsView.tsx
+5
-10
index.ts
web/src/hooks/index.ts
+0
-1
useFilterWithUrlParams.ts
web/src/hooks/useFilterWithUrlParams.ts
+0
-45
Archived.tsx
web/src/pages/Archived.tsx
+11
-9
Explore.tsx
web/src/pages/Explore.tsx
+11
-9
Home.tsx
web/src/pages/Home.tsx
+16
-19
UserProfile.tsx
web/src/pages/UserProfile.tsx
+11
-9
index.ts
web/src/store/v1/index.ts
+1
-0
memoFilter.ts
web/src/store/v1/memoFilter.ts
+40
-0
No files found.
web/src/components/MemoContent/Tag.tsx
View file @
cd38ec93
import
clsx
from
"clsx"
;
import
{
useContext
}
from
"react"
;
import
{
use
FilterStore
}
from
"@/store/module
"
;
import
{
use
MemoFilterStore
}
from
"@/store/v1
"
;
import
{
RendererContext
}
from
"./types"
;
interface
Props
{
...
...
@@ -9,18 +9,21 @@ interface Props {
const
Tag
:
React
.
FC
<
Props
>
=
({
content
}:
Props
)
=>
{
const
context
=
useContext
(
RendererContext
);
const
filterStore
=
use
FilterStore
();
const
memoFilterStore
=
useMemo
FilterStore
();
const
handleTagClick
=
()
=>
{
if
(
context
.
disableFilter
)
{
return
;
}
const
currTagQuery
=
filterStore
.
getState
().
tag
;
if
(
currTagQuery
===
content
)
{
filterStore
.
setTagFilter
(
undefined
);
const
isActive
=
memoFilterStore
.
getFiltersByFactor
(
"tag"
).
some
((
filter
)
=>
filter
.
value
===
content
)
;
if
(
isActive
)
{
memoFilterStore
.
removeFilter
((
f
)
=>
f
.
factor
===
"tag"
&&
f
.
value
===
content
);
}
else
{
filterStore
.
setTagFilter
(
content
);
memoFilterStore
.
addFilter
({
factor
:
"tag"
,
value
:
content
,
});
}
};
...
...
web/src/components/MemoFilters.tsx
0 → 100644
View file @
cd38ec93
import
{
isEqual
}
from
"lodash-es"
;
import
{
useMemoFilterStore
}
from
"@/store/v1"
;
import
Icon
from
"./Icon"
;
const
MemoFilters
=
()
=>
{
const
memoFilterStore
=
useMemoFilterStore
();
const
filters
=
memoFilterStore
.
filters
;
if
(
filters
.
length
===
0
)
{
return
undefined
;
}
return
(
<
div
className=
"w-full mb-2 flex flex-row justify-start items-start gap-2"
>
<
span
className=
"flex flex-row items-center gap-0.5 text-gray-500 text-sm leading-6"
>
<
Icon
.
Filter
className=
"w-4 h-auto opacity-60 inline"
/>
Filters
</
span
>
<
div
className=
"flex flex-row justify-start items-center flex-wrap gap-2 leading-6"
>
{
filters
.
map
((
filter
)
=>
(
<
div
key=
{
filter
.
factor
}
className=
"flex flex-row items-center gap-1 bg-gray-100 dark:bg-zinc-800 border dark:border-zinc-700 px-1 rounded-md"
>
<
span
className=
"text-gray-600 dark:text-gray-500 text-sm"
>
{
filter
.
factor
}
</
span
>
{
filter
.
value
&&
<
span
className=
"text-gray-500 dark:text-gray-400 text-sm max-w-12 truncate"
>
{
filter
.
value
}
</
span
>
}
<
button
onClick=
{
()
=>
memoFilterStore
.
removeFilter
((
f
)
=>
isEqual
(
f
,
filter
))
}
className=
"text-gray-500 dark:text-gray-300 opacity-60 hover:opacity-100"
>
<
Icon
.
X
className=
"w-3 h-auto"
/>
</
button
>
</
div
>
))
}
</
div
>
</
div
>
);
};
export
default
MemoFilters
;
web/src/components/RenameTagDialog.tsx
View file @
cd38ec93
...
...
@@ -4,7 +4,6 @@ import { toast } from "react-hot-toast";
import
{
memoServiceClient
}
from
"@/grpcweb"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
{
useFilterStore
}
from
"@/store/module"
;
import
{
useTagStore
}
from
"@/store/v1"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateDialog
}
from
"./Dialog"
;
...
...
@@ -18,7 +17,6 @@ const RenameTagDialog: React.FC<Props> = (props: Props) => {
const
{
tag
,
destroy
}
=
props
;
const
t
=
useTranslate
();
const
tagStore
=
useTagStore
();
const
filterStore
=
useFilterStore
();
const
[
newName
,
setNewName
]
=
useState
(
tag
);
const
requestState
=
useLoading
(
false
);
const
user
=
useCurrentUser
();
...
...
@@ -44,7 +42,6 @@ const RenameTagDialog: React.FC<Props> = (props: Props) => {
newTag
:
newName
,
});
toast
.
success
(
"Rename tag successfully"
);
filterStore
.
setTagFilter
(
newName
);
tagStore
.
fetchTags
({
user
},
{
skipCache
:
true
});
}
catch
(
error
:
any
)
{
console
.
error
(
error
);
...
...
web/src/components/SearchBar.tsx
View file @
cd38ec93
import
{
useEffect
,
useState
}
from
"react"
;
import
useDebounce
from
"react-use/lib/useDebounce"
;
import
{
useFilterStore
}
from
"@/store/module"
;
import
{
useState
}
from
"react"
;
import
{
useMemoFilterStore
}
from
"@/store/v1"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
Icon
from
"./Icon"
;
const
SearchBar
=
()
=>
{
const
t
=
useTranslate
();
const
filterStore
=
use
FilterStore
();
const
memoFilterStore
=
useMemo
FilterStore
();
const
[
queryText
,
setQueryText
]
=
useState
(
""
);
useEffect
(()
=>
{
const
text
=
filterStore
.
getState
().
text
;
setQueryText
(
text
===
undefined
?
""
:
text
);
},
[
filterStore
.
state
.
text
]);
useDebounce
(
()
=>
{
filterStore
.
setTextFilter
(
queryText
.
length
===
0
?
undefined
:
queryText
);
},
1000
,
[
queryText
],
);
const
handleTextQueryInput
=
(
event
:
React
.
FormEvent
<
HTMLInputElement
>
)
=>
{
const
onTextChange
=
(
event
:
React
.
FormEvent
<
HTMLInputElement
>
)
=>
{
setQueryText
(
event
.
currentTarget
.
value
);
};
const
onKeyDown
=
(
e
:
React
.
KeyboardEvent
<
HTMLInputElement
>
)
=>
{
if
(
e
.
key
===
"Enter"
)
{
e
.
preventDefault
();
if
(
queryText
!==
""
)
{
memoFilterStore
.
removeFilter
((
f
)
=>
f
.
factor
===
"contentSearch"
);
memoFilterStore
.
addFilter
({
factor
:
"contentSearch"
,
value
:
queryText
,
});
}
}
};
return
(
<
div
className=
"relative w-full h-auto flex flex-row justify-start items-center"
>
<
Icon
.
Search
className=
"absolute left-3 w-4 h-auto opacity-30"
/>
...
...
@@ -33,7 +32,8 @@ const SearchBar = () => {
className=
"w-full text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 border dark:border-zinc-800 text-sm leading-7 rounded-lg p-1 pl-8 outline-none"
placeholder=
{
t
(
"memo.search-placeholder"
)
}
value=
{
queryText
}
onChange=
{
handleTextQueryInput
}
onChange=
{
onTextChange
}
onKeyDown=
{
onKeyDown
}
/>
</
div
>
);
...
...
web/src/components/TagTree.tsx
View file @
cd38ec93
import
{
useEffect
,
useState
}
from
"react"
;
import
useToggle
from
"react-use/lib/useToggle"
;
import
{
use
FilterStore
}
from
"@/store/module
"
;
import
{
use
MemoFilterStore
}
from
"@/store/v1
"
;
import
Icon
from
"./Icon"
;
interface
Tag
{
...
...
@@ -14,8 +14,6 @@ interface Props {
}
const
TagTree
=
({
tags
:
rawTags
}:
Props
)
=>
{
const
filterStore
=
useFilterStore
();
const
filter
=
filterStore
.
state
;
const
[
tags
,
setTags
]
=
useState
<
Tag
[]
>
([]);
useEffect
(()
=>
{
...
...
@@ -67,7 +65,7 @@ const TagTree = ({ tags: rawTags }: Props) => {
return
(
<
div
className=
"flex flex-col justify-start items-start relative w-full h-auto flex-nowrap gap-2 mt-1"
>
{
tags
.
map
((
t
,
idx
)
=>
(
<
TagItemContainer
key=
{
t
.
text
+
"-"
+
idx
}
tag=
{
t
}
tagQuery=
{
filter
.
tag
}
/>
<
TagItemContainer
key=
{
t
.
text
+
"-"
+
idx
}
tag=
{
t
}
/>
))
}
</
div
>
);
...
...
@@ -75,21 +73,24 @@ const TagTree = ({ tags: rawTags }: Props) => {
interface
TagItemContainerProps
{
tag
:
Tag
;
tagQuery
?:
string
;
}
const
TagItemContainer
:
React
.
FC
<
TagItemContainerProps
>
=
(
props
:
TagItemContainerProps
)
=>
{
const
filterStore
=
useFilterStore
();
const
{
tag
,
tagQuery
}
=
props
;
const
isActive
=
tagQuery
===
tag
.
text
;
const
{
tag
}
=
props
;
const
memoFilterStore
=
useMemoFilterStore
();
const
tagFilters
=
memoFilterStore
.
getFiltersByFactor
(
"tag"
);
const
isActive
=
tagFilters
.
some
((
f
)
=>
f
.
value
===
tag
.
text
);
const
hasSubTags
=
tag
.
subTags
.
length
>
0
;
const
[
showSubTags
,
toggleSubTags
]
=
useToggle
(
false
);
const
handleTagClick
=
()
=>
{
if
(
isActive
)
{
filterStore
.
setTagFilter
(
undefined
);
memoFilterStore
.
removeFilter
((
f
)
=>
f
.
factor
===
"tag"
&&
f
.
value
===
tag
.
text
);
}
else
{
filterStore
.
setTagFilter
(
tag
.
text
);
memoFilterStore
.
addFilter
({
factor
:
"tag"
,
value
:
tag
.
text
,
});
}
};
...
...
@@ -131,7 +132,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
}`
}
>
{
tag
.
subTags
.
map
((
st
,
idx
)
=>
(
<
TagItemContainer
key=
{
st
.
text
+
"-"
+
idx
}
tag=
{
st
}
tagQuery=
{
tagQuery
}
/>
<
TagItemContainer
key=
{
st
.
text
+
"-"
+
idx
}
tag=
{
st
}
/>
))
}
</
div
>
)
:
null
}
...
...
web/src/components/UserStatisticsView.tsx
View file @
cd38ec93
...
...
@@ -7,8 +7,7 @@ import { memoServiceClient } from "@/grpcweb";
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
i18n
from
"@/i18n"
;
import
{
useFilterStore
}
from
"@/store/module"
;
import
{
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemoFilterStore
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
ActivityCalendar
from
"./ActivityCalendar"
;
import
Icon
from
"./Icon"
;
...
...
@@ -25,14 +24,13 @@ const UserStatisticsView = () => {
const
t
=
useTranslate
();
const
currentUser
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
filterStore
=
use
FilterStore
();
const
memoFilterStore
=
useMemo
FilterStore
();
const
[
memoAmount
,
setMemoAmount
]
=
useState
(
0
);
const
[
memoStats
,
setMemoStats
]
=
useState
<
UserMemoStats
>
({
link
:
0
,
taskList
:
0
,
code
:
0
,
incompleteTasks
:
0
});
const
[
activityStats
,
setActivityStats
]
=
useState
<
Record
<
string
,
number
>>
({});
const
[
selectedDate
]
=
useState
(
new
Date
());
const
[
monthString
,
setMonthString
]
=
useState
(
dayjs
(
selectedDate
.
toDateString
()).
format
(
"YYYY-MM"
));
const
days
=
Math
.
ceil
((
Date
.
now
()
-
currentUser
.
createTime
!
.
getTime
())
/
86400000
);
const
filter
=
filterStore
.
state
;
useAsyncEffect
(
async
()
=>
{
const
{
properties
}
=
await
memoServiceClient
.
listMemoProperties
({
...
...
@@ -120,9 +118,8 @@ const UserStatisticsView = () => {
<
div
className=
{
clsx
(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow"
,
filter
.
memoPropertyFilter
?.
hasLink
?
"bg-blue-50 dark:bg-blue-900 shadow"
:
""
,
)
}
onClick=
{
()
=>
filterStore
.
setMemoPropertyFilter
({
hasLink
:
!
filter
.
memoPropertyFilter
?.
hasLink
})
}
onClick=
{
()
=>
memoFilterStore
.
addFilter
({
factor
:
"property.hasLink"
,
value
:
""
})
}
>
<
div
className=
"w-auto flex justify-start items-center mr-1"
>
<
Icon
.
Link
className=
"w-4 h-auto mr-1"
/>
...
...
@@ -133,9 +130,8 @@ const UserStatisticsView = () => {
<
div
className=
{
clsx
(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow"
,
filter
.
memoPropertyFilter
?.
hasTaskList
?
"bg-blue-50 dark:bg-blue-900 shadow"
:
""
,
)
}
onClick=
{
()
=>
filterStore
.
setMemoPropertyFilter
({
hasTaskList
:
!
filter
.
memoPropertyFilter
?.
hasTaskList
})
}
onClick=
{
()
=>
memoFilterStore
.
addFilter
({
factor
:
"property.hasTaskList"
,
value
:
""
})
}
>
<
div
className=
"w-auto flex justify-start items-center mr-1"
>
{
memoStats
.
incompleteTasks
>
0
?
(
...
...
@@ -160,9 +156,8 @@ const UserStatisticsView = () => {
<
div
className=
{
clsx
(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow"
,
filter
.
memoPropertyFilter
?.
hasCode
?
"bg-blue-50 dark:bg-blue-900 shadow"
:
""
,
)
}
onClick=
{
()
=>
filterStore
.
setMemoPropertyFilter
({
hasCode
:
!
filter
.
memoPropertyFilter
?.
hasCode
})
}
onClick=
{
()
=>
memoFilterStore
.
addFilter
({
factor
:
"property.hasCode"
,
value
:
""
})
}
>
<
div
className=
"w-auto flex justify-start items-center mr-1"
>
<
Icon
.
Code2
className=
"w-4 h-auto mr-1"
/>
...
...
web/src/hooks/index.ts
View file @
cd38ec93
...
...
@@ -2,5 +2,4 @@ export * from "./useLoading";
export
*
from
"./useCurrentUser"
;
export
*
from
"./useNavigateTo"
;
export
*
from
"./useAsyncEffect"
;
export
*
from
"./useFilterWithUrlParams"
;
export
*
from
"./useResponsiveWidth"
;
web/src/hooks/useFilterWithUrlParams.ts
deleted
100644 → 0
View file @
b3b4aa9d
import
{
useEffect
}
from
"react"
;
import
{
useLocation
}
from
"react-router-dom"
;
import
{
useFilterStore
}
from
"@/store/module"
;
const
useFilterWithUrlParams
=
()
=>
{
const
location
=
useLocation
();
const
filterStore
=
useFilterStore
();
const
{
tag
,
text
,
memoPropertyFilter
}
=
filterStore
.
state
;
useEffect
(()
=>
{
const
urlParams
=
new
URLSearchParams
(
location
.
search
);
const
tag
=
urlParams
.
get
(
"tag"
);
const
text
=
urlParams
.
get
(
"text"
);
if
(
tag
)
{
filterStore
.
setTagFilter
(
tag
);
}
if
(
text
)
{
filterStore
.
setTextFilter
(
text
);
}
},
[]);
useEffect
(()
=>
{
const
urlParams
=
new
URLSearchParams
(
location
.
search
);
if
(
tag
)
{
urlParams
.
set
(
"tag"
,
tag
);
}
else
{
urlParams
.
delete
(
"tag"
);
}
if
(
text
)
{
urlParams
.
set
(
"text"
,
text
);
}
else
{
urlParams
.
delete
(
"text"
);
}
const
params
=
urlParams
.
toString
();
window
.
history
.
replaceState
({},
""
,
`
${
location
.
pathname
}${
params
?.
length
>
0
?
`?
${
params
}
`
:
""
}
`);
}, [tag, text]);
return {
tag,
text,
memoPropertyFilter,
};
};
export default useFilterWithUrlParams;
web/src/pages/Archived.tsx
View file @
cd38ec93
...
...
@@ -5,13 +5,13 @@ import toast from "react-hot-toast";
import
Empty
from
"@/components/Empty"
;
import
Icon
from
"@/components/Icon"
;
import
MemoContent
from
"@/components/MemoContent"
;
import
MemoFilters
from
"@/components/MemoFilters"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
SearchBar
from
"@/components/SearchBar"
;
import
{
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
{
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useFilterWithUrlParams
from
"@/hooks/useFilterWithUrlParams"
;
import
{
useMemoList
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemoFilterStore
,
useMemoList
,
useMemoStore
}
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"
;
...
...
@@ -21,9 +21,9 @@ const Archived = () => {
const
user
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
memoList
=
useMemoList
();
const
memoFilterStore
=
useMemoFilterStore
();
const
[
isRequesting
,
setIsRequesting
]
=
useState
(
true
);
const
[
nextPageToken
,
setNextPageToken
]
=
useState
<
string
>
(
""
);
const
{
tag
:
tagQuery
,
text
:
textQuery
}
=
useFilterWithUrlParams
();
const
sortedMemos
=
memoList
.
value
.
filter
((
memo
)
=>
memo
.
rowStatus
===
RowStatus
.
ARCHIVED
)
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
));
...
...
@@ -31,21 +31,22 @@ const Archived = () => {
useEffect
(()
=>
{
memoList
.
reset
();
fetchMemos
(
""
);
},
[
tagQuery
,
textQuery
]);
},
[
memoFilterStore
.
filters
]);
const
fetchMemos
=
async
(
nextPageToken
:
string
)
=>
{
setIsRequesting
(
true
);
const
filters
=
[
`creator == "
${
user
.
name
}
"`
,
`row_status == "ARCHIVED"`
];
const
contentSearch
:
string
[]
=
[];
if
(
textQuery
)
{
contentSearch
.
push
(
JSON
.
stringify
(
textQuery
));
for
(
const
filter
of
memoFilterStore
.
filters
)
{
if
(
filter
.
factor
===
"contentSearch"
)
{
contentSearch
.
push
(
`"
${
filter
.
value
}
"`
);
}
else
if
(
filter
.
factor
===
"tag"
)
{
filters
.
push
(
`tag == "
${
filter
.
value
}
"`
);
}
}
if
(
contentSearch
.
length
>
0
)
{
filters
.
push
(
`content_search == [
${
contentSearch
.
join
(
", "
)}
]`
);
}
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
const
response
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
...
...
@@ -92,6 +93,7 @@ const Archived = () => {
<
SearchBar
/>
</
div
>
</
div
>
<
MemoFilters
/>
{
sortedMemos
.
map
((
memo
)
=>
(
<
div
key=
{
memo
.
name
}
...
...
web/src/pages/Explore.tsx
View file @
cd38ec93
...
...
@@ -4,14 +4,14 @@ import { useEffect, useState } from "react";
import
Empty
from
"@/components/Empty"
;
import
{
ExploreSidebar
,
ExploreSidebarDrawer
}
from
"@/components/ExploreSidebar"
;
import
Icon
from
"@/components/Icon"
;
import
MemoFilters
from
"@/components/MemoFilters"
;
import
MemoView
from
"@/components/MemoView"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
{
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
{
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useFilterWithUrlParams
from
"@/hooks/useFilterWithUrlParams"
;
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
{
useMemoList
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemo
FilterStore
,
useMemo
List
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
Explore
=
()
=>
{
...
...
@@ -20,29 +20,30 @@ const Explore = () => {
const
user
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
memoList
=
useMemoList
();
const
memoFilterStore
=
useMemoFilterStore
();
const
[
isRequesting
,
setIsRequesting
]
=
useState
(
true
);
const
[
nextPageToken
,
setNextPageToken
]
=
useState
<
string
>
(
""
);
const
{
tag
:
tagQuery
,
text
:
textQuery
}
=
useFilterWithUrlParams
();
const
sortedMemos
=
memoList
.
value
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
));
useEffect
(()
=>
{
memoList
.
reset
();
fetchMemos
(
""
);
},
[
tagQuery
,
textQuery
]);
},
[
memoFilterStore
.
filters
]);
const
fetchMemos
=
async
(
nextPageToken
:
string
)
=>
{
setIsRequesting
(
true
);
const
filters
=
[
`row_status == "NORMAL"`
,
`visibilities == [
${
user
?
"'PUBLIC', 'PROTECTED'"
:
"'PUBLIC'"
}
]`
];
const
contentSearch
:
string
[]
=
[];
if
(
textQuery
)
{
contentSearch
.
push
(
JSON
.
stringify
(
textQuery
));
for
(
const
filter
of
memoFilterStore
.
filters
)
{
if
(
filter
.
factor
===
"contentSearch"
)
{
contentSearch
.
push
(
`"
${
filter
.
value
}
"`
);
}
else
if
(
filter
.
factor
===
"tag"
)
{
filters
.
push
(
`tag == "
${
filter
.
value
}
"`
);
}
}
if
(
contentSearch
.
length
>
0
)
{
filters
.
push
(
`content_search == [
${
contentSearch
.
join
(
", "
)}
]`
);
}
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
const
response
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
...
...
@@ -61,6 +62,7 @@ const Explore = () => {
)
}
<
div
className=
{
clsx
(
"w-full flex flex-row justify-start items-start px-4 sm:px-6 gap-4"
)
}
>
<
div
className=
{
clsx
(
md
?
"w-[calc(100%-15rem)]"
:
"w-full"
)
}
>
<
MemoFilters
/>
<
div
className=
"flex flex-col justify-start items-start w-full max-w-full"
>
{
sortedMemos
.
map
((
memo
)
=>
(
<
MemoView
key=
{
`${memo.name}-${memo.updateTime}`
}
memo=
{
memo
}
showCreator
showVisibility
showPinned
compact
/>
...
...
web/src/pages/Home.tsx
View file @
cd38ec93
...
...
@@ -5,14 +5,14 @@ import Empty from "@/components/Empty";
import
{
HomeSidebar
,
HomeSidebarDrawer
}
from
"@/components/HomeSidebar"
;
import
Icon
from
"@/components/Icon"
;
import
MemoEditor
from
"@/components/MemoEditor"
;
import
MemoFilters
from
"@/components/MemoFilters"
;
import
MemoView
from
"@/components/MemoView"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
{
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
{
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useFilterWithUrlParams
from
"@/hooks/useFilterWithUrlParams"
;
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
{
useMemoList
,
useMemoStore
}
from
"@/store/v1"
;
import
{
useMemo
FilterStore
,
useMemo
List
,
useMemoStore
}
from
"@/store/v1"
;
import
{
RowStatus
}
from
"@/types/proto/api/v1/common"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -22,9 +22,9 @@ const Home = () => {
const
user
=
useCurrentUser
();
const
memoStore
=
useMemoStore
();
const
memoList
=
useMemoList
();
const
memoFilterStore
=
useMemoFilterStore
();
const
[
isRequesting
,
setIsRequesting
]
=
useState
(
true
);
const
[
nextPageToken
,
setNextPageToken
]
=
useState
<
string
>
(
""
);
const
filter
=
useFilterWithUrlParams
();
const
sortedMemos
=
memoList
.
value
.
filter
((
memo
)
=>
memo
.
rowStatus
===
RowStatus
.
ACTIVE
)
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
))
...
...
@@ -33,32 +33,28 @@ const Home = () => {
useEffect
(()
=>
{
memoList
.
reset
();
fetchMemos
(
""
);
},
[
filter
.
tag
,
filter
.
text
,
filter
.
memoPropertyFilter
]);
},
[
memoFilterStore
.
filters
]);
const
fetchMemos
=
async
(
nextPageToken
:
string
)
=>
{
setIsRequesting
(
true
);
const
filters
=
[
`creator == "
${
user
.
name
}
"`
,
`row_status == "NORMAL"`
,
`order_by_pinned == true`
];
const
contentSearch
:
string
[]
=
[];
if
(
filter
.
text
)
{
contentSearch
.
push
(
JSON
.
stringify
(
filter
.
text
));
}
if
(
contentSearch
.
length
>
0
)
{
filters
.
push
(
`content_search == [
${
contentSearch
.
join
(
", "
)}
]`
);
}
if
(
filter
.
tag
)
{
filters
.
push
(
`tag == "
${
filter
.
tag
}
"`
);
}
if
(
filter
.
memoPropertyFilter
)
{
if
(
filter
.
memoPropertyFilter
.
hasLink
)
{
for
(
const
filter
of
memoFilterStore
.
filters
)
{
if
(
filter
.
factor
===
"contentSearch"
)
{
contentSearch
.
push
(
`"
${
filter
.
value
}
"`
);
}
else
if
(
filter
.
factor
===
"tag"
)
{
filters
.
push
(
`tag == "
${
filter
.
value
}
"`
);
}
else
if
(
filter
.
factor
===
"property.hasLink"
)
{
filters
.
push
(
`has_link == true`
);
}
if
(
filter
.
memoPropertyFilter
.
hasTaskList
)
{
}
else
if
(
filter
.
factor
===
"property.hasTaskList"
)
{
filters
.
push
(
`has_task_list == true`
);
}
if
(
filter
.
memoPropertyFilter
.
hasCode
)
{
}
else
if
(
filter
.
factor
===
"property.hasCode"
)
{
filters
.
push
(
`has_code == true`
);
}
}
if
(
contentSearch
.
length
>
0
)
{
filters
.
push
(
`content_search == [
${
contentSearch
.
join
(
", "
)}
]`
);
}
const
response
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
...
...
@@ -78,6 +74,7 @@ const Home = () => {
<
div
className=
{
clsx
(
"w-full flex flex-row justify-start items-start px-4 sm:px-6 gap-4"
)
}
>
<
div
className=
{
clsx
(
md
?
"w-[calc(100%-15rem)]"
:
"w-full"
)
}
>
<
MemoEditor
className=
"mb-2"
cacheKey=
"home-memo-editor"
/>
<
MemoFilters
/>
<
div
className=
"flex flex-col justify-start items-start w-full max-w-full"
>
{
sortedMemos
.
map
((
memo
)
=>
(
<
MemoView
key=
{
`${memo.name}-${memo.updateTime}`
}
memo=
{
memo
}
showVisibility
showPinned
compact
/>
...
...
web/src/pages/UserProfile.tsx
View file @
cd38ec93
...
...
@@ -5,14 +5,14 @@ import { toast } from "react-hot-toast";
import
{
useParams
}
from
"react-router-dom"
;
import
Empty
from
"@/components/Empty"
;
import
Icon
from
"@/components/Icon"
;
import
MemoFilters
from
"@/components/MemoFilters"
;
import
MemoView
from
"@/components/MemoView"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
UserAvatar
from
"@/components/UserAvatar"
;
import
{
DEFAULT_LIST_MEMOS_PAGE_SIZE
}
from
"@/helpers/consts"
;
import
{
getTimeStampByDate
}
from
"@/helpers/datetime"
;
import
useFilterWithUrlParams
from
"@/hooks/useFilterWithUrlParams"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
{
useMemoList
,
useMemoStore
,
useUserStore
}
from
"@/store/v1"
;
import
{
useMemo
FilterStore
,
useMemo
List
,
useMemoStore
,
useUserStore
}
from
"@/store/v1"
;
import
{
User
}
from
"@/types/proto/api/v1/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
@@ -24,9 +24,9 @@ const UserProfile = () => {
const
[
user
,
setUser
]
=
useState
<
User
>
();
const
memoStore
=
useMemoStore
();
const
memoList
=
useMemoList
();
const
memoFilterStore
=
useMemoFilterStore
();
const
[
isRequesting
,
setIsRequesting
]
=
useState
(
true
);
const
[
nextPageToken
,
setNextPageToken
]
=
useState
<
string
>
(
""
);
const
{
tag
:
tagQuery
,
text
:
textQuery
}
=
useFilterWithUrlParams
();
const
sortedMemos
=
memoList
.
value
.
sort
((
a
,
b
)
=>
getTimeStampByDate
(
b
.
displayTime
)
-
getTimeStampByDate
(
a
.
displayTime
))
.
sort
((
a
,
b
)
=>
Number
(
b
.
pinned
)
-
Number
(
a
.
pinned
));
...
...
@@ -60,7 +60,7 @@ const UserProfile = () => {
memoList
.
reset
();
fetchMemos
(
""
);
},
[
user
,
tagQuery
,
textQuery
]);
},
[
user
,
memoFilterStore
.
filters
]);
const
fetchMemos
=
async
(
nextPageToken
:
string
)
=>
{
if
(
!
user
)
{
...
...
@@ -70,15 +70,16 @@ const UserProfile = () => {
setIsRequesting
(
true
);
const
filters
=
[
`creator == "
${
user
.
name
}
"`
,
`row_status == "NORMAL"`
,
`order_by_pinned == true`
];
const
contentSearch
:
string
[]
=
[];
if
(
textQuery
)
{
contentSearch
.
push
(
JSON
.
stringify
(
textQuery
));
for
(
const
filter
of
memoFilterStore
.
filters
)
{
if
(
filter
.
factor
===
"contentSearch"
)
{
contentSearch
.
push
(
`"
${
filter
.
value
}
"`
);
}
else
if
(
filter
.
factor
===
"tag"
)
{
filters
.
push
(
`tag == "
${
filter
.
value
}
"`
);
}
}
if
(
contentSearch
.
length
>
0
)
{
filters
.
push
(
`content_search == [
${
contentSearch
.
join
(
", "
)}
]`
);
}
if
(
tagQuery
)
{
filters
.
push
(
`tag == "
${
tagQuery
}
"`
);
}
const
response
=
await
memoStore
.
fetchMemos
({
pageSize
:
DEFAULT_LIST_MEMOS_PAGE_SIZE
,
filter
:
filters
.
join
(
" && "
),
...
...
@@ -125,6 +126,7 @@ const UserProfile = () => {
</
p
>
</
div
>
</
div
>
<
MemoFilters
/>
{
sortedMemos
.
map
((
memo
)
=>
(
<
MemoView
key=
{
`${memo.name}-${memo.displayTime}`
}
memo=
{
memo
}
showVisibility
showPinned
compact
/>
))
}
...
...
web/src/store/v1/index.ts
View file @
cd38ec93
...
...
@@ -5,3 +5,4 @@ export * from "./resourceName";
export
*
from
"./resource"
;
export
*
from
"./workspaceSetting"
;
export
*
from
"./tag"
;
export
*
from
"./memoFilter"
;
web/src/store/v1/memoFilter.ts
0 → 100644
View file @
cd38ec93
import
{
uniq
}
from
"lodash-es"
;
import
{
create
}
from
"zustand"
;
import
{
combine
,
persist
}
from
"zustand/middleware"
;
type
FilterFactor
=
|
"tag"
|
"visibility"
|
"contentSearch"
|
"displayTime"
|
"property.hasLink"
|
"property.hasTaskList"
|
"property.hasCode"
;
export
interface
MemoFilter
{
factor
:
FilterFactor
;
value
:
string
;
}
interface
State
{
filters
:
MemoFilter
[];
}
const
getDefaultState
=
():
State
=>
({
filters
:
[],
});
export
const
useMemoFilterStore
=
create
(
persist
(
combine
(
getDefaultState
(),
(
set
,
get
)
=>
({
setState
:
(
state
:
State
)
=>
set
(
state
),
getState
:
()
=>
get
(),
getFiltersByFactor
:
(
factor
:
FilterFactor
)
=>
get
().
filters
.
filter
((
f
)
=>
f
.
factor
===
factor
),
addFilter
:
(
filter
:
MemoFilter
)
=>
set
((
state
)
=>
({
filters
:
uniq
([...
state
.
filters
,
filter
])
})),
removeFilter
:
(
filterFn
:
(
f
:
MemoFilter
)
=>
boolean
)
=>
set
((
state
)
=>
({
filters
:
state
.
filters
.
filter
((
f
)
=>
!
filterFn
(
f
))
})),
})),
{
name
:
"memo-filter"
,
},
),
);
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