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
5a2f18da
Commit
5a2f18da
authored
Feb 09, 2025
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor(frontend): retire redux
parent
13f6fa7b
Changes
38
Hide whitespace changes
Inline
Side-by-side
Showing
38 changed files
with
482 additions
and
492 deletions
+482
-492
apidocs.swagger.yaml
proto/gen/apidocs.swagger.yaml
+90
-30
package.json
web/package.json
+3
-3
pnpm-lock.yaml
web/pnpm-lock.yaml
+34
-73
App.tsx
web/src/App.tsx
+15
-24
ActivityCalendar.tsx
web/src/components/ActivityCalendar.tsx
+2
-2
ChangeMemberPasswordDialog.tsx
web/src/components/ChangeMemberPasswordDialog.tsx
+1
-2
CreateShortcutDialog.tsx
web/src/components/CreateShortcutDialog.tsx
+1
-2
BaseDialog.tsx
web/src/components/Dialog/BaseDialog.tsx
+15
-22
ShortcutsSection.tsx
web/src/components/HomeSidebar/ShortcutsSection.tsx
+6
-5
MemoCommentMessage.tsx
web/src/components/Inbox/MemoCommentMessage.tsx
+3
-4
VersionUpdateMessage.tsx
web/src/components/Inbox/VersionUpdateMessage.tsx
+3
-3
index.tsx
web/src/components/MemoEditor/index.tsx
+6
-5
Navigation.tsx
web/src/components/Navigation.tsx
+6
-17
PasswordSignInForm.tsx
web/src/components/PasswordSignInForm.tsx
+6
-6
PreferencesSection.tsx
web/src/components/Settings/PreferencesSection.tsx
+7
-9
StatisticsView.tsx
web/src/components/StatisticsView.tsx
+4
-4
polyfill.ts
web/src/helpers/polyfill.ts
+0
-15
useCurrentUser.ts
web/src/hooks/useCurrentUser.ts
+2
-3
CommonContextProvider.tsx
web/src/layouts/CommonContextProvider.tsx
+0
-84
main.tsx
web/src/main.tsx
+14
-14
AdminSignIn.tsx
web/src/pages/AdminSignIn.tsx
+8
-8
Home.tsx
web/src/pages/Home.tsx
+6
-5
Inboxes.tsx
web/src/pages/Inboxes.tsx
+6
-6
Setting.tsx
web/src/pages/Setting.tsx
+5
-5
SignIn.tsx
web/src/pages/SignIn.tsx
+8
-8
SignUp.tsx
web/src/pages/SignUp.tsx
+9
-9
index.ts
web/src/store/index.ts
+0
-17
dialog.ts
web/src/store/module/dialog.ts
+0
-26
index.ts
web/src/store/module/index.ts
+0
-1
dialog.ts
web/src/store/reducer/dialog.ts
+0
-37
inbox.ts
web/src/store/v1/inbox.ts
+0
-31
index.ts
web/src/store/v1/index.ts
+0
-1
user.ts
web/src/store/v1/user.ts
+1
-11
dialog.ts
web/src/store/v2/dialog.ts
+32
-0
index.ts
web/src/store/v2/index.ts
+4
-0
user.ts
web/src/store/v2/user.ts
+112
-0
workspace.ts
web/src/store/v2/workspace.ts
+68
-0
i18n.ts
web/src/utils/i18n.ts
+5
-0
No files found.
proto/gen/apidocs.swagger.yaml
View file @
5a2f18da
...
@@ -305,7 +305,9 @@ paths:
...
@@ -305,7 +305,9 @@ paths:
$ref
:
'
#/definitions/googlerpcStatus'
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
parameters
:
-
name
:
parent
-
name
:
parent
description
:
"
The
parent
is
the
owner
of
the
memos.
\r\n
If
not
specified
or
`users/-`,
it
will
list
all
memos."
description
:
|-
The parent is the owner of the memos.
If not specified or `users/-`, it will list all memos.
in: query
in: query
required: false
required: false
type: string
type: string
...
@@ -316,12 +318,16 @@ paths:
...
@@ -316,12 +318,16 @@ paths:
type
:
integer
type
:
integer
format
:
int32
format
:
int32
-
name
:
pageToken
-
name
:
pageToken
description
:
"
A
page
token,
received
from
a
previous
`ListMemos`
call.
\r\n
Provide
this
to
retrieve
the
subsequent
page."
description
:
|-
A page token, received from a previous `ListMemos` call.
Provide this to retrieve the subsequent page.
in: query
in: query
required: false
required: false
type: string
type: string
-
name
:
state
-
name
:
state
description
:
"
The
state
of
the
memos
to
list.
\r\n
Default
to
`NORMAL`.
Set
to
`ARCHIVED`
to
list
archived
memos."
description
:
|-
The state of the memos to list.
Default to `NORMAL`. Set to `ARCHIVED` to list archived memos.
in: query
in: query
required: false
required: false
type: string
type: string
...
@@ -331,12 +337,16 @@ paths:
...
@@ -331,12 +337,16 @@ paths:
- ARCHIVED
- ARCHIVED
default: STATE_UNSPECIFIED
default: STATE_UNSPECIFIED
-
name
:
sort
-
name
:
sort
description
:
"
What
field
to
sort
the
results
by.
\r\n
Default
to
display_time."
description
:
|-
What field to sort the results by.
Default to display_time.
in: query
in: query
required: false
required: false
type: string
type: string
-
name
:
direction
-
name
:
direction
description
:
"
The
direction
to
sort
the
results
by.
\r\n
Default
to
DESC."
description
:
|-
The direction to sort the results by.
Default to DESC.
in: query
in: query
required: false
required: false
type: string
type: string
...
@@ -346,12 +356,16 @@ paths:
...
@@ -346,12 +356,16 @@ paths:
- DESC
- DESC
default: DIRECTION_UNSPECIFIED
default: DIRECTION_UNSPECIFIED
-
name
:
filter
-
name
:
filter
description
:
"
Filter
is
a
CEL
expression
to
filter
memos.
\r\n
Refer
to
`Shortcut.filter`."
description
:
|-
Filter is a CEL expression to filter memos.
Refer to `Shortcut.filter`.
in: query
in: query
required: false
required: false
type: string
type: string
-
name
:
oldFilter
-
name
:
oldFilter
description
:
"
[Deprecated]
Old
filter
contains
some
specific
conditions
to
filter
memos.
\r\n
Format:
\"
creator
==
'users/{user}'
&&
visibilities
==
['PUBLIC',
'PROTECTED']
\"
"
description
:
|-
[Deprecated] Old filter contains some specific conditions to filter memos.
Format: "creator == 'users/{user}' && visibilities == ['PUBLIC', 'PROTECTED']"
in: query
in: query
required: false
required: false
type: string
type: string
...
@@ -396,7 +410,9 @@ paths:
...
@@ -396,7 +410,9 @@ paths:
$ref
:
'
#/definitions/googlerpcStatus'
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
parameters
:
-
name
:
id
-
name
:
id
description
:
"
The
id
of
the
reaction.
\r\n
Refer
to
the
`Reaction.id`."
description
:
|-
The id of the reaction.
Refer to the `Reaction.id`.
in: path
in: path
required: true
required: true
type: integer
type: integer
...
@@ -810,13 +826,17 @@ paths:
...
@@ -810,13 +826,17 @@ paths:
$ref
:
'
#/definitions/googlerpcStatus'
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
parameters
:
-
name
:
memo.name
-
name
:
memo.name
description
:
"
The
name
of
the
memo.
\r\n
Format:
memos/{memo},
memo
is
the
user
defined
id
or
uuid."
description
:
|-
The name of the memo.
Format: memos/{memo}, memo is the user defined id or uuid.
in: path
in: path
required: true
required: true
type: string
type: string
pattern: memos/[^/]+
pattern: memos/[^/]+
-
name
:
memo
-
name
:
memo
description
:
"
The
memo
to
update.
\r\n
The
`name`
field
is
required."
description
:
|-
The memo to update.
The `name` field is required.
in: body
in: body
required: true
required: true
schema:
schema:
...
@@ -826,7 +846,9 @@ paths:
...
@@ -826,7 +846,9 @@ paths:
$ref: '#/definitions/v1State'
$ref: '#/definitions/v1State'
creator:
creator:
type: string
type: string
title
:
"
The
name
of
the
creator.
\r\n
Format:
users/{user}"
title: |-
The name of the creator.
Format: users/{user}
createTime:
createTime:
type: string
type: string
format: date-time
format: date-time
...
@@ -874,7 +896,9 @@ paths:
...
@@ -874,7 +896,9 @@ paths:
readOnly: true
readOnly: true
parent:
parent:
type: string
type: string
title
:
"
The
name
of
the
parent
memo.
\r\n
Format:
memos/{id}"
title: |-
The name of the parent memo.
Format: memos/{id}
readOnly: true
readOnly: true
snippet:
snippet:
type: string
type: string
...
@@ -883,7 +907,9 @@ paths:
...
@@ -883,7 +907,9 @@ paths:
location:
location:
$ref: '#/definitions/apiv1Location'
$ref: '#/definitions/apiv1Location'
description: The location of the memo.
description: The location of the memo.
title
:
"
The
memo
to
update.
\r\n
The
`name`
field
is
required."
title: |-
The memo to update.
The `name` field is required.
required:
required:
- memo
- memo
tags
:
tags
:
...
@@ -1440,7 +1466,9 @@ paths:
...
@@ -1440,7 +1466,9 @@ paths:
$ref
:
'
#/definitions/googlerpcStatus'
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
parameters
:
-
name
:
parent
-
name
:
parent
description
:
"
The
parent
is
the
owner
of
the
memos.
\r\n
If
not
specified
or
`users/-`,
it
will
list
all
memos."
description
:
|-
The parent is the owner of the memos.
If not specified or `users/-`, it will list all memos.
in: path
in: path
required: true
required: true
type: string
type: string
...
@@ -1452,12 +1480,16 @@ paths:
...
@@ -1452,12 +1480,16 @@ paths:
type
:
integer
type
:
integer
format
:
int32
format
:
int32
-
name
:
pageToken
-
name
:
pageToken
description
:
"
A
page
token,
received
from
a
previous
`ListMemos`
call.
\r\n
Provide
this
to
retrieve
the
subsequent
page."
description
:
|-
A page token, received from a previous `ListMemos` call.
Provide this to retrieve the subsequent page.
in: query
in: query
required: false
required: false
type: string
type: string
-
name
:
state
-
name
:
state
description
:
"
The
state
of
the
memos
to
list.
\r\n
Default
to
`NORMAL`.
Set
to
`ARCHIVED`
to
list
archived
memos."
description
:
|-
The state of the memos to list.
Default to `NORMAL`. Set to `ARCHIVED` to list archived memos.
in: query
in: query
required: false
required: false
type: string
type: string
...
@@ -1467,12 +1499,16 @@ paths:
...
@@ -1467,12 +1499,16 @@ paths:
- ARCHIVED
- ARCHIVED
default: STATE_UNSPECIFIED
default: STATE_UNSPECIFIED
-
name
:
sort
-
name
:
sort
description
:
"
What
field
to
sort
the
results
by.
\r\n
Default
to
display_time."
description
:
|-
What field to sort the results by.
Default to display_time.
in: query
in: query
required: false
required: false
type: string
type: string
-
name
:
direction
-
name
:
direction
description
:
"
The
direction
to
sort
the
results
by.
\r\n
Default
to
DESC."
description
:
|-
The direction to sort the results by.
Default to DESC.
in: query
in: query
required: false
required: false
type: string
type: string
...
@@ -1482,12 +1518,16 @@ paths:
...
@@ -1482,12 +1518,16 @@ paths:
- DESC
- DESC
default: DIRECTION_UNSPECIFIED
default: DIRECTION_UNSPECIFIED
-
name
:
filter
-
name
:
filter
description
:
"
Filter
is
a
CEL
expression
to
filter
memos.
\r\n
Refer
to
`Shortcut.filter`."
description
:
|-
Filter is a CEL expression to filter memos.
Refer to `Shortcut.filter`.
in: query
in: query
required: false
required: false
type: string
type: string
-
name
:
oldFilter
-
name
:
oldFilter
description
:
"
[Deprecated]
Old
filter
contains
some
specific
conditions
to
filter
memos.
\r\n
Format:
\"
creator
==
'users/{user}'
&&
visibilities
==
['PUBLIC',
'PROTECTED']
\"
"
description
:
|-
[Deprecated] Old filter contains some specific conditions to filter memos.
Format: "creator == 'users/{user}' && visibilities == ['PUBLIC', 'PROTECTED']"
in: query
in: query
required: false
required: false
type: string
type: string
...
@@ -1625,7 +1665,9 @@ paths:
...
@@ -1625,7 +1665,9 @@ paths:
$ref
:
'
#/definitions/googlerpcStatus'
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
parameters
:
-
name
:
parent
-
name
:
parent
description
:
"
The
parent,
who
owns
the
tags.
\r\n
Format:
memos/{id}.
Use
\"
memos/-
\"
to
delete
all
tags."
description
:
|-
The parent, who owns the tags.
Format: memos/{id}. Use "memos/-" to delete all tags.
in: path
in: path
required: true
required: true
type: string
type: string
...
@@ -1656,7 +1698,9 @@ paths:
...
@@ -1656,7 +1698,9 @@ paths:
$ref
:
'
#/definitions/googlerpcStatus'
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
parameters
:
-
name
:
parent
-
name
:
parent
description
:
"
The
parent,
who
owns
the
tags.
\r\n
Format:
memos/{id}.
Use
\"
memos/-
\"
to
rename
all
tags."
description
:
|-
The parent, who owns the tags.
Format: memos/{id}. Use "memos/-" to rename all tags.
in: path
in: path
required: true
required: true
type: string
type: string
...
@@ -1771,7 +1815,9 @@ paths:
...
@@ -1771,7 +1815,9 @@ paths:
$ref
:
'
#/definitions/googlerpcStatus'
$ref
:
'
#/definitions/googlerpcStatus'
parameters
:
parameters
:
-
name
:
user.name
-
name
:
user.name
description
:
"
The
name
of
the
user.
\r\n
Format:
users/{id},
id
is
the
system
generated
auto-incremented
id."
description
:
|-
The name of the user.
Format: users/{id}, id is the system generated auto-incremented id.
in: path
in: path
required: true
required: true
type: string
type: string
...
@@ -2106,13 +2152,17 @@ definitions:
...
@@ -2106,13 +2152,17 @@ definitions:
properties
:
properties
:
name
:
name
:
type
:
string
type
:
string
description
:
"
The
name
of
the
memo.
\r\n
Format:
memos/{memo},
memo
is
the
user
defined
id
or
uuid."
description
:
|-
The name of the memo.
Format: memos/{memo}, memo is the user defined id or uuid.
readOnly: true
readOnly: true
state
:
state
:
$ref
:
'
#/definitions/v1State'
$ref
:
'
#/definitions/v1State'
creator
:
creator
:
type
:
string
type
:
string
title
:
"
The
name
of
the
creator.
\r\n
Format:
users/{user}"
title
:
|-
The name of the creator.
Format: users/{user}
createTime
:
createTime
:
type
:
string
type
:
string
format
:
date-time
format
:
date-time
...
@@ -2160,7 +2210,9 @@ definitions:
...
@@ -2160,7 +2210,9 @@ definitions:
readOnly
:
true
readOnly
:
true
parent
:
parent
:
type
:
string
type
:
string
title
:
"
The
name
of
the
parent
memo.
\r\n
Format:
memos/{id}"
title
:
|-
The name of the parent memo.
Format: memos/{id}
readOnly: true
readOnly: true
snippet
:
snippet
:
type
:
string
type
:
string
...
@@ -2738,7 +2790,9 @@ definitions:
...
@@ -2738,7 +2790,9 @@ definitions:
$ref
:
'
#/definitions/apiv1Memo'
$ref
:
'
#/definitions/apiv1Memo'
nextPageToken
:
nextPageToken
:
type
:
string
type
:
string
description
:
"
A
token,
which
can
be
sent
as
`page_token`
to
retrieve
the
next
page.
\r\n
If
this
field
is
omitted,
there
are
no
subsequent
pages."
description
:
|-
A token, which can be sent as `page_token` to retrieve the next page.
If this field is omitted, there are no subsequent pages.
v1ListNode
:
v1ListNode
:
type
:
object
type
:
object
properties
:
properties
:
...
@@ -3156,7 +3210,9 @@ definitions:
...
@@ -3156,7 +3210,9 @@ definitions:
properties
:
properties
:
name
:
name
:
type
:
string
type
:
string
description
:
"
The
name
of
the
user.
\r\n
Format:
users/{id},
id
is
the
system
generated
auto-incremented
id."
description
:
|-
The name of the user.
Format: users/{id}, id is the system generated auto-incremented id.
role
:
role
:
$ref
:
'
#/definitions/UserRole'
$ref
:
'
#/definitions/UserRole'
username
:
username
:
...
@@ -3205,7 +3261,9 @@ definitions:
...
@@ -3205,7 +3261,9 @@ definitions:
items
:
items
:
type
:
string
type
:
string
format
:
date-time
format
:
date-time
description
:
"
The
timestamps
when
the
memos
were
displayed.
\r\n
We
should
return
raw
data
to
the
client,
and
let
the
client
format
the
data
with
the
user's
timezone."
description
:
|-
The timestamps when the memos were displayed.
We should return raw data to the client, and let the client format the data with the user's timezone.
memoTypeStats
:
memoTypeStats
:
$ref
:
'
#/definitions/UserStatsMemoTypeStats'
$ref
:
'
#/definitions/UserStatsMemoTypeStats'
description
:
The stats of memo types.
description
:
The stats of memo types.
...
@@ -3214,7 +3272,9 @@ definitions:
...
@@ -3214,7 +3272,9 @@ definitions:
additionalProperties
:
additionalProperties
:
type
:
integer
type
:
integer
format
:
int32
format
:
int32
title
:
"
The
count
of
tags.
\r\n
Format:
\"
tag1
\"
:
1,
\"
tag2
\"
:
2"
title
:
|-
The count of tags.
Format: "tag1": 1, "tag2": 2
v1Visibility
:
v1Visibility
:
type
:
string
type
:
string
enum
:
enum
:
...
...
web/package.json
View file @
5a2f18da
...
@@ -18,7 +18,6 @@
...
@@ -18,7 +18,6 @@
"@matejmazur/react-katex"
:
"^3.1.3"
,
"@matejmazur/react-katex"
:
"^3.1.3"
,
"@mui/joy"
:
"5.0.0-beta.51"
,
"@mui/joy"
:
"5.0.0-beta.51"
,
"@radix-ui/react-popover"
:
"^1.1.5"
,
"@radix-ui/react-popover"
:
"^1.1.5"
,
"@reduxjs/toolkit"
:
"^2.5.0"
,
"@usememos/mui"
:
"0.0.1-alpha.26"
,
"@usememos/mui"
:
"0.0.1-alpha.26"
,
"class-variance-authority"
:
"^0.7.1"
,
"class-variance-authority"
:
"^0.7.1"
,
"clsx"
:
"^2.1.1"
,
"clsx"
:
"^2.1.1"
,
...
@@ -32,6 +31,8 @@
...
@@ -32,6 +31,8 @@
"lodash-es"
:
"^4.17.21"
,
"lodash-es"
:
"^4.17.21"
,
"lucide-react"
:
"^0.453.0"
,
"lucide-react"
:
"^0.453.0"
,
"mermaid"
:
"^11.4.1"
,
"mermaid"
:
"^11.4.1"
,
"mobx"
:
"^6.13.6"
,
"mobx-react-lite"
:
"^4.1.0"
,
"react"
:
"^18.3.1"
,
"react"
:
"^18.3.1"
,
"react-datepicker"
:
"^7.5.0"
,
"react-datepicker"
:
"^7.5.0"
,
"react-dom"
:
"^18.3.1"
,
"react-dom"
:
"^18.3.1"
,
...
@@ -39,7 +40,6 @@
...
@@ -39,7 +40,6 @@
"react-hot-toast"
:
"^2.5.1"
,
"react-hot-toast"
:
"^2.5.1"
,
"react-i18next"
:
"^15.4.0"
,
"react-i18next"
:
"^15.4.0"
,
"react-leaflet"
:
"^4.2.1"
,
"react-leaflet"
:
"^4.2.1"
,
"react-redux"
:
"^9.2.0"
,
"react-router-dom"
:
"^7.1.1"
,
"react-router-dom"
:
"^7.1.1"
,
"react-simple-pull-to-refresh"
:
"^1.3.3"
,
"react-simple-pull-to-refresh"
:
"^1.3.3"
,
"react-use"
:
"^17.6.0"
,
"react-use"
:
"^17.6.0"
,
...
@@ -82,4 +82,4 @@
...
@@ -82,4 +82,4 @@
"typescript"
:
"^5.7.3"
,
"typescript"
:
"^5.7.3"
,
"vite"
:
"^6.0.6"
"vite"
:
"^6.0.6"
}
}
}
}
\ No newline at end of file
web/pnpm-lock.yaml
View file @
5a2f18da
...
@@ -35,9 +35,6 @@ importers:
...
@@ -35,9 +35,6 @@ importers:
'
@radix-ui/react-popover'
:
'
@radix-ui/react-popover'
:
specifier
:
^1.1.5
specifier
:
^1.1.5
version
:
1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
1.1.5(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'
@reduxjs/toolkit'
:
specifier
:
^2.5.0
version
:
2.5.0(react-redux@9.2.0(@types/react@18.3.18)(react@18.3.1)(redux@5.0.1))(react@18.3.1)
'
@usememos/mui'
:
'
@usememos/mui'
:
specifier
:
0.0.1-alpha.26
specifier
:
0.0.1-alpha.26
version
:
0.0.1-alpha.26(lucide-react@0.453.0(react@18.3.1))(postcss@8.4.49)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.6.0)(tailwindcss@3.4.17)
version
:
0.0.1-alpha.26(lucide-react@0.453.0(react@18.3.1))(postcss@8.4.49)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.6.0)(tailwindcss@3.4.17)
...
@@ -77,6 +74,12 @@ importers:
...
@@ -77,6 +74,12 @@ importers:
mermaid
:
mermaid
:
specifier
:
^11.4.1
specifier
:
^11.4.1
version
:
11.4.1
version
:
11.4.1
mobx
:
specifier
:
^6.13.6
version
:
6.13.6
mobx-react-lite
:
specifier
:
^4.1.0
version
:
4.1.0(mobx@6.13.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react
:
react
:
specifier
:
^18.3.1
specifier
:
^18.3.1
version
:
18.3.1
version
:
18.3.1
...
@@ -98,9 +101,6 @@ importers:
...
@@ -98,9 +101,6 @@ importers:
react-leaflet
:
react-leaflet
:
specifier
:
^4.2.1
specifier
:
^4.2.1
version
:
4.2.1(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
4.2.1(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-redux
:
specifier
:
^9.2.0
version
:
9.2.0(@types/react@18.3.18)(react@18.3.1)(redux@5.0.1)
react-router-dom
:
react-router-dom
:
specifier
:
^7.1.1
specifier
:
^7.1.1
version
:
7.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version
:
7.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
...
@@ -1095,17 +1095,6 @@ packages:
...
@@ -1095,17 +1095,6 @@ packages:
react
:
^18.0.0
react
:
^18.0.0
react-dom
:
^18.0.0
react-dom
:
^18.0.0
'
@reduxjs/toolkit@2.5.0'
:
resolution
:
{
integrity
:
sha512-awNe2oTodsZ6LmRqmkFhtb/KH03hUhxOamEQy411m3Njj3BbFvoBovxo4Q1cBWnV1ErprVj9MlF0UPXkng0eyg==
}
peerDependencies
:
react
:
^16.9.0 || ^17.0.0 || ^18 || ^19
react-redux
:
^7.2.1 || ^8.1.3 || ^9.0.0
peerDependenciesMeta
:
react
:
optional
:
true
react-redux
:
optional
:
true
'
@rollup/rollup-android-arm-eabi@4.29.1'
:
'
@rollup/rollup-android-arm-eabi@4.29.1'
:
resolution
:
{
integrity
:
sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==
}
resolution
:
{
integrity
:
sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==
}
cpu
:
[
arm
]
cpu
:
[
arm
]
...
@@ -1368,9 +1357,6 @@ packages:
...
@@ -1368,9 +1357,6 @@ packages:
'
@types/trusted-types@2.0.7'
:
'
@types/trusted-types@2.0.7'
:
resolution
:
{
integrity
:
sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
}
resolution
:
{
integrity
:
sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
}
'
@types/use-sync-external-store@0.0.6'
:
resolution
:
{
integrity
:
sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
}
'
@types/uuid@10.0.0'
:
'
@types/uuid@10.0.0'
:
resolution
:
{
integrity
:
sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==
}
resolution
:
{
integrity
:
sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==
}
...
@@ -2678,6 +2664,22 @@ packages:
...
@@ -2678,6 +2664,22 @@ packages:
mlly@1.7.3
:
mlly@1.7.3
:
resolution
:
{
integrity
:
sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==
}
resolution
:
{
integrity
:
sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==
}
mobx-react-lite@4.1.0
:
resolution
:
{
integrity
:
sha512-QEP10dpHHBeQNv1pks3WnHRCem2Zp636lq54M2nKO2Sarr13pL4u6diQXf65yzXUn0mkk18SyIDCm9UOJYTi1w==
}
peerDependencies
:
mobx
:
^6.9.0
react
:
^16.8.0 || ^17 || ^18 || ^19
react-dom
:
'
*'
react-native
:
'
*'
peerDependenciesMeta
:
react-dom
:
optional
:
true
react-native
:
optional
:
true
mobx@6.13.6
:
resolution
:
{
integrity
:
sha512-r19KNV0uBN4b+ER8Z0gA4y+MzDYIQ2SvOmn3fUrqPnWXdQfakd9yfbPBDBF/p5I+bd3N5Rk1fHONIvMay+bJGA==
}
ms@2.1.3
:
ms@2.1.3
:
resolution
:
{
integrity
:
sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
}
resolution
:
{
integrity
:
sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
}
...
@@ -2981,18 +2983,6 @@ packages:
...
@@ -2981,18 +2983,6 @@ packages:
react
:
^18.0.0
react
:
^18.0.0
react-dom
:
^18.0.0
react-dom
:
^18.0.0
react-redux@9.2.0
:
resolution
:
{
integrity
:
sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==
}
peerDependencies
:
'
@types/react'
:
^18.2.25 || ^19
react
:
^18.0 || ^19
redux
:
^5.0.0
peerDependenciesMeta
:
'
@types/react'
:
optional
:
true
redux
:
optional
:
true
react-refresh@0.14.2
:
react-refresh@0.14.2
:
resolution
:
{
integrity
:
sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
}
resolution
:
{
integrity
:
sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
}
engines
:
{
node
:
'
>=0.10.0'
}
engines
:
{
node
:
'
>=0.10.0'
}
...
@@ -3073,14 +3063,6 @@ packages:
...
@@ -3073,14 +3063,6 @@ packages:
resolution
:
{
integrity
:
sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
}
resolution
:
{
integrity
:
sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
}
engines
:
{
node
:
'
>=8.10.0'
}
engines
:
{
node
:
'
>=8.10.0'
}
redux-thunk@3.1.0
:
resolution
:
{
integrity
:
sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==
}
peerDependencies
:
redux
:
^5.0.0
redux@5.0.1
:
resolution
:
{
integrity
:
sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==
}
reflect.getprototypeof@1.0.9
:
reflect.getprototypeof@1.0.9
:
resolution
:
{
integrity
:
sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==
}
resolution
:
{
integrity
:
sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==
}
engines
:
{
node
:
'
>=
0.4'
}
engines
:
{
node
:
'
>=
0.4'
}
...
@@ -3092,9 +3074,6 @@ packages:
...
@@ -3092,9 +3074,6 @@ packages:
resolution
:
{
integrity
:
sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==
}
resolution
:
{
integrity
:
sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==
}
engines
:
{
node
:
'
>=
0.4'
}
engines
:
{
node
:
'
>=
0.4'
}
reselect@5.1.1
:
resolution
:
{
integrity
:
sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==
}
resize-observer-polyfill@1.5.1
:
resize-observer-polyfill@1.5.1
:
resolution
:
{
integrity
:
sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
}
resolution
:
{
integrity
:
sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
}
...
@@ -4460,16 +4439,6 @@ snapshots:
...
@@ -4460,16 +4439,6 @@ snapshots:
react
:
18.3.1
react
:
18.3.1
react-dom
:
18.3.1(react@18.3.1)
react-dom
:
18.3.1(react@18.3.1)
'
@reduxjs/toolkit@2.5.0(react-redux@9.2.0(@types/react@18.3.18)(react@18.3.1)(redux@5.0.1))(react@18.3.1)'
:
dependencies
:
immer
:
10.1.1
redux
:
5.0.1
redux-thunk
:
3.1.0(redux@5.0.1)
reselect
:
5.1.1
optionalDependencies
:
react
:
18.3.1
react-redux
:
9.2.0(@types/react@18.3.18)(react@18.3.1)(redux@5.0.1)
'
@rollup/rollup-android-arm-eabi@4.29.1'
:
'
@rollup/rollup-android-arm-eabi@4.29.1'
:
optional
:
true
optional
:
true
...
@@ -4723,8 +4692,6 @@ snapshots:
...
@@ -4723,8 +4692,6 @@ snapshots:
'
@types/trusted-types@2.0.7'
:
'
@types/trusted-types@2.0.7'
:
optional
:
true
optional
:
true
'
@types/use-sync-external-store@0.0.6'
:
{}
'
@types/uuid@10.0.0'
:
{}
'
@types/uuid@10.0.0'
:
{}
'
@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)'
:
'
@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)'
:
...
@@ -5914,7 +5881,8 @@ snapshots:
...
@@ -5914,7 +5881,8 @@ snapshots:
image-size@0.5.5
:
image-size@0.5.5
:
optional
:
true
optional
:
true
immer@10.1.1
:
{}
immer@10.1.1
:
optional
:
true
import-fresh@3.3.0
:
import-fresh@3.3.0
:
dependencies
:
dependencies
:
...
@@ -6275,6 +6243,16 @@ snapshots:
...
@@ -6275,6 +6243,16 @@ snapshots:
pkg-types
:
1.3.0
pkg-types
:
1.3.0
ufo
:
1.5.4
ufo
:
1.5.4
mobx-react-lite@4.1.0(mobx@6.13.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
:
dependencies
:
mobx
:
6.13.6
react
:
18.3.1
use-sync-external-store
:
1.4.0(react@18.3.1)
optionalDependencies
:
react-dom
:
18.3.1(react@18.3.1)
mobx@6.13.6
:
{}
ms@2.1.3
:
{}
ms@2.1.3
:
{}
mz@2.7.0
:
mz@2.7.0
:
...
@@ -6586,15 +6564,6 @@ snapshots:
...
@@ -6586,15 +6564,6 @@ snapshots:
react
:
18.3.1
react
:
18.3.1
react-dom
:
18.3.1(react@18.3.1)
react-dom
:
18.3.1(react@18.3.1)
react-redux@9.2.0(@types/react@18.3.18)(react@18.3.1)(redux@5.0.1)
:
dependencies
:
'
@types/use-sync-external-store'
:
0.0.6
react
:
18.3.1
use-sync-external-store
:
1.4.0(react@18.3.1)
optionalDependencies
:
'
@types/react'
:
18.3.18
redux
:
5.0.1
react-refresh@0.14.2
:
{}
react-refresh@0.14.2
:
{}
react-remove-scroll-bar@2.3.8(@types/react@18.3.18)(react@18.3.1)
:
react-remove-scroll-bar@2.3.8(@types/react@18.3.18)(react@18.3.1)
:
...
@@ -6681,12 +6650,6 @@ snapshots:
...
@@ -6681,12 +6650,6 @@ snapshots:
dependencies
:
dependencies
:
picomatch
:
2.3.1
picomatch
:
2.3.1
redux-thunk@3.1.0(redux@5.0.1)
:
dependencies
:
redux
:
5.0.1
redux@5.0.1
:
{}
reflect.getprototypeof@1.0.9
:
reflect.getprototypeof@1.0.9
:
dependencies
:
dependencies
:
call-bind
:
1.0.8
call-bind
:
1.0.8
...
@@ -6707,8 +6670,6 @@ snapshots:
...
@@ -6707,8 +6670,6 @@ snapshots:
es-errors
:
1.3.0
es-errors
:
1.3.0
set-function-name
:
2.0.2
set-function-name
:
2.0.2
reselect@5.1.1
:
{}
resize-observer-polyfill@1.5.1
:
{}
resize-observer-polyfill@1.5.1
:
{}
resolve-from@4.0.0
:
{}
resolve-from@4.0.0
:
{}
...
...
web/src/App.tsx
View file @
5a2f18da
import
{
useColorScheme
}
from
"@mui/joy"
;
import
{
useColorScheme
}
from
"@mui/joy"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useEffect
}
from
"react"
;
import
{
useEffect
}
from
"react"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
useTranslation
}
from
"react-i18next"
;
import
{
Outlet
}
from
"react-router-dom"
;
import
{
Outlet
}
from
"react-router-dom"
;
import
useLocalStorage
from
"react-use/lib/useLocalStorage"
;
import
{
getSystemColorScheme
}
from
"./helpers/utils"
;
import
{
getSystemColorScheme
}
from
"./helpers/utils"
;
import
useNavigateTo
from
"./hooks/useNavigateTo"
;
import
useNavigateTo
from
"./hooks/useNavigateTo"
;
import
{
useCommonContext
}
from
"./layouts/CommonContextProvider"
;
import
{
userStore
,
workspaceStore
}
from
"./store/v2"
;
import
{
useUserStore
,
useWorkspaceSettingStore
}
from
"./store/v1"
;
import
{
WorkspaceGeneralSetting
,
WorkspaceSettingKey
}
from
"./types/proto/store/workspace_setting"
;
const
App
=
()
=>
{
const
App
=
observer
(
()
=>
{
const
{
i18n
}
=
useTranslation
();
const
{
i18n
}
=
useTranslation
();
const
navigateTo
=
useNavigateTo
();
const
navigateTo
=
useNavigateTo
();
const
{
mode
,
setMode
}
=
useColorScheme
();
const
{
mode
,
setMode
}
=
useColorScheme
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
workspaceProfile
=
workspaceStore
.
state
.
profile
;
const
userStore
=
useUserStore
();
const
userSetting
=
userStore
.
state
.
userSetting
;
const
commonContext
=
useCommonContext
();
const
workspaceGeneralSetting
=
workspaceStore
.
generalSetting
;
const
[,
setLocale
]
=
useLocalStorage
(
"locale"
,
"en"
);
const
[,
setAppearance
]
=
useLocalStorage
(
"appearance"
,
"system"
);
const
workspaceProfile
=
commonContext
.
profile
;
const
userSetting
=
userStore
.
userSetting
;
const
workspaceGeneralSetting
=
workspaceSettingStore
.
getWorkspaceSettingByKey
(
WorkspaceSettingKey
.
GENERAL
).
generalSetting
||
WorkspaceGeneralSetting
.
fromPartial
({});
// Redirect to sign up page if no instance owner.
// Redirect to sign up page if no instance owner.
useEffect
(()
=>
{
useEffect
(()
=>
{
...
@@ -78,7 +69,7 @@ const App = () => {
...
@@ -78,7 +69,7 @@ const App = () => {
},
[
workspaceGeneralSetting
.
customProfile
]);
},
[
workspaceGeneralSetting
.
customProfile
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
const
currentLocale
=
commonContext
.
locale
;
const
currentLocale
=
workspaceStore
.
state
.
locale
;
i18n
.
changeLanguage
(
currentLocale
);
i18n
.
changeLanguage
(
currentLocale
);
document
.
documentElement
.
setAttribute
(
"lang"
,
currentLocale
);
document
.
documentElement
.
setAttribute
(
"lang"
,
currentLocale
);
if
([
"ar"
,
"fa"
].
includes
(
currentLocale
))
{
if
([
"ar"
,
"fa"
].
includes
(
currentLocale
))
{
...
@@ -86,17 +77,15 @@ const App = () => {
...
@@ -86,17 +77,15 @@ const App = () => {
}
else
{
}
else
{
document
.
documentElement
.
setAttribute
(
"dir"
,
"ltr"
);
document
.
documentElement
.
setAttribute
(
"dir"
,
"ltr"
);
}
}
setLocale
(
currentLocale
);
},
[
workspaceStore
.
state
.
locale
]);
},
[
commonContext
.
locale
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
let
currentAppearance
=
commonContext
.
appearance
as
Appearance
;
let
currentAppearance
=
workspaceStore
.
state
.
appearance
as
Appearance
;
if
(
currentAppearance
===
"system"
)
{
if
(
currentAppearance
===
"system"
)
{
currentAppearance
=
getSystemColorScheme
();
currentAppearance
=
getSystemColorScheme
();
}
}
setMode
(
currentAppearance
);
setMode
(
currentAppearance
);
setAppearance
(
currentAppearance
);
},
[
workspaceStore
.
state
.
appearance
]);
},
[
commonContext
.
appearance
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
const
root
=
document
.
documentElement
;
const
root
=
document
.
documentElement
;
...
@@ -112,11 +101,13 @@ const App = () => {
...
@@ -112,11 +101,13 @@ const App = () => {
return
;
return
;
}
}
commonContext
.
setLocale
(
userSetting
.
locale
);
workspaceStore
.
setPartial
({
commonContext
.
setAppearance
(
userSetting
.
appearance
);
locale
:
userSetting
.
locale
||
workspaceStore
.
state
.
locale
,
appearance
:
userSetting
.
appearance
||
workspaceStore
.
state
.
appearance
,
});
},
[
userSetting
?.
locale
,
userSetting
?.
appearance
]);
},
[
userSetting
?.
locale
,
userSetting
?.
appearance
]);
return
<
Outlet
/>;
return
<
Outlet
/>;
};
}
)
;
export
default
App
;
export
default
App
;
web/src/components/ActivityCalendar.tsx
View file @
5a2f18da
...
@@ -78,7 +78,7 @@ const ActivityCalendar = (props: Props) => {
...
@@ -78,7 +78,7 @@ const ActivityCalendar = (props: Props) => {
return
(
return
(
<
div
<
div
key=
{
`${date}-${index}`
}
key=
{
`${date}-${index}`
}
className=
{
cn
(
"w-6 h-6 text-xs flex justify-center items-center cursor-default"
,
"opacity-60 text-gray-400"
)
}
className=
{
cn
(
"w-6 h-6 text-xs
lg:text-[13px]
flex justify-center items-center cursor-default"
,
"opacity-60 text-gray-400"
)
}
>
>
{
item
.
day
}
{
item
.
day
}
</
div
>
</
div
>
...
@@ -101,7 +101,7 @@ const ActivityCalendar = (props: Props) => {
...
@@ -101,7 +101,7 @@ const ActivityCalendar = (props: Props) => {
<
Tooltip
className=
"shrink-0"
key=
{
`${date}-${index}`
}
title=
{
tooltipText
}
placement=
"top"
arrow
>
<
Tooltip
className=
"shrink-0"
key=
{
`${date}-${index}`
}
title=
{
tooltipText
}
placement=
"top"
arrow
>
<
div
<
div
className=
{
cn
(
className=
{
cn
(
"w-6 h-6 text-xs flex justify-center items-center cursor-default"
,
"w-6 h-6 text-xs
lg:text-[13px]
flex justify-center items-center cursor-default"
,
"rounded-lg border-2 text-gray-400"
,
"rounded-lg border-2 text-gray-400"
,
item
.
isCurrentMonth
&&
getCellAdditionalStyles
(
count
,
maxCount
),
item
.
isCurrentMonth
&&
getCellAdditionalStyles
(
count
,
maxCount
),
item
.
isCurrentMonth
&&
isToday
&&
"border-zinc-400"
,
item
.
isCurrentMonth
&&
isToday
&&
"border-zinc-400"
,
...
...
web/src/components/ChangeMemberPasswordDialog.tsx
View file @
5a2f18da
...
@@ -2,7 +2,7 @@ import { Button, Input } from "@usememos/mui";
...
@@ -2,7 +2,7 @@ import { Button, Input } from "@usememos/mui";
import
{
XIcon
}
from
"lucide-react"
;
import
{
XIcon
}
from
"lucide-react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
use
UserStore
}
from
"@/store/v1
"
;
import
{
use
rStore
}
from
"@/store/v2
"
;
import
{
User
}
from
"@/types/proto/api/v1/user_service"
;
import
{
User
}
from
"@/types/proto/api/v1/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
{
generateDialog
}
from
"./Dialog"
;
...
@@ -14,7 +14,6 @@ interface Props extends DialogProps {
...
@@ -14,7 +14,6 @@ interface Props extends DialogProps {
const
ChangeMemberPasswordDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
ChangeMemberPasswordDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
{
user
,
destroy
}
=
props
;
const
{
user
,
destroy
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
userStore
=
useUserStore
();
const
[
newPassword
,
setNewPassword
]
=
useState
(
""
);
const
[
newPassword
,
setNewPassword
]
=
useState
(
""
);
const
[
newPasswordAgain
,
setNewPasswordAgain
]
=
useState
(
""
);
const
[
newPasswordAgain
,
setNewPasswordAgain
]
=
useState
(
""
);
...
...
web/src/components/CreateShortcutDialog.tsx
View file @
5a2f18da
...
@@ -6,7 +6,7 @@ import { toast } from "react-hot-toast";
...
@@ -6,7 +6,7 @@ import { toast } from "react-hot-toast";
import
{
userServiceClient
}
from
"@/grpcweb"
;
import
{
userServiceClient
}
from
"@/grpcweb"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
{
use
UserStore
}
from
"@/store/v1
"
;
import
{
use
rStore
}
from
"@/store/v2
"
;
import
{
Shortcut
}
from
"@/types/proto/api/v1/user_service"
;
import
{
Shortcut
}
from
"@/types/proto/api/v1/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateUUID
}
from
"@/utils/uuid"
;
import
{
generateUUID
}
from
"@/utils/uuid"
;
...
@@ -20,7 +20,6 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
...
@@ -20,7 +20,6 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
const
{
destroy
}
=
props
;
const
{
destroy
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
user
=
useCurrentUser
();
const
user
=
useCurrentUser
();
const
userStore
=
useUserStore
();
const
[
shortcut
,
setShortcut
]
=
useState
(
Shortcut
.
fromPartial
({
...
props
.
shortcut
}));
const
[
shortcut
,
setShortcut
]
=
useState
(
Shortcut
.
fromPartial
({
...
props
.
shortcut
}));
const
requestState
=
useLoading
(
false
);
const
requestState
=
useLoading
(
false
);
const
isCreating
=
!
props
.
shortcut
;
const
isCreating
=
!
props
.
shortcut
;
...
...
web/src/components/Dialog/BaseDialog.tsx
View file @
5a2f18da
import
{
CssVarsProvider
}
from
"@mui/joy"
;
import
{
CssVarsProvider
}
from
"@mui/joy"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useEffect
,
useRef
}
from
"react"
;
import
{
useEffect
,
useRef
}
from
"react"
;
import
{
createRoot
}
from
"react-dom/client"
;
import
{
createRoot
}
from
"react-dom/client"
;
import
{
Provider
}
from
"react-redux"
;
import
dialogStore
from
"@/store/v2/dialog"
;
import
CommonContextProvider
from
"@/layouts/CommonContextProvider"
;
import
store
from
"@/store"
;
import
{
useDialogStore
}
from
"@/store/module"
;
import
theme
from
"@/theme"
;
import
theme
from
"@/theme"
;
import
{
cn
}
from
"@/utils"
;
import
{
cn
}
from
"@/utils"
;
import
"@/less/base-dialog.less"
;
import
"@/less/base-dialog.less"
;
...
@@ -19,17 +17,16 @@ interface Props extends DialogConfig, DialogProps {
...
@@ -19,17 +17,16 @@ interface Props extends DialogConfig, DialogProps {
children
:
React
.
ReactNode
;
children
:
React
.
ReactNode
;
}
}
const
BaseDialog
:
React
.
FC
<
Props
>
=
(
props
:
Props
)
=>
{
const
BaseDialog
=
observer
(
(
props
:
Props
)
=>
{
const
{
children
,
className
,
clickSpaceDestroy
,
dialogName
,
destroy
}
=
props
;
const
{
children
,
className
,
clickSpaceDestroy
,
dialogName
,
destroy
}
=
props
;
const
dialogStore
=
useDialogStore
();
const
dialogContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
dialogContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
dialogIndex
=
dialogStore
.
state
.
dialogS
tack
.
findIndex
((
item
)
=>
item
===
dialogName
);
const
dialogIndex
=
dialogStore
.
state
.
s
tack
.
findIndex
((
item
)
=>
item
===
dialogName
);
useEffect
(()
=>
{
useEffect
(()
=>
{
dialogStore
.
pushDialog
Stack
(
dialogName
);
dialogStore
.
pushDialog
(
dialogName
);
const
handleKeyDown
=
(
event
:
KeyboardEvent
)
=>
{
const
handleKeyDown
=
(
event
:
KeyboardEvent
)
=>
{
if
(
event
.
code
===
"Escape"
)
{
if
(
event
.
code
===
"Escape"
)
{
if
(
dialogName
===
dialogStore
.
topDialog
Stack
()
)
{
if
(
dialogName
===
dialogStore
.
topDialog
)
{
destroy
();
destroy
();
}
}
}
}
...
@@ -62,7 +59,7 @@ const BaseDialog: React.FC<Props> = (props: Props) => {
...
@@ -62,7 +59,7 @@ const BaseDialog: React.FC<Props> = (props: Props) => {
</
div
>
</
div
>
</
div
>
</
div
>
);
);
};
}
)
;
export
function
generateDialog
<
T
extends
DialogProps
>
(
export
function
generateDialog
<
T
extends
DialogProps
>
(
config
:
DialogConfig
,
config
:
DialogConfig
,
...
@@ -87,19 +84,15 @@ export function generateDialog<T extends DialogProps>(
...
@@ -87,19 +84,15 @@ export function generateDialog<T extends DialogProps>(
destroy
:
cbs
.
destroy
,
destroy
:
cbs
.
destroy
,
}
as
T
;
}
as
T
;
const
Fragment
=
(
const
Fragment
=
observer
(()
=>
(
<
Provider
store=
{
store
}
>
<
CssVarsProvider
theme=
{
theme
}
>
<
CssVarsProvider
theme=
{
theme
}
>
<
BaseDialog
destroy=
{
cbs
.
destroy
}
clickSpaceDestroy=
{
true
}
{
...
config
}
>
<
CommonContextProvider
>
<
DialogComponent
{
...
dialogProps
}
/>
<
BaseDialog
destroy=
{
cbs
.
destroy
}
clickSpaceDestroy=
{
true
}
{
...
config
}
>
</
BaseDialog
>
<
DialogComponent
{
...
dialogProps
}
/>
</
CssVarsProvider
>
</
BaseDialog
>
));
</
CommonContextProvider
>
</
CssVarsProvider
>
</
Provider
>
);
dialog
.
render
(
Fragment
);
dialog
.
render
(
<
Fragment
/>
);
return
cbs
;
return
cbs
;
}
}
web/src/components/HomeSidebar/ShortcutsSection.tsx
View file @
5a2f18da
import
{
Dropdown
,
Menu
,
MenuButton
,
MenuItem
,
Tooltip
}
from
"@mui/joy"
;
import
{
Dropdown
,
Menu
,
MenuButton
,
MenuItem
,
Tooltip
}
from
"@mui/joy"
;
import
{
Edit3Icon
,
MoreVerticalIcon
,
TrashIcon
,
PlusIcon
}
from
"lucide-react"
;
import
{
Edit3Icon
,
MoreVerticalIcon
,
TrashIcon
,
PlusIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
userServiceClient
}
from
"@/grpcweb"
;
import
{
userServiceClient
}
from
"@/grpcweb"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useMemoFilterStore
,
useUserStore
}
from
"@/store/v1"
;
import
{
useMemoFilterStore
}
from
"@/store/v1"
;
import
{
userStore
}
from
"@/store/v2"
;
import
{
Shortcut
}
from
"@/types/proto/api/v1/user_service"
;
import
{
Shortcut
}
from
"@/types/proto/api/v1/user_service"
;
import
{
cn
}
from
"@/utils"
;
import
{
cn
}
from
"@/utils"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
showCreateShortcutDialog
from
"../CreateShortcutDialog"
;
import
showCreateShortcutDialog
from
"../CreateShortcutDialog"
;
const
ShortcutsSection
=
()
=>
{
const
ShortcutsSection
=
observer
(
()
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
user
=
useCurrentUser
();
const
user
=
useCurrentUser
();
const
userStore
=
useUserStore
();
const
memoFilterStore
=
useMemoFilterStore
();
const
memoFilterStore
=
useMemoFilterStore
();
const
shortcuts
=
userStore
.
getState
()
.
shortcuts
;
const
shortcuts
=
userStore
.
state
.
shortcuts
;
useAsyncEffect
(
async
()
=>
{
useAsyncEffect
(
async
()
=>
{
await
userStore
.
fetchShortcuts
();
await
userStore
.
fetchShortcuts
();
...
@@ -71,6 +72,6 @@ const ShortcutsSection = () => {
...
@@ -71,6 +72,6 @@ const ShortcutsSection = () => {
</
div
>
</
div
>
</
div
>
</
div
>
);
);
};
}
)
;
export
default
ShortcutsSection
;
export
default
ShortcutsSection
;
web/src/components/Inbox/MemoCommentMessage.tsx
View file @
5a2f18da
...
@@ -5,7 +5,8 @@ import toast from "react-hot-toast";
...
@@ -5,7 +5,8 @@ import toast from "react-hot-toast";
import
{
activityServiceClient
}
from
"@/grpcweb"
;
import
{
activityServiceClient
}
from
"@/grpcweb"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
activityNamePrefix
,
useInboxStore
,
useMemoStore
,
useUserStore
}
from
"@/store/v1"
;
import
{
activityNamePrefix
,
useMemoStore
}
from
"@/store/v1"
;
import
{
userStore
}
from
"@/store/v2"
;
import
{
Inbox
,
Inbox_Status
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
Inbox
,
Inbox_Status
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
Memo
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
User
}
from
"@/types/proto/api/v1/user_service"
;
import
{
User
}
from
"@/types/proto/api/v1/user_service"
;
...
@@ -19,9 +20,7 @@ interface Props {
...
@@ -19,9 +20,7 @@ interface Props {
const
MemoCommentMessage
=
({
inbox
}:
Props
)
=>
{
const
MemoCommentMessage
=
({
inbox
}:
Props
)
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
navigateTo
=
useNavigateTo
();
const
navigateTo
=
useNavigateTo
();
const
inboxStore
=
useInboxStore
();
const
memoStore
=
useMemoStore
();
const
memoStore
=
useMemoStore
();
const
userStore
=
useUserStore
();
const
[
relatedMemo
,
setRelatedMemo
]
=
useState
<
Memo
|
undefined
>
(
undefined
);
const
[
relatedMemo
,
setRelatedMemo
]
=
useState
<
Memo
|
undefined
>
(
undefined
);
const
[
sender
,
setSender
]
=
useState
<
User
|
undefined
>
(
undefined
);
const
[
sender
,
setSender
]
=
useState
<
User
|
undefined
>
(
undefined
);
const
[
initialized
,
setInitialized
]
=
useState
<
boolean
>
(
false
);
const
[
initialized
,
setInitialized
]
=
useState
<
boolean
>
(
false
);
...
@@ -58,7 +57,7 @@ const MemoCommentMessage = ({ inbox }: Props) => {
...
@@ -58,7 +57,7 @@ const MemoCommentMessage = ({ inbox }: Props) => {
};
};
const
handleArchiveMessage
=
async
(
silence
=
false
)
=>
{
const
handleArchiveMessage
=
async
(
silence
=
false
)
=>
{
await
inbox
Store
.
updateInbox
(
await
user
Store
.
updateInbox
(
{
{
name
:
inbox
.
name
,
name
:
inbox
.
name
,
status
:
Inbox_Status
.
ARCHIVED
,
status
:
Inbox_Status
.
ARCHIVED
,
...
...
web/src/components/Inbox/VersionUpdateMessage.tsx
View file @
5a2f18da
...
@@ -3,7 +3,8 @@ import { ArrowUpIcon, InboxIcon } from "lucide-react";
...
@@ -3,7 +3,8 @@ import { ArrowUpIcon, InboxIcon } from "lucide-react";
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
toast
from
"react-hot-toast"
;
import
toast
from
"react-hot-toast"
;
import
{
activityServiceClient
}
from
"@/grpcweb"
;
import
{
activityServiceClient
}
from
"@/grpcweb"
;
import
{
activityNamePrefix
,
useInboxStore
}
from
"@/store/v1"
;
import
{
activityNamePrefix
}
from
"@/store/v1"
;
import
{
userStore
}
from
"@/store/v2"
;
import
{
Activity
}
from
"@/types/proto/api/v1/activity_service"
;
import
{
Activity
}
from
"@/types/proto/api/v1/activity_service"
;
import
{
Inbox
,
Inbox_Status
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
Inbox
,
Inbox_Status
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
cn
}
from
"@/utils"
;
import
{
cn
}
from
"@/utils"
;
...
@@ -15,7 +16,6 @@ interface Props {
...
@@ -15,7 +16,6 @@ interface Props {
const
VersionUpdateMessage
=
({
inbox
}:
Props
)
=>
{
const
VersionUpdateMessage
=
({
inbox
}:
Props
)
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
inboxStore
=
useInboxStore
();
const
[
activity
,
setActivity
]
=
useState
<
Activity
|
undefined
>
(
undefined
);
const
[
activity
,
setActivity
]
=
useState
<
Activity
|
undefined
>
(
undefined
);
useEffect
(()
=>
{
useEffect
(()
=>
{
...
@@ -43,7 +43,7 @@ const VersionUpdateMessage = ({ inbox }: Props) => {
...
@@ -43,7 +43,7 @@ const VersionUpdateMessage = ({ inbox }: Props) => {
};
};
const handleArchiveMessage = async (silence = false) => {
const handleArchiveMessage = async (silence = false) => {
await
inbox
Store.updateInbox(
await
user
Store.updateInbox(
{
{
name: inbox.name,
name: inbox.name,
status: Inbox_Status.ARCHIVED,
status: Inbox_Status.ARCHIVED,
...
...
web/src/components/MemoEditor/index.tsx
View file @
5a2f18da
...
@@ -2,6 +2,7 @@ import { Select, Option, Divider } from "@mui/joy";
...
@@ -2,6 +2,7 @@ import { Select, Option, Divider } from "@mui/joy";
import
{
Button
}
from
"@usememos/mui"
;
import
{
Button
}
from
"@usememos/mui"
;
import
{
isEqual
}
from
"lodash-es"
;
import
{
isEqual
}
from
"lodash-es"
;
import
{
LoaderIcon
,
SendIcon
}
from
"lucide-react"
;
import
{
LoaderIcon
,
SendIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
React
,
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
"react"
;
import
React
,
{
useEffect
,
useMemo
,
useRef
,
useState
}
from
"react"
;
import
DatePicker
from
"react-datepicker"
;
import
DatePicker
from
"react-datepicker"
;
import
"react-datepicker/dist/react-datepicker.css"
;
import
"react-datepicker/dist/react-datepicker.css"
;
...
@@ -13,7 +14,8 @@ import { TAB_SPACE_WIDTH } from "@/helpers/consts";
...
@@ -13,7 +14,8 @@ import { TAB_SPACE_WIDTH } from "@/helpers/consts";
import
{
isValidUrl
}
from
"@/helpers/utils"
;
import
{
isValidUrl
}
from
"@/helpers/utils"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useAsyncEffect
from
"@/hooks/useAsyncEffect"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useMemoStore
,
useResourceStore
,
useUserStore
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
useMemoStore
,
useResourceStore
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
userStore
}
from
"@/store/v2"
;
import
{
MemoRelation
,
MemoRelation_Type
}
from
"@/types/proto/api/v1/memo_relation_service"
;
import
{
MemoRelation
,
MemoRelation_Type
}
from
"@/types/proto/api/v1/memo_relation_service"
;
import
{
Location
,
Memo
,
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
Location
,
Memo
,
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
Resource
}
from
"@/types/proto/api/v1/resource_service"
;
import
{
Resource
}
from
"@/types/proto/api/v1/resource_service"
;
...
@@ -57,12 +59,11 @@ interface State {
...
@@ -57,12 +59,11 @@ interface State {
isComposing
:
boolean
;
isComposing
:
boolean
;
}
}
const
MemoEditor
=
(
props
:
Props
)
=>
{
const
MemoEditor
=
observer
(
(
props
:
Props
)
=>
{
const
{
className
,
cacheKey
,
memoName
,
parentMemoName
,
autoFocus
,
onConfirm
,
onCancel
}
=
props
;
const
{
className
,
cacheKey
,
memoName
,
parentMemoName
,
autoFocus
,
onConfirm
,
onCancel
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
{
i18n
}
=
useTranslation
();
const
{
i18n
}
=
useTranslation
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
userStore
=
useUserStore
();
const
memoStore
=
useMemoStore
();
const
memoStore
=
useMemoStore
();
const
resourceStore
=
useResourceStore
();
const
resourceStore
=
useResourceStore
();
const
currentUser
=
useCurrentUser
();
const
currentUser
=
useCurrentUser
();
...
@@ -78,7 +79,7 @@ const MemoEditor = (props: Props) => {
...
@@ -78,7 +79,7 @@ const MemoEditor = (props: Props) => {
const
[
displayTime
,
setDisplayTime
]
=
useState
<
Date
|
undefined
>
();
const
[
displayTime
,
setDisplayTime
]
=
useState
<
Date
|
undefined
>
();
const
[
hasContent
,
setHasContent
]
=
useState
<
boolean
>
(
false
);
const
[
hasContent
,
setHasContent
]
=
useState
<
boolean
>
(
false
);
const
editorRef
=
useRef
<
EditorRefActions
>
(
null
);
const
editorRef
=
useRef
<
EditorRefActions
>
(
null
);
const
userSetting
=
userStore
.
userSetting
as
UserSetting
;
const
userSetting
=
userStore
.
state
.
userSetting
as
UserSetting
;
const
contentCacheKey
=
`
${
currentUser
.
name
}
-
${
cacheKey
||
""
}
`
;
const
contentCacheKey
=
`
${
currentUser
.
name
}
-
${
cacheKey
||
""
}
`
;
const
[
contentCache
,
setContentCache
]
=
useLocalStorage
<
string
>
(
contentCacheKey
,
""
);
const
[
contentCache
,
setContentCache
]
=
useLocalStorage
<
string
>
(
contentCacheKey
,
""
);
const
referenceRelations
=
memoName
const
referenceRelations
=
memoName
...
@@ -521,6 +522,6 @@ const MemoEditor = (props: Props) => {
...
@@ -521,6 +522,6 @@ const MemoEditor = (props: Props) => {
</
div
>
</
div
>
</
MemoEditorContext
.
Provider
>
</
MemoEditorContext
.
Provider
>
);
);
};
}
)
;
export
default
MemoEditor
;
export
default
MemoEditor
;
web/src/components/Navigation.tsx
View file @
5a2f18da
import
{
Tooltip
}
from
"@mui/joy"
;
import
{
Tooltip
}
from
"@mui/joy"
;
import
{
ArchiveIcon
,
BellIcon
,
Globe2Icon
,
HomeIcon
,
LogInIcon
,
PaperclipIcon
,
SettingsIcon
,
SmileIcon
,
User2Icon
}
from
"lucide-react"
;
import
{
ArchiveIcon
,
BellIcon
,
Globe2Icon
,
HomeIcon
,
LogInIcon
,
PaperclipIcon
,
SettingsIcon
,
SmileIcon
,
User2Icon
}
from
"lucide-react"
;
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
{
use
InboxStore
}
from
"@/store/v1
"
;
import
{
use
rStore
}
from
"@/store/v2
"
;
import
{
Inbox_Status
}
from
"@/types/proto/api/v1/inbox_service"
;
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"
;
...
@@ -22,30 +23,18 @@ interface Props {
...
@@ -22,30 +23,18 @@ interface Props {
className
?:
string
;
className
?:
string
;
}
}
const
Navigation
=
(
props
:
Props
)
=>
{
const
Navigation
=
observer
(
(
props
:
Props
)
=>
{
const
{
collapsed
,
className
}
=
props
;
const
{
collapsed
,
className
}
=
props
;
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
user
=
useCurrentUser
();
const
user
=
useCurrentUser
();
const
inboxStore
=
useInboxStore
();
const
hasUnreadInbox
=
userStore
.
state
.
inboxes
.
some
((
inbox
)
=>
inbox
.
status
===
Inbox_Status
.
UNREAD
);
const
hasUnreadInbox
=
inboxStore
.
inboxes
.
some
((
inbox
)
=>
inbox
.
status
===
Inbox_Status
.
UNREAD
);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
!
user
)
{
if
(
!
user
)
{
return
;
return
;
}
}
inboxStore
.
fetchInboxes
();
userStore
.
fetchInboxes
();
// Fetch inboxes every 5 minutes.
const
timer
=
setInterval
(
async
()
=>
{
await
inboxStore
.
fetchInboxes
();
},
1000
*
60
*
5
,
);
return
()
=>
{
clearInterval
(
timer
);
};
},
[]);
},
[]);
const
homeNavLink
:
NavLinkItem
=
{
const
homeNavLink
:
NavLinkItem
=
{
...
@@ -147,6 +136,6 @@ const Navigation = (props: Props) => {
...
@@ -147,6 +136,6 @@ const Navigation = (props: Props) => {
</
div
>
</
div
>
</
header
>
</
header
>
);
);
};
}
)
;
export
default
Navigation
;
export
default
Navigation
;
web/src/components/PasswordSignInForm.tsx
View file @
5a2f18da
import
{
Button
,
Checkbox
,
Input
}
from
"@usememos/mui"
;
import
{
Button
,
Checkbox
,
Input
}
from
"@usememos/mui"
;
import
{
LoaderIcon
}
from
"lucide-react"
;
import
{
LoaderIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
ClientError
}
from
"nice-grpc-web"
;
import
{
ClientError
}
from
"nice-grpc-web"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
authServiceClient
}
from
"@/grpcweb"
;
import
{
authServiceClient
}
from
"@/grpcweb"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useCommonContext
}
from
"@/layouts/CommonContextProvider"
;
import
{
useUserStore
}
from
"@/store/v1"
;
import
{
useUserStore
}
from
"@/store/v1"
;
import
{
workspaceStore
}
from
"@/store/v2"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
PasswordSignInForm
=
()
=>
{
const
PasswordSignInForm
=
observer
(
()
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
navigateTo
=
useNavigateTo
();
const
navigateTo
=
useNavigateTo
();
const
commonContext
=
useCommonContext
();
const
userStore
=
useUserStore
();
const
userStore
=
useUserStore
();
const
actionBtnLoadingState
=
useLoading
(
false
);
const
actionBtnLoadingState
=
useLoading
(
false
);
const
[
username
,
setUsername
]
=
useState
(
""
);
const
[
username
,
setUsername
]
=
useState
(
""
);
...
@@ -21,11 +21,11 @@ const PasswordSignInForm = () => {
...
@@ -21,11 +21,11 @@ const PasswordSignInForm = () => {
const
[
remember
,
setRemember
]
=
useState
(
true
);
const
[
remember
,
setRemember
]
=
useState
(
true
);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
commonContext
.
profile
.
mode
===
"demo"
)
{
if
(
workspaceStore
.
state
.
profile
.
mode
===
"demo"
)
{
setUsername
(
"yourselfhosted"
);
setUsername
(
"yourselfhosted"
);
setPassword
(
"yourselfhosted"
);
setPassword
(
"yourselfhosted"
);
}
}
},
[
commonContext
.
profile
.
mode
]);
},
[
workspaceStore
.
state
.
profile
.
mode
]);
const
handleUsernameInputChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
handleUsernameInputChanged
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
text
=
e
.
target
.
value
as
string
;
const
text
=
e
.
target
.
value
as
string
;
...
@@ -117,6 +117,6 @@ const PasswordSignInForm = () => {
...
@@ -117,6 +117,6 @@ const PasswordSignInForm = () => {
</
div
>
</
div
>
</
form
>
</
form
>
);
);
};
}
)
;
export
default
PasswordSignInForm
;
export
default
PasswordSignInForm
;
web/src/components/Settings/PreferencesSection.tsx
View file @
5a2f18da
import
{
Divider
,
Option
,
Select
}
from
"@mui/joy"
;
import
{
Divider
,
Option
,
Select
}
from
"@mui/joy"
;
import
{
useCommonContext
}
from
"@/layouts/CommonContextProvider
"
;
import
{
observer
}
from
"mobx-react-lite
"
;
import
{
use
UserStore
}
from
"@/store/v1
"
;
import
{
use
rStore
,
workspaceStore
}
from
"@/store/v2
"
;
import
{
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
Visibility
}
from
"@/types/proto/api/v1/memo_service"
;
import
{
UserSetting
}
from
"@/types/proto/api/v1/user_service"
;
import
{
UserSetting
}
from
"@/types/proto/api/v1/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
@@ -10,14 +10,12 @@ import LocaleSelect from "../LocaleSelect";
...
@@ -10,14 +10,12 @@ import LocaleSelect from "../LocaleSelect";
import
VisibilityIcon
from
"../VisibilityIcon"
;
import
VisibilityIcon
from
"../VisibilityIcon"
;
import
WebhookSection
from
"./WebhookSection"
;
import
WebhookSection
from
"./WebhookSection"
;
const
PreferencesSection
=
()
=>
{
const
PreferencesSection
=
observer
(
()
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
commonContext
=
useCommonContext
();
const
setting
=
userStore
.
state
.
userSetting
as
UserSetting
;
const
userStore
=
useUserStore
();
const
setting
=
userStore
.
userSetting
as
UserSetting
;
const
handleLocaleSelectChange
=
async
(
locale
:
Locale
)
=>
{
const
handleLocaleSelectChange
=
async
(
locale
:
Locale
)
=>
{
commonContext
.
setLocale
(
locale
);
workspaceStore
.
setPartial
({
locale
}
);
await
userStore
.
updateUserSetting
(
await
userStore
.
updateUserSetting
(
{
{
locale
,
locale
,
...
@@ -27,7 +25,7 @@ const PreferencesSection = () => {
...
@@ -27,7 +25,7 @@ const PreferencesSection = () => {
};
};
const
handleAppearanceSelectChange
=
async
(
appearance
:
Appearance
)
=>
{
const
handleAppearanceSelectChange
=
async
(
appearance
:
Appearance
)
=>
{
commonContext
.
setAppearance
(
appearance
);
workspaceStore
.
setPartial
({
appearance
}
);
await
userStore
.
updateUserSetting
(
await
userStore
.
updateUserSetting
(
{
{
appearance
,
appearance
,
...
@@ -84,6 +82,6 @@ const PreferencesSection = () => {
...
@@ -84,6 +82,6 @@ const PreferencesSection = () => {
<
WebhookSection
/>
<
WebhookSection
/>
</
div
>
</
div
>
);
);
};
}
)
;
export
default
PreferencesSection
;
export
default
PreferencesSection
;
web/src/components/StatisticsView.tsx
View file @
5a2f18da
import
{
Tooltip
}
from
"@mui/joy"
;
import
{
Tooltip
}
from
"@mui/joy"
;
import
dayjs
from
"dayjs"
;
import
dayjs
from
"dayjs"
;
import
{
countBy
}
from
"lodash-es"
;
import
{
countBy
}
from
"lodash-es"
;
import
{
CheckCircleIcon
,
Chevron
DownIcon
,
ChevronUp
Icon
,
Code2Icon
,
LinkIcon
,
ListTodoIcon
}
from
"lucide-react"
;
import
{
CheckCircleIcon
,
Chevron
RightIcon
,
ChevronLeft
Icon
,
Code2Icon
,
LinkIcon
,
ListTodoIcon
}
from
"lucide-react"
;
import
{
useState
}
from
"react"
;
import
{
useState
}
from
"react"
;
import
DatePicker
from
"react-datepicker"
;
import
DatePicker
from
"react-datepicker"
;
import
"react-datepicker/dist/react-datepicker.css"
;
import
"react-datepicker/dist/react-datepicker.css"
;
...
@@ -60,7 +60,7 @@ const StatisticsView = () => {
...
@@ -60,7 +60,7 @@ const StatisticsView = () => {
showMonthYearPicker
showMonthYearPicker
showFullMonthYearPicker
showFullMonthYearPicker
customInput=
{
customInput=
{
<
span
className=
"cursor-pointer hover:text-gray-600 dark:hover:text-gray-300"
>
<
span
className=
"cursor-pointer
text-base md:text-lg
hover:text-gray-600 dark:hover:text-gray-300"
>
{
dayjs
(
visibleMonthString
).
toDate
().
toLocaleString
(
i18n
.
language
,
{
year
:
"numeric"
,
month
:
"long"
})
}
{
dayjs
(
visibleMonthString
).
toDate
().
toLocaleString
(
i18n
.
language
,
{
year
:
"numeric"
,
month
:
"long"
})
}
</
span
>
</
span
>
}
}
...
@@ -73,13 +73,13 @@ const StatisticsView = () => {
...
@@ -73,13 +73,13 @@ const StatisticsView = () => {
className=
"cursor-pointer hover:opacity-80"
className=
"cursor-pointer hover:opacity-80"
onClick=
{
()
=>
setVisibleMonthString
(
dayjs
(
visibleMonthString
).
subtract
(
1
,
"month"
).
format
(
"YYYY-MM"
))
}
onClick=
{
()
=>
setVisibleMonthString
(
dayjs
(
visibleMonthString
).
subtract
(
1
,
"month"
).
format
(
"YYYY-MM"
))
}
>
>
<
Chevron
Up
Icon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
<
Chevron
Left
Icon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
</
span
>
</
span
>
<
span
<
span
className=
"cursor-pointer hover:opacity-80"
className=
"cursor-pointer hover:opacity-80"
onClick=
{
()
=>
setVisibleMonthString
(
dayjs
(
visibleMonthString
).
add
(
1
,
"month"
).
format
(
"YYYY-MM"
))
}
onClick=
{
()
=>
setVisibleMonthString
(
dayjs
(
visibleMonthString
).
add
(
1
,
"month"
).
format
(
"YYYY-MM"
))
}
>
>
<
Chevron
Down
Icon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
<
Chevron
Right
Icon
className=
"w-5 h-auto shrink-0 opacity-40"
/>
</
span
>
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
...
...
web/src/helpers/polyfill.ts
deleted
100644 → 0
View file @
13f6fa7b
(()
=>
{
if
(
!
String
.
prototype
.
replaceAll
)
{
String
.
prototype
.
replaceAll
=
function
(
str
:
any
,
newStr
:
any
)
{
// If a regex pattern
if
(
Object
.
prototype
.
toString
.
call
(
str
).
toLowerCase
()
===
"[object regexp]"
)
{
return
this
.
replace
(
str
,
newStr
);
}
// If a string
return
this
.
replace
(
new
RegExp
(
str
,
"g"
),
newStr
);
};
}
})();
export
default
null
;
web/src/hooks/useCurrentUser.ts
View file @
5a2f18da
import
{
use
UserStore
}
from
"@/store/v1
"
;
import
{
use
rStore
}
from
"@/store/v2
"
;
const
useCurrentUser
=
()
=>
{
const
useCurrentUser
=
()
=>
{
const
userStore
=
useUserStore
();
return
userStore
.
state
.
userMapByName
[
userStore
.
state
.
currentUser
||
""
];
return
userStore
.
getUserByName
(
userStore
.
currentUser
||
""
);
};
};
export
default
useCurrentUser
;
export
default
useCurrentUser
;
web/src/layouts/CommonContextProvider.tsx
deleted
100644 → 0
View file @
13f6fa7b
import
{
createContext
,
useContext
,
useEffect
,
useState
}
from
"react"
;
import
useLocalStorage
from
"react-use/lib/useLocalStorage"
;
import
{
workspaceServiceClient
}
from
"@/grpcweb"
;
import
{
useUserStore
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
WorkspaceProfile
}
from
"@/types/proto/api/v1/workspace_service"
;
import
{
WorkspaceGeneralSetting
,
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
interface
Context
{
locale
:
string
;
appearance
:
string
;
profile
:
WorkspaceProfile
;
setLocale
:
(
locale
:
string
)
=>
void
;
setAppearance
:
(
appearance
:
string
)
=>
void
;
}
const
CommonContext
=
createContext
<
Context
>
({
locale
:
"en"
,
appearance
:
"system"
,
profile
:
WorkspaceProfile
.
fromPartial
({}),
setLocale
:
()
=>
{},
setAppearance
:
()
=>
{},
});
const
CommonContextProvider
=
({
children
}:
{
children
:
React
.
ReactNode
})
=>
{
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
userStore
=
useUserStore
();
const
[
initialized
,
setInitialized
]
=
useState
(
false
);
const
[
commonContext
,
setCommonContext
]
=
useState
<
Pick
<
Context
,
"locale"
|
"appearance"
|
"profile"
>>
({
locale
:
"en"
,
appearance
:
"system"
,
profile
:
WorkspaceProfile
.
fromPartial
({}),
});
const
[
locale
]
=
useLocalStorage
(
"locale"
,
"en"
);
const
[
appearance
]
=
useLocalStorage
(
"appearance"
,
"system"
);
useEffect
(()
=>
{
const
initialWorkspace
=
async
()
=>
{
const
workspaceProfile
=
await
workspaceServiceClient
.
getWorkspaceProfile
({});
// Initial fetch for workspace settings.
(
async
()
=>
{
[
WorkspaceSettingKey
.
GENERAL
,
WorkspaceSettingKey
.
MEMO_RELATED
].
forEach
(
async
(
key
)
=>
{
await
workspaceSettingStore
.
fetchWorkspaceSetting
(
key
);
});
})();
const
workspaceGeneralSetting
=
workspaceSettingStore
.
getWorkspaceSettingByKey
(
WorkspaceSettingKey
.
GENERAL
).
generalSetting
||
WorkspaceGeneralSetting
.
fromPartial
({});
setCommonContext
({
locale
:
locale
||
workspaceGeneralSetting
.
customProfile
?.
locale
||
"en"
,
appearance
:
appearance
||
workspaceGeneralSetting
.
customProfile
?.
appearance
||
"system"
,
profile
:
workspaceProfile
,
});
};
const
initialUser
=
async
()
=>
{
try
{
await
userStore
.
fetchCurrentUser
();
}
catch
(
error
)
{
// Do nothing.
}
};
Promise
.
all
([
initialWorkspace
(),
initialUser
()]).
then
(()
=>
setInitialized
(
true
));
},
[]);
return
(
<
CommonContext
.
Provider
value=
{
{
...
commonContext
,
setLocale
:
(
locale
:
string
)
=>
setCommonContext
({
...
commonContext
,
locale
}),
setAppearance
:
(
appearance
:
string
)
=>
setCommonContext
({
...
commonContext
,
appearance
}),
}
}
>
{
!
initialized
?
null
:
<>
{
children
}
</>
}
</
CommonContext
.
Provider
>
);
};
export
const
useCommonContext
=
()
=>
{
return
useContext
(
CommonContext
);
};
export
default
CommonContextProvider
;
web/src/main.tsx
View file @
5a2f18da
...
@@ -2,30 +2,30 @@ import "@github/relative-time-element";
...
@@ -2,30 +2,30 @@ import "@github/relative-time-element";
import
{
CssVarsProvider
}
from
"@mui/joy"
;
import
{
CssVarsProvider
}
from
"@mui/joy"
;
import
"@usememos/mui/dist/index.css"
;
import
"@usememos/mui/dist/index.css"
;
import
"leaflet/dist/leaflet.css"
;
import
"leaflet/dist/leaflet.css"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
createRoot
}
from
"react-dom/client"
;
import
{
createRoot
}
from
"react-dom/client"
;
import
{
Toaster
}
from
"react-hot-toast"
;
import
{
Toaster
}
from
"react-hot-toast"
;
import
{
Provider
}
from
"react-redux"
;
import
{
RouterProvider
}
from
"react-router-dom"
;
import
{
RouterProvider
}
from
"react-router-dom"
;
import
"./css/tailwind.css"
;
import
"./css/tailwind.css"
;
import
"./helpers/polyfill"
;
import
"./i18n"
;
import
"./i18n"
;
import
CommonContextProvider
from
"./layouts/CommonContextProvider"
;
import
"./less/highlight.less"
;
import
"./less/highlight.less"
;
import
router
from
"./router"
;
import
router
from
"./router"
;
import
store
from
"./store"
;
import
{
initialUserStore
}
from
"./store/v2/user"
;
import
{
initialWorkspaceStore
}
from
"./store/v2/workspace"
;
import
theme
from
"./theme"
;
import
theme
from
"./theme"
;
const
Main
=
observer
(()
=>
(
<
CssVarsProvider
theme=
{
theme
}
>
<
RouterProvider
router=
{
router
}
/>
<
Toaster
position=
"top-right"
toastOptions=
{
{
className
:
"dark:bg-zinc-700 dark:text-gray-300"
}
}
/>
</
CssVarsProvider
>
));
(
async
()
=>
{
(
async
()
=>
{
await
initialWorkspaceStore
();
await
initialUserStore
();
const
container
=
document
.
getElementById
(
"root"
);
const
container
=
document
.
getElementById
(
"root"
);
const
root
=
createRoot
(
container
as
HTMLElement
);
const
root
=
createRoot
(
container
as
HTMLElement
);
root
.
render
(
root
.
render
(<
Main
/>);
<
Provider
store=
{
store
}
>
<
CssVarsProvider
theme=
{
theme
}
>
<
CommonContextProvider
>
<
RouterProvider
router=
{
router
}
/>
</
CommonContextProvider
>
<
Toaster
position=
"top-right"
toastOptions=
{
{
className
:
"dark:bg-zinc-700 dark:text-gray-300"
}
}
/>
</
CssVarsProvider
>
</
Provider
>,
);
})();
})();
web/src/pages/AdminSignIn.tsx
View file @
5a2f18da
import
{
observer
}
from
"mobx-react-lite"
;
import
AppearanceSelect
from
"@/components/AppearanceSelect"
;
import
AppearanceSelect
from
"@/components/AppearanceSelect"
;
import
LocaleSelect
from
"@/components/LocaleSelect"
;
import
LocaleSelect
from
"@/components/LocaleSelect"
;
import
PasswordSignInForm
from
"@/components/PasswordSignInForm"
;
import
PasswordSignInForm
from
"@/components/PasswordSignInForm"
;
import
{
useCommonContext
}
from
"@/layouts/CommonContextProvider"
;
import
{
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
workspaceStore
}
from
"@/store/v2"
;
import
{
WorkspaceGeneralSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
import
{
WorkspaceGeneralSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
const
AdminSignIn
=
()
=>
{
const
AdminSignIn
=
observer
(()
=>
{
const
commonContext
=
useCommonContext
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
workspaceGeneralSetting
=
const
workspaceGeneralSetting
=
workspaceSettingStore
.
getWorkspaceSettingByKey
(
WorkspaceSettingKey
.
GENERAL
).
generalSetting
||
WorkspaceGeneralSetting
.
fromPartial
({});
workspaceSettingStore
.
getWorkspaceSettingByKey
(
WorkspaceSettingKey
.
GENERAL
).
generalSetting
||
WorkspaceGeneralSetting
.
fromPartial
({});
const
handleLocaleSelectChange
=
(
locale
:
Locale
)
=>
{
const
handleLocaleSelectChange
=
(
locale
:
Locale
)
=>
{
commonContext
.
setLocale
(
locale
);
workspaceStore
.
setPartial
({
locale
}
);
};
};
const
handleAppearanceSelectChange
=
(
appearance
:
Appearance
)
=>
{
const
handleAppearanceSelectChange
=
(
appearance
:
Appearance
)
=>
{
commonContext
.
setAppearance
(
appearance
);
workspaceStore
.
setPartial
({
appearance
}
);
};
};
return
(
return
(
...
@@ -33,11 +33,11 @@ const AdminSignIn = () => {
...
@@ -33,11 +33,11 @@ const AdminSignIn = () => {
<
PasswordSignInForm
/>
<
PasswordSignInForm
/>
</
div
>
</
div
>
<
div
className=
"mt-4 flex flex-row items-center justify-center w-full gap-2"
>
<
div
className=
"mt-4 flex flex-row items-center justify-center w-full gap-2"
>
<
LocaleSelect
value=
{
commonContext
.
locale
}
onChange=
{
handleLocaleSelectChange
}
/>
<
LocaleSelect
value=
{
workspaceStore
.
state
.
locale
}
onChange=
{
handleLocaleSelectChange
}
/>
<
AppearanceSelect
value=
{
commonContext
.
appearance
as
Appearance
}
onChange=
{
handleAppearanceSelectChange
}
/>
<
AppearanceSelect
value=
{
workspaceStore
.
state
.
appearance
as
Appearance
}
onChange=
{
handleAppearanceSelectChange
}
/>
</
div
>
</
div
>
</
div
>
</
div
>
);
);
};
}
)
;
export
default
AdminSignIn
;
export
default
AdminSignIn
;
web/src/pages/Home.tsx
View file @
5a2f18da
import
dayjs
from
"dayjs"
;
import
dayjs
from
"dayjs"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useMemo
}
from
"react"
;
import
{
useMemo
}
from
"react"
;
import
{
HomeSidebar
,
HomeSidebarDrawer
}
from
"@/components/HomeSidebar"
;
import
{
HomeSidebar
,
HomeSidebarDrawer
}
from
"@/components/HomeSidebar"
;
import
MemoEditor
from
"@/components/MemoEditor"
;
import
MemoEditor
from
"@/components/MemoEditor"
;
...
@@ -7,17 +8,17 @@ import MobileHeader from "@/components/MobileHeader";
...
@@ -7,17 +8,17 @@ import MobileHeader from "@/components/MobileHeader";
import
PagedMemoList
from
"@/components/PagedMemoList"
;
import
PagedMemoList
from
"@/components/PagedMemoList"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
useResponsiveWidth
from
"@/hooks/useResponsiveWidth"
;
import
{
useMemoFilterStore
,
useUserStore
}
from
"@/store/v1"
;
import
{
useMemoFilterStore
}
from
"@/store/v1"
;
import
{
userStore
}
from
"@/store/v2"
;
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"
;
import
{
cn
}
from
"@/utils"
;
import
{
cn
}
from
"@/utils"
;
const
Home
=
()
=>
{
const
Home
=
observer
(
()
=>
{
const
{
md
,
lg
}
=
useResponsiveWidth
();
const
{
md
,
lg
}
=
useResponsiveWidth
();
const
user
=
useCurrentUser
();
const
user
=
useCurrentUser
();
const
userStore
=
useUserStore
();
const
memoFilterStore
=
useMemoFilterStore
();
const
memoFilterStore
=
useMemoFilterStore
();
const
selectedShortcut
=
userStore
.
shortcuts
.
find
((
shortcut
)
=>
shortcut
.
id
===
memoFilterStore
.
shortcut
);
const
selectedShortcut
=
userStore
.
s
tate
.
s
hortcuts
.
find
((
shortcut
)
=>
shortcut
.
id
===
memoFilterStore
.
shortcut
);
const
memoListFilter
=
useMemo
(()
=>
{
const
memoListFilter
=
useMemo
(()
=>
{
const
conditions
=
[];
const
conditions
=
[];
...
@@ -95,6 +96,6 @@ const Home = () => {
...
@@ -95,6 +96,6 @@ const Home = () => {
</
div
>
</
div
>
</
section
>
</
section
>
);
);
};
}
)
;
export
default
Home
;
export
default
Home
;
web/src/pages/Inboxes.tsx
View file @
5a2f18da
import
{
BellIcon
}
from
"lucide-react"
;
import
{
BellIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useEffect
}
from
"react"
;
import
{
useEffect
}
from
"react"
;
import
Empty
from
"@/components/Empty"
;
import
Empty
from
"@/components/Empty"
;
import
MemoCommentMessage
from
"@/components/Inbox/MemoCommentMessage"
;
import
MemoCommentMessage
from
"@/components/Inbox/MemoCommentMessage"
;
import
VersionUpdateMessage
from
"@/components/Inbox/VersionUpdateMessage"
;
import
VersionUpdateMessage
from
"@/components/Inbox/VersionUpdateMessage"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
{
use
InboxStore
}
from
"@/store/v1
"
;
import
{
use
rStore
}
from
"@/store/v2
"
;
import
{
Inbox_Status
,
Inbox_Type
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
Inbox_Status
,
Inbox_Type
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
Inboxes
=
()
=>
{
const
Inboxes
=
observer
(
()
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
inboxStore
=
useInboxStore
();
const
inboxes
=
userStore
.
state
.
inboxes
.
sort
((
a
,
b
)
=>
{
const
inboxes
=
inboxStore
.
inboxes
.
sort
((
a
,
b
)
=>
{
if
(
a
.
status
===
b
.
status
)
{
if
(
a
.
status
===
b
.
status
)
{
return
0
;
return
0
;
}
}
...
@@ -19,7 +19,7 @@ const Inboxes = () => {
...
@@ -19,7 +19,7 @@ const Inboxes = () => {
});
});
useEffect
(()
=>
{
useEffect
(()
=>
{
inbox
Store
.
fetchInboxes
();
user
Store
.
fetchInboxes
();
},
[]);
},
[]);
return
(
return
(
...
@@ -55,6 +55,6 @@ const Inboxes = () => {
...
@@ -55,6 +55,6 @@ const Inboxes = () => {
</
div
>
</
div
>
</
section
>
</
section
>
);
);
};
}
)
;
export
default
Inboxes
;
export
default
Inboxes
;
web/src/pages/Setting.tsx
View file @
5a2f18da
import
{
Option
,
Select
}
from
"@mui/joy"
;
import
{
Option
,
Select
}
from
"@mui/joy"
;
import
{
CogIcon
,
DatabaseIcon
,
KeyIcon
,
LibraryIcon
,
LucideIcon
,
Settings2Icon
,
UserIcon
,
UsersIcon
}
from
"lucide-react"
;
import
{
CogIcon
,
DatabaseIcon
,
KeyIcon
,
LibraryIcon
,
LucideIcon
,
Settings2Icon
,
UserIcon
,
UsersIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
"react"
;
import
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
"react"
;
import
{
useLocation
}
from
"react-router-dom"
;
import
{
useLocation
}
from
"react-router-dom"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
...
@@ -12,8 +13,8 @@ import SectionMenuItem from "@/components/Settings/SectionMenuItem";
...
@@ -12,8 +13,8 @@ import SectionMenuItem from "@/components/Settings/SectionMenuItem";
import
StorageSection
from
"@/components/Settings/StorageSection"
;
import
StorageSection
from
"@/components/Settings/StorageSection"
;
import
WorkspaceSection
from
"@/components/Settings/WorkspaceSection"
;
import
WorkspaceSection
from
"@/components/Settings/WorkspaceSection"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useCommonContext
}
from
"@/layouts/CommonContextProvider"
;
import
{
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
workspaceStore
}
from
"@/store/v2"
;
import
{
User_Role
}
from
"@/types/proto/api/v1/user_service"
;
import
{
User_Role
}
from
"@/types/proto/api/v1/user_service"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
@@ -36,10 +37,9 @@ const SECTION_ICON_MAP: Record<SettingSection, LucideIcon> = {
...
@@ -36,10 +37,9 @@ const SECTION_ICON_MAP: Record<SettingSection, LucideIcon> = {
sso
:
KeyIcon
,
sso
:
KeyIcon
,
};
};
const
Setting
=
()
=>
{
const
Setting
=
observer
(
()
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
location
=
useLocation
();
const
location
=
useLocation
();
const
commonContext
=
useCommonContext
();
const
user
=
useCurrentUser
();
const
user
=
useCurrentUser
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
[
state
,
setState
]
=
useState
<
State
>
({
const
[
state
,
setState
]
=
useState
<
State
>
({
...
@@ -115,7 +115,7 @@ const Setting = () => {
...
@@ -115,7 +115,7 @@ const Setting = () => {
/>
/>
))
}
))
}
<
span
className=
"px-3 mt-2 opacity-70 text-sm"
>
<
span
className=
"px-3 mt-2 opacity-70 text-sm"
>
{
t
(
"setting.version"
)
}
: v
{
commonContext
.
profile
.
version
}
{
t
(
"setting.version"
)
}
: v
{
workspaceStore
.
state
.
profile
.
version
}
</
span
>
</
span
>
</
div
>
</
div
>
</>
</>
...
@@ -151,6 +151,6 @@ const Setting = () => {
...
@@ -151,6 +151,6 @@ const Setting = () => {
</
div
>
</
div
>
</
section
>
</
section
>
);
);
};
}
)
;
export
default
Setting
;
export
default
Setting
;
web/src/pages/SignIn.tsx
View file @
5a2f18da
import
{
Divider
}
from
"@mui/joy"
;
import
{
Divider
}
from
"@mui/joy"
;
import
{
Button
}
from
"@usememos/mui"
;
import
{
Button
}
from
"@usememos/mui"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
Link
}
from
"react-router-dom"
;
import
{
Link
}
from
"react-router-dom"
;
...
@@ -9,18 +10,17 @@ import PasswordSignInForm from "@/components/PasswordSignInForm";
...
@@ -9,18 +10,17 @@ import PasswordSignInForm from "@/components/PasswordSignInForm";
import
{
identityProviderServiceClient
}
from
"@/grpcweb"
;
import
{
identityProviderServiceClient
}
from
"@/grpcweb"
;
import
{
absolutifyLink
}
from
"@/helpers/utils"
;
import
{
absolutifyLink
}
from
"@/helpers/utils"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useCommonContext
}
from
"@/layouts/CommonContextProvider"
;
import
{
Routes
}
from
"@/router"
;
import
{
Routes
}
from
"@/router"
;
import
{
extractIdentityProviderIdFromName
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
extractIdentityProviderIdFromName
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
workspaceStore
}
from
"@/store/v2"
;
import
{
IdentityProvider
,
IdentityProvider_Type
}
from
"@/types/proto/api/v1/idp_service"
;
import
{
IdentityProvider
,
IdentityProvider_Type
}
from
"@/types/proto/api/v1/idp_service"
;
import
{
WorkspaceGeneralSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
import
{
WorkspaceGeneralSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
SignIn
=
()
=>
{
const
SignIn
=
observer
(
()
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
currentUser
=
useCurrentUser
();
const
currentUser
=
useCurrentUser
();
const
commonContext
=
useCommonContext
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
[
identityProviderList
,
setIdentityProviderList
]
=
useState
<
IdentityProvider
[]
>
([]);
const
[
identityProviderList
,
setIdentityProviderList
]
=
useState
<
IdentityProvider
[]
>
([]);
const
workspaceGeneralSetting
=
const
workspaceGeneralSetting
=
...
@@ -43,11 +43,11 @@ const SignIn = () => {
...
@@ -43,11 +43,11 @@ const SignIn = () => {
},
[]);
},
[]);
const
handleLocaleSelectChange
=
(
locale
:
Locale
)
=>
{
const
handleLocaleSelectChange
=
(
locale
:
Locale
)
=>
{
commonContext
.
setLocale
(
locale
);
workspaceStore
.
setPartial
({
locale
}
);
};
};
const
handleAppearanceSelectChange
=
(
appearance
:
Appearance
)
=>
{
const
handleAppearanceSelectChange
=
(
appearance
:
Appearance
)
=>
{
commonContext
.
setAppearance
(
appearance
);
workspaceStore
.
setPartial
({
appearance
}
);
};
};
const
handleSignInWithIdentityProvider
=
async
(
identityProvider
:
IdentityProvider
)
=>
{
const
handleSignInWithIdentityProvider
=
async
(
identityProvider
:
IdentityProvider
)
=>
{
...
@@ -110,11 +110,11 @@ const SignIn = () => {
...
@@ -110,11 +110,11 @@ const SignIn = () => {
)
}
)
}
</
div
>
</
div
>
<
div
className=
"mt-4 flex flex-row items-center justify-center w-full gap-2"
>
<
div
className=
"mt-4 flex flex-row items-center justify-center w-full gap-2"
>
<
LocaleSelect
value=
{
commonContext
.
locale
}
onChange=
{
handleLocaleSelectChange
}
/>
<
LocaleSelect
value=
{
workspaceStore
.
state
.
locale
}
onChange=
{
handleLocaleSelectChange
}
/>
<
AppearanceSelect
value=
{
commonContext
.
appearance
as
Appearance
}
onChange=
{
handleAppearanceSelectChange
}
/>
<
AppearanceSelect
value=
{
workspaceStore
.
state
.
appearance
as
Appearance
}
onChange=
{
handleAppearanceSelectChange
}
/>
</
div
>
</
div
>
</
div
>
</
div
>
);
);
};
}
)
;
export
default
SignIn
;
export
default
SignIn
;
web/src/pages/SignUp.tsx
View file @
5a2f18da
import
{
Button
,
Input
}
from
"@usememos/mui"
;
import
{
Button
,
Input
}
from
"@usememos/mui"
;
import
{
LoaderIcon
}
from
"lucide-react"
;
import
{
LoaderIcon
}
from
"lucide-react"
;
import
{
observer
}
from
"mobx-react-lite"
;
import
{
ClientError
}
from
"nice-grpc-web"
;
import
{
ClientError
}
from
"nice-grpc-web"
;
import
{
useState
}
from
"react"
;
import
{
useState
}
from
"react"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
toast
}
from
"react-hot-toast"
;
...
@@ -9,16 +10,15 @@ import LocaleSelect from "@/components/LocaleSelect";
...
@@ -9,16 +10,15 @@ import LocaleSelect from "@/components/LocaleSelect";
import
{
authServiceClient
}
from
"@/grpcweb"
;
import
{
authServiceClient
}
from
"@/grpcweb"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useCommonContext
}
from
"@/layouts/CommonContextProvider"
;
import
{
useUserStore
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
useUserStore
,
useWorkspaceSettingStore
}
from
"@/store/v1"
;
import
{
workspaceStore
}
from
"@/store/v2"
;
import
{
WorkspaceGeneralSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
import
{
WorkspaceGeneralSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
SignUp
=
()
=>
{
const
SignUp
=
observer
(
()
=>
{
const
t
=
useTranslate
();
const
t
=
useTranslate
();
const
navigateTo
=
useNavigateTo
();
const
navigateTo
=
useNavigateTo
();
const
commonContext
=
useCommonContext
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
workspaceSettingStore
=
useWorkspaceSettingStore
();
const
userStore
=
useUserStore
();
const
userStore
=
useUserStore
();
const
actionBtnLoadingState
=
useLoading
(
false
);
const
actionBtnLoadingState
=
useLoading
(
false
);
...
@@ -38,11 +38,11 @@ const SignUp = () => {
...
@@ -38,11 +38,11 @@ const SignUp = () => {
};
};
const
handleLocaleSelectChange
=
(
locale
:
Locale
)
=>
{
const
handleLocaleSelectChange
=
(
locale
:
Locale
)
=>
{
commonContext
.
setLocale
(
locale
);
workspaceStore
.
setPartial
({
locale
}
);
};
};
const
handleAppearanceSelectChange
=
(
appearance
:
Appearance
)
=>
{
const
handleAppearanceSelectChange
=
(
appearance
:
Appearance
)
=>
{
commonContext
.
setAppearance
(
appearance
);
workspaceStore
.
setPartial
({
appearance
}
);
};
};
const
handleFormSubmit
=
(
e
:
React
.
FormEvent
<
HTMLFormElement
>
)
=>
{
const
handleFormSubmit
=
(
e
:
React
.
FormEvent
<
HTMLFormElement
>
)
=>
{
...
@@ -136,7 +136,7 @@ const SignUp = () => {
...
@@ -136,7 +136,7 @@ const SignUp = () => {
)
:
(
)
:
(
<
p
className=
"w-full text-2xl mt-2 dark:text-gray-500"
>
Sign up is not allowed.
</
p
>
<
p
className=
"w-full text-2xl mt-2 dark:text-gray-500"
>
Sign up is not allowed.
</
p
>
)
}
)
}
{
!
commonContext
.
profile
.
owner
?
(
{
!
workspaceStore
.
state
.
profile
.
owner
?
(
<
p
className=
"w-full mt-4 text-sm font-medium dark:text-gray-500"
>
{
t
(
"auth.host-tip"
)
}
</
p
>
<
p
className=
"w-full mt-4 text-sm font-medium dark:text-gray-500"
>
{
t
(
"auth.host-tip"
)
}
</
p
>
)
:
(
)
:
(
<
p
className=
"w-full mt-4 text-sm"
>
<
p
className=
"w-full mt-4 text-sm"
>
...
@@ -148,11 +148,11 @@ const SignUp = () => {
...
@@ -148,11 +148,11 @@ const SignUp = () => {
)
}
)
}
</
div
>
</
div
>
<
div
className=
"mt-4 flex flex-row items-center justify-center w-full gap-2"
>
<
div
className=
"mt-4 flex flex-row items-center justify-center w-full gap-2"
>
<
LocaleSelect
value=
{
commonContext
.
locale
}
onChange=
{
handleLocaleSelectChange
}
/>
<
LocaleSelect
value=
{
workspaceStore
.
state
.
locale
}
onChange=
{
handleLocaleSelectChange
}
/>
<
AppearanceSelect
value=
{
commonContext
.
appearance
as
Appearance
}
onChange=
{
handleAppearanceSelectChange
}
/>
<
AppearanceSelect
value=
{
workspaceStore
.
state
.
appearance
as
Appearance
}
onChange=
{
handleAppearanceSelectChange
}
/>
</
div
>
</
div
>
</
div
>
</
div
>
);
);
};
}
)
;
export
default
SignUp
;
export
default
SignUp
;
web/src/store/index.ts
deleted
100644 → 0
View file @
13f6fa7b
import
{
configureStore
}
from
"@reduxjs/toolkit"
;
import
{
TypedUseSelectorHook
,
useDispatch
,
useSelector
}
from
"react-redux"
;
import
dialogReducer
from
"./reducer/dialog"
;
const
store
=
configureStore
({
reducer
:
{
dialog
:
dialogReducer
,
},
});
type
AppState
=
ReturnType
<
typeof
store
.
getState
>
;
type
AppDispatch
=
typeof
store
.
dispatch
;
export
const
useAppSelector
:
TypedUseSelectorHook
<
AppState
>
=
useSelector
;
export
const
useAppDispatch
:
()
=>
AppDispatch
=
useDispatch
;
export
default
store
;
web/src/store/module/dialog.ts
deleted
100644 → 0
View file @
13f6fa7b
import
{
last
}
from
"lodash-es"
;
import
store
,
{
useAppSelector
}
from
".."
;
import
{
popDialogStack
,
pushDialogStack
,
removeDialog
}
from
"../reducer/dialog"
;
export
const
useDialogStore
=
()
=>
{
const
state
=
useAppSelector
((
state
)
=>
state
.
dialog
);
return
{
state
,
getState
:
()
=>
{
return
store
.
getState
().
dialog
;
},
pushDialogStack
:
(
dialogName
:
string
)
=>
{
store
.
dispatch
(
pushDialogStack
(
dialogName
));
},
popDialogStack
:
()
=>
{
store
.
dispatch
(
popDialogStack
());
},
removeDialog
:
(
dialogName
:
string
)
=>
{
store
.
dispatch
(
removeDialog
(
dialogName
));
},
topDialogStack
:
()
=>
{
return
last
(
store
.
getState
().
dialog
.
dialogStack
);
},
};
};
web/src/store/module/index.ts
deleted
100644 → 0
View file @
13f6fa7b
export
*
from
"./dialog"
;
web/src/store/reducer/dialog.ts
deleted
100644 → 0
View file @
13f6fa7b
import
{
createSlice
,
PayloadAction
}
from
"@reduxjs/toolkit"
;
interface
State
{
dialogStack
:
string
[];
}
const
dialogSlice
=
createSlice
({
name
:
"dialog"
,
initialState
:
{
dialogStack
:
[],
}
as
State
,
reducers
:
{
pushDialogStack
:
(
state
,
action
:
PayloadAction
<
string
>
)
=>
{
return
{
...
state
,
dialogStack
:
[...
state
.
dialogStack
,
action
.
payload
],
};
},
popDialogStack
:
(
state
)
=>
{
return
{
...
state
,
dialogStack
:
state
.
dialogStack
.
slice
(
0
,
state
.
dialogStack
.
length
-
1
),
};
},
removeDialog
:
(
state
,
action
:
PayloadAction
<
string
>
)
=>
{
const
filterDialogStack
=
state
.
dialogStack
.
filter
((
dialogName
)
=>
dialogName
!==
action
.
payload
);
return
{
...
state
,
dialogStack
:
filterDialogStack
,
};
},
},
});
export
const
{
pushDialogStack
,
popDialogStack
,
removeDialog
}
=
dialogSlice
.
actions
;
export
default
dialogSlice
.
reducer
;
web/src/store/v1/inbox.ts
deleted
100644 → 0
View file @
13f6fa7b
import
{
create
}
from
"zustand"
;
import
{
combine
}
from
"zustand/middleware"
;
import
{
inboxServiceClient
}
from
"@/grpcweb"
;
import
{
Inbox
}
from
"@/types/proto/api/v1/inbox_service"
;
interface
State
{
inboxes
:
Inbox
[];
}
const
getDefaultState
=
():
State
=>
({
inboxes
:
[],
});
export
const
useInboxStore
=
create
(
combine
(
getDefaultState
(),
(
set
,
get
)
=>
({
fetchInboxes
:
async
()
=>
{
const
{
inboxes
}
=
await
inboxServiceClient
.
listInboxes
({});
set
({
inboxes
});
return
inboxes
;
},
updateInbox
:
async
(
inbox
:
Partial
<
Inbox
>
,
updateMask
:
string
[])
=>
{
const
updatedInbox
=
await
inboxServiceClient
.
updateInbox
({
inbox
,
updateMask
,
});
const
inboxes
=
get
().
inboxes
;
set
({
inboxes
:
inboxes
.
map
((
i
)
=>
(
i
.
name
===
updatedInbox
.
name
?
updatedInbox
:
i
))
});
return
updatedInbox
;
},
})),
);
web/src/store/v1/index.ts
View file @
5a2f18da
export
*
from
"./user"
;
export
*
from
"./user"
;
export
*
from
"./memo"
;
export
*
from
"./memo"
;
export
*
from
"./inbox"
;
export
*
from
"./resourceName"
;
export
*
from
"./resourceName"
;
export
*
from
"./resource"
;
export
*
from
"./resource"
;
export
*
from
"./workspaceSetting"
;
export
*
from
"./workspaceSetting"
;
...
...
web/src/store/v1/user.ts
View file @
5a2f18da
import
{
create
}
from
"zustand"
;
import
{
create
}
from
"zustand"
;
import
{
combine
}
from
"zustand/middleware"
;
import
{
combine
}
from
"zustand/middleware"
;
import
{
authServiceClient
,
userServiceClient
}
from
"@/grpcweb"
;
import
{
authServiceClient
,
userServiceClient
}
from
"@/grpcweb"
;
import
{
Shortcut
,
User
,
UserSetting
,
User_Role
}
from
"@/types/proto/api/v1/user_service"
;
import
{
User
,
UserSetting
,
User_Role
}
from
"@/types/proto/api/v1/user_service"
;
interface
State
{
interface
State
{
userMapByName
:
Record
<
string
,
User
>
;
userMapByName
:
Record
<
string
,
User
>
;
// The name of current user. Format: `users/${uid}`
// The name of current user. Format: `users/${uid}`
currentUser
?:
string
;
currentUser
?:
string
;
userSetting
?:
UserSetting
;
userSetting
?:
UserSetting
;
shortcuts
:
Shortcut
[];
}
}
const
getDefaultState
=
():
State
=>
({
const
getDefaultState
=
():
State
=>
({
userMapByName
:
{},
userMapByName
:
{},
currentUser
:
undefined
,
currentUser
:
undefined
,
userSetting
:
undefined
,
userSetting
:
undefined
,
shortcuts
:
[],
});
});
const
getDefaultUserSetting
=
()
=>
{
const
getDefaultUserSetting
=
()
=>
{
...
@@ -131,14 +129,6 @@ export const useUserStore = create(
...
@@ -131,14 +129,6 @@ export const useUserStore = create(
set
({
userSetting
:
updatedUserSetting
});
set
({
userSetting
:
updatedUserSetting
});
return
updatedUserSetting
;
return
updatedUserSetting
;
},
},
fetchShortcuts
:
async
()
=>
{
const
{
currentUser
}
=
get
();
if
(
!
currentUser
)
{
return
;
}
const
{
shortcuts
}
=
await
userServiceClient
.
listShortcuts
({
parent
:
currentUser
});
set
({
shortcuts
});
},
})),
})),
);
);
...
...
web/src/store/v2/dialog.ts
0 → 100644
View file @
5a2f18da
import
{
last
}
from
"lodash-es"
;
import
{
makeAutoObservable
}
from
"mobx"
;
const
dialogStore
=
(()
=>
{
const
state
=
makeAutoObservable
<
{
stack
:
string
[];
}
>
({
stack
:
[],
});
const
pushDialog
=
(
name
:
string
)
=>
{
state
.
stack
.
push
(
name
);
};
const
popDialog
=
()
=>
state
.
stack
.
pop
();
const
removeDialog
=
(
name
:
string
)
=>
{
state
.
stack
=
state
.
stack
.
filter
((
n
)
=>
n
!==
name
);
};
const
topDialog
=
last
(
state
.
stack
);
return
{
state
,
topDialog
,
pushDialog
,
popDialog
,
removeDialog
,
};
})();
export
default
dialogStore
;
web/src/store/v2/index.ts
0 → 100644
View file @
5a2f18da
import
userStore
from
"./user"
;
import
workspaceStore
from
"./workspace"
;
export
{
workspaceStore
,
userStore
};
web/src/store/v2/user.ts
0 → 100644
View file @
5a2f18da
import
{
makeAutoObservable
}
from
"mobx"
;
import
{
authServiceClient
,
inboxServiceClient
,
userServiceClient
}
from
"@/grpcweb"
;
import
{
Inbox
}
from
"@/types/proto/api/v1/inbox_service"
;
import
{
Shortcut
,
User
,
UserSetting
}
from
"@/types/proto/api/v1/user_service"
;
interface
LocalState
{
// The name of current user. Format: `users/${uid}`
currentUser
?:
string
;
// userSetting is the setting of the current user.
userSetting
?:
UserSetting
;
// shortcuts is the list of shortcuts of the current user.
shortcuts
:
Shortcut
[];
// inboxes is the list of inboxes of the current user.
inboxes
:
Inbox
[];
// userMapByName is used to cache user information.
// Key is the `user.name` and value is the `User` object.
userMapByName
:
Record
<
string
,
User
>
;
}
const
userStore
=
(()
=>
{
const
state
=
makeAutoObservable
<
LocalState
>
({
shortcuts
:
[],
inboxes
:
[],
userMapByName
:
{},
});
const
getOrFetchUserByName
=
async
(
name
:
string
)
=>
{
const
userMap
=
state
.
userMapByName
;
if
(
userMap
[
name
])
{
return
userMap
[
name
]
as
User
;
}
const
user
=
await
userServiceClient
.
getUser
({
name
:
name
,
});
userMap
[
name
]
=
user
;
state
.
userMapByName
=
userMap
;
return
user
;
};
const
updateUser
=
async
(
user
:
Partial
<
User
>
,
updateMask
:
string
[])
=>
{
const
updatedUser
=
await
userServiceClient
.
updateUser
({
user
,
updateMask
,
});
state
.
userMapByName
=
{
...
state
.
userMapByName
,
[
updatedUser
.
name
]:
updatedUser
,
};
};
const
updateUserSetting
=
async
(
userSetting
:
Partial
<
UserSetting
>
,
updateMask
:
string
[])
=>
{
const
updatedUserSetting
=
await
userServiceClient
.
updateUserSetting
({
setting
:
userSetting
,
updateMask
:
updateMask
,
});
state
.
userSetting
=
UserSetting
.
fromPartial
(
updatedUserSetting
);
};
const
fetchShortcuts
=
async
()
=>
{
if
(
!
state
.
currentUser
)
{
return
;
}
const
{
shortcuts
}
=
await
userServiceClient
.
listShortcuts
({
parent
:
state
.
currentUser
});
state
.
shortcuts
=
shortcuts
;
};
const
fetchInboxes
=
async
()
=>
{
const
{
inboxes
}
=
await
inboxServiceClient
.
listInboxes
({});
state
.
inboxes
=
inboxes
;
console
.
log
(
"inboxes"
,
inboxes
);
};
const
updateInbox
=
async
(
inbox
:
Partial
<
Inbox
>
,
updateMask
:
string
[])
=>
{
const
updatedInbox
=
await
inboxServiceClient
.
updateInbox
({
inbox
,
updateMask
,
});
state
.
inboxes
=
state
.
inboxes
.
map
((
i
)
=>
(
i
.
name
===
updatedInbox
.
name
?
updatedInbox
:
i
));
return
updatedInbox
;
};
return
{
state
,
getOrFetchUserByName
,
updateUser
,
updateUserSetting
,
fetchShortcuts
,
fetchInboxes
,
updateInbox
,
};
})();
export
const
initialUserStore
=
async
()
=>
{
try
{
const
currentUser
=
await
authServiceClient
.
getAuthStatus
({});
const
userSetting
=
await
userServiceClient
.
getUserSetting
({});
Object
.
assign
(
userStore
.
state
,
{
currentUser
:
currentUser
.
name
,
userSetting
:
UserSetting
.
fromPartial
({
...
userSetting
,
}),
userMapByName
:
{
[
currentUser
.
name
]:
currentUser
,
},
});
}
catch
{
// Do nothing.
}
};
export
default
userStore
;
web/src/store/v2/workspace.ts
0 → 100644
View file @
5a2f18da
import
{
makeAutoObservable
}
from
"mobx"
;
import
{
workspaceServiceClient
,
workspaceSettingServiceClient
}
from
"@/grpcweb"
;
import
{
WorkspaceProfile
}
from
"@/types/proto/api/v1/workspace_service"
;
import
{
WorkspaceGeneralSetting
,
WorkspaceSetting
}
from
"@/types/proto/api/v1/workspace_setting_service"
;
import
{
WorkspaceSettingKey
}
from
"@/types/proto/store/workspace_setting"
;
import
{
isValidateLocale
}
from
"@/utils/i18n"
;
import
{
workspaceSettingNamePrefix
}
from
"../v1"
;
interface
LocalState
{
locale
:
string
;
appearance
:
string
;
profile
:
WorkspaceProfile
;
settings
:
WorkspaceSetting
[];
}
const
workspaceStore
=
(()
=>
{
const
state
=
makeAutoObservable
<
LocalState
>
({
locale
:
"en"
,
appearance
:
"system"
,
profile
:
WorkspaceProfile
.
fromPartial
({}),
settings
:
[],
});
const
generalSetting
=
state
.
settings
.
find
((
setting
)
=>
setting
.
name
===
`
${
workspaceSettingNamePrefix
}${
WorkspaceSettingKey
.
GENERAL
}
`
)?.
generalSetting
||
WorkspaceGeneralSetting
.
fromPartial
({});
const
setPartial
=
(
partial
:
Partial
<
LocalState
>
)
=>
{
Object
.
assign
(
state
,
partial
);
};
const
fetchWorkspaceSetting
=
async
(
settingKey
:
WorkspaceSettingKey
)
=>
{
const
setting
=
await
workspaceSettingServiceClient
.
getWorkspaceSetting
({
name
:
`
${
workspaceSettingNamePrefix
}${
settingKey
}
`
});
state
.
settings
.
push
(
setting
);
};
return
{
state
,
generalSetting
,
setPartial
,
fetchWorkspaceSetting
,
};
})();
export
const
initialWorkspaceStore
=
async
()
=>
{
const
workspaceProfile
=
await
workspaceServiceClient
.
getWorkspaceProfile
({});
// Prepare workspace settings.
for
(
const
key
of
[
WorkspaceSettingKey
.
GENERAL
,
WorkspaceSettingKey
.
MEMO_RELATED
])
{
await
workspaceStore
.
fetchWorkspaceSetting
(
key
);
}
const
workspaceGeneralSetting
=
workspaceStore
.
generalSetting
;
let
locale
=
workspaceGeneralSetting
.
customProfile
?.
locale
;
if
(
!
isValidateLocale
(
locale
))
{
locale
=
"en"
;
}
let
appearance
=
workspaceGeneralSetting
.
customProfile
?.
appearance
;
if
(
!
appearance
||
!
[
"system"
,
"light"
,
"dark"
].
includes
(
appearance
))
{
appearance
=
"system"
;
}
workspaceStore
.
setPartial
({
locale
:
locale
,
appearance
:
appearance
,
profile
:
workspaceProfile
,
});
};
export
default
workspaceStore
;
web/src/utils/i18n.ts
View file @
5a2f18da
...
@@ -46,3 +46,8 @@ export const useTranslate = (): TypedT => {
...
@@ -46,3 +46,8 @@ export const useTranslate = (): TypedT => {
const
{
t
}
=
useTranslation
<
Translations
>
();
const
{
t
}
=
useTranslation
<
Translations
>
();
return
t
;
return
t
;
};
};
export
const
isValidateLocale
=
(
locale
:
string
|
undefined
|
null
):
boolean
=>
{
if
(
!
locale
)
return
false
;
return
locales
.
includes
(
locale
);
};
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