From 5eae16db6ab892350f6573592a833d90a3bc0fc9 Mon Sep 17 00:00:00 2001 From: Joseph Cha Date: Tue, 20 Aug 2024 16:27:50 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8#297:=20=EB=8B=A4=EB=A5=B8=20=EC=95=B1?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Share=20Extension=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A8=20ExtensionItem=EA=B0=92=EC=9D=B4?= =?UTF-8?q?=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0,=20=EC=9D=8C?= =?UTF-8?q?=EC=95=85=20=EC=A0=95=EB=B3=B4=20=EB=B6=88=EB=9F=AC=EC=98=A4?= =?UTF-8?q?=EC=A7=80=20=EB=AA=BB=ED=96=88=EB=8B=A4=EB=8A=94=20UI=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StreetDrop/Podfile.lock | 2 +- .../FailedLoadingMusicView.swift | 105 ++++++++++++++++++ .../View/ShareViewController.swift | 40 ++++++- .../ViewModel/ShareViewModel.swift | 18 +-- .../StreetDrop.xcodeproj/project.pbxproj | 12 ++ .../warning.imageset/Contents.json | 23 ++++ .../warning.imageset/warning.png | Bin 0 -> 632 bytes .../warning.imageset/warning@2x.png | Bin 0 -> 1106 bytes .../warning.imageset/warning@3x.png | Bin 0 -> 1550 bytes 9 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 StreetDrop/ShareExtension/View/FailedLoadingMusicView/FailedLoadingMusicView.swift create mode 100644 StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/Contents.json create mode 100644 StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning.png create mode 100644 StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@2x.png create mode 100644 StreetDrop/StreetDrop/Resource/Assets.xcassets/warning.imageset/warning@3x.png 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 0000000000000000000000000000000000000000..b9050404809c0f89b56429530371be4167bb8c55 GIT binary patch literal 632 zcmV-;0*C#HP)BgqE`Oj$T~@6%Ab$5G9H)6hk1on}c_|pn^`C zoT*5>A05;O8V#P@yJigqbnF@XZahD*J>a=%O> z-($F6P)#Dua43hU$85dkBmpK!k2XZd@YQ9gAx`6QIJV7@Na91*)<{xX6*|5;SCj2} zDEW8%i9U+dpUa0zoF^lRPl&RaMEyIHf%N=|66#?X%71L^wRIdbIqFZco^Lebpk Se(wqUd05jVRQ(!O9$KsW*BsdV~4I6=b+YED3Ug1RRt(}oEp z#Of+G#8wipEybiW`er2!>8kTR?3)(61d}3Zt?d>oIxwx?W27B)^%^75$#F{=RNyM1FlZ_i3bFi zblqFQiqE1yBox=hU6moA`>btDpE`aC*wX1Qv??J~0KQ?e`L}5B$7u&I+Uf&MEA%BY zS{0#93E>pDyT-Tzd|VD}U2h$bHI(;l{>6jW8G!goMQ0crw8R5TWju_2L*Ilg8W4MRh0Lk znE{qXwDIFICMiD;%9b$63dm`#r*T@n!w-88Ja8#%yeTo(ET4S3cv6uJ>a|Xg$qz(*zwa~7IAOWM{2(iWqB>+V1a56 zNlD-%CrXw;ncG!T!fPmqu=o<|8SE!=qGSmc0($CJEM?`QB9toq735hv#CqS3f1@@$ zsM?9?Y)Q)Yu58hEp*L8$Q*@Wv| z=VLY9t=XePWCeApol_{FtFrV1$rO5664RYLO{ru06lK<-fR<#5@^-n3WG~|G1e)oh zH)$g$Q+_cCZPjMlZ6Ys^gJk2ZeUGJmRf~u$Kq$4IkOQYwUv!{|wzQcRPt9=ea0Jcu z9kPgPm4b&Tr$tksE?uHJrsz|hx!!zqv{MgP^4tx=TR5w|sQQ%7& zqBPG_CQ(m#bX@$`FKNh(Ue{rp!II>pBL5!C){L3eJl9T>bxr)*V&v-udOixdb07X$0MNt$*Q4~c{6h%=KMNt$*Q4~c{ zmIi|*!Nzg>18z4Xm{-*3j)i?qpSFQ%Ki&g7Fo^o}*MNRnF9R-DV0)h>Cw(-SMvK55 z{}H!)7U&}XEQsmRw;i{Dw26W=QiKojPL~??4y8KY%PEO**d#F;CBjX-(-{KqA=CL! zX^a>V8p6l@@AmFte?=Gpm@N|g_v?7)9crd_O9=e*`$)+uw(GKrT16-XxS&5fGyY4T zCh1@3(~B_dwa^-jcN~A;J$)B@PvjYO$;`i*=etRW^*V&8O@yoXTXHd@i*Xmzh0Wc~ zGy3!VWpa{YfqF8uVgtJ|tuCsE>snt+e&`_}wTLi_07d5?uVFPYNisl53M<(vysim^ z7b3U_GY&ywWAu!3P|%?%s0h6Hy2dSgKppQrTK2RR4YJ63SJ0ms8auJj&RLn1Xvi(A4ZDkwJEh5|9YLxiwhl z5IKa|gaE?BQRFmXj)bDYSc+MBN<@YvVG($;bkayG7$bFGHm@Oomm?dyZy1OLmU^(czE*eCY&pBcu~o$|`h!|AnXsg}a!#%BJUp)&Og>FXjXNMyJ*%QKM(@d>4g7f8fkk$83(qSEwH~3JMTopp3@yN-<3)mV z`Fk?VhDEkzW~wW!0L;=ICPy9yx#Z0iTIBSf9wr|mYIAN7zmkW#b#tEY{@)!YAEB>R zXn+VKW;oJXImJt=4*9qgYNRnDFl-ff`D7eo;a2O@KTe}W7$Gc1S{ztU(GVIb!iZwN z=;C1~u2hx=cl>2l@5!i|MvE{`ZXM>ef#p}Ty<*pv+|JJP7ZDoz*j@(n2fvc%C05Uy z*zt;@D2k#eilQirq9}@@D2k#eilQirqAV%@061-CEy}dm$p8QV07*qoM6N<$f?;pF A$p8QV literal 0 HcmV?d00001