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
e60e47f7
Commit
e60e47f7
authored
Nov 05, 2023
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: update user definition
parent
e67820ca
Changes
21
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
322 additions
and
279 deletions
+322
-279
resource_name.go
api/v2/resource_name.go
+10
-0
user_service.go
api/v2/user_service.go
+31
-28
user_service.proto
proto/api/v2/user_service.proto
+25
-15
README.md
proto/gen/api/v2/README.md
+5
-5
user_service.pb.go
proto/gen/api/v2/user_service.pb.go
+154
-148
user_service.pb.gw.go
proto/gen/api/v2/user_service.pb.gw.go
+56
-56
ChangePasswordDialog.tsx
web/src/components/ChangePasswordDialog.tsx
+2
-1
CreateAccessTokenDialog.tsx
web/src/components/CreateAccessTokenDialog.tsx
+1
-1
MemoCommentMessage.tsx
web/src/components/Inbox/MemoCommentMessage.tsx
+1
-1
Memo.tsx
web/src/components/Memo.tsx
+3
-2
MemoList.tsx
web/src/components/MemoList.tsx
+2
-1
AccessTokenSection.tsx
web/src/components/Settings/AccessTokenSection.tsx
+5
-4
MyAccountSection.tsx
web/src/components/Settings/MyAccountSection.tsx
+2
-1
ShareMemoDialog.tsx
web/src/components/ShareMemoDialog.tsx
+2
-1
UsageHeatMap.tsx
web/src/components/UsageHeatMap.tsx
+5
-4
UserBanner.tsx
web/src/components/UserBanner.tsx
+2
-1
DatePicker.tsx
web/src/components/kit/DatePicker.tsx
+3
-2
DailyReview.tsx
web/src/pages/DailyReview.tsx
+2
-1
MemoDetail.tsx
web/src/pages/MemoDetail.tsx
+2
-1
resourceName.ts
web/src/store/v1/resourceName.ts
+5
-0
user.ts
web/src/store/v1/user.ts
+4
-6
No files found.
api/v2/resource_name.go
View file @
e60e47f7
...
@@ -10,6 +10,7 @@ import (
...
@@ -10,6 +10,7 @@ import (
)
)
const
(
const
(
UserNamePrefix
=
"users/"
InboxNamePrefix
=
"inboxes/"
InboxNamePrefix
=
"inboxes/"
)
)
...
@@ -33,6 +34,15 @@ func GetNameParentTokens(name string, tokenPrefixes ...string) ([]string, error)
...
@@ -33,6 +34,15 @@ func GetNameParentTokens(name string, tokenPrefixes ...string) ([]string, error)
return
tokens
,
nil
return
tokens
,
nil
}
}
// GetUsername returns the username from a resource name.
func
GetUsername
(
name
string
)
(
string
,
error
)
{
tokens
,
err
:=
GetNameParentTokens
(
name
,
UserNamePrefix
)
if
err
!=
nil
{
return
""
,
err
}
return
tokens
[
0
],
nil
}
// GetInboxID returns the inbox ID from a resource name.
// GetInboxID returns the inbox ID from a resource name.
func
GetInboxID
(
name
string
)
(
int32
,
error
)
{
func
GetInboxID
(
name
string
)
(
int32
,
error
)
{
tokens
,
err
:=
GetNameParentTokens
(
name
,
InboxNamePrefix
)
tokens
,
err
:=
GetNameParentTokens
(
name
,
InboxNamePrefix
)
...
...
api/v2/user_service.go
View file @
e60e47f7
...
@@ -2,6 +2,7 @@ package v2
...
@@ -2,6 +2,7 @@ package v2
import
(
import
(
"context"
"context"
"fmt"
"net/http"
"net/http"
"regexp"
"regexp"
"strings"
"strings"
...
@@ -27,8 +28,12 @@ var (
...
@@ -27,8 +28,12 @@ var (
)
)
func
(
s
*
APIV2Service
)
GetUser
(
ctx
context
.
Context
,
request
*
apiv2pb
.
GetUserRequest
)
(
*
apiv2pb
.
GetUserResponse
,
error
)
{
func
(
s
*
APIV2Service
)
GetUser
(
ctx
context
.
Context
,
request
*
apiv2pb
.
GetUserRequest
)
(
*
apiv2pb
.
GetUserResponse
,
error
)
{
username
,
err
:=
GetUsername
(
request
.
Name
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"name is required"
)
}
user
,
err
:=
s
.
Store
.
GetUser
(
ctx
,
&
store
.
FindUser
{
user
,
err
:=
s
.
Store
.
GetUser
(
ctx
,
&
store
.
FindUser
{
Username
:
&
request
.
U
sername
,
Username
:
&
u
sername
,
})
})
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user: %v"
,
err
)
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user: %v"
,
err
)
...
@@ -53,8 +58,12 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs
...
@@ -53,8 +58,12 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
}
}
if
!
usernameMatcher
.
MatchString
(
strings
.
ToLower
(
request
.
User
.
Username
))
{
username
,
err
:=
GetUsername
(
request
.
User
.
Name
)
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid username: %s"
,
request
.
User
.
Username
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"name is required"
)
}
if
!
usernameMatcher
.
MatchString
(
strings
.
ToLower
(
username
))
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid username: %s"
,
username
)
}
}
passwordHash
,
err
:=
bcrypt
.
GenerateFromPassword
([]
byte
(
request
.
User
.
Password
),
bcrypt
.
DefaultCost
)
passwordHash
,
err
:=
bcrypt
.
GenerateFromPassword
([]
byte
(
request
.
User
.
Password
),
bcrypt
.
DefaultCost
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -62,7 +71,7 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs
...
@@ -62,7 +71,7 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs
}
}
user
,
err
:=
s
.
Store
.
CreateUser
(
ctx
,
&
store
.
User
{
user
,
err
:=
s
.
Store
.
CreateUser
(
ctx
,
&
store
.
User
{
Username
:
request
.
User
.
U
sername
,
Username
:
u
sername
,
Role
:
convertUserRoleToStore
(
request
.
User
.
Role
),
Role
:
convertUserRoleToStore
(
request
.
User
.
Role
),
Email
:
request
.
User
.
Email
,
Email
:
request
.
User
.
Email
,
Nickname
:
request
.
User
.
Nickname
,
Nickname
:
request
.
User
.
Nickname
,
...
@@ -79,11 +88,15 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs
...
@@ -79,11 +88,15 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs
}
}
func
(
s
*
APIV2Service
)
UpdateUser
(
ctx
context
.
Context
,
request
*
apiv2pb
.
UpdateUserRequest
)
(
*
apiv2pb
.
UpdateUserResponse
,
error
)
{
func
(
s
*
APIV2Service
)
UpdateUser
(
ctx
context
.
Context
,
request
*
apiv2pb
.
UpdateUserRequest
)
(
*
apiv2pb
.
UpdateUserResponse
,
error
)
{
username
,
err
:=
GetUsername
(
request
.
User
.
Name
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"name is required"
)
}
currentUser
,
err
:=
getCurrentUser
(
ctx
,
s
.
Store
)
currentUser
,
err
:=
getCurrentUser
(
ctx
,
s
.
Store
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user: %v"
,
err
)
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user: %v"
,
err
)
}
}
if
currentUser
.
Username
!=
request
.
User
.
U
sername
&&
currentUser
.
Role
!=
store
.
RoleAdmin
{
if
currentUser
.
Username
!=
u
sername
&&
currentUser
.
Role
!=
store
.
RoleAdmin
{
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
}
}
if
request
.
UpdateMask
==
nil
||
len
(
request
.
UpdateMask
.
Paths
)
==
0
{
if
request
.
UpdateMask
==
nil
||
len
(
request
.
UpdateMask
.
Paths
)
==
0
{
...
@@ -97,10 +110,10 @@ func (s *APIV2Service) UpdateUser(ctx context.Context, request *apiv2pb.UpdateUs
...
@@ -97,10 +110,10 @@ func (s *APIV2Service) UpdateUser(ctx context.Context, request *apiv2pb.UpdateUs
}
}
for
_
,
field
:=
range
request
.
UpdateMask
.
Paths
{
for
_
,
field
:=
range
request
.
UpdateMask
.
Paths
{
if
field
==
"username"
{
if
field
==
"username"
{
if
!
usernameMatcher
.
MatchString
(
strings
.
ToLower
(
request
.
User
.
U
sername
))
{
if
!
usernameMatcher
.
MatchString
(
strings
.
ToLower
(
u
sername
))
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid username: %s"
,
request
.
User
.
U
sername
)
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid username: %s"
,
u
sername
)
}
}
update
.
Username
=
&
request
.
User
.
U
sername
update
.
Username
=
&
u
sername
}
else
if
field
==
"nickname"
{
}
else
if
field
==
"nickname"
{
update
.
Nickname
=
&
request
.
User
.
Nickname
update
.
Nickname
=
&
request
.
User
.
Nickname
}
else
if
field
==
"email"
{
}
else
if
field
==
"email"
{
...
@@ -146,17 +159,21 @@ func (s *APIV2Service) ListUserAccessTokens(ctx context.Context, request *apiv2p
...
@@ -146,17 +159,21 @@ func (s *APIV2Service) ListUserAccessTokens(ctx context.Context, request *apiv2p
}
}
userID
:=
user
.
ID
userID
:=
user
.
ID
username
,
err
:=
GetUsername
(
request
.
Name
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"name is required"
)
}
// List access token for other users need to be verified.
// List access token for other users need to be verified.
if
user
.
Username
!=
request
.
U
sername
{
if
user
.
Username
!=
u
sername
{
// Normal users can only list their access tokens.
// Normal users can only list their access tokens.
if
user
.
Role
==
store
.
RoleUser
{
if
user
.
Role
==
store
.
RoleUser
{
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
}
}
// The request user must be exist.
// The request user must be exist.
requestUser
,
err
:=
s
.
Store
.
GetUser
(
ctx
,
&
store
.
FindUser
{
Username
:
&
request
.
U
sername
})
requestUser
,
err
:=
s
.
Store
.
GetUser
(
ctx
,
&
store
.
FindUser
{
Username
:
&
u
sername
})
if
requestUser
==
nil
||
err
!=
nil
{
if
requestUser
==
nil
||
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"fail to find user %s"
,
request
.
U
sername
)
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"fail to find user %s"
,
u
sername
)
}
}
userID
=
requestUser
.
ID
userID
=
requestUser
.
ID
}
}
...
@@ -217,21 +234,7 @@ func (s *APIV2Service) CreateUserAccessToken(ctx context.Context, request *apiv2
...
@@ -217,21 +234,7 @@ func (s *APIV2Service) CreateUserAccessToken(ctx context.Context, request *apiv2
expiresAt
=
request
.
ExpiresAt
.
AsTime
()
expiresAt
=
request
.
ExpiresAt
.
AsTime
()
}
}
// Create access token for other users need to be verified.
accessToken
,
err
:=
auth
.
GenerateAccessToken
(
user
.
Username
,
user
.
ID
,
expiresAt
,
[]
byte
(
s
.
Secret
))
if
user
.
Username
!=
request
.
Username
{
// Normal users can only create access tokens for others.
if
user
.
Role
==
store
.
RoleUser
{
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
}
// The request user must be exist.
requestUser
,
err
:=
s
.
Store
.
GetUser
(
ctx
,
&
store
.
FindUser
{
Username
:
&
request
.
Username
})
if
requestUser
==
nil
||
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
NotFound
,
"fail to find user %s"
,
request
.
Username
)
}
}
accessToken
,
err
:=
auth
.
GenerateAccessToken
(
request
.
Username
,
user
.
ID
,
expiresAt
,
[]
byte
(
s
.
Secret
))
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to generate access token: %v"
,
err
)
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to generate access token: %v"
,
err
)
}
}
...
@@ -329,11 +332,11 @@ func (s *APIV2Service) UpsertAccessTokenToStore(ctx context.Context, user *store
...
@@ -329,11 +332,11 @@ func (s *APIV2Service) UpsertAccessTokenToStore(ctx context.Context, user *store
func
convertUserFromStore
(
user
*
store
.
User
)
*
apiv2pb
.
User
{
func
convertUserFromStore
(
user
*
store
.
User
)
*
apiv2pb
.
User
{
return
&
apiv2pb
.
User
{
return
&
apiv2pb
.
User
{
Id
:
int32
(
user
.
ID
),
Name
:
fmt
.
Sprintf
(
"%s%s"
,
UserNamePrefix
,
user
.
Username
),
Id
:
user
.
ID
,
RowStatus
:
convertRowStatusFromStore
(
user
.
RowStatus
),
RowStatus
:
convertRowStatusFromStore
(
user
.
RowStatus
),
CreateTime
:
timestamppb
.
New
(
time
.
Unix
(
user
.
CreatedTs
,
0
)),
CreateTime
:
timestamppb
.
New
(
time
.
Unix
(
user
.
CreatedTs
,
0
)),
UpdateTime
:
timestamppb
.
New
(
time
.
Unix
(
user
.
UpdatedTs
,
0
)),
UpdateTime
:
timestamppb
.
New
(
time
.
Unix
(
user
.
UpdatedTs
,
0
)),
Username
:
user
.
Username
,
Role
:
convertUserRoleFromStore
(
user
.
Role
),
Role
:
convertUserRoleFromStore
(
user
.
Role
),
Email
:
user
.
Email
,
Email
:
user
.
Email
,
Nickname
:
user
.
Nickname
,
Nickname
:
user
.
Nickname
,
...
...
proto/api/v2/user_service.proto
View file @
e60e47f7
...
@@ -13,8 +13,8 @@ option go_package = "gen/api/v2";
...
@@ -13,8 +13,8 @@ option go_package = "gen/api/v2";
service
UserService
{
service
UserService
{
rpc
GetUser
(
GetUserRequest
)
returns
(
GetUserResponse
)
{
rpc
GetUser
(
GetUserRequest
)
returns
(
GetUserResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/api/v2/
users/{username
}"
};
option
(
google.api.http
)
=
{
get
:
"/api/v2/
{name=users/*
}"
};
option
(
google.api.method_signature
)
=
"
user
name"
;
option
(
google.api.method_signature
)
=
"name"
;
}
}
rpc
CreateUser
(
CreateUserRequest
)
returns
(
CreateUserResponse
)
{
rpc
CreateUser
(
CreateUserRequest
)
returns
(
CreateUserResponse
)
{
option
(
google.api.http
)
=
{
option
(
google.api.http
)
=
{
...
@@ -25,35 +25,37 @@ service UserService {
...
@@ -25,35 +25,37 @@ service UserService {
}
}
rpc
UpdateUser
(
UpdateUserRequest
)
returns
(
UpdateUserResponse
)
{
rpc
UpdateUser
(
UpdateUserRequest
)
returns
(
UpdateUserResponse
)
{
option
(
google.api.http
)
=
{
option
(
google.api.http
)
=
{
patch
:
"/api/v2/
users/{user.username
}"
patch
:
"/api/v2/
{user.name=users/*
}"
body
:
"user"
body
:
"user"
};
};
option
(
google.api.method_signature
)
=
"user,update_mask"
;
option
(
google.api.method_signature
)
=
"user,update_mask"
;
}
}
// ListUserAccessTokens returns a list of access tokens for a user.
// ListUserAccessTokens returns a list of access tokens for a user.
rpc
ListUserAccessTokens
(
ListUserAccessTokensRequest
)
returns
(
ListUserAccessTokensResponse
)
{
rpc
ListUserAccessTokens
(
ListUserAccessTokensRequest
)
returns
(
ListUserAccessTokensResponse
)
{
option
(
google.api.http
)
=
{
get
:
"/api/v2/
users/{username
}/access_tokens"
};
option
(
google.api.http
)
=
{
get
:
"/api/v2/
{name=users/*
}/access_tokens"
};
option
(
google.api.method_signature
)
=
"
user
name"
;
option
(
google.api.method_signature
)
=
"name"
;
}
}
// CreateUserAccessToken creates a new access token for a user.
// CreateUserAccessToken creates a new access token for a user.
rpc
CreateUserAccessToken
(
CreateUserAccessTokenRequest
)
returns
(
CreateUserAccessTokenResponse
)
{
rpc
CreateUserAccessToken
(
CreateUserAccessTokenRequest
)
returns
(
CreateUserAccessTokenResponse
)
{
option
(
google.api.http
)
=
{
option
(
google.api.http
)
=
{
post
:
"/api/v2/
users/{username
}/access_tokens"
post
:
"/api/v2/
{name=users/*
}/access_tokens"
body
:
"*"
body
:
"*"
};
};
option
(
google.api.method_signature
)
=
"
user
name"
;
option
(
google.api.method_signature
)
=
"name"
;
}
}
// DeleteUserAccessToken deletes an access token for a user.
// DeleteUserAccessToken deletes an access token for a user.
rpc
DeleteUserAccessToken
(
DeleteUserAccessTokenRequest
)
returns
(
DeleteUserAccessTokenResponse
)
{
rpc
DeleteUserAccessToken
(
DeleteUserAccessTokenRequest
)
returns
(
DeleteUserAccessTokenResponse
)
{
option
(
google.api.http
)
=
{
delete
:
"/api/v2/
users/{username
}/access_tokens/{access_token}"
};
option
(
google.api.http
)
=
{
delete
:
"/api/v2/
{name=users/*
}/access_tokens/{access_token}"
};
option
(
google.api.method_signature
)
=
"
user
name,access_token"
;
option
(
google.api.method_signature
)
=
"name,access_token"
;
}
}
}
}
message
User
{
message
User
{
int32
id
=
1
;
// The name of the user.
// Format: users/{username}
string
name
=
1
;
string
username
=
2
;
int32
id
=
2
;
enum
Role
{
enum
Role
{
ROLE_UNSPECIFIED
=
0
;
ROLE_UNSPECIFIED
=
0
;
...
@@ -79,7 +81,9 @@ message User {
...
@@ -79,7 +81,9 @@ message User {
}
}
message
GetUserRequest
{
message
GetUserRequest
{
string
username
=
1
;
// The name of the user.
// Format: users/{username}
string
name
=
1
;
}
}
message
GetUserResponse
{
message
GetUserResponse
{
...
@@ -105,7 +109,9 @@ message UpdateUserResponse {
...
@@ -105,7 +109,9 @@ message UpdateUserResponse {
}
}
message
ListUserAccessTokensRequest
{
message
ListUserAccessTokensRequest
{
string
username
=
1
;
// The name of the user.
// Format: users/{username}
string
name
=
1
;
}
}
message
ListUserAccessTokensResponse
{
message
ListUserAccessTokensResponse
{
...
@@ -113,7 +119,9 @@ message ListUserAccessTokensResponse {
...
@@ -113,7 +119,9 @@ message ListUserAccessTokensResponse {
}
}
message
CreateUserAccessTokenRequest
{
message
CreateUserAccessTokenRequest
{
string
username
=
1
;
// The name of the user.
// Format: users/{username}
string
name
=
1
;
string
description
=
2
;
string
description
=
2
;
...
@@ -125,7 +133,9 @@ message CreateUserAccessTokenResponse {
...
@@ -125,7 +133,9 @@ message CreateUserAccessTokenResponse {
}
}
message
DeleteUserAccessTokenRequest
{
message
DeleteUserAccessTokenRequest
{
string
username
=
1
;
// The name of the user.
// Format: users/{username}
string
name
=
1
;
// access_token is the access token to delete.
// access_token is the access token to delete.
string
access_token
=
2
;
string
access_token
=
2
;
}
}
...
...
proto/gen/api/v2/README.md
View file @
e60e47f7
...
@@ -1032,7 +1032,7 @@
...
@@ -1032,7 +1032,7 @@
| Field | Type | Label | Description |
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| ----- | ---- | ----- | ----------- |
|
username |
[
string
](
#string
)
| |
|
|
name |
[
string
](
#string
)
| | The name of the user. Format: users/{username}
|
| description |
[
string
](
#string
)
| | |
| description |
[
string
](
#string
)
| | |
| expires_at |
[
google.protobuf.Timestamp
](
#google-protobuf-Timestamp
)
| optional | |
| expires_at |
[
google.protobuf.Timestamp
](
#google-protobuf-Timestamp
)
| optional | |
...
@@ -1094,7 +1094,7 @@
...
@@ -1094,7 +1094,7 @@
| Field | Type | Label | Description |
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| ----- | ---- | ----- | ----------- |
|
username |
[
string
](
#string
)
| |
|
|
name |
[
string
](
#string
)
| | The name of the user. Format: users/{username}
|
| access_token |
[
string
](
#string
)
| | access_token is the access token to delete. |
| access_token |
[
string
](
#string
)
| | access_token is the access token to delete. |
...
@@ -1120,7 +1120,7 @@
...
@@ -1120,7 +1120,7 @@
| Field | Type | Label | Description |
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| ----- | ---- | ----- | ----------- |
|
username |
[
string
](
#string
)
| |
|
|
name |
[
string
](
#string
)
| | The name of the user. Format: users/{username}
|
...
@@ -1150,7 +1150,7 @@
...
@@ -1150,7 +1150,7 @@
| Field | Type | Label | Description |
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| ----- | ---- | ----- | ----------- |
|
username |
[
string
](
#string
)
| |
|
|
name |
[
string
](
#string
)
| | The name of the user. Format: users/{username}
|
...
@@ -1211,8 +1211,8 @@
...
@@ -1211,8 +1211,8 @@
| Field | Type | Label | Description |
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| ----- | ---- | ----- | ----------- |
| name |
[
string
](
#string
)
| | The name of the user. Format: users/{username} |
| id |
[
int32
](
#int32
)
| | |
| id |
[
int32
](
#int32
)
| | |
| username |
[
string
](
#string
)
| | |
| role |
[
User.Role
](
#memos-api-v2-User-Role
)
| | |
| role |
[
User.Role
](
#memos-api-v2-User-Role
)
| | |
| email |
[
string
](
#string
)
| | |
| email |
[
string
](
#string
)
| | |
| nickname |
[
string
](
#string
)
| | |
| nickname |
[
string
](
#string
)
| | |
...
...
proto/gen/api/v2/user_service.pb.go
View file @
e60e47f7
This diff is collapsed.
Click to expand it.
proto/gen/api/v2/user_service.pb.gw.go
View file @
e60e47f7
This diff is collapsed.
Click to expand it.
web/src/components/ChangePasswordDialog.tsx
View file @
e60e47f7
...
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
...
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import
{
toast
}
from
"react-hot-toast"
;
import
{
toast
}
from
"react-hot-toast"
;
import
{
useGlobalStore
,
useUserStore
}
from
"@/store/module"
;
import
{
useGlobalStore
,
useUserStore
}
from
"@/store/module"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
UserNamePrefix
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
Icon
from
"./Icon"
;
import
Icon
from
"./Icon"
;
...
@@ -54,7 +55,7 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
...
@@ -54,7 +55,7 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
const
user
=
userStore
.
getState
().
user
as
User
;
const
user
=
userStore
.
getState
().
user
as
User
;
await
userV1Store
.
updateUser
(
await
userV1Store
.
updateUser
(
{
{
username
:
user
.
username
,
name
:
`
${
UserNamePrefix
}${
user
.
username
}
`
,
password
:
newPassword
,
password
:
newPassword
,
},
},
[
"password"
]
[
"password"
]
...
...
web/src/components/CreateAccessTokenDialog.tsx
View file @
e60e47f7
...
@@ -69,7 +69,7 @@ const CreateAccessTokenDialog: React.FC<Props> = (props: Props) => {
...
@@ -69,7 +69,7 @@ const CreateAccessTokenDialog: React.FC<Props> = (props: Props) => {
try
{
try
{
await
userServiceClient
.
createUserAccessToken
({
await
userServiceClient
.
createUserAccessToken
({
username
:
currentUser
.
user
name
,
name
:
currentUser
.
name
,
description
:
state
.
description
,
description
:
state
.
description
,
expiresAt
:
state
.
expiration
?
new
Date
(
Date
.
now
()
+
state
.
expiration
*
1000
)
:
undefined
,
expiresAt
:
state
.
expiration
?
new
Date
(
Date
.
now
()
+
state
.
expiration
*
1000
)
:
undefined
,
});
});
...
...
web/src/components/Inbox/MemoCommentMessage.tsx
View file @
e60e47f7
...
@@ -5,7 +5,7 @@ import toast from "react-hot-toast";
...
@@ -5,7 +5,7 @@ import toast from "react-hot-toast";
import
{
activityServiceClient
}
from
"@/grpcweb"
;
import
{
activityServiceClient
}
from
"@/grpcweb"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useInboxStore
from
"@/store/v1/inbox"
;
import
useInboxStore
from
"@/store/v1/inbox"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/
user
"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/
resourceName
"
;
import
{
Activity
}
from
"@/types/proto/api/v2/activity_service"
;
import
{
Activity
}
from
"@/types/proto/api/v2/activity_service"
;
import
{
Inbox
,
Inbox_Status
}
from
"@/types/proto/api/v2/inbox_service"
;
import
{
Inbox
,
Inbox_Status
}
from
"@/types/proto/api/v2/inbox_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
...
web/src/components/Memo.tsx
View file @
e60e47f7
...
@@ -9,6 +9,7 @@ import useCurrentUser from "@/hooks/useCurrentUser";
...
@@ -9,6 +9,7 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useFilterStore
,
useMemoStore
,
useUserStore
}
from
"@/store/module"
;
import
{
useFilterStore
,
useMemoStore
,
useUserStore
}
from
"@/store/module"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
showChangeMemoCreatedTsDialog
from
"./ChangeMemoCreatedTsDialog"
;
import
showChangeMemoCreatedTsDialog
from
"./ChangeMemoCreatedTsDialog"
;
import
{
showCommonDialog
}
from
"./Dialog/CommonDialog"
;
import
{
showCommonDialog
}
from
"./Dialog/CommonDialog"
;
...
@@ -42,7 +43,7 @@ const Memo: React.FC<Props> = (props: Props) => {
...
@@ -42,7 +43,7 @@ const Memo: React.FC<Props> = (props: Props) => {
const
[
shouldRender
,
setShouldRender
]
=
useState
<
boolean
>
(
lazyRendering
?
false
:
true
);
const
[
shouldRender
,
setShouldRender
]
=
useState
<
boolean
>
(
lazyRendering
?
false
:
true
);
const
[
displayTime
,
setDisplayTime
]
=
useState
<
string
>
(
getRelativeTimeString
(
memo
.
displayTs
));
const
[
displayTime
,
setDisplayTime
]
=
useState
<
string
>
(
getRelativeTimeString
(
memo
.
displayTs
));
const
memoContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
memoContainerRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
readonly
=
memo
.
creatorUsername
!==
user
?.
username
;
const
readonly
=
memo
.
creatorUsername
!==
extractUsernameFromName
(
user
?.
name
)
;
const
[
creator
,
setCreator
]
=
useState
(
userV1Store
.
getUserByUsername
(
memo
.
creatorUsername
));
const
[
creator
,
setCreator
]
=
useState
(
userV1Store
.
getUserByUsername
(
memo
.
creatorUsername
));
const
referenceRelations
=
memo
.
relationList
.
filter
((
relation
)
=>
relation
.
type
===
"REFERENCE"
);
const
referenceRelations
=
memo
.
relationList
.
filter
((
relation
)
=>
relation
.
type
===
"REFERENCE"
);
const
commentRelations
=
memo
.
relationList
.
filter
((
relation
)
=>
relation
.
relatedMemoId
===
memo
.
id
&&
relation
.
type
===
"COMMENT"
);
const
commentRelations
=
memo
.
relationList
.
filter
((
relation
)
=>
relation
.
relatedMemoId
===
memo
.
id
&&
relation
.
type
===
"COMMENT"
);
...
@@ -300,7 +301,7 @@ const Memo: React.FC<Props> = (props: Props) => {
...
@@ -300,7 +301,7 @@ const Memo: React.FC<Props> = (props: Props) => {
<
span
className=
"flex flex-row justify-start items-center"
>
<
span
className=
"flex flex-row justify-start items-center"
>
<
UserAvatar
className=
"!w-5 !h-auto mr-1"
avatarUrl=
{
creator
.
avatarUrl
}
/>
<
UserAvatar
className=
"!w-5 !h-auto mr-1"
avatarUrl=
{
creator
.
avatarUrl
}
/>
<
span
className=
"text-sm text-gray-600 max-w-[8em] truncate dark:text-gray-400"
>
<
span
className=
"text-sm text-gray-600 max-w-[8em] truncate dark:text-gray-400"
>
{
creator
.
nickname
||
creator
.
username
}
{
creator
.
nickname
||
extractUsernameFromName
(
creator
.
name
)
}
</
span
>
</
span
>
</
span
>
</
span
>
</
Tooltip
>
</
Tooltip
>
...
...
web/src/components/MemoList.tsx
View file @
e60e47f7
...
@@ -7,6 +7,7 @@ import { getTimeStampByDate } from "@/helpers/datetime";
...
@@ -7,6 +7,7 @@ import { getTimeStampByDate } from "@/helpers/datetime";
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
TAG_REG
}
from
"@/labs/marked/parser"
;
import
{
TAG_REG
}
from
"@/labs/marked/parser"
;
import
{
useFilterStore
,
useMemoStore
}
from
"@/store/module"
;
import
{
useFilterStore
,
useMemoStore
}
from
"@/store/module"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
Empty
from
"./Empty"
;
import
Empty
from
"./Empty"
;
import
Memo
from
"./Memo"
;
import
Memo
from
"./Memo"
;
...
@@ -21,7 +22,7 @@ const MemoList: React.FC = () => {
...
@@ -21,7 +22,7 @@ const MemoList: React.FC = () => {
const
user
=
useCurrentUser
();
const
user
=
useCurrentUser
();
const
{
tag
:
tagQuery
,
duration
,
text
:
textQuery
,
visibility
}
=
filter
;
const
{
tag
:
tagQuery
,
duration
,
text
:
textQuery
,
visibility
}
=
filter
;
const
showMemoFilter
=
Boolean
(
tagQuery
||
(
duration
&&
duration
.
from
<
duration
.
to
)
||
textQuery
||
visibility
);
const
showMemoFilter
=
Boolean
(
tagQuery
||
(
duration
&&
duration
.
from
<
duration
.
to
)
||
textQuery
||
visibility
);
const
username
=
params
.
username
||
user
?
.
username
||
"";
const
username
=
params
.
username
||
extractUsernameFromName
(
user
.
name
)
||
"";
const
fetchMoreRef
=
useRef
<
HTMLSpanElement
>
(null);
const
fetchMoreRef
=
useRef
<
HTMLSpanElement
>
(null);
...
...
web/src/components/Settings/AccessTokenSection.tsx
View file @
e60e47f7
...
@@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
...
@@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
import
{
toast
}
from
"react-hot-toast"
;
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
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
UserAccessToken
}
from
"@/types/proto/api/v2/user_service"
;
import
{
UserAccessToken
}
from
"@/types/proto/api/v2/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
showCreateAccessTokenDialog
from
"../CreateAccessTokenDialog"
;
import
showCreateAccessTokenDialog
from
"../CreateAccessTokenDialog"
;
...
@@ -12,7 +13,7 @@ import Icon from "../Icon";
...
@@ -12,7 +13,7 @@ import Icon from "../Icon";
import
LearnMore
from
"../LearnMore"
;
import
LearnMore
from
"../LearnMore"
;
const
listAccessTokens
=
async
(
username
:
string
)
=>
{
const
listAccessTokens
=
async
(
username
:
string
)
=>
{
const
{
accessTokens
}
=
await
userServiceClient
.
listUserAccessTokens
({
username
:
username
});
const
{
accessTokens
}
=
await
userServiceClient
.
listUserAccessTokens
({
name
:
`
${
UserAccessToken
}${
username
}
`
});
return
accessTokens
;
return
accessTokens
;
};
};
...
@@ -22,13 +23,13 @@ const AccessTokenSection = () => {
...
@@ -22,13 +23,13 @@ const AccessTokenSection = () => {
const
[
userAccessTokens
,
setUserAccessTokens
]
=
useState
<
UserAccessToken
[]
>
([]);
const
[
userAccessTokens
,
setUserAccessTokens
]
=
useState
<
UserAccessToken
[]
>
([]);
useEffect
(()
=>
{
useEffect
(()
=>
{
listAccessTokens
(
currentUser
.
username
).
then
((
accessTokens
)
=>
{
listAccessTokens
(
extractUsernameFromName
(
currentUser
.
name
)
).
then
((
accessTokens
)
=>
{
setUserAccessTokens
(
accessTokens
);
setUserAccessTokens
(
accessTokens
);
});
});
},
[]);
},
[]);
const
handleCreateAccessTokenDialogConfirm
=
async
()
=>
{
const
handleCreateAccessTokenDialogConfirm
=
async
()
=>
{
const
accessTokens
=
await
listAccessTokens
(
currentUser
.
username
);
const
accessTokens
=
await
listAccessTokens
(
extractUsernameFromName
(
currentUser
.
name
)
);
setUserAccessTokens
(
accessTokens
);
setUserAccessTokens
(
accessTokens
);
};
};
...
@@ -44,7 +45,7 @@ const AccessTokenSection = () => {
...
@@ -44,7 +45,7 @@ const AccessTokenSection = () => {
style
:
"danger"
,
style
:
"danger"
,
dialogName
:
"delete-access-token-dialog"
,
dialogName
:
"delete-access-token-dialog"
,
onConfirm
:
async
()
=>
{
onConfirm
:
async
()
=>
{
await
userServiceClient
.
deleteUserAccessToken
({
username
:
currentUser
.
user
name
,
accessToken
:
accessToken
});
await
userServiceClient
.
deleteUserAccessToken
({
name
:
currentUser
.
name
,
accessToken
:
accessToken
});
setUserAccessTokens
(
userAccessTokens
.
filter
((
token
)
=>
token
.
accessToken
!==
accessToken
));
setUserAccessTokens
(
userAccessTokens
.
filter
((
token
)
=>
token
.
accessToken
!==
accessToken
));
},
},
});
});
...
...
web/src/components/Settings/MyAccountSection.tsx
View file @
e60e47f7
import
{
Button
}
from
"@mui/joy"
;
import
{
Button
}
from
"@mui/joy"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
showChangePasswordDialog
from
"../ChangePasswordDialog"
;
import
showChangePasswordDialog
from
"../ChangePasswordDialog"
;
import
showUpdateAccountDialog
from
"../UpdateAccountDialog"
;
import
showUpdateAccountDialog
from
"../UpdateAccountDialog"
;
...
@@ -18,7 +19,7 @@ const MyAccountSection = () => {
...
@@ -18,7 +19,7 @@ const MyAccountSection = () => {
<
UserAvatar
className=
"mr-2 w-14 h-14"
avatarUrl=
{
user
.
avatarUrl
}
/>
<
UserAvatar
className=
"mr-2 w-14 h-14"
avatarUrl=
{
user
.
avatarUrl
}
/>
<
div
className=
"flex flex-col justify-center items-start"
>
<
div
className=
"flex flex-col justify-center items-start"
>
<
span
className=
"text-2xl font-medium"
>
{
user
.
nickname
}
</
span
>
<
span
className=
"text-2xl font-medium"
>
{
user
.
nickname
}
</
span
>
<
span
className=
"-mt-2 text-base text-gray-500 dark:text-gray-400"
>
(
{
user
.
username
}
)
</
span
>
<
span
className=
"-mt-2 text-base text-gray-500 dark:text-gray-400"
>
(
{
extractUsernameFromName
(
user
.
name
)
}
)
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className=
"w-full flex flex-row justify-start items-center mt-4 space-x-2"
>
<
div
className=
"w-full flex flex-row justify-start items-center mt-4 space-x-2"
>
...
...
web/src/components/ShareMemoDialog.tsx
View file @
e60e47f7
...
@@ -6,6 +6,7 @@ import { getDateTimeString } from "@/helpers/datetime";
...
@@ -6,6 +6,7 @@ import { getDateTimeString } from "@/helpers/datetime";
import
useLoading
from
"@/hooks/useLoading"
;
import
useLoading
from
"@/hooks/useLoading"
;
import
toImage
from
"@/labs/html2image"
;
import
toImage
from
"@/labs/html2image"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
{
generateDialog
}
from
"./Dialog"
;
import
showEmbedMemoDialog
from
"./EmbedMemoDialog"
;
import
showEmbedMemoDialog
from
"./EmbedMemoDialog"
;
...
@@ -120,7 +121,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
...
@@ -120,7 +121,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
<
UserAvatar
className=
"mr-2"
avatarUrl=
{
user
.
avatarUrl
}
/>
<
UserAvatar
className=
"mr-2"
avatarUrl=
{
user
.
avatarUrl
}
/>
<
div
className=
"w-auto grow truncate flex mr-2 flex-col justify-center items-start"
>
<
div
className=
"w-auto grow truncate flex mr-2 flex-col justify-center items-start"
>
<
span
className=
"w-full text truncate font-medium text-gray-600 dark:text-gray-300"
>
<
span
className=
"w-full text truncate font-medium text-gray-600 dark:text-gray-300"
>
{
user
.
nickname
||
user
.
username
}
{
user
.
nickname
||
extractUsernameFromName
(
user
.
name
)
}
</
span
>
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
...
...
web/src/components/UsageHeatMap.tsx
View file @
e60e47f7
...
@@ -6,6 +6,7 @@ import * as utils from "@/helpers/utils";
...
@@ -6,6 +6,7 @@ import * as utils from "@/helpers/utils";
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useGlobalStore
}
from
"@/store/module"
;
import
{
useGlobalStore
}
from
"@/store/module"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
,
Translations
}
from
"@/utils/i18n"
;
import
{
useTranslate
,
Translations
}
from
"@/utils/i18n"
;
import
{
useFilterStore
,
useMemoStore
}
from
"../store/module"
;
import
{
useFilterStore
,
useMemoStore
}
from
"../store/module"
;
import
"@/less/usage-heat-map.less"
;
import
"@/less/usage-heat-map.less"
;
...
@@ -53,20 +54,20 @@ const UsageHeatMap = () => {
...
@@ -53,20 +54,20 @@ const UsageHeatMap = () => {
const
containerElRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
containerElRef
=
useRef
<
HTMLDivElement
>
(
null
);
useEffect
(()
=>
{
useEffect
(()
=>
{
userV1Store
.
getOrFetchUserByUsername
(
user
.
username
).
then
((
user
)
=>
{
userV1Store
.
getOrFetchUserByUsername
(
extractUsernameFromName
(
user
.
name
)
).
then
((
user
)
=>
{
if
(
!
user
)
{
if
(
!
user
)
{
return
;
return
;
}
}
setCreatedDays
(
Math
.
ceil
((
Date
.
now
()
-
getTimeStampByDate
(
user
.
createTime
))
/
1000
/
3600
/
24
));
setCreatedDays
(
Math
.
ceil
((
Date
.
now
()
-
getTimeStampByDate
(
user
.
createTime
))
/
1000
/
3600
/
24
));
});
});
},
[
user
.
user
name
]);
},
[
user
.
name
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
memos
.
length
===
0
)
{
if
(
memos
.
length
===
0
)
{
return
;
return
;
}
}
getMemoStats
(
user
.
username
)
getMemoStats
(
extractUsernameFromName
(
user
.
name
)
)
.
then
(({
data
})
=>
{
.
then
(({
data
})
=>
{
setMemoAmount
(
data
.
length
);
setMemoAmount
(
data
.
length
);
const
newStat
:
DailyUsageStat
[]
=
getInitialUsageStat
(
usedDaysAmount
,
beginDayTimestamp
);
const
newStat
:
DailyUsageStat
[]
=
getInitialUsageStat
(
usedDaysAmount
,
beginDayTimestamp
);
...
@@ -85,7 +86,7 @@ const UsageHeatMap = () => {
...
@@ -85,7 +86,7 @@ const UsageHeatMap = () => {
.
catch
((
error
)
=>
{
.
catch
((
error
)
=>
{
console
.
error
(
error
);
console
.
error
(
error
);
});
});
},
[
memos
.
length
,
user
.
user
name
]);
},
[
memos
.
length
,
user
.
name
]);
const
handleUsageStatItemMouseEnter
=
useCallback
((
event
:
React
.
MouseEvent
,
item
:
DailyUsageStat
)
=>
{
const
handleUsageStatItemMouseEnter
=
useCallback
((
event
:
React
.
MouseEvent
,
item
:
DailyUsageStat
)
=>
{
const
tempDiv
=
document
.
createElement
(
"div"
);
const
tempDiv
=
document
.
createElement
(
"div"
);
...
...
web/src/components/UserBanner.tsx
View file @
e60e47f7
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useGlobalStore
,
useUserStore
}
from
"@/store/module"
;
import
{
useGlobalStore
,
useUserStore
}
from
"@/store/module"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
User_Role
}
from
"@/types/proto/api/v2/user_service"
;
import
{
User_Role
}
from
"@/types/proto/api/v2/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
import
showAboutSiteDialog
from
"./AboutSiteDialog"
;
...
@@ -18,7 +19,7 @@ const UserBanner = () => {
...
@@ -18,7 +19,7 @@ const UserBanner = () => {
const
title
=
user
?
user
.
nickname
:
systemStatus
.
customizedProfile
.
name
||
"memos"
;
const
title
=
user
?
user
.
nickname
:
systemStatus
.
customizedProfile
.
name
||
"memos"
;
const
handleMyAccountClick
=
()
=>
{
const
handleMyAccountClick
=
()
=>
{
navigateTo
(
`/u/
${
encodeURIComponent
(
user
.
username
)}
`
);
navigateTo
(
`/u/
${
encodeURIComponent
(
extractUsernameFromName
(
user
.
name
)
)}
`
);
};
};
const
handleAboutBtnClick
=
()
=>
{
const
handleAboutBtnClick
=
()
=>
{
...
...
web/src/components/kit/DatePicker.tsx
View file @
e60e47f7
...
@@ -5,6 +5,7 @@ import { getMemoStats } from "@/helpers/api";
...
@@ -5,6 +5,7 @@ import { getMemoStats } from "@/helpers/api";
import
{
DAILY_TIMESTAMP
}
from
"@/helpers/consts"
;
import
{
DAILY_TIMESTAMP
}
from
"@/helpers/consts"
;
import
{
getDateStampByDate
,
isFutureDate
}
from
"@/helpers/datetime"
;
import
{
getDateStampByDate
,
isFutureDate
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
Icon
from
"../Icon"
;
import
Icon
from
"../Icon"
;
import
"@/less/common/date-picker.less"
;
import
"@/less/common/date-picker.less"
;
...
@@ -28,7 +29,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
...
@@ -28,7 +29,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
},
[
datestamp
]);
},
[
datestamp
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
getMemoStats
(
user
.
username
).
then
(({
data
})
=>
{
getMemoStats
(
extractUsernameFromName
(
user
.
name
)
).
then
(({
data
})
=>
{
const
m
=
new
Map
();
const
m
=
new
Map
();
for
(
const
record
of
data
)
{
for
(
const
record
of
data
)
{
const
date
=
getDateStampByDate
(
record
*
1000
);
const
date
=
getDateStampByDate
(
record
*
1000
);
...
@@ -36,7 +37,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
...
@@ -36,7 +37,7 @@ const DatePicker: React.FC<DatePickerProps> = (props: DatePickerProps) => {
}
}
setCountByDate
(
m
);
setCountByDate
(
m
);
});
});
},
[
user
.
user
name
]);
},
[
user
.
name
]);
const
firstDate
=
new
Date
(
currentDateStamp
);
const
firstDate
=
new
Date
(
currentDateStamp
);
const
dayList
=
[];
const
dayList
=
[];
...
...
web/src/pages/DailyReview.tsx
View file @
e60e47f7
...
@@ -15,6 +15,7 @@ import { DAILY_TIMESTAMP, DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
...
@@ -15,6 +15,7 @@ import { DAILY_TIMESTAMP, DEFAULT_MEMO_LIMIT } from "@/helpers/consts";
import
{
getDateStampByDate
,
getNormalizedDateString
,
getTimeStampByDate
,
getTimeString
}
from
"@/helpers/datetime"
;
import
{
getDateStampByDate
,
getNormalizedDateString
,
getTimeStampByDate
,
getTimeString
}
from
"@/helpers/datetime"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useMemoStore
,
useUserStore
}
from
"@/store/module"
;
import
{
useMemoStore
,
useUserStore
}
from
"@/store/module"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
const
DailyReview
=
()
=>
{
const
DailyReview
=
()
=>
{
...
@@ -32,7 +33,7 @@ const DailyReview = () => {
...
@@ -32,7 +33,7 @@ const DailyReview = () => {
const
selectedDateStampWithOffset
=
selectedDateStamp
+
localSetting
.
dailyReviewTimeOffset
*
60
*
60
*
1000
;
const
selectedDateStampWithOffset
=
selectedDateStamp
+
localSetting
.
dailyReviewTimeOffset
*
60
*
60
*
1000
;
return
(
return
(
m
.
rowStatus
===
"NORMAL"
&&
m
.
rowStatus
===
"NORMAL"
&&
m
.
creatorUsername
===
user
.
username
&&
m
.
creatorUsername
===
extractUsernameFromName
(
user
.
name
)
&&
displayTimestamp
>=
selectedDateStampWithOffset
&&
displayTimestamp
>=
selectedDateStampWithOffset
&&
displayTimestamp
<
selectedDateStampWithOffset
+
DAILY_TIMESTAMP
displayTimestamp
<
selectedDateStampWithOffset
+
DAILY_TIMESTAMP
);
);
...
...
web/src/pages/MemoDetail.tsx
View file @
e60e47f7
...
@@ -20,6 +20,7 @@ import useCurrentUser from "@/hooks/useCurrentUser";
...
@@ -20,6 +20,7 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
useNavigateTo
from
"@/hooks/useNavigateTo"
;
import
{
useGlobalStore
,
useMemoStore
}
from
"@/store/module"
;
import
{
useGlobalStore
,
useMemoStore
}
from
"@/store/module"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
useUserV1Store
}
from
"@/store/v1"
;
import
{
extractUsernameFromName
}
from
"@/store/v1/resourceName"
;
import
{
User
,
User_Role
}
from
"@/types/proto/api/v2/user_service"
;
import
{
User
,
User_Role
}
from
"@/types/proto/api/v2/user_service"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
...
@@ -35,7 +36,7 @@ const MemoDetail = () => {
...
@@ -35,7 +36,7 @@ const MemoDetail = () => {
const
{
systemStatus
}
=
globalStore
.
state
;
const
{
systemStatus
}
=
globalStore
.
state
;
const
memoId
=
Number
(
params
.
memoId
);
const
memoId
=
Number
(
params
.
memoId
);
const
memo
=
memoStore
.
state
.
memos
.
find
((
memo
)
=>
memo
.
id
===
memoId
);
const
memo
=
memoStore
.
state
.
memos
.
find
((
memo
)
=>
memo
.
id
===
memoId
);
const
allowEdit
=
memo
?.
creatorUsername
===
currentUser
?.
username
;
const
allowEdit
=
memo
?.
creatorUsername
===
extractUsernameFromName
(
currentUser
.
name
)
;
const
referenceRelations
=
memo
?.
relationList
.
filter
((
relation
)
=>
relation
.
type
===
"REFERENCE"
)
||
[];
const
referenceRelations
=
memo
?.
relationList
.
filter
((
relation
)
=>
relation
.
type
===
"REFERENCE"
)
||
[];
const
commentRelations
=
memo
?.
relationList
.
filter
((
relation
)
=>
relation
.
relatedMemoId
===
memo
.
id
&&
relation
.
type
===
"COMMENT"
)
||
[];
const
commentRelations
=
memo
?.
relationList
.
filter
((
relation
)
=>
relation
.
relatedMemoId
===
memo
.
id
&&
relation
.
type
===
"COMMENT"
)
||
[];
const
comments
=
commentRelations
const
comments
=
commentRelations
...
...
web/src/store/v1/resourceName.ts
0 → 100644
View file @
e60e47f7
export
const
UserNamePrefix
=
"users/"
;
export
const
extractUsernameFromName
=
(
name
:
string
)
=>
{
return
name
.
split
(
"/"
)[
1
];
};
web/src/store/v1/user.ts
View file @
e60e47f7
import
{
create
}
from
"zustand"
;
import
{
create
}
from
"zustand"
;
import
{
userServiceClient
}
from
"@/grpcweb"
;
import
{
userServiceClient
}
from
"@/grpcweb"
;
import
{
User
}
from
"@/types/proto/api/v2/user_service"
;
import
{
User
}
from
"@/types/proto/api/v2/user_service"
;
import
{
UserNamePrefix
,
extractUsernameFromName
}
from
"./resourceName"
;
interface
UserV1Store
{
interface
UserV1Store
{
userMapByUsername
:
Record
<
string
,
User
>
;
userMapByUsername
:
Record
<
string
,
User
>
;
...
@@ -25,7 +26,7 @@ const useUserV1Store = create<UserV1Store>()((set, get) => ({
...
@@ -25,7 +26,7 @@ const useUserV1Store = create<UserV1Store>()((set, get) => ({
const
promisedUser
=
userServiceClient
const
promisedUser
=
userServiceClient
.
getUser
({
.
getUser
({
username
:
username
,
name
:
`
${
UserNamePrefix
}${
username
}
`
,
})
})
.
then
(({
user
})
=>
user
);
.
then
(({
user
})
=>
user
);
requestCache
.
set
(
username
,
promisedUser
);
requestCache
.
set
(
username
,
promisedUser
);
...
@@ -50,15 +51,12 @@ const useUserV1Store = create<UserV1Store>()((set, get) => ({
...
@@ -50,15 +51,12 @@ const useUserV1Store = create<UserV1Store>()((set, get) => ({
if
(
!
updatedUser
)
{
if
(
!
updatedUser
)
{
throw
new
Error
(
"User not found"
);
throw
new
Error
(
"User not found"
);
}
}
const
username
=
extractUsernameFromName
(
updatedUser
.
name
);
const
userMap
=
get
().
userMapByUsername
;
const
userMap
=
get
().
userMapByUsername
;
userMap
[
u
pdatedUser
.
u
sername
]
=
updatedUser
;
userMap
[
username
]
=
updatedUser
;
set
(
userMap
);
set
(
userMap
);
return
updatedUser
;
return
updatedUser
;
},
},
}));
}));
export
const
extractUsernameFromName
=
(
name
:
string
)
=>
{
return
name
.
split
(
"/"
)[
1
];
};
export
default
useUserV1Store
;
export
default
useUserV1Store
;
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