diff --git a/Data/Data/Element/EditVideoElement.swift b/Data/Data/Element/EditVideoElement.swift index 334541be..7924ac05 100644 --- a/Data/Data/Element/EditVideoElement.swift +++ b/Data/Data/Element/EditVideoElement.swift @@ -18,14 +18,12 @@ struct EditVideoElement: Codable, Hashable { let endTime: Double func hash(into hasher: inout Hasher) { -// hasher.combine(url.lastPathComponent) hasher.combine(name) } static func == (lhs: EditVideoElement, rhs: EditVideoElement) -> Bool { return lhs.name == rhs.name } - // lhs.url.lastPathComponent == rhs.url.lastPathComponent && } struct User: Codable, Hashable { diff --git a/Domain/UseCase/VideoUseCase.swift b/Domain/UseCase/VideoUseCase.swift index 07b9d83e..4efe43dd 100644 --- a/Domain/UseCase/VideoUseCase.swift +++ b/Domain/UseCase/VideoUseCase.swift @@ -15,7 +15,7 @@ import Interfaces public final class VideoUseCase { private var cancellables: Set = [] private var sharedVideos: [SharedVideo] = [] - private var editingVideos = [String: Video]() + private var editingVideos = [Video]() private let sharingVideoRepository: SharingVideoRepositoryInterface private let editVideoRepository: EditVideoRepositoryInterface @@ -61,7 +61,7 @@ extension VideoUseCase: EditVideoUseCaseInterface { public func fetchVideos() async -> [Video] { var videos = [Video]() let sortedVideos = sharedVideos - .sorted { $0.localUrl.path < $1.localUrl.path } + .sorted { $0.localUrl.lastPathComponent < $1.localUrl.lastPathComponent } for (index, video) in sortedVideos.enumerated() { let duration = await duration(url: video.localUrl) @@ -76,8 +76,8 @@ extension VideoUseCase: EditVideoUseCaseInterface { videos.append(video) } - videos.forEach { editingVideos[$0.url.path] = $0 } editVideoRepository.initializedVideo(videos) + editingVideos = videos return videos } @@ -87,7 +87,7 @@ extension VideoUseCase: EditVideoUseCaseInterface { } public func reArrangingVideo(url: URL, index: Int) { - let videos = updatedVideos(url: url, index: index) + let videos = updatedVideos(url: url, to: index) editVideoRepository.reArrangingVideo(videos) } } @@ -107,12 +107,16 @@ private extension VideoUseCase { .store(in: &cancellables) editVideoRepository.editedVideos - .subscribe(editedVideos) + .sink(with: self) { (owner, videos) in + owner.editingVideos = videos + owner.editedVideos.send(videos) + } .store(in: &cancellables) } func updatedVideo(url: URL, startTime: Double, endTime: Double) -> Video? { - guard let video = editingVideos[url.path] else { return nil } + guard let video = editingVideos.first(where: { $0.url.path == url.path }) + else { return nil } let newVideo = Video( url: video.url, @@ -125,28 +129,21 @@ private extension VideoUseCase { endTime: endTime ) - editingVideos[url.path] = video - return newVideo } - func updatedVideos(url: URL, index: Int) -> [Video] { - guard let video = editingVideos[url.path] else { return [] } - var newVideos = [Video]() - let beforeIndex = video.index - let adder = beforeIndex < index ? -1 : 1 - - let lowerBound = min(beforeIndex, index) - let upperBound = min(beforeIndex, index) - - let videos = editingVideos.values - .filter { $0.index >= lowerBound && $0.index < upperBound } - .map { updatedVideo(video: $0, index: index + adder) } + func updatedVideos(url: URL, to index: Int) -> [Video] { + guard let oldIndex = editingVideos.firstIndex(where: { $0.url.lastPathComponent == url.lastPathComponent }) + else { return editingVideos } - newVideos.append(contentsOf: videos) - newVideos.append(updatedVideo(video: video, index: index)) - newVideos.forEach { editingVideos[$0.url.path] = $0 } + let video = editingVideos.remove(at: oldIndex) + editingVideos.insert(video, at: index) + let listIndexOrderedVideo = editingVideos.enumerated().map { (index, video) in + updatedVideo(video: video, index: index) + } + + editingVideos = listIndexOrderedVideo return newVideos } @@ -154,7 +151,7 @@ private extension VideoUseCase { return Video( url: video.url, name: video.name, - index: video.index, + index: index, duration: video.duration, author: video.author, editor: video.editor, diff --git a/Feature/Feature/PresentationModel/VideoPresentationModel.swift b/Feature/Feature/PresentationModel/VideoPresentationModel.swift index a7b0edb8..99d8a9aa 100644 --- a/Feature/Feature/PresentationModel/VideoPresentationModel.swift +++ b/Feature/Feature/PresentationModel/VideoPresentationModel.swift @@ -16,3 +16,10 @@ struct VideoPresentationModel { let endTime: Double let frameImage: UIImageWrapper } + +extension VideoPresentationModel: Equatable { + static func == (lhs: VideoPresentationModel, rhs: VideoPresentationModel) -> Bool { + if lhs.url == rhs.url { return true } + return false + } +} diff --git a/Feature/Feature/SharedVideoEditView/SharedVideoEditViewController.swift b/Feature/Feature/SharedVideoEditView/SharedVideoEditViewController.swift index 3d428872..c00d907e 100644 --- a/Feature/Feature/SharedVideoEditView/SharedVideoEditViewController.swift +++ b/Feature/Feature/SharedVideoEditView/SharedVideoEditViewController.swift @@ -453,14 +453,25 @@ extension SharedVideoEditViewController: UICollectionViewDropDelegate { snapshot.deleteItems(draggedItems) let targetIndex = destinationIndexPath.item + let currentItems = snapshot.itemIdentifiers var updatedItems = currentItems updatedItems.insert(contentsOf: draggedItems, at: targetIndex) - - applySnapShot(with: updatedItems) coordinator.items.forEach { item in coordinator.drop(item.dragItem, toItemAt: destinationIndexPath) } + + applySnapShot(with: updatedItems) + + collectionView.scrollToItem( + at: destinationIndexPath, + at: .centeredHorizontally, + animated: true + ) + + guard let reorderItem = videoTimelineDataSource.itemIdentifier(for: destinationIndexPath) else { return } + + input.send(.timelineCellOrderDidChanged(to: targetIndex, url: reorderItem.url)) } } diff --git a/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewInput.swift b/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewInput.swift index b269983f..7ca1f148 100644 --- a/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewInput.swift +++ b/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewInput.swift @@ -13,4 +13,5 @@ enum SharedVideoEditViewInput { case sliderModelLowerValueDidChanged(value: Double) case sliderModelUpperValueDidChanged(value: Double) case sliderEditSaveButtonDidTapped + case timelineCellOrderDidChanged(to: Int, url: URL) } diff --git a/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewModel.swift b/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewModel.swift index 18b30ac8..d539d49b 100644 --- a/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewModel.swift +++ b/Feature/Feature/SharedVideoEditView/ViewModel/SharedVideoEditViewModel.swift @@ -43,12 +43,18 @@ extension SharedVideoEditViewModel { owner.updateTappedVideoPresentationModel(upperValue: value) case .sliderEditSaveButtonDidTapped: guard let currentTappedVideoPresentationModel = owner.tappedVideoPresentationModel else { return } - + if let index = owner.videoPresentationModels.firstIndex(where: { + $0 == currentTappedVideoPresentationModel + }) { + owner.videoPresentationModels[index] = currentTappedVideoPresentationModel + } owner.usecase.trimmingVideo( url: currentTappedVideoPresentationModel.url, startTime: currentTappedVideoPresentationModel.startTime, endTime: currentTappedVideoPresentationModel.endTime ) + case .timelineCellOrderDidChanged(let to, let url): + owner.videoOrderChanged(to: to, url: url) } } .store(in: &cancellables) @@ -69,19 +75,24 @@ private extension SharedVideoEditViewModel { if let currentTappedVideoPresentationModel = owner.tappedVideoPresentationModel { guard let tappedVideo = videos.first( - where: { $0.url == currentTappedVideoPresentationModel.url }), + where: { $0.url.path == currentTappedVideoPresentationModel.url.path }), let model = await owner.makeVideoPresentationModel(video: tappedVideo) else { return } owner.setTappedVideoPresentationModel(model: model) } - // Timeline 순서 편집 처리 - let newTimelineItems = await videos.asyncCompactMap { video in + let orderdVideos = videos.sorted { $0.index < $1.index } + var timeLineItem: [VideoTimelineItem] = [] + for video in orderdVideos { let asset = AVAsset(url: video.url) owner.appendVideoPresentationModels(video: video) - return await owner.makeVideoTimelineItem(with: video.url, asset: asset) + async let item = owner.makeVideoTimelineItem(with: video.url, asset: asset) + await timeLineItem.append(item) } - owner.output.send(.timelineItemsDidChanged(items: newTimelineItems)) +// let newTimelineItems = await orderdVideos.asyncCompactMap { video in +// return await +// } + owner.output.send(.timelineItemsDidChanged(items: timeLineItem)) } } .store(in: &cancellables) @@ -236,4 +247,15 @@ private extension SharedVideoEditViewModel { setTappedVideoPresentationModel(model: tappedModel) } } + + func videoOrderChanged( + to: Int, + url: URL + ) { + guard let index = videoPresentationModels.firstIndex(where: { $0.url == url }) + else { return } + let video = videoPresentationModels.remove(at: index) + videoPresentationModels.insert(video, at: to) + usecase.reArrangingVideo(url: video.url, index: to) + } }