diff --git a/NADA-iOS-forRelease.xcodeproj/project.pbxproj b/NADA-iOS-forRelease.xcodeproj/project.pbxproj index 27aed4d7..3f48f4f8 100644 --- a/NADA-iOS-forRelease.xcodeproj/project.pbxproj +++ b/NADA-iOS-forRelease.xcodeproj/project.pbxproj @@ -93,7 +93,6 @@ 77A4D5F629BC304C00367B7C /* UIStackView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4D5F529BC304C00367B7C /* UIStackView+Extension.swift */; }; 77A4D5F929BC320600367B7C /* AroundMeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4D5F829BC320600367B7C /* AroundMeCollectionViewCell.swift */; }; 77A4D5FC29BD6F4600367B7C /* AroundMeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4D5FB29BD6F4600367B7C /* AroundMeViewModel.swift */; }; - 77A4D5FE29BD6F9500367B7C /* ViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4D5FD29BD6F9500367B7C /* ViewModelType.swift */; }; 77A4D60129BD708C00367B7C /* AroundMeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4D60029BD708C00367B7C /* AroundMeResponse.swift */; }; 77A4D60429BD743600367B7C /* ModuleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4D60329BD743600367B7C /* ModuleFactory.swift */; }; 77A4D60629BD747300367B7C /* controllerFromStoryBoard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A4D60529BD747300367B7C /* controllerFromStoryBoard.swift */; }; @@ -170,6 +169,12 @@ F8915A22275728F20013D609 /* SelectBirthBottomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8915A20275728F20013D609 /* SelectBirthBottomViewController.swift */; }; F8915A23275728F20013D609 /* SelectMBTIBottomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8915A21275728F20013D609 /* SelectMBTIBottomViewController.swift */; }; F8A9FE1A2743DC6B00BC60CE /* CardListInGroupRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A9FE192743DC6B00BC60CE /* CardListInGroupRequest.swift */; }; + F8B3F40829F67B07006586BB /* CompanyCardCreation.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8B3F40729F67B07006586BB /* CompanyCardCreation.storyboard */; }; + F8B3F40A29F67B1C006586BB /* CompanyCardCreationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B3F40929F67B1C006586BB /* CompanyCardCreationViewController.swift */; }; + F8B3F40D29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8B3F40B29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.xib */; }; + F8B3F40E29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B3F40C29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.swift */; }; + F8B3F41129F67F8D006586BB /* CompanyFrontCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B3F40F29F67F8D006586BB /* CompanyFrontCardCell.swift */; }; + F8B3F41229F67F8D006586BB /* CompanyFrontCardCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8B3F41029F67F8D006586BB /* CompanyFrontCardCell.xib */; }; F8BCDD22298FFA830044D0B4 /* QRCodeWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8BCDD21298FFA830044D0B4 /* QRCodeWidget.swift */; }; F8BCDD24298FFA940044D0B4 /* OpenAppLockScreenWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8BCDD23298FFA940044D0B4 /* OpenAppLockScreenWidget.swift */; }; F8BCDD25298FFAF90044D0B4 /* WidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F838B664298E5C5300D84340 /* WidgetsBundle.swift */; }; @@ -334,7 +339,6 @@ 77A4D5F529BC304C00367B7C /* UIStackView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Extension.swift"; sourceTree = ""; }; 77A4D5F829BC320600367B7C /* AroundMeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AroundMeCollectionViewCell.swift; sourceTree = ""; }; 77A4D5FB29BD6F4600367B7C /* AroundMeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AroundMeViewModel.swift; sourceTree = ""; }; - 77A4D5FD29BD6F9500367B7C /* ViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelType.swift; sourceTree = ""; }; 77A4D60029BD708C00367B7C /* AroundMeResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AroundMeResponse.swift; sourceTree = ""; }; 77A4D60329BD743600367B7C /* ModuleFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleFactory.swift; sourceTree = ""; }; 77A4D60529BD747300367B7C /* controllerFromStoryBoard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = controllerFromStoryBoard.swift; sourceTree = ""; }; @@ -417,6 +421,12 @@ F8915A20275728F20013D609 /* SelectBirthBottomViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectBirthBottomViewController.swift; sourceTree = ""; }; F8915A21275728F20013D609 /* SelectMBTIBottomViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectMBTIBottomViewController.swift; sourceTree = ""; }; F8A9FE192743DC6B00BC60CE /* CardListInGroupRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardListInGroupRequest.swift; sourceTree = ""; }; + F8B3F40729F67B07006586BB /* CompanyCardCreation.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = CompanyCardCreation.storyboard; sourceTree = ""; }; + F8B3F40929F67B1C006586BB /* CompanyCardCreationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompanyCardCreationViewController.swift; sourceTree = ""; }; + F8B3F40B29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CompanyFrontCardCreationCollectionViewCell.xib; sourceTree = ""; }; + F8B3F40C29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompanyFrontCardCreationCollectionViewCell.swift; sourceTree = ""; }; + F8B3F40F29F67F8D006586BB /* CompanyFrontCardCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompanyFrontCardCell.swift; sourceTree = ""; }; + F8B3F41029F67F8D006586BB /* CompanyFrontCardCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CompanyFrontCardCell.xib; sourceTree = ""; }; F8BCDD21298FFA830044D0B4 /* QRCodeWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeWidget.swift; sourceTree = ""; }; F8BCDD23298FFA940044D0B4 /* OpenAppLockScreenWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAppLockScreenWidget.swift; sourceTree = ""; }; F8C310BF273A7360008EC5BA /* FrontCardCreationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrontCardCreationDelegate.swift; sourceTree = ""; }; @@ -913,6 +923,8 @@ F83206AC26F61E5800D61711 /* CardCell */ = { isa = PBXGroup; children = ( + F8B3F40F29F67F8D006586BB /* CompanyFrontCardCell.swift */, + F8B3F41029F67F8D006586BB /* CompanyFrontCardCell.xib */, F8458D6929F54EDF000D53A7 /* FanFrontCardCell.swift */, F8458D6A29F54EDF000D53A7 /* FanFrontCardCell.xib */, F82FEB4A27639F3100DA7847 /* MainCardCell.swift */, @@ -939,6 +951,7 @@ F84BAF9E26FDB425004CA335 /* CardCreation */ = { isa = PBXGroup; children = ( + F8B3F40729F67B07006586BB /* CompanyCardCreation.storyboard */, F8458D6129F5183B000D53A7 /* FanCardCreation.storyboard */, F82FF81C2701EBCE00E57F8B /* CardCreation.storyboard */, F85711A6274A6B3400F59F0B /* CardCreationPreview.storyboard */, @@ -951,6 +964,8 @@ children = ( F82FF8222701FFE300E57F8B /* RequiredFlavor */, F858079B2700348000872484 /* Background */, + F8B3F40C29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.swift */, + F8B3F40B29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.xib */, F8458D6529F51B2F000D53A7 /* FanFrontCardCreationCollectionViewCell.swift */, F8458D6629F51B2F000D53A7 /* FanFrontCardCreationCollectionViewCell.xib */, F84BAFAB26FDB543004CA335 /* FrontCardCreationCollectionViewCell.swift */, @@ -1087,6 +1102,7 @@ F885C8A829B827030079FA35 /* VC */ = { isa = PBXGroup; children = ( + F8B3F40929F67B1C006586BB /* CompanyCardCreationViewController.swift */, F8458D6329F51870000D53A7 /* FanCardCreationViewController.swift */, F84BAF9C26FDB417004CA335 /* CardCreationViewController.swift */, F85711A4274A6B2200F59F0B /* CardCreationPreviewViewController.swift */, @@ -1302,7 +1318,6 @@ children = ( F88C8DE829B8919700196A33 /* ViewModelType.swift */, F8915A1F275713A10013D609 /* CardCreation */, - 77A4D5FD29BD6F9500367B7C /* ViewModelType.swift */, ); path = Protocols; sourceTree = ""; @@ -1567,6 +1582,7 @@ 39C394D5277CD33500DBA566 /* EmptyCardListTableViewCell.xib in Resources */, F84BAFB226FDB552004CA335 /* BackCardCreationCollectionViewCell.xib in Resources */, F8458D6C29F54EDF000D53A7 /* FanFrontCardCell.xib in Resources */, + F8B3F40829F67B07006586BB /* CompanyCardCreation.storyboard in Resources */, 77B4E7482990E420006098DC /* Home.storyboard in Resources */, 397B75172763B5B7004AEB03 /* TeamNADA.storyboard in Resources */, 3936993B274A53C600684420 /* GroupEdit.storyboard in Resources */, @@ -1577,8 +1593,10 @@ 7766A410274FEBE200714912 /* CardInGroupCollectionViewCell.xib in Resources */, 7705CF402752C844005195DF /* CardView.xib in Resources */, 397B750D2763A5AF004AEB03 /* OpenSource.storyboard in Resources */, + F8B3F40D29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.xib in Resources */, 393E334A275F6A6600965BBF /* Splash.storyboard in Resources */, 39523E5C2701A48900536900 /* CardListTableViewCell.xib in Resources */, + F8B3F41229F67F8D006586BB /* CompanyFrontCardCell.xib in Resources */, F8FC439226C01CDE0033E151 /* LaunchScreen.storyboard in Resources */, F8FC438F26C01CDE0033E151 /* Assets.xcassets in Resources */, F8518055275D040C006BD5ED /* OnboardingCollectionViewCell.xib in Resources */, @@ -1741,10 +1759,13 @@ 770E58862778A78900498C2E /* Status.swift in Sources */, F851805B275D047C006BD5ED /* OnboardingViewController.swift in Sources */, 392BAF7B2793E90D0013DCB3 /* KeyChainKey.swift in Sources */, + F8B3F40A29F67B1C006586BB /* CompanyCardCreationViewController.swift in Sources */, 77A4D5FC29BD6F4600367B7C /* AroundMeViewModel.swift in Sources */, F843DD6D29A5F83000D8D20D /* CardCreationCategoryViewModel.swift in Sources */, + F8B3F40E29F67B6F006586BB /* CompanyFrontCardCreationCollectionViewCell.swift in Sources */, 39C1E88F270EC762006D2089 /* UIColor+Extension.swift in Sources */, F8C83FC9272FA3190009DF0D /* GroupAPI.swift in Sources */, + F8B3F41129F67F8D006586BB /* CompanyFrontCardCell.swift in Sources */, F8F00C3E29DD489200A15377 /* UpdateAPI.swift in Sources */, F8FC43B826C0227D0033E151 /* Const.swift in Sources */, 394F314C27081B3A00C83291 /* FrontViewController.swift in Sources */, @@ -1788,7 +1809,6 @@ F8458D6429F51870000D53A7 /* FanCardCreationViewController.swift in Sources */, 77A4D60129BD708C00367B7C /* AroundMeResponse.swift in Sources */, 7734D5B82777A8E8004360E4 /* String+Extension.swift in Sources */, - 77A4D5FE29BD6F9500367B7C /* ViewModelType.swift in Sources */, 77A4D60429BD743600367B7C /* ModuleFactory.swift in Sources */, F8C83FC3272FA17B0009DF0D /* URL.swift in Sources */, 392F7FB4274621F1008CDBF5 /* MoreListTableViewCell.swift in Sources */, diff --git a/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/Contents.json b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/Contents.json new file mode 100644 index 00000000..589d1363 --- /dev/null +++ b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "scale" : "1x", + "filename" : "mailOutline.png", + "idiom" : "universal" + }, + { + "scale" : "2x", + "filename" : "mailOutline@2x.png", + "idiom" : "universal" + }, + { + "scale" : "3x", + "idiom" : "universal", + "filename" : "mailOutline@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline.png b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline.png new file mode 100644 index 00000000..0e7727db Binary files /dev/null and b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline.png differ diff --git a/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline@2x.png b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline@2x.png new file mode 100644 index 00000000..46cc990d Binary files /dev/null and b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline@2x.png differ diff --git a/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline@3x.png b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline@3x.png new file mode 100644 index 00000000..6dbabd8f Binary files /dev/null and b/NADA-iOS-forRelease/Resouces/Assets/Assets.xcassets/mailOutline.imageset/mailOutline@3x.png differ diff --git a/NADA-iOS-forRelease/Resouces/Constants/Notification.swift b/NADA-iOS-forRelease/Resouces/Constants/Notification.swift index 263ea3f8..6c61720f 100644 --- a/NADA-iOS-forRelease/Resouces/Constants/Notification.swift +++ b/NADA-iOS-forRelease/Resouces/Constants/Notification.swift @@ -22,4 +22,6 @@ extension Notification.Name { static let creationReloadMainCardSwiper = Notification.Name("creationReloadMainCardSwiper") static let dismissQRCodeCardResult = Notification.Name("dismissQRCodeCardResult") static let scrollToSecondIndex = Notification.Name("scrollToSecondIndex") + static let presentMail = Notification.Name("presentMail") + static let presentDynamicLink = Notification.Name("presentDynamicLink") } diff --git a/NADA-iOS-forRelease/Resouces/Storyboards/CardCreation/CompanyCardCreation.storyboard b/NADA-iOS-forRelease/Resouces/Storyboards/CardCreation/CompanyCardCreation.storyboard new file mode 100644 index 00000000..095e871e --- /dev/null +++ b/NADA-iOS-forRelease/Resouces/Storyboards/CardCreation/CompanyCardCreation.storyboard @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NADA-iOS-forRelease/Sources/Cells/CardCell/CompanyFrontCardCell.swift b/NADA-iOS-forRelease/Sources/Cells/CardCell/CompanyFrontCardCell.swift new file mode 100644 index 00000000..f7d0f7ac --- /dev/null +++ b/NADA-iOS-forRelease/Sources/Cells/CardCell/CompanyFrontCardCell.swift @@ -0,0 +1,181 @@ +// +// CompanyFrontCardCell.swift +// NADA-iOS-forRelease +// +// Created by kimhyungyu on 2023/04/24. +// + +import UIKit + +import VerticalCardSwiper +import Kingfisher + +class CompanyFrontCardCell: CardCell { + + // MARK: - Properties + + private var cardData: Card? + + // MARK: - @IBOutlet Properties + @IBOutlet weak var backgroundImageView: UIImageView! + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var descriptionLabel: UILabel! + @IBOutlet weak var userNameLabel: UILabel! + @IBOutlet weak var birthLabel: UILabel! + @IBOutlet weak var mbtiLabel: UILabel! + @IBOutlet weak var mailLabel: UILabel! + @IBOutlet weak var phoneNumberLabel: UILabel! + @IBOutlet weak var linkURLLabel: UILabel! + @IBOutlet weak var shareButton: UIButton! + + @IBOutlet weak var mailImageView: UIImageView! + @IBOutlet weak var phoneNumberImageView: UIImageView! + @IBOutlet weak var linkURLImageView: UIImageView! + + @IBOutlet weak var mailStackView: UIStackView! + @IBOutlet weak var phoneNumberStackView: UIStackView! + @IBOutlet weak var linkURLStackView: UIStackView! + @IBOutlet weak var totalStackView: UIStackView! + + // MARK: - Life Cycle + + override func awakeFromNib() { + super.awakeFromNib() + + setUI() + setTapGesture() + } + @IBAction func touchShareButton(_ sender: Any) { + NotificationCenter.default.post(name: Notification.Name.presentCardShare, object: cardData, userInfo: nil) + } + + static func nib() -> UINib { + return UINib(nibName: CompanyFrontCardCell.className, bundle: Bundle(for: CompanyFrontCardCell.self)) + } +} + +// MARK: - Extensions + +extension CompanyFrontCardCell { + private func setUI() { + titleLabel.font = .title02 + titleLabel.textColor = .white + descriptionLabel.font = .textRegular03 + descriptionLabel.textColor = .white + userNameLabel.font = .title01 + userNameLabel.textColor = .white + birthLabel.font = .textRegular03 + birthLabel.textColor = .white + mbtiLabel.font = .textRegular03 + mbtiLabel.textColor = .white + mailLabel.font = .textRegular04 + mailLabel.textColor = .white + mailLabel.lineBreakMode = .byTruncatingTail + phoneNumberLabel.font = .textRegular04 + phoneNumberLabel.textColor = .white + phoneNumberLabel.lineBreakMode = .byTruncatingTail + linkURLLabel.font = .textRegular04 + linkURLLabel.textColor = .white + linkURLLabel.numberOfLines = 2 + linkURLLabel.lineBreakMode = .byTruncatingTail + + linkURLStackView.alignment = .center + } + private func setTapGesture() { + mailLabel.isUserInteractionEnabled = true + let mailTapGesture = UITapGestureRecognizer(target: self, action: #selector(tapMailLabel)) + mailLabel.addGestureRecognizer(mailTapGesture) + + linkURLLabel.isUserInteractionEnabled = true + let linkURLTapGesture = UITapGestureRecognizer(target: self, action: #selector(tapLinkURLLabel)) + linkURLLabel.addGestureRecognizer(linkURLTapGesture) + } + + // MARK: - @objc Methods + @objc + private func tapMailLabel() { + NotificationCenter.default.post(name: .presentMail, object: mailLabel.text ?? "") + } + @objc + private func tapLinkURLLabel() { + let linkURL = linkURLLabel.text ?? "" + let webURL: URL + + if linkURL.hasPrefix("https://") { + webURL = URL(string: linkURL)! + } else { + webURL = URL(string: "https://" + linkURL)! + } + + if UIApplication.shared.canOpenURL(webURL) { + UIApplication.shared.open(webURL, options: [:], completionHandler: nil) + } + } + + /// 명함 조회 시 사용. + func initCellFromServer(cardData: Card, isShareable: Bool) { + self.cardData = cardData + + if cardData.cardImage.hasPrefix("https://") { + self.backgroundImageView.updateServerImage(cardData.cardImage) + } else { + if let bgImage = UIImage(named: cardData.cardImage) { + self.backgroundImageView.image = bgImage + } + } + + titleLabel.text = cardData.cardName + descriptionLabel.text = cardData.departmentName + userNameLabel.text = cardData.userName + birthLabel.text = cardData.birth + mbtiLabel.text = cardData.mbti + mailLabel.text = cardData.mailAddress + phoneNumberLabel.text = cardData.phoneNumber + + if let urls = cardData.urls { + if urls[0].isEmpty { + linkURLStackView.isHidden = true + } else { + linkURLLabel.text = urls[0] + } + } + if cardData.mailAddress?.isEmpty ?? false { + mailStackView.isHidden = true + } + if cardData.phoneNumber?.isEmpty ?? false { + phoneNumberStackView.isHidden = true + } + + shareButton.isHidden = !isShareable + } + + /// 명함 미리보기 시 사용. + func initCell(_ backgroundImage: UIImage?, + _ frontCardDataModel: FrontCardDataModel) { + backgroundImageView.image = backgroundImage ?? UIImage() + titleLabel.text = frontCardDataModel.cardName + descriptionLabel.text = frontCardDataModel.departmentName + userNameLabel.text = frontCardDataModel.userName + birthLabel.text = frontCardDataModel.birth + mbtiLabel.text = frontCardDataModel.mbti + mailLabel.text = frontCardDataModel.mailAddress + phoneNumberLabel.text = frontCardDataModel.phoneNumber + + if let urls = frontCardDataModel.urls { + if urls[0].isEmpty { + linkURLStackView.isHidden = true + } else { + linkURLLabel.text = urls[0] + } + } + + if frontCardDataModel.mailAddress?.isEmpty ?? false { + mailStackView.isHidden = true + } + if frontCardDataModel.phoneNumber?.isEmpty ?? false { + phoneNumberStackView.isHidden = true + } + + shareButton.isHidden = true + } +} diff --git a/NADA-iOS-forRelease/Sources/Cells/CardCell/CompanyFrontCardCell.xib b/NADA-iOS-forRelease/Sources/Cells/CardCell/CompanyFrontCardCell.xib new file mode 100644 index 00000000..87ef0005 --- /dev/null +++ b/NADA-iOS-forRelease/Sources/Cells/CardCell/CompanyFrontCardCell.xib @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NADA-iOS-forRelease/Sources/Cells/CardCell/FanFrontCardCell.swift b/NADA-iOS-forRelease/Sources/Cells/CardCell/FanFrontCardCell.swift index 68118688..3e1c8168 100644 --- a/NADA-iOS-forRelease/Sources/Cells/CardCell/FanFrontCardCell.swift +++ b/NADA-iOS-forRelease/Sources/Cells/CardCell/FanFrontCardCell.swift @@ -190,7 +190,7 @@ extension FanFrontCardCell { /// 명함 미리보기 시 사용. func initCell(_ backgroundImage: UIImage?, - frontCardDataModel: FrontCardDataModel) { + _ frontCardDataModel: FrontCardDataModel) { backgroundImageView.image = backgroundImage ?? UIImage() titleLabel.text = frontCardDataModel.cardName userNameLabel.text = frontCardDataModel.userName diff --git a/NADA-iOS-forRelease/Sources/Cells/CardCell/FrontCardCell.swift b/NADA-iOS-forRelease/Sources/Cells/CardCell/FrontCardCell.swift index b3684d50..59fea3bc 100644 --- a/NADA-iOS-forRelease/Sources/Cells/CardCell/FrontCardCell.swift +++ b/NADA-iOS-forRelease/Sources/Cells/CardCell/FrontCardCell.swift @@ -120,7 +120,7 @@ extension FrontCardCell { } } - /// 서버에서 image 를 URL 로 가져올 경우 사용. + /// 명함 조회 시 사용. func initCellFromServer(cardData: Card, isShareable: Bool) { self.cardData = cardData @@ -157,37 +157,33 @@ extension FrontCardCell { shareButton.isHidden = !isShareable } - /// 명함생성할 때 image 를 UIImage 로 가져올 경우 사용 + /// 명함 미리보기 시 사용. func initCell(_ backgroundImage: UIImage?, - _ cardTitle: String, - _ cardDescription: String, - _ userName: String, - _ birth: String, - _ mbti: String, - _ instagramID: String, - _ phoneNumber: String, - _ linkURL: String, - isShareable: Bool) { + _ frontCardDataModel: FrontCardDataModel) { backgroundImageView.image = backgroundImage ?? UIImage() - titleLabel.text = cardTitle - descriptionLabel.text = cardDescription - userNameLabel.text = userName - birthLabel.text = birth - mbtiLabel.text = mbti - instagramIDLabel.text = instagramID - phoneNumberLabel.text = phoneNumber - linkURLLabel.text = linkURL + titleLabel.text = frontCardDataModel.cardName + descriptionLabel.text = frontCardDataModel.departmentName + userNameLabel.text = frontCardDataModel.userName + birthLabel.text = frontCardDataModel.birth + mbtiLabel.text = frontCardDataModel.mbti + instagramIDLabel.text = frontCardDataModel.instagram + phoneNumberLabel.text = frontCardDataModel.phoneNumber - if instagramID.isEmpty { + if let urls = frontCardDataModel.urls { + if urls[0].isEmpty { + linkURLStackView.isHidden = true + } else { + linkURLLabel.text = urls[0] + } + } + + if frontCardDataModel.instagram?.isEmpty ?? false { instagramStackView.isHidden = true } - if phoneNumber.isEmpty { + if frontCardDataModel.phoneNumber?.isEmpty ?? false { phoneNumberStackView.isHidden = true } - if linkURL.isEmpty { - linkURLStackView.isHidden = true - } - shareButton.isHidden = !isShareable + shareButton.isHidden = true } } diff --git a/NADA-iOS-forRelease/Sources/Cells/CardCell/MainCardCell.swift b/NADA-iOS-forRelease/Sources/Cells/CardCell/MainCardCell.swift index ce26947b..3a826b63 100644 --- a/NADA-iOS-forRelease/Sources/Cells/CardCell/MainCardCell.swift +++ b/NADA-iOS-forRelease/Sources/Cells/CardCell/MainCardCell.swift @@ -61,7 +61,13 @@ extension MainCardCell { contentView.addSubview(frontCard) case .company: - return + guard let frontCard = CompanyFrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? CompanyFrontCardCell else { return } + + frontCard.frame = CGRect(x: 0, y: 0, width: contentView.frame.width, height: contentView.frame.height) + guard let cardDataModel = cardDataModel else { return } + frontCard.initCellFromServer(cardData: cardDataModel, isShareable: isShareable ?? false) + + contentView.addSubview(frontCard) case .fan: guard let frontCard = FanFrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? FanFrontCardCell else { return } diff --git a/NADA-iOS-forRelease/Sources/Cells/CreationCard/CompanyFrontCardCreationCollectionViewCell.swift b/NADA-iOS-forRelease/Sources/Cells/CreationCard/CompanyFrontCardCreationCollectionViewCell.swift new file mode 100644 index 00000000..dcf4e064 --- /dev/null +++ b/NADA-iOS-forRelease/Sources/Cells/CreationCard/CompanyFrontCardCreationCollectionViewCell.swift @@ -0,0 +1,425 @@ +// +// FrontCardCreationCell.swift +// NADA-iOS-forRelease +// +// Created by kimhyungyu on 2021/09/24. +// + +import UIKit + +import IQKeyboardManagerSwift + +class CompanyFrontCardCreationCollectionViewCell: UICollectionViewCell { + + // MARK: - Protocols + + public weak var frontCardCreationDelegate: FrontCardCreationDelegate? + + // MARK: - Properties + + private let backgroundList = ["", "imageDefaultBg01", "imageDefaultBg02", "imageDefaultBg03", "imageDefaultBg04", "imageDefaultBg05", "imageDefaultBg06", "imageDefaultBg07"] + private var requiredTextFieldList = [UITextField]() + private var optionalTextFieldList = [UITextField]() + private var cardBackgroundImage: UIImage? + private var defaultImageIndex: Int? + private let maxLength: Int = 15 + + public var presentingBirthBottomVCClosure: (() -> Void)? + public var presentingMBTIBottomVCClosure: (() -> Void)? + + // MARK: - @IBOutlet Properties + + @IBOutlet weak var scrollView: UIScrollView! + + @IBOutlet weak var setBackgroundTextLabel: UILabel! + @IBOutlet weak var cardTitleInfoTextLabel: UILabel! + @IBOutlet weak var requiredInfoTextLabel: UILabel! + @IBOutlet weak var optionalInfoTextLabel: UILabel! + + @IBOutlet weak var backgroundSettingCollectionView: UICollectionView! + @IBOutlet weak var cardTitleTextField: UITextField! + @IBOutlet weak var userNameTextField: UITextField! + + @IBOutlet weak var birthLabel: UILabel! + @IBOutlet weak var birthView: UIView! + @IBOutlet weak var mbtiLabel: UILabel! + @IBOutlet weak var mbtiView: UIView! + + @IBOutlet weak var descriptionTextField: UITextField! + @IBOutlet weak var mailTextField: UITextField! + @IBOutlet weak var phoneNumberTextField: UITextField! + @IBOutlet weak var linkURLTextField: UITextField! + + @IBOutlet weak var bgView: UIView! + + // MARK: - Cell Life Cycle + + override func awakeFromNib() { + super.awakeFromNib() + + setUI() + setTapAction() + registerCell() + textFieldDelegate() + setNotification() + setAddTargets() + } +} + +// MARK: - Extensions + +extension CompanyFrontCardCreationCollectionViewCell { + private func setUI() { + IQKeyboardManager.shared.shouldResignOnTouchOutside = true + + initUITextFieldList() + backgroundSettingCollectionView.showsHorizontalScrollIndicator = false + scrollView.indicatorStyle = .default + scrollView.backgroundColor = .background + bgView.backgroundColor = .background + backgroundSettingCollectionView.backgroundColor = .background + + let collectionViewLayout = backgroundSettingCollectionView.collectionViewLayout as? UICollectionViewFlowLayout + collectionViewLayout?.estimatedItemSize = .zero + collectionViewLayout?.scrollDirection = .horizontal + + let backgroundAttributeString = NSMutableAttributedString(string: "*명함의 배경을 선택해 주세요.") + backgroundAttributeString.addAttribute(.foregroundColor, value: UIColor.mainColorNadaMain, range: NSRange(location: 0, length: 1)) + backgroundAttributeString.addAttribute(.foregroundColor, value: UIColor.secondary, range: NSRange(location: 1, length: backgroundAttributeString.length - 1)) + setBackgroundTextLabel.attributedText = backgroundAttributeString + setBackgroundTextLabel.font = .textBold01 + + let cardTitleAttributeString = NSMutableAttributedString(string: "*명함에 이름을 붙여 주세요.") + cardTitleAttributeString.addAttribute(.foregroundColor, value: UIColor.mainColorNadaMain, range: NSRange(location: 0, length: 1)) + cardTitleAttributeString.addAttribute(.foregroundColor, value: UIColor.secondary, range: NSRange(location: 1, length: cardTitleAttributeString.length - 1)) + cardTitleInfoTextLabel.attributedText = cardTitleAttributeString + cardTitleInfoTextLabel.font = .textBold01 + + let requiredAttributeString = NSMutableAttributedString(string: "*명함에 대한 기본정보를 입력해 주세요.") + requiredAttributeString.addAttribute(.foregroundColor, value: UIColor.mainColorNadaMain, range: NSRange(location: 0, length: 1)) + requiredAttributeString.addAttribute(.foregroundColor, value: UIColor.secondary, range: NSRange(location: 1, length: requiredAttributeString.length - 1)) + requiredInfoTextLabel.attributedText = requiredAttributeString + requiredInfoTextLabel.font = .textBold01 + + optionalInfoTextLabel.text = "나를 더 표현할 수 있는 정보가 있나요?" + optionalInfoTextLabel.font = .textBold01 + optionalInfoTextLabel.textColor = .secondary + + cardTitleTextField.attributedPlaceholder = NSAttributedString(string: "명함 이름 (15자)", attributes: [ + NSAttributedString.Key.foregroundColor: UIColor.quaternary + ]) + userNameTextField.attributedPlaceholder = NSAttributedString(string: "본인 이름 (15자)", attributes: [ + NSAttributedString.Key.foregroundColor: UIColor.quaternary + ]) + + birthView.layer.cornerRadius = 10 + birthView.backgroundColor = .textBox + birthLabel.font = .textRegular04 + birthLabel.textColor = .quaternary + birthLabel.text = "생일" + + mbtiView.layer.cornerRadius = 10 + mbtiView.backgroundColor = .textBox + mbtiLabel.font = .textRegular04 + mbtiLabel.textColor = .quaternary + mbtiLabel.text = "MBTI" + + descriptionTextField.attributedPlaceholder = NSAttributedString(string: "부서명 (15자)", + attributes: [ + NSAttributedString.Key.foregroundColor: UIColor.quaternary + ]) + mailTextField.attributedPlaceholder = NSAttributedString(string: "메일주소", + attributes: [ + NSAttributedString.Key.foregroundColor: UIColor.quaternary + ]) + phoneNumberTextField.attributedPlaceholder = NSAttributedString(string: "전화번호", + attributes: [ + NSAttributedString.Key.foregroundColor: UIColor.quaternary + ]) + linkURLTextField.attributedPlaceholder = NSAttributedString(string: "URL (Github, Blog, Linkedin)", + attributes: [ + NSAttributedString.Key.foregroundColor: UIColor.quaternary + ]) + + phoneNumberTextField.keyboardType = .numberPad + + _ = requiredTextFieldList.map { + $0.font = .textRegular04 + $0.backgroundColor = .textBox + $0.textColor = .primary + $0.layer.cornerRadius = 10 + $0.borderStyle = .none + $0.setLeftPaddingPoints(12) + } + _ = optionalTextFieldList.map { + $0.font = .textRegular04 + $0.backgroundColor = .textBox + $0.textColor = .primary + $0.layer.cornerRadius = 10 + $0.borderStyle = .none + $0.setLeftPaddingPoints(12) + } + } + private func setTapAction() { + let birthViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(touchBirthView)) + birthView.addGestureRecognizer(birthViewTapGesture) + + let mbtiViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(touchMBTIView)) + mbtiView.addGestureRecognizer(mbtiViewTapGesture) + } + private func initUITextFieldList() { + requiredTextFieldList.append(contentsOf: [ + cardTitleTextField, + userNameTextField + ]) + optionalTextFieldList.append(contentsOf: [ + mailTextField, + linkURLTextField, + descriptionTextField, + phoneNumberTextField + ]) + } + private func registerCell() { + backgroundSettingCollectionView.delegate = self + backgroundSettingCollectionView.dataSource = self + + backgroundSettingCollectionView.register(BackgroundCollectionViewCell.nib(), forCellWithReuseIdentifier: BackgroundCollectionViewCell.className) + } + private func textFieldDelegate() { + _ = requiredTextFieldList.map { $0.delegate = self } + _ = optionalTextFieldList.map { $0.delegate = self } + } + private func setNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(setBirthText(notification:)), name: .completeFrontCardBirth, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(setMBTIText(notification:)), name: .completeFrontCardMBTI, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(setCardBackgroundImage(notifiation:)), name: .sendNewImage, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(textFieldDidChange(_:)), name: UITextField.textDidChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(dismissBorderLine), name: .dismissRequiredBottomSheet, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(cancelImagePicker), name: .cancelImagePicker, object: nil) + } + + /// front card 가 편집되었는지. 필수 항목이 다 입력되었는지 체크. + private func checkFrontCradStatus() { + frontCardCreationDelegate?.frontCardCreation(endEditing: true) + if cardTitleTextField.hasText && + userNameTextField.hasText && + birthLabel.text != "생년월일" && + mbtiLabel.text != "MBTI" && + defaultImageIndex != nil { + frontCardCreationDelegate?.frontCardCreation(requiredInfo: true) + } else { + frontCardCreationDelegate?.frontCardCreation(requiredInfo: false) + } + if let defaultImageIndex { + frontCardCreationDelegate?.frontCardCreation(with: FrontCardDataModel(birth: birthLabel.text ?? "", + cardName: cardTitleTextField.text ?? "", + userName: userNameTextField.text ?? "", + departmentName: descriptionTextField.text, + mailAddress: mailTextField.text, + mbti: mbtiLabel.text, + phoneNumber: phoneNumberTextField.text, + instagram: nil, + twitter: nil, + urls: linkURLTextField.text == nil ? nil : [linkURLTextField.text ?? ""], + defaultImageIndex: defaultImageIndex)) + } + } + private func setAddTargets() { + phoneNumberTextField.addTarget(self, action: #selector(phoneNumberTextFieldDidChange), for: .editingChanged) + } + static func nib() -> UINib { + return UINib(nibName: CompanyFrontCardCreationCollectionViewCell.className, bundle: Bundle(for: CompanyFrontCardCreationCollectionViewCell.self)) + } + + // MARK: - @objc Methods + + @objc + private func setBirthText(notification: NSNotification) { + birthLabel.text = notification.object as? String + birthLabel.textColor = .primary + birthView.borderWidth = 0 + + checkFrontCradStatus() + } + @objc + private func setMBTIText(notification: NSNotification) { + mbtiLabel.text = notification.object as? String + mbtiLabel.textColor = .primary + mbtiView.borderWidth = 0 + + checkFrontCradStatus() + } + @objc + private func setCardBackgroundImage(notifiation: NSNotification) { + cardBackgroundImage = notifiation.object as? UIImage + defaultImageIndex = 0 + backgroundSettingCollectionView.reloadData() + + checkFrontCradStatus() + } + @objc + private func textFieldDidChange(_ notification: Notification) { + if let textField = notification.object as? UITextField { + switch textField { + case cardTitleTextField: + if let text = cardTitleTextField.text { + if text.count > maxLength { + let maxIndex = text.index(text.startIndex, offsetBy: maxLength) + let newString = String(text[text.startIndex.. maxLength { + let maxIndex = text.index(text.startIndex, offsetBy: maxLength) + let newString = String(text[text.startIndex.. maxLength { + let maxIndex = text.index(text.startIndex, offsetBy: maxLength) + let newString = String(text[text.startIndex.. Int { + return backgroundList.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Const.Xib.backgroundCollectionViewCell, for: indexPath) as? BackgroundCollectionViewCell else { + return UICollectionViewCell() + } + switch indexPath.item { + case 0: + cell.initCell(image: cardBackgroundImage ?? UIImage(), isFirst: true) + default: + guard let image = UIImage(named: backgroundList[indexPath.item]) else { return UICollectionViewCell() } + cell.initCell(image: image, isFirst: false) + } + + if defaultImageIndex == 0 && + indexPath.item == 0 { + collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .init()) + } + return cell + } +} + +// MARK: - UICollectionViewDelegateFlowLayout +extension CompanyFrontCardCreationCollectionViewCell: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets(top: 0, left: 26, bottom: 0, right: 26) + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 12 + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: CGFloat(60), height: CGFloat(60)) + } +} + +// MARK: - UITextFieldDelegate +extension CompanyFrontCardCreationCollectionViewCell: UITextFieldDelegate { + func textFieldDidBeginEditing(_ textField: UITextField) { + textField.borderWidth = 1 + textField.borderColor = .tertiary + } + func textFieldDidEndEditing(_ textField: UITextField) { + checkFrontCradStatus() + textField.resignFirstResponder() + textField.borderWidth = 0 + } + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + return textField.resignFirstResponder() + } +} diff --git a/NADA-iOS-forRelease/Sources/Cells/CreationCard/CompanyFrontCardCreationCollectionViewCell.xib b/NADA-iOS-forRelease/Sources/Cells/CreationCard/CompanyFrontCardCreationCollectionViewCell.xib new file mode 100644 index 00000000..a50f222a --- /dev/null +++ b/NADA-iOS-forRelease/Sources/Cells/CreationCard/CompanyFrontCardCreationCollectionViewCell.xib @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NADA-iOS-forRelease/Sources/Cells/CreationCard/FrontCardCreationCollectionViewCell.swift b/NADA-iOS-forRelease/Sources/Cells/CreationCard/FrontCardCreationCollectionViewCell.swift index fbad1419..22161b7f 100644 --- a/NADA-iOS-forRelease/Sources/Cells/CreationCard/FrontCardCreationCollectionViewCell.swift +++ b/NADA-iOS-forRelease/Sources/Cells/CreationCard/FrontCardCreationCollectionViewCell.swift @@ -285,14 +285,6 @@ extension FrontCardCreationCollectionViewCell { descriptionTextField.text = newString } } - case phoneNumberTextField: - if let text = phoneNumberTextField.text { - if text.count > maxLength { - let maxIndex = text.index(text.startIndex, offsetBy: maxLength) - let newString = String(text[text.startIndex..) -> Void) { - groupProvider.request(.groupReset(token: token)) { (result) in + func groupReset(completion: @escaping (NetworkResult) -> Void) { + groupProvider.request(.groupReset) { (result) in switch result { case .success(let response): let statusCode = response.statusCode @@ -144,47 +144,7 @@ public class GroupAPI { } // MARK: - JudgeStatus methods - - private func judgeGroupListFetchStatus(by statusCode: Int, _ data: Data) -> NetworkResult { - - let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) - else { - return .pathErr - } - - switch statusCode { - case 200: - return .success(decodedData.data ?? "None-Data") - case 400..<500: - return .requestErr(decodedData.message ?? "error message") - case 500: - return .serverErr - default: - return .networkFail - } - } - - private func judgeCardListFetchInGroupStatus(by statusCode: Int, _ data: Data) -> NetworkResult { - - let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) - else { - return .pathErr - } - - switch statusCode { - case 200: - return .success(decodedData.data ?? "None-Data") - case 400..<500: - return .requestErr(decodedData.message ?? "error message") - case 500: - return .serverErr - default: - return .networkFail - } - } - + private func judgeStatus(by statusCode: Int, data: Data, type: T.Type) -> NetworkResult { let decoder = JSONDecoder() guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) diff --git a/NADA-iOS-forRelease/Sources/NetworkService/Group/GroupService.swift b/NADA-iOS-forRelease/Sources/NetworkService/Group/GroupService.swift index 866fa3a7..cdc6fc20 100644 --- a/NADA-iOS-forRelease/Sources/NetworkService/Group/GroupService.swift +++ b/NADA-iOS-forRelease/Sources/NetworkService/Group/GroupService.swift @@ -16,7 +16,7 @@ enum GroupService { case cardAddInGroup(cardRequest: CardAddInGroupRequest) case cardListFetchInGroup(cardListInGroupRequest: CardListInGroupRequest) case cardDeleteInGroup(groupID: Int, cardID: String) - case groupReset(token: String) + case groupReset } extension GroupService: TargetType { @@ -35,8 +35,10 @@ extension GroupService: TargetType { var path: String { switch self { - case .groupListFetch, .groupReset: + case .groupListFetch: return "/card-group/list" + case .groupReset: + return "/card-group/clear" case .groupDelete(let groupID, _): return "/group/\(groupID)" case .groupAdd, .groupEdit: @@ -54,9 +56,9 @@ extension GroupService: TargetType { switch self { case .groupListFetch, .cardListFetchInGroup: return .get - case .groupDelete, .cardDeleteInGroup, .groupReset: + case .groupDelete, .cardDeleteInGroup: return .delete - case .groupAdd, .cardAddInGroup: + case .groupAdd, .cardAddInGroup, .groupReset: return .post case .groupEdit: return .put diff --git a/NADA-iOS-forRelease/Sources/NetworkService/Plugin/MoyaLoggerPlugin.swift b/NADA-iOS-forRelease/Sources/NetworkService/Plugin/MoyaLoggerPlugin.swift index 78b50bc4..ce865cb1 100644 --- a/NADA-iOS-forRelease/Sources/NetworkService/Plugin/MoyaLoggerPlugin.swift +++ b/NADA-iOS-forRelease/Sources/NetworkService/Plugin/MoyaLoggerPlugin.swift @@ -56,15 +56,15 @@ final class MoyaLoggerPlugin: PluginType { log.append("------------------- END HTTP (\(response.data.count)-byte body) -------------------") print(log) - switch statusCode { - case 401: - let acessToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken) - let refreshToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.refreshToken) - userTokenReissueWithAPI(request: UserReissueToken(accessToken: acessToken ?? "", - refreshToken: refreshToken ?? "")) - default: - return - } +// switch statusCode { +// case 401: +// let acessToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken) +// let refreshToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.refreshToken) +// userTokenReissueWithAPI(request: UserReissueToken(accessToken: acessToken ?? "", +// refreshToken: refreshToken ?? "")) +// default: +// return +// } } func onFail(_ error: MoyaError, target: TargetType) { @@ -88,32 +88,32 @@ final class MoyaLoggerPlugin: PluginType { extension MoyaLoggerPlugin { func userTokenReissueWithAPI(request: UserReissueToken) { - UserAPI.shared.userTokenReissue(request: request) { response in - switch response { - case .success(let data): - if let tokenData = data as? UserReissueToken { - UserDefaults.standard.set(tokenData.accessToken, forKey: Const.UserDefaultsKey.accessToken) - UserDefaults.standard.set(tokenData.refreshToken, forKey: Const.UserDefaultsKey.refreshToken) - - print("userTokenReissueWithAPI - success") - } - case .requestErr(let statusCode): - if let statusCode = statusCode as? Int, statusCode == 406 { - let loginVC = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.loginViewController) - UIApplication.shared.windows.first {$0.isKeyWindow}?.rootViewController = loginVC - - UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.accessToken) - UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.refreshToken) - UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.userID) - } - print("userTokenReissueWithAPI - requestErr: \(statusCode)") - case .pathErr: - print("userTokenReissueWithAPI - pathErr") - case .serverErr: - print("userTokenReissueWithAPI - serverErr") - case .networkFail: - print("userTokenReissueWithAPI - networkFail") - } - } +// UserAPI.shared.userTokenReissue(request: request) { response in +// switch response { +// case .success(let data): +// if let tokenData = data as? UserReissueToken { +// UserDefaults.standard.set(tokenData.accessToken, forKey: Const.UserDefaultsKey.accessToken) +// UserDefaults.standard.set(tokenData.refreshToken, forKey: Const.UserDefaultsKey.refreshToken) +// +// print("userTokenReissueWithAPI - success") +// } +// case .requestErr(let statusCode): +// if let statusCode = statusCode as? Int, statusCode == 406 { +// let loginVC = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.loginViewController) +// UIApplication.shared.windows.first {$0.isKeyWindow}?.rootViewController = loginVC +// +// UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.accessToken) +// UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.refreshToken) +// UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.userID) +// } +// print("userTokenReissueWithAPI - requestErr: \(statusCode)") +// case .pathErr: +// print("userTokenReissueWithAPI - pathErr") +// case .serverErr: +// print("userTokenReissueWithAPI - serverErr") +// case .networkFail: +// print("userTokenReissueWithAPI - networkFail") +// } +// } } } diff --git a/NADA-iOS-forRelease/Sources/NetworkService/User/UserAPI.swift b/NADA-iOS-forRelease/Sources/NetworkService/User/UserAPI.swift index aa2640ea..0f3c58b1 100644 --- a/NADA-iOS-forRelease/Sources/NetworkService/User/UserAPI.swift +++ b/NADA-iOS-forRelease/Sources/NetworkService/User/UserAPI.swift @@ -15,14 +15,14 @@ public class UserAPI { public init() { } - func userDelete(token: String, completion: @escaping (NetworkResult) -> Void) { - userProvider.request(.userDelete(token: token)) { (result) in + func userDelete(completion: @escaping (NetworkResult) -> Void) { + userProvider.request(.userDelete) { (result) in switch result { case .success(let response): let statusCode = response.statusCode let data = response.data - let networkResult = self.judgeStatus(by: statusCode, data) + let networkResult = self.judgeStatus(by: statusCode, data: data, type: String.self) completion(networkResult) case .failure(let err): @@ -37,7 +37,7 @@ public class UserAPI { case .success(let response): let statusCode = response.statusCode let data = response.data - let networkResult = self.judgeUserSocialSignUpStatus(by: statusCode, data) + let networkResult = self.judgeStatus(by: statusCode, data: data, type: AccessToken.self) completion(networkResult) case .failure(let err): @@ -46,102 +46,11 @@ public class UserAPI { } } - func userLogout(token: String, completion: @escaping (NetworkResult) -> Void) { - userProvider.request(.userLogout(token: token)) { (result) in - switch result { - case .success(let response): - let statusCode = response.statusCode - let data = response.data - - let networkResult = self.judgeStatus(by: statusCode, data) - completion(networkResult) - - case .failure(let err): - print(err) - } - } - } - - func userTokenReissue(request: UserReissueToken, completion: @escaping (NetworkResult) -> Void) { - userProvider.request(.userTokenReissue(request: request)) { (result) in - switch result { - case .success(let response): - let statusCode = response.statusCode - let data = response.data - - let networkResult = self.judgeUserTokenReissueStatus(by: statusCode, data) - completion(networkResult) - - case .failure(let err): - print(err) - } - } - } - // MARK: - JudgeStatus methods - private func judgeUserSocialSignUpStatus(by statusCode: Int, _ data: Data) -> NetworkResult { - let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) - else { - return .pathErr - } - - switch statusCode { - case 200: - return .success(decodedData.data ?? "None-Data") - case 400..<500: - return .requestErr(decodedData.message ?? "error message") - case 500: - return .serverErr - default: - return .networkFail - } - } - - private func judgeUserTokenFetchStatus(by statusCode: Int, _ data: Data) -> NetworkResult { - - let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) - else { - return .pathErr - } - - switch statusCode { - case 200: - return .success(decodedData.data ?? "None-Data") - case 400..<500: - return .requestErr(decodedData.message ?? "error message") - case 500: - return .serverErr - default: - return .networkFail - } - } - - private func judgeUserTokenReissueStatus(by statusCode: Int, _ data: Data) -> NetworkResult { - - let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) - else { - return .pathErr - } - - switch statusCode { - case 200: - return .success(decodedData.data ?? "None-Data") - case 400..<500: - return .requestErr(statusCode) - case 500: - return .serverErr - default: - return .networkFail - } - } - - private func judgeStatus(by statusCode: Int, _ data: Data) -> NetworkResult { + private func judgeStatus(by statusCode: Int, data: Data, type: T.Type) -> NetworkResult { let decoder = JSONDecoder() - guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) + guard let decodedData = try? decoder.decode(GenericResponse.self, from: data) else { return .pathErr } switch statusCode { diff --git a/NADA-iOS-forRelease/Sources/NetworkService/User/UserSevice.swift b/NADA-iOS-forRelease/Sources/NetworkService/User/UserSevice.swift index 3b532395..0d8e78d8 100644 --- a/NADA-iOS-forRelease/Sources/NetworkService/User/UserSevice.swift +++ b/NADA-iOS-forRelease/Sources/NetworkService/User/UserSevice.swift @@ -9,10 +9,8 @@ import Foundation import Moya enum UserSevice { - case userDelete(token: String) + case userDelete case userSocialSignUp(socialID: String, socialType: String) - case userLogout(token: String) - case userTokenReissue(request: UserReissueToken) } extension UserSevice: TargetType { @@ -24,21 +22,17 @@ extension UserSevice: TargetType { var path: String { switch self { case .userDelete: - return "/user" + return "/member" case .userSocialSignUp: return "/auth/signup" - case .userLogout: - return "auth/logout" - case .userTokenReissue: - return "auth/reissue" } } var method: Moya.Method { switch self { - case .userSocialSignUp, .userTokenReissue: + case .userSocialSignUp: return .post - case .userDelete, .userLogout: + case .userDelete: return .delete } } @@ -49,22 +43,20 @@ extension UserSevice: TargetType { var task: Task { switch self { - case .userDelete, .userLogout: + case .userDelete: return .requestPlain case .userSocialSignUp(let socialID, let socialType): return .requestParameters(parameters: ["socialId": socialID, "socialType": socialType], encoding: JSONEncoding.default) - case .userTokenReissue(let request): - return .requestJSONEncodable(request) } } var headers: [String: String]? { switch self { - case .userSocialSignUp, .userTokenReissue: + case .userSocialSignUp: return Const.Header.applicationJsonHeader() - case .userDelete, .userLogout: + case .userDelete: return Const.Header.bearerHeader() } } diff --git a/NADA-iOS-forRelease/Sources/SceneDelegate.swift b/NADA-iOS-forRelease/Sources/SceneDelegate.swift index 71671dac..96fa7187 100644 --- a/NADA-iOS-forRelease/Sources/SceneDelegate.swift +++ b/NADA-iOS-forRelease/Sources/SceneDelegate.swift @@ -56,6 +56,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { if let url = userActivity.webpageURL { let _ = DynamicLinks.dynamicLinks().handleUniversalLink(url) { dynamicLink, error in if let cardUUID = self.handleDynamicLink(dynamicLink) { + NotificationCenter.default.post(name: .presentDynamicLink, object: cardUUID) + UserDefaults.standard.setValue(cardUUID, forKey: Const.UserDefaultsKey.dynamicLinkCardUUID) } } } diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationPreviewViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationPreviewViewController.swift index b63d873a..5fe7c8be 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationPreviewViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationPreviewViewController.swift @@ -120,27 +120,23 @@ extension CardCreationPreviewViewController { frontCard.frame = CGRect(x: 0, y: 0, width: cardView.frame.width, height: cardView.frame.height) guard let frontCardDataModel = frontCardDataModel else { return } - frontCard.initCell(cardBackgroundImage, - frontCardDataModel.cardName, - frontCardDataModel.departmentName ?? "", - frontCardDataModel.userName, - frontCardDataModel.birth, - frontCardDataModel.mbti ?? "", - frontCardDataModel.instagram ?? "", - frontCardDataModel.phoneNumber ?? "", - frontCardDataModel.urls?[0] ?? "", - isShareable: isShareable) + frontCard.initCell(cardBackgroundImage, frontCardDataModel) cardView.addSubview(frontCard) case .company: - return + guard let frontCard = CompanyFrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? CompanyFrontCardCell else { return } + frontCard.frame = CGRect(x: 0, y: 0, width: cardView.frame.width, height: cardView.frame.height) + + guard let frontCardDataModel = frontCardDataModel else { return } + frontCard.initCell(cardBackgroundImage, frontCardDataModel) + + cardView.addSubview(frontCard) case .fan: guard let frontCard = FanFrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? FanFrontCardCell else { return } frontCard.frame = CGRect(x: 0, y: 0, width: cardView.frame.width, height: cardView.frame.height) guard let frontCardDataModel = frontCardDataModel else { return } - frontCard.initCell(cardBackgroundImage, - frontCardDataModel: frontCardDataModel) + frontCard.initCell(cardBackgroundImage, frontCardDataModel) cardView.addSubview(frontCard) } diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationViewController.swift index bd8028b4..5d73e743 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CardCreationViewController.swift @@ -87,7 +87,7 @@ class CardCreationViewController: UIViewController { nextVC.backCardDataModel = backCard nextVC.cardBackgroundImage = backgroundImage nextVC.tasteInfo = tasteInfo - nextVC.cardType = .basic + nextVC.cardType = cardType navigationController?.pushViewController(nextVC, animated: true) } } diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CompanyCardCreationViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CompanyCardCreationViewController.swift new file mode 100644 index 00000000..66ff1026 --- /dev/null +++ b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/CompanyCardCreationViewController.swift @@ -0,0 +1,400 @@ +// +// CompanyCardCreationViewController.swift +// NADA-iOS-forRelease +// +// Created by kimhyungyu on 2023/04/24. +// + +import UIKit + +import YPImagePicker + +class CompanyCardCreationViewController: UIViewController { + + // MARK: - Properties + + enum ButtonState { + case enable + case disable + } + + var completeButtonIsEnabled: ButtonState = .disable { + didSet { + if completeButtonIsEnabled == .disable { + completeButton.isEnabled = false + if #available(iOS 15.0, *) { + completeButton.setNeedsUpdateConfiguration() + } + } else { + completeButton.isEnabled = true + if #available(iOS 15.0, *) { + completeButton.setNeedsUpdateConfiguration() + } + } + } + } + + private var frontCardRequiredIsEmpty = true + private var backCardRequiredIsEmpty = true + private var isEditingMode = false + private var currentIndex = 0 + private var frontCard: FrontCardDataModel? + private var backCard: BackCardDataModel? + private var mbtiText: String? + private var birthText: String? + private var backgroundImage: UIImage? + private var tasteInfo: [TasteInfo]? + + private let cardType: CardType = .company + + // MARK: - @IBOutlet Properties + + @IBOutlet weak var creationTextLabel: UILabel! + @IBOutlet weak var frontTextLabel: UILabel! + @IBOutlet weak var backTextLabel: UILabel! + + @IBOutlet weak var statusMovedView: UIView! + @IBOutlet weak var cardCreationCollectionView: UICollectionView! + @IBOutlet weak var closeButton: UIButton! + @IBOutlet weak var completeButton: UIButton! + + // MARK: - View Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + setUI() + registerCell() + setTextLabelGesture() + setNotification() + tasteFetchWithAPI(cardType: cardType) + } + + // MARK: - @IBAction Properties + + @IBAction func dismissToPreviousView(_ sender: Any) { + if isEditingMode { + makeOKCancelAlert(title: "입력 취소", message: "입력한 내용이 모두 삭제됩니다. 돌아가시겠습니까?", okAction: { _ in + self.dismiss(animated: true, completion: nil) + }) + } else { + self.dismiss(animated: true, completion: nil) + } + } + @IBAction func pushToCardCompletionView(_ sender: Any) { + guard let nextVC = UIStoryboard.init(name: Const.Storyboard.Name.cardCreationPreview, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.cardCreationPreviewViewController) as? CardCreationPreviewViewController else { return } + + nextVC.frontCardDataModel = frontCard + nextVC.backCardDataModel = backCard + nextVC.cardBackgroundImage = backgroundImage + nextVC.tasteInfo = tasteInfo + nextVC.cardType = cardType + navigationController?.pushViewController(nextVC, animated: true) + } +} + +// MARK: - Extensions +extension CompanyCardCreationViewController { + private func setUI() { + navigationController?.navigationBar.isHidden = true + + view.backgroundColor = .background + statusMovedView.backgroundColor = .secondary + cardCreationCollectionView.backgroundColor = .background + cardCreationCollectionView.isPagingEnabled = true + + creationTextLabel.text = "명함 생성" + creationTextLabel.font = .title02 + creationTextLabel.textColor = .primary + + frontTextLabel.text = "앞면" + frontTextLabel.font = .title01 + frontTextLabel.textColor = .primary + + backTextLabel.text = "뒷면" + backTextLabel.font = .title01 + backTextLabel.textColor = .quaternary + + closeButton.setImage(UIImage(named: "iconClear"), for: .normal) + closeButton.setTitle("", for: .normal) + + completeButton.titleLabel?.font = .button01 + completeButton.isEnabled = false + + // MARK: - #available(iOS 15.0, *) + if #available(iOS 15.0, *) { + var config = UIButton.Configuration.filled() + config.background.cornerRadius = 15 + completeButton.configuration = config + + let configHandler: UIButton.ConfigurationUpdateHandler = { button in + switch button.state { + case .disabled: + button.configuration?.title = "완료" + button.configuration?.baseBackgroundColor = .textBox + button.configuration?.baseForegroundColor = .white + default: + button.configuration?.title = "완료" + button.configuration?.baseBackgroundColor = .mainColorNadaMain + button.configuration?.baseForegroundColor = .white + } + } + completeButton.configurationUpdateHandler = configHandler + } else { + completeButton.layer.cornerRadius = 15 + + completeButton.setTitle("완료", for: .normal) + completeButton.setTitleColor(.white, for: .normal) + completeButton.setBackgroundImage(UIImage(named: "enableButtonBackground"), for: .normal) + + completeButton.setTitle("완료", for: .disabled) + completeButton.setTitleColor(.white, for: .disabled) + completeButton.setBackgroundImage(UIImage(named: "disableButtonBackground"), for: .disabled) + } + + let cardCreationCollectionViewlayout = cardCreationCollectionView.collectionViewLayout as? UICollectionViewFlowLayout + cardCreationCollectionViewlayout?.scrollDirection = .horizontal + cardCreationCollectionViewlayout?.estimatedItemSize = .zero + cardCreationCollectionView.showsHorizontalScrollIndicator = false + cardCreationCollectionView.showsVerticalScrollIndicator = false + } + private func registerCell() { + cardCreationCollectionView.delegate = self + cardCreationCollectionView.dataSource = self + + cardCreationCollectionView.register(CompanyFrontCardCreationCollectionViewCell.nib(), forCellWithReuseIdentifier: CompanyFrontCardCreationCollectionViewCell.className) + cardCreationCollectionView.register(BackCardCreationCollectionViewCell.nib(), forCellWithReuseIdentifier: BackCardCreationCollectionViewCell.className) + } + private func setTextLabelGesture() { + let tapFrontTextLabelGesture = UITapGestureRecognizer(target: self, action: #selector(dragToFront)) + frontTextLabel.addGestureRecognizer(tapFrontTextLabelGesture) + frontTextLabel.isUserInteractionEnabled = true + let tapBackTextLabelGesture = UITapGestureRecognizer(target: self, action: #selector(dragToBack)) + backTextLabel.addGestureRecognizer(tapBackTextLabelGesture) + backTextLabel.isUserInteractionEnabled = true + } + private func setNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(presentToImagePicker), name: .presentingImagePicker, object: nil) + } + + // MARK: - @objc Methods + + @objc + private func dragToBack() { + let indexPath = IndexPath(item: 1, section: 0) + cardCreationCollectionView.scrollToItem(at: indexPath, at: .right, animated: true) + if currentIndex == 0 { + UIView.animate(withDuration: 0.3) { + self.statusMovedView.transform = CGAffineTransform(translationX: self.backTextLabel.frame.origin.x - self.statusMovedView.frame.origin.x - 5, y: 0) + } + currentIndex = 1 + self.frontTextLabel.textColor = .quaternary + self.backTextLabel.textColor = .secondary + } + } + @objc + private func dragToFront() { + if currentIndex == 1 { + let indexPath = IndexPath(item: 0, section: 0) + cardCreationCollectionView.scrollToItem(at: indexPath, at: .left, animated: true) + UIView.animate(withDuration: 0.3) { + self.statusMovedView.transform = .identity + } + currentIndex = 0 + self.frontTextLabel.textColor = .secondary + self.backTextLabel.textColor = .quaternary + } + } + @objc + private func presentToImagePicker() { + var config = YPImagePickerConfiguration() + config.screens = [.library] + config.startOnScreen = .library + config.library.isSquareByDefault = false + config.showsPhotoFilters = false + config.shouldSaveNewPicturesToAlbum = false + config.showsCrop = .rectangle(ratio: 0.6) + config.colors.tintColor = .mainColorNadaMain + + let imagePicker = YPImagePicker(configuration: config) + imagePicker.imagePickerDelegate = self + + imagePicker.didFinishPicking { [weak self] items, cancelled in + guard let self = self else { return } + + if cancelled { + NotificationCenter.default.post(name: .cancelImagePicker, object: nil) + } + + if let photo = items.singlePhoto { + backgroundImage = photo.image + NotificationCenter.default.post(name: .sendNewImage, object: backgroundImage) + } + imagePicker.dismiss(animated: true) + } + + imagePicker.modalPresentationStyle = .overFullScreen + present(imagePicker, animated: true, completion: nil) + } +} + +// MARK: - YPImagePickerDelegate +extension CompanyCardCreationViewController: YPImagePickerDelegate { + func imagePickerHasNoItemsInLibrary(_ picker: YPImagePicker) { + self.makeOKAlert(title: "", message: "가져올 수 있는 사진이 없습니다.") + } + + func shouldAddToSelection(indexPath: IndexPath, numSelections: Int) -> Bool { + return true + } +} + +// MARK: - UICollectionViewDelegate +extension CompanyCardCreationViewController: UICollectionViewDelegate { + func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + let targetIndex = targetContentOffset.pointee.x / scrollView.frame.size.width + if targetIndex == 1 && currentIndex == 0 { + UIView.animate(withDuration: 0.3) { + self.statusMovedView.transform = CGAffineTransform(translationX: self.backTextLabel.frame.origin.x - self.statusMovedView.frame.origin.x - 5, y: 0) + } + currentIndex = 1 + self.frontTextLabel.textColor = .quaternary + self.backTextLabel.textColor = .primary + } else if targetIndex == 0 && currentIndex == 1 { + UIView.animate(withDuration: 0.2) { + self.statusMovedView.transform = .identity + } + currentIndex = 0 + self.frontTextLabel.textColor = .primary + self.backTextLabel.textColor = .quaternary + } + } +} + +// MARK: - UICollectionViewDataSource +extension CompanyCardCreationViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 2 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + if collectionView == cardCreationCollectionView { + if indexPath.item == 0 { + guard let frontCreationCell = collectionView.dequeueReusableCell(withReuseIdentifier: CompanyFrontCardCreationCollectionViewCell.className, for: indexPath) as? CompanyFrontCardCreationCollectionViewCell else { + return UICollectionViewCell() + } + frontCreationCell.frontCardCreationDelegate = self + frontCreationCell.presentingBirthBottomVCClosure = { + let nextVC = SelectBirthBottomSheetViewController() + .setTitle("날짜") + .setHeight(355) + nextVC.modalPresentationStyle = .overFullScreen + self.present(nextVC, animated: false, completion: nil) + } + frontCreationCell.presentingMBTIBottomVCClosure = { + let nextVC = SelectMBTIBottmViewController() + .setTitle("MBTI") + .setHeight(355) + nextVC.modalPresentationStyle = .overFullScreen + self.present(nextVC, animated: false, completion: nil) + } + + return frontCreationCell + } else if indexPath.item == 1 { + guard let backCreationCell = collectionView.dequeueReusableCell(withReuseIdentifier: Const.Xib.backCardCreationCollectionViewCell, for: indexPath) as? BackCardCreationCollectionViewCell else { + return UICollectionViewCell() + } + backCreationCell.backCardCreationDelegate = self + if let tasteInfo { + backCreationCell.flavorList = tasteInfo.map { $0.tasteName } + } + + return backCreationCell + } + } + return UICollectionViewCell() + } +} + +// MARK: - UICollectionViewDelegateFlowLayout +extension CompanyCardCreationViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let height = collectionView.frame.height + let width = collectionView.frame.width + + return CGSize(width: width, height: height) + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return .zero + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } +} + +// MARK: - FrontCardCreationDelegate + +extension CompanyCardCreationViewController: FrontCardCreationDelegate { + func frontCardCreation(requiredInfo valid: Bool) { + frontCardRequiredIsEmpty = !valid + if frontCardRequiredIsEmpty == false && backCardRequiredIsEmpty == false { + completeButtonIsEnabled = .enable + } else { + completeButtonIsEnabled = .disable + } + } + func frontCardCreation(endEditing valid: Bool) { + isEditingMode = valid + } + func frontCardCreation(with frontCardDataModel: FrontCardDataModel) { + frontCard = frontCardDataModel + } +} + +// MARK: - BackCardCreationDelegate + +extension CompanyCardCreationViewController: BackCardCreationDelegate { + func backCardCreation(requiredInfo valid: Bool) { + backCardRequiredIsEmpty = !valid + if frontCardRequiredIsEmpty == false && backCardRequiredIsEmpty == false { + completeButtonIsEnabled = .enable + } else { + completeButtonIsEnabled = .disable + } + } + func backCardCreation(endEditing valid: Bool) { + isEditingMode = valid + } + func backCardCreation(withRequired requiredInfo: [String], withOptional optionalInfo: String?) { + backCard = BackCardDataModel(tastes: requiredInfo, tmi: optionalInfo) + } +} + +// MARK: - API methods + +extension CompanyCardCreationViewController { + func tasteFetchWithAPI(cardType: CardType) { + CardAPI.shared.tasteFetch(cardType: cardType) { response in + switch response { + case .success(let data): + print("cardCreationWithAPI - success") + if let tastes = data as? Taste { + self.tasteInfo = tastes.tasteInfos.sorted { $0.sortOrder > $1.sortOrder } + DispatchQueue.main.async { [weak self] in + self?.cardCreationCollectionView.reloadData() + } + } + case .requestErr(let message): + print("tasteFetchWithAPI - requestErr: \(message)") + case .pathErr: + print("tasteFetchWithAPI - pathErr") + case .serverErr: + print("tasteFetchWithAPI - serverErr") + case .networkFail: + print("tasteFetchWithAPI - networkFail") + } + } + } +} diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/FanCardCreationViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/FanCardCreationViewController.swift index 50ee4f9e..eb236799 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/FanCardCreationViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/CardCreation/VC/FanCardCreationViewController.swift @@ -87,7 +87,7 @@ class FanCardCreationViewController: UIViewController { nextVC.backCardDataModel = backCard nextVC.cardBackgroundImage = backgroundImage nextVC.tasteInfo = tasteInfo - nextVC.cardType = .fan + nextVC.cardType = cardType navigationController?.pushViewController(nextVC, animated: true) } } diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/CardDetail/CardDetailViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/CardDetail/CardDetailViewController.swift index b8fc8c43..c75402fc 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/CardDetail/CardDetailViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/CardDetail/CardDetailViewController.swift @@ -146,13 +146,35 @@ extension CardDetailViewController { optionButton.showsMenuAsPrimaryAction = true } private func setFrontCard() { - guard let frontCard = FrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? FrontCardCell else { return } + guard let cardTypeString = cardDataModel?.cardType, + let cardType = CardType(rawValue: cardTypeString) else { return } - frontCard.frame = CGRect(x: 0, y: 0, width: cardView.frame.width, height: cardView.frame.height) - guard let cardDataModel = cardDataModel else { return } - frontCard.initCellFromServer(cardData: cardDataModel, isShareable: isShareable) - - cardView.addSubview(frontCard) + switch cardType { + case .basic: + guard let frontCard = FrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? FrontCardCell else { return } + + frontCard.frame = CGRect(x: 0, y: 0, width: cardView.frame.width, height: cardView.frame.height) + guard let cardDataModel = cardDataModel else { return } + frontCard.initCellFromServer(cardData: cardDataModel, isShareable: isShareable) + + cardView.addSubview(frontCard) + case .company: + guard let frontCard = CompanyFrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? CompanyFrontCardCell else { return } + + frontCard.frame = CGRect(x: 0, y: 0, width: cardView.frame.width, height: cardView.frame.height) + guard let cardDataModel = cardDataModel else { return } + frontCard.initCellFromServer(cardData: cardDataModel, isShareable: isShareable) + + cardView.addSubview(frontCard) + case .fan: + guard let frontCard = FanFrontCardCell.nib().instantiate(withOwner: self, options: nil).first as? FanFrontCardCell else { return } + + frontCard.frame = CGRect(x: 0, y: 0, width: cardView.frame.width, height: cardView.frame.height) + guard let cardDataModel = cardDataModel else { return } + frontCard.initCellFromServer(cardData: cardDataModel, isShareable: isShareable) + + cardView.addSubview(frontCard) + } } private func setGestureRecognizer() { let swipeLeftGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(transitionCardWithAnimation(_:))) diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift index e92abfef..2e065a19 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/Home/VC/HomeViewController.swift @@ -174,6 +174,27 @@ extension HomeViewController { }.disposed(by: self.disposeBag) } + private func checkUpdateVersion() { + updateUserInfoFetchWithAPI { [weak self] checkUpdateNote in + if !checkUpdateNote { + self?.updateNoteFetchWithAPI { [weak self] updateNote in + if self?.checkUpdateAvailable(updateNote.latestVersion) ?? false { + self?.presentToUpdateVC(with: updateNote) + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.dynamicLinkCardUUID) + } else { + if let dynamicLinkCardUUID = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.dynamicLinkCardUUID) { + self?.checkDynamicLink(dynamicLinkCardUUID) + } + } + } + } else { + if let dynamicLinkCardUUID = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.dynamicLinkCardUUID) { + self?.checkDynamicLink(dynamicLinkCardUUID) + } + } + } + } + private func checkUpdateAvailable(_ latestVersion: String) -> Bool { var latestVersion = latestVersion latestVersion.removeFirst() @@ -189,39 +210,27 @@ extension HomeViewController { } } - private func checkUpdateVersion() { - updateUserInfoFetchWithAPI { [weak self] checkUpdateNote in - if !checkUpdateNote { - self?.updateNoteFetchWithAPI { [weak self] updateNote in - if self?.checkUpdateAvailable(updateNote.latestVersion) ?? false { - self?.presentToUpdateVC(with: updateNote) - } - } - } - } - } - private func presentToUpdateVC(with updateNote: UpdateNote?) { let updateVC = ModuleFactory.shared.makeUpdateVC() updateVC.updateNote = updateNote self.present(updateVC, animated: true) } - private func presentToCardDetailVC(cardDataModel: Card) { - let cardDetailVC = moduleFactory.makeCardDetailVC() - cardDetailVC.status = .add - cardDetailVC.cardDataModel = cardDataModel - self.present(cardDetailVC, animated: true) - } - - public func checkDynamicLink() { - let dynamicLinkCardUUID = "" + private func checkDynamicLink(_ dynamicLinkCardUUID: String) { self.cardDetailFetchWithAPI(cardUUID: dynamicLinkCardUUID) { [weak self] cardDataModel in self?.cardAddInGroupWithAPI(cardUUID: dynamicLinkCardUUID) { [weak self] in + UserDefaults.standard.removeObject(forKey: Const.UserDefaultsKey.dynamicLinkCardUUID) self?.presentToCardDetailVC(cardDataModel: cardDataModel) } } } + + private func presentToCardDetailVC(cardDataModel: Card) { + let cardDetailVC = moduleFactory.makeCardDetailVC() + cardDetailVC.status = .add + cardDetailVC.cardDataModel = cardDataModel + self.present(cardDetailVC, animated: true) + } } // MARK: - Network diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/More/MoreViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/More/MoreViewController.swift index ed65d090..93901632 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/More/MoreViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/More/MoreViewController.swift @@ -14,7 +14,7 @@ class MoreViewController: UIViewController { let defaults = UserDefaults.standard var firstItems = ["개인정보 처리방침", "서비스 이용약관", "Team NADA", "오픈소스 라이브러리"] - var secondItems = ["로그아웃", "받은 명함 초기화", "모든 명함 삭제하기"] + var secondItems = ["로그아웃", "받은 명함 초기화", "회원탈퇴"] // MARK: - @IBOutlet Properties @IBOutlet weak var moreListTableView: UITableView! @@ -111,10 +111,8 @@ extension MoreViewController { makeOKCancelAlert(title: "", message: "로그아웃 하시겠습니까?", okAction: { _ in self.makeOKAlert(title: "", message: "로그아웃이 완료 되었습니다.") { _ in if let acToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken) { - self.logoutUserWithAPI(token: acToken) - self.defaults.removeObject(forKey: Const.UserDefaultsKey.accessToken) - self.defaults.removeObject(forKey: Const.UserDefaultsKey.refreshToken) +// self.defaults.removeObject(forKey: Const.UserDefaultsKey.refreshToken) self.defaults.removeObject(forKey: Const.UserDefaultsKey.darkModeState) let nextVC = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.loginViewController) @@ -126,39 +124,26 @@ extension MoreViewController { } func setResetClicked() { - makeOKCancelAlert(title: "", message: "받은 명함과 그룹이 모두 초기화됩니다. 정말 초기화하시겠습니까?", okAction: { _ in - UserApi.shared.logout { (error) in - if let error = error { - print(error) - } else { - self.makeOKAlert(title: "", message: "받은 명함이 초기화 되었습니다.") - if let acToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken) { - } + makeOKCancelAlert(title: "", message: "받은 명함과 명함 모음 그룹이 모두 초기화됩니다. 정말 초기화하시겠습니까?", okAction: { [weak self] _ in + self?.groupResetWithAPI { + self?.makeOKAlert(title: "", message: "받은 명함이 초기화 되었습니다.") { _ in + self?.tabBarController?.selectedIndex = 2 } } }) } func setDeleteCicked() { - makeOKCancelAlert(title: "", message: "내 명함과 받은 명함이 모두 삭제됩니다. 삭제 하시겠습니까?", okAction: { _ in - UserApi.shared.logout { (error) in - if let error = error { - print(error) - } else { - self.makeOKAlert(title: "", message: "모든 명함이 삭제되었습니다.") { _ in - // TODO: - KeyChain 적용 - if let acToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken) { - self.deleteUserWithAPI(token: acToken) - - self.defaults.removeObject(forKey: Const.UserDefaultsKey.accessToken) - self.defaults.removeObject(forKey: Const.UserDefaultsKey.refreshToken) - self.defaults.removeObject(forKey: Const.UserDefaultsKey.darkModeState) - - let nextVC = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.loginViewController) - nextVC.modalPresentationStyle = .overFullScreen - self.navigationController?.changeRootViewController(nextVC) - } - } + makeOKCancelAlert(title: "", message: "정말 탈퇴하시겠습니까?\n앱 내 정보가 모두 삭제되며, 이후 복구는 불가합니다.", okAction: { [weak self ]_ in + self?.deleteUserWithAPI { + self?.makeOKAlert(title: "", message: "나다를 이용해주셔서 감사합니다.\n다음에 또 뵈어요! 🥹") { _ in + self?.defaults.removeObject(forKey: Const.UserDefaultsKey.accessToken) +// self.defaults.removeObject(forKey: Const.UserDefaultsKey.refreshToken) + self?.defaults.removeObject(forKey: Const.UserDefaultsKey.darkModeState) + + let nextVC = UIStoryboard(name: Const.Storyboard.Name.login, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.loginViewController) + nextVC.modalPresentationStyle = .overFullScreen + self?.navigationController?.changeRootViewController(nextVC) } } }) @@ -203,11 +188,12 @@ extension MoreViewController: UITableViewDataSource { // MARK: - Network extension MoreViewController { - func deleteUserWithAPI(token: String) { - UserAPI.shared.userDelete(token: token) { response in + func deleteUserWithAPI(completion: @escaping () -> Void) { + UserAPI.shared.userDelete { response in switch response { case .success: print("deleteUserWithAPI - success") + completion() case .requestErr(let message): print("deleteUserWithAPI - requestErr: \(message)") case .pathErr: @@ -220,11 +206,12 @@ extension MoreViewController { } } - func groupResetWithAPI(token: String) { - GroupAPI.shared.groupReset(token: token) { response in + func groupResetWithAPI(completion: @escaping () -> Void) { + GroupAPI.shared.groupReset { response in switch response { case .success: print("groupResetWithAPI - success") + completion() case .requestErr(let message): print("groupResetWithAPI - requestErr: \(message)") case .pathErr: @@ -236,22 +223,4 @@ extension MoreViewController { } } } - - func logoutUserWithAPI(token: String) { - UserAPI.shared.userLogout(token: token) { response in - switch response { - case .success: - print("logoutUserWithAPI - success") - case .requestErr(let message): - print("logoutUserWithAPI - requestErr: \(message)") - case .pathErr: - print("logoutUserWithAPI - pathErr") - case .serverErr: - print("logoutUserWithAPI - serverErr") - case .networkFail: - print("logoutUserWithAPI - networkFail") - } - } - } - } diff --git a/NADA-iOS-forRelease/Sources/ViewControllers/TabBar/TabBarViewController.swift b/NADA-iOS-forRelease/Sources/ViewControllers/TabBar/TabBarViewController.swift index 3af79a29..bfcf131d 100644 --- a/NADA-iOS-forRelease/Sources/ViewControllers/TabBar/TabBarViewController.swift +++ b/NADA-iOS-forRelease/Sources/ViewControllers/TabBar/TabBarViewController.swift @@ -5,6 +5,7 @@ // Created by 민 on 2021/11/18. // +import MessageUI import UIKit class TabBarViewController: UITabBarController { @@ -31,6 +32,7 @@ class TabBarViewController: UITabBarController { tabBar.addSubview(borderLineView) setupLayout() + setNotification() } // 레이아웃 세팅 @@ -43,5 +45,117 @@ class TabBarViewController: UITabBarController { borderLineView.heightAnchor.constraint(equalToConstant: 1) ]) } +} + +// MARK: - Methods + +extension TabBarViewController { + private func setNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(presentMail), name: .presentMail, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(presentCardDetailVC), name: .presentDynamicLink, object: nil) + } + + private func presentToCardDetailVC(cardDataModel: Card) { + let cardDetailVC = ModuleFactory.shared.makeCardDetailVC() + cardDetailVC.status = .add + cardDetailVC.cardDataModel = cardDataModel + self.present(cardDetailVC, animated: true) + } + + private func checkDynamicLink(_ dynamicLinkCardUUID: String) { + self.cardDetailFetchWithAPI(cardUUID: dynamicLinkCardUUID) { [weak self] cardDataModel in + self?.cardAddInGroupWithAPI(cardUUID: dynamicLinkCardUUID) { [weak self] in + self?.presentToCardDetailVC(cardDataModel: cardDataModel) + } + } + } + + // MARK: - @objc Methods + + @objc + private func presentMail(_ notification: Notification) { + if MFMailComposeViewController.canSendMail() { + let mailComposeVC = MFMailComposeViewController() + mailComposeVC.mailComposeDelegate = self + + guard let mailAddress = notification.object as? String else { return } + mailComposeVC.setToRecipients([mailAddress]) + + present(mailComposeVC, animated: true, completion: nil) + } else { + // 메일이 계정과 연동되지 않은 경우. + let mailErrorAlert = UIAlertController(title: "메일 전송 실패", message: "이메일 설정을 확인하고 다시 시도해주세요.", preferredStyle: .alert) + let confirmAction = UIAlertAction(title: "확인", style: .default) { _ in } + mailErrorAlert.addAction(confirmAction) + present(mailErrorAlert, animated: true, completion: nil) + } + } + @objc + private func presentCardDetailVC(_ notification: Notification) { + guard let cardUUID = notification.object as? String else { return } + checkDynamicLink(cardUUID) + } +} + +// MARK: - Network + +extension TabBarViewController { + private func cardDetailFetchWithAPI(cardUUID: String, completion: @escaping (Card) -> Void) { + CardAPI.shared.cardDetailFetch(cardUUID: cardUUID) { response in + switch response { + case .success(let data): + if let cardDataModel = data as? Card { + completion(cardDataModel) + } + print("cardDetailFetchWithAPI - success") + case .requestErr(let message): + print("cardDetailFetchWithAPI - requestErr", message) + case .pathErr: + print("cardDetailFetchWithAPI - pathErr") + case .serverErr: + print("cardDetailFetchWithAPI - serverErr") + case .networkFail: + print("deleteGroupWithAPI - networkFail") + } + } + } + private func cardAddInGroupWithAPI(cardUUID: String, completion: @escaping () -> Void) { + GroupAPI.shared.cardAddInGroup(cardRequest: CardAddInGroupRequest(cardUUID: cardUUID, cardGroupID: 1)) { response in + switch response { + case .success: + completion() + print("cardAddInGroupWithAPI - success") + case .requestErr(let message): + print("cardAddInGroupWithAPI - requestErr", message) + case .pathErr: + print("cardAddInGroupWithAPI - pathErr") + case .serverErr: + print("cardAddInGroupWithAPI - serverErr") + case .networkFail: + print("cardAddInGroupWithAPI - networkFail") + } + } + } +} + +// MARK: - MFMailComposeViewControllerDelegate + +extension TabBarViewController: MFMailComposeViewControllerDelegate { + func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { + switch result { + case .cancelled: + controller.dismiss(animated: true) { print("mailComposeController - cancelled.")} + case .saved: + controller.dismiss(animated: true) { print("mailComposeController - saved.")} + case .sent: + controller.dismiss(animated: true) { + self.showToast(message: "성공적으로 메일을 보냈어요!", font: .button02, view: "default") + } + case .failed: + controller.dismiss(animated: true) { print("mailComposeController - filed.")} + @unknown default: + return + } + } }