Skip to content

Commit f6741b0

Browse files
committed
Tweak logic to fix capture upload duplicates
1 parent 0a0ea5e commit f6741b0

File tree

2 files changed

+87
-38
lines changed

2 files changed

+87
-38
lines changed

ui/src/data-services/hooks/captures/useUploadCapture.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface UploadCaptureFieldValues {
99
file: File
1010
}
1111

12-
export const useUploadCapture = (onSuccess?: () => void) => {
12+
export const useUploadCapture = (onSuccess?: (id: string) => void) => {
1313
const { user } = useUser()
1414
const queryClient = useQueryClient()
1515

@@ -25,16 +25,20 @@ export const useUploadCapture = (onSuccess?: () => void) => {
2525
data.append('image', '')
2626
}
2727

28-
return axios.post(`${API_URL}/${API_ROUTES.CAPTURES}/upload/`, data, {
29-
headers: {
30-
...getAuthHeader(user),
31-
'Content-Type': 'multipart/form-data',
32-
},
33-
})
28+
return axios.post<{ source_image: { id: number } }>(
29+
`${API_URL}/${API_ROUTES.CAPTURES}/upload/`,
30+
data,
31+
{
32+
headers: {
33+
...getAuthHeader(user),
34+
'Content-Type': 'multipart/form-data',
35+
},
36+
}
37+
)
3438
},
35-
onSuccess: () => {
39+
onSuccess: ({ data }) => {
3640
queryClient.invalidateQueries([API_ROUTES.DEPLOYMENTS])
37-
onSuccess?.()
41+
onSuccess?.(`${data.source_image.id}`)
3842
},
3943
})
4044

ui/src/pages/deployment-details/deployment-details-form/section-example-captures/section-example-captures.tsx

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,21 @@ export const SectionExampleCaptures = ({
4545
}: {
4646
deployment: DeploymentDetails
4747
}) => {
48-
const [files, setFiles] = useState<File[]>([])
48+
const [addQueue, setAddQueue] = useState<
49+
{ file: File; id: string | undefined; uploaded?: boolean }[]
50+
>([])
51+
52+
useEffect(() => {
53+
// Remove items from queue when deployment capture data is updated to free memory
54+
setAddQueue((prev) =>
55+
prev.filter(({ id }) => {
56+
const deploymentHasCapture = deployment.exampleCaptures.some(
57+
(exampleCapture) => exampleCapture.id === id
58+
)
59+
return !deploymentHasCapture
60+
})
61+
)
62+
}, [deployment.exampleCaptures])
4963

5064
if (!deployment.createdAt) {
5165
return (
@@ -57,7 +71,7 @@ export const SectionExampleCaptures = ({
5771
}
5872

5973
const canUpload =
60-
deployment.exampleCaptures.length + files.length <
74+
deployment.exampleCaptures.length + addQueue.length <
6175
CAPTURE_CONFIG.NUM_CAPTURES
6276

6377
return (
@@ -74,15 +88,38 @@ export const SectionExampleCaptures = ({
7488
/>
7589
))}
7690

77-
{files.map((file, index) => (
78-
<AddedExampleCapture
79-
key={index}
80-
deploymentId={deployment.id}
81-
file={file}
82-
index={deployment.exampleCaptures.length + index}
83-
onUploaded={() => setFiles(files.filter((f) => f !== file))}
84-
/>
85-
))}
91+
{addQueue
92+
.filter(({ id }) => {
93+
// Only render queue items that are not yet part of deployment captures
94+
const deploymentHasCapture = deployment.exampleCaptures.some(
95+
(exampleCapture) => exampleCapture.id === id
96+
)
97+
return !deploymentHasCapture
98+
})
99+
.map(({ file }, index) => (
100+
<AddedExampleCapture
101+
key={file.name}
102+
deploymentId={deployment.id}
103+
file={file}
104+
index={deployment.exampleCaptures.length + index}
105+
onCancel={() => {
106+
// Remove item from queue
107+
setAddQueue((prev) => prev.filter(({ file }) => file !== file))
108+
}}
109+
onSuccess={(id: string) => {
110+
// Update queue item with upload status
111+
setAddQueue((prev) =>
112+
prev.map((item) => {
113+
if (item.file === file) {
114+
item.id = id
115+
item.uploaded = true
116+
}
117+
return item
118+
})
119+
)
120+
}}
121+
/>
122+
))}
86123

87124
{canUpload && (
88125
<Card>
@@ -98,9 +135,19 @@ export const SectionExampleCaptures = ({
98135
theme={IconButtonTheme.Success}
99136
/>
100137
)}
101-
onChange={(newFiles) =>
102-
setFiles([...files, ...Array.from(newFiles ?? [])])
103-
}
138+
onChange={(newFiles) => {
139+
if (!newFiles) {
140+
return
141+
}
142+
143+
setAddQueue((prev) => [
144+
...prev,
145+
...Array.from(newFiles).map((file) => ({
146+
file,
147+
id: undefined,
148+
})),
149+
])
150+
}}
104151
/>
105152
</Card>
106153
)}
@@ -135,44 +182,40 @@ const ExampleCapture = ({ id, src }: { id: string; src: string }) => (
135182

136183
const AddedExampleCapture = ({
137184
deploymentId,
138-
index,
139185
file,
140-
onUploaded,
186+
index,
187+
onCancel,
188+
onSuccess,
141189
}: {
142190
deploymentId: string
143-
file: File
144191
index: number
145-
onUploaded: () => void
192+
file: File
193+
onCancel: () => void
194+
onSuccess: (id: string) => void
146195
}) => {
147-
const { uploadCapture, isLoading, isSuccess, error } =
148-
useUploadCapture(onUploaded)
149-
196+
const { uploadCapture, error } = useUploadCapture(onSuccess)
150197
const { isValid, errorMessage, allowRetry } = useCaptureError({
151198
error,
152199
file,
153200
index,
154201
})
155202

156203
useEffect(() => {
157-
if (!isValid || isSuccess) {
204+
if (!isValid) {
158205
return
159206
}
160207

208+
// Trigger capture upload on component mount
161209
uploadCapture({ deploymentId, file })
162210
}, [])
163211

164-
if (isSuccess) {
165-
return null
166-
}
167-
168212
return (
169213
<Card>
170214
<div className={styles.cardContent}>
171215
<img src={URL.createObjectURL(file)} />
172216
</div>
173217
<div className={styles.cardContent}>
174-
{isLoading ? <LoadingSpinner size={32} /> : null}
175-
{errorMessage && (
218+
{errorMessage ? (
176219
<>
177220
<Tooltip content={errorMessage}>
178221
{allowRetry ? (
@@ -194,10 +237,12 @@ const AddedExampleCapture = ({
194237
<IconButton
195238
icon={IconType.Cross}
196239
shape={IconButtonShape.Round}
197-
onClick={onUploaded}
240+
onClick={onCancel}
198241
/>
199242
</div>
200243
</>
244+
) : (
245+
<LoadingSpinner size={32} />
201246
)}
202247
</div>
203248
</Card>

0 commit comments

Comments
 (0)