-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathuseClubImages.ts
More file actions
182 lines (156 loc) · 4.73 KB
/
Copy pathuseClubImages.ts
File metadata and controls
182 lines (156 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { feedApi, logoApi, uploadToStorage } from '@/apis/image';
import { queryKeys } from '@/constants/queryKeys';
type ItemStatus = 'pending' | 'uploading' | 'failed';
interface FeedUploadParams {
clubId: string;
files: File[];
existingUrls: string[];
onItemStatusChange?: (index: number, status: ItemStatus) => void;
}
interface FeedUpdateParams {
clubId: string;
urls: string[];
}
interface LogoUploadParams {
clubId: string;
file: File;
}
export const useUploadFeed = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
clubId,
files,
existingUrls,
onItemStatusChange,
}: FeedUploadParams) => {
// 1. presigned URL 요청
const ALLOWED_TYPES = [
'image/jpeg',
'image/jpg',
'image/png',
'image/gif',
'image/bmp',
'image/webp',
];
const uploadRequests = files.map((file) => ({
fileName: file.name,
contentType: ALLOWED_TYPES.includes(file.type)
? file.type
: 'image/jpeg',
}));
const feedResArr = await feedApi.getUploadUrls(clubId, uploadRequests);
if (!feedResArr) {
throw new Error('피드 업로드 URL 생성 실패');
}
// 2. r2에 병렬 업로드 (개별 성공/실패 추적)
// presigned URL 생성 자체가 실패한 항목은 업로드 건너뜀
const uploadResults = await Promise.allSettled(
files.map((file, i) => {
if (!feedResArr[i].success || !feedResArr[i].presignedUrl) {
return Promise.reject(
new Error(
feedResArr[i].failureReason ?? 'presigned URL 생성 실패',
),
);
}
return uploadToStorage(feedResArr[i].presignedUrl, file);
}),
);
// 3. 성공한 파일만 추출
const successfulUrls: string[] = [];
const failedFiles: string[] = [];
uploadResults.forEach((result, i) => {
if (result.status === 'fulfilled') {
successfulUrls.push(feedResArr[i].finalUrl);
} else {
failedFiles.push(files[i].name);
onItemStatusChange?.(i, 'failed');
}
});
// 4. 성공한 파일이 없으면 에러
if (successfulUrls.length === 0) {
throw new Error('모든 파일 업로드에 실패했습니다.');
}
// 5. 기존 URL과 성공한 URL만 합쳐서 전체 배열 생성
const allUrls = [...existingUrls, ...successfulUrls];
// 6. 서버에 전체 배열 PUT으로 갱신
await feedApi.updateFeeds(clubId, allUrls);
// 7. 실패한 파일 정보 및 성공 URL 반환
return { clubId, failedFiles, successfulUrls };
},
onSuccess: (data) => {
queryClient.invalidateQueries({
queryKey: queryKeys.club.detail(data.clubId),
});
},
onError: () => {
console.error('Error uploading feed images');
},
});
};
export const useUpdateFeed = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ clubId, urls }: FeedUpdateParams) => {
await feedApi.updateFeeds(clubId, urls);
return { clubId };
},
onSuccess: (data) => {
queryClient.invalidateQueries({
queryKey: queryKeys.club.detail(data.clubId),
});
},
onError: () => {
console.error('Error updating feed images');
},
});
};
export const useUploadLogo = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ clubId, file }: LogoUploadParams) => {
// 1. presigned URL 받기
const uploadUrlData = await logoApi.getUploadUrl(
clubId,
file.name,
file.type,
);
if (!uploadUrlData) {
throw new Error('로고 업로드 URL 생성 실패');
}
const { presignedUrl, finalUrl } = uploadUrlData;
// 2. r2 업로드
await uploadToStorage(presignedUrl, file);
// 3. 완료 처리
await logoApi.completeUpload(clubId, finalUrl);
return { finalUrl, clubId };
},
onSuccess: (data) => {
queryClient.invalidateQueries({
queryKey: queryKeys.club.detail(data.clubId),
});
},
onError: () => {
console.error('Error uploading logo');
},
});
};
export const useDeleteLogo = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (clubId: string) => {
await logoApi.delete(clubId);
return clubId;
},
onSuccess: (clubId) => {
queryClient.invalidateQueries({
queryKey: queryKeys.club.detail(clubId),
});
},
onError: () => {
console.error('Error deleting logo');
},
});
};