Skip to content

Commit 4a5b491

Browse files
committed
[Feat] #202 - 현재 폴더 선택 불가 및 복제시 다중 폴더 선택 가능하게 수정
1 parent fc3af95 commit 4a5b491

6 files changed

Lines changed: 64 additions & 58 deletions

File tree

Neki-iOS/Features/Archive/Sources/Presentation/Sources/Feature/AlbumSelectionFeature.swift

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ struct AlbumSelectionFeature {
1313
@ObservableState
1414
struct State {
1515
var albums: IdentifiedArrayOf<AlbumItem> = []
16-
var selectedAlbumId: Int? = nil
16+
var selectedAlbumIDs: Set<Int> = []
1717
var uploadCount: Int
1818
var isFetching: Bool = false
1919

20+
var selectionPurpose: PhotoSelectionPurpose
21+
var currentAlbumId: Int?
22+
2023
// 앨범 생성관련
2124
var newAlbumTitle: String = ""
2225
var albumTitleErrorMessage: String? = nil
@@ -25,8 +28,10 @@ struct AlbumSelectionFeature {
2528
return !newAlbumTitle.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && albumTitleErrorMessage == nil
2629
}
2730

28-
init(uploadCount: Int = 1) {
31+
init(uploadCount: Int = 1, selectionPurpose: PhotoSelectionPurpose, currentAlbumId: Int? = nil) {
2932
self.uploadCount = uploadCount
33+
self.selectionPurpose = selectionPurpose
34+
self.currentAlbumId = currentAlbumId
3035
}
3136
}
3237

@@ -50,7 +55,7 @@ struct AlbumSelectionFeature {
5055

5156
case delegate(DelegateAction)
5257
enum DelegateAction {
53-
case didSelectAlbum(albumId: Int)
58+
case didSelectAlbums(albumIds: [Int])
5459
case didTapCancel
5560
case showToast(NekiToastItem)
5661
}
@@ -108,19 +113,30 @@ struct AlbumSelectionFeature {
108113
return .send(.delegate(.didTapCancel))
109114

110115
case let .tapAlbum(id):
111-
// 즐겨찾기 앨범은 선택 불가하게
116+
// 즐겨찾기 앨범이거나, 현재 진입한 앨범이면 선택 무시
112117
guard id != -1 else { return .none }
118+
guard id != state.currentAlbumId else { return .none }
113119

114-
if state.selectedAlbumId == id {
115-
state.selectedAlbumId = nil
120+
if state.selectionPurpose == .move {
121+
// [사진 이동] 단일 선택 처리
122+
if state.selectedAlbumIDs.contains(id) {
123+
state.selectedAlbumIDs.removeAll()
124+
} else {
125+
state.selectedAlbumIDs = [id]
126+
}
116127
} else {
117-
state.selectedAlbumId = id
128+
// [사진 복제] 다중 선택 처리
129+
if state.selectedAlbumIDs.contains(id) {
130+
state.selectedAlbumIDs.remove(id)
131+
} else {
132+
state.selectedAlbumIDs.insert(id)
133+
}
118134
}
119135
return .none
120136

121137
case .tapConfirm:
122-
guard let id = state.selectedAlbumId else { return .none }
123-
return .send(.delegate(.didSelectAlbum(albumId: id)))
138+
guard !state.selectedAlbumIDs.isEmpty else { return .none }
139+
return .send(.delegate(.didSelectAlbums(albumIds: Array(state.selectedAlbumIDs))))
124140

125141
// MARK: - 앨범 생성 관련 처리
126142
case .onTapCancelAddAlbum:

Neki-iOS/Features/Archive/Sources/Presentation/Sources/Feature/ArchiveAlbumDetailFeature.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,19 +226,18 @@ struct ArchiveAlbumDetailFeature {
226226

227227
case .onTapDuplicateButton:
228228
state.selectionPurpose = .duplicate
229-
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count)
229+
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count, selectionPurpose: .duplicate, currentAlbumId: state.album.id)
230230
return .none
231231

232232
case .onTapMoveButton:
233233
state.selectionPurpose = .move
234-
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count)
234+
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count, selectionPurpose: .move, currentAlbumId: state.album.id)
235235
return .none
236236

237237
case let .albumSelection(.presented(.delegate(delegateAction))):
238238
switch delegateAction {
239-
case let .didSelectAlbum(albumId):
239+
case let .didSelectAlbums(albumIds):
240240
let purpose = state.selectionPurpose
241-
let selectedIDs = Array(state.selectedIDs)
242241

243242
state.albumSelection = nil
244243
state.selectionPurpose = nil
@@ -288,7 +287,6 @@ struct ArchiveAlbumDetailFeature {
288287
state.isLoading = false
289288
return .send(.delegate(.showToast(NekiToastItem("사진 이동에 실패했어요", style: .error))))
290289

291-
292290
case .onTapDownloadButton:
293291
guard !state.selectedIDs.isEmpty else { return .none }
294292
state.isLoading = true
@@ -352,7 +350,7 @@ struct ArchiveAlbumDetailFeature {
352350

353351
case let .photoImport(.presented(.delegate(delegateAction))):
354352
switch delegateAction {
355-
case let .didImportPhotos(photoIDs):
353+
case .didImportPhotos:
356354
state.photoImport = nil
357355
state.isLoading = true
358356

Neki-iOS/Features/Archive/Sources/Presentation/Sources/Feature/ArchiveAllPhotosFeature.swift

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,31 +178,30 @@ struct ArchiveAllPhotosFeature {
178178

179179
case .onTapDuplicateButton:
180180
state.selectionPurpose = .duplicate
181-
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count)
181+
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count, selectionPurpose: .duplicate, currentAlbumId: nil)
182182
return .none
183183

184184
case .onTapMoveButton:
185185
state.selectionPurpose = .move
186-
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count)
186+
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count, selectionPurpose: .move, currentAlbumId: nil)
187187
return .none
188188

189189
case let .albumSelection(.presented(.delegate(delegateAction))):
190190
switch delegateAction {
191-
case let .didSelectAlbum(albumId):
191+
case .didSelectAlbums:
192192
let purpose = state.selectionPurpose
193193

194194
state.isLoading = true
195195
state.albumSelection = nil
196196
state.selectionPurpose = nil
197197

198+
// TODO: - 실제 API 연결하기
198199
return .run { send in
199200
if purpose == .duplicate {
200-
// TODO: API 붙이기
201-
try? await Task.sleep(for: .seconds(1)) // 테스트용 임시 딜레이
201+
try? await Task.sleep(for: .seconds(1))
202202
await send(.duplicatePhotosResponse(.success(())))
203203
} else {
204-
// TODO: API 붙이기
205-
try? await Task.sleep(for: .seconds(1)) // 테스트용 임시 딜레이
204+
try? await Task.sleep(for: .seconds(1))
206205
await send(.movePhotosResponse(.success(())))
207206
}
208207
}
@@ -230,7 +229,10 @@ struct ArchiveAllPhotosFeature {
230229
state.isLoading = false
231230
state.isSelectionMode = false
232231
state.selectedIDs.removeAll()
233-
return .send(.delegate(.showToast(NekiToastItem("사진을 앨범으로 이동했어요", style: .success))))
232+
return .merge(
233+
.send(.delegate(.showToast(NekiToastItem("사진을 앨범으로 이동했어요", style: .success)))),
234+
.send(.fetchPhotos)
235+
)
234236

235237
case .movePhotosResponse(.failure):
236238
state.isLoading = false

Neki-iOS/Features/Archive/Sources/Presentation/Sources/Feature/ArchiveFavoriteAlbumFeature.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,31 +175,30 @@ struct ArchiveFavoriteAlbumFeature {
175175

176176
case .onTapDuplicateButton:
177177
state.selectionPurpose = .duplicate
178-
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count)
178+
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count, selectionPurpose: .duplicate, currentAlbumId: state.album.id)
179179
return .none
180180

181181
case .onTapMoveButton:
182182
state.selectionPurpose = .move
183-
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count)
183+
state.albumSelection = AlbumSelectionFeature.State(uploadCount: state.selectedIDs.count, selectionPurpose: .move, currentAlbumId: state.album.id)
184184
return .none
185185

186186
case let .albumSelection(.presented(.delegate(delegateAction))):
187187
switch delegateAction {
188-
case let .didSelectAlbum(albumId):
188+
case .didSelectAlbums:
189189
let purpose = state.selectionPurpose
190190

191191
state.albumSelection = nil
192192
state.selectionPurpose = nil
193193
state.isLoading = true
194194

195+
// TODO: - 실제 API 연결하기
195196
return .run { send in
196197
if purpose == .duplicate {
197-
// TODO: API 붙이기
198-
try? await Task.sleep(for: .seconds(1)) // 테스트용 임시 딜레이
198+
try? await Task.sleep(for: .seconds(1))
199199
await send(.duplicatePhotosResponse(.success(())))
200200
} else {
201-
// TODO: API 붙이기
202-
try? await Task.sleep(for: .seconds(1)) // 테스트용 임시 딜레이
201+
try? await Task.sleep(for: .seconds(1))
203202
await send(.movePhotosResponse(.success(())))
204203
}
205204
}
@@ -221,7 +220,7 @@ struct ArchiveFavoriteAlbumFeature {
221220

222221
case .duplicatePhotosResponse(.failure):
223222
state.isLoading = false
224-
return .send(.delegate(.showToast(NekiToastItem("사진 추가에 실패했어요", style: .error))))
223+
return .send(.delegate(.showToast(NekiToastItem("사진 복제에 실패했어요", style: .error))))
225224

226225
case .movePhotosResponse(.success):
227226
state.isLoading = false

Neki-iOS/Features/Archive/Sources/Presentation/Sources/Feature/ArchivePhotoDetailFeature.swift

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -171,32 +171,21 @@ struct ArchivePhotoDetailFeature {
171171
// MARK: - Add To Album
172172
case .onTapAddToAlbum:
173173
state.showDropDownMenu = false
174-
state.albumSelection = AlbumSelectionFeature.State(uploadCount: 1)
174+
state.albumSelection = AlbumSelectionFeature.State(uploadCount: 1, selectionPurpose: .duplicate, currentAlbumId: state.folderId)
175175
return .none
176176

177177
case let .albumSelection(.presented(.delegate(delegateAction))):
178178
switch delegateAction {
179179

180-
case let .didSelectAlbum(albumId):
181-
// let photoId = state.currentItemID
182-
// state.albumSelection = nil
183-
// state.isLoading = true
184-
//
185-
// TODO: - 어떤 식으로 업로드 할 건지 서버측과 논의 필요함
186-
// 그냥 킹피셔로 데이터 추출해서 새로운 파일로 업로드하는 것도 가능하긴 한데, 흠
187-
// return .run { send in
188-
// await send(.addToAlbumResponse(Result {
189-
// let isFavorite = albumId == -1
190-
// let targetFolderId = isFavorite ? nil : albumId
191-
//
192-
// try await archiveClient.registerPhotos(
193-
// folderId: targetFolderId,
194-
// uploads: [(mediaID: photoId, memo: String?.none, uploadMethod: PhotoUploadMethod.direct)],
195-
// favorite: isFavorite
196-
// )
197-
// }))
198-
// }
199-
return .none
180+
case .didSelectAlbums:
181+
state.albumSelection = nil
182+
state.isLoading = true
183+
184+
// TODO: - 실제 API 연결하기
185+
return .run { send in
186+
try? await Task.sleep(for: .seconds(1))
187+
await send(.addToAlbumResponse(.success(())))
188+
}
200189

201190
case .didTapCancel:
202191
state.albumSelection = nil
@@ -234,7 +223,7 @@ struct ArchivePhotoDetailFeature {
234223
guard let url = state.currentItem?.imageURL else { return .none }
235224
state.isLoading = true
236225

237-
return .run { send in
226+
return .run { [url = url] send in
238227
let count = try await imageDownloadClient.downloadImages(urls: [url])
239228
await send(.downloadImageResponse(successCount: count))
240229
}

Neki-iOS/Features/Archive/Sources/Presentation/Sources/View/AlbumSelectionView.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,14 @@ struct AlbumSelectionView: View {
5353
album: album,
5454
isSelectMode: true,
5555
isDeleteMode: false,
56-
isSelected: store.selectedAlbumId == album.id
56+
isSelected: store.selectedAlbumIDs.contains(album.id)
5757
)
5858
.padding(.horizontal, 20)
5959
.contentShape(Rectangle())
60+
.opacity(album.id == store.currentAlbumId ? 0.4 : 1.0)
6061
.onTapGesture {
61-
// 즐겨찾기 앨범은 선택하지 못하도록 예외 처리
62-
if !album.isFavorite {
62+
// 즐겨찾기 앨범이랑 현재 앨범은 선택하지 못하도록 예외 처리
63+
if !album.isFavorite || !(album.id == store.currentAlbumId) {
6364
store.send(.tapAlbum(album.id))
6465
}
6566
}
@@ -68,6 +69,7 @@ struct AlbumSelectionView: View {
6869
.padding(.top, 8)
6970
.padding(.bottom, 40)
7071
}
72+
.scrollIndicators(.hidden)
7173
}
7274
}
7375
.navigationBarHidden(true)
@@ -115,9 +117,9 @@ extension AlbumSelectionView {
115117
} label: {
116118
Text("\(store.uploadCount)장 업로드")
117119
.nekiFont(.body16SemiBold)
118-
.foregroundStyle(store.selectedAlbumId == nil ? .gray200 : .primary500)
120+
.foregroundStyle(store.selectedAlbumIDs.isEmpty ? .gray200 : .primary500)
119121
}
120-
.disabled(store.selectedAlbumId == nil)
122+
.disabled(store.selectedAlbumIDs.isEmpty)
121123
}
122124
.frame(height: 54)
123125
.padding(.horizontal, 20)

0 commit comments

Comments
 (0)