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
c76ab87a
Commit
c76ab87a
authored
Jan 15, 2025
by
johnnyjoy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: update list user stats
parent
8b65d248
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
70 additions
and
54 deletions
+70
-54
user_service.proto
proto/api/v1/user_service.proto
+1
-9
user_service_stats.go
server/router/api/v1/user_service_stats.go
+64
-36
ExploreSidebar.tsx
web/src/components/ExploreSidebar/ExploreSidebar.tsx
+1
-4
HomeSidebar.tsx
web/src/components/HomeSidebar/HomeSidebar.tsx
+1
-2
userStats.ts
web/src/store/v1/userStats.ts
+3
-3
No files found.
proto/api/v1/user_service.proto
View file @
c76ab87a
...
@@ -198,11 +198,7 @@ message UserStats {
...
@@ -198,11 +198,7 @@ message UserStats {
}
}
}
}
message
ListAllUserStatsRequest
{
message
ListAllUserStatsRequest
{}
// Filter is used to filter memos to calculate stats.
// Same as `ListMemosRequest.filter`.
string
filter
=
1
;
}
message
ListAllUserStatsResponse
{
message
ListAllUserStatsResponse
{
repeated
UserStats
user_stats
=
1
;
repeated
UserStats
user_stats
=
1
;
...
@@ -212,10 +208,6 @@ message GetUserStatsRequest {
...
@@ -212,10 +208,6 @@ message GetUserStatsRequest {
// The name of the user.
// The name of the user.
// Format: users/{user}.
// Format: users/{user}.
string
name
=
1
;
string
name
=
1
;
// Filter is used to filter memos to calculate stats.
// Same as `ListMemosRequest.filter`.
string
filter
=
2
;
}
}
message
UserSetting
{
message
UserSetting
{
...
...
server/router/api/v1/user_service_stats.go
View file @
c76ab87a
...
@@ -3,7 +3,6 @@ package v1
...
@@ -3,7 +3,6 @@ package v1
import
(
import
(
"context"
"context"
"fmt"
"fmt"
"slices"
"time"
"time"
"github.com/pkg/errors"
"github.com/pkg/errors"
...
@@ -16,19 +15,66 @@ import (
...
@@ -16,19 +15,66 @@ import (
)
)
func
(
s
*
APIV1Service
)
ListAllUserStats
(
ctx
context
.
Context
,
request
*
v1pb
.
ListAllUserStatsRequest
)
(
*
v1pb
.
ListAllUserStatsResponse
,
error
)
{
func
(
s
*
APIV1Service
)
ListAllUserStats
(
ctx
context
.
Context
,
request
*
v1pb
.
ListAllUserStatsRequest
)
(
*
v1pb
.
ListAllUserStatsResponse
,
error
)
{
users
,
err
:=
s
.
Store
.
ListUsers
(
ctx
,
&
store
.
FindUser
{}
)
currentUser
,
err
:=
s
.
GetCurrentUser
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to
list users
: %v"
,
err
)
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to
get user
: %v"
,
err
)
}
}
userStatsList
:=
[]
*
v1pb
.
UserStats
{
}
visibilities
:=
[]
store
.
Visibility
{
store
.
Public
}
for
_
,
user
:=
range
users
{
if
currentUser
!=
nil
{
userStats
,
err
:=
s
.
GetUserStats
(
ctx
,
&
v1pb
.
GetUserStatsRequest
{
visibilities
=
append
(
visibilities
,
store
.
Protected
)
Name
:
fmt
.
Sprintf
(
"%s%d"
,
UserNamePrefix
,
user
.
ID
),
}
Filter
:
request
.
Filter
,
}
)
workspaceMemoRelatedSetting
,
err
:=
s
.
Store
.
GetWorkspaceMemoRelatedSetting
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to get user stats: %v"
,
err
)
return
nil
,
errors
.
Wrap
(
err
,
"failed to get workspace memo related setting"
)
}
}
memoFind
:=
&
store
.
FindMemo
{
// Exclude comments by default.
ExcludeComments
:
true
,
ExcludeContent
:
true
,
VisibilityList
:
visibilities
,
}
memos
,
err
:=
s
.
Store
.
ListMemos
(
ctx
,
memoFind
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to list memos: %v"
,
err
)
}
userStatsMap
:=
map
[
string
]
*
v1pb
.
UserStats
{}
for
_
,
memo
:=
range
memos
{
creator
:=
fmt
.
Sprintf
(
"%s%d"
,
UserNamePrefix
,
memo
.
CreatorID
)
if
_
,
ok
:=
userStatsMap
[
creator
];
!
ok
{
userStatsMap
[
creator
]
=
&
v1pb
.
UserStats
{
Name
:
creator
,
MemoDisplayTimestamps
:
[]
*
timestamppb
.
Timestamp
{},
MemoTypeStats
:
&
v1pb
.
UserStats_MemoTypeStats
{},
TagCount
:
map
[
string
]
int32
{},
}
}
displayTs
:=
memo
.
CreatedTs
if
workspaceMemoRelatedSetting
.
DisplayWithUpdateTime
{
displayTs
=
memo
.
UpdatedTs
}
userStats
:=
userStatsMap
[
creator
]
userStats
.
MemoDisplayTimestamps
=
append
(
userStats
.
MemoDisplayTimestamps
,
timestamppb
.
New
(
time
.
Unix
(
displayTs
,
0
)))
// Handle duplicated tags.
for
_
,
tag
:=
range
memo
.
Payload
.
Tags
{
userStats
.
TagCount
[
tag
]
++
}
if
memo
.
Payload
.
Property
.
GetHasLink
()
{
userStats
.
MemoTypeStats
.
LinkCount
++
}
if
memo
.
Payload
.
Property
.
GetHasCode
()
{
userStats
.
MemoTypeStats
.
CodeCount
++
}
if
memo
.
Payload
.
Property
.
GetHasTaskList
()
{
userStats
.
MemoTypeStats
.
TodoCount
++
}
if
memo
.
Payload
.
Property
.
GetHasIncompleteTasks
()
{
userStats
.
MemoTypeStats
.
UndoCount
++
}
}
userStatsList
:=
[]
*
v1pb
.
UserStats
{}
for
_
,
userStats
:=
range
userStatsMap
{
userStatsList
=
append
(
userStatsList
,
userStats
)
userStatsList
=
append
(
userStatsList
,
userStats
)
}
}
return
&
v1pb
.
ListAllUserStatsResponse
{
return
&
v1pb
.
ListAllUserStatsResponse
{
...
@@ -60,16 +106,13 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
...
@@ -60,16 +106,13 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
// Exclude comments by default.
// Exclude comments by default.
ExcludeComments
:
true
,
ExcludeComments
:
true
,
ExcludeContent
:
true
,
ExcludeContent
:
true
,
}
CreatorID
:
&
userID
,
if
err
:=
s
.
buildMemoFindWithFilter
(
ctx
,
memoFind
,
request
.
Filter
);
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"failed to build find memos with filter: %v"
,
err
)
}
}
currentUser
,
err
:=
s
.
GetCurrentUser
(
ctx
)
currentUser
,
err
:=
s
.
GetCurrentUser
(
ctx
)
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
len
(
memoFind
.
VisibilityList
)
==
0
{
visibilities
:=
[]
store
.
Visibility
{
store
.
Public
}
visibilities
:=
[]
store
.
Visibility
{
store
.
Public
}
if
currentUser
!=
nil
{
if
currentUser
!=
nil
{
visibilities
=
append
(
visibilities
,
store
.
Protected
)
visibilities
=
append
(
visibilities
,
store
.
Protected
)
...
@@ -78,21 +121,6 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
...
@@ -78,21 +121,6 @@ func (s *APIV1Service) GetUserStats(ctx context.Context, request *v1pb.GetUserSt
}
}
}
}
memoFind
.
VisibilityList
=
visibilities
memoFind
.
VisibilityList
=
visibilities
}
else
{
if
slices
.
Contains
(
memoFind
.
VisibilityList
,
store
.
Private
)
{
if
currentUser
==
nil
||
currentUser
.
ID
!=
user
.
ID
{
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
}
}
if
slices
.
Contains
(
memoFind
.
VisibilityList
,
store
.
Protected
)
{
if
currentUser
==
nil
{
return
nil
,
status
.
Errorf
(
codes
.
PermissionDenied
,
"permission denied"
)
}
}
}
// Override the creator ID.
memoFind
.
CreatorID
=
&
user
.
ID
memos
,
err
:=
s
.
Store
.
ListMemos
(
ctx
,
memoFind
)
memos
,
err
:=
s
.
Store
.
ListMemos
(
ctx
,
memoFind
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to list memos: %v"
,
err
)
return
nil
,
status
.
Errorf
(
codes
.
Internal
,
"failed to list memos: %v"
,
err
)
...
...
web/src/components/ExploreSidebar/ExploreSidebar.tsx
View file @
c76ab87a
import
clsx
from
"clsx"
;
import
clsx
from
"clsx"
;
import
useDebounce
from
"react-use/lib/useDebounce"
;
import
useDebounce
from
"react-use/lib/useDebounce"
;
import
SearchBar
from
"@/components/SearchBar"
;
import
SearchBar
from
"@/components/SearchBar"
;
import
useCurrentUser
from
"@/hooks/useCurrentUser"
;
import
{
useUserStatsStore
}
from
"@/store/v1"
;
import
{
useUserStatsStore
}
from
"@/store/v1"
;
import
TagsSection
from
"../HomeSidebar/TagsSection"
;
import
TagsSection
from
"../HomeSidebar/TagsSection"
;
import
StatisticsView
from
"../StatisticsView"
;
import
StatisticsView
from
"../StatisticsView"
;
...
@@ -11,13 +10,11 @@ interface Props {
...
@@ -11,13 +10,11 @@ interface Props {
}
}
const
ExploreSidebar
=
(
props
:
Props
)
=>
{
const
ExploreSidebar
=
(
props
:
Props
)
=>
{
const
currentUser
=
useCurrentUser
();
const
userStatsStore
=
useUserStatsStore
();
const
userStatsStore
=
useUserStatsStore
();
useDebounce
(
useDebounce
(
async
()
=>
{
async
()
=>
{
const
filters
=
[
`state == "NORMAL"`
,
`visibilities == [
${
currentUser
?
"'PUBLIC', 'PROTECTED'"
:
"'PUBLIC'"
}
]`
];
userStatsStore
.
listUserStats
();
userStatsStore
.
listUserStats
(
undefined
,
filters
.
join
(
" && "
));
},
},
300
,
300
,
[],
[],
...
...
web/src/components/HomeSidebar/HomeSidebar.tsx
View file @
c76ab87a
...
@@ -17,8 +17,7 @@ const HomeSidebar = (props: Props) => {
...
@@ -17,8 +17,7 @@ const HomeSidebar = (props: Props) => {
useDebounce
(
useDebounce
(
async
()
=>
{
async
()
=>
{
const
filters
=
[
`state == "NORMAL"`
,
`creator == "
${
currentUser
.
name
}
"`
];
await
userStatsStore
.
listUserStats
(
currentUser
.
name
);
await
userStatsStore
.
listUserStats
(
currentUser
.
name
,
filters
.
join
(
" && "
));
},
},
300
,
300
,
[
memoList
.
size
(),
currentUser
],
[
memoList
.
size
(),
currentUser
],
...
...
web/src/store/v1/userStats.ts
View file @
c76ab87a
...
@@ -20,15 +20,15 @@ export const useUserStatsStore = create(
...
@@ -20,15 +20,15 @@ export const useUserStatsStore = create(
combine
(
getDefaultState
(),
(
set
,
get
)
=>
({
combine
(
getDefaultState
(),
(
set
,
get
)
=>
({
setState
:
(
state
:
State
)
=>
set
(
state
),
setState
:
(
state
:
State
)
=>
set
(
state
),
getState
:
()
=>
get
(),
getState
:
()
=>
get
(),
listUserStats
:
async
(
user
?:
string
,
filter
?:
string
)
=>
{
listUserStats
:
async
(
user
?:
string
)
=>
{
const
userStatsByName
:
Record
<
string
,
UserStats
>
=
{};
const
userStatsByName
:
Record
<
string
,
UserStats
>
=
{};
if
(
!
user
)
{
if
(
!
user
)
{
const
{
userStats
}
=
await
userServiceClient
.
listAllUserStats
({
filter
});
const
{
userStats
}
=
await
userServiceClient
.
listAllUserStats
({});
for
(
const
stats
of
userStats
)
{
for
(
const
stats
of
userStats
)
{
userStatsByName
[
stats
.
name
]
=
stats
;
userStatsByName
[
stats
.
name
]
=
stats
;
}
}
}
else
{
}
else
{
const
userStats
=
await
userServiceClient
.
getUserStats
({
name
:
user
,
filter
});
const
userStats
=
await
userServiceClient
.
getUserStats
({
name
:
user
});
userStatsByName
[
user
]
=
userStats
;
userStatsByName
[
user
]
=
userStats
;
}
}
set
({
stateId
:
uniqueId
(),
userStatsByName
});
set
({
stateId
:
uniqueId
(),
userStatsByName
});
...
...
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