mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
refactor(frontend): retire redux
This commit is contained in:
@@ -305,7 +305,9 @@ paths:
|
||||
$ref: '#/definitions/googlerpcStatus'
|
||||
parameters:
|
||||
- name: parent
|
||||
description: "The parent is the owner of the memos.\r\nIf 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
|
||||
required: false
|
||||
type: string
|
||||
@@ -316,12 +318,16 @@ paths:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
description: "A page token, received from a previous `ListMemos` call.\r\nProvide 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
|
||||
required: false
|
||||
type: string
|
||||
- name: state
|
||||
description: "The state of the memos to list.\r\nDefault 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
|
||||
required: false
|
||||
type: string
|
||||
@@ -331,12 +337,16 @@ paths:
|
||||
- ARCHIVED
|
||||
default: STATE_UNSPECIFIED
|
||||
- name: sort
|
||||
description: "What field to sort the results by.\r\nDefault to display_time."
|
||||
description: |-
|
||||
What field to sort the results by.
|
||||
Default to display_time.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: direction
|
||||
description: "The direction to sort the results by.\r\nDefault to DESC."
|
||||
description: |-
|
||||
The direction to sort the results by.
|
||||
Default to DESC.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
@@ -346,12 +356,16 @@ paths:
|
||||
- DESC
|
||||
default: DIRECTION_UNSPECIFIED
|
||||
- name: filter
|
||||
description: "Filter is a CEL expression to filter memos.\r\nRefer to `Shortcut.filter`."
|
||||
description: |-
|
||||
Filter is a CEL expression to filter memos.
|
||||
Refer to `Shortcut.filter`.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: oldFilter
|
||||
description: "[Deprecated] Old filter contains some specific conditions to filter memos.\r\nFormat: \"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
|
||||
required: false
|
||||
type: string
|
||||
@@ -396,7 +410,9 @@ paths:
|
||||
$ref: '#/definitions/googlerpcStatus'
|
||||
parameters:
|
||||
- name: id
|
||||
description: "The id of the reaction.\r\nRefer to the `Reaction.id`."
|
||||
description: |-
|
||||
The id of the reaction.
|
||||
Refer to the `Reaction.id`.
|
||||
in: path
|
||||
required: true
|
||||
type: integer
|
||||
@@ -810,13 +826,17 @@ paths:
|
||||
$ref: '#/definitions/googlerpcStatus'
|
||||
parameters:
|
||||
- name: memo.name
|
||||
description: "The name of the memo.\r\nFormat: 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
|
||||
required: true
|
||||
type: string
|
||||
pattern: memos/[^/]+
|
||||
- name: memo
|
||||
description: "The memo to update.\r\nThe `name` field is required."
|
||||
description: |-
|
||||
The memo to update.
|
||||
The `name` field is required.
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
@@ -826,7 +846,9 @@ paths:
|
||||
$ref: '#/definitions/v1State'
|
||||
creator:
|
||||
type: string
|
||||
title: "The name of the creator.\r\nFormat: users/{user}"
|
||||
title: |-
|
||||
The name of the creator.
|
||||
Format: users/{user}
|
||||
createTime:
|
||||
type: string
|
||||
format: date-time
|
||||
@@ -874,7 +896,9 @@ paths:
|
||||
readOnly: true
|
||||
parent:
|
||||
type: string
|
||||
title: "The name of the parent memo.\r\nFormat: memos/{id}"
|
||||
title: |-
|
||||
The name of the parent memo.
|
||||
Format: memos/{id}
|
||||
readOnly: true
|
||||
snippet:
|
||||
type: string
|
||||
@@ -883,7 +907,9 @@ paths:
|
||||
location:
|
||||
$ref: '#/definitions/apiv1Location'
|
||||
description: The location of the memo.
|
||||
title: "The memo to update.\r\nThe `name` field is required."
|
||||
title: |-
|
||||
The memo to update.
|
||||
The `name` field is required.
|
||||
required:
|
||||
- memo
|
||||
tags:
|
||||
@@ -1440,7 +1466,9 @@ paths:
|
||||
$ref: '#/definitions/googlerpcStatus'
|
||||
parameters:
|
||||
- name: parent
|
||||
description: "The parent is the owner of the memos.\r\nIf 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
|
||||
required: true
|
||||
type: string
|
||||
@@ -1452,12 +1480,16 @@ paths:
|
||||
type: integer
|
||||
format: int32
|
||||
- name: pageToken
|
||||
description: "A page token, received from a previous `ListMemos` call.\r\nProvide 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
|
||||
required: false
|
||||
type: string
|
||||
- name: state
|
||||
description: "The state of the memos to list.\r\nDefault 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
|
||||
required: false
|
||||
type: string
|
||||
@@ -1467,12 +1499,16 @@ paths:
|
||||
- ARCHIVED
|
||||
default: STATE_UNSPECIFIED
|
||||
- name: sort
|
||||
description: "What field to sort the results by.\r\nDefault to display_time."
|
||||
description: |-
|
||||
What field to sort the results by.
|
||||
Default to display_time.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: direction
|
||||
description: "The direction to sort the results by.\r\nDefault to DESC."
|
||||
description: |-
|
||||
The direction to sort the results by.
|
||||
Default to DESC.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
@@ -1482,12 +1518,16 @@ paths:
|
||||
- DESC
|
||||
default: DIRECTION_UNSPECIFIED
|
||||
- name: filter
|
||||
description: "Filter is a CEL expression to filter memos.\r\nRefer to `Shortcut.filter`."
|
||||
description: |-
|
||||
Filter is a CEL expression to filter memos.
|
||||
Refer to `Shortcut.filter`.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
- name: oldFilter
|
||||
description: "[Deprecated] Old filter contains some specific conditions to filter memos.\r\nFormat: \"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
|
||||
required: false
|
||||
type: string
|
||||
@@ -1625,7 +1665,9 @@ paths:
|
||||
$ref: '#/definitions/googlerpcStatus'
|
||||
parameters:
|
||||
- name: parent
|
||||
description: "The parent, who owns the tags.\r\nFormat: 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
|
||||
required: true
|
||||
type: string
|
||||
@@ -1656,7 +1698,9 @@ paths:
|
||||
$ref: '#/definitions/googlerpcStatus'
|
||||
parameters:
|
||||
- name: parent
|
||||
description: "The parent, who owns the tags.\r\nFormat: 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
|
||||
required: true
|
||||
type: string
|
||||
@@ -1771,7 +1815,9 @@ paths:
|
||||
$ref: '#/definitions/googlerpcStatus'
|
||||
parameters:
|
||||
- name: user.name
|
||||
description: "The name of the user.\r\nFormat: 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
|
||||
required: true
|
||||
type: string
|
||||
@@ -2106,13 +2152,17 @@ definitions:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: "The name of the memo.\r\nFormat: 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
|
||||
state:
|
||||
$ref: '#/definitions/v1State'
|
||||
creator:
|
||||
type: string
|
||||
title: "The name of the creator.\r\nFormat: users/{user}"
|
||||
title: |-
|
||||
The name of the creator.
|
||||
Format: users/{user}
|
||||
createTime:
|
||||
type: string
|
||||
format: date-time
|
||||
@@ -2160,7 +2210,9 @@ definitions:
|
||||
readOnly: true
|
||||
parent:
|
||||
type: string
|
||||
title: "The name of the parent memo.\r\nFormat: memos/{id}"
|
||||
title: |-
|
||||
The name of the parent memo.
|
||||
Format: memos/{id}
|
||||
readOnly: true
|
||||
snippet:
|
||||
type: string
|
||||
@@ -2738,7 +2790,9 @@ definitions:
|
||||
$ref: '#/definitions/apiv1Memo'
|
||||
nextPageToken:
|
||||
type: string
|
||||
description: "A token, which can be sent as `page_token` to retrieve the next page.\r\nIf 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:
|
||||
type: object
|
||||
properties:
|
||||
@@ -3156,7 +3210,9 @@ definitions:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: "The name of the user.\r\nFormat: 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:
|
||||
$ref: '#/definitions/UserRole'
|
||||
username:
|
||||
@@ -3205,7 +3261,9 @@ definitions:
|
||||
items:
|
||||
type: string
|
||||
format: date-time
|
||||
description: "The timestamps when the memos were displayed.\r\nWe 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:
|
||||
$ref: '#/definitions/UserStatsMemoTypeStats'
|
||||
description: The stats of memo types.
|
||||
@@ -3214,7 +3272,9 @@ definitions:
|
||||
additionalProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
title: "The count of tags.\r\nFormat: \"tag1\": 1, \"tag2\": 2"
|
||||
title: |-
|
||||
The count of tags.
|
||||
Format: "tag1": 1, "tag2": 2
|
||||
v1Visibility:
|
||||
type: string
|
||||
enum:
|
||||
|
@@ -18,7 +18,6 @@
|
||||
"@matejmazur/react-katex": "^3.1.3",
|
||||
"@mui/joy": "5.0.0-beta.51",
|
||||
"@radix-ui/react-popover": "^1.1.5",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"@usememos/mui": "0.0.1-alpha.26",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
@@ -32,6 +31,8 @@
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "^0.453.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"mobx": "^6.13.6",
|
||||
"mobx-react-lite": "^4.1.0",
|
||||
"react": "^18.3.1",
|
||||
"react-datepicker": "^7.5.0",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -39,7 +40,6 @@
|
||||
"react-hot-toast": "^2.5.1",
|
||||
"react-i18next": "^15.4.0",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^7.1.1",
|
||||
"react-simple-pull-to-refresh": "^1.3.3",
|
||||
"react-use": "^17.6.0",
|
||||
@@ -82,4 +82,4 @@
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.0.6"
|
||||
}
|
||||
}
|
||||
}
|
107
web/pnpm-lock.yaml
generated
107
web/pnpm-lock.yaml
generated
@@ -35,9 +35,6 @@ importers:
|
||||
'@radix-ui/react-popover':
|
||||
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)
|
||||
'@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':
|
||||
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)
|
||||
@@ -77,6 +74,12 @@ importers:
|
||||
mermaid:
|
||||
specifier: ^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:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
@@ -98,9 +101,6 @@ importers:
|
||||
react-leaflet:
|
||||
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)
|
||||
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:
|
||||
specifier: ^7.1.1
|
||||
version: 7.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -1095,17 +1095,6 @@ packages:
|
||||
react: ^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':
|
||||
resolution: {integrity: sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==}
|
||||
cpu: [arm]
|
||||
@@ -1368,9 +1357,6 @@ packages:
|
||||
'@types/trusted-types@2.0.7':
|
||||
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':
|
||||
resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
|
||||
|
||||
@@ -2678,6 +2664,22 @@ packages:
|
||||
mlly@1.7.3:
|
||||
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:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@@ -2981,18 +2983,6 @@ packages:
|
||||
react: ^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:
|
||||
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3073,14 +3063,6 @@ packages:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
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:
|
||||
resolution: {integrity: sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3092,9 +3074,6 @@ packages:
|
||||
resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
reselect@5.1.1:
|
||||
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
@@ -4460,16 +4439,6 @@ snapshots:
|
||||
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':
|
||||
optional: true
|
||||
|
||||
@@ -4723,8 +4692,6 @@ snapshots:
|
||||
'@types/trusted-types@2.0.7':
|
||||
optional: true
|
||||
|
||||
'@types/use-sync-external-store@0.0.6': {}
|
||||
|
||||
'@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)':
|
||||
@@ -5914,7 +5881,8 @@ snapshots:
|
||||
image-size@0.5.5:
|
||||
optional: true
|
||||
|
||||
immer@10.1.1: {}
|
||||
immer@10.1.1:
|
||||
optional: true
|
||||
|
||||
import-fresh@3.3.0:
|
||||
dependencies:
|
||||
@@ -6275,6 +6243,16 @@ snapshots:
|
||||
pkg-types: 1.3.0
|
||||
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: {}
|
||||
|
||||
mz@2.7.0:
|
||||
@@ -6586,15 +6564,6 @@ snapshots:
|
||||
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-remove-scroll-bar@2.3.8(@types/react@18.3.18)(react@18.3.1):
|
||||
@@ -6681,12 +6650,6 @@ snapshots:
|
||||
dependencies:
|
||||
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:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -6707,8 +6670,6 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
set-function-name: 2.0.2
|
||||
|
||||
reselect@5.1.1: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
|
@@ -1,28 +1,19 @@
|
||||
import { useColorScheme } from "@mui/joy";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import useLocalStorage from "react-use/lib/useLocalStorage";
|
||||
import { getSystemColorScheme } from "./helpers/utils";
|
||||
import useNavigateTo from "./hooks/useNavigateTo";
|
||||
import { useCommonContext } from "./layouts/CommonContextProvider";
|
||||
import { useUserStore, useWorkspaceSettingStore } from "./store/v1";
|
||||
import { WorkspaceGeneralSetting, WorkspaceSettingKey } from "./types/proto/store/workspace_setting";
|
||||
import { userStore, workspaceStore } from "./store/v2";
|
||||
|
||||
const App = () => {
|
||||
const App = observer(() => {
|
||||
const { i18n } = useTranslation();
|
||||
const navigateTo = useNavigateTo();
|
||||
const { mode, setMode } = useColorScheme();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const userStore = useUserStore();
|
||||
const commonContext = useCommonContext();
|
||||
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({});
|
||||
const workspaceProfile = workspaceStore.state.profile;
|
||||
const userSetting = userStore.state.userSetting;
|
||||
const workspaceGeneralSetting = workspaceStore.generalSetting;
|
||||
|
||||
// Redirect to sign up page if no instance owner.
|
||||
useEffect(() => {
|
||||
@@ -78,7 +69,7 @@ const App = () => {
|
||||
}, [workspaceGeneralSetting.customProfile]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentLocale = commonContext.locale;
|
||||
const currentLocale = workspaceStore.state.locale;
|
||||
i18n.changeLanguage(currentLocale);
|
||||
document.documentElement.setAttribute("lang", currentLocale);
|
||||
if (["ar", "fa"].includes(currentLocale)) {
|
||||
@@ -86,17 +77,15 @@ const App = () => {
|
||||
} else {
|
||||
document.documentElement.setAttribute("dir", "ltr");
|
||||
}
|
||||
setLocale(currentLocale);
|
||||
}, [commonContext.locale]);
|
||||
}, [workspaceStore.state.locale]);
|
||||
|
||||
useEffect(() => {
|
||||
let currentAppearance = commonContext.appearance as Appearance;
|
||||
let currentAppearance = workspaceStore.state.appearance as Appearance;
|
||||
if (currentAppearance === "system") {
|
||||
currentAppearance = getSystemColorScheme();
|
||||
}
|
||||
setMode(currentAppearance);
|
||||
setAppearance(currentAppearance);
|
||||
}, [commonContext.appearance]);
|
||||
}, [workspaceStore.state.appearance]);
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
@@ -112,11 +101,13 @@ const App = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
commonContext.setLocale(userSetting.locale);
|
||||
commonContext.setAppearance(userSetting.appearance);
|
||||
workspaceStore.setPartial({
|
||||
locale: userSetting.locale || workspaceStore.state.locale,
|
||||
appearance: userSetting.appearance || workspaceStore.state.appearance,
|
||||
});
|
||||
}, [userSetting?.locale, userSetting?.appearance]);
|
||||
|
||||
return <Outlet />;
|
||||
};
|
||||
});
|
||||
|
||||
export default App;
|
||||
|
@@ -78,7 +78,7 @@ const ActivityCalendar = (props: Props) => {
|
||||
return (
|
||||
<div
|
||||
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}
|
||||
</div>
|
||||
@@ -101,7 +101,7 @@ const ActivityCalendar = (props: Props) => {
|
||||
<Tooltip className="shrink-0" key={`${date}-${index}`} title={tooltipText} placement="top" arrow>
|
||||
<div
|
||||
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",
|
||||
item.isCurrentMonth && getCellAdditionalStyles(count, maxCount),
|
||||
item.isCurrentMonth && isToday && "border-zinc-400",
|
||||
|
@@ -2,7 +2,7 @@ import { Button, Input } from "@usememos/mui";
|
||||
import { XIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useUserStore } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
import { User } from "@/types/proto/api/v1/user_service";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { generateDialog } from "./Dialog";
|
||||
@@ -14,7 +14,6 @@ interface Props extends DialogProps {
|
||||
const ChangeMemberPasswordDialog: React.FC<Props> = (props: Props) => {
|
||||
const { user, destroy } = props;
|
||||
const t = useTranslate();
|
||||
const userStore = useUserStore();
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [newPasswordAgain, setNewPasswordAgain] = useState("");
|
||||
|
||||
|
@@ -6,7 +6,7 @@ import { toast } from "react-hot-toast";
|
||||
import { userServiceClient } from "@/grpcweb";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { useUserStore } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
import { Shortcut } from "@/types/proto/api/v1/user_service";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { generateUUID } from "@/utils/uuid";
|
||||
@@ -20,7 +20,6 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
|
||||
const { destroy } = props;
|
||||
const t = useTranslate();
|
||||
const user = useCurrentUser();
|
||||
const userStore = useUserStore();
|
||||
const [shortcut, setShortcut] = useState(Shortcut.fromPartial({ ...props.shortcut }));
|
||||
const requestState = useLoading(false);
|
||||
const isCreating = !props.shortcut;
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import { CssVarsProvider } from "@mui/joy";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { Provider } from "react-redux";
|
||||
import CommonContextProvider from "@/layouts/CommonContextProvider";
|
||||
import store from "@/store";
|
||||
import { useDialogStore } from "@/store/module";
|
||||
import dialogStore from "@/store/v2/dialog";
|
||||
import theme from "@/theme";
|
||||
import { cn } from "@/utils";
|
||||
import "@/less/base-dialog.less";
|
||||
@@ -19,17 +17,16 @@ interface Props extends DialogConfig, DialogProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const BaseDialog: React.FC<Props> = (props: Props) => {
|
||||
const BaseDialog = observer((props: Props) => {
|
||||
const { children, className, clickSpaceDestroy, dialogName, destroy } = props;
|
||||
const dialogStore = useDialogStore();
|
||||
const dialogContainerRef = useRef<HTMLDivElement>(null);
|
||||
const dialogIndex = dialogStore.state.dialogStack.findIndex((item) => item === dialogName);
|
||||
const dialogIndex = dialogStore.state.stack.findIndex((item) => item === dialogName);
|
||||
|
||||
useEffect(() => {
|
||||
dialogStore.pushDialogStack(dialogName);
|
||||
dialogStore.pushDialog(dialogName);
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.code === "Escape") {
|
||||
if (dialogName === dialogStore.topDialogStack()) {
|
||||
if (dialogName === dialogStore.topDialog) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
@@ -62,7 +59,7 @@ const BaseDialog: React.FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export function generateDialog<T extends DialogProps>(
|
||||
config: DialogConfig,
|
||||
@@ -87,19 +84,15 @@ export function generateDialog<T extends DialogProps>(
|
||||
destroy: cbs.destroy,
|
||||
} as T;
|
||||
|
||||
const Fragment = (
|
||||
<Provider store={store}>
|
||||
<CssVarsProvider theme={theme}>
|
||||
<CommonContextProvider>
|
||||
<BaseDialog destroy={cbs.destroy} clickSpaceDestroy={true} {...config}>
|
||||
<DialogComponent {...dialogProps} />
|
||||
</BaseDialog>
|
||||
</CommonContextProvider>
|
||||
</CssVarsProvider>
|
||||
</Provider>
|
||||
);
|
||||
const Fragment = observer(() => (
|
||||
<CssVarsProvider theme={theme}>
|
||||
<BaseDialog destroy={cbs.destroy} clickSpaceDestroy={true} {...config}>
|
||||
<DialogComponent {...dialogProps} />
|
||||
</BaseDialog>
|
||||
</CssVarsProvider>
|
||||
));
|
||||
|
||||
dialog.render(Fragment);
|
||||
dialog.render(<Fragment />);
|
||||
|
||||
return cbs;
|
||||
}
|
||||
|
@@ -1,20 +1,21 @@
|
||||
import { Dropdown, Menu, MenuButton, MenuItem, Tooltip } from "@mui/joy";
|
||||
import { Edit3Icon, MoreVerticalIcon, TrashIcon, PlusIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { userServiceClient } from "@/grpcweb";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
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 { cn } from "@/utils";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import showCreateShortcutDialog from "../CreateShortcutDialog";
|
||||
|
||||
const ShortcutsSection = () => {
|
||||
const ShortcutsSection = observer(() => {
|
||||
const t = useTranslate();
|
||||
const user = useCurrentUser();
|
||||
const userStore = useUserStore();
|
||||
const memoFilterStore = useMemoFilterStore();
|
||||
const shortcuts = userStore.getState().shortcuts;
|
||||
const shortcuts = userStore.state.shortcuts;
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
await userStore.fetchShortcuts();
|
||||
@@ -71,6 +72,6 @@ const ShortcutsSection = () => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default ShortcutsSection;
|
||||
|
@@ -5,7 +5,8 @@ import toast from "react-hot-toast";
|
||||
import { activityServiceClient } from "@/grpcweb";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
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 { Memo } from "@/types/proto/api/v1/memo_service";
|
||||
import { User } from "@/types/proto/api/v1/user_service";
|
||||
@@ -19,9 +20,7 @@ interface Props {
|
||||
const MemoCommentMessage = ({ inbox }: Props) => {
|
||||
const t = useTranslate();
|
||||
const navigateTo = useNavigateTo();
|
||||
const inboxStore = useInboxStore();
|
||||
const memoStore = useMemoStore();
|
||||
const userStore = useUserStore();
|
||||
const [relatedMemo, setRelatedMemo] = useState<Memo | undefined>(undefined);
|
||||
const [sender, setSender] = useState<User | undefined>(undefined);
|
||||
const [initialized, setInitialized] = useState<boolean>(false);
|
||||
@@ -58,7 +57,7 @@ const MemoCommentMessage = ({ inbox }: Props) => {
|
||||
};
|
||||
|
||||
const handleArchiveMessage = async (silence = false) => {
|
||||
await inboxStore.updateInbox(
|
||||
await userStore.updateInbox(
|
||||
{
|
||||
name: inbox.name,
|
||||
status: Inbox_Status.ARCHIVED,
|
||||
|
@@ -3,7 +3,8 @@ import { ArrowUpIcon, InboxIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
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 { Inbox, Inbox_Status } from "@/types/proto/api/v1/inbox_service";
|
||||
import { cn } from "@/utils";
|
||||
@@ -15,7 +16,6 @@ interface Props {
|
||||
|
||||
const VersionUpdateMessage = ({ inbox }: Props) => {
|
||||
const t = useTranslate();
|
||||
const inboxStore = useInboxStore();
|
||||
const [activity, setActivity] = useState<Activity | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -43,7 +43,7 @@ const VersionUpdateMessage = ({ inbox }: Props) => {
|
||||
};
|
||||
|
||||
const handleArchiveMessage = async (silence = false) => {
|
||||
await inboxStore.updateInbox(
|
||||
await userStore.updateInbox(
|
||||
{
|
||||
name: inbox.name,
|
||||
status: Inbox_Status.ARCHIVED,
|
||||
|
@@ -2,6 +2,7 @@ import { Select, Option, Divider } from "@mui/joy";
|
||||
import { Button } from "@usememos/mui";
|
||||
import { isEqual } from "lodash-es";
|
||||
import { LoaderIcon, SendIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
@@ -13,7 +14,8 @@ import { TAB_SPACE_WIDTH } from "@/helpers/consts";
|
||||
import { isValidUrl } from "@/helpers/utils";
|
||||
import useAsyncEffect from "@/hooks/useAsyncEffect";
|
||||
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 { Location, Memo, Visibility } from "@/types/proto/api/v1/memo_service";
|
||||
import { Resource } from "@/types/proto/api/v1/resource_service";
|
||||
@@ -57,12 +59,11 @@ interface State {
|
||||
isComposing: boolean;
|
||||
}
|
||||
|
||||
const MemoEditor = (props: Props) => {
|
||||
const MemoEditor = observer((props: Props) => {
|
||||
const { className, cacheKey, memoName, parentMemoName, autoFocus, onConfirm, onCancel } = props;
|
||||
const t = useTranslate();
|
||||
const { i18n } = useTranslation();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const userStore = useUserStore();
|
||||
const memoStore = useMemoStore();
|
||||
const resourceStore = useResourceStore();
|
||||
const currentUser = useCurrentUser();
|
||||
@@ -78,7 +79,7 @@ const MemoEditor = (props: Props) => {
|
||||
const [displayTime, setDisplayTime] = useState<Date | undefined>();
|
||||
const [hasContent, setHasContent] = useState<boolean>(false);
|
||||
const editorRef = useRef<EditorRefActions>(null);
|
||||
const userSetting = userStore.userSetting as UserSetting;
|
||||
const userSetting = userStore.state.userSetting as UserSetting;
|
||||
const contentCacheKey = `${currentUser.name}-${cacheKey || ""}`;
|
||||
const [contentCache, setContentCache] = useLocalStorage<string>(contentCacheKey, "");
|
||||
const referenceRelations = memoName
|
||||
@@ -521,6 +522,6 @@ const MemoEditor = (props: Props) => {
|
||||
</div>
|
||||
</MemoEditorContext.Provider>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default MemoEditor;
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { Tooltip } from "@mui/joy";
|
||||
import { ArchiveIcon, BellIcon, Globe2Icon, HomeIcon, LogInIcon, PaperclipIcon, SettingsIcon, SmileIcon, User2Icon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect } from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { Routes } from "@/router";
|
||||
import { useInboxStore } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
import { Inbox_Status } from "@/types/proto/api/v1/inbox_service";
|
||||
import { cn } from "@/utils";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
@@ -22,30 +23,18 @@ interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Navigation = (props: Props) => {
|
||||
const Navigation = observer((props: Props) => {
|
||||
const { collapsed, className } = props;
|
||||
const t = useTranslate();
|
||||
const user = useCurrentUser();
|
||||
const inboxStore = useInboxStore();
|
||||
const hasUnreadInbox = inboxStore.inboxes.some((inbox) => inbox.status === Inbox_Status.UNREAD);
|
||||
const hasUnreadInbox = userStore.state.inboxes.some((inbox) => inbox.status === Inbox_Status.UNREAD);
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
inboxStore.fetchInboxes();
|
||||
// Fetch inboxes every 5 minutes.
|
||||
const timer = setInterval(
|
||||
async () => {
|
||||
await inboxStore.fetchInboxes();
|
||||
},
|
||||
1000 * 60 * 5,
|
||||
);
|
||||
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
};
|
||||
userStore.fetchInboxes();
|
||||
}, []);
|
||||
|
||||
const homeNavLink: NavLinkItem = {
|
||||
@@ -147,6 +136,6 @@ const Navigation = (props: Props) => {
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default Navigation;
|
||||
|
@@ -1,19 +1,19 @@
|
||||
import { Button, Checkbox, Input } from "@usememos/mui";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ClientError } from "nice-grpc-web";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { authServiceClient } from "@/grpcweb";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { useCommonContext } from "@/layouts/CommonContextProvider";
|
||||
import { useUserStore } from "@/store/v1";
|
||||
import { workspaceStore } from "@/store/v2";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const PasswordSignInForm = () => {
|
||||
const PasswordSignInForm = observer(() => {
|
||||
const t = useTranslate();
|
||||
const navigateTo = useNavigateTo();
|
||||
const commonContext = useCommonContext();
|
||||
const userStore = useUserStore();
|
||||
const actionBtnLoadingState = useLoading(false);
|
||||
const [username, setUsername] = useState("");
|
||||
@@ -21,11 +21,11 @@ const PasswordSignInForm = () => {
|
||||
const [remember, setRemember] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (commonContext.profile.mode === "demo") {
|
||||
if (workspaceStore.state.profile.mode === "demo") {
|
||||
setUsername("yourselfhosted");
|
||||
setPassword("yourselfhosted");
|
||||
}
|
||||
}, [commonContext.profile.mode]);
|
||||
}, [workspaceStore.state.profile.mode]);
|
||||
|
||||
const handleUsernameInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const text = e.target.value as string;
|
||||
@@ -117,6 +117,6 @@ const PasswordSignInForm = () => {
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default PasswordSignInForm;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { Divider, Option, Select } from "@mui/joy";
|
||||
import { useCommonContext } from "@/layouts/CommonContextProvider";
|
||||
import { useUserStore } from "@/store/v1";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { userStore, workspaceStore } from "@/store/v2";
|
||||
import { Visibility } from "@/types/proto/api/v1/memo_service";
|
||||
import { UserSetting } from "@/types/proto/api/v1/user_service";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
@@ -10,14 +10,12 @@ import LocaleSelect from "../LocaleSelect";
|
||||
import VisibilityIcon from "../VisibilityIcon";
|
||||
import WebhookSection from "./WebhookSection";
|
||||
|
||||
const PreferencesSection = () => {
|
||||
const PreferencesSection = observer(() => {
|
||||
const t = useTranslate();
|
||||
const commonContext = useCommonContext();
|
||||
const userStore = useUserStore();
|
||||
const setting = userStore.userSetting as UserSetting;
|
||||
const setting = userStore.state.userSetting as UserSetting;
|
||||
|
||||
const handleLocaleSelectChange = async (locale: Locale) => {
|
||||
commonContext.setLocale(locale);
|
||||
workspaceStore.setPartial({ locale });
|
||||
await userStore.updateUserSetting(
|
||||
{
|
||||
locale,
|
||||
@@ -27,7 +25,7 @@ const PreferencesSection = () => {
|
||||
};
|
||||
|
||||
const handleAppearanceSelectChange = async (appearance: Appearance) => {
|
||||
commonContext.setAppearance(appearance);
|
||||
workspaceStore.setPartial({ appearance });
|
||||
await userStore.updateUserSetting(
|
||||
{
|
||||
appearance,
|
||||
@@ -84,6 +82,6 @@ const PreferencesSection = () => {
|
||||
<WebhookSection />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default PreferencesSection;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { Tooltip } from "@mui/joy";
|
||||
import dayjs from "dayjs";
|
||||
import { countBy } from "lodash-es";
|
||||
import { CheckCircleIcon, ChevronDownIcon, ChevronUpIcon, Code2Icon, LinkIcon, ListTodoIcon } from "lucide-react";
|
||||
import { CheckCircleIcon, ChevronRightIcon, ChevronLeftIcon, Code2Icon, LinkIcon, ListTodoIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
@@ -60,7 +60,7 @@ const StatisticsView = () => {
|
||||
showMonthYearPicker
|
||||
showFullMonthYearPicker
|
||||
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" })}
|
||||
</span>
|
||||
}
|
||||
@@ -73,13 +73,13 @@ const StatisticsView = () => {
|
||||
className="cursor-pointer hover:opacity-80"
|
||||
onClick={() => setVisibleMonthString(dayjs(visibleMonthString).subtract(1, "month").format("YYYY-MM"))}
|
||||
>
|
||||
<ChevronUpIcon className="w-5 h-auto shrink-0 opacity-40" />
|
||||
<ChevronLeftIcon className="w-5 h-auto shrink-0 opacity-40" />
|
||||
</span>
|
||||
<span
|
||||
className="cursor-pointer hover:opacity-80"
|
||||
onClick={() => setVisibleMonthString(dayjs(visibleMonthString).add(1, "month").format("YYYY-MM"))}
|
||||
>
|
||||
<ChevronDownIcon className="w-5 h-auto shrink-0 opacity-40" />
|
||||
<ChevronRightIcon className="w-5 h-auto shrink-0 opacity-40" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,15 +0,0 @@
|
||||
(() => {
|
||||
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;
|
@@ -1,8 +1,7 @@
|
||||
import { useUserStore } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
|
||||
const useCurrentUser = () => {
|
||||
const userStore = useUserStore();
|
||||
return userStore.getUserByName(userStore.currentUser || "");
|
||||
return userStore.state.userMapByName[userStore.state.currentUser || ""];
|
||||
};
|
||||
|
||||
export default useCurrentUser;
|
||||
|
@@ -1,84 +0,0 @@
|
||||
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;
|
@@ -2,30 +2,30 @@ import "@github/relative-time-element";
|
||||
import { CssVarsProvider } from "@mui/joy";
|
||||
import "@usememos/mui/dist/index.css";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import { Provider } from "react-redux";
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
import "./css/tailwind.css";
|
||||
import "./helpers/polyfill";
|
||||
import "./i18n";
|
||||
import CommonContextProvider from "./layouts/CommonContextProvider";
|
||||
import "./less/highlight.less";
|
||||
import router from "./router";
|
||||
import store from "./store";
|
||||
import { initialUserStore } from "./store/v2/user";
|
||||
import { initialWorkspaceStore } from "./store/v2/workspace";
|
||||
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 () => {
|
||||
await initialWorkspaceStore();
|
||||
await initialUserStore();
|
||||
|
||||
const container = document.getElementById("root");
|
||||
const root = createRoot(container as HTMLElement);
|
||||
root.render(
|
||||
<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>,
|
||||
);
|
||||
root.render(<Main />);
|
||||
})();
|
||||
|
@@ -1,23 +1,23 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import AppearanceSelect from "@/components/AppearanceSelect";
|
||||
import LocaleSelect from "@/components/LocaleSelect";
|
||||
import PasswordSignInForm from "@/components/PasswordSignInForm";
|
||||
import { useCommonContext } from "@/layouts/CommonContextProvider";
|
||||
import { useWorkspaceSettingStore } from "@/store/v1";
|
||||
import { workspaceStore } from "@/store/v2";
|
||||
import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service";
|
||||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
||||
|
||||
const AdminSignIn = () => {
|
||||
const commonContext = useCommonContext();
|
||||
const AdminSignIn = observer(() => {
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const workspaceGeneralSetting =
|
||||
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.GENERAL).generalSetting || WorkspaceGeneralSetting.fromPartial({});
|
||||
|
||||
const handleLocaleSelectChange = (locale: Locale) => {
|
||||
commonContext.setLocale(locale);
|
||||
workspaceStore.setPartial({ locale });
|
||||
};
|
||||
|
||||
const handleAppearanceSelectChange = (appearance: Appearance) => {
|
||||
commonContext.setAppearance(appearance);
|
||||
workspaceStore.setPartial({ appearance });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -33,11 +33,11 @@ const AdminSignIn = () => {
|
||||
<PasswordSignInForm />
|
||||
</div>
|
||||
<div className="mt-4 flex flex-row items-center justify-center w-full gap-2">
|
||||
<LocaleSelect value={commonContext.locale} onChange={handleLocaleSelectChange} />
|
||||
<AppearanceSelect value={commonContext.appearance as Appearance} onChange={handleAppearanceSelectChange} />
|
||||
<LocaleSelect value={workspaceStore.state.locale} onChange={handleLocaleSelectChange} />
|
||||
<AppearanceSelect value={workspaceStore.state.appearance as Appearance} onChange={handleAppearanceSelectChange} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default AdminSignIn;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import dayjs from "dayjs";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useMemo } from "react";
|
||||
import { HomeSidebar, HomeSidebarDrawer } from "@/components/HomeSidebar";
|
||||
import MemoEditor from "@/components/MemoEditor";
|
||||
@@ -7,17 +8,17 @@ import MobileHeader from "@/components/MobileHeader";
|
||||
import PagedMemoList from "@/components/PagedMemoList";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
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 { Memo } from "@/types/proto/api/v1/memo_service";
|
||||
import { cn } from "@/utils";
|
||||
|
||||
const Home = () => {
|
||||
const Home = observer(() => {
|
||||
const { md, lg } = useResponsiveWidth();
|
||||
const user = useCurrentUser();
|
||||
const userStore = useUserStore();
|
||||
const memoFilterStore = useMemoFilterStore();
|
||||
const selectedShortcut = userStore.shortcuts.find((shortcut) => shortcut.id === memoFilterStore.shortcut);
|
||||
const selectedShortcut = userStore.state.shortcuts.find((shortcut) => shortcut.id === memoFilterStore.shortcut);
|
||||
|
||||
const memoListFilter = useMemo(() => {
|
||||
const conditions = [];
|
||||
@@ -95,6 +96,6 @@ const Home = () => {
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default Home;
|
||||
|
@@ -1,17 +1,17 @@
|
||||
import { BellIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect } from "react";
|
||||
import Empty from "@/components/Empty";
|
||||
import MemoCommentMessage from "@/components/Inbox/MemoCommentMessage";
|
||||
import VersionUpdateMessage from "@/components/Inbox/VersionUpdateMessage";
|
||||
import MobileHeader from "@/components/MobileHeader";
|
||||
import { useInboxStore } from "@/store/v1";
|
||||
import { userStore } from "@/store/v2";
|
||||
import { Inbox_Status, Inbox_Type } from "@/types/proto/api/v1/inbox_service";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const Inboxes = () => {
|
||||
const Inboxes = observer(() => {
|
||||
const t = useTranslate();
|
||||
const inboxStore = useInboxStore();
|
||||
const inboxes = inboxStore.inboxes.sort((a, b) => {
|
||||
const inboxes = userStore.state.inboxes.sort((a, b) => {
|
||||
if (a.status === b.status) {
|
||||
return 0;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ const Inboxes = () => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
inboxStore.fetchInboxes();
|
||||
userStore.fetchInboxes();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -55,6 +55,6 @@ const Inboxes = () => {
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default Inboxes;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Option, Select } from "@mui/joy";
|
||||
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 { useLocation } from "react-router-dom";
|
||||
import MobileHeader from "@/components/MobileHeader";
|
||||
@@ -12,8 +13,8 @@ import SectionMenuItem from "@/components/Settings/SectionMenuItem";
|
||||
import StorageSection from "@/components/Settings/StorageSection";
|
||||
import WorkspaceSection from "@/components/Settings/WorkspaceSection";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useCommonContext } from "@/layouts/CommonContextProvider";
|
||||
import { useWorkspaceSettingStore } from "@/store/v1";
|
||||
import { workspaceStore } from "@/store/v2";
|
||||
import { User_Role } from "@/types/proto/api/v1/user_service";
|
||||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
@@ -36,10 +37,9 @@ const SECTION_ICON_MAP: Record<SettingSection, LucideIcon> = {
|
||||
sso: KeyIcon,
|
||||
};
|
||||
|
||||
const Setting = () => {
|
||||
const Setting = observer(() => {
|
||||
const t = useTranslate();
|
||||
const location = useLocation();
|
||||
const commonContext = useCommonContext();
|
||||
const user = useCurrentUser();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const [state, setState] = useState<State>({
|
||||
@@ -115,7 +115,7 @@ const Setting = () => {
|
||||
/>
|
||||
))}
|
||||
<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>
|
||||
</div>
|
||||
</>
|
||||
@@ -151,6 +151,6 @@ const Setting = () => {
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default Setting;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Divider } from "@mui/joy";
|
||||
import { Button } from "@usememos/mui";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { Link } from "react-router-dom";
|
||||
@@ -9,18 +10,17 @@ import PasswordSignInForm from "@/components/PasswordSignInForm";
|
||||
import { identityProviderServiceClient } from "@/grpcweb";
|
||||
import { absolutifyLink } from "@/helpers/utils";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import { useCommonContext } from "@/layouts/CommonContextProvider";
|
||||
import { Routes } from "@/router";
|
||||
import { extractIdentityProviderIdFromName, useWorkspaceSettingStore } from "@/store/v1";
|
||||
import { workspaceStore } from "@/store/v2";
|
||||
import { IdentityProvider, IdentityProvider_Type } from "@/types/proto/api/v1/idp_service";
|
||||
import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service";
|
||||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const SignIn = () => {
|
||||
const SignIn = observer(() => {
|
||||
const t = useTranslate();
|
||||
const currentUser = useCurrentUser();
|
||||
const commonContext = useCommonContext();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const [identityProviderList, setIdentityProviderList] = useState<IdentityProvider[]>([]);
|
||||
const workspaceGeneralSetting =
|
||||
@@ -43,11 +43,11 @@ const SignIn = () => {
|
||||
}, []);
|
||||
|
||||
const handleLocaleSelectChange = (locale: Locale) => {
|
||||
commonContext.setLocale(locale);
|
||||
workspaceStore.setPartial({ locale });
|
||||
};
|
||||
|
||||
const handleAppearanceSelectChange = (appearance: Appearance) => {
|
||||
commonContext.setAppearance(appearance);
|
||||
workspaceStore.setPartial({ appearance });
|
||||
};
|
||||
|
||||
const handleSignInWithIdentityProvider = async (identityProvider: IdentityProvider) => {
|
||||
@@ -110,11 +110,11 @@ const SignIn = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 flex flex-row items-center justify-center w-full gap-2">
|
||||
<LocaleSelect value={commonContext.locale} onChange={handleLocaleSelectChange} />
|
||||
<AppearanceSelect value={commonContext.appearance as Appearance} onChange={handleAppearanceSelectChange} />
|
||||
<LocaleSelect value={workspaceStore.state.locale} onChange={handleLocaleSelectChange} />
|
||||
<AppearanceSelect value={workspaceStore.state.appearance as Appearance} onChange={handleAppearanceSelectChange} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default SignIn;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Button, Input } from "@usememos/mui";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ClientError } from "nice-grpc-web";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
@@ -9,16 +10,15 @@ import LocaleSelect from "@/components/LocaleSelect";
|
||||
import { authServiceClient } from "@/grpcweb";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import useNavigateTo from "@/hooks/useNavigateTo";
|
||||
import { useCommonContext } from "@/layouts/CommonContextProvider";
|
||||
import { useUserStore, useWorkspaceSettingStore } from "@/store/v1";
|
||||
import { workspaceStore } from "@/store/v2";
|
||||
import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service";
|
||||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
const SignUp = () => {
|
||||
const SignUp = observer(() => {
|
||||
const t = useTranslate();
|
||||
const navigateTo = useNavigateTo();
|
||||
const commonContext = useCommonContext();
|
||||
const workspaceSettingStore = useWorkspaceSettingStore();
|
||||
const userStore = useUserStore();
|
||||
const actionBtnLoadingState = useLoading(false);
|
||||
@@ -38,11 +38,11 @@ const SignUp = () => {
|
||||
};
|
||||
|
||||
const handleLocaleSelectChange = (locale: Locale) => {
|
||||
commonContext.setLocale(locale);
|
||||
workspaceStore.setPartial({ locale });
|
||||
};
|
||||
|
||||
const handleAppearanceSelectChange = (appearance: Appearance) => {
|
||||
commonContext.setAppearance(appearance);
|
||||
workspaceStore.setPartial({ appearance });
|
||||
};
|
||||
|
||||
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
@@ -136,7 +136,7 @@ const SignUp = () => {
|
||||
) : (
|
||||
<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">
|
||||
@@ -148,11 +148,11 @@ const SignUp = () => {
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 flex flex-row items-center justify-center w-full gap-2">
|
||||
<LocaleSelect value={commonContext.locale} onChange={handleLocaleSelectChange} />
|
||||
<AppearanceSelect value={commonContext.appearance as Appearance} onChange={handleAppearanceSelectChange} />
|
||||
<LocaleSelect value={workspaceStore.state.locale} onChange={handleLocaleSelectChange} />
|
||||
<AppearanceSelect value={workspaceStore.state.appearance as Appearance} onChange={handleAppearanceSelectChange} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default SignUp;
|
||||
|
@@ -1,17 +0,0 @@
|
||||
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;
|
@@ -1,26 +0,0 @@
|
||||
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);
|
||||
},
|
||||
};
|
||||
};
|
@@ -1 +0,0 @@
|
||||
export * from "./dialog";
|
@@ -1,37 +0,0 @@
|
||||
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;
|
@@ -1,31 +0,0 @@
|
||||
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;
|
||||
},
|
||||
})),
|
||||
);
|
@@ -1,6 +1,5 @@
|
||||
export * from "./user";
|
||||
export * from "./memo";
|
||||
export * from "./inbox";
|
||||
export * from "./resourceName";
|
||||
export * from "./resource";
|
||||
export * from "./workspaceSetting";
|
||||
|
@@ -1,21 +1,19 @@
|
||||
import { create } from "zustand";
|
||||
import { combine } from "zustand/middleware";
|
||||
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 {
|
||||
userMapByName: Record<string, User>;
|
||||
// The name of current user. Format: `users/${uid}`
|
||||
currentUser?: string;
|
||||
userSetting?: UserSetting;
|
||||
shortcuts: Shortcut[];
|
||||
}
|
||||
|
||||
const getDefaultState = (): State => ({
|
||||
userMapByName: {},
|
||||
currentUser: undefined,
|
||||
userSetting: undefined,
|
||||
shortcuts: [],
|
||||
});
|
||||
|
||||
const getDefaultUserSetting = () => {
|
||||
@@ -131,14 +129,6 @@ export const useUserStore = create(
|
||||
set({ userSetting: updatedUserSetting });
|
||||
return updatedUserSetting;
|
||||
},
|
||||
fetchShortcuts: async () => {
|
||||
const { currentUser } = get();
|
||||
if (!currentUser) {
|
||||
return;
|
||||
}
|
||||
const { shortcuts } = await userServiceClient.listShortcuts({ parent: currentUser });
|
||||
set({ shortcuts });
|
||||
},
|
||||
})),
|
||||
);
|
||||
|
||||
|
32
web/src/store/v2/dialog.ts
Normal file
32
web/src/store/v2/dialog.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
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;
|
4
web/src/store/v2/index.ts
Normal file
4
web/src/store/v2/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import userStore from "./user";
|
||||
import workspaceStore from "./workspace";
|
||||
|
||||
export { workspaceStore, userStore };
|
112
web/src/store/v2/user.ts
Normal file
112
web/src/store/v2/user.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
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;
|
68
web/src/store/v2/workspace.ts
Normal file
68
web/src/store/v2/workspace.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
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;
|
@@ -46,3 +46,8 @@ export const useTranslate = (): TypedT => {
|
||||
const { t } = useTranslation<Translations>();
|
||||
return t;
|
||||
};
|
||||
|
||||
export const isValidateLocale = (locale: string | undefined | null): boolean => {
|
||||
if (!locale) return false;
|
||||
return locales.includes(locale);
|
||||
};
|
||||
|
Reference in New Issue
Block a user