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
3343dc73
Commit
3343dc73
authored
Jun 02, 2025
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: update navigator
parent
663e5674
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
74 additions
and
151 deletions
+74
-151
DateTimeInput.tsx
web/src/components/DateTimeInput.tsx
+2
-4
HomeSidebar.tsx
web/src/components/HomeSidebar/HomeSidebar.tsx
+2
-50
index.tsx
web/src/components/MemoEditor/index.tsx
+1
-1
Navigation.tsx
web/src/components/Navigation.tsx
+15
-24
MonthNavigator.tsx
web/src/components/StatisticsView/MonthNavigator.tsx
+5
-6
StatCard.tsx
web/src/components/StatisticsView/StatCard.tsx
+2
-2
StatisticsView.tsx
web/src/components/StatisticsView/StatisticsView.tsx
+8
-4
UserBanner.tsx
web/src/components/UserBanner.tsx
+10
-2
Explore.tsx
web/src/pages/Explore.tsx
+21
-50
index.tsx
web/src/router/index.tsx
+8
-8
No files found.
web/src/components/DateTimeInput.tsx
View file @
3343dc73
import
dayjs
from
"dayjs"
;
import
{
isEqual
}
from
"lodash-es"
;
import
{
isEqual
}
from
"lodash-es"
;
import
toast
from
"react-hot-toast"
;
import
toast
from
"react-hot-toast"
;
import
{
cn
}
from
"@/utils"
;
import
{
cn
}
from
"@/utils"
;
// Helper function to convert Date to local datetime string.
// Helper function to convert Date to local datetime string.
const
toLocalDateTimeString
=
(
date
:
Date
|
undefined
):
string
=>
{
const
toLocalDateTimeString
=
(
date
:
Date
|
undefined
):
string
=>
{
if
(
!
date
)
return
""
;
return
date
?.
toLocaleString
()
||
""
;
return
dayjs
(
date
).
format
(
"YYYY-MM-DDTHH:mm:ss"
);
};
};
interface
Props
{
interface
Props
{
...
@@ -20,7 +18,7 @@ const DateTimeInput: React.FC<Props> = ({ value, originalValue, onChange }) => {
...
@@ -20,7 +18,7 @@ const DateTimeInput: React.FC<Props> = ({ value, originalValue, onChange }) => {
<
input
<
input
type=
"text"
type=
"text"
className=
{
cn
(
className=
{
cn
(
"
w-auto
px-1 bg-transparent rounded text-xs transition-all"
,
"px-1 bg-transparent rounded text-xs transition-all"
,
"border-transparent outline-none focus:border-gray-300 dark:focus:border-zinc-700"
,
"border-transparent outline-none focus:border-gray-300 dark:focus:border-zinc-700"
,
!
isEqual
(
value
,
originalValue
)
&&
"border-gray-300 dark:border-zinc-700"
,
!
isEqual
(
value
,
originalValue
)
&&
"border-gray-300 dark:border-zinc-700"
,
"border"
,
"border"
,
...
...
web/src/components/HomeSidebar/HomeSidebar.tsx
View file @
3343dc73
import
{
last
}
from
"lodash-es"
;
import
{
last
}
from
"lodash-es"
;
import
{
Globe2Icon
,
HomeIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
matchPath
,
NavLink
,
useLocation
}
from
"react-router-dom"
;
import
{
matchPath
,
useLocation
}
from
"react-router-dom"
;
import
useDebounce
from
"react-use/lib/useDebounce"
;
import
useDebounce
from
"react-use/lib/useDebounce"
;
import
SearchBar
from
"@/components/SearchBar"
;
import
SearchBar
from
"@/components/SearchBar"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
Routes
}
from
"@/router"
;
import
{
Routes
}
from
"@/router"
;
import
{
memoStore
,
userStore
}
from
"@/store/v2"
;
import
{
memoStore
,
userStore
}
from
"@/store/v2"
;
import
{
cn
}
from
"@/utils"
;
import
{
cn
}
from
"@/utils"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
MemoFilters
from
"../MemoFilters"
;
import
MemoFilters
from
"../MemoFilters"
;
import
StatisticsView
from
"../StatisticsView"
;
import
StatisticsView
from
"../StatisticsView"
;
import
ShortcutsSection
from
"./ShortcutsSection"
;
import
ShortcutsSection
from
"./ShortcutsSection"
;
import
TagsSection
from
"./TagsSection"
;
import
TagsSection
from
"./TagsSection"
;
interface
NavLinkItem
{
id
:
string
;
path
:
string
;
title
:
string
;
icon
:
React
.
ReactNode
;
}
interface
Props
{
interface
Props
{
className
?:
string
;
className
?:
string
;
}
}
const
HomeSidebar
=
observer
((
props
:
Props
)
=>
{
const
HomeSidebar
=
observer
((
props
:
Props
)
=>
{
const
t
=
useTranslate
();
const
location
=
useLocation
();
const
location
=
useLocation
();
const
currentUser
=
useCurrentUser
();
const
currentUser
=
useCurrentUser
();
const
homeNavLink
:
NavLinkItem
=
{
id
:
"header-home"
,
path
:
Routes
.
ROOT
,
title
:
t
(
"common.home"
),
icon
:
<
HomeIcon
className=
"w-4 h-auto opacity-70 shrink-0"
/>,
};
const
exploreNavLink
:
NavLinkItem
=
{
id
:
"header-explore"
,
path
:
Routes
.
EXPLORE
,
title
:
t
(
"common.explore"
),
icon
:
<
Globe2Icon
className=
"w-4 h-auto opacity-70 shrink-0"
/>,
};
const
navLinks
:
NavLinkItem
[]
=
currentUser
?
[
homeNavLink
,
exploreNavLink
]
:
[
exploreNavLink
];
useDebounce
(
useDebounce
(
async
()
=>
{
async
()
=>
{
let
parent
:
string
|
undefined
=
undefined
;
let
parent
:
string
|
undefined
=
undefined
;
...
@@ -65,30 +40,7 @@ const HomeSidebar = observer((props: Props) => {
...
@@ -65,30 +40,7 @@ const HomeSidebar = observer((props: Props) => {
return
(
return
(
<
aside
className=
{
cn
(
"relative w-full h-full overflow-auto flex flex-col justify-start items-start"
,
props
.
className
)
}
>
<
aside
className=
{
cn
(
"relative w-full h-full overflow-auto flex flex-col justify-start items-start"
,
props
.
className
)
}
>
<
SearchBar
/>
<
SearchBar
/>
<
div
className=
"mt-2 w-full space-y-1"
>
<
div
className=
"mt-1 px-1 w-full"
>
{
navLinks
.
map
((
navLink
)
=>
(
<
NavLink
key=
{
navLink
.
id
}
className=
{
({
isActive
})
=>
cn
(
"w-full px-2 rounded-xl border flex flex-row items-center justify-between text-sm text-zinc-600 dark:text-gray-400 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-700 dark:hover:bg-zinc-800"
,
isActive
?
"bg-white drop-shadow-sm dark:bg-zinc-800 border-gray-200 dark:border-zinc-700"
:
"border-transparent"
,
)
}
to=
{
navLink
.
path
}
viewTransition
>
<
div
className=
"flex flex-row items-center"
>
{
navLink
.
icon
}
<
span
className=
"ml-2 truncate leading-8"
>
{
navLink
.
title
}
</
span
>
</
div
>
{
navLink
.
path
===
Routes
.
ROOT
&&
currentUser
&&
userStore
.
state
.
currentUserStats
&&
(
<
span
className=
"font-mono text-xs opacity-80"
>
{
userStore
.
state
.
currentUserStats
.
totalMemoCount
}
</
span
>
)
}
</
NavLink
>
))
}
</
div
>
<
div
className=
"px-2 w-full"
>
<
StatisticsView
/>
<
StatisticsView
/>
<
MemoFilters
/>
<
MemoFilters
/>
{
currentUser
&&
<
ShortcutsSection
/>
}
{
currentUser
&&
<
ShortcutsSection
/>
}
...
...
web/src/components/MemoEditor/index.tsx
View file @
3343dc73
...
@@ -548,7 +548,7 @@ const MemoEditor = observer((props: Props) => {
...
@@ -548,7 +548,7 @@ const MemoEditor = observer((props: Props) => {
{
/* Show memo metadata if memoName is provided */
}
{
/* Show memo metadata if memoName is provided */
}
{
memoName
&&
(
{
memoName
&&
(
<
div
className=
"w-full mb-4 text-xs leading-5 px-4 opacity-60 font-mono text-gray-500 dark:text-zinc-500"
>
<
div
className=
"w-full
-mt-1
mb-4 text-xs leading-5 px-4 opacity-60 font-mono text-gray-500 dark:text-zinc-500"
>
<
div
className=
"grid grid-cols-[auto_1fr] gap-x-4 gap-y-0.5 items-center"
>
<
div
className=
"grid grid-cols-[auto_1fr] gap-x-4 gap-y-0.5 items-center"
>
{
!
isEqual
(
createTime
,
updateTime
)
&&
(
{
!
isEqual
(
createTime
,
updateTime
)
&&
(
<>
<>
...
...
web/src/components/Navigation.tsx
View file @
3343dc73
import
{
Tooltip
}
from
"@mui/joy"
;
import
{
Tooltip
}
from
"@mui/joy"
;
import
{
BellIcon
,
PaperclipIcon
,
Settings
Icon
,
UserCircleIcon
}
from
"lucide-react"
;
import
{
EarthIcon
,
LibraryIcon
,
Paperclip
Icon
,
UserCircleIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useEffect
}
from
"react"
;
import
{
useEffect
}
from
"react"
;
import
{
NavLink
}
from
"react-router-dom"
;
import
{
NavLink
}
from
"react-router-dom"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
Routes
}
from
"@/router"
;
import
{
Routes
}
from
"@/router"
;
import
{
userStore
}
from
"@/store/v2"
;
import
{
userStore
}
from
"@/store/v2"
;
import
{
Inbox_Status
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
cn
}
from
"@/utils"
;
import
{
cn
}
from
"@/utils"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
BrandBanner
from
"./BrandBanner"
;
import
BrandBanner
from
"./BrandBanner"
;
...
@@ -28,7 +27,6 @@ const Navigation = observer((props: Props) => {
...
@@ -28,7 +27,6 @@ const Navigation = observer((props: Props) => {
const
{
collapsed
,
className
}
=
props
;
const
{
collapsed
,
className
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
currentUser
=
useCurrentUser
();
const
currentUser
=
useCurrentUser
();
const
hasUnreadInbox
=
userStore
.
state
.
inboxes
.
some
((
inbox
)
=>
inbox
.
status
===
Inbox_Status
.
UNREAD
);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
!
currentUser
)
{
if
(
!
currentUser
)
{
...
@@ -38,31 +36,24 @@ const Navigation = observer((props: Props) => {
...
@@ -38,31 +36,24 @@ const Navigation = observer((props: Props) => {
userStore
.
fetchInboxes
();
userStore
.
fetchInboxes
();
},
[]);
},
[]);
const
homeNavLink
:
NavLinkItem
=
{
id
:
"header-memos"
,
path
:
Routes
.
ROOT
,
title
:
t
(
"common.memos"
),
icon
:
<
LibraryIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>,
};
const
exploreNavLink
:
NavLinkItem
=
{
id
:
"header-explore"
,
path
:
Routes
.
EXPLORE
,
title
:
t
(
"common.explore"
),
icon
:
<
EarthIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>,
};
const
resourcesNavLink
:
NavLinkItem
=
{
const
resourcesNavLink
:
NavLinkItem
=
{
id
:
"header-resources"
,
id
:
"header-resources"
,
path
:
Routes
.
RESOURCES
,
path
:
Routes
.
RESOURCES
,
title
:
t
(
"common.resources"
),
title
:
t
(
"common.resources"
),
icon
:
<
PaperclipIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>,
icon
:
<
PaperclipIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>,
};
};
const
inboxNavLink
:
NavLinkItem
=
{
id
:
"header-inbox"
,
path
:
Routes
.
INBOX
,
title
:
t
(
"common.inbox"
),
icon
:
(
<>
<
div
className=
"relative"
>
<
BellIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>
{
hasUnreadInbox
&&
<
div
className=
"absolute top-0 left-5 w-2 h-2 rounded-full bg-blue-500"
></
div
>
}
</
div
>
</>
),
};
const
settingNavLink
:
NavLinkItem
=
{
id
:
"header-setting"
,
path
:
Routes
.
SETTING
,
title
:
t
(
"common.settings"
),
icon
:
<
SettingsIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>,
};
const
signInNavLink
:
NavLinkItem
=
{
const
signInNavLink
:
NavLinkItem
=
{
id
:
"header-auth"
,
id
:
"header-auth"
,
path
:
Routes
.
AUTH
,
path
:
Routes
.
AUTH
,
...
@@ -70,7 +61,7 @@ const Navigation = observer((props: Props) => {
...
@@ -70,7 +61,7 @@ const Navigation = observer((props: Props) => {
icon
:
<
UserCircleIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>,
icon
:
<
UserCircleIcon
className=
"w-6 h-auto opacity-70 shrink-0"
/>,
};
};
const
navLinks
:
NavLinkItem
[]
=
currentUser
?
[
resourcesNavLink
,
inboxNavLink
,
settingNavLink
]
:
[
signInNavLink
];
const
navLinks
:
NavLinkItem
[]
=
currentUser
?
[
homeNavLink
,
exploreNavLink
,
resourcesNavLink
]
:
[
exploreNavLink
,
signInNavLink
];
return
(
return
(
<
header
<
header
...
@@ -80,7 +71,7 @@ const Navigation = observer((props: Props) => {
...
@@ -80,7 +71,7 @@ const Navigation = observer((props: Props) => {
)
}
)
}
>
>
<
div
className=
"w-full px-1 py-1 flex flex-col justify-start items-start space-y-2 overflow-auto hide-scrollbar shrink"
>
<
div
className=
"w-full px-1 py-1 flex flex-col justify-start items-start space-y-2 overflow-auto hide-scrollbar shrink"
>
<
NavLink
className=
"mb-2"
to=
{
currentUser
?
Routes
.
ROOT
:
Routes
.
EXPLORE
}
>
<
NavLink
className=
"mb-2
cursor-default
"
to=
{
currentUser
?
Routes
.
ROOT
:
Routes
.
EXPLORE
}
>
<
BrandBanner
collapsed=
{
collapsed
}
/>
<
BrandBanner
collapsed=
{
collapsed
}
/>
</
NavLink
>
</
NavLink
>
{
navLinks
.
map
((
navLink
)
=>
(
{
navLinks
.
map
((
navLink
)
=>
(
...
...
web/src/components/StatisticsView/MonthNavigator.tsx
View file @
3343dc73
import
dayjs
from
"dayjs"
;
import
dayjs
from
"dayjs"
;
import
{
C
alendarIcon
,
C
hevronLeftIcon
,
ChevronRightIcon
}
from
"lucide-react"
;
import
{
ChevronLeftIcon
,
ChevronRightIcon
}
from
"lucide-react"
;
import
i18n
from
"@/i18n"
;
import
i18n
from
"@/i18n"
;
import
type
{
MonthNavigatorProps
}
from
"@/types/statistics"
;
import
type
{
MonthNavigatorProps
}
from
"@/types/statistics"
;
...
@@ -16,15 +16,14 @@ export const MonthNavigator = ({ visibleMonth, onMonthChange }: MonthNavigatorPr
...
@@ -16,15 +16,14 @@ export const MonthNavigator = ({ visibleMonth, onMonthChange }: MonthNavigatorPr
return
(
return
(
<
div
className=
"w-full mb-1 flex flex-row justify-between items-center gap-1"
>
<
div
className=
"w-full mb-1 flex flex-row justify-between items-center gap-1"
>
<
div
className=
"relative text-sm inline-flex flex-row items-center w-auto gap-2 dark:text-gray-400"
>
<
span
className=
"relative text-sm dark:text-gray-400"
>
<
CalendarIcon
className=
"w-4 h-auto opacity-70 ml-px"
/>
{
currentMonth
.
toLocaleString
(
i18n
.
language
,
{
year
:
"numeric"
,
month
:
"long"
})
}
{
currentMonth
.
toLocaleString
(
i18n
.
language
,
{
year
:
"numeric"
,
month
:
"long"
})
}
</
div
>
</
span
>
<
div
className=
"flex justify-end items-center shrink-0 gap-1"
>
<
div
className=
"flex justify-end items-center shrink-0 gap-1"
>
<
button
className=
"
p-1
cursor-pointer hover:opacity-80 transition-opacity"
onClick=
{
handlePrevMonth
}
aria
-
label=
"Previous month"
>
<
button
className=
"cursor-pointer hover:opacity-80 transition-opacity"
onClick=
{
handlePrevMonth
}
aria
-
label=
"Previous month"
>
<
ChevronLeftIcon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
<
ChevronLeftIcon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
</
button
>
</
button
>
<
button
className=
"
p-1
cursor-pointer hover:opacity-80 transition-opacity"
onClick=
{
handleNextMonth
}
aria
-
label=
"Next month"
>
<
button
className=
"cursor-pointer hover:opacity-80 transition-opacity"
onClick=
{
handleNextMonth
}
aria
-
label=
"Next month"
>
<
ChevronRightIcon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
<
ChevronRightIcon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
</
button
>
</
button
>
</
div
>
</
div
>
...
...
web/src/components/StatisticsView/StatCard.tsx
View file @
3343dc73
...
@@ -14,9 +14,9 @@ export const StatCard = ({ icon, label, count, onClick, tooltip, className }: St
...
@@ -14,9 +14,9 @@ export const StatCard = ({ icon, label, count, onClick, tooltip, className }: St
>
>
<
div
className=
"w-auto flex justify-start items-center mr-1"
>
<
div
className=
"w-auto flex justify-start items-center mr-1"
>
{
icon
}
{
icon
}
<
span
className=
"block text-
sm
"
>
{
label
}
</
span
>
<
span
className=
"block text-
xs opacity-80
"
>
{
label
}
</
span
>
</
div
>
</
div
>
<
span
className=
"text-
sm truncate
"
>
{
count
}
</
span
>
<
span
className=
"text-
xs truncate opacity-80
"
>
{
count
}
</
span
>
</
div
>
</
div
>
);
);
...
...
web/src/components/StatisticsView/StatisticsView.tsx
View file @
3343dc73
...
@@ -49,7 +49,7 @@ const StatisticsView = observer(() => {
...
@@ -49,7 +49,7 @@ const StatisticsView = observer(() => {
<
div
className=
"pt-1 w-full flex flex-row justify-start items-center gap-1 flex-wrap"
>
<
div
className=
"pt-1 w-full flex flex-row justify-start items-center gap-1 flex-wrap"
>
{
isRootPath
&&
hasPinnedMemos
&&
(
{
isRootPath
&&
hasPinnedMemos
&&
(
<
StatCard
<
StatCard
icon=
{
<
BookmarkIcon
className=
"w-
4 h-auto mr-1
"
/>
}
icon=
{
<
BookmarkIcon
className=
"w-
3 h-auto mr-1 opacity-70
"
/>
}
label=
{
t
(
"common.pinned"
)
}
label=
{
t
(
"common.pinned"
)
}
count=
{
userStore
.
state
.
currentUserStats
!
.
pinnedMemos
.
length
}
count=
{
userStore
.
state
.
currentUserStats
!
.
pinnedMemos
.
length
}
onClick=
{
()
=>
handleFilterClick
(
"pinned"
)
}
onClick=
{
()
=>
handleFilterClick
(
"pinned"
)
}
...
@@ -57,7 +57,7 @@ const StatisticsView = observer(() => {
...
@@ -57,7 +57,7 @@ const StatisticsView = observer(() => {
)
}
)
}
<
StatCard
<
StatCard
icon=
{
<
LinkIcon
className=
"w-
4 h-auto mr-1
"
/>
}
icon=
{
<
LinkIcon
className=
"w-
3 h-auto mr-1 opacity-70
"
/>
}
label=
{
t
(
"memo.links"
)
}
label=
{
t
(
"memo.links"
)
}
count=
{
memoTypeStats
.
linkCount
}
count=
{
memoTypeStats
.
linkCount
}
onClick=
{
()
=>
handleFilterClick
(
"property.hasLink"
)
}
onClick=
{
()
=>
handleFilterClick
(
"property.hasLink"
)
}
...
@@ -65,7 +65,11 @@ const StatisticsView = observer(() => {
...
@@ -65,7 +65,11 @@ const StatisticsView = observer(() => {
<
StatCard
<
StatCard
icon=
{
icon=
{
memoTypeStats
.
undoCount
>
0
?
<
ListTodoIcon
className=
"w-4 h-auto mr-1"
/>
:
<
CheckCircleIcon
className=
"w-4 h-auto mr-1"
/>
memoTypeStats
.
undoCount
>
0
?
(
<
ListTodoIcon
className=
"w-3 h-auto mr-1 opacity-70"
/>
)
:
(
<
CheckCircleIcon
className=
"w-3 h-auto mr-1 opacity-70"
/>
)
}
}
label=
{
t
(
"memo.to-do"
)
}
label=
{
t
(
"memo.to-do"
)
}
count=
{
count=
{
...
@@ -84,7 +88,7 @@ const StatisticsView = observer(() => {
...
@@ -84,7 +88,7 @@ const StatisticsView = observer(() => {
/>
/>
<
StatCard
<
StatCard
icon=
{
<
Code2Icon
className=
"w-
4 h-auto mr-1
"
/>
}
icon=
{
<
Code2Icon
className=
"w-
3 h-auto mr-1 opacity-70
"
/>
}
label=
{
t
(
"memo.code"
)
}
label=
{
t
(
"memo.code"
)
}
count=
{
memoTypeStats
.
codeCount
}
count=
{
memoTypeStats
.
codeCount
}
onClick=
{
()
=>
handleFilterClick
(
"property.hasCode"
)
}
onClick=
{
()
=>
handleFilterClick
(
"property.hasCode"
)
}
...
...
web/src/components/UserBanner.tsx
View file @
3343dc73
import
{
Dropdown
,
Menu
,
MenuButton
,
MenuItem
}
from
"@mui/joy"
;
import
{
Dropdown
,
Menu
,
MenuButton
,
MenuItem
}
from
"@mui/joy"
;
import
{
ArchiveIcon
,
LogOutIcon
,
User2Icon
,
SquareUserIcon
}
from
"lucide-react"
;
import
{
ArchiveIcon
,
LogOutIcon
,
User2Icon
,
SquareUserIcon
,
SettingsIcon
,
BellIcon
}
from
"lucide-react"
;
import
{
authServiceClient
}
from
"@/grpcweb"
;
import
{
authServiceClient
}
from
"@/grpcweb"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
...
@@ -45,7 +45,7 @@ const UserBanner = (props: Props) => {
...
@@ -45,7 +45,7 @@ const UserBanner = (props: Props) => {
)
}
)
}
</
div
>
</
div
>
</
MenuButton
>
</
MenuButton
>
<
Menu
placement=
"bottom-start"
style=
{
{
zIndex
:
"9999"
}
}
>
<
Menu
size=
"sm"
placement=
"bottom-start"
style=
{
{
zIndex
:
"9999"
}
}
>
<
MenuItem
onClick=
{
()
=>
navigateTo
(
`/u/${encodeURIComponent(currentUser.username)}`
)
}
>
<
MenuItem
onClick=
{
()
=>
navigateTo
(
`/u/${encodeURIComponent(currentUser.username)}`
)
}
>
<
SquareUserIcon
className=
"w-4 h-auto opacity-60"
/>
<
SquareUserIcon
className=
"w-4 h-auto opacity-60"
/>
<
span
className=
"truncate"
>
{
t
(
"common.profile"
)
}
</
span
>
<
span
className=
"truncate"
>
{
t
(
"common.profile"
)
}
</
span
>
...
@@ -54,6 +54,14 @@ const UserBanner = (props: Props) => {
...
@@ -54,6 +54,14 @@ const UserBanner = (props: Props) => {
<
ArchiveIcon
className=
"w-4 h-auto opacity-60"
/>
<
ArchiveIcon
className=
"w-4 h-auto opacity-60"
/>
<
span
className=
"truncate"
>
{
t
(
"common.archived"
)
}
</
span
>
<
span
className=
"truncate"
>
{
t
(
"common.archived"
)
}
</
span
>
</
MenuItem
>
</
MenuItem
>
<
MenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
INBOX
)
}
>
<
BellIcon
className=
"w-4 h-auto opacity-60"
/>
<
span
className=
"truncate"
>
{
t
(
"common.inbox"
)
}
</
span
>
</
MenuItem
>
<
MenuItem
onClick=
{
()
=>
navigateTo
(
Routes
.
SETTING
)
}
>
<
SettingsIcon
className=
"w-4 h-auto opacity-60"
/>
<
span
className=
"truncate"
>
{
t
(
"common.settings"
)
}
</
span
>
</
MenuItem
>
<
MenuItem
onClick=
{
handleSignOut
}
>
<
MenuItem
onClick=
{
handleSignOut
}
>
<
LogOutIcon
className=
"w-4 h-auto opacity-60"
/>
<
LogOutIcon
className=
"w-4 h-auto opacity-60"
/>
<
span
className=
"truncate"
>
{
t
(
"common.sign-out"
)
}
</
span
>
<
span
className=
"truncate"
>
{
t
(
"common.sign-out"
)
}
</
span
>
...
...
web/src/pages/Explore.tsx
View file @
3343dc73
import
dayjs
from
"dayjs"
;
import
dayjs
from
"dayjs"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useMemo
}
from
"react"
;
import
MemoView
from
"@/components/MemoView"
;
import
MemoView
from
"@/components/MemoView"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
PagedMemoList
from
"@/components/PagedMemoList"
;
import
PagedMemoList
from
"@/components/PagedMemoList"
;
import
use
CurrentUser
from
"@/hooks/useCurrentUser
"
;
import
use
ResponsiveWidth
from
"@/hooks/useResponsiveWidth
"
;
import
{
viewStore
}
from
"@/store/v2"
;
import
{
viewStore
}
from
"@/store/v2"
;
import
memoFilterStore
from
"@/store/v2/memoFilter"
;
import
{
Direction
,
State
}
from
"@/types/proto/api/v1/common"
;
import
{
Direction
,
State
}
from
"@/types/proto/api/v1/common"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
const
Explore
=
observer
(()
=>
{
const
Explore
=
observer
(()
=>
{
const
user
=
useCurrentUser
();
const
{
md
}
=
useResponsiveWidth
();
const
memoListFilter
=
useMemo
(()
=>
{
const
conditions
=
[];
const
contentSearch
:
string
[]
=
[];
const
tagSearch
:
string
[]
=
[];
for
(
const
filter
of
memoFilterStore
.
filters
)
{
if
(
filter
.
factor
===
"contentSearch"
)
{
contentSearch
.
push
(
`"
${
filter
.
value
}
"`
);
}
else
if
(
filter
.
factor
===
"tagSearch"
)
{
tagSearch
.
push
(
`"
${
filter
.
value
}
"`
);
}
else
if
(
filter
.
factor
===
"property.hasLink"
)
{
conditions
.
push
(
`has_link == true`
);
}
else
if
(
filter
.
factor
===
"property.hasTaskList"
)
{
conditions
.
push
(
`has_task_list == true`
);
}
else
if
(
filter
.
factor
===
"property.hasCode"
)
{
conditions
.
push
(
`has_code == true`
);
}
else
if
(
filter
.
factor
===
"displayTime"
)
{
const
filterDate
=
new
Date
(
filter
.
value
);
const
filterUtcTimestamp
=
filterDate
.
getTime
()
+
filterDate
.
getTimezoneOffset
()
*
60
*
1000
;
const
timestampAfter
=
filterUtcTimestamp
/
1000
;
conditions
.
push
(
`display_time_after ==
${
timestampAfter
}
`
);
conditions
.
push
(
`display_time_before ==
${
timestampAfter
+
60
*
60
*
24
}
`
);
}
}
if
(
contentSearch
.
length
>
0
)
{
conditions
.
push
(
`content_search == [
${
contentSearch
.
join
(
", "
)}
]`
);
}
if
(
tagSearch
.
length
>
0
)
{
conditions
.
push
(
`tag_search == [
${
tagSearch
.
join
(
", "
)}
]`
);
}
return
conditions
.
join
(
" && "
);
},
[
user
,
memoFilterStore
.
filters
,
viewStore
.
state
.
orderByTimeAsc
]);
return
(
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"
>
{
!
md
&&
<
MobileHeader
/>
}
<
div
className=
"w-full px-4 sm:px-6"
>
<
PagedMemoList
<
PagedMemoList
renderer=
{
(
memo
:
Memo
)
=>
<
MemoView
key=
{
`${memo.name}-${memo.updateTime}`
}
memo=
{
memo
}
showCreator
showVisibility
compact
/>
}
renderer=
{
(
memo
:
Memo
)
=>
<
MemoView
key=
{
`${memo.name}-${memo.updateTime}`
}
memo=
{
memo
}
showCreator
showVisibility
compact
/>
}
listSort=
{
(
memos
:
Memo
[])
=>
listSort=
{
(
memos
:
Memo
[])
=>
...
@@ -57,8 +27,9 @@ const Explore = observer(() => {
...
@@ -57,8 +27,9 @@ const Explore = observer(() => {
)
)
}
}
direction=
{
viewStore
.
state
.
orderByTimeAsc
?
Direction
.
ASC
:
Direction
.
DESC
}
direction=
{
viewStore
.
state
.
orderByTimeAsc
?
Direction
.
ASC
:
Direction
.
DESC
}
oldFilter=
{
memoListFilter
}
/>
/>
</
div
>
</
section
>
);
);
});
});
...
...
web/src/router/index.tsx
View file @
3343dc73
...
@@ -84,14 +84,6 @@ const router = createBrowserRouter([
...
@@ -84,14 +84,6 @@ const router = createBrowserRouter([
path
:
""
,
path
:
""
,
element
:
<
Home
/>,
element
:
<
Home
/>,
},
},
{
path
:
Routes
.
EXPLORE
,
element
:
(
<
Suspense
fallback=
{
<
Loading
/>
}
>
<
Explore
/>
</
Suspense
>
),
},
{
{
path
:
Routes
.
ARCHIVED
,
path
:
Routes
.
ARCHIVED
,
element
:
(
element
:
(
...
@@ -110,6 +102,14 @@ const router = createBrowserRouter([
...
@@ -110,6 +102,14 @@ const router = createBrowserRouter([
},
},
],
],
},
},
{
path
:
Routes
.
EXPLORE
,
element
:
(
<
Suspense
fallback=
{
<
Loading
/>
}
>
<
Explore
/>
</
Suspense
>
),
},
{
{
path
:
Routes
.
RESOURCES
,
path
:
Routes
.
RESOURCES
,
element
:
(
element
:
(
...
...
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