diff --git a/StreetDrop/Podfile.lock b/StreetDrop/Podfile.lock index 922103a..4273963 100644 --- a/StreetDrop/Podfile.lock +++ b/StreetDrop/Podfile.lock @@ -25,4 +25,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: a8c0902adb23412ed3e2bbd632c3c7f7846b9405 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/StreetDrop/ShareExtension/View/FailedLoadingMusicView/FailedLoadingMusicView.swift b/StreetDrop/ShareExtension/View/FailedLoadingMusicView/FailedLoadingMusicView.swift new file mode 100644 index 0000000..03c3d70 --- /dev/null +++ b/StreetDrop/ShareExtension/View/FailedLoadingMusicView/FailedLoadingMusicView.swift @@ -0,0 +1,105 @@ +// +// FailedLoadingMusicView.swift +// ShareExtension +// +// Created by 차요셉 on 8/20/24. +// + +import UIKit + +import RxSwift +import RxRelay +import SnapKit + +final class FailedLoadingMusicView: UIView { + private let searchingMusicButtonEventRelay: PublishRelay = .init() + var searchingMusicButtonEvent: Observable { + searchingMusicButtonEventRelay.asObservable() + } + + private let disposeBag: DisposeBag = .init() + + override init(frame: CGRect) { + super.init(frame: frame) + bindAction() + configureUI() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private let warningImageView: UIImageView = { + let imageView: UIImageView = .init(image: .init(named: "warning")) + + return imageView + }() + + private let guidingLabel: UILabel = { + let label: UILabel = .init() + label.text = "음악 정보를 불러오지 못했어요" + label.font = .pretendard(size: 18, weight: 700) + label.textColor = .white + label.setLineHeight(lineHeight: 28) + + return label + }() + + private let searchingMusicButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("음악 검색하기", for: .normal) + button.setTitleColor(.gray900, for: .normal) + button.titleLabel?.font = .pretendard(size: 16, weight: 700) + button.layer.cornerRadius = 12 + button.backgroundColor = .primary400 + + return button + }() +} + +private extension FailedLoadingMusicView { + func bindAction() { + searchingMusicButton.rx.tap + .bind(to: searchingMusicButtonEventRelay) + .disposed(by: disposeBag) + } + + func configureUI() { + layer.cornerRadius = 20 + layer.borderWidth = 1 + layer.borderColor = UIColor.gray600.cgColor + + backgroundColor = .gray800 + + snp.makeConstraints { + $0.height.equalTo(192) + } + + [ + warningImageView, + guidingLabel, + searchingMusicButton + ].forEach { + addSubview($0) + } + + warningImageView.snp.makeConstraints { + $0.width.height.equalTo(32) + $0.top.equalToSuperview().inset(20) + $0.leading.equalToSuperview().inset(24) + } + + guidingLabel.snp.makeConstraints { + $0.height.equalTo(32) + $0.top.equalToSuperview().inset(20) + $0.leading.equalTo(warningImageView.snp.trailing) + } + + searchingMusicButton.snp.makeConstraints { + $0.height.equalTo(56) + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalToSuperview().inset(48) + } + } +} diff --git a/StreetDrop/ShareExtension/View/ShareViewController.swift b/StreetDrop/ShareExtension/View/ShareViewController.swift index 187b386..b77f19b 100644 --- a/StreetDrop/ShareExtension/View/ShareViewController.swift +++ b/StreetDrop/ShareExtension/View/ShareViewController.swift @@ -23,7 +23,7 @@ final class ShareViewController: UIViewController { } private let viewModel: ShareViewModel = .init() private let disposeBag: DisposeBag = .init() - private let sharedMusicKeyWordEvent: PublishRelay = .init() + private let sharedMusicKeyWordEvent: PublishRelay = .init() private let dropButtonClickEvent: PublishRelay = .init() private let containerView: UIView = { @@ -218,6 +218,13 @@ final class ShareViewController: UIViewController { private var dropDoneView: DropDoneView? + private let failedLoadingMusicView: FailedLoadingMusicView = { + let failedLoadingMusicView: FailedLoadingMusicView = .init() + failedLoadingMusicView.isHidden = true + + return failedLoadingMusicView + }() + override func viewDidLoad() { super.viewDidLoad() bindAction() @@ -429,11 +436,28 @@ private extension ShareViewController { }) }) .disposed(by: disposeBag) + + output.goFailedLoadingMusicView + .bind(with: self) { owner, _ in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: { [weak self] in + guard let self = self else { return } + containerView.isHidden = true + reSearchingMusicForSharingView.isHidden = true + failedLoadingMusicView.isHidden = false + view.layoutIfNeeded() + }) + } + .disposed(by: disposeBag) } func configureUI() { - view.addSubview(containerView) - view.addSubview(reSearchingMusicForSharingView) + [ + containerView, + reSearchingMusicForSharingView, + failedLoadingMusicView + ].forEach { + view.addSubview($0) + } view.backgroundColor = UIColor.black.withAlphaComponent(0.5) containerView.snp.makeConstraints { @@ -559,6 +583,11 @@ private extension ShareViewController { $0.height.equalTo(56) $0.horizontalEdges.equalToSuperview() } + + failedLoadingMusicView.snp.makeConstraints { + $0.height.equalTo(192) + $0.horizontalEdges.bottom.equalToSuperview() + } } } @@ -567,7 +596,10 @@ private extension ShareViewController { func handleExtensionItem(_ extensionItem: NSExtensionItem) { // ExtensionItem에서 ContentText 추출 guard let sharedTextItem = extensionItem.attributedContentText, - let videoID = extractVideoID(from: sharedTextItem.string) else { return } + let videoID = extractVideoID(from: sharedTextItem.string) else { + sharedMusicKeyWordEvent.accept(nil) + return + } fetchVideoDetails(videoID: videoID) { [weak self] songName, artistName in self?.sharedMusicKeyWordEvent.accept("\(songName ?? "")-\(artistName ?? "")") diff --git a/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift b/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift index d003ac0..02e0863 100644 --- a/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift +++ b/StreetDrop/ShareExtension/ViewModel/ShareViewModel.swift @@ -39,7 +39,7 @@ final class ShareViewModel: NSObject, ShareViewModelType { struct Input { let viewDidLoadEvent: Observable - let sharedMusicKeyWordEvent: Observable + let sharedMusicKeyWordEvent: Observable let changingMusicViewClickEvent: Observable let reSearchingEvent: Observable let dropButtonClickEvent: Observable @@ -62,6 +62,10 @@ final class ShareViewModel: NSObject, ShareViewModelType { var goDropDoneView: Observable<(Music, String, String)> { goDropDoneViewRelay.asObservable() } + fileprivate let goFailedLoadingMusicViewRelay: PublishRelay = .init() + var goFailedLoadingMusicView: Observable { + goFailedLoadingMusicViewRelay.asObservable() + } } func convert(input: Input, disposedBag: DisposeBag) -> Output { @@ -75,10 +79,14 @@ final class ShareViewModel: NSObject, ShareViewModelType { input.sharedMusicKeyWordEvent .bind(with: self) { owner, sharedMusicKeyWord in + guard let sharedMusicKeyWord = sharedMusicKeyWord else { + owner.output.goFailedLoadingMusicViewRelay.accept(Void()) + return + } owner.searchMusicUsecase.searchMusic(keyword: sharedMusicKeyWord) .subscribe(with: self) { owner, musicList in guard let firstMusic = musicList.first else { - // TODO: 요셉, 검색된 음악 없다는 이벤트 view에 전달 + owner.output.goFailedLoadingMusicViewRelay.accept(Void()) return } owner.output.showSearchedMusicRelay.accept(firstMusic) @@ -131,6 +139,7 @@ final class ShareViewModel: NSObject, ShareViewModelType { (selectedMusic, currentLocation.address, comment) ) } onFailure: { owner, error in + // TODO: 드랍 실패 팝업 print(error.localizedDescription) } .disposed(by: disposedBag) @@ -176,9 +185,4 @@ extension ShareViewModel: CLLocationManagerDelegate { manager.stopUpdatingLocation() } - - func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) { - // TODO: 요셉, 에러메세지 띄우기 - print("위치 정보를 가져오는데 실패했습니다: \(error.localizedDescription)") - } } diff --git a/StreetDrop/StreetDrop.xcodeproj/project.pbxproj b/StreetDrop/StreetDrop.xcodeproj/project.pbxproj index d6e87c4..12fcc25 100644 --- a/StreetDrop/StreetDrop.xcodeproj/project.pbxproj +++ b/StreetDrop/StreetDrop.xcodeproj/project.pbxproj @@ -389,6 +389,7 @@ C434A4D82A17AAAB00C63526 /* SearchingMusicViewModelTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C434A4D72A17AAAB00C63526 /* SearchingMusicViewModelTest.swift */; }; C434A4DC2A19CA6F00C63526 /* SearchingMusicTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C434A4DB2A19CA6F00C63526 /* SearchingMusicTableViewCell.swift */; }; C434A4DD2A19CA6F00C63526 /* SearchingMusicTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C434A4DB2A19CA6F00C63526 /* SearchingMusicTableViewCell.swift */; }; + C4429C002C74515B00DC8C36 /* FailedLoadingMusicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4429BFF2C74515B00DC8C36 /* FailedLoadingMusicView.swift */; }; C44442F32C6C7CA700CA93EA /* CommunityGuideDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41589AB92A474B3D0029A2EE /* CommunityGuideDetailView.swift */; }; C44980782BC37CB70001E6C3 /* NaverMaps.plist in Resources */ = {isa = PBXBuildFile; fileRef = C44980772BC37CB70001E6C3 /* NaverMaps.plist */; }; C449807A2BC3AF9F0001E6C3 /* PopUpUserReadingRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44980792BC3AF9F0001E6C3 /* PopUpUserReadingRequestDTO.swift */; }; @@ -735,6 +736,7 @@ C434A4D22A17983A00C63526 /* SearchingMusicRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingMusicRepository.swift; sourceTree = ""; }; C434A4D72A17AAAB00C63526 /* SearchingMusicViewModelTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingMusicViewModelTest.swift; sourceTree = ""; }; C434A4DB2A19CA6F00C63526 /* SearchingMusicTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingMusicTableViewCell.swift; sourceTree = ""; }; + C4429BFF2C74515B00DC8C36 /* FailedLoadingMusicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedLoadingMusicView.swift; sourceTree = ""; }; C44980772BC37CB70001E6C3 /* NaverMaps.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = NaverMaps.plist; sourceTree = ""; }; C44980792BC3AF9F0001E6C3 /* PopUpUserReadingRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpUserReadingRequestDTO.swift; sourceTree = ""; }; C449807B2BC3B07E0001E6C3 /* PostingPopUpUserReadingUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingPopUpUserReadingUseCase.swift; sourceTree = ""; }; @@ -1779,6 +1781,7 @@ C42182BC2C697F9700B73A99 /* ShareViewController.swift */, C44DE1FC2C6EE089004F211C /* ReSearchingMusic */, C48B76F32C7314F20003BC3C /* DropDone */, + C4429BFE2C74514300DC8C36 /* FailedLoadingMusicView */, ); path = View; sourceTree = ""; @@ -1838,6 +1841,14 @@ path = SearchingMusic; sourceTree = ""; }; + C4429BFE2C74514300DC8C36 /* FailedLoadingMusicView */ = { + isa = PBXGroup; + children = ( + C4429BFF2C74515B00DC8C36 /* FailedLoadingMusicView.swift */, + ); + path = FailedLoadingMusicView; + sourceTree = ""; + }; C44A54942BBC080700354F8F /* PopUp */ = { isa = PBXGroup; children = ( @@ -2985,6 +2996,7 @@ C432DF7A2C69F7F3003DBA18 /* DefaultFetchingMyLevelUseCase.swift in Sources */, C432DF7B2C69F7F3003DBA18 /* FetchingMyLikeListUseCase.swift in Sources */, C432DF7C2C69F7F3003DBA18 /* DefaultFetchingMyLikeListUseCase.swift in Sources */, + C4429C002C74515B00DC8C36 /* FailedLoadingMusicView.swift in Sources */, C432DF7D2C69F7F3003DBA18 /* FetchingMyDropListUseCase.swift in Sources */, C432DF7E2C69F7F3003DBA18 /* DefaultFetchingMyDropListUseCase.swift in Sources */, C432DF7F2C69F7F3003DBA18 /* FetchingCityAndDistrictsUseCase.swift in Sources */, diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/Contents.json b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/Contents.json new file mode 100644 index 0000000..acb4256 --- /dev/null +++ b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "warning.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "warning@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "warning@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning.png b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning.png new file mode 100644 index 0000000..b905040 Binary files /dev/null and b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning.png differ diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@2x.png b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@2x.png new file mode 100644 index 0000000..e6f77ab Binary files /dev/null and b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@2x.png differ diff --git a/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@3x.png b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@3x.png new file mode 100644 index 0000000..4ee8613 Binary files /dev/null and b/StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@3x.png differ