Skip to content

Commit ea80664

Browse files
committed
feat: display total amount of operations in profile page
1 parent 15f62a9 commit ea80664

File tree

5 files changed

+103
-72
lines changed

5 files changed

+103
-72
lines changed

src/apis/operation-set.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ export function useOperationSets({
6767
)
6868

6969
const isReachingEnd = !!pages?.some((page) => !page.hasNext)
70+
const total = pages?.[0]?.total ?? 0
7071
const operationSets = pages?.map((page) => page.data).flat()
7172

7273
return {
7374
operationSets,
75+
total,
7476
error,
7577
setSize,
7678
isValidating,
@@ -125,6 +127,7 @@ export function useOperationSetSearch({
125127
if (id) {
126128
return {
127129
operationSets: [operationSet],
130+
total: operationSet ? 1 : 0,
128131
isReachingEnd: true,
129132
setSize: noop,
130133

src/apis/operation.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export function useOperations({
9292
// 如果指定了 id 列表,但是列表为空,就直接返回空数据。不然要是直接传空列表,就相当于没有这个参数,
9393
// 会导致后端返回所有数据
9494
if (req.copilotIds?.length === 0) {
95-
return { data: [], hasNext: false }
95+
return { data: [], hasNext: false, total: 0 }
9696
}
9797

9898
const res = await new OperationApi({
@@ -121,6 +121,7 @@ export function useOperations({
121121
)
122122

123123
const isReachingEnd = !!pages?.some((page) => !page.hasNext)
124+
const total = pages?.[0]?.total ?? 0
124125

125126
const _operations = pages?.map((page) => page.data).flat() ?? []
126127

@@ -134,6 +135,7 @@ export function useOperations({
134135
return {
135136
error,
136137
operations,
138+
total,
137139
setSize,
138140
isValidating,
139141
isReachingEnd,

src/components/OperationList.tsx

Lines changed: 68 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,84 @@ import { Button, NonIdealState } from '@blueprintjs/core'
22

33
import { UseOperationsParams, useOperations } from 'apis/operation'
44
import { useAtomValue } from 'jotai'
5-
import { ComponentType, ReactNode } from 'react'
5+
import { ComponentType, ReactNode, useEffect } from 'react'
66

77
import { neoLayoutAtom } from 'store/pref'
88

99
import { NeoOperationCard, OperationCard } from './OperationCard'
1010
import { withSuspensable } from './Suspensable'
1111

12-
export const OperationList: ComponentType<UseOperationsParams> =
13-
withSuspensable(
14-
(props) => {
15-
const neoLayout = useAtomValue(neoLayoutAtom)
12+
interface OperationListProps extends UseOperationsParams {
13+
onUpdate?: (params: { total: number }) => void
14+
}
1615

17-
const { operations, setSize, isValidating, isReachingEnd } =
18-
useOperations({
19-
...props,
20-
suspense: true,
21-
})
16+
export const OperationList: ComponentType<OperationListProps> = withSuspensable(
17+
({ onUpdate, ...params }) => {
18+
const neoLayout = useAtomValue(neoLayoutAtom)
2219

23-
// make TS happy: we got Suspense out there
24-
if (!operations) throw new Error('unreachable')
20+
const { operations, total, setSize, isValidating, isReachingEnd } =
21+
useOperations({
22+
...params,
23+
suspense: true,
24+
})
2525

26-
const items: ReactNode = neoLayout ? (
27-
<div
28-
className="grid gap-4"
29-
style={{
30-
gridTemplateColumns: 'repeat(auto-fill, minmax(20rem, 1fr)',
31-
}}
32-
>
33-
{operations.map((operation) => (
34-
<NeoOperationCard operation={operation} key={operation.id} />
35-
))}
36-
</div>
37-
) : (
38-
operations.map((operation) => (
39-
<OperationCard operation={operation} key={operation.id} />
40-
))
41-
)
26+
// make TS happy: we got Suspense out there
27+
if (!operations) throw new Error('unreachable')
4228

43-
return (
44-
<>
45-
{items}
29+
useEffect(() => {
30+
onUpdate?.({ total })
31+
}, [total, onUpdate])
4632

47-
{isReachingEnd && operations.length === 0 && (
48-
<NonIdealState
49-
icon="slash"
50-
title="没有找到任何作业"
51-
description="(つД`)・゚・"
52-
/>
53-
)}
33+
const items: ReactNode = neoLayout ? (
34+
<div
35+
className="grid gap-4"
36+
style={{
37+
gridTemplateColumns: 'repeat(auto-fill, minmax(20rem, 1fr)',
38+
}}
39+
>
40+
{operations.map((operation) => (
41+
<NeoOperationCard operation={operation} key={operation.id} />
42+
))}
43+
</div>
44+
) : (
45+
operations.map((operation) => (
46+
<OperationCard operation={operation} key={operation.id} />
47+
))
48+
)
5449

55-
{isReachingEnd && operations.length !== 0 && (
56-
<div className="mt-8 w-full tracking-wider text-center select-none text-slate-500">
57-
已经到底了哦 (゚▽゚)/
58-
</div>
59-
)}
50+
return (
51+
<>
52+
{items}
6053

61-
{!isReachingEnd && (
62-
<Button
63-
loading={isValidating}
64-
text="加载更多"
65-
icon="more"
66-
className="mt-2"
67-
large
68-
fill
69-
onClick={() => setSize((size) => size + 1)}
70-
/>
71-
)}
72-
</>
73-
)
74-
},
75-
{
76-
retryOnChange: ['orderBy', 'keyword', 'levelKeyword', 'operator'],
77-
},
78-
)
54+
{isReachingEnd && operations.length === 0 && (
55+
<NonIdealState
56+
icon="slash"
57+
title="没有找到任何作业"
58+
description="(つД`)・゚・"
59+
/>
60+
)}
61+
62+
{isReachingEnd && operations.length !== 0 && (
63+
<div className="mt-8 w-full tracking-wider text-center select-none text-slate-500">
64+
已经到底了哦 (゚▽゚)/
65+
</div>
66+
)}
67+
68+
{!isReachingEnd && (
69+
<Button
70+
loading={isValidating}
71+
text="加载更多"
72+
icon="more"
73+
className="mt-2"
74+
large
75+
fill
76+
onClick={() => setSize((size) => size + 1)}
77+
/>
78+
)}
79+
</>
80+
)
81+
},
82+
{
83+
retryOnChange: ['orderBy', 'keyword', 'levelKeyword', 'operator'],
84+
},
85+
)

src/components/OperationSetList.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,32 @@ import {
55
useOperationSetSearch,
66
} from 'apis/operation-set'
77
import { useAtomValue } from 'jotai'
8-
import { ComponentType, ReactNode } from 'react'
8+
import { ComponentType, ReactNode, useEffect } from 'react'
99

1010
import { neoLayoutAtom } from 'store/pref'
1111

1212
import { NeoOperationSetCard, OperationSetCard } from './OperationSetCard'
1313
import { withSuspensable } from './Suspensable'
1414

15-
export const OperationSetList: ComponentType<UseOperationSetsParams> =
15+
interface OperationSetListProps extends UseOperationSetsParams {
16+
onUpdate?: (params: { total: number }) => void
17+
}
18+
19+
export const OperationSetList: ComponentType<OperationSetListProps> =
1620
withSuspensable(
17-
(props) => {
21+
({ onUpdate, ...params }) => {
1822
const neoLayout = useAtomValue(neoLayoutAtom)
1923

20-
const { operationSets, setSize, isValidating, isReachingEnd } =
21-
useOperationSetSearch({ ...props, suspense: true })
24+
const { operationSets, total, setSize, isValidating, isReachingEnd } =
25+
useOperationSetSearch({ ...params, suspense: true })
2226

2327
// make TS happy: we got Suspense out there
2428
if (!operationSets) throw new Error('unreachable')
2529

30+
useEffect(() => {
31+
onUpdate?.({ total })
32+
}, [total, onUpdate])
33+
2634
const items: ReactNode = neoLayout ? (
2735
<div
2836
className="grid gap-4"

src/pages/profile.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Button, ButtonGroup, Card } from '@blueprintjs/core'
33
import { ComponentType, useState } from 'react'
44
import { Navigate, useParams } from 'react-router-dom'
55

6-
import { withGlobalErrorBoundary } from 'components/GlobalErrorBoundary'
76
import { OperationList } from 'components/OperationList'
87
import { OperationSetList } from 'components/OperationSetList'
98
import { OperationDrawer } from 'components/drawer/OperationDrawer'
@@ -25,6 +24,8 @@ const _ProfilePage: ComponentType = () => {
2524
const [listMode, setListMode] = useState<'operation' | 'operationSet'>(
2625
'operation',
2726
)
27+
const [operationCount, setOperationCount] = useState(-1)
28+
const [operationSetCount, setOperationSetCount] = useState(-1)
2829

2930
return (
3031
<div className="flex flex-col md:flex-row px-8 mt-8 max-w-[96rem] mx-auto">
@@ -36,23 +37,33 @@ const _ProfilePage: ComponentType = () => {
3637
active={listMode === 'operation'}
3738
onClick={() => setListMode('operation')}
3839
>
39-
作业
40+
作业{operationCount === -1 ? '' : ` (${operationCount})`}
4041
</Button>
4142
<Button
4243
icon="folder-close"
4344
active={listMode === 'operationSet'}
4445
onClick={() => setListMode('operationSet')}
4546
>
46-
作业集
47+
作业集{operationSetCount === -1 ? '' : ` (${operationSetCount})`}
4748
</Button>
4849
</ButtonGroup>
4950
</div>
5051

5152
<div className="tabular-nums">
5253
{listMode === 'operation' && (
53-
<OperationList limit={10} orderBy="id" uploaderId={id} />
54+
<OperationList
55+
limit={10}
56+
orderBy="id"
57+
uploaderId={id}
58+
onUpdate={({ total }) => setOperationCount(total)}
59+
/>
60+
)}
61+
{listMode === 'operationSet' && (
62+
<OperationSetList
63+
creatorId={id}
64+
onUpdate={({ total }) => setOperationSetCount(total)}
65+
/>
5466
)}
55-
{listMode === 'operationSet' && <OperationSetList creatorId={id} />}
5667
</div>
5768
</div>
5869
<div className="md:w-1/3 order-1 md:order-2">

0 commit comments

Comments
 (0)