From 7bb1667100e3443796684327d5da6c4fe6279aa4 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 13:29:14 +0900 Subject: [PATCH 001/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Endpoint?= =?UTF-8?q?=EC=97=90=20Label=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/Endpoint.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index 76599bccc..6cadbac95 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -25,6 +25,7 @@ struct Endpoint { enum Path: String { case login = "/login" + case label = "/label" var pathString: String { return self.rawValue From d2782279b6e26e8a1d0cfe8b9bbf56ac6fc9b9c7 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 13:29:48 +0900 Subject: [PATCH 002/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20NetworkManager?= =?UTF-8?q?=EA=B0=80=20Networkable=20=EC=B1=84=ED=83=9D=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index 91fcab113..37ac50ef5 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -12,7 +12,7 @@ protocol Networkable { func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) } -class NetworkManager { +class NetworkManager: Networkable { private let httpHeaders: HTTPHeaders = ["Content-Type": "application/json", "Accept": "application/json"] func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) { From 6d77a6361e49f96940f3680a05e95b41408bd00f Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 13:30:05 +0900 Subject: [PATCH 003/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=98=A4?= =?UTF-8?q?=ED=86=A0=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/View/IssueList/IssueTableViewCell.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index 100122cc1..7fd593e3e 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -71,13 +71,13 @@ class IssueTableViewCell: UITableViewCell { labelDescription.snp.makeConstraints { label in label.top.equalTo(largeTitle.snp.bottom).offset(16) - label.leading.trailing.equalToSuperview().offset(16) + label.leading.trailing.equalToSuperview().inset(16) label.height.equalTo(22) } milestoneView.snp.makeConstraints { view in view.top.equalTo(labelDescription.snp.bottom).offset(16) - view.leading.trailing.equalTo(16) + view.leading.trailing.equalToSuperview().inset(16) view.height.equalTo(22) } From 9e04c71fba57e507acb214551a2621b432cdddc9 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 13:30:31 +0900 Subject: [PATCH 004/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=84=9C=EB=A1=9C=20=EB=B0=94=EB=80=8C?= =?UTF-8?q?=EC=97=88=EB=8D=98=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift b/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift index 821a3d6c6..cca74ad61 100644 --- a/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift +++ b/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift @@ -35,8 +35,8 @@ class LoginView: UIView { return button }() - let appleLoginButton = GitHubLoginButton() - let githubLoginButton = AppleLoginButton() + let appleLoginButton = AppleLoginButton() + let githubLoginButton = GitHubLoginButton() let textField = IDPasswordTextField() override init(frame: CGRect) { From 72a52e30c2e2626ef3a5fe487ce79fb4b3c6eb6c Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 13:31:06 +0900 Subject: [PATCH 005/104] =?UTF-8?q?feat:=20=E2=9C=A8=20LabelListViewModel?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 16 +++++++++++ .../ViewModel/LabelListViewModel.swift | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index aa1268579..38b567eed 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ B32FEACE267260E400BF37A1 /* AddIssueButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32FEACD267260E400BF37A1 /* AddIssueButton.swift */; }; B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997C266F8D0B0091A44A /* GitHubLoginButton.swift */; }; B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997E266F90710091A44A /* AppleLoginButton.swift */; }; + B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB57267840290060DC85 /* LabelListViewModel.swift */; }; + B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB5A26784AA50060DC85 /* LabelList.swift */; }; B3B559E1266E095E00901C55 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E0266E095E00901C55 /* AppDelegate.swift */; }; B3B559E3266E095E00901C55 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E2266E095E00901C55 /* SceneDelegate.swift */; }; B3B559E8266E095E00901C55 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3B559E6266E095E00901C55 /* Main.storyboard */; }; @@ -100,6 +102,8 @@ B32FEACD267260E400BF37A1 /* AddIssueButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIssueButton.swift; sourceTree = ""; }; B349997C266F8D0B0091A44A /* GitHubLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubLoginButton.swift; sourceTree = ""; }; B349997E266F90710091A44A /* AppleLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleLoginButton.swift; sourceTree = ""; }; + B3A5CB57267840290060DC85 /* LabelListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelListViewModel.swift; sourceTree = ""; }; + B3A5CB5A26784AA50060DC85 /* LabelList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelList.swift; sourceTree = ""; }; B3B559DD266E095E00901C55 /* issue-tracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "issue-tracker.app"; sourceTree = BUILT_PRODUCTS_DIR; }; B3B559E0266E095E00901C55 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B3B559E2266E095E00901C55 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -210,6 +214,14 @@ path = IssueList; sourceTree = ""; }; + B3A5CB59267840310060DC85 /* ViewModel */ = { + isa = PBXGroup; + children = ( + B3A5CB57267840290060DC85 /* LabelListViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; B3B559D4266E095E00901C55 = { isa = PBXGroup; children = ( @@ -238,6 +250,7 @@ B3B559EE266E096000901C55 /* Info.plist */, B3B559E6266E095E00901C55 /* Main.storyboard */, D0ADB68F266F0A2200E0762C /* Supporting Files */, + B3A5CB59267840310060DC85 /* ViewModel */, D0FD50E92671B0AD008C6031 /* Controller */, B3F527562670A0D5002B0812 /* Extension */, 49903DAC266F558700D2A6DD /* View */, @@ -270,6 +283,7 @@ isa = PBXGroup; children = ( B3D7D0C3267735CC000F02F4 /* IssueList.swift */, + B3A5CB5A26784AA50060DC85 /* LabelList.swift */, D03AF8F92677494F001C2CBF /* Comment.swift */, D03AF8F726774909001C2CBF /* MilestoneList.swift */, B3D7D0C826773AEA000F02F4 /* NewIssue.swift */, @@ -600,12 +614,14 @@ B3FBA7B726730B9A0006E5E6 /* IssueToolbar.swift in Sources */, D0FD509E26708DDE008C6031 /* LoginViewController.swift in Sources */, B32FEACC26724CF400BF37A1 /* IssueTableFooterView.swift in Sources */, + B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */, B3EADABE2675EFED0007C4B6 /* AddLabelButton.swift in Sources */, B32FEAC12671D9F600BF37A1 /* IssueListViewController.swift in Sources */, B32FEACA2671FD1600BF37A1 /* SelectBarButton.swift in Sources */, D0A88E0F26765325005877F6 /* UIKit+Extension.swift in Sources */, B3F5275C2670C0EF002B0812 /* PaddingLabel.swift in Sources */, D0FD50DC2671AA04008C6031 /* InputView.swift in Sources */, + B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */, B32FEAC32671DCB100BF37A1 /* IssueTableViewCell.swift in Sources */, 49903DAB266F558300D2A6DD /* LoginView.swift in Sources */, D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift new file mode 100644 index 000000000..bbb45146d --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift @@ -0,0 +1,27 @@ +// +// LabelViewModel.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/15. +// + +import Foundation +import RxSwift + +class LabelListViewModel { + let networkManager: NetworkManager + var labelList: Observable<[IssueLabel]>? = nil + + init(networkManager: NetworkManager, labelList: Observable<[IssueLabel]>? = nil) { + self.networkManager = networkManager + self.labelList = labelList + fetchLabelList() + } + + func fetchLabelList() { + networkManager.request(url: Endpoint(path: .label).url()!, decodableType: LabelList.self) { labelList in + self.labelList = Observable<[IssueLabel]>.of(labelList.labels) + } + } + +} From 885ef9d326868d6174e97818b37e741393ae0fd5 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 13:31:23 +0900 Subject: [PATCH 006/104] =?UTF-8?q?feat:=20=E2=9C=A8=20LabelList=20DTO=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/UserDTO/LabelList.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift new file mode 100644 index 000000000..708ff6c78 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift @@ -0,0 +1,12 @@ +// +// LabelList.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/15. +// + +import Foundation + +struct LabelList: Decodable { + let labels: [IssueLabel] +} From 8bdc263b295a5fc70e641d0941385b13b0592b40 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 13:32:07 +0900 Subject: [PATCH 007/104] =?UTF-8?q?feat:=20=E2=9C=A8=20ReactiveX=EB=A5=BC?= =?UTF-8?q?=20=EC=9D=B4=EC=9A=A9=ED=95=9C=20tableView=EC=9D=98=20dataSourc?= =?UTF-8?q?e=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/LabelViewController.swift | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 85fd2ee01..c39953ad2 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -6,27 +6,37 @@ // import UIKit - -struct Label { - let title: String - let description: String - let color: String -} +import RxSwift +import RxCocoa class LabelViewController: UIViewController { @IBOutlet weak var labelTableView: UITableView! + var labelListViewModel = LabelListViewModel(networkManager: NetworkManager()) var addLabelButton = AddLabelButton() - - let fakeData = [Label(title: "hidsfadsfsafasfdfadsf", description: "hello", color: "#B1CAE5"), Label(title: "wow", description: "amazing", color: "#DFCD85")] + var bag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() setNavigationBar() labelTableView.register(LabelTableViewCell.self, forCellReuseIdentifier: LabelTableViewCell.identifier) - labelTableView.dataSource = self addLabelButton.addTarget(self, action: #selector(addLabelButtonTapped), for: .touchUpInside) + fetchLabel() + bindTableView() + } + + func fetchLabel() { + labelListViewModel.fetchLabelList() + } + + func bindTableView() { + labelListViewModel.labelList?.bind(to: labelTableView.rx.items) { talbeView, index, element in + guard let cell = talbeView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell()} + cell.setLabelCell(title: element.title, description: element.description!, color: element.color) + return cell + } + .disposed(by: bag) } @objc func addLabelButtonTapped() { @@ -39,18 +49,3 @@ class LabelViewController: UIViewController { navigationItem.rightBarButtonItem = UIBarButtonItem(customView: addLabelButton) } } - -extension LabelViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return fakeData.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell() } - cell.setLabelCell(title: fakeData[indexPath.row].title, description: fakeData[indexPath.row].description, color: fakeData[indexPath.row].color) - - return cell - } - - -} From e9b4fd0984ea5b9b7331efede9594a70420a3752 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 17:18:51 +0900 Subject: [PATCH 008/104] =?UTF-8?q?feat:=20=E2=9C=A8=20AddLabelViewControl?= =?UTF-8?q?ler=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 29 +++------ .../Controller/AddLabelViewController.swift | 64 +++++++++++++++++++ ...swift => AddMilestoneViewController.swift} | 6 +- 3 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift rename iOS/issue-tracker/issue-tracker/Controller/{AddViewController.swift => AddMilestoneViewController.swift} (92%) diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 38b567eed..b88e8f686 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 491F08942679E5420081C5C5 /* AddLabelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491F08932679E5410081C5C5 /* AddLabelViewController.swift */; }; 49903DAB266F558300D2A6DD /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49903DAA266F558300D2A6DD /* LoginView.swift */; }; 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49903DAD266F588B00D2A6DD /* IDPasswordTextField.swift */; }; 8345AD7BD9CAFAD9A4F8374A /* Pods_issue_tracker_issue_trackerUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0166F39C919837292E32AEF /* Pods_issue_tracker_issue_trackerUITests.framework */; }; @@ -47,7 +48,6 @@ D03AF8FA2677494F001C2CBF /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F92677494F001C2CBF /* Comment.swift */; }; D0A88E0226732D21005877F6 /* NewIssueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E0126732D21005877F6 /* NewIssueViewController.swift */; }; D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */; }; - D0A88E072674D9F0005877F6 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = D0A88E062674D9F0005877F6 /* MarkdownKit */; }; D0A88E092674F888005877F6 /* IssueDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E082674F888005877F6 /* IssueDetailTableViewCell.swift */; }; D0A88E0D26762D51005877F6 /* ModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E0C26762D51005877F6 /* ModalViewController.swift */; }; D0A88E0F26765325005877F6 /* UIKit+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E0E26765325005877F6 /* UIKit+Extension.swift */; }; @@ -62,7 +62,7 @@ D0FD509E26708DDE008C6031 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E4266E095E00901C55 /* LoginViewController.swift */; }; D0FD50AA267096C0008C6031 /* MilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */; }; D0FD50BB26709F8B008C6031 /* MilestoneTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */; }; - D0FD50D4267192AF008C6031 /* AddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50D3267192AF008C6031 /* AddViewController.swift */; }; + D0FD50D4267192AF008C6031 /* AddMilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50D3267192AF008C6031 /* AddMilestoneViewController.swift */; }; D0FD50DC2671AA04008C6031 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50DB2671AA04008C6031 /* InputView.swift */; }; D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */; }; /* End PBXBuildFile section */ @@ -86,6 +86,7 @@ /* Begin PBXFileReference section */ 3D239EFE5DEB1D1C49840391 /* Pods-issue-trackerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-issue-trackerTests.release.xcconfig"; path = "Target Support Files/Pods-issue-trackerTests/Pods-issue-trackerTests.release.xcconfig"; sourceTree = ""; }; + 491F08932679E5410081C5C5 /* AddLabelViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLabelViewController.swift; sourceTree = ""; }; 49903DAA266F558300D2A6DD /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 49903DAD266F588B00D2A6DD /* IDPasswordTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IDPasswordTextField.swift; sourceTree = ""; }; 57FD79E124256E10A8CD0326 /* Pods_issue_trackerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_issue_trackerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -145,7 +146,7 @@ D0ADB6F7266F5BBE00E0762C /* OAuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthManager.swift; sourceTree = ""; }; D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewController.swift; sourceTree = ""; }; D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneTableViewCell.swift; sourceTree = ""; }; - D0FD50D3267192AF008C6031 /* AddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddViewController.swift; sourceTree = ""; }; + D0FD50D3267192AF008C6031 /* AddMilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMilestoneViewController.swift; sourceTree = ""; }; D0FD50DB2671AA04008C6031 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelTableViewCell.swift; sourceTree = ""; }; E7F242466FC866CAD16EFF69 /* Pods-issue-tracker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-issue-tracker.debug.xcconfig"; path = "Target Support Files/Pods-issue-tracker/Pods-issue-tracker.debug.xcconfig"; sourceTree = ""; }; @@ -161,7 +162,6 @@ 9F61736E94FA27116F3653A8 /* Pods_issue_tracker.framework in Frameworks */, D0ADB6D5266F4F3D00E0762C /* RxSwift in Frameworks */, D0ADB6CC266F4F1C00E0762C /* Alamofire in Frameworks */, - D0A88E072674D9F0005877F6 /* MarkdownKit in Frameworks */, D0ADB6D7266F4F4700E0762C /* SnapKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -342,7 +342,8 @@ B3B559E4266E095E00901C55 /* LoginViewController.swift */, B3F527502670968F002B0812 /* LabelViewController.swift */, D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */, - D0FD50D3267192AF008C6031 /* AddViewController.swift */, + D0FD50D3267192AF008C6031 /* AddMilestoneViewController.swift */, + 491F08932679E5410081C5C5 /* AddLabelViewController.swift */, D0A88E0126732D21005877F6 /* NewIssueViewController.swift */, D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */, D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */, @@ -414,7 +415,6 @@ D0ADB6D0266F4F3D00E0762C /* RxCocoa */, D0ADB6D4266F4F3D00E0762C /* RxSwift */, D0ADB6D6266F4F4700E0762C /* SnapKit */, - D0A88E062674D9F0005877F6 /* MarkdownKit */, ); productName = "issue-tracker"; productReference = B3B559DD266E095E00901C55 /* issue-tracker.app */; @@ -493,7 +493,6 @@ B3B55A10266E0C8600901C55 /* XCRemoteSwiftPackageReference "Alamofire" */, B3B55A13266E0D5600901C55 /* XCRemoteSwiftPackageReference "RxSwift" */, B3F50EE7266E12340009C260 /* XCRemoteSwiftPackageReference "SnapKit" */, - D0A88E052674D9F0005877F6 /* XCRemoteSwiftPackageReference "MarkdownKit" */, ); productRefGroup = B3B559DE266E095E00901C55 /* Products */; projectDirPath = ""; @@ -616,6 +615,7 @@ B32FEACC26724CF400BF37A1 /* IssueTableFooterView.swift in Sources */, B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */, B3EADABE2675EFED0007C4B6 /* AddLabelButton.swift in Sources */, + 491F08942679E5420081C5C5 /* AddLabelViewController.swift in Sources */, B32FEAC12671D9F600BF37A1 /* IssueListViewController.swift in Sources */, B32FEACA2671FD1600BF37A1 /* SelectBarButton.swift in Sources */, D0A88E0F26765325005877F6 /* UIKit+Extension.swift in Sources */, @@ -625,7 +625,7 @@ B32FEAC32671DCB100BF37A1 /* IssueTableViewCell.swift in Sources */, 49903DAB266F558300D2A6DD /* LoginView.swift in Sources */, D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */, - D0FD50D4267192AF008C6031 /* AddViewController.swift in Sources */, + D0FD50D4267192AF008C6031 /* AddMilestoneViewController.swift in Sources */, B32FEACE267260E400BF37A1 /* AddIssueButton.swift in Sources */, 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */, @@ -1016,22 +1016,9 @@ minimumVersion = 5.0.1; }; }; - D0A88E052674D9F0005877F6 /* XCRemoteSwiftPackageReference "MarkdownKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/bmoliveira/MarkdownKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.7.1; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - D0A88E062674D9F0005877F6 /* MarkdownKit */ = { - isa = XCSwiftPackageProductDependency; - package = D0A88E052674D9F0005877F6 /* XCRemoteSwiftPackageReference "MarkdownKit" */; - productName = MarkdownKit; - }; D0ADB6CB266F4F1C00E0762C /* Alamofire */ = { isa = XCSwiftPackageProductDependency; package = B3B55A10266E0C8600901C55 /* XCRemoteSwiftPackageReference "Alamofire" */; diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift new file mode 100644 index 000000000..23b05e21f --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -0,0 +1,64 @@ +// +// AddViewController.swift +// issue-tracker +// +// Created by Ador on 2021/06/10. +// + +import UIKit + +class AddLabelViewController: UIViewController { + private let cellReuseIdentifier = "AddLabelViewControllerCell" + private lazy var tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .grouped) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) + return tableView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.title = "새로운 레이블" + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", + style: .plain, + target: self, + action: nil) + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", + style: .plain, + target: self, + action: nil) + + tableView.dataSource = self + view.addSubview(tableView) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + tableView.frame = view.bounds + } +} + +extension AddLabelViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 3 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) + let textField = UITextField(frame: CGRect(x: cell.frame.origin.x + 120, y: cell.frame.origin.y, width: cell.frame.width - 140, height: 44)) + cell.contentView.addSubview(textField) + switch indexPath.row { + case 0: + cell.textLabel?.text = "제목" + return cell + case 1: + cell.textLabel?.text = "설명" + return cell + case 2: + cell.textLabel?.text = "배경색" + return cell + default: + fatalError() + } + } +} diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift similarity index 92% rename from iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift rename to iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index b4ea9902e..6dd0aa886 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -7,8 +7,8 @@ import UIKit -class AddViewController: UIViewController { - private let cellReuseIdentifier = "AddViewControllerCell" +class AddMilestoneViewController: UIViewController { + private let cellReuseIdentifier = "AddMilestoneViewControllerCell" private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) @@ -38,7 +38,7 @@ class AddViewController: UIViewController { } } -extension AddViewController: UITableViewDataSource { +extension AddMilestoneViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } From 319a00fe33d1535d2c1d80bff98517ce4cedfcbe Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 17:20:36 +0900 Subject: [PATCH 009/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Ovservable?= =?UTF-8?q?=EC=97=90=EC=84=9C=20BehaviorRelay=EB=A1=9C=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/ViewModel/LabelListViewModel.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift index bbb45146d..166adf185 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift @@ -7,20 +7,21 @@ import Foundation import RxSwift +import RxCocoa class LabelListViewModel { let networkManager: NetworkManager - var labelList: Observable<[IssueLabel]>? = nil + var labelList = BehaviorRelay<[IssueLabel]>(value: []) + var bag = DisposeBag() - init(networkManager: NetworkManager, labelList: Observable<[IssueLabel]>? = nil) { + init(networkManager: NetworkManager) { self.networkManager = networkManager - self.labelList = labelList fetchLabelList() } func fetchLabelList() { - networkManager.request(url: Endpoint(path: .label).url()!, decodableType: LabelList.self) { labelList in - self.labelList = Observable<[IssueLabel]>.of(labelList.labels) + networkManager.request(url: Endpoint(path: .label).url()!, decodableType: LabelList.self) { label in + self.labelList.accept(label.data) } } From 8a819b4b7eff56c0201af5b3c4ed23e48b81c149 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 17:20:55 +0900 Subject: [PATCH 010/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20DTO=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift index 708ff6c78..2e1640613 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/LabelList.swift @@ -8,5 +8,5 @@ import Foundation struct LabelList: Decodable { - let labels: [IssueLabel] + let data: [IssueLabel] } From ec5d0e3ea92b7b19e4265720581e06a591f51f0b Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 17:21:25 +0900 Subject: [PATCH 011/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Endpoint?= =?UTF-8?q?=EC=97=90=20host=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/Endpoint.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index 6cadbac95..f34865e50 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -9,7 +9,7 @@ import Foundation struct Endpoint { let scheme: String = "https" - let host: String = "" + let host: String = "77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io" let port: Int = 8080 var path: Path @@ -17,7 +17,7 @@ struct Endpoint { var urlComponents = URLComponents() urlComponents.scheme = scheme urlComponents.host = host - urlComponents.port = port +// urlComponents.port = port urlComponents.path = path.pathString urlComponents.queryItems = queryItems return urlComponents.url From a5a2c2bfd922535cbb6508be776b33bd5bce3130 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 17:22:01 +0900 Subject: [PATCH 012/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20AddViewControll?= =?UTF-8?q?er=20=EC=9D=B4=EB=A6=84=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Controller/MilestoneViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index df52dd8f9..db24325c7 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -33,7 +33,7 @@ class MilestoneViewController: UIViewController { } @objc func addMilestone() { - let nav = UINavigationController(rootViewController: AddViewController()) + let nav = UINavigationController(rootViewController: AddMilestoneViewController()) present(nav, animated: true, completion: nil) } } From 338d351d7988b64ac48196ee847813e575b310e6 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 17:22:33 +0900 Subject: [PATCH 013/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=98=B5?= =?UTF-8?q?=EC=85=94=EB=84=90=20=ED=83=80=EC=9E=85=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Controller/LabelViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index c39953ad2..76de58b7c 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -31,8 +31,8 @@ class LabelViewController: UIViewController { } func bindTableView() { - labelListViewModel.labelList?.bind(to: labelTableView.rx.items) { talbeView, index, element in - guard let cell = talbeView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell()} + labelListViewModel.labelList.bind(to: labelTableView.rx.items) { tableView, index, element in + guard let cell = tableView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell()} cell.setLabelCell(title: element.title, description: element.description!, color: element.color) return cell } From 524704cb4fdb72738f3267425854b39457db3025 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 16 Jun 2021 17:43:47 +0900 Subject: [PATCH 014/104] =?UTF-8?q?feat:=20=E2=9C=A8=20AddLabelViewModel?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=ED=99=94=EB=A9=B4=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/AddLabelViewController.swift | 1 + .../Controller/LabelViewController.swift | 4 +++- .../ViewModel/AddLabelViewModel.swift | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index 23b05e21f..bfafec9a5 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -9,6 +9,7 @@ import UIKit class AddLabelViewController: UIViewController { private let cellReuseIdentifier = "AddLabelViewControllerCell" + let addLabelViewModel = AddLabelViewModel() private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 76de58b7c..7f31c3fba 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -40,7 +40,9 @@ class LabelViewController: UIViewController { } @objc func addLabelButtonTapped() { - + let vc = AddLabelViewController().add + let nv = UINavigationController(rootViewController: vc) + present(nv, animated: true, completion: nil) } func setNavigationBar() { diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift new file mode 100644 index 000000000..fd61c3456 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift @@ -0,0 +1,16 @@ +// +// AddLabelViewModel.swift +// issue-tracker +// +// Created by zeke on 2021/06/16. +// + +import Foundation + +class AddLabelViewModel { + var color: String = "" + + func setColor(color: String) { + self.color = color + } +} From 4881b0d65ccd571047a226ff847d126c0b05c60f Mon Sep 17 00:00:00 2001 From: lena Date: Wed, 16 Jun 2021 22:47:03 +0900 Subject: [PATCH 015/104] feat: add milestone view model, binding (#49) --- .../issue-tracker.xcodeproj/project.pbxproj | 4 ++ .../Controller/MilestoneViewController.swift | 37 +++++++++++++++---- .../issue-tracker/MilestoneViewModel.swift | 21 +++++++++++ .../Model/UserDTO/MilestoneList.swift | 3 +- .../Milestone/MilestoneTableViewCell.swift | 17 ++++++--- 5 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index aa1268579..85f330bfb 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ D03AF8F626774707001C2CBF /* UserList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F526774707001C2CBF /* UserList.swift */; }; D03AF8F826774909001C2CBF /* MilestoneList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F726774909001C2CBF /* MilestoneList.swift */; }; D03AF8FA2677494F001C2CBF /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F92677494F001C2CBF /* Comment.swift */; }; + D04C694E267A35960056DD79 /* MilestoneViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C694D267A35960056DD79 /* MilestoneViewModel.swift */; }; D0A88E0226732D21005877F6 /* NewIssueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E0126732D21005877F6 /* NewIssueViewController.swift */; }; D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */; }; D0A88E072674D9F0005877F6 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = D0A88E062674D9F0005877F6 /* MarkdownKit */; }; @@ -130,6 +131,7 @@ D03AF8F526774707001C2CBF /* UserList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserList.swift; sourceTree = ""; }; D03AF8F726774909001C2CBF /* MilestoneList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneList.swift; sourceTree = ""; }; D03AF8F92677494F001C2CBF /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; + D04C694D267A35960056DD79 /* MilestoneViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewModel.swift; sourceTree = ""; }; D0A88E0126732D21005877F6 /* NewIssueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssueViewController.swift; sourceTree = ""; }; D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailViewController.swift; sourceTree = ""; }; D0A88E082674F888005877F6 /* IssueDetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailTableViewCell.swift; sourceTree = ""; }; @@ -244,6 +246,7 @@ D0ADB69D266F0E7900E0762C /* Model */, D0A88E102676563E005877F6 /* Modal.storyboard */, D03AF8F126771FA3001C2CBF /* Login.storyboard */, + D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, ); path = "issue-tracker"; sourceTree = ""; @@ -618,6 +621,7 @@ B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */, D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */, B32FEAC82671F65F00BF37A1 /* FilterBarButton.swift in Sources */, + D04C694E267A35960056DD79 /* MilestoneViewModel.swift in Sources */, D0ADB6F8266F5BBE00E0762C /* OAuthManager.swift in Sources */, B3B559E1266E095E00901C55 /* AppDelegate.swift in Sources */, D0A88E0D26762D51005877F6 /* ModalViewController.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index df52dd8f9..fd00611df 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -6,9 +6,14 @@ // import UIKit +import RxSwift +import RxCocoa class MilestoneViewController: UIViewController { - + + private let viewModel = MilestoneViewModel() + private let disposeBag = DisposeBag() + private let tableView: UITableView = { let tableView = UITableView() tableView.rowHeight = 200 @@ -18,13 +23,16 @@ class MilestoneViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - tableView.dataSource = self + tableView.delegate = self view.addSubview(tableView) navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "마일스톤" navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addMilestone)) + + setupObserver() + setupCellConfiguration() } override func viewDidLayoutSubviews() { @@ -38,14 +46,27 @@ class MilestoneViewController: UIViewController { } } -extension MilestoneViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 3 +private extension MilestoneViewController { + func setupObserver() { + viewModel + .subject.asObservable() + .subscribe(onNext: { + [unowned self] milestones in + self.tableView.reloadData() + }) + .disposed(by: disposeBag) } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: MilestoneTableViewCell.reuseId, for: indexPath) - return cell + func setupCellConfiguration() { + viewModel + .subject + .bind(to: tableView + .rx + .items(cellIdentifier: MilestoneTableViewCell.reuseId, + cellType: MilestoneTableViewCell.self)) { row, milestone, cell in + cell.configure(with: milestone) + } + .disposed(by: disposeBag) } } diff --git a/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift new file mode 100644 index 000000000..1c74cacae --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift @@ -0,0 +1,21 @@ +// +// MilestoneViewModel.swift +// issue-tracker +// +// Created by Ador on 2021/06/16. +// + +import Foundation +import RxSwift + +class MilestoneViewModel { + var subject: Observable<[Milestone]> = Observable.just([]) + + init() { + let urlString = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/milestone" + let url = URL(string: urlString)! + NetworkManager().request(url: url, decodableType: MilestoneList.self) { [weak self] data in + self?.subject = Observable.just(data.milestone) + } + } +} diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift index 7b667151c..9b68275e3 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift @@ -7,7 +7,7 @@ import Foundation -struct MilestoneList { +struct MilestoneList: Codable { let milestone: [Milestone] } @@ -24,6 +24,5 @@ struct Milestone: Codable { case id, title, description, closedIssueCount, openedIssueCount case createdTime = "created_time" case dueDate = "due_date" - } } diff --git a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift index 48fe88455..03ba9b75b 100644 --- a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift @@ -41,7 +41,6 @@ class MilestoneTableViewCell: UITableViewCell { private let titleLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 22, weight: .bold) - label.text = "마일스톤 타이틀" label.textAlignment = .left return label }() @@ -57,7 +56,6 @@ class MilestoneTableViewCell: UITableViewCell { let label = UILabel() label.font = .systemFont(ofSize: 17) label.textColor = .systemGray - label.text = "마일스톤 설명" return label }() @@ -65,7 +63,6 @@ class MilestoneTableViewCell: UITableViewCell { let label = UILabel() label.font = .systemFont(ofSize: 17) label.textColor = .systemGray - label.text = "2021-06-21" return label }() @@ -75,7 +72,6 @@ class MilestoneTableViewCell: UITableViewCell { label.textColor = .white label.layer.masksToBounds = true label.layer.cornerRadius = 15 - label.text = "Opend Issue 1개" label.backgroundColor = UIColor.hexStringToUIColor(hex: "#B1CAE5") return label }() @@ -86,7 +82,6 @@ class MilestoneTableViewCell: UITableViewCell { label.textColor = .white label.layer.masksToBounds = true label.layer.cornerRadius = 15 - label.text = "Closed Issue 1개" label.backgroundColor = UIColor.hexStringToUIColor(hex: "#DFCD85") return label }() @@ -116,4 +111,16 @@ class MilestoneTableViewCell: UITableViewCell { maker.edges.equalToSuperview().inset(20) } } + + func configure(with milestone: Milestone) { + titleLabel.text = milestone.title + descriptionLabel.text = milestone.description + dueDateLabel.text = milestone.dueDate + if let open = milestone.openedIssueCount { + openedIssue.text = "열린 이슈 \(open)개" + } + if let close = milestone.closedIssueCount { + closedIssue.text = "닫힌 이슈 \(close)개" + } + } } From b3682b6e88f7613918528b066a345a9adcfe24eb Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 17 Jun 2021 11:20:28 +0900 Subject: [PATCH 016/104] feat: add new milestone view model using rx (#49) --- .../issue-tracker.xcodeproj/project.pbxproj | 4 ++ .../issue-tracker/AddTableViewCell.swift | 27 ++++++++ .../Controller/AddViewController.swift | 64 +++++++++++++------ .../issue-tracker/MilestoneViewModel.swift | 1 + 4 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/AddTableViewCell.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 85f330bfb..78490e42c 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ D03AF8F826774909001C2CBF /* MilestoneList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F726774909001C2CBF /* MilestoneList.swift */; }; D03AF8FA2677494F001C2CBF /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F92677494F001C2CBF /* Comment.swift */; }; D04C694E267A35960056DD79 /* MilestoneViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C694D267A35960056DD79 /* MilestoneViewModel.swift */; }; + D04C6950267A389D0056DD79 /* AddTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C694F267A389D0056DD79 /* AddTableViewCell.swift */; }; D0A88E0226732D21005877F6 /* NewIssueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E0126732D21005877F6 /* NewIssueViewController.swift */; }; D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */; }; D0A88E072674D9F0005877F6 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = D0A88E062674D9F0005877F6 /* MarkdownKit */; }; @@ -132,6 +133,7 @@ D03AF8F726774909001C2CBF /* MilestoneList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneList.swift; sourceTree = ""; }; D03AF8F92677494F001C2CBF /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; D04C694D267A35960056DD79 /* MilestoneViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewModel.swift; sourceTree = ""; }; + D04C694F267A389D0056DD79 /* AddTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTableViewCell.swift; sourceTree = ""; }; D0A88E0126732D21005877F6 /* NewIssueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssueViewController.swift; sourceTree = ""; }; D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailViewController.swift; sourceTree = ""; }; D0A88E082674F888005877F6 /* IssueDetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailTableViewCell.swift; sourceTree = ""; }; @@ -247,6 +249,7 @@ D0A88E102676563E005877F6 /* Modal.storyboard */, D03AF8F126771FA3001C2CBF /* Login.storyboard */, D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, + D04C694F267A389D0056DD79 /* AddTableViewCell.swift */, ); path = "issue-tracker"; sourceTree = ""; @@ -617,6 +620,7 @@ 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */, B3F527552670A0CD002B0812 /* UIColor+HexInit.swift in Sources */, + D04C6950267A389D0056DD79 /* AddTableViewCell.swift in Sources */, D03AF8F626774707001C2CBF /* UserList.swift in Sources */, B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */, D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift new file mode 100644 index 000000000..f9b090901 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift @@ -0,0 +1,27 @@ +// +// AddTableViewCell.swift +// issue-tracker +// +// Created by Ador on 2021/06/16. +// + +import UIKit + +class AddTableViewCell: UITableViewCell { + + static let reuseIdentifier = "AddTableViewCell" + var textField = UITextField() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) + contentView.addSubview(textField) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func bind(_ completion: (UITextField) -> Void) { + completion(textField) + } +} diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift index b4ea9902e..aab3446ab 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift @@ -6,12 +6,16 @@ // import UIKit +import RxSwift class AddViewController: UIViewController { - private let cellReuseIdentifier = "AddViewControllerCell" + + private let disposeBag = DisposeBag() + private let viewModel: MilestoneViewModel! = MilestoneViewModel() + private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) - tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) + tableView.register(AddTableViewCell.self, forCellReuseIdentifier: AddTableViewCell.reuseIdentifier) return tableView }() @@ -26,15 +30,30 @@ class AddViewController: UIViewController { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", style: .plain, target: self, - action: nil) + action: #selector(didTapSave)) tableView.dataSource = self + tableView.frame = view.bounds + view.addSubview(tableView) } - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - tableView.frame = view.bounds + @objc + private func didTapSave() { + // 저장을 눌렀을 때 텍스트 필드의 값들을 post + let urlString = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/milestone" + let url = URL(string: urlString)! + + print(viewModel.model) + let new = Milestone(id: 1, title: viewModel.model["제목"]!, + description: viewModel.model["설명"], + createdTime: nil, + dueDate: viewModel.model["완료일"], + closedIssueCount: nil, + openedIssueCount: nil) + NetworkManager().postRequest(url: url, encodable: new) { milestone in + print(milestone) + } } } @@ -44,21 +63,26 @@ extension AddViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) - let textField = UITextField(frame: CGRect(x: cell.frame.origin.x + 120, y: cell.frame.origin.y, width: cell.frame.width - 140, height: 44)) - cell.contentView.addSubview(textField) - switch indexPath.row { - case 0: - cell.textLabel?.text = "제목" - return cell - case 1: - cell.textLabel?.text = "설명" - return cell - case 2: - cell.textLabel?.text = "완료일" - return cell - default: + guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.reuseIdentifier, for: indexPath) as? AddTableViewCell else { fatalError() } + if indexPath.row == 1 { + cell.becomeFirstResponder() + } + let textLabel = ["제목", "설명", "완료일"] + cell.textLabel?.text = textLabel[indexPath.row] + cell.bind { textField in + textField.rx.text + .orEmpty + .observe(on: MainScheduler.instance) + .distinctUntilChanged() + .debounce(.milliseconds(500), scheduler: MainScheduler.instance) + .subscribe(onNext: { text in + let key = textLabel[indexPath.row] + self.viewModel.model[key] = text + }) + .disposed(by: disposeBag) + } + return cell } } diff --git a/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift index 1c74cacae..d8136793f 100644 --- a/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift @@ -10,6 +10,7 @@ import RxSwift class MilestoneViewModel { var subject: Observable<[Milestone]> = Observable.just([]) + var model:[String: String] = ["제목": "", "설명": "", "완료일": ""] init() { let urlString = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/milestone" From c174b3df5ac3bdcca6ce5c79dfff53e7db0d43b9 Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 17 Jun 2021 15:14:32 +0900 Subject: [PATCH 017/104] chore: delete comment --- .../issue-tracker/Controller/AddViewController.swift | 3 +-- .../issue-tracker/Controller/MilestoneViewController.swift | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift index aab3446ab..a793b6b18 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift @@ -40,7 +40,6 @@ class AddViewController: UIViewController { @objc private func didTapSave() { - // 저장을 눌렀을 때 텍스트 필드의 값들을 post let urlString = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/milestone" let url = URL(string: urlString)! @@ -66,7 +65,7 @@ extension AddViewController: UITableViewDataSource { guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.reuseIdentifier, for: indexPath) as? AddTableViewCell else { fatalError() } - if indexPath.row == 1 { + if indexPath.row == 0 { cell.becomeFirstResponder() } let textLabel = ["제목", "설명", "완료일"] diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index fd00611df..b1b14b721 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -61,9 +61,9 @@ private extension MilestoneViewController { viewModel .subject .bind(to: tableView - .rx - .items(cellIdentifier: MilestoneTableViewCell.reuseId, - cellType: MilestoneTableViewCell.self)) { row, milestone, cell in + .rx + .items(cellIdentifier: MilestoneTableViewCell.reuseId, + cellType: MilestoneTableViewCell.self)) { row, milestone, cell in cell.configure(with: milestone) } .disposed(by: disposeBag) From ad35608e04bb7a64b5004d4f9fb3fbe474750d6a Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:26:24 +0900 Subject: [PATCH 018/104] =?UTF-8?q?feat:=20=E2=9C=A8=20EstimatedLabelView?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Label/EstimatedLabelView.swift | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift diff --git a/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift b/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift new file mode 100644 index 000000000..897e8f6d6 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift @@ -0,0 +1,48 @@ +// +// EstimatedLabelView.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/17. +// + +import UIKit +import SnapKit + +class EstimatedLabelView: UIView { + + var label: PaddingLabel = { + var label = PaddingLabel(withInsets: 0, 0, 10, 10) + label.text = "레이블" + label.layer.masksToBounds = true + label.layer.cornerRadius = 10 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(label) + self.layer.masksToBounds = true + self.layer.cornerRadius = 10 + self.backgroundColor = #colorLiteral(red: 0.9102189541, green: 0.9093225002, blue: 0.9310914278, alpha: 1) + configureLabelAutolayout() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + func configureLabelAutolayout() { + label.snp.makeConstraints { label in + label.centerX.centerY.equalToSuperview() + } + } + + func modifyLabelTitle(title: String) { + self.label.text = title + label.sizeToFit() + } + + func setupLabelColor(color: UIColor) { + self.label.backgroundColor = color + } +} From 6cda9bfcf0f740468c5809a16242c861f405e4a2 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:27:18 +0900 Subject: [PATCH 019/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Controller/LabelViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 7f31c3fba..056e0c415 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -40,7 +40,7 @@ class LabelViewController: UIViewController { } @objc func addLabelButtonTapped() { - let vc = AddLabelViewController().add + let vc = AddLabelViewController() let nv = UINavigationController(rootViewController: vc) present(nv, animated: true, completion: nil) } From c9f901c729927d34537aa98f2b43e934fd24cb7e Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:28:12 +0900 Subject: [PATCH 020/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20IssueLabel?= =?UTF-8?q?=EC=9D=98=20id=20=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD=20(#42?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift index 741bd2237..89b62dc78 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift @@ -54,7 +54,7 @@ struct Author: Codable { // MARK: - IssueLabel struct IssueLabel: Codable { - let id: Int + let id: Int? let title, color: String let fontColor: String? let description: String? From a7093d142b9888b5a18bdf85c6753a70c1d35181 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:29:00 +0900 Subject: [PATCH 021/104] =?UTF-8?q?feat:=20=E2=9C=A8=20NetworkManager?= =?UTF-8?q?=EC=9D=98=20post=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/NetworkManager.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index 37ac50ef5..a906f7f56 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -10,13 +10,14 @@ import Alamofire protocol Networkable { func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) + func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) } class NetworkManager: Networkable { private let httpHeaders: HTTPHeaders = ["Content-Type": "application/json", "Accept": "application/json"] func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) { - AF.request(url, method: .get, encoding: URLEncoding.default, headers: httpHeaders) + AF.request(url, method: .get, headers: httpHeaders) .validate(statusCode: 200..<300) .responseDecodable(of: decodableType) { (response) in switch response.result { @@ -27,6 +28,19 @@ class NetworkManager: Networkable { } } } + + func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) { + AF.request(url, method: .post, parameters: encodable, encoder: JSONParameterEncoder.default, headers: httpHeaders) + .validate(statusCode: 200..<300) + .responseData { response in + switch response.result { + case .success : + completion() + case .failure(let error): + print(error) + } + } + } } From 608bc6a566b1ea3ca06a85c9ea8a0f3780cddbca Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:29:41 +0900 Subject: [PATCH 022/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20MarkdownKit=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EC=B6=94=EA=B0=80=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/swiftpm/Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved index 88324d757..779a725f9 100644 --- a/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -12,7 +12,7 @@ }, { "package": "MarkdownKit", - "repositoryURL": "https://github.com/bmoliveira/MarkdownKit.git", + "repositoryURL": "https://github.com/bmoliveira/MarkdownKit", "state": { "branch": null, "revision": "5056f3305d3499f44d8815530d560b87082e0cf5", From 0245345ca537c0b5d32c9f845e89be38f53ce5bd Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:31:23 +0900 Subject: [PATCH 023/104] =?UTF-8?q?feat:=20=E2=9C=A8=20convertHexToString,?= =?UTF-8?q?=20getRandomColor=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extension/UIColor+HexInit.swift | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift b/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift index 92a021f07..0bea25fcd 100644 --- a/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift +++ b/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift @@ -29,4 +29,29 @@ extension UIColor { alpha: CGFloat(1.0) ) } + + func convertHexToString() -> String { + var r:CGFloat = 0 + var g:CGFloat = 0 + var b:CGFloat = 0 + var a:CGFloat = 0 + + getRed(&r, green: &g, blue: &b, alpha: &a) + + let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 + + return NSString(format:"#%06x", rgb) as String + } + + static func getRandomColor() -> UIColor{ + + let randomRed:CGFloat = CGFloat(drand48()) + + let randomGreen:CGFloat = CGFloat(drand48()) + + let randomBlue:CGFloat = CGFloat(drand48()) + + return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0) + + } } From 3c9b4048ab7d777b31d6cafc3e3dc3a19f57450f Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:36:06 +0900 Subject: [PATCH 024/104] =?UTF-8?q?feat:=20=E2=9C=A8=20AddTableViewCell=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Label/AddTableViewCell.swift | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift diff --git a/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift new file mode 100644 index 000000000..83d4e25b5 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift @@ -0,0 +1,40 @@ +// +// AddTableViewCell.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/16. +// + +import UIKit +import RxSwift + +class AddTableViewCell: UITableViewCell { + + static let identifier = "AddTableViewCell" + + let textField = UITextField() + let reloadButton: UIButton = { + var button = UIButton() + button.setBackgroundImage(UIImage(systemName: "gobackward"), for: .normal) + return button + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) + contentView.addSubview(textField) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configureTextFieldPlaceHolder(text: String) { + self.textField.placeholder = text + } + + func configureBackgroundCellMode() { + self.textField.text = "#3DDCFF" + self.textField.rightView = reloadButton + self.textField.rightViewMode = .always + } +} From ee51a3e4f68ee97a6d056fa61ac47aea67f7d24d Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:36:36 +0900 Subject: [PATCH 025/104] =?UTF-8?q?feat:=20=E2=9C=A8=20AddLabelViewModel?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewModel/AddLabelViewModel.swift | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift index fd61c3456..8bdad77bb 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift @@ -6,11 +6,25 @@ // import Foundation +import RxSwift +import RxCocoa class AddLabelViewModel { - var color: String = "" - func setColor(color: String) { - self.color = color + var title = BehaviorRelay(value: "") + var description = BehaviorRelay(value: "") + var color = BehaviorRelay(value: "3DDCFF") + var fontColor = "#FFFFFF" + + + var networkManager: Networkable + + init(networkManager: Networkable) { + self.networkManager = networkManager + } + + func postAddedLabel(completion: @escaping () -> Void) { + let encodableLabel = IssueLabel(id: nil, title: self.title.value, color: self.color.value, fontColor: self.fontColor, description: self.description.value) + networkManager.postRequest(url: Endpoint(path: .label).url()!, encodable: encodableLabel, completion: completion) } } From 72ddb630a75ae20cfde7a08373c3fedb2b570404 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 21:38:11 +0900 Subject: [PATCH 026/104] =?UTF-8?q?feat:=20=E2=9C=A8=20AddLabelViewControl?= =?UTF-8?q?ler=EC=9D=98=20=EC=98=A4=ED=86=A0=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83,=20Rx=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=9C=20bind?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 29 +++++++ .../Controller/AddLabelViewController.swift | 83 +++++++++++++++---- 2 files changed, 98 insertions(+), 14 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index b88e8f686..aec2d9712 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ B32FEACE267260E400BF37A1 /* AddIssueButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B32FEACD267260E400BF37A1 /* AddIssueButton.swift */; }; B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997C266F8D0B0091A44A /* GitHubLoginButton.swift */; }; B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997E266F90710091A44A /* AppleLoginButton.swift */; }; + B34FF73E267AE9D8002D9C56 /* EstimatedLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */; }; B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB57267840290060DC85 /* LabelListViewModel.swift */; }; B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB5A26784AA50060DC85 /* LabelList.swift */; }; B3B559E1266E095E00901C55 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E0266E095E00901C55 /* AppDelegate.swift */; }; @@ -33,6 +34,9 @@ B3B55A03266E096000901C55 /* issue_trackerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B55A02266E096000901C55 /* issue_trackerUITests.swift */; }; B3D7D0C4267735CC000F02F4 /* IssueList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D7D0C3267735CC000F02F4 /* IssueList.swift */; }; B3D7D0C926773AEA000F02F4 /* NewIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D7D0C826773AEA000F02F4 /* NewIssue.swift */; }; + B3E01193267A0B44001155D4 /* AddLabelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */; }; + B3E01196267A28B2001155D4 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = B3E01195267A28B2001155D4 /* MarkdownKit */; }; + B3E01198267A29E8001155D4 /* AddTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E01197267A29E8001155D4 /* AddTableViewCell.swift */; }; B3EADABE2675EFED0007C4B6 /* AddLabelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EADABD2675EFED0007C4B6 /* AddLabelButton.swift */; }; B3EADAC0267628FB0007C4B6 /* LabelsCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EADABF267628FB0007C4B6 /* LabelsCollectionView.swift */; }; B3EADAC2267629190007C4B6 /* LabelsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EADAC1267629190007C4B6 /* LabelsCollectionViewCell.swift */; }; @@ -103,6 +107,7 @@ B32FEACD267260E400BF37A1 /* AddIssueButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIssueButton.swift; sourceTree = ""; }; B349997C266F8D0B0091A44A /* GitHubLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubLoginButton.swift; sourceTree = ""; }; B349997E266F90710091A44A /* AppleLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleLoginButton.swift; sourceTree = ""; }; + B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EstimatedLabelView.swift; sourceTree = ""; }; B3A5CB57267840290060DC85 /* LabelListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelListViewModel.swift; sourceTree = ""; }; B3A5CB5A26784AA50060DC85 /* LabelList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelList.swift; sourceTree = ""; }; B3B559DD266E095E00901C55 /* issue-tracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "issue-tracker.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -121,6 +126,8 @@ B3B55A04266E096000901C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B3D7D0C3267735CC000F02F4 /* IssueList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueList.swift; sourceTree = ""; }; B3D7D0C826773AEA000F02F4 /* NewIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssue.swift; sourceTree = ""; }; + B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLabelViewModel.swift; sourceTree = ""; }; + B3E01197267A29E8001155D4 /* AddTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTableViewCell.swift; sourceTree = ""; }; B3EADABD2675EFED0007C4B6 /* AddLabelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLabelButton.swift; sourceTree = ""; }; B3EADABF267628FB0007C4B6 /* LabelsCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsCollectionView.swift; sourceTree = ""; }; B3EADAC1267629190007C4B6 /* LabelsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; @@ -162,6 +169,7 @@ 9F61736E94FA27116F3653A8 /* Pods_issue_tracker.framework in Frameworks */, D0ADB6D5266F4F3D00E0762C /* RxSwift in Frameworks */, D0ADB6CC266F4F1C00E0762C /* Alamofire in Frameworks */, + B3E01196267A28B2001155D4 /* MarkdownKit in Frameworks */, D0ADB6D7266F4F4700E0762C /* SnapKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -218,6 +226,7 @@ isa = PBXGroup; children = ( B3A5CB57267840290060DC85 /* LabelListViewModel.swift */, + B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -366,6 +375,8 @@ D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */, B3EADABD2675EFED0007C4B6 /* AddLabelButton.swift */, B3F5275B2670C0EF002B0812 /* PaddingLabel.swift */, + B3E01197267A29E8001155D4 /* AddTableViewCell.swift */, + B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */, ); path = Label; sourceTree = ""; @@ -415,6 +426,7 @@ D0ADB6D0266F4F3D00E0762C /* RxCocoa */, D0ADB6D4266F4F3D00E0762C /* RxSwift */, D0ADB6D6266F4F4700E0762C /* SnapKit */, + B3E01195267A28B2001155D4 /* MarkdownKit */, ); productName = "issue-tracker"; productReference = B3B559DD266E095E00901C55 /* issue-tracker.app */; @@ -493,6 +505,7 @@ B3B55A10266E0C8600901C55 /* XCRemoteSwiftPackageReference "Alamofire" */, B3B55A13266E0D5600901C55 /* XCRemoteSwiftPackageReference "RxSwift" */, B3F50EE7266E12340009C260 /* XCRemoteSwiftPackageReference "SnapKit" */, + B3E01194267A28B1001155D4 /* XCRemoteSwiftPackageReference "MarkdownKit" */, ); productRefGroup = B3B559DE266E095E00901C55 /* Products */; projectDirPath = ""; @@ -642,10 +655,13 @@ D0ADB699266F0C1300E0762C /* NetworkManager.swift in Sources */, B3FBA7B9267335C30006E5E6 /* CancelButton.swift in Sources */, D03AF8FA2677494F001C2CBF /* Comment.swift in Sources */, + B34FF73E267AE9D8002D9C56 /* EstimatedLabelView.swift in Sources */, D03AF8F42677253C001C2CBF /* IssueFilterViewController.swift in Sources */, D0ADB694266F0A5000E0762C /* Endpoint.swift in Sources */, B3D7D0C926773AEA000F02F4 /* NewIssue.swift in Sources */, B3B559E3266E095E00901C55 /* SceneDelegate.swift in Sources */, + B3E01193267A0B44001155D4 /* AddLabelViewModel.swift in Sources */, + B3E01198267A29E8001155D4 /* AddTableViewCell.swift in Sources */, B32FEAC62671DDA600BF37A1 /* MilestoneView.swift in Sources */, B3EADAC0267628FB0007C4B6 /* LabelsCollectionView.swift in Sources */, B3EADAC2267629190007C4B6 /* LabelsCollectionViewCell.swift in Sources */, @@ -1008,6 +1024,14 @@ minimumVersion = 6.2.0; }; }; + B3E01194267A28B1001155D4 /* XCRemoteSwiftPackageReference "MarkdownKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/bmoliveira/MarkdownKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.7.0; + }; + }; B3F50EE7266E12340009C260 /* XCRemoteSwiftPackageReference "SnapKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SnapKit/SnapKit.git"; @@ -1019,6 +1043,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + B3E01195267A28B2001155D4 /* MarkdownKit */ = { + isa = XCSwiftPackageProductDependency; + package = B3E01194267A28B1001155D4 /* XCRemoteSwiftPackageReference "MarkdownKit" */; + productName = MarkdownKit; + }; D0ADB6CB266F4F1C00E0762C /* Alamofire */ = { isa = XCSwiftPackageProductDependency; package = B3B55A10266E0C8600901C55 /* XCRemoteSwiftPackageReference "Alamofire" */; diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index bfafec9a5..bb9298b27 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -6,36 +6,68 @@ // import UIKit +import RxSwift +import RxCocoa +import SnapKit class AddLabelViewController: UIViewController { - private let cellReuseIdentifier = "AddLabelViewControllerCell" - let addLabelViewModel = AddLabelViewModel() + + var addLabelViewModel: AddLabelViewModel! private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) - tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) + tableView.allowsSelection = false + tableView.register(AddTableViewCell.self, forCellReuseIdentifier: AddTableViewCell.identifier) return tableView }() + var estimatedLabelView = EstimatedLabelView() + var bag = DisposeBag() + var saveButton = UIBarButtonItem(title: "저장", style: .plain, target: nil, action: nil) override func viewDidLoad() { super.viewDidLoad() - + self.view.backgroundColor = #colorLiteral(red: 0.9494308829, green: 0.9485411048, blue: 0.9703034759, alpha: 1) + self.addLabelViewModel = AddLabelViewModel(networkManager: NetworkManager()) + navigationItem.title = "새로운 레이블" self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", style: .plain, target: self, action: nil) - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", - style: .plain, - target: self, - action: nil) + self.navigationItem.rightBarButtonItem = saveButton tableView.dataSource = self view.addSubview(tableView) + view.addSubview(estimatedLabelView) + configureAutolayout() + binding() } - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - tableView.frame = view.bounds + func binding() { + addLabelViewModel.color + .map { UIColor.hexStringToUIColor(hex: $0)} + .bind(to: estimatedLabelView.label.rx.backgroundColor) + .disposed(by: bag) + + saveButton.rx.tap + .subscribe { [weak self] observer in + self?.addLabelViewModel.postAddedLabel(completion: { + self?.dismiss(animated: true, completion: nil) + }) + } + .disposed(by: bag) + } + + func configureAutolayout() { + tableView.snp.makeConstraints { view in + view.top.equalToSuperview().inset(40) + view.leading.trailing.bottom.equalToSuperview() + } + + estimatedLabelView.snp.makeConstraints { view in + view.leading.trailing.equalToSuperview().inset(16) + view.centerX.centerY.equalToSuperview() + view.height.equalTo(160) + } } } @@ -45,18 +77,41 @@ extension AddLabelViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) - let textField = UITextField(frame: CGRect(x: cell.frame.origin.x + 120, y: cell.frame.origin.y, width: cell.frame.width - 140, height: 44)) - cell.contentView.addSubview(textField) + guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.identifier, for: indexPath) as? AddTableViewCell else { return UITableViewCell() } switch indexPath.row { case 0: cell.textLabel?.text = "제목" + cell.configureTextFieldPlaceHolder(text: "(필수입력)") + cell.textField.rx.text + .orEmpty + .distinctUntilChanged() + .observe(on: MainScheduler.instance) + .bind(to: addLabelViewModel.title) + .disposed(by: bag) return cell case 1: cell.textLabel?.text = "설명" + cell.configureTextFieldPlaceHolder(text: "(선택사항)") + cell.textField.rx.text + .orEmpty + .distinctUntilChanged() + .observe(on: MainScheduler.instance) + .bind(to: addLabelViewModel.description) + .disposed(by: bag) return cell case 2: cell.textLabel?.text = "배경색" + + addLabelViewModel.color + .bind(to: cell.textField.rx.text) + .disposed(by: bag) + + cell.reloadButton.rx.tap + .map { UIColor.getRandomColor().convertHexToString() } + .bind(to: addLabelViewModel.color) + .disposed(by: bag) + + cell.configureBackgroundCellMode() return cell default: fatalError() From 6ba4117133210e3ce77e0c0ae9c0acfe59472982 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 22:15:26 +0900 Subject: [PATCH 027/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20AddMilestoneVie?= =?UTF-8?q?wController=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 4 -- .../AddMilestoneViewController.swift | 64 ------------------- 2 files changed, 68 deletions(-) delete mode 100644 iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index aec2d9712..f13a75612 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -66,7 +66,6 @@ D0FD509E26708DDE008C6031 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E4266E095E00901C55 /* LoginViewController.swift */; }; D0FD50AA267096C0008C6031 /* MilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */; }; D0FD50BB26709F8B008C6031 /* MilestoneTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */; }; - D0FD50D4267192AF008C6031 /* AddMilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50D3267192AF008C6031 /* AddMilestoneViewController.swift */; }; D0FD50DC2671AA04008C6031 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50DB2671AA04008C6031 /* InputView.swift */; }; D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */; }; /* End PBXBuildFile section */ @@ -153,7 +152,6 @@ D0ADB6F7266F5BBE00E0762C /* OAuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthManager.swift; sourceTree = ""; }; D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewController.swift; sourceTree = ""; }; D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneTableViewCell.swift; sourceTree = ""; }; - D0FD50D3267192AF008C6031 /* AddMilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMilestoneViewController.swift; sourceTree = ""; }; D0FD50DB2671AA04008C6031 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelTableViewCell.swift; sourceTree = ""; }; E7F242466FC866CAD16EFF69 /* Pods-issue-tracker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-issue-tracker.debug.xcconfig"; path = "Target Support Files/Pods-issue-tracker/Pods-issue-tracker.debug.xcconfig"; sourceTree = ""; }; @@ -351,7 +349,6 @@ B3B559E4266E095E00901C55 /* LoginViewController.swift */, B3F527502670968F002B0812 /* LabelViewController.swift */, D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */, - D0FD50D3267192AF008C6031 /* AddMilestoneViewController.swift */, 491F08932679E5410081C5C5 /* AddLabelViewController.swift */, D0A88E0126732D21005877F6 /* NewIssueViewController.swift */, D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */, @@ -638,7 +635,6 @@ B32FEAC32671DCB100BF37A1 /* IssueTableViewCell.swift in Sources */, 49903DAB266F558300D2A6DD /* LoginView.swift in Sources */, D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */, - D0FD50D4267192AF008C6031 /* AddMilestoneViewController.swift in Sources */, B32FEACE267260E400BF37A1 /* AddIssueButton.swift in Sources */, 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift deleted file mode 100644 index 6dd0aa886..000000000 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// AddViewController.swift -// issue-tracker -// -// Created by Ador on 2021/06/10. -// - -import UIKit - -class AddMilestoneViewController: UIViewController { - private let cellReuseIdentifier = "AddMilestoneViewControllerCell" - private lazy var tableView: UITableView = { - let tableView = UITableView(frame: .zero, style: .grouped) - tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) - return tableView - }() - - override func viewDidLoad() { - super.viewDidLoad() - - navigationItem.title = "새로운 마일스톤" - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", - style: .plain, - target: self, - action: nil) - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", - style: .plain, - target: self, - action: nil) - - tableView.dataSource = self - view.addSubview(tableView) - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - tableView.frame = view.bounds - } -} - -extension AddMilestoneViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 3 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) - let textField = UITextField(frame: CGRect(x: cell.frame.origin.x + 120, y: cell.frame.origin.y, width: cell.frame.width - 140, height: 44)) - cell.contentView.addSubview(textField) - switch indexPath.row { - case 0: - cell.textLabel?.text = "제목" - return cell - case 1: - cell.textLabel?.text = "설명" - return cell - case 2: - cell.textLabel?.text = "완료일" - return cell - default: - fatalError() - } - } -} From da27dee3c397f84983ac9c6ab855947843df7bbf Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 17 Jun 2021 22:20:21 +0900 Subject: [PATCH 028/104] chore: delete add view controller --- .../issue-tracker.xcodeproj/project.pbxproj | 8 +- .../Controller/AddViewController.swift | 87 ------------------- 2 files changed, 2 insertions(+), 93 deletions(-) delete mode 100644 iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 4b28c693e..edb38e0e7 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -43,9 +43,9 @@ D03AF8F626774707001C2CBF /* UserList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F526774707001C2CBF /* UserList.swift */; }; D03AF8F826774909001C2CBF /* MilestoneList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F726774909001C2CBF /* MilestoneList.swift */; }; D03AF8FA2677494F001C2CBF /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F92677494F001C2CBF /* Comment.swift */; }; - D04C6956267B2C560056DD79 /* ToolBarTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C6955267B2C560056DD79 /* ToolBarTextField.swift */; }; D04C694E267A35960056DD79 /* MilestoneViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C694D267A35960056DD79 /* MilestoneViewModel.swift */; }; D04C6950267A389D0056DD79 /* AddTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C694F267A389D0056DD79 /* AddTableViewCell.swift */; }; + D04C6956267B2C560056DD79 /* ToolBarTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C6955267B2C560056DD79 /* ToolBarTextField.swift */; }; D0A88E0226732D21005877F6 /* NewIssueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E0126732D21005877F6 /* NewIssueViewController.swift */; }; D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */; }; D0A88E072674D9F0005877F6 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = D0A88E062674D9F0005877F6 /* MarkdownKit */; }; @@ -63,7 +63,6 @@ D0FD509E26708DDE008C6031 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E4266E095E00901C55 /* LoginViewController.swift */; }; D0FD50AA267096C0008C6031 /* MilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */; }; D0FD50BB26709F8B008C6031 /* MilestoneTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */; }; - D0FD50D4267192AF008C6031 /* AddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50D3267192AF008C6031 /* AddViewController.swift */; }; D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */; }; /* End PBXBuildFile section */ @@ -132,9 +131,9 @@ D03AF8F526774707001C2CBF /* UserList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserList.swift; sourceTree = ""; }; D03AF8F726774909001C2CBF /* MilestoneList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneList.swift; sourceTree = ""; }; D03AF8F92677494F001C2CBF /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; - D04C6955267B2C560056DD79 /* ToolBarTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolBarTextField.swift; sourceTree = ""; }; D04C694D267A35960056DD79 /* MilestoneViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewModel.swift; sourceTree = ""; }; D04C694F267A389D0056DD79 /* AddTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTableViewCell.swift; sourceTree = ""; }; + D04C6955267B2C560056DD79 /* ToolBarTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolBarTextField.swift; sourceTree = ""; }; D0A88E0126732D21005877F6 /* NewIssueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssueViewController.swift; sourceTree = ""; }; D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailViewController.swift; sourceTree = ""; }; D0A88E082674F888005877F6 /* IssueDetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailTableViewCell.swift; sourceTree = ""; }; @@ -146,7 +145,6 @@ D0ADB6F7266F5BBE00E0762C /* OAuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthManager.swift; sourceTree = ""; }; D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewController.swift; sourceTree = ""; }; D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneTableViewCell.swift; sourceTree = ""; }; - D0FD50D3267192AF008C6031 /* AddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddViewController.swift; sourceTree = ""; }; D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelTableViewCell.swift; sourceTree = ""; }; E7F242466FC866CAD16EFF69 /* Pods-issue-tracker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-issue-tracker.debug.xcconfig"; path = "Target Support Files/Pods-issue-tracker/Pods-issue-tracker.debug.xcconfig"; sourceTree = ""; }; F0166F39C919837292E32AEF /* Pods_issue_tracker_issue_trackerUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_issue_tracker_issue_trackerUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -335,7 +333,6 @@ B3B559E4266E095E00901C55 /* LoginViewController.swift */, B3F527502670968F002B0812 /* LabelViewController.swift */, D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */, - D0FD50D3267192AF008C6031 /* AddViewController.swift */, D0A88E0126732D21005877F6 /* NewIssueViewController.swift */, D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */, D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */, @@ -614,7 +611,6 @@ B32FEAC32671DCB100BF37A1 /* IssueTableViewCell.swift in Sources */, 49903DAB266F558300D2A6DD /* LoginView.swift in Sources */, D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */, - D0FD50D4267192AF008C6031 /* AddViewController.swift in Sources */, D04C6956267B2C560056DD79 /* ToolBarTextField.swift in Sources */, B32FEACE267260E400BF37A1 /* AddIssueButton.swift in Sources */, 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift deleted file mode 100644 index a793b6b18..000000000 --- a/iOS/issue-tracker/issue-tracker/Controller/AddViewController.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// AddViewController.swift -// issue-tracker -// -// Created by Ador on 2021/06/10. -// - -import UIKit -import RxSwift - -class AddViewController: UIViewController { - - private let disposeBag = DisposeBag() - private let viewModel: MilestoneViewModel! = MilestoneViewModel() - - private lazy var tableView: UITableView = { - let tableView = UITableView(frame: .zero, style: .grouped) - tableView.register(AddTableViewCell.self, forCellReuseIdentifier: AddTableViewCell.reuseIdentifier) - return tableView - }() - - override func viewDidLoad() { - super.viewDidLoad() - - navigationItem.title = "새로운 마일스톤" - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", - style: .plain, - target: self, - action: nil) - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", - style: .plain, - target: self, - action: #selector(didTapSave)) - - tableView.dataSource = self - tableView.frame = view.bounds - - view.addSubview(tableView) - } - - @objc - private func didTapSave() { - let urlString = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/milestone" - let url = URL(string: urlString)! - - print(viewModel.model) - let new = Milestone(id: 1, title: viewModel.model["제목"]!, - description: viewModel.model["설명"], - createdTime: nil, - dueDate: viewModel.model["완료일"], - closedIssueCount: nil, - openedIssueCount: nil) - NetworkManager().postRequest(url: url, encodable: new) { milestone in - print(milestone) - } - } -} - -extension AddViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 3 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.reuseIdentifier, for: indexPath) as? AddTableViewCell else { - fatalError() - } - if indexPath.row == 0 { - cell.becomeFirstResponder() - } - let textLabel = ["제목", "설명", "완료일"] - cell.textLabel?.text = textLabel[indexPath.row] - cell.bind { textField in - textField.rx.text - .orEmpty - .observe(on: MainScheduler.instance) - .distinctUntilChanged() - .debounce(.milliseconds(500), scheduler: MainScheduler.instance) - .subscribe(onNext: { text in - let key = textLabel[indexPath.row] - self.viewModel.model[key] = text - }) - .disposed(by: disposeBag) - } - return cell - } -} From 8e75f81f271db11bca3d28795ae7c80c53584d44 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 17 Jun 2021 22:44:13 +0900 Subject: [PATCH 029/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=EB=82=9C=20=ED=8C=8C=EC=9D=BC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 17 ++++++-- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../issue-tracker/AddLabelTableViewCell.swift | 40 +++++++++++++++++++ .../Controller/AddLabelViewController.swift | 4 +- .../Controller/LabelViewController.swift | 15 +------ 5 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 9bb8b5c1e..4dad61d71 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997C266F8D0B0091A44A /* GitHubLoginButton.swift */; }; B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997E266F90710091A44A /* AppleLoginButton.swift */; }; B34FF73E267AE9D8002D9C56 /* EstimatedLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */; }; + B34FF741267B8800002D9C56 /* AddLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */; }; B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB57267840290060DC85 /* LabelListViewModel.swift */; }; B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB5A26784AA50060DC85 /* LabelList.swift */; }; B3B559E1266E095E00901C55 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E0266E095E00901C55 /* AppDelegate.swift */; }; @@ -36,7 +37,6 @@ B3D7D0C926773AEA000F02F4 /* NewIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D7D0C826773AEA000F02F4 /* NewIssue.swift */; }; B3E01193267A0B44001155D4 /* AddLabelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */; }; B3E01196267A28B2001155D4 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = B3E01195267A28B2001155D4 /* MarkdownKit */; }; - B3E01198267A29E8001155D4 /* AddTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E01197267A29E8001155D4 /* AddTableViewCell.swift */; }; B3EADABE2675EFED0007C4B6 /* AddLabelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EADABD2675EFED0007C4B6 /* AddLabelButton.swift */; }; B3EADAC0267628FB0007C4B6 /* LabelsCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EADABF267628FB0007C4B6 /* LabelsCollectionView.swift */; }; B3EADAC2267629190007C4B6 /* LabelsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3EADAC1267629190007C4B6 /* LabelsCollectionViewCell.swift */; }; @@ -69,7 +69,6 @@ D0FD509E26708DDE008C6031 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E4266E095E00901C55 /* LoginViewController.swift */; }; D0FD50AA267096C0008C6031 /* MilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */; }; D0FD50BB26709F8B008C6031 /* MilestoneTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */; }; - D0FD50DC2671AA04008C6031 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50DB2671AA04008C6031 /* InputView.swift */; }; D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */; }; /* End PBXBuildFile section */ @@ -110,6 +109,7 @@ B349997C266F8D0B0091A44A /* GitHubLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitHubLoginButton.swift; sourceTree = ""; }; B349997E266F90710091A44A /* AppleLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleLoginButton.swift; sourceTree = ""; }; B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EstimatedLabelView.swift; sourceTree = ""; }; + B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLabelTableViewCell.swift; sourceTree = ""; }; B3A5CB57267840290060DC85 /* LabelListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelListViewModel.swift; sourceTree = ""; }; B3A5CB5A26784AA50060DC85 /* LabelList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelList.swift; sourceTree = ""; }; B3B559DD266E095E00901C55 /* issue-tracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "issue-tracker.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -227,6 +227,14 @@ path = IssueList; sourceTree = ""; }; + B34FF73F267B83C2002D9C56 /* Recovered References */ = { + isa = PBXGroup; + children = ( + D0FD50DB2671AA04008C6031 /* InputView.swift */, + ); + name = "Recovered References"; + sourceTree = ""; + }; B3A5CB59267840310060DC85 /* ViewModel */ = { isa = PBXGroup; children = ( @@ -245,6 +253,7 @@ B3B559DE266E095E00901C55 /* Products */, DD720597B58AE4F3E0B9F4F7 /* Pods */, E53A20619409B79F9C593158 /* Frameworks */, + B34FF73F267B83C2002D9C56 /* Recovered References */, ); sourceTree = ""; }; @@ -273,6 +282,7 @@ D03AF8F126771FA3001C2CBF /* Login.storyboard */, D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, D04C694F267A389D0056DD79 /* AddTableViewCell.swift */, + B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */, ); path = "issue-tracker"; sourceTree = ""; @@ -638,13 +648,13 @@ B32FEACA2671FD1600BF37A1 /* SelectBarButton.swift in Sources */, D0A88E0F26765325005877F6 /* UIKit+Extension.swift in Sources */, B3F5275C2670C0EF002B0812 /* PaddingLabel.swift in Sources */, - D0FD50DC2671AA04008C6031 /* InputView.swift in Sources */, B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */, B32FEAC32671DCB100BF37A1 /* IssueTableViewCell.swift in Sources */, 49903DAB266F558300D2A6DD /* LoginView.swift in Sources */, D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */, D04C6956267B2C560056DD79 /* ToolBarTextField.swift in Sources */, B32FEACE267260E400BF37A1 /* AddIssueButton.swift in Sources */, + B34FF741267B8800002D9C56 /* AddLabelTableViewCell.swift in Sources */, 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */, B3F527552670A0CD002B0812 /* UIColor+HexInit.swift in Sources */, @@ -668,7 +678,6 @@ B3D7D0C926773AEA000F02F4 /* NewIssue.swift in Sources */, B3B559E3266E095E00901C55 /* SceneDelegate.swift in Sources */, B3E01193267A0B44001155D4 /* AddLabelViewModel.swift in Sources */, - B3E01198267A29E8001155D4 /* AddTableViewCell.swift in Sources */, B32FEAC62671DDA600BF37A1 /* MilestoneView.swift in Sources */, B3EADAC0267628FB0007C4B6 /* LabelsCollectionView.swift in Sources */, B3EADAC2267629190007C4B6 /* LabelsCollectionViewCell.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved index 88324d757..779a725f9 100644 --- a/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/iOS/issue-tracker/issue-tracker.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -12,7 +12,7 @@ }, { "package": "MarkdownKit", - "repositoryURL": "https://github.com/bmoliveira/MarkdownKit.git", + "repositoryURL": "https://github.com/bmoliveira/MarkdownKit", "state": { "branch": null, "revision": "5056f3305d3499f44d8815530d560b87082e0cf5", diff --git a/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift b/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift new file mode 100644 index 000000000..eb76784fc --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift @@ -0,0 +1,40 @@ +// +// AddLabelTableViewCell.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/17. +// + +import UIKit +import RxSwift + +class AddLabelTableViewCell: UITableViewCell { + + static let identifier = "AddLabelTableViewCell" + + let textField = UITextField() + let reloadButton: UIButton = { + var button = UIButton() + button.setBackgroundImage(UIImage(systemName: "gobackward"), for: .normal) + return button + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) + contentView.addSubview(textField) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configureTextFieldPlaceHolder(text: String) { + self.textField.placeholder = text + } + + func configureBackgroundCellMode() { + self.textField.text = "#3DDCFF" + self.textField.rightView = reloadButton + self.textField.rightViewMode = .always + } +} diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index bb9298b27..873d139ae 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -16,7 +16,7 @@ class AddLabelViewController: UIViewController { private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.allowsSelection = false - tableView.register(AddTableViewCell.self, forCellReuseIdentifier: AddTableViewCell.identifier) + tableView.register(AddLabelTableViewCell.self, forCellReuseIdentifier: AddLabelTableViewCell.identifier) return tableView }() var estimatedLabelView = EstimatedLabelView() @@ -77,7 +77,7 @@ extension AddLabelViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.identifier, for: indexPath) as? AddTableViewCell else { return UITableViewCell() } + guard let cell = tableView.dequeueReusableCell(withIdentifier: AddLabelTableViewCell.identifier, for: indexPath) as? AddLabelTableViewCell else { return UITableViewCell() } switch indexPath.row { case 0: cell.textLabel?.text = "제목" diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 2ed657397..61c688043 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -33,7 +33,7 @@ class LabelViewController: UIViewController { func bindTableView() { labelListViewModel.labelList.bind(to: labelTableView.rx.items) { tableView, index, element in guard let cell = tableView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell()} - cell.setLabelCell(title: element.title, description: element.description!, color: element.color) + cell.setupLabelCell(title: element.title, description: element.description!, color: element.color) return cell } .disposed(by: bag) @@ -51,16 +51,3 @@ class LabelViewController: UIViewController { navigationItem.rightBarButtonItem = UIBarButtonItem(customView: addLabelButton) } } - -extension LabelViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return fakeData.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell() } - cell.setupLabelCell(title: fakeData[indexPath.row].title, description: fakeData[indexPath.row].description, color: fakeData[indexPath.row].color) - - return cell - } -} From 22a736b771751db6e23b5242cf28a309a88ff0c3 Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 17 Jun 2021 22:49:47 +0900 Subject: [PATCH 030/104] chore: add AddMilestoneViewController --- .../issue-tracker.xcodeproj/project.pbxproj | 4 + .../AddMilestoneViewController.swift | 75 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 4dad61d71..12303de2e 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ B3F5275C2670C0EF002B0812 /* PaddingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3F5275B2670C0EF002B0812 /* PaddingLabel.swift */; }; B3FBA7B726730B9A0006E5E6 /* IssueToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FBA7B626730B9A0006E5E6 /* IssueToolbar.swift */; }; B3FBA7B9267335C30006E5E6 /* CancelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FBA7B8267335C30006E5E6 /* CancelButton.swift */; }; + D0103BDE267B897E0079FC3D /* AddMilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0103BDD267B897E0079FC3D /* AddMilestoneViewController.swift */; }; D03AF8F226771FA3001C2CBF /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D03AF8F126771FA3001C2CBF /* Login.storyboard */; }; D03AF8F42677253C001C2CBF /* IssueFilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */; }; D03AF8F626774707001C2CBF /* UserList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F526774707001C2CBF /* UserList.swift */; }; @@ -139,6 +140,7 @@ B3FBA7B626730B9A0006E5E6 /* IssueToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = IssueToolbar.swift; path = "issue-tracker/Controller/IssueToolbar.swift"; sourceTree = SOURCE_ROOT; }; B3FBA7B8267335C30006E5E6 /* CancelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelButton.swift; sourceTree = ""; }; CCF0F131FB8BE0BC0EF7FE4E /* Pods-issue-tracker.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-issue-tracker.release.xcconfig"; path = "Target Support Files/Pods-issue-tracker/Pods-issue-tracker.release.xcconfig"; sourceTree = ""; }; + D0103BDD267B897E0079FC3D /* AddMilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMilestoneViewController.swift; sourceTree = ""; }; D03AF8F126771FA3001C2CBF /* Login.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Login.storyboard; sourceTree = ""; }; D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueFilterViewController.swift; sourceTree = ""; }; D03AF8F526774707001C2CBF /* UserList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserList.swift; sourceTree = ""; }; @@ -372,6 +374,7 @@ D0A88E0126732D21005877F6 /* NewIssueViewController.swift */, D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */, D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */, + D0103BDD267B897E0079FC3D /* AddMilestoneViewController.swift */, ); path = Controller; sourceTree = ""; @@ -667,6 +670,7 @@ D0ADB6F8266F5BBE00E0762C /* OAuthManager.swift in Sources */, B3B559E1266E095E00901C55 /* AppDelegate.swift in Sources */, D0A88E0D26762D51005877F6 /* ModalViewController.swift in Sources */, + D0103BDE267B897E0079FC3D /* AddMilestoneViewController.swift in Sources */, D0A88E092674F888005877F6 /* IssueDetailTableViewCell.swift in Sources */, B3D7D0C4267735CC000F02F4 /* IssueList.swift in Sources */, D0ADB699266F0C1300E0762C /* NetworkManager.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift new file mode 100644 index 000000000..d9d96433b --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -0,0 +1,75 @@ +// +// AddMilestoneViewController.swift +// issue-tracker +// +// Created by Ador on 2021/06/17. +// + +import UIKit +import RxSwift + +class AddMilestoneViewController: UIViewController { + + private let disposeBag = DisposeBag() + private let viewModel: MilestoneViewModel! = MilestoneViewModel() + + private lazy var tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .grouped) + tableView.register(AddTableViewCell.self, forCellReuseIdentifier: AddTableViewCell.reuseIdentifier) + return tableView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.title = "새로운 마일스톤" + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", + style: .plain, + target: self, + action: nil) + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", + style: .plain, + target: self, + action: #selector(didTapSave)) + + tableView.dataSource = self + tableView.frame = view.bounds + + view.addSubview(tableView) + } + + @objc + private func didTapSave() { + + } +} + +extension AddMilestoneViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 3 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.reuseIdentifier, for: indexPath) as? AddTableViewCell else { + fatalError() + } + if indexPath.row == 0 { + cell.becomeFirstResponder() + } + let textLabel = ["제목", "설명", "완료일"] + cell.textLabel?.text = textLabel[indexPath.row] + cell.bind { textField in + textField.rx.text + .orEmpty + .observe(on: MainScheduler.instance) + .distinctUntilChanged() + .debounce(.milliseconds(500), scheduler: MainScheduler.instance) + .subscribe(onNext: { text in + let key = textLabel[indexPath.row] + self.viewModel.model[key] = text + }) + .disposed(by: disposeBag) + } + return cell + } +} From c3501f7ceb0b66765c613986c83f5f7fd43ea13e Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 11:20:13 +0900 Subject: [PATCH 031/104] =?UTF-8?q?feat:=20=E2=9C=A8=20configure=20SwiftLi?= =?UTF-8?q?nt=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/.swiftlint.yml | 6 +++++ .../issue-tracker.xcodeproj/project.pbxproj | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 iOS/issue-tracker/.swiftlint.yml diff --git a/iOS/issue-tracker/.swiftlint.yml b/iOS/issue-tracker/.swiftlint.yml new file mode 100644 index 000000000..3f37f4bfa --- /dev/null +++ b/iOS/issue-tracker/.swiftlint.yml @@ -0,0 +1,6 @@ +disabled_rules: +- line_length +included: +excluded: +- issue-trackerTests +- issue-trackerUITests diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 12303de2e..76f883d31 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ B3B559ED266E096000901C55 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3B559EB266E096000901C55 /* LaunchScreen.storyboard */; }; B3B559F8266E096000901C55 /* issue_trackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559F7266E096000901C55 /* issue_trackerTests.swift */; }; B3B55A03266E096000901C55 /* issue_trackerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B55A02266E096000901C55 /* issue_trackerUITests.swift */; }; + B3CBD19B267C359500F8D733 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = B3CBD19A267C359500F8D733 /* .swiftlint.yml */; }; B3D7D0C4267735CC000F02F4 /* IssueList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D7D0C3267735CC000F02F4 /* IssueList.swift */; }; B3D7D0C926773AEA000F02F4 /* NewIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D7D0C826773AEA000F02F4 /* NewIssue.swift */; }; B3E01193267A0B44001155D4 /* AddLabelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */; }; @@ -127,6 +128,7 @@ B3B559FE266E096000901C55 /* issue-trackerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "issue-trackerUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; B3B55A02266E096000901C55 /* issue_trackerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = issue_trackerUITests.swift; sourceTree = ""; }; B3B55A04266E096000901C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B3CBD19A267C359500F8D733 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; B3D7D0C3267735CC000F02F4 /* IssueList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueList.swift; sourceTree = ""; }; B3D7D0C826773AEA000F02F4 /* NewIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssue.swift; sourceTree = ""; }; B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLabelViewModel.swift; sourceTree = ""; }; @@ -249,6 +251,7 @@ B3B559D4266E095E00901C55 = { isa = PBXGroup; children = ( + B3CBD19A267C359500F8D733 /* .swiftlint.yml */, B3B559DF266E095E00901C55 /* issue-tracker */, B3B559F6266E096000901C55 /* issue-trackerTests */, B3B55A01266E096000901C55 /* issue-trackerUITests */, @@ -433,6 +436,7 @@ B3B559D9266E095E00901C55 /* Sources */, B3B559DA266E095E00901C55 /* Frameworks */, B3B559DB266E095E00901C55 /* Resources */, + B3CBD199267C34F200F8D733 /* ShellScript */, ); buildRules = ( ); @@ -541,6 +545,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + B3CBD19B267C359500F8D733 /* .swiftlint.yml in Resources */, D0A88E112676563E005877F6 /* Modal.storyboard in Resources */, B3B559ED266E096000901C55 /* LaunchScreen.storyboard in Resources */, B3B559EA266E096000901C55 /* Assets.xcassets in Resources */, @@ -610,6 +615,23 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + B3CBD199267C34F200F8D733 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${PODS_ROOT}/SwiftLint/swiftlint\n"; + }; F949EFDA4F0A02CAD51691D6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; From 938f2091d12f47bbaa8f83abbc579a070dcab70a Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 11:29:40 +0900 Subject: [PATCH 032/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20ViewModel?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/ViewModel/AddLabelViewModel.swift | 8 ++------ .../issue-tracker/ViewModel/LabelListViewModel.swift | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift index 8bdad77bb..fb5a15fde 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift @@ -10,21 +10,17 @@ import RxSwift import RxCocoa class AddLabelViewModel { - var title = BehaviorRelay(value: "") var description = BehaviorRelay(value: "") var color = BehaviorRelay(value: "3DDCFF") var fontColor = "#FFFFFF" - - var networkManager: Networkable - + init(networkManager: Networkable) { self.networkManager = networkManager } - func postAddedLabel(completion: @escaping () -> Void) { - let encodableLabel = IssueLabel(id: nil, title: self.title.value, color: self.color.value, fontColor: self.fontColor, description: self.description.value) + let encodableLabel = IssueLabel(id: nil, title: title.value, color: color.value, fontColor: fontColor, description: description.value) networkManager.postRequest(url: Endpoint(path: .label).url()!, encodable: encodableLabel, completion: completion) } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift index 166adf185..517f28878 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift @@ -13,12 +13,12 @@ class LabelListViewModel { let networkManager: NetworkManager var labelList = BehaviorRelay<[IssueLabel]>(value: []) var bag = DisposeBag() - + init(networkManager: NetworkManager) { self.networkManager = networkManager fetchLabelList() } - + func fetchLabelList() { networkManager.request(url: Endpoint(path: .label).url()!, decodableType: LabelList.self) { label in self.labelList.accept(label.data) From 0d212f2031b3dcfa3d43fd28f45e23870e164044 Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:06:52 +0900 Subject: [PATCH 033/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Controller?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/AddLabelViewController.swift | 23 +++++++------ .../AddMilestoneViewController.swift | 11 +++---- .../IssueDetailViewController.swift | 27 ++++++++-------- .../IssueFilterViewController.swift | 12 +++---- .../Controller/LabelViewController.swift | 16 +++++----- .../Controller/MilestoneViewController.swift | 24 +++++++------- .../Controller/ModalViewController.swift | 9 +++--- .../Controller/NewIssueViewController.swift | 29 ++++++++--------- .../View/IssueListViewController.swift | 32 +++++++++---------- .../ViewModel/AddLabelViewModel.swift | 1 + 10 files changed, 89 insertions(+), 95 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index 873d139ae..a656cd428 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -11,7 +11,6 @@ import RxCocoa import SnapKit class AddLabelViewController: UIViewController { - var addLabelViewModel: AddLabelViewModel! private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) @@ -27,42 +26,42 @@ class AddLabelViewController: UIViewController { super.viewDidLoad() self.view.backgroundColor = #colorLiteral(red: 0.9494308829, green: 0.9485411048, blue: 0.9703034759, alpha: 1) self.addLabelViewModel = AddLabelViewModel(networkManager: NetworkManager()) - + navigationItem.title = "새로운 레이블" self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", style: .plain, target: self, action: nil) self.navigationItem.rightBarButtonItem = saveButton - + tableView.dataSource = self view.addSubview(tableView) view.addSubview(estimatedLabelView) configureAutolayout() binding() } - + func binding() { addLabelViewModel.color .map { UIColor.hexStringToUIColor(hex: $0)} .bind(to: estimatedLabelView.label.rx.backgroundColor) .disposed(by: bag) - + saveButton.rx.tap - .subscribe { [weak self] observer in + .subscribe { [weak self] _ in self?.addLabelViewModel.postAddedLabel(completion: { self?.dismiss(animated: true, completion: nil) }) } .disposed(by: bag) } - + func configureAutolayout() { tableView.snp.makeConstraints { view in view.top.equalToSuperview().inset(40) view.leading.trailing.bottom.equalToSuperview() } - + estimatedLabelView.snp.makeConstraints { view in view.leading.trailing.equalToSuperview().inset(16) view.centerX.centerY.equalToSuperview() @@ -75,7 +74,7 @@ extension AddLabelViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: AddLabelTableViewCell.identifier, for: indexPath) as? AddLabelTableViewCell else { return UITableViewCell() } switch indexPath.row { @@ -101,16 +100,16 @@ extension AddLabelViewController: UITableViewDataSource { return cell case 2: cell.textLabel?.text = "배경색" - + addLabelViewModel.color .bind(to: cell.textField.rx.text) .disposed(by: bag) - + cell.reloadButton.rx.tap .map { UIColor.getRandomColor().convertHexToString() } .bind(to: addLabelViewModel.color) .disposed(by: bag) - + cell.configureBackgroundCellMode() return cell default: diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index d9d96433b..9575b21f8 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -12,7 +12,6 @@ class AddMilestoneViewController: UIViewController { private let disposeBag = DisposeBag() private let viewModel: MilestoneViewModel! = MilestoneViewModel() - private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.register(AddTableViewCell.self, forCellReuseIdentifier: AddTableViewCell.reuseIdentifier) @@ -31,16 +30,16 @@ class AddMilestoneViewController: UIViewController { style: .plain, target: self, action: #selector(didTapSave)) - + tableView.dataSource = self tableView.frame = view.bounds - + view.addSubview(tableView) } - + @objc private func didTapSave() { - + } } @@ -48,7 +47,7 @@ extension AddMilestoneViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 3 } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.reuseIdentifier, for: indexPath) as? AddTableViewCell else { fatalError() diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index b31d99d80..a52256755 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -9,10 +9,9 @@ import UIKit import SnapKit class IssueDetailViewController: UIViewController { - private let cellReuseIdentifier = "IssueDetailCell" private let data = [1, 2, 3, 4, 5, 6, 7, 8, 9] - + private let tableView: UITableView = { let tableView = UITableView() tableView.rowHeight = 130 @@ -23,16 +22,16 @@ class IssueDetailViewController: UIViewController { private let toolbar: UIToolbar = { let toolbar = UIToolbar(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 100))) let textField = ToolBarTextField(frame: toolbar.bounds) - let up = UIBarButtonItem(image: UIImage(systemName: "chevron.up.circle"), + let upButton = UIBarButtonItem(image: UIImage(systemName: "chevron.up.circle"), style: .plain, target: self, action: #selector(scrollToBefore)) - let down = UIBarButtonItem(image: UIImage(systemName: "chevron.down.circle"), + let downButton = UIBarButtonItem(image: UIImage(systemName: "chevron.down.circle"), style: .plain, target: self, action: #selector(scrollToNext)) let comment = UIBarButtonItem(customView: textField) let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: comment, action: nil) - toolbar.setItems([up, down, space, comment], animated: false) + toolbar.setItems([upButton, downButton, space, comment], animated: false) return toolbar }() - + override func viewDidLoad() { super.viewDidLoad() @@ -40,18 +39,18 @@ class IssueDetailViewController: UIViewController { navigationController?.navigationBar.prefersLargeTitles = true navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain, target: self, action: #selector(showIssueDetailInfo)) navigationItem.title = "테스트 이슈 #2" - + tableView.dataSource = self tableView.delegate = self tableView.frame = view.bounds tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .none) - + view.addSubview(tableView) view.addSubview(toolbar) - + setupAutolayout() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -64,7 +63,7 @@ class IssueDetailViewController: UIViewController { maker.height.equalTo(44) } } - + @objc private func scrollToBefore() { guard let indexPath = tableView.indexPathForSelectedRow, @@ -72,7 +71,7 @@ class IssueDetailViewController: UIViewController { tableView.selectRow(at: IndexPath(row: indexPath.row - 1, section: indexPath.section), animated: true, scrollPosition: .top) } - + @objc private func scrollToNext() { guard let indexPath = tableView.indexPathForSelectedRow, @@ -80,7 +79,7 @@ class IssueDetailViewController: UIViewController { tableView.selectRow(at: IndexPath(row: indexPath.row + 1, section: indexPath.section), animated: true, scrollPosition: .bottom) } - + @objc private func showIssueDetailInfo() { let storyboard = UIStoryboard(name: "Modal", bundle: nil) @@ -94,7 +93,7 @@ extension IssueDetailViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data.count } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) cell.accessoryView = UIImageView(image: UIImage(systemName: "ellipsis")) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift index 229fbb155..f8d7aa9df 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift @@ -36,10 +36,10 @@ class IssueFilterViewController: UIViewController { tableView.delegate = self tableView.dataSource = self tableView.frame = view.bounds - + view.addSubview(tableView) } - + @objc private func cancelButtonTapped() { dismiss(animated: true) @@ -59,12 +59,12 @@ extension IssueFilterViewController: UITableViewDelegate { return nil } } - + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selected = tableView.cellForRow(at: indexPath) selected?.accessoryType = .checkmark } - + func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { let selected = tableView.cellForRow(at: indexPath) selected?.accessoryType = .none @@ -75,7 +75,7 @@ extension IssueFilterViewController: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 3 } - + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: @@ -88,7 +88,7 @@ extension IssueFilterViewController: UITableViewDataSource { return 0 } } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) cell.selectionStyle = .none diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 61c688043..c454e2b7e 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -25,26 +25,26 @@ class LabelViewController: UIViewController { fetchLabel() bindTableView() } - + func fetchLabel() { labelListViewModel.fetchLabelList() } - + func bindTableView() { - labelListViewModel.labelList.bind(to: labelTableView.rx.items) { tableView, index, element in + labelListViewModel.labelList.bind(to: labelTableView.rx.items) { tableView, _, element in guard let cell = tableView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell()} cell.setupLabelCell(title: element.title, description: element.description!, color: element.color) return cell } .disposed(by: bag) } - + @objc func addLabelButtonTapped() { - let vc = AddLabelViewController() - let nv = UINavigationController(rootViewController: vc) - present(nv, animated: true, completion: nil) + let viewController = AddLabelViewController() + let navigationViewController = UINavigationController(rootViewController: viewController) + present(navigationViewController, animated: true, completion: nil) } - + func seuptNavigationBar() { navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "레이블" diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index bebdd10f0..81c7c60b8 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -10,10 +10,8 @@ import RxSwift import RxCocoa class MilestoneViewController: UIViewController { - private let viewModel = MilestoneViewModel() private let disposeBag = DisposeBag() - private let tableView: UITableView = { let tableView = UITableView() tableView.rowHeight = 200 @@ -23,19 +21,20 @@ class MilestoneViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - + tableView.delegate = self tableView.frame = view.bounds + view.addSubview(tableView) - + navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "마일스톤" navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addMilestone)) - + setupObserver() setupCellConfiguration() } - + @objc func addMilestone() { let nav = UINavigationController(rootViewController: AddMilestoneViewController()) present(nav, animated: true, completion: nil) @@ -46,20 +45,19 @@ private extension MilestoneViewController { func setupObserver() { viewModel .subject.asObservable() - .subscribe(onNext: { - [unowned self] milestones in + .subscribe(onNext: { [unowned self] _ in self.tableView.reloadData() }) .disposed(by: disposeBag) } - + func setupCellConfiguration() { viewModel .subject .bind(to: tableView .rx .items(cellIdentifier: MilestoneTableViewCell.reuseId, - cellType: MilestoneTableViewCell.self)) { row, milestone, cell in + cellType: MilestoneTableViewCell.self)) { _, milestone, cell in cell.configure(with: milestone) } .disposed(by: disposeBag) @@ -68,14 +66,14 @@ private extension MilestoneViewController { extension MilestoneViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - let delete = UIContextualAction(style: .destructive, title: "삭제", handler: { action, view, completion in + let delete = UIContextualAction(style: .destructive, title: "삭제", handler: { _, _, completion in completion(true) }) delete.image = UIImage(systemName: "trash") - let edit = UIContextualAction(style: .normal, title: "수정", handler: { action, view, completion in + let edit = UIContextualAction(style: .normal, title: "수정", handler: { _, _, completion in completion(true) }) edit.image = UIImage(systemName: "pencil") - return UISwipeActionsConfiguration(actions:[delete, edit]) + return UISwipeActionsConfiguration(actions: [delete, edit]) } } diff --git a/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift index b9b71146d..1bfc25edd 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift @@ -12,14 +12,14 @@ class ModalViewController: UIViewController { @IBOutlet weak var tableView: UITableView! private let labelText = ["레이블", "마일스톤", "이슈 편집", "이슈 닫기", "열린 이슈"] private let supplementary = ["document", "없음", "pencil", "archivebox", "trash"] - + override func viewDidLoad() { super.viewDidLoad() view.layer.cornerRadius = 15 setupGestureRecognizers() } - + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let height: CGFloat = 353 + bottomSafeAreaHeight @@ -45,9 +45,8 @@ extension ModalViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return labelText.count } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = labelText[indexPath.row] switch indexPath.row { @@ -66,7 +65,7 @@ class DetailTextStyleTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: .value1, reuseIdentifier: reuseIdentifier) } - + required init?(coder: NSCoder) { super.init(coder: coder) } diff --git a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift index f0d7c1f11..bb4910682 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift @@ -11,23 +11,22 @@ import MarkdownKit @IBDesignable class NewIssueViewController: UIViewController { - private let additionalInfo = ["레이블", "마일스톤", "작성자"] private let cellReuseIdentifier = "NewIssueViewCell" private let markdownParser = MarkdownParser() - + private let subject: UILabel = { let label = UILabel() label.text = "제목" return label }() - + private let textField: UITextField = { let textField = UITextField() textField.placeholder = "제목을 입력하세요." return textField }() - + private lazy var tableView: UITableView = { let tableView = UITableView() tableView.isScrollEnabled = false @@ -38,30 +37,30 @@ class NewIssueViewController: UIViewController { private let segmentedControl = UISegmentedControl(items: ["마크다운", "미리보기"]) private var textView: UITextView? private var textString: String? - + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground - + tableView.dataSource = self tableView.delegate = self - + let save = UIBarButtonItem(title: "저장", style: .plain, target: self, action: nil) navigationItem.rightBarButtonItem = save navigationItem.titleView = segmentedControl segmentedControl.selectedSegmentIndex = 0 segmentedControl.addTarget(self, action: #selector(reload), for: .valueChanged) - + view.addSubview(subject) view.addSubview(textField) view.addSubview(tableView) } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true } - + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() subject.snp.makeConstraints { maker in @@ -80,7 +79,7 @@ class NewIssueViewController: UIViewController { maker.bottom.equalTo(view.safeAreaLayoutGuide) } } - + @objc func reload(sender: UISegmentedControl) { switch sender.selectedSegmentIndex { @@ -99,7 +98,7 @@ extension NewIssueViewController: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 2 } - + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: @@ -110,7 +109,7 @@ extension NewIssueViewController: UITableViewDataSource { return 0 } } - + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) cell.selectionStyle = .none @@ -135,7 +134,7 @@ extension NewIssueViewController: UITableViewDelegate { } return nil } - + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { switch indexPath.section { case 0: @@ -146,7 +145,7 @@ extension NewIssueViewController: UITableViewDelegate { return 0 } } - + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { return UIView() } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index b7abf37f6..c4a409df6 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -34,12 +34,12 @@ class IssueListViewController: UIViewController { addIssueButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(addIssueButtonTapped))) cancelButton.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside) } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = false } - + @objc func filterButtonTapped() { let controller = UINavigationController(rootViewController: IssueFilterViewController()) present(controller, animated: true) @@ -54,18 +54,18 @@ class IssueListViewController: UIViewController { view.addSubview(issueToolbar) setupToolbarAutoulayout() } - + @objc func addIssueButtonTapped() { let controller = NewIssueViewController() navigationController?.pushViewController(controller, animated: true) } - + @objc func cancelButtonTapped() { setupNavigationItem() tabBarController?.tabBar.isHidden = false issueToolbar.removeFromSuperview() } - + func setupAddIssueButtonAutolayout() { view.addSubview(addIssueButton) addIssueButton.snp.makeConstraints { button in @@ -74,7 +74,7 @@ class IssueListViewController: UIViewController { button.bottom.equalToSuperview().offset(-100) } } - + func setupToolbarAutoulayout() { issueToolbar.snp.makeConstraints { toolbar in toolbar.leading.trailing.equalToSuperview() @@ -82,7 +82,7 @@ class IssueListViewController: UIViewController { toolbar.height.equalTo(44) } } - + func setupNavigationItem() { navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "이슈" @@ -90,7 +90,7 @@ class IssueListViewController: UIViewController { navigationItem.rightBarButtonItem = UIBarButtonItem(customView: selectBarButton) navigationItem.searchController = searchController } - + func setupIssueTableView() { issueTableView.register(IssueTableViewCell.self, forCellReuseIdentifier: IssueTableViewCell.identifier) issueTableView.allowsMultipleSelection = true @@ -117,29 +117,29 @@ extension IssueListViewController: UITableViewDelegate { guard let cell = tableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } cell.selectionStyle = .none cell.check() - + let controller = IssueDetailViewController() navigationController?.pushViewController(controller, animated: true) } - + func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { guard let cell = tableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } cell.uncheck() } - + func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - let deleteAction = UIContextualAction(style: .destructive, title: "삭제") { ac, view, success in + let deleteAction = UIContextualAction(style: .destructive, title: "삭제") { _, _, success in success(true) } - - let shareAction = UIContextualAction(style: .normal, title: "닫기") { ac, view, success in + + let shareAction = UIContextualAction(style: .normal, title: "닫기") { _, _, success in success(true) } - + deleteAction.image = UIImage(systemName: "trash") shareAction.image = UIImage(systemName: "archivebox") shareAction.backgroundColor = #colorLiteral(red: 0.7988751531, green: 0.8300203681, blue: 0.9990373254, alpha: 1) - + return UISwipeActionsConfiguration(actions: [shareAction, deleteAction]) } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift index fb5a15fde..022fb4987 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift @@ -19,6 +19,7 @@ class AddLabelViewModel { init(networkManager: Networkable) { self.networkManager = networkManager } + func postAddedLabel(completion: @escaping () -> Void) { let encodableLabel = IssueLabel(id: nil, title: title.value, color: color.value, fontColor: fontColor, description: description.value) networkManager.postRequest(url: Endpoint(path: .label).url()!, encodable: encodableLabel, completion: completion) From d74b813a9a82c9a24582db7372a9d4f963078b71 Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:07:10 +0900 Subject: [PATCH 034/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Extension?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extension/UIColor+HexInit.swift | 52 +++++++++---------- .../Extension/UIKit+Extension.swift | 4 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift b/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift index 0bea25fcd..ee8604310 100644 --- a/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift +++ b/iOS/issue-tracker/issue-tracker/Extension/UIColor+HexInit.swift @@ -8,18 +8,18 @@ import UIKit extension UIColor { - static func hexStringToUIColor (hex:String) -> UIColor { - var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + static func hexStringToUIColor(hex: String) -> UIColor { + var cString: String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() - if (cString.hasPrefix("#")) { + if cString.hasPrefix("#") { cString.remove(at: cString.startIndex) } - if ((cString.count) != 6) { + if (cString.count) != 6 { return UIColor.gray } - var rgbValue:UInt64 = 0 + var rgbValue: UInt64 = 0 Scanner(string: cString).scanHexInt64(&rgbValue) return UIColor( @@ -29,29 +29,29 @@ extension UIColor { alpha: CGFloat(1.0) ) } - + func convertHexToString() -> String { - var r:CGFloat = 0 - var g:CGFloat = 0 - var b:CGFloat = 0 - var a:CGFloat = 0 - - getRed(&r, green: &g, blue: &b, alpha: &a) - - let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 - - return NSString(format:"#%06x", rgb) as String + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + + getRed(&red, green: &green, blue: &blue, alpha: &alpha) + + let rgb: Int = (Int)(red*255)<<16 | (Int)(green*255)<<8 | (Int)(blue*255)<<0 + + return NSString(format: "#%06x", rgb) as String } - - static func getRandomColor() -> UIColor{ - - let randomRed:CGFloat = CGFloat(drand48()) - - let randomGreen:CGFloat = CGFloat(drand48()) - - let randomBlue:CGFloat = CGFloat(drand48()) - + + static func getRandomColor() -> UIColor { + + let randomRed: CGFloat = CGFloat(drand48()) + + let randomGreen: CGFloat = CGFloat(drand48()) + + let randomBlue: CGFloat = CGFloat(drand48()) + return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0) - + } } diff --git a/iOS/issue-tracker/issue-tracker/Extension/UIKit+Extension.swift b/iOS/issue-tracker/issue-tracker/Extension/UIKit+Extension.swift index 9b08b7390..e343bb159 100644 --- a/iOS/issue-tracker/issue-tracker/Extension/UIKit+Extension.swift +++ b/iOS/issue-tracker/issue-tracker/Extension/UIKit+Extension.swift @@ -12,11 +12,11 @@ extension UIViewController { guard let statusBarHeight = view.window?.windowScene?.statusBarManager?.statusBarFrame.height else { return 0 } return statusBarHeight } - + var topBarHeight: CGFloat { return statusBarHeight + (navigationController?.navigationBar.frame.height ?? 0) } - + var bottomSafeAreaHeight: CGFloat { return view.safeAreaInsets.bottom } From 54649813a2abdd9ca670975e7f0841db511d08cb Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:13:25 +0900 Subject: [PATCH 035/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20View/IssueList?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LabelsCollectionViewCell.swift | 10 ++--- .../Controller/IssueTableFooterView.swift | 6 +-- .../Controller/IssueToolbar.swift | 12 +++--- .../View/IssueList/AddIssueButton.swift | 6 +-- .../View/IssueList/CancelButton.swift | 2 +- .../View/IssueList/FilterBarButton.swift | 2 +- .../View/IssueList/IssueTableViewCell.swift | 38 +++++++++---------- .../View/IssueList/LabelsCollectionView.swift | 7 ++-- .../View/IssueList/MilestoneView.swift | 12 +++--- .../View/IssueList/SelectBarButton.swift | 2 +- .../View/IssueList/ToolBarTextField.swift | 4 +- 11 files changed, 50 insertions(+), 51 deletions(-) diff --git a/iOS/issue-tracker/LabelsCollectionViewCell.swift b/iOS/issue-tracker/LabelsCollectionViewCell.swift index 3540c0208..1ca14160d 100644 --- a/iOS/issue-tracker/LabelsCollectionViewCell.swift +++ b/iOS/issue-tracker/LabelsCollectionViewCell.swift @@ -10,7 +10,7 @@ import SnapKit class LabelsCollectionViewCell: UICollectionViewCell { static var identifiers = "LabelsCollectionViewCell" - + let label: PaddingLabel = { var label = PaddingLabel(withInsets: 0, 0, 10, 10) label.textAlignment = .center @@ -19,25 +19,25 @@ class LabelsCollectionViewCell: UICollectionViewCell { label.layer.cornerRadius = 15 return label }() - + override init(frame: CGRect) { super.init(frame: frame) addSubview(label) setupAutolayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) addSubview(label) setupAutolayout() } - + func setupAutolayout() { label.snp.makeConstraints { label in label.edges.equalToSuperview() } } - + func configure(title: String, color: String) { label.text = title label.backgroundColor = UIColor.hexStringToUIColor(hex: color) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift index 144c66843..923d986b5 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift @@ -16,21 +16,21 @@ class IssueTableFooterView: UIView { label.textColor = .lightGray return label }() - + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = #colorLiteral(red: 0.9489405751, green: 0.9490727782, blue: 0.9685038924, alpha: 1) addSubview(label) setupAutolayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) backgroundColor = #colorLiteral(red: 0.9489405751, green: 0.9490727782, blue: 0.9685038924, alpha: 1) addSubview(label) setupAutolayout() } - + func setupAutolayout() { label.snp.makeConstraints { label in label.centerX.equalToSuperview() diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift index 13e085548..1703b4c9f 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift @@ -8,31 +8,31 @@ import UIKit class IssueToolbar: UIToolbar { - + let checkBoxBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.image = UIImage(systemName: "checkmark.circle") return item }() - + let closeIssueBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.image = UIImage(systemName: "archivebox") return item }() - + let labelBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.title = "이슈를 선택하세요" item.isEnabled = false return item }() - + let flexibleBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) return item }() - + override init(frame: CGRect) { super.init(frame: frame) setupToolbar() @@ -42,7 +42,7 @@ class IssueToolbar: UIToolbar { super.init(coder: coder) setupToolbar() } - + func setupToolbar() { let items = [checkBoxBarButtonItem, flexibleBarButtonItem, labelBarButtonItem, flexibleBarButtonItem, closeIssueBarButtonItem] setItems(items, animated: false) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift index 6f3c2d992..96bdcfb59 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift @@ -18,10 +18,10 @@ class AddIssueButton: UIView { super.init(coder: coder) setupButton() } - + override func draw(_ rect: CGRect) { let path = UIBezierPath() - + path.move(to: CGPoint(x: self.bounds.width * 0.25, y: self.bounds.height * 0.5)) path.addLine(to: CGPoint(x: self.bounds.width * 0.75, y: self.bounds.height * 0.5)) path.move(to: CGPoint(x: self.bounds.width * 0.5, y: self.bounds.height * 0.25)) @@ -29,7 +29,7 @@ class AddIssueButton: UIView { UIColor.white.set() path.stroke() } - + func setupButton() { clipsToBounds = true layer.cornerRadius = self.bounds.size.width * 0.5 diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift index 628731a40..ab3f23d22 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift @@ -13,7 +13,7 @@ class CancelButton: UIButton { setTitle("취소", for: .normal) setTitleColor(.systemBlue, for: .normal) } - + required init?(coder: NSCoder) { super.init(coder: coder) setTitle("취소", for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift index 74ac0a4a7..76c59aaac 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift @@ -14,7 +14,7 @@ class FilterBarButton: UIButton { setTitle("필터", for: .normal) setTitleColor(.systemBlue, for: .normal) } - + required init?(coder: NSCoder) { super.init(coder: coder) setImage(UIImage(systemName: "doc.text.magnifyingglass"), for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index 41685f058..1c46006eb 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -9,39 +9,39 @@ import UIKit import SnapKit class IssueTableViewCell: UITableViewCell { - + static var identifier = "IssueTableViewCell" - + var fakeData = [IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85")] - + var largeTitle: UILabel = { var label = UILabel() label.font = UIFont.boldSystemFont(ofSize: 22) return label }() - + var labelDescription: UILabel = { var label = UILabel() label.textColor = .lightGray return label }() - + var milestoneView: MilestoneView = { var milestone = MilestoneView() return milestone }() - + var labelsCollectionView: LabelsCollectionView = { var collectionView = LabelsCollectionView() return collectionView }() - + var checkBoxImageView: UIImageView = { var imageView = UIImageView() imageView.image = UIImage(systemName: "checkmark.circle.fill") return imageView }() - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) labelsCollectionView.dataSource = self @@ -49,7 +49,7 @@ class IssueTableViewCell: UITableViewCell { setupAutolayout() checkBoxImageView.isHidden = true } - + required init?(coder: NSCoder) { super.init(coder: coder) labelsCollectionView.dataSource = self @@ -57,7 +57,7 @@ class IssueTableViewCell: UITableViewCell { setupAutolayout() checkBoxImageView.isHidden = true } - + func addSubviews() { addSubview(labelsCollectionView) addSubview(labelDescription) @@ -65,49 +65,49 @@ class IssueTableViewCell: UITableViewCell { addSubview(largeTitle) addSubview(checkBoxImageView) } - + func setupAutolayout() { largeTitle.snp.makeConstraints { title in title.top.equalTo(24) title.leading.trailing.equalTo(16) title.height.equalTo(28) } - + labelDescription.snp.makeConstraints { label in label.top.equalTo(largeTitle.snp.bottom).offset(16) label.leading.trailing.equalToSuperview().inset(16) label.height.equalTo(22) } - + milestoneView.snp.makeConstraints { view in view.top.equalTo(labelDescription.snp.bottom).offset(16) view.leading.trailing.equalToSuperview().inset(16) view.height.equalTo(22) } - + labelsCollectionView.snp.makeConstraints { view in view.top.equalTo(milestoneView.snp.bottom).offset(16) view.leading.trailing.equalToSuperview().inset(16) view.bottom.equalToSuperview() } - + checkBoxImageView.snp.makeConstraints { image in image.top.equalToSuperview().inset(24) image.trailing.equalToSuperview().inset(16) image.width.height.equalTo(30) } } - + func setupIssueCell(title: String, description: String, milestoneTitle: String, color: String) { self.largeTitle.text = title self.labelDescription.text = description self.milestoneView.setMilestoneTitle(title: milestoneTitle) } - + func check() { checkBoxImageView.isHidden = false } - + func uncheck() { checkBoxImageView.isHidden = true } @@ -117,7 +117,7 @@ extension IssueTableViewCell: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return fakeData.count } - + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LabelsCollectionViewCell.identifiers, for: indexPath) as? LabelsCollectionViewCell else { return UICollectionViewCell() } cell.configure(title: fakeData[indexPath.item].title, color: fakeData[indexPath.item].color) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift index 914a6c80e..85f60421a 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift @@ -8,7 +8,7 @@ import UIKit class LabelsCollectionView: UICollectionView { - + var labelsLayout: UICollectionViewFlowLayout = { var layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical @@ -17,16 +17,15 @@ class LabelsCollectionView: UICollectionView { layout.minimumInteritemSpacing = 10 return layout }() - + override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: frame, collectionViewLayout: labelsLayout) register(LabelsCollectionViewCell.self, forCellWithReuseIdentifier: LabelsCollectionViewCell.identifiers) isScrollEnabled = false backgroundColor = .white } - + required init?(coder: NSCoder) { super.init(coder: coder) } - } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift index 564d4732f..509f097c9 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift @@ -14,30 +14,30 @@ class MilestoneView: UIView { imageView.image = UIImage(named: "vector") return imageView }() - + var milestoneTitle: UILabel = { var label = UILabel() label.textColor = .lightGray return label }() - + override init(frame: CGRect) { super.init(frame: frame) addSubviews() setupAutolayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) addSubviews() setupAutolayout() } - + func addSubviews() { addSubview(sfsymbolImageView) addSubview(milestoneTitle) } - + func setupAutolayout() { sfsymbolImageView.snp.makeConstraints { imageView in imageView.top.leading.bottom.equalToSuperview() @@ -49,7 +49,7 @@ class MilestoneView: UIView { label.width.greaterThanOrEqualTo(30) } } - + func setMilestoneTitle(title: String) { self.milestoneTitle.text = title } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift index 067438b88..3afb89f59 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift @@ -15,7 +15,7 @@ class SelectBarButton: UIButton { setTitleColor(.systemBlue, for: .normal) semanticContentAttribute = .forceRightToLeft } - + required init?(coder: NSCoder) { super.init(coder: coder) setImage(UIImage(systemName: "checkmark.circle"), for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift index 1b65c3ce4..6adff1774 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift @@ -14,7 +14,7 @@ class ToolBarTextField: UITextField { button.setImage(UIImage(systemName: "arrow.up.circle.fill"), for: .normal) return button }() - + override init(frame: CGRect) { super.init(frame: frame) self.placeholder = "코멘트를 입력하세요" @@ -24,7 +24,7 @@ class ToolBarTextField: UITextField { self.layer.masksToBounds = true self.translatesAutoresizingMaskIntoConstraints = false } - + required init?(coder: NSCoder) { super.init(coder: coder) } From c77f9fe7b7a4dc4910937fd056e6dc50ef96ccb1 Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:16:03 +0900 Subject: [PATCH 036/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20View/Label=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Label/AddLabelButton.swift | 2 +- .../View/Label/AddTableViewCell.swift | 8 ++++---- .../View/Label/EstimatedLabelView.swift | 10 +++++----- .../View/Label/LabelTableViewCell.swift | 16 ++++++++-------- .../issue-tracker/View/Label/PaddingLabel.swift | 12 +++++------- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift b/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift index 76b5b0e6d..cee6d7709 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift @@ -15,7 +15,7 @@ class AddLabelButton: UIButton { setTitleColor(.systemBlue, for: .normal) semanticContentAttribute = .forceRightToLeft } - + required init?(coder: NSCoder) { super.init(coder: coder) setImage(UIImage(systemName: "plus"), for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift index 83d4e25b5..ebcfdb112 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift @@ -9,16 +9,16 @@ import UIKit import RxSwift class AddTableViewCell: UITableViewCell { - + static let identifier = "AddTableViewCell" - + let textField = UITextField() let reloadButton: UIButton = { var button = UIButton() button.setBackgroundImage(UIImage(systemName: "gobackward"), for: .normal) return button }() - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) @@ -27,7 +27,7 @@ class AddTableViewCell: UITableViewCell { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func configureTextFieldPlaceHolder(text: String) { self.textField.placeholder = text } diff --git a/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift b/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift index 897e8f6d6..7d6cba63e 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift @@ -17,7 +17,7 @@ class EstimatedLabelView: UIView { label.layer.cornerRadius = 10 return label }() - + override init(frame: CGRect) { super.init(frame: frame) addSubview(label) @@ -26,22 +26,22 @@ class EstimatedLabelView: UIView { self.backgroundColor = #colorLiteral(red: 0.9102189541, green: 0.9093225002, blue: 0.9310914278, alpha: 1) configureLabelAutolayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) } - + func configureLabelAutolayout() { label.snp.makeConstraints { label in label.centerX.centerY.equalToSuperview() } } - + func modifyLabelTitle(title: String) { self.label.text = title label.sizeToFit() } - + func setupLabelColor(color: UIColor) { self.label.backgroundColor = color } diff --git a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift index 192b0e288..b03bf313a 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift @@ -9,7 +9,7 @@ import UIKit import SnapKit class LabelTableViewCell: UITableViewCell { - + static var identifier = "LabelTableViewCell" var labelView: PaddingLabel = { @@ -20,29 +20,29 @@ class LabelTableViewCell: UITableViewCell { label.layer.cornerRadius = 15 return label }() - + var labelDescription: UILabel = { var label = UILabel() return label }() - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) addSubviews() setupAutolayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) addSubviews() setupAutolayout() } - + func addSubviews() { addSubview(labelView) addSubview(labelDescription) } - + func setupAutolayout() { labelView.snp.makeConstraints { view in view.top.equalToSuperview().offset(24) @@ -50,14 +50,14 @@ class LabelTableViewCell: UITableViewCell { view.width.greaterThanOrEqualTo(50) view.height.equalTo(30) } - + labelDescription.snp.makeConstraints { label in label.top.equalTo(labelView.snp.bottom).offset(16) label.leading.trailing.equalToSuperview().offset(16) label.height.equalTo(22) } } - + func setupLabelCell(title: String, description: String, color: String) { self.labelView.text = title self.labelView.backgroundColor = UIColor.hexStringToUIColor(hex: color) diff --git a/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift b/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift index e5d9bbda4..4b9a8a9a7 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift @@ -29,18 +29,16 @@ class PaddingLabel: UILabel { self.rightInset = 0 super.init(coder: coder) } - + override func drawText(in rect: CGRect) { let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset) super.drawText(in: rect.inset(by: insets)) } override var intrinsicContentSize: CGSize { - get { - var contentSize = super.intrinsicContentSize - contentSize.height += topInset + bottomInset - contentSize.width += leftInset + rightInset - return contentSize - } + var contentSize = super.intrinsicContentSize + contentSize.height += topInset + bottomInset + contentSize.width += leftInset + rightInset + return contentSize } } From 3e81f81236bd4c5ecf27480183bc6d64bf500857 Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:22:11 +0900 Subject: [PATCH 037/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20View/Milestone?= =?UTF-8?q?=20=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Milestone/MilestoneTableViewCell.swift | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift index 030ba1912..23a41ab08 100644 --- a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift @@ -11,7 +11,7 @@ import SnapKit class MilestoneTableViewCell: UITableViewCell { static let reuseId = "MilestoneTableViewCell" - + private let verticalStackView: UIStackView = { let stackView = UIStackView() stackView.spacing = 10 @@ -20,7 +20,7 @@ class MilestoneTableViewCell: UITableViewCell { stackView.distribution = .fillEqually return stackView }() - + private let horizenStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal @@ -28,8 +28,8 @@ class MilestoneTableViewCell: UITableViewCell { stackView.distribution = .fill return stackView }() - - private let IssueLabelStackView: UIStackView = { + + private let issueLabelStackView: UIStackView = { let stackView = UIStackView() stackView.spacing = 5 stackView.axis = .horizontal @@ -37,28 +37,28 @@ class MilestoneTableViewCell: UITableViewCell { stackView.distribution = .fillEqually return stackView }() - + private let titleLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 22, weight: .bold) label.textAlignment = .left return label }() - + private let achievementLabel: UILabel = { let label = UILabel() label.text = "50%" label.textAlignment = .right return label }() - + private let descriptionLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 17) label.textColor = .systemGray return label }() - + private let dueDateLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 17) @@ -75,7 +75,7 @@ class MilestoneTableViewCell: UITableViewCell { label.backgroundColor = UIColor.hexStringToUIColor(hex: "#B1CAE5") return label }() - + private let closedIssue: PaddingLabel = { let label = PaddingLabel(withInsets: 5, 5, 10, 10) label.textAlignment = .center @@ -88,29 +88,29 @@ class MilestoneTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) - IssueLabelStackView.addArrangedSubview(openedIssue) - IssueLabelStackView.addArrangedSubview(closedIssue) - + issueLabelStackView.addArrangedSubview(openedIssue) + issueLabelStackView.addArrangedSubview(closedIssue) + verticalStackView.addArrangedSubview(titleLabel) verticalStackView.addArrangedSubview(descriptionLabel) verticalStackView.addArrangedSubview(dueDateLabel) - verticalStackView.addArrangedSubview(IssueLabelStackView) - + verticalStackView.addArrangedSubview(issueLabelStackView) + horizenStackView.addArrangedSubview(verticalStackView) horizenStackView.addArrangedSubview(achievementLabel) addSubview(horizenStackView) } - + required init?(coder: NSCoder) { super.init(coder: coder) - IssueLabelStackView.addArrangedSubview(openedIssue) - IssueLabelStackView.addArrangedSubview(closedIssue) - + issueLabelStackView.addArrangedSubview(openedIssue) + issueLabelStackView.addArrangedSubview(closedIssue) + verticalStackView.addArrangedSubview(titleLabel) verticalStackView.addArrangedSubview(descriptionLabel) verticalStackView.addArrangedSubview(dueDateLabel) - verticalStackView.addArrangedSubview(IssueLabelStackView) - + verticalStackView.addArrangedSubview(issueLabelStackView) + horizenStackView.addArrangedSubview(verticalStackView) horizenStackView.addArrangedSubview(achievementLabel) addSubview(horizenStackView) @@ -122,7 +122,7 @@ class MilestoneTableViewCell: UITableViewCell { maker.edges.equalToSuperview().inset(20) } } - + func configure(with milestone: Milestone) { titleLabel.text = milestone.title descriptionLabel.text = milestone.description From 8f353a062f61f93404f66749bf2ba3e6699e39b5 Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:26:32 +0900 Subject: [PATCH 038/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20View/Login=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueDetailTableViewCell.swift | 16 +++++----- .../View/Login/AppleLoginButton.swift | 18 +++++------ .../View/Login/GitHubLoginButton.swift | 18 +++++------ .../View/Login/IDPasswordTextField.swift | 28 ++++++++--------- .../issue-tracker/View/Login/LoginView.swift | 31 ++++++++----------- 5 files changed, 52 insertions(+), 59 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueDetailTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueDetailTableViewCell.swift index 83a554540..6913be46a 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueDetailTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueDetailTableViewCell.swift @@ -18,7 +18,7 @@ class IssueDetailTableViewCell: UITableViewCell { stackViw.spacing = 10 return stackViw }() - + private let verticalStackView: UIStackView = { let stackViw = UIStackView() stackViw.axis = .vertical @@ -26,7 +26,7 @@ class IssueDetailTableViewCell: UITableViewCell { stackViw.distribution = .fill return stackViw }() - + private let profile: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(systemName: "bell") @@ -35,14 +35,14 @@ class IssueDetailTableViewCell: UITableViewCell { imageView.layer.cornerRadius = imageView.frame.width / 2 return imageView }() - + private let author: UILabel = { let label = UILabel() label.text = "Oni" label.textColor = .label return label }() - + private let timestamp: UILabel = { let label = UILabel() label.text = "1분 전" @@ -50,7 +50,7 @@ class IssueDetailTableViewCell: UITableViewCell { label.font = .systemFont(ofSize: 14) return label }() - + private let comment: UILabel = { let label = UILabel() label.numberOfLines = 0 @@ -58,7 +58,7 @@ class IssueDetailTableViewCell: UITableViewCell { label.textColor = .label return label }() - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) self.selectionStyle = .gray @@ -69,11 +69,11 @@ class IssueDetailTableViewCell: UITableViewCell { horizenStackView.addArrangedSubview(verticalStackView) contentView.addSubview(horizenStackView) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func layoutSubviews() { super.layoutSubviews() profile.snp.makeConstraints { maker in diff --git a/iOS/issue-tracker/issue-tracker/View/Login/AppleLoginButton.swift b/iOS/issue-tracker/issue-tracker/View/Login/AppleLoginButton.swift index 49be3777a..7946109fd 100644 --- a/iOS/issue-tracker/issue-tracker/View/Login/AppleLoginButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/Login/AppleLoginButton.swift @@ -9,7 +9,7 @@ import UIKit import SnapKit class AppleLoginButton: UIView { - + var stackView: UIStackView = { var stackView = UIStackView() stackView.axis = .horizontal @@ -18,14 +18,14 @@ class AppleLoginButton: UIView { stackView.spacing = 2 return stackView }() - + var appleImageView: UIImageView = { var imageView = UIImageView() imageView.contentMode = .scaleAspectFill imageView.image = UIImage(named: "apple") return imageView }() - + var loginLabel: UILabel = { var label = UILabel() label.text = "Apple 계정으로 로그인" @@ -33,7 +33,7 @@ class AppleLoginButton: UIView { label.textColor = .white return label }() - + override init(frame: CGRect) { super.init(frame: frame) setGitHubLoginButton() @@ -41,7 +41,7 @@ class AppleLoginButton: UIView { self.addSubview(stackView) setAutolayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) setGitHubLoginButton() @@ -49,23 +49,23 @@ class AppleLoginButton: UIView { self.addSubview(stackView) setAutolayout() } - + func setGitHubLoginButton() { self.backgroundColor = .black self.layer.masksToBounds = true self.layer.cornerRadius = 10 } - + func setStackView() { stackView.addArrangedSubview(appleImageView) stackView.addArrangedSubview(loginLabel) } - + func setAutolayout() { appleImageView.snp.makeConstraints { image in image.width.height.equalTo(50) } - + stackView.snp.makeConstraints { stackView in stackView.centerX.centerY.equalToSuperview() } diff --git a/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift b/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift index 0888a258b..d96208ec7 100644 --- a/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift @@ -9,7 +9,7 @@ import UIKit import SnapKit class GitHubLoginButton: UIView { - + var stackView: UIStackView = { var stackView = UIStackView() stackView.axis = .horizontal @@ -18,14 +18,14 @@ class GitHubLoginButton: UIView { stackView.spacing = 8 return stackView }() - + var octocatImageView: UIImageView = { var imageView = UIImageView() imageView.contentMode = .scaleAspectFill imageView.image = UIImage(named: "github") return imageView }() - + var loginLabel: UILabel = { var label = UILabel() label.text = "GitHub 계정으로 로그인" @@ -33,7 +33,7 @@ class GitHubLoginButton: UIView { label.textColor = .white return label }() - + override init(frame: CGRect) { super.init(frame: frame) setGitHubLoginButton() @@ -41,7 +41,7 @@ class GitHubLoginButton: UIView { self.addSubview(stackView) setAutolayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) setGitHubLoginButton() @@ -49,23 +49,23 @@ class GitHubLoginButton: UIView { self.addSubview(stackView) setAutolayout() } - + func setGitHubLoginButton() { self.backgroundColor = .black self.layer.masksToBounds = true self.layer.cornerRadius = 10 } - + func setStackView() { stackView.addArrangedSubview(octocatImageView) stackView.addArrangedSubview(loginLabel) } - + func setAutolayout() { octocatImageView.snp.makeConstraints { image in image.width.height.equalTo(30) } - + stackView.snp.makeConstraints { stackView in stackView.centerX.centerY.equalToSuperview() } diff --git a/iOS/issue-tracker/issue-tracker/View/Login/IDPasswordTextField.swift b/iOS/issue-tracker/issue-tracker/View/Login/IDPasswordTextField.swift index 1e1481880..a8319991f 100644 --- a/iOS/issue-tracker/issue-tracker/View/Login/IDPasswordTextField.swift +++ b/iOS/issue-tracker/issue-tracker/View/Login/IDPasswordTextField.swift @@ -7,54 +7,53 @@ import UIKit import SnapKit -@IBDesignable class IDPasswordTextField: UIView { - + let IDLabel: UILabel = { var label = UILabel() label.text = "아이디" return label }() - + let passwordLabel: UILabel = { var label = UILabel() label.text = "비밀번호" return label }() - + let line: UIView = { var line = UIView() line.layer.borderColor = UIColor.lightGray.cgColor line.layer.borderWidth = 1 return line }() - + let IDTextField: UITextField = { var textField = UITextField() textField.borderStyle = .none return textField }() - + let passwordTextField: UITextField = { var textField = UITextField() textField.borderStyle = .none return textField }() - + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white addSubViews() autoLayout() } - + required init?(coder: NSCoder) { super.init(coder: coder) backgroundColor = .white addSubViews() autoLayout() } - + func addSubViews() { self.addSubview(IDLabel) self.addSubview(passwordLabel) @@ -62,33 +61,33 @@ class IDPasswordTextField: UIView { self.addSubview(IDTextField) self.addSubview(passwordTextField) } - + func autoLayout() { IDLabel.snp.makeConstraints { label in label.top.equalTo(self.safeAreaLayoutGuide).offset(12) label.leading.equalTo(self.safeAreaLayoutGuide).offset(20) label.width.equalTo(47) } - + line.snp.makeConstraints { view in view.height.equalTo(1) view.top.equalTo(IDLabel.snp.bottom).offset(10.5) view.leading.trailing.equalTo(self.safeAreaLayoutGuide) } - + passwordLabel.snp.makeConstraints { label in label.top.equalTo(line.snp.bottom).offset(10.5) label.leading.equalTo(self.safeAreaLayoutGuide).offset(20) label.width.equalTo(62) } - + IDTextField.snp.makeConstraints { textField in textField.top.equalTo(self.safeAreaLayoutGuide).offset(11) textField.leading.equalTo(IDLabel.snp.trailing).offset(61) textField.trailing.equalTo(self.safeAreaLayoutGuide) textField.height.equalTo(22) } - + passwordTextField.snp.makeConstraints { textField in textField.top.equalTo(line.snp.bottom).offset(11) textField.leading.equalTo(passwordLabel.snp.trailing).offset(61) @@ -96,5 +95,4 @@ class IDPasswordTextField: UIView { textField.height.equalTo(22) } } - } diff --git a/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift b/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift index cca74ad61..dee4e711f 100644 --- a/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift +++ b/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift @@ -8,9 +8,8 @@ import UIKit import SnapKit -@IBDesignable class LoginView: UIView { - + let titleLabel: UILabel = { var label = UILabel() label.textAlignment = .center @@ -18,7 +17,7 @@ class LoginView: UIView { label.font = UIFont.systemFont(ofSize: 48) return label }() - + let login: UIButton = { var button = UIButton() button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16) @@ -26,7 +25,7 @@ class LoginView: UIView { button.setTitleColor(.systemBlue, for: .normal) return button }() - + let signUp: UIButton = { var button = UIButton() button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16) @@ -34,27 +33,25 @@ class LoginView: UIView { button.setTitleColor(.systemBlue, for: .normal) return button }() - + let appleLoginButton = AppleLoginButton() let githubLoginButton = GitHubLoginButton() let textField = IDPasswordTextField() - + override init(frame: CGRect) { super.init(frame: frame) backgroundColor = #colorLiteral(red: 0.9490135312, green: 0.9490135312, blue: 0.9694761634, alpha: 1) addSubViews() setAutolayout() - } - + required init?(coder: NSCoder) { super.init(coder: coder) backgroundColor = #colorLiteral(red: 0.9490135312, green: 0.9490135312, blue: 0.9694761634, alpha: 1) addSubViews() setAutolayout() - } - + func addSubViews() { addSubview(titleLabel) addSubview(appleLoginButton) @@ -63,46 +60,44 @@ class LoginView: UIView { addSubview(login) addSubview(signUp) } - + func setAutolayout() { titleLabel.snp.makeConstraints { title in title.height.equalTo(72) title.top.equalToSuperview().offset(165) title.leading.trailing.equalTo(self).inset(40) } - + textField.snp.makeConstraints { textField in textField.top.equalTo(titleLabel.snp.bottom).offset(72) textField.leading.trailing.equalTo(self) textField.height.equalTo(89) } - + login.snp.makeConstraints { login in login.top.equalTo(textField.snp.bottom).offset(32) login.leading.equalTo(self).inset(96) login.width.equalTo(45) login.height.equalTo(21) } - + signUp.snp.makeConstraints { signUp in signUp.top.equalTo(textField.snp.bottom).offset(32) signUp.leading.equalTo(login.snp.trailing).offset(79) signUp.width.equalTo(59) signUp.height.equalTo(21) } - + githubLoginButton.snp.makeConstraints { button in button.top.equalTo(login.snp.bottom).offset(175) button.leading.trailing.equalTo(self).inset(16) button.height.equalTo(56) } - + appleLoginButton.snp.makeConstraints { button in button.top.equalTo(githubLoginButton.snp.bottom).offset(14) button.leading.trailing.equalTo(self).inset(16) button.height.equalTo(56) } - } - } From 662f289357e95358734aea76555d0d165f430bdb Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:31:19 +0900 Subject: [PATCH 039/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20View/Model=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=ED=95=98=EC=9C=84=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=93=A4=EC=97=90=20SwiftLint=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/.swiftlint.yml | 2 ++ iOS/issue-tracker/issue-tracker/Model/Endpoint.swift | 1 - iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift | 4 +--- iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift | 7 +++---- .../issue-tracker/Model/UserDTO/MilestoneList.swift | 2 +- .../issue-tracker/Model/UserDTO/NewIssue.swift | 2 +- .../issue-tracker/Model/UserDTO/UserList.swift | 2 +- 7 files changed, 9 insertions(+), 11 deletions(-) diff --git a/iOS/issue-tracker/.swiftlint.yml b/iOS/issue-tracker/.swiftlint.yml index 3f37f4bfa..d3f374f01 100644 --- a/iOS/issue-tracker/.swiftlint.yml +++ b/iOS/issue-tracker/.swiftlint.yml @@ -1,6 +1,8 @@ disabled_rules: - line_length +- identifier_name included: excluded: - issue-trackerTests - issue-trackerUITests + diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index f34865e50..73c297607 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -17,7 +17,6 @@ struct Endpoint { var urlComponents = URLComponents() urlComponents.scheme = scheme urlComponents.host = host -// urlComponents.port = port urlComponents.path = path.pathString urlComponents.queryItems = queryItems return urlComponents.url diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index a906f7f56..a7efd4c86 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -28,7 +28,7 @@ class NetworkManager: Networkable { } } } - + func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) { AF.request(url, method: .post, parameters: encodable, encoder: JSONParameterEncoder.default, headers: httpHeaders) .validate(statusCode: 200..<300) @@ -42,5 +42,3 @@ class NetworkManager: Networkable { } } } - - diff --git a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift index accb90fc4..c9d8c92f4 100644 --- a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift @@ -12,9 +12,9 @@ class OAuthManager { private let cliendId = "" private lazy var authURL = URL(string: "https://github.com/login/oauth/authorize?client_id=\(cliendId)&scope=user:email")! private let callbackUrlScheme = "issueTracker" - + var networkManager: Networkable - + init(networkManager: Networkable) { self.networkManager = networkManager } @@ -33,14 +33,13 @@ class OAuthManager { }) return authenticationSession } - + private func requestJWTToken(with code: String) { let query = URLQueryItem(name: "code", value: code) let endpoint = Endpoint(path: .login) guard let url = endpoint.url(queryItems: [query]) else { return } networkManager.request(url: url, decodableType: [String: String].self) { (token) in UserDefaults.standard.set(token, forKey: "token") - // 메인 이슈 화면으로 } } } diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift index 9b68275e3..98088b6a1 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift @@ -19,7 +19,7 @@ struct Milestone: Codable { let createdTime: String? let dueDate: String? let closedIssueCount, openedIssueCount: Int? - + enum CodingKeys: String, CodingKey { case id, title, description, closedIssueCount, openedIssueCount case createdTime = "created_time" diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift index c5e3654f4..2d7ac67b5 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift @@ -13,7 +13,7 @@ struct NewIssue { let labelsID: [Int] let milestoneID: Int let assigneeID: [Int] - + enum CodingKeys: String, CodingKey { case title, comment case labelsID = "labels_ids" diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/UserList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/UserList.swift index 87c5deea6..ebbbb1ccd 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/UserList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/UserList.swift @@ -15,7 +15,7 @@ struct User: Decodable { let id: Int let name: String let imageUrl: String - + enum CodingKeys: String, CodingKey { case id, name case imageUrl = "image_url" From f0b4166e4000e6a0149f05b71bfe41d4faec9bdc Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 12:36:53 +0900 Subject: [PATCH 040/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20SwiftLint=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/AddLabelTableViewCell.swift | 9 +++++---- iOS/issue-tracker/issue-tracker/AddTableViewCell.swift | 7 ++++--- .../Controller/IssueFilterViewController.swift | 4 ++-- .../issue-tracker/Controller/LabelViewController.swift | 4 ++-- .../issue-tracker/Controller/LoginViewController.swift | 2 +- .../Controller/MilestoneViewController.swift | 2 +- iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift | 4 ++-- .../issue-tracker/Supporting Files/AppDelegate.swift | 3 +-- .../issue-tracker/Supporting Files/SceneDelegate.swift | 4 ---- .../issue-tracker/View/IssueListViewController.swift | 6 +++--- 10 files changed, 21 insertions(+), 24 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift b/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift index eb76784fc..25f72462a 100644 --- a/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift @@ -9,25 +9,26 @@ import UIKit import RxSwift class AddLabelTableViewCell: UITableViewCell { - + static let identifier = "AddLabelTableViewCell" - + let textField = UITextField() let reloadButton: UIButton = { var button = UIButton() button.setBackgroundImage(UIImage(systemName: "gobackward"), for: .normal) return button }() - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) contentView.addSubview(textField) } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func configureTextFieldPlaceHolder(text: String) { self.textField.placeholder = text } diff --git a/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift index f9b090901..efb021ebe 100644 --- a/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift @@ -8,19 +8,20 @@ import UIKit class AddTableViewCell: UITableViewCell { - + static let reuseIdentifier = "AddTableViewCell" var textField = UITextField() - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) contentView.addSubview(textField) } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func bind(_ completion: (UITextField) -> Void) { completion(textField) } diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift index f8d7aa9df..13af37da2 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift @@ -20,10 +20,10 @@ class IssueFilterViewController: UIViewController { var authors = ["Zeke", "Soo"] private let issueFilter = ["열린 이슈", "내가 작성한 이슈", "나에게 할당된 이슈", "내가 댓글을 남긴 이슈", "닫힌 이슈"] private let issueLabels = ["레이블 없음", "bug", "feature"] - + override func viewDidLoad() { super.viewDidLoad() - + navigationItem.title = "필터" self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "취소", style: .plain, diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index c454e2b7e..380064f64 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -12,11 +12,11 @@ import RxCocoa class LabelViewController: UIViewController { @IBOutlet weak var labelTableView: UITableView! - + var labelListViewModel = LabelListViewModel(networkManager: NetworkManager()) var addLabelButton = AddLabelButton() var bag = DisposeBag() - + override func viewDidLoad() { super.viewDidLoad() seuptNavigationBar() diff --git a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift index 35587aad0..7e71842d9 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift @@ -8,7 +8,7 @@ import UIKit class LoginViewController: UIViewController { - + override func viewDidLoad() { super.viewDidLoad() } diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index 81c7c60b8..841b779f6 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -18,7 +18,7 @@ class MilestoneViewController: UIViewController { tableView.register(MilestoneTableViewCell.self, forCellReuseIdentifier: MilestoneTableViewCell.reuseId) return tableView }() - + override func viewDidLoad() { super.viewDidLoad() diff --git a/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift index d8136793f..0b174a65e 100644 --- a/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift @@ -10,8 +10,8 @@ import RxSwift class MilestoneViewModel { var subject: Observable<[Milestone]> = Observable.just([]) - var model:[String: String] = ["제목": "", "설명": "", "완료일": ""] - + var model: [String: String] = ["제목": "", "설명": "", "완료일": ""] + init() { let urlString = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/milestone" let url = URL(string: urlString)! diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/AppDelegate.swift b/iOS/issue-tracker/issue-tracker/Supporting Files/AppDelegate.swift index 426cd8038..74efe1035 100644 --- a/iOS/issue-tracker/issue-tracker/Supporting Files/AppDelegate.swift +++ b/iOS/issue-tracker/issue-tracker/Supporting Files/AppDelegate.swift @@ -9,9 +9,8 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } } - diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift b/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift index be86411a9..b478923f7 100644 --- a/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift +++ b/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift @@ -11,8 +11,4 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - guard let _ = (scene as? UIWindowScene) else { return } - } } - diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index c4a409df6..401a69f42 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -11,7 +11,7 @@ import SnapKit class IssueListViewController: UIViewController { @IBOutlet weak var issueTableView: UITableView! - + let addIssueButton = AddIssueButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64)) let filterBarButton = FilterBarButton() let selectBarButton = SelectBarButton() @@ -23,7 +23,7 @@ class IssueListViewController: UIViewController { searchController.searchBar.showsBookmarkButton = true return searchController }() - + override func viewDidLoad() { super.viewDidLoad() setupNavigationItem() @@ -44,7 +44,7 @@ class IssueListViewController: UIViewController { let controller = UINavigationController(rootViewController: IssueFilterViewController()) present(controller, animated: true) } - + @objc func selectButtonTapped() { navigationItem.leftBarButtonItem = nil navigationItem.rightBarButtonItem = UIBarButtonItem(customView: cancelButton) From 9032663b10ba2de46ad1dcc0032c4cf61634aa98 Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 15:30:48 +0900 Subject: [PATCH 041/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Controller?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=EC=9D=98=20=EC=A0=91=EA=B7=BC=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=20=EB=B3=80=EA=B2=BD=20(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/AddLabelViewController.swift | 14 ++++----- .../Controller/LabelViewController.swift | 16 +++++----- .../Controller/NewIssueViewController.swift | 6 ++-- .../View/IssueListViewController.swift | 30 +++++++++---------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index a656cd428..7ccbbe89e 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -10,17 +10,17 @@ import RxSwift import RxCocoa import SnapKit -class AddLabelViewController: UIViewController { - var addLabelViewModel: AddLabelViewModel! +final class AddLabelViewController: UIViewController { + private var addLabelViewModel: AddLabelViewModel! private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.allowsSelection = false tableView.register(AddLabelTableViewCell.self, forCellReuseIdentifier: AddLabelTableViewCell.identifier) return tableView }() - var estimatedLabelView = EstimatedLabelView() - var bag = DisposeBag() - var saveButton = UIBarButtonItem(title: "저장", style: .plain, target: nil, action: nil) + private var estimatedLabelView = EstimatedLabelView() + private var bag = DisposeBag() + private var saveButton = UIBarButtonItem(title: "저장", style: .plain, target: nil, action: nil) override func viewDidLoad() { super.viewDidLoad() @@ -41,7 +41,7 @@ class AddLabelViewController: UIViewController { binding() } - func binding() { + private func binding() { addLabelViewModel.color .map { UIColor.hexStringToUIColor(hex: $0)} .bind(to: estimatedLabelView.label.rx.backgroundColor) @@ -56,7 +56,7 @@ class AddLabelViewController: UIViewController { .disposed(by: bag) } - func configureAutolayout() { + private func configureAutolayout() { tableView.snp.makeConstraints { view in view.top.equalToSuperview().inset(40) view.leading.trailing.bottom.equalToSuperview() diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 380064f64..72f95f10d 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -9,13 +9,13 @@ import UIKit import RxSwift import RxCocoa -class LabelViewController: UIViewController { +final class LabelViewController: UIViewController { @IBOutlet weak var labelTableView: UITableView! - var labelListViewModel = LabelListViewModel(networkManager: NetworkManager()) - var addLabelButton = AddLabelButton() - var bag = DisposeBag() + private var labelListViewModel = LabelListViewModel(networkManager: NetworkManager()) + private var addLabelButton = AddLabelButton() + private var bag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() @@ -26,11 +26,11 @@ class LabelViewController: UIViewController { bindTableView() } - func fetchLabel() { + private func fetchLabel() { labelListViewModel.fetchLabelList() } - func bindTableView() { + private func bindTableView() { labelListViewModel.labelList.bind(to: labelTableView.rx.items) { tableView, _, element in guard let cell = tableView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell()} cell.setupLabelCell(title: element.title, description: element.description!, color: element.color) @@ -39,13 +39,13 @@ class LabelViewController: UIViewController { .disposed(by: bag) } - @objc func addLabelButtonTapped() { + @objc private func addLabelButtonTapped() { let viewController = AddLabelViewController() let navigationViewController = UINavigationController(rootViewController: viewController) present(navigationViewController, animated: true, completion: nil) } - func seuptNavigationBar() { + private func seuptNavigationBar() { navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "레이블" navigationItem.rightBarButtonItem = UIBarButtonItem(customView: addLabelButton) diff --git a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift index bb4910682..548a63f2a 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift @@ -7,13 +7,13 @@ import UIKit import SnapKit -import MarkdownKit +//import MarkdownKit @IBDesignable class NewIssueViewController: UIViewController { private let additionalInfo = ["레이블", "마일스톤", "작성자"] private let cellReuseIdentifier = "NewIssueViewCell" - private let markdownParser = MarkdownParser() +// private let markdownParser = MarkdownParser() private let subject: UILabel = { let label = UILabel() @@ -87,7 +87,7 @@ class NewIssueViewController: UIViewController { textView?.text = textString case 1: textString = textView?.text - textView?.attributedText = markdownParser.parse(textView!.text) +// textView?.attributedText = markdownParser.parse(textView!.text) default: break } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 401a69f42..5230e4df6 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -8,16 +8,16 @@ import UIKit import SnapKit -class IssueListViewController: UIViewController { +final class IssueListViewController: UIViewController { @IBOutlet weak var issueTableView: UITableView! - let addIssueButton = AddIssueButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64)) - let filterBarButton = FilterBarButton() - let selectBarButton = SelectBarButton() - let cancelButton = CancelButton() - let issueToolbar = IssueToolbar(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) - let searchController: UISearchController = { + private let addIssueButton = AddIssueButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64)) + private let filterBarButton = FilterBarButton() + private let selectBarButton = SelectBarButton() + private let cancelButton = CancelButton() + private let issueToolbar = IssueToolbar(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) + private let searchController: UISearchController = { var searchController = UISearchController(searchResultsController: nil) searchController.searchBar.setImage(UIImage(systemName: "mic.fill"), for: .bookmark, state: .normal) searchController.searchBar.showsBookmarkButton = true @@ -40,12 +40,12 @@ class IssueListViewController: UIViewController { tabBarController?.tabBar.isHidden = false } - @objc func filterButtonTapped() { + @objc private func filterButtonTapped() { let controller = UINavigationController(rootViewController: IssueFilterViewController()) present(controller, animated: true) } - @objc func selectButtonTapped() { + @objc private func selectButtonTapped() { navigationItem.leftBarButtonItem = nil navigationItem.rightBarButtonItem = UIBarButtonItem(customView: cancelButton) navigationItem.title = "이슈 선택" @@ -55,18 +55,18 @@ class IssueListViewController: UIViewController { setupToolbarAutoulayout() } - @objc func addIssueButtonTapped() { + @objc private func addIssueButtonTapped() { let controller = NewIssueViewController() navigationController?.pushViewController(controller, animated: true) } - @objc func cancelButtonTapped() { + @objc private func cancelButtonTapped() { setupNavigationItem() tabBarController?.tabBar.isHidden = false issueToolbar.removeFromSuperview() } - func setupAddIssueButtonAutolayout() { + private func setupAddIssueButtonAutolayout() { view.addSubview(addIssueButton) addIssueButton.snp.makeConstraints { button in button.width.height.equalTo(64) @@ -75,7 +75,7 @@ class IssueListViewController: UIViewController { } } - func setupToolbarAutoulayout() { + private func setupToolbarAutoulayout() { issueToolbar.snp.makeConstraints { toolbar in toolbar.leading.trailing.equalToSuperview() toolbar.bottom.equalTo(view.safeAreaLayoutGuide) @@ -83,7 +83,7 @@ class IssueListViewController: UIViewController { } } - func setupNavigationItem() { + private func setupNavigationItem() { navigationController?.navigationBar.prefersLargeTitles = true navigationItem.title = "이슈" navigationItem.leftBarButtonItem = UIBarButtonItem(customView: filterBarButton) @@ -91,7 +91,7 @@ class IssueListViewController: UIViewController { navigationItem.searchController = searchController } - func setupIssueTableView() { + private func setupIssueTableView() { issueTableView.register(IssueTableViewCell.self, forCellReuseIdentifier: IssueTableViewCell.identifier) issueTableView.allowsMultipleSelection = true issueTableView.dataSource = self From fca1a55f63e0af708b67617e7bbbefe79061d26a Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 15:58:53 +0900 Subject: [PATCH 042/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20View=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EC=9D=98=20=EC=A0=91=EA=B7=BC=EC=A0=9C=EC=96=B4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LabelsCollectionViewCell.swift | 6 +++--- .../Controller/IssueTableFooterView.swift | 6 +++--- .../Controller/IssueToolbar.swift | 12 ++++++------ .../View/IssueList/AddIssueButton.swift | 4 ++-- .../View/IssueList/CancelButton.swift | 2 +- .../View/IssueList/FilterBarButton.swift | 2 +- .../View/IssueList/IssueTableViewCell.swift | 18 +++++++++--------- .../View/IssueList/LabelsCollectionView.swift | 4 ++-- .../View/IssueList/MilestoneView.swift | 10 +++++----- .../View/IssueList/SelectBarButton.swift | 2 +- .../View/IssueList/ToolBarTextField.swift | 4 ++-- .../View/Label/AddLabelButton.swift | 2 +- .../View/Label/AddTableViewCell.swift | 6 +++--- .../View/Label/EstimatedLabelView.swift | 10 +++++++--- .../View/Label/LabelTableViewCell.swift | 10 +++++----- .../View/Label/PaddingLabel.swift | 10 +++++----- 16 files changed, 56 insertions(+), 52 deletions(-) diff --git a/iOS/issue-tracker/LabelsCollectionViewCell.swift b/iOS/issue-tracker/LabelsCollectionViewCell.swift index 1ca14160d..40b00ab36 100644 --- a/iOS/issue-tracker/LabelsCollectionViewCell.swift +++ b/iOS/issue-tracker/LabelsCollectionViewCell.swift @@ -8,10 +8,10 @@ import UIKit import SnapKit -class LabelsCollectionViewCell: UICollectionViewCell { +final class LabelsCollectionViewCell: UICollectionViewCell { static var identifiers = "LabelsCollectionViewCell" - let label: PaddingLabel = { + private let label: PaddingLabel = { var label = PaddingLabel(withInsets: 0, 0, 10, 10) label.textAlignment = .center label.textColor = .white @@ -32,7 +32,7 @@ class LabelsCollectionViewCell: UICollectionViewCell { setupAutolayout() } - func setupAutolayout() { + private func setupAutolayout() { label.snp.makeConstraints { label in label.edges.equalToSuperview() } diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift index 923d986b5..59341005d 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueTableFooterView.swift @@ -8,9 +8,9 @@ import UIKit import SnapKit -class IssueTableFooterView: UIView { +final class IssueTableFooterView: UIView { - var label: UILabel = { + private var label: UILabel = { var label = UILabel() label.text = "아래로 당기면 검색바가 보여요!👀" label.textColor = .lightGray @@ -31,7 +31,7 @@ class IssueTableFooterView: UIView { setupAutolayout() } - func setupAutolayout() { + private func setupAutolayout() { label.snp.makeConstraints { label in label.centerX.equalToSuperview() label.top.equalTo(39) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift index 1703b4c9f..ecd7b4150 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift @@ -7,28 +7,28 @@ import UIKit -class IssueToolbar: UIToolbar { +final class IssueToolbar: UIToolbar { - let checkBoxBarButtonItem: UIBarButtonItem = { + private let checkBoxBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.image = UIImage(systemName: "checkmark.circle") return item }() - let closeIssueBarButtonItem: UIBarButtonItem = { + private let closeIssueBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.image = UIImage(systemName: "archivebox") return item }() - let labelBarButtonItem: UIBarButtonItem = { + private let labelBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.title = "이슈를 선택하세요" item.isEnabled = false return item }() - let flexibleBarButtonItem: UIBarButtonItem = { + private let flexibleBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) return item }() @@ -43,7 +43,7 @@ class IssueToolbar: UIToolbar { setupToolbar() } - func setupToolbar() { + private func setupToolbar() { let items = [checkBoxBarButtonItem, flexibleBarButtonItem, labelBarButtonItem, flexibleBarButtonItem, closeIssueBarButtonItem] setItems(items, animated: false) } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift index 96bdcfb59..016ac585d 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/AddIssueButton.swift @@ -7,7 +7,7 @@ import UIKit -class AddIssueButton: UIView { +final class AddIssueButton: UIView { override init(frame: CGRect) { super.init(frame: frame) @@ -30,7 +30,7 @@ class AddIssueButton: UIView { path.stroke() } - func setupButton() { + private func setupButton() { clipsToBounds = true layer.cornerRadius = self.bounds.size.width * 0.5 backgroundColor = .systemBlue diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift index ab3f23d22..d16b376ad 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/CancelButton.swift @@ -7,7 +7,7 @@ import UIKit -class CancelButton: UIButton { +final class CancelButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) setTitle("취소", for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift index 76c59aaac..295f4cb5f 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/FilterBarButton.swift @@ -7,7 +7,7 @@ import UIKit -class FilterBarButton: UIButton { +final class FilterBarButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) setImage(UIImage(systemName: "doc.text.magnifyingglass"), for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index 1c46006eb..6653aeab4 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -8,35 +8,35 @@ import UIKit import SnapKit -class IssueTableViewCell: UITableViewCell { +final class IssueTableViewCell: UITableViewCell { static var identifier = "IssueTableViewCell" - var fakeData = [IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85")] + private var fakeData = [IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85")] - var largeTitle: UILabel = { + private var largeTitle: UILabel = { var label = UILabel() label.font = UIFont.boldSystemFont(ofSize: 22) return label }() - var labelDescription: UILabel = { + private var labelDescription: UILabel = { var label = UILabel() label.textColor = .lightGray return label }() - var milestoneView: MilestoneView = { + private var milestoneView: MilestoneView = { var milestone = MilestoneView() return milestone }() - var labelsCollectionView: LabelsCollectionView = { + private var labelsCollectionView: LabelsCollectionView = { var collectionView = LabelsCollectionView() return collectionView }() - var checkBoxImageView: UIImageView = { + private var checkBoxImageView: UIImageView = { var imageView = UIImageView() imageView.image = UIImage(systemName: "checkmark.circle.fill") return imageView @@ -58,7 +58,7 @@ class IssueTableViewCell: UITableViewCell { checkBoxImageView.isHidden = true } - func addSubviews() { + private func addSubviews() { addSubview(labelsCollectionView) addSubview(labelDescription) addSubview(milestoneView) @@ -66,7 +66,7 @@ class IssueTableViewCell: UITableViewCell { addSubview(checkBoxImageView) } - func setupAutolayout() { + private func setupAutolayout() { largeTitle.snp.makeConstraints { title in title.top.equalTo(24) title.leading.trailing.equalTo(16) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift index 85f60421a..a6ba7698b 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift @@ -7,9 +7,9 @@ import UIKit -class LabelsCollectionView: UICollectionView { +final class LabelsCollectionView: UICollectionView { - var labelsLayout: UICollectionViewFlowLayout = { + private var labelsLayout: UICollectionViewFlowLayout = { var layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.estimatedItemSize = CGSize(width: 84, height: 22) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift index 509f097c9..6d60f8c57 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/MilestoneView.swift @@ -8,14 +8,14 @@ import UIKit import SnapKit -class MilestoneView: UIView { - var sfsymbolImageView: UIImageView = { +final class MilestoneView: UIView { + private var sfsymbolImageView: UIImageView = { var imageView = UIImageView() imageView.image = UIImage(named: "vector") return imageView }() - var milestoneTitle: UILabel = { + private var milestoneTitle: UILabel = { var label = UILabel() label.textColor = .lightGray return label @@ -33,12 +33,12 @@ class MilestoneView: UIView { setupAutolayout() } - func addSubviews() { + private func addSubviews() { addSubview(sfsymbolImageView) addSubview(milestoneTitle) } - func setupAutolayout() { + private func setupAutolayout() { sfsymbolImageView.snp.makeConstraints { imageView in imageView.top.leading.bottom.equalToSuperview() imageView.width.equalTo(sfsymbolImageView.snp.height).multipliedBy(1) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift index 3afb89f59..c6f76857d 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/SelectBarButton.swift @@ -7,7 +7,7 @@ import UIKit -class SelectBarButton: UIButton { +final class SelectBarButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) setImage(UIImage(systemName: "checkmark.circle"), for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift index 6adff1774..fb3dcfc57 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift @@ -7,9 +7,9 @@ import UIKit -class ToolBarTextField: UITextField { +final class ToolBarTextField: UITextField { - let sendButton: UIButton = { + private let sendButton: UIButton = { let button = UIButton() button.setImage(UIImage(systemName: "arrow.up.circle.fill"), for: .normal) return button diff --git a/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift b/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift index cee6d7709..9ad00bd62 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/AddLabelButton.swift @@ -7,7 +7,7 @@ import UIKit -class AddLabelButton: UIButton { +final class AddLabelButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) setImage(UIImage(systemName: "plus"), for: .normal) diff --git a/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift index ebcfdb112..e723c0abc 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift @@ -8,12 +8,12 @@ import UIKit import RxSwift -class AddTableViewCell: UITableViewCell { +final class AddTableViewCell: UITableViewCell { static let identifier = "AddTableViewCell" - let textField = UITextField() - let reloadButton: UIButton = { + private let textField = UITextField() + private let reloadButton: UIButton = { var button = UIButton() button.setBackgroundImage(UIImage(systemName: "gobackward"), for: .normal) return button diff --git a/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift b/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift index 7d6cba63e..ba6972892 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/EstimatedLabelView.swift @@ -8,9 +8,9 @@ import UIKit import SnapKit -class EstimatedLabelView: UIView { +final class EstimatedLabelView: UIView { - var label: PaddingLabel = { + private var label: PaddingLabel = { var label = PaddingLabel(withInsets: 0, 0, 10, 10) label.text = "레이블" label.layer.masksToBounds = true @@ -31,7 +31,7 @@ class EstimatedLabelView: UIView { super.init(coder: coder) } - func configureLabelAutolayout() { + private func configureLabelAutolayout() { label.snp.makeConstraints { label in label.centerX.centerY.equalToSuperview() } @@ -45,4 +45,8 @@ class EstimatedLabelView: UIView { func setupLabelColor(color: UIColor) { self.label.backgroundColor = color } + + func getLabel() -> PaddingLabel { + return self.label + } } diff --git a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift index b03bf313a..4ca176425 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift @@ -8,11 +8,11 @@ import UIKit import SnapKit -class LabelTableViewCell: UITableViewCell { +final class LabelTableViewCell: UITableViewCell { static var identifier = "LabelTableViewCell" - var labelView: PaddingLabel = { + private var labelView: PaddingLabel = { var label = PaddingLabel(withInsets: 0, 0, 10, 10) label.textAlignment = .center label.textColor = .white @@ -21,7 +21,7 @@ class LabelTableViewCell: UITableViewCell { return label }() - var labelDescription: UILabel = { + private var labelDescription: UILabel = { var label = UILabel() return label }() @@ -38,12 +38,12 @@ class LabelTableViewCell: UITableViewCell { setupAutolayout() } - func addSubviews() { + private func addSubviews() { addSubview(labelView) addSubview(labelDescription) } - func setupAutolayout() { + private func setupAutolayout() { labelView.snp.makeConstraints { view in view.top.equalToSuperview().offset(24) view.left.equalToSuperview().offset(16) diff --git a/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift b/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift index 4b9a8a9a7..8178201e9 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/PaddingLabel.swift @@ -7,12 +7,12 @@ import UIKit -class PaddingLabel: UILabel { +final class PaddingLabel: UILabel { - var topInset: CGFloat - var bottomInset: CGFloat - var leftInset: CGFloat - var rightInset: CGFloat + private var topInset: CGFloat + private var bottomInset: CGFloat + private var leftInset: CGFloat + private var rightInset: CGFloat required init(withInsets top: CGFloat, _ bottom: CGFloat, _ left: CGFloat, _ right: CGFloat) { self.topInset = top From c82fddd6caf8cb2da9e27250720faa426205bf2b Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 18 Jun 2021 15:59:23 +0900 Subject: [PATCH 043/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20getLabel()?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Controller/AddLabelViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index 7ccbbe89e..bebf055ce 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -44,7 +44,7 @@ final class AddLabelViewController: UIViewController { private func binding() { addLabelViewModel.color .map { UIColor.hexStringToUIColor(hex: $0)} - .bind(to: estimatedLabelView.label.rx.backgroundColor) + .bind(to: estimatedLabelView.getLabel().rx.backgroundColor) .disposed(by: bag) saveButton.rx.tap From 3c19dd9290335a98f1ee9f582dae9db666ed70ff Mon Sep 17 00:00:00 2001 From: zeke Date: Sat, 19 Jun 2021 19:26:48 +0900 Subject: [PATCH 044/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20TableViewCell?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=ED=8F=B4=EB=8D=94=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj | 6 +++--- .../issue-tracker/Controller/NewIssueViewController.swift | 6 +++--- .../{ => View/Label}/AddLabelTableViewCell.swift | 0 .../{ => View/Milestone}/AddTableViewCell.swift | 0 .../issue-tracker/{ => ViewModel}/MilestoneViewModel.swift | 0 5 files changed, 6 insertions(+), 6 deletions(-) rename iOS/issue-tracker/issue-tracker/{ => View/Label}/AddLabelTableViewCell.swift (100%) rename iOS/issue-tracker/issue-tracker/{ => View/Milestone}/AddTableViewCell.swift (100%) rename iOS/issue-tracker/issue-tracker/{ => ViewModel}/MilestoneViewModel.swift (100%) diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 76f883d31..91fc6a083 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -244,6 +244,7 @@ children = ( B3A5CB57267840290060DC85 /* LabelListViewModel.swift */, B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */, + D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -285,9 +286,6 @@ D0ADB69D266F0E7900E0762C /* Model */, D0A88E102676563E005877F6 /* Modal.storyboard */, D03AF8F126771FA3001C2CBF /* Login.storyboard */, - D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, - D04C694F267A389D0056DD79 /* AddTableViewCell.swift */, - B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */, ); path = "issue-tracker"; sourceTree = ""; @@ -386,6 +384,7 @@ isa = PBXGroup; children = ( D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */, + D04C694F267A389D0056DD79 /* AddTableViewCell.swift */, ); path = Milestone; sourceTree = ""; @@ -398,6 +397,7 @@ B3F5275B2670C0EF002B0812 /* PaddingLabel.swift */, B3E01197267A29E8001155D4 /* AddTableViewCell.swift */, B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */, + B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */, ); path = Label; sourceTree = ""; diff --git a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift index 548a63f2a..bb4910682 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift @@ -7,13 +7,13 @@ import UIKit import SnapKit -//import MarkdownKit +import MarkdownKit @IBDesignable class NewIssueViewController: UIViewController { private let additionalInfo = ["레이블", "마일스톤", "작성자"] private let cellReuseIdentifier = "NewIssueViewCell" -// private let markdownParser = MarkdownParser() + private let markdownParser = MarkdownParser() private let subject: UILabel = { let label = UILabel() @@ -87,7 +87,7 @@ class NewIssueViewController: UIViewController { textView?.text = textString case 1: textString = textView?.text -// textView?.attributedText = markdownParser.parse(textView!.text) + textView?.attributedText = markdownParser.parse(textView!.text) default: break } diff --git a/iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/AddLabelTableViewCell.swift similarity index 100% rename from iOS/issue-tracker/issue-tracker/AddLabelTableViewCell.swift rename to iOS/issue-tracker/issue-tracker/View/Label/AddLabelTableViewCell.swift diff --git a/iOS/issue-tracker/issue-tracker/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Milestone/AddTableViewCell.swift similarity index 100% rename from iOS/issue-tracker/issue-tracker/AddTableViewCell.swift rename to iOS/issue-tracker/issue-tracker/View/Milestone/AddTableViewCell.swift diff --git a/iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift similarity index 100% rename from iOS/issue-tracker/issue-tracker/MilestoneViewModel.swift rename to iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift From 4ddc279c76965d2239edc54b72ea3bd45b256a42 Mon Sep 17 00:00:00 2001 From: zeke Date: Sat, 19 Jun 2021 19:50:48 +0900 Subject: [PATCH 045/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=BA=A1?= =?UTF-8?q?=EC=B3=90=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/ViewModel/AddLabelViewModel.swift | 1 - .../issue-tracker/ViewModel/LabelListViewModel.swift | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift index 022fb4987..768dd49a8 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift @@ -6,7 +6,6 @@ // import Foundation -import RxSwift import RxCocoa class AddLabelViewModel { diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift index 517f28878..ec75f3f95 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift @@ -6,13 +6,11 @@ // import Foundation -import RxSwift import RxCocoa class LabelListViewModel { let networkManager: NetworkManager var labelList = BehaviorRelay<[IssueLabel]>(value: []) - var bag = DisposeBag() init(networkManager: NetworkManager) { self.networkManager = networkManager @@ -20,9 +18,8 @@ class LabelListViewModel { } func fetchLabelList() { - networkManager.request(url: Endpoint(path: .label).url()!, decodableType: LabelList.self) { label in - self.labelList.accept(label.data) + networkManager.request(url: Endpoint(path: .label).url()!, decodableType: LabelList.self) { [weak self] label in + self?.labelList.accept(label.data) } } - } From 22b3643f9a08f03329edb886aa2c6d204c018d30 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 14:57:59 +0900 Subject: [PATCH 046/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 6 --- .../View/Label/AddTableViewCell.swift | 40 ------------------- .../View/Milestone/AddTableViewCell.swift | 28 ------------- 3 files changed, 74 deletions(-) delete mode 100644 iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift delete mode 100644 iOS/issue-tracker/issue-tracker/View/Milestone/AddTableViewCell.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 91fc6a083..077f8d913 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -53,7 +53,6 @@ D03AF8F826774909001C2CBF /* MilestoneList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F726774909001C2CBF /* MilestoneList.swift */; }; D03AF8FA2677494F001C2CBF /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F92677494F001C2CBF /* Comment.swift */; }; D04C694E267A35960056DD79 /* MilestoneViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C694D267A35960056DD79 /* MilestoneViewModel.swift */; }; - D04C6950267A389D0056DD79 /* AddTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C694F267A389D0056DD79 /* AddTableViewCell.swift */; }; D04C6956267B2C560056DD79 /* ToolBarTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C6955267B2C560056DD79 /* ToolBarTextField.swift */; }; D0A88E0226732D21005877F6 /* NewIssueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E0126732D21005877F6 /* NewIssueViewController.swift */; }; D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */; }; @@ -132,7 +131,6 @@ B3D7D0C3267735CC000F02F4 /* IssueList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueList.swift; sourceTree = ""; }; B3D7D0C826773AEA000F02F4 /* NewIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssue.swift; sourceTree = ""; }; B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddLabelViewModel.swift; sourceTree = ""; }; - B3E01197267A29E8001155D4 /* AddTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTableViewCell.swift; sourceTree = ""; }; B3EADABD2675EFED0007C4B6 /* AddLabelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLabelButton.swift; sourceTree = ""; }; B3EADABF267628FB0007C4B6 /* LabelsCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsCollectionView.swift; sourceTree = ""; }; B3EADAC1267629190007C4B6 /* LabelsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelsCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; @@ -149,7 +147,6 @@ D03AF8F726774909001C2CBF /* MilestoneList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneList.swift; sourceTree = ""; }; D03AF8F92677494F001C2CBF /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; D04C694D267A35960056DD79 /* MilestoneViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewModel.swift; sourceTree = ""; }; - D04C694F267A389D0056DD79 /* AddTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTableViewCell.swift; sourceTree = ""; }; D04C6955267B2C560056DD79 /* ToolBarTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolBarTextField.swift; sourceTree = ""; }; D0A88E0126732D21005877F6 /* NewIssueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssueViewController.swift; sourceTree = ""; }; D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailViewController.swift; sourceTree = ""; }; @@ -384,7 +381,6 @@ isa = PBXGroup; children = ( D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */, - D04C694F267A389D0056DD79 /* AddTableViewCell.swift */, ); path = Milestone; sourceTree = ""; @@ -395,7 +391,6 @@ D0FD50F62671B156008C6031 /* LabelTableViewCell.swift */, B3EADABD2675EFED0007C4B6 /* AddLabelButton.swift */, B3F5275B2670C0EF002B0812 /* PaddingLabel.swift */, - B3E01197267A29E8001155D4 /* AddTableViewCell.swift */, B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */, B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */, ); @@ -683,7 +678,6 @@ 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */, B3F527552670A0CD002B0812 /* UIColor+HexInit.swift in Sources */, - D04C6950267A389D0056DD79 /* AddTableViewCell.swift in Sources */, D03AF8F626774707001C2CBF /* UserList.swift in Sources */, B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */, D0A88E04267334A6005877F6 /* IssueDetailViewController.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift deleted file mode 100644 index e723c0abc..000000000 --- a/iOS/issue-tracker/issue-tracker/View/Label/AddTableViewCell.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// AddTableViewCell.swift -// issue-tracker -// -// Created by 양준혁 on 2021/06/16. -// - -import UIKit -import RxSwift - -final class AddTableViewCell: UITableViewCell { - - static let identifier = "AddTableViewCell" - - private let textField = UITextField() - private let reloadButton: UIButton = { - var button = UIButton() - button.setBackgroundImage(UIImage(systemName: "gobackward"), for: .normal) - return button - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) - contentView.addSubview(textField) - } - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configureTextFieldPlaceHolder(text: String) { - self.textField.placeholder = text - } - - func configureBackgroundCellMode() { - self.textField.text = "#3DDCFF" - self.textField.rightView = reloadButton - self.textField.rightViewMode = .always - } -} diff --git a/iOS/issue-tracker/issue-tracker/View/Milestone/AddTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Milestone/AddTableViewCell.swift deleted file mode 100644 index efb021ebe..000000000 --- a/iOS/issue-tracker/issue-tracker/View/Milestone/AddTableViewCell.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// AddTableViewCell.swift -// issue-tracker -// -// Created by Ador on 2021/06/16. -// - -import UIKit - -class AddTableViewCell: UITableViewCell { - - static let reuseIdentifier = "AddTableViewCell" - var textField = UITextField() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - textField.frame = CGRect(x: self.frame.origin.x + 120, y: self.frame.origin.y, width: self.frame.width - 140, height: 44) - contentView.addSubview(textField) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func bind(_ completion: (UITextField) -> Void) { - completion(textField) - } -} From e9b8f1664b2eee807e1a294c93cba2d9adec6e05 Mon Sep 17 00:00:00 2001 From: lena Date: Mon, 21 Jun 2021 15:19:38 +0900 Subject: [PATCH 047/104] chore: add milestone table view cell --- .../issue-tracker.xcodeproj/project.pbxproj | 4 +++ .../AddMilestoneViewController.swift | 4 +-- .../Milestone/AddMilestoneTableViewCell.swift | 29 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/View/Milestone/AddMilestoneTableViewCell.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 077f8d913..a0286e21c 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ B3FBA7B726730B9A0006E5E6 /* IssueToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FBA7B626730B9A0006E5E6 /* IssueToolbar.swift */; }; B3FBA7B9267335C30006E5E6 /* CancelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FBA7B8267335C30006E5E6 /* CancelButton.swift */; }; D0103BDE267B897E0079FC3D /* AddMilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0103BDD267B897E0079FC3D /* AddMilestoneViewController.swift */; }; + D0103BE0268066A80079FC3D /* AddMilestoneTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0103BDF268066A80079FC3D /* AddMilestoneTableViewCell.swift */; }; D03AF8F226771FA3001C2CBF /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D03AF8F126771FA3001C2CBF /* Login.storyboard */; }; D03AF8F42677253C001C2CBF /* IssueFilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */; }; D03AF8F626774707001C2CBF /* UserList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03AF8F526774707001C2CBF /* UserList.swift */; }; @@ -141,6 +142,7 @@ B3FBA7B8267335C30006E5E6 /* CancelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelButton.swift; sourceTree = ""; }; CCF0F131FB8BE0BC0EF7FE4E /* Pods-issue-tracker.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-issue-tracker.release.xcconfig"; path = "Target Support Files/Pods-issue-tracker/Pods-issue-tracker.release.xcconfig"; sourceTree = ""; }; D0103BDD267B897E0079FC3D /* AddMilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMilestoneViewController.swift; sourceTree = ""; }; + D0103BDF268066A80079FC3D /* AddMilestoneTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddMilestoneTableViewCell.swift; sourceTree = ""; }; D03AF8F126771FA3001C2CBF /* Login.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Login.storyboard; sourceTree = ""; }; D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IssueFilterViewController.swift; sourceTree = ""; }; D03AF8F526774707001C2CBF /* UserList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserList.swift; sourceTree = ""; }; @@ -380,6 +382,7 @@ D0FD50ED2671B0E8008C6031 /* Milestone */ = { isa = PBXGroup; children = ( + D0103BDF268066A80079FC3D /* AddMilestoneTableViewCell.swift */, D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */, ); path = Milestone; @@ -677,6 +680,7 @@ B34FF741267B8800002D9C56 /* AddLabelTableViewCell.swift in Sources */, 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, B349997D266F8D0B0091A44A /* GitHubLoginButton.swift in Sources */, + D0103BE0268066A80079FC3D /* AddMilestoneTableViewCell.swift in Sources */, B3F527552670A0CD002B0812 /* UIColor+HexInit.swift in Sources */, D03AF8F626774707001C2CBF /* UserList.swift in Sources */, B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index 9575b21f8..afc788237 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -14,7 +14,7 @@ class AddMilestoneViewController: UIViewController { private let viewModel: MilestoneViewModel! = MilestoneViewModel() private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) - tableView.register(AddTableViewCell.self, forCellReuseIdentifier: AddTableViewCell.reuseIdentifier) + tableView.register(AddMilestoneTableViewCell.self, forCellReuseIdentifier: AddMilestoneTableViewCell.reuseIdentifier) return tableView }() @@ -49,7 +49,7 @@ extension AddMilestoneViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: AddTableViewCell.reuseIdentifier, for: indexPath) as? AddTableViewCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: AddMilestoneTableViewCell.reuseIdentifier, for: indexPath) as? AddMilestoneTableViewCell else { fatalError() } if indexPath.row == 0 { diff --git a/iOS/issue-tracker/issue-tracker/View/Milestone/AddMilestoneTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Milestone/AddMilestoneTableViewCell.swift new file mode 100644 index 000000000..b8c84b5e7 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/View/Milestone/AddMilestoneTableViewCell.swift @@ -0,0 +1,29 @@ +// +// AddTableViewCell.swift +// issue-tracker +// +// Created by Ador on 2021/06/16. +// + +import UIKit + +class AddMilestoneTableViewCell: UITableViewCell { + + static let reuseIdentifier = "AddMilestoneTableViewCell" + var textField = UITextField() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + textField.frame = CGRect(x: self.frame.origin.x + 100, y: self.frame.origin.y, width: self.frame.width - 100, height: 44) + contentView.addSubview(textField) + } + required init?(coder: NSCoder) { + super.init(coder: coder) + textField.frame = CGRect(x: self.frame.origin.x + 100, y: self.frame.origin.y, width: self.frame.width - 100, height: 44) + contentView.addSubview(textField) + } + + func bind(_ completion: (UITextField) -> Void) { + completion(textField) + } +} From a2a3b14f654f263f0c4f1fbdd230b74eeafda02b Mon Sep 17 00:00:00 2001 From: lena Date: Mon, 21 Jun 2021 15:24:06 +0900 Subject: [PATCH 048/104] chore: update directory --- iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj | 2 +- .../View/{ => IssueList}/IssueDetailTableViewCell.swift | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename iOS/issue-tracker/issue-tracker/View/{ => IssueList}/IssueDetailTableViewCell.swift (100%) diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index a0286e21c..3cbad8a68 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -207,7 +207,6 @@ D0FD50F12671B108008C6031 /* Label */, D0FD50ED2671B0E8008C6031 /* Milestone */, B3F5274F26709614002B0812 /* Login */, - D0A88E082674F888005877F6 /* IssueDetailTableViewCell.swift */, ); path = View; sourceTree = ""; @@ -226,6 +225,7 @@ B32FEAC72671F65F00BF37A1 /* FilterBarButton.swift */, B3FBA7B8267335C30006E5E6 /* CancelButton.swift */, B32FEAC92671FD1600BF37A1 /* SelectBarButton.swift */, + D0A88E082674F888005877F6 /* IssueDetailTableViewCell.swift */, ); path = IssueList; sourceTree = ""; diff --git a/iOS/issue-tracker/issue-tracker/View/IssueDetailTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueDetailTableViewCell.swift similarity index 100% rename from iOS/issue-tracker/issue-tracker/View/IssueDetailTableViewCell.swift rename to iOS/issue-tracker/issue-tracker/View/IssueList/IssueDetailTableViewCell.swift From c06c8ec3e54c7e5a436f6908f99795515c199395 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:29:25 +0900 Subject: [PATCH 049/104] =?UTF-8?q?feat:=20=E2=9C=A8=20IssueListViewModel?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 4 +++ .../ViewModel/IssueListViewModel.swift | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 3cbad8a68..e45719963 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ B3B559ED266E096000901C55 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B3B559EB266E096000901C55 /* LaunchScreen.storyboard */; }; B3B559F8266E096000901C55 /* issue_trackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559F7266E096000901C55 /* issue_trackerTests.swift */; }; B3B55A03266E096000901C55 /* issue_trackerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B55A02266E096000901C55 /* issue_trackerUITests.swift */; }; + B3BA329A2680686E0009FE72 /* IssueListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BA32992680686E0009FE72 /* IssueListViewModel.swift */; }; B3CBD19B267C359500F8D733 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = B3CBD19A267C359500F8D733 /* .swiftlint.yml */; }; B3D7D0C4267735CC000F02F4 /* IssueList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D7D0C3267735CC000F02F4 /* IssueList.swift */; }; B3D7D0C926773AEA000F02F4 /* NewIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D7D0C826773AEA000F02F4 /* NewIssue.swift */; }; @@ -128,6 +129,7 @@ B3B559FE266E096000901C55 /* issue-trackerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "issue-trackerUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; B3B55A02266E096000901C55 /* issue_trackerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = issue_trackerUITests.swift; sourceTree = ""; }; B3B55A04266E096000901C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B3BA32992680686E0009FE72 /* IssueListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueListViewModel.swift; sourceTree = ""; }; B3CBD19A267C359500F8D733 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; B3D7D0C3267735CC000F02F4 /* IssueList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueList.swift; sourceTree = ""; }; B3D7D0C826773AEA000F02F4 /* NewIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssue.swift; sourceTree = ""; }; @@ -242,6 +244,7 @@ isa = PBXGroup; children = ( B3A5CB57267840290060DC85 /* LabelListViewModel.swift */, + B3BA32992680686E0009FE72 /* IssueListViewModel.swift */, B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */, D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, ); @@ -667,6 +670,7 @@ B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */, B3EADABE2675EFED0007C4B6 /* AddLabelButton.swift in Sources */, 491F08942679E5420081C5C5 /* AddLabelViewController.swift in Sources */, + B3BA329A2680686E0009FE72 /* IssueListViewModel.swift in Sources */, B32FEAC12671D9F600BF37A1 /* IssueListViewController.swift in Sources */, B32FEACA2671FD1600BF37A1 /* SelectBarButton.swift in Sources */, D0A88E0F26765325005877F6 /* UIKit+Extension.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift new file mode 100644 index 000000000..533e8ed3b --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -0,0 +1,32 @@ +// +// IssueListViewModel.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/21. +// + +import Foundation +import RxCocoa + +class IssueListViewModel { + + let issueList = BehaviorRelay<[Issue]>(value: []) + let networkManager: Networkable + var selectMode = BehaviorRelay(value: false) + + init(networkManager: Networkable) { + self.networkManager = networkManager + } + + func fetchIssueList() { + networkManager.request(url: Endpoint(path: .issue).url()!, decodableType: IssueList.self) { issueList in + self.issueList.accept(issueList.data) + } + } + + func deleteIssue(id: Int) { + networkManager.deleteRequest(url: Endpoint(path: .issue).url(id: 1)!) { + print("success") + } + } +} From 8a478d55ce43b1316a786f93e84d30202b3bcc39 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:30:11 +0900 Subject: [PATCH 050/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20IssueList=20DTO?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/UserDTO/IssueList.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift index 89b62dc78..caa45e68e 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift @@ -21,8 +21,8 @@ struct IssueDetail: Codable { struct Issue: Codable { let id: Int? let title: String - let issueNumber: Int - let isOpen: Bool + let number: Int + let open: Bool let createdTime: String let author: Author let label: [IssueLabel] @@ -33,8 +33,8 @@ struct Issue: Codable { enum CodingKeys: String, CodingKey { case id case title - case issueNumber = "issue_number" - case isOpen + case number + case open = "is_open" case createdTime = "created_time" case author, label, assignee, milestone, comment } From d4e7ffd5dc3c7ef2ec64847a0821f4025a87a9ad Mon Sep 17 00:00:00 2001 From: lena Date: Mon, 21 Jun 2021 23:31:20 +0900 Subject: [PATCH 051/104] feat: add new issue view model with rx --- .../issue-tracker.xcodeproj/project.pbxproj | 8 ++ .../Controller/NewIssueViewController.swift | 129 ++++++++++-------- .../Model/UserDTO/NewIssue.swift | 12 +- .../ViewModel/NewIssueViewModel.swift | 26 ++++ 4 files changed, 113 insertions(+), 62 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 3cbad8a68..9eb0f5069 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -68,6 +68,8 @@ D0ADB6D5266F4F3D00E0762C /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = D0ADB6D4266F4F3D00E0762C /* RxSwift */; }; D0ADB6D7266F4F4700E0762C /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = D0ADB6D6266F4F4700E0762C /* SnapKit */; }; D0ADB6F8266F5BBE00E0762C /* OAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ADB6F7266F5BBE00E0762C /* OAuthManager.swift */; }; + D0DAAE28268071F00075E794 /* NewIssueViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DAAE27268071F00075E794 /* NewIssueViewModel.swift */; }; + D0DAAE2A2680A8DE0075E794 /* AdditionalInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DAAE292680A8DE0075E794 /* AdditionalInfoViewController.swift */; }; D0FD509E26708DDE008C6031 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E4266E095E00901C55 /* LoginViewController.swift */; }; D0FD50AA267096C0008C6031 /* MilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */; }; D0FD50BB26709F8B008C6031 /* MilestoneTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */; }; @@ -159,6 +161,8 @@ D0ADB693266F0A5000E0762C /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; D0ADB698266F0C1300E0762C /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; D0ADB6F7266F5BBE00E0762C /* OAuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthManager.swift; sourceTree = ""; }; + D0DAAE27268071F00075E794 /* NewIssueViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssueViewModel.swift; sourceTree = ""; }; + D0DAAE292680A8DE0075E794 /* AdditionalInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdditionalInfoViewController.swift; sourceTree = ""; }; D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewController.swift; sourceTree = ""; }; D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneTableViewCell.swift; sourceTree = ""; }; D0FD50DB2671AA04008C6031 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; @@ -244,6 +248,7 @@ B3A5CB57267840290060DC85 /* LabelListViewModel.swift */, B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */, D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, + D0DAAE27268071F00075E794 /* NewIssueViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -375,6 +380,7 @@ D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */, D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */, D0103BDD267B897E0079FC3D /* AddMilestoneViewController.swift */, + D0DAAE292680A8DE0075E794 /* AdditionalInfoViewController.swift */, ); path = Controller; sourceTree = ""; @@ -676,6 +682,7 @@ 49903DAB266F558300D2A6DD /* LoginView.swift in Sources */, D0FD50F72671B156008C6031 /* LabelTableViewCell.swift in Sources */, D04C6956267B2C560056DD79 /* ToolBarTextField.swift in Sources */, + D0DAAE28268071F00075E794 /* NewIssueViewModel.swift in Sources */, B32FEACE267260E400BF37A1 /* AddIssueButton.swift in Sources */, B34FF741267B8800002D9C56 /* AddLabelTableViewCell.swift in Sources */, 49903DAE266F588B00D2A6DD /* IDPasswordTextField.swift in Sources */, @@ -705,6 +712,7 @@ B32FEAC62671DDA600BF37A1 /* MilestoneView.swift in Sources */, B3EADAC0267628FB0007C4B6 /* LabelsCollectionView.swift in Sources */, B3EADAC2267629190007C4B6 /* LabelsCollectionViewCell.swift in Sources */, + D0DAAE2A2680A8DE0075E794 /* AdditionalInfoViewController.swift in Sources */, D0FD50AA267096C0008C6031 /* MilestoneViewController.swift in Sources */, D0FD50BB26709F8B008C6031 /* MilestoneTableViewCell.swift in Sources */, D03AF8F826774909001C2CBF /* MilestoneList.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift index bb4910682..2ea47b90f 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift @@ -8,12 +8,14 @@ import UIKit import SnapKit import MarkdownKit +import RxSwift -@IBDesignable class NewIssueViewController: UIViewController { - private let additionalInfo = ["레이블", "마일스톤", "작성자"] + private let additionalInfo = ["레이블", "마일스톤", "담당자"] private let cellReuseIdentifier = "NewIssueViewCell" private let markdownParser = MarkdownParser() + private var viewModel = NewIssueViewModel() + private let disposeBag = DisposeBag() private let subject: UILabel = { let label = UILabel() @@ -21,21 +23,29 @@ class NewIssueViewController: UIViewController { return label }() - private let textField: UITextField = { + private let subjectTextField: UITextField = { let textField = UITextField() + textField.becomeFirstResponder() textField.placeholder = "제목을 입력하세요." return textField }() - private lazy var tableView: UITableView = { + private let tableView: UITableView = { let tableView = UITableView() + tableView.rowHeight = 44.0 tableView.isScrollEnabled = false - tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "NewIssueViewCell") return tableView }() + private let saveButton = UIBarButtonItem(title: "저장", style: .plain, target: self, action: #selector(didTapSave)) private let segmentedControl = UISegmentedControl(items: ["마크다운", "미리보기"]) - private var textView: UITextView? + private let contentTextView: UITextView = { + let textView = UITextView() + textView.text = "코멘트는 여기에 입력하세요." + return textView + }() + private var textString: String? override func viewDidLoad() { @@ -45,49 +55,84 @@ class NewIssueViewController: UIViewController { tableView.dataSource = self tableView.delegate = self - let save = UIBarButtonItem(title: "저장", style: .plain, target: self, action: nil) - navigationItem.rightBarButtonItem = save + navigationItem.largeTitleDisplayMode = .never + navigationItem.rightBarButtonItem = saveButton navigationItem.titleView = segmentedControl + segmentedControl.selectedSegmentIndex = 0 segmentedControl.addTarget(self, action: #selector(reload), for: .valueChanged) view.addSubview(subject) - view.addSubview(textField) + view.addSubview(subjectTextField) + view.addSubview(contentTextView) view.addSubview(tableView) + + setupAutolayout() + bind() } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - tabBarController?.tabBar.isHidden = true + private func bind() { + subjectTextField.rx.text + .orEmpty + .distinctUntilChanged() + .bind(onNext: { [unowned self] title in + viewModel.title = title + }) + .disposed(by: disposeBag) + + contentTextView.rx.text + .orEmpty + .distinctUntilChanged() + .bind(onNext: { [unowned self] text in + viewModel.content = text + }) + .disposed(by: disposeBag) } - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() + private func setupAutolayout() { subject.snp.makeConstraints { maker in maker.leading.top.equalTo(view.safeAreaLayoutGuide).inset(20) maker.width.equalTo(70) } - textField.snp.makeConstraints { maker in + subjectTextField.snp.makeConstraints { maker in maker.leading.equalTo(subject.snp.trailing) maker.trailing.equalTo(view.safeAreaLayoutGuide).inset(20) maker.height.equalTo(44) maker.centerY.equalTo(subject.snp.centerY) } - tableView.snp.makeConstraints { maker in - maker.top.equalTo(textField.snp.bottom) + contentTextView.snp.makeConstraints { maker in + maker.top.equalTo(subjectTextField.snp.bottom) maker.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(20) + maker.height.equalTo(440) + } + tableView.snp.makeConstraints { maker in + maker.top.equalTo(contentTextView.snp.bottom) + maker.leading.trailing.equalTo(view.safeAreaLayoutGuide) maker.bottom.equalTo(view.safeAreaLayoutGuide) } } + @objc + func didTapSave(sender: UIButton) { + guard let title = subjectTextField.text, let content = contentTextView.text, !title.isEmpty, !content.isEmpty else { + let alert = UIAlertController(title: "Oops!", message: "제목과 내용을 모두 입력해주세요!", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "확인", style: .default)) + self.present(alert, animated: true) + return + } + viewModel.post { [weak self] in + self?.navigationController?.popViewController(animated: true) + } + } + @objc func reload(sender: UISegmentedControl) { switch sender.selectedSegmentIndex { case 0: - textView?.text = textString + contentTextView.text = textString case 1: - textString = textView?.text - textView?.attributedText = markdownParser.parse(textView!.text) + textString = contentTextView.text + contentTextView.attributedText = markdownParser.parse(contentTextView.text) default: break } @@ -95,55 +140,27 @@ class NewIssueViewController: UIViewController { } extension NewIssueViewController: UITableViewDataSource { - func numberOfSections(in tableView: UITableView) -> Int { - return 2 - } - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch section { - case 0: - return 1 - case 1: - return 3 - default: - return 0 - } + return 3 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) cell.selectionStyle = .none - switch indexPath.section { - case 0: - textView = UITextView(frame: cell.contentView.bounds) - cell.addSubview(textView!) - case 1: - cell.textLabel?.text = additionalInfo[indexPath.row] - cell.accessoryType = .disclosureIndicator - default: - break - } + cell.textLabel?.text = additionalInfo[indexPath.row] + cell.accessoryType = .disclosureIndicator return cell } } extension NewIssueViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - if section == 1 { - return "추가 정보" - } - return nil + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let vc = AdditionalInfoViewController() + navigationController?.pushViewController(vc, animated: true) } - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - switch indexPath.section { - case 0: - return 440 - case 1: - return 44 - default: - return 0 - } + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return "추가 정보" } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift index 2d7ac67b5..d6a3f5a49 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/NewIssue.swift @@ -7,16 +7,16 @@ import Foundation -struct NewIssue { - let title: String +struct NewIssue: Codable { + var title: String let comment: String - let labelsID: [Int] - let milestoneID: Int - let assigneeID: [Int] + let labelID: [Int] = [] + let milestoneID: Int? = nil + let assigneeID: [Int] = [] enum CodingKeys: String, CodingKey { case title, comment - case labelsID = "labels_ids" + case labelID = "label_ids" case milestoneID = "milestone_id" case assigneeID = "assignee_id" } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift new file mode 100644 index 000000000..77052c92e --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift @@ -0,0 +1,26 @@ +// +// NewIssueViewModel.swift +// issue-tracker +// +// Created by Ador on 2021/06/21. +// + +import Foundation + +class NewIssueViewModel { + var title: String? + var content: String? + let url = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/issue" + + func post(completion: @escaping () -> Void) { + let url = URL(string: url)! + guard let title = title, let content = content, !content.isEmpty else { + assertionFailure("issue post fail") + return + } + let newIssue = NewIssue(title: title, comment: content) + NetworkManager().postRequest(url: url, encodable: newIssue) { + completion() + } + } +} From b13bbd43a16757a027124228747d328761496561 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:32:51 +0900 Subject: [PATCH 052/104] =?UTF-8?q?feat:=20=E2=9C=A8=20RxCocoa=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20tableView=20delegate,=20dataSourc?= =?UTF-8?q?e=20=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueListViewController.swift | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 5230e4df6..16fa2f2d7 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -7,6 +7,8 @@ import UIKit import SnapKit +import RxSwift +import RxCocoa final class IssueListViewController: UIViewController { @@ -24,14 +26,16 @@ final class IssueListViewController: UIViewController { return searchController }() + private var issueListViewModel = IssueListViewModel(networkManager: NetworkManager()) + private let bag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() setupNavigationItem() setupIssueTableView() setupAddIssueButtonAutolayout() filterBarButton.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside) - selectBarButton.addTarget(self, action: #selector(selectButtonTapped), for: .touchUpInside) - addIssueButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(addIssueButtonTapped))) + bindTableViewDataSource() + bindTableViewDelegate() cancelButton.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside) } @@ -40,7 +44,40 @@ final class IssueListViewController: UIViewController { tabBarController?.tabBar.isHidden = false } - @objc private func filterButtonTapped() { + private func bindTableViewDataSource() { + issueListViewModel.issueList + .bind(to: issueTableView.rx.items) { tableView, _, issue in + guard let cell = tableView.dequeueReusableCell(withIdentifier: IssueTableViewCell.identifier) as? IssueTableViewCell else { return UITableViewCell() } + cell.setupIssueCell(title: issue.title, description: "구현이 더 필요함", milestoneTitle: issue.milestone.title, relay: BehaviorRelay<[IssueLabel]>(value: issue.label)) + return cell + } + .disposed(by: bag) + } + + func bindTableViewDelegate() { + issueTableView.rx.itemSelected + .bind { [weak self] indexPath in + guard let self = self, let cell = self.issueTableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } + if self.issueListViewModel.selectMode.value { + cell.selectionStyle = .none + cell.check() + } else { + cell.selectionStyle = .none + let controller = IssueDetailViewController() + self.navigationController?.pushViewController(controller, animated: true) + } + } + .disposed(by: bag) + + issueTableView.rx.itemDeselected + .bind { [weak self] indexPath in + guard let self = self, let cell = self.issueTableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } + if self.issueListViewModel.selectMode.value { + cell.uncheck() + } + } + .disposed(by: bag) + } let controller = UINavigationController(rootViewController: IssueFilterViewController()) present(controller, animated: true) } From 11534877d935a51a351bc250e721f69df43dc782 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:34:07 +0900 Subject: [PATCH 053/104] =?UTF-8?q?feat:=20=E2=9C=A8=20Rx=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=98=EC=97=AC=20UIButton=EC=9D=98=20acti?= =?UTF-8?q?on=20=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueListViewController.swift | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 16fa2f2d7..b441f5043 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -18,6 +18,7 @@ final class IssueListViewController: UIViewController { private let filterBarButton = FilterBarButton() private let selectBarButton = SelectBarButton() private let cancelButton = CancelButton() + private let addIssueTapGesture = UITapGestureRecognizer() private let issueToolbar = IssueToolbar(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) private let searchController: UISearchController = { var searchController = UISearchController(searchResultsController: nil) @@ -33,10 +34,10 @@ final class IssueListViewController: UIViewController { setupNavigationItem() setupIssueTableView() setupAddIssueButtonAutolayout() - filterBarButton.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside) + addIssueButton.addGestureRecognizer(addIssueTapGesture) bindTableViewDataSource() bindTableViewDelegate() - cancelButton.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside) + bindButton() } override func viewWillAppear(_ animated: Bool) { @@ -78,6 +79,30 @@ final class IssueListViewController: UIViewController { } .disposed(by: bag) } + + func bindButton() { + selectBarButton.rx.tap + .map { true } + .bind(to: issueListViewModel.selectMode) + .disposed(by: bag) + + cancelButton.rx.tap + .map { false } + .bind(to: issueListViewModel.selectMode) + .disposed(by: bag) + + filterBarButton.rx.tap + .bind { [weak self] _ in + self?.filterButtonTapped() + } + .disposed(by: bag) + + addIssueTapGesture.rx.event + .bind(onNext: { [weak self] _ in + self?.addIssueButtonTapped() + }) + .disposed(by: bag) + } let controller = UINavigationController(rootViewController: IssueFilterViewController()) present(controller, animated: true) } From cfb1990fb8061ddaf5bf385ffd471514e5579ad8 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:35:25 +0900 Subject: [PATCH 054/104] =?UTF-8?q?feat:=20=E2=9C=A8=20selectMode=EB=A1=9C?= =?UTF-8?q?=20=EC=A0=84=ED=99=98=20=EA=B0=80=EB=8A=A5=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?bind=EB=B0=8F=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueListViewController.swift | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index b441f5043..94fa54138 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -29,6 +29,7 @@ final class IssueListViewController: UIViewController { private var issueListViewModel = IssueListViewModel(networkManager: NetworkManager()) private let bag = DisposeBag() + override func viewDidLoad() { super.viewDidLoad() setupNavigationItem() @@ -38,6 +39,8 @@ final class IssueListViewController: UIViewController { bindTableViewDataSource() bindTableViewDelegate() bindButton() + bindSelectMode() + issueListViewModel.fetchIssueList() } override func viewWillAppear(_ animated: Bool) { @@ -103,11 +106,25 @@ final class IssueListViewController: UIViewController { }) .disposed(by: bag) } + + func bindSelectMode() { + issueListViewModel.selectMode + .subscribe { [weak self] event in + if let element = event.element, element { + self?.selectButtonTapped() + } else { + self?.cancelButtonTapped() + } + } + .disposed(by: bag) + } + + private func filterButtonTapped() { let controller = UINavigationController(rootViewController: IssueFilterViewController()) present(controller, animated: true) } - @objc private func selectButtonTapped() { + private func selectButtonTapped() { navigationItem.leftBarButtonItem = nil navigationItem.rightBarButtonItem = UIBarButtonItem(customView: cancelButton) navigationItem.title = "이슈 선택" @@ -117,12 +134,12 @@ final class IssueListViewController: UIViewController { setupToolbarAutoulayout() } - @objc private func addIssueButtonTapped() { + private func addIssueButtonTapped() { let controller = NewIssueViewController() navigationController?.pushViewController(controller, animated: true) } - @objc private func cancelButtonTapped() { + private func cancelButtonTapped() { setupNavigationItem() tabBarController?.tabBar.isHidden = false issueToolbar.removeFromSuperview() From b509b4e261be9738811a5dfac793ec3231fe948c Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:35:45 +0900 Subject: [PATCH 055/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueListViewController.swift | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 94fa54138..3f970b4e4 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -173,45 +173,21 @@ final class IssueListViewController: UIViewController { private func setupIssueTableView() { issueTableView.register(IssueTableViewCell.self, forCellReuseIdentifier: IssueTableViewCell.identifier) issueTableView.allowsMultipleSelection = true - issueTableView.dataSource = self issueTableView.delegate = self issueTableView.tableFooterView = IssueTableFooterView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 300)) } } -extension IssueListViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 2 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: IssueTableViewCell.identifier) as? IssueTableViewCell else { return UITableViewCell() } - cell.setupIssueCell(title: "제목", description: "이슈에 대한 설명", milestoneTitle: "마일스톤 이름", color: "#DFCD85") - return cell - } -} - extension IssueListViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let cell = tableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } - cell.selectionStyle = .none - cell.check() - - let controller = IssueDetailViewController() - navigationController?.pushViewController(controller, animated: true) - } - - func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { - guard let cell = tableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } - cell.uncheck() - } - func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - let deleteAction = UIContextualAction(style: .destructive, title: "삭제") { _, _, success in + let deleteAction = UIContextualAction(style: .destructive, title: "삭제") { [weak self] _, _, success in + guard let self = self else { return } + self.issueListViewModel.deleteIssue(id: self.issueListViewModel.issueList.value[indexPath.row].id!) success(true) } let shareAction = UIContextualAction(style: .normal, title: "닫기") { _, _, success in + success(true) } From efa4ea1cb1df2a672e961547ce80071d34555877 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:36:10 +0900 Subject: [PATCH 056/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20FakeData?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueList/IssueTableViewCell.swift | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index 6653aeab4..d644abf98 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -7,12 +7,14 @@ import UIKit import SnapKit +import RxSwift +import RxCocoa final class IssueTableViewCell: UITableViewCell { static var identifier = "IssueTableViewCell" - private var fakeData = [IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfa", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85"), IssueLabels(title: "gdsfaewqeqwrqw2ewqweq", color: "#DFCD85")] + private var bag = DisposeBag() private var largeTitle: UILabel = { var label = UILabel() @@ -44,7 +46,6 @@ final class IssueTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) - labelsCollectionView.dataSource = self addSubviews() setupAutolayout() checkBoxImageView.isHidden = true @@ -52,7 +53,6 @@ final class IssueTableViewCell: UITableViewCell { required init?(coder: NSCoder) { super.init(coder: coder) - labelsCollectionView.dataSource = self addSubviews() setupAutolayout() checkBoxImageView.isHidden = true @@ -98,10 +98,19 @@ final class IssueTableViewCell: UITableViewCell { } } - func setupIssueCell(title: String, description: String, milestoneTitle: String, color: String) { + func setupIssueCell(title: String, description: String, milestoneTitle: String, relay: BehaviorRelay<[IssueLabel]>) { self.largeTitle.text = title self.labelDescription.text = description self.milestoneView.setMilestoneTitle(title: milestoneTitle) + self.bindLabelCollectionView(relay: relay) + } + + func bindLabelCollectionView(relay: BehaviorRelay<[IssueLabel]>) { + relay.bind(to: labelsCollectionView.rx.items(cellIdentifier: LabelsCollectionViewCell.identifiers)) { index, model, cell in + guard let cell = UICollectionViewCell() as? LabelsCollectionViewCell else { return } + cell.configure(title: model.title, color: model.color) + } + .disposed(by: bag) } func check() { @@ -112,20 +121,3 @@ final class IssueTableViewCell: UITableViewCell { checkBoxImageView.isHidden = true } } - -extension IssueTableViewCell: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return fakeData.count - } - - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LabelsCollectionViewCell.identifiers, for: indexPath) as? LabelsCollectionViewCell else { return UICollectionViewCell() } - cell.configure(title: fakeData[indexPath.item].title, color: fakeData[indexPath.item].color) - return cell - } -} - -struct IssueLabels { - var title: String - var color: String -} From 0f90edd5f9fe44bfe7a1b3acbd71d506d1639732 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:36:42 +0900 Subject: [PATCH 057/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20EndPoint?= =?UTF-8?q?=EC=97=90=EC=84=9C=20id=EB=A5=BC=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=EB=A1=9C=20=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EA=B2=8C=20=EC=88=98=EC=A0=95=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/Endpoint.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index 73c297607..f841b6be2 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -13,11 +13,14 @@ struct Endpoint { let port: Int = 8080 var path: Path - func url(queryItems: [URLQueryItem] = []) -> URL? { + func url(queryItems: [URLQueryItem] = [], id: Int? = nil) -> URL? { var urlComponents = URLComponents() urlComponents.scheme = scheme urlComponents.host = host urlComponents.path = path.pathString + if let id = id { + urlComponents.path = path.pathString + "/" + String(id) + } urlComponents.queryItems = queryItems return urlComponents.url } @@ -25,6 +28,7 @@ struct Endpoint { enum Path: String { case login = "/login" case label = "/label" + case issue = "/issue" var pathString: String { return self.rawValue From e25ee6664ebfecf5bf04abf0c8d40fc7329945a9 Mon Sep 17 00:00:00 2001 From: zeke Date: Mon, 21 Jun 2021 23:37:04 +0900 Subject: [PATCH 058/104] =?UTF-8?q?feat:=20=E2=9C=A8=20deleteRequest?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/NetworkManager.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index a7efd4c86..f5a93fdda 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -11,6 +11,7 @@ import Alamofire protocol Networkable { func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) + func deleteRequest(url: URL, completion: @escaping () -> Void) } class NetworkManager: Networkable { @@ -41,4 +42,17 @@ class NetworkManager: Networkable { } } } + + func deleteRequest(url: URL, completion: @escaping () -> Void) { + AF.request(url, method: .delete) + .validate(statusCode: 200..<300) + .response { response in + switch response.result { + case .success: + completion() + case .failure(let error): + print(error) + } + } + } } From 4121b86327bb6e521b611a038743f378dfa7d998 Mon Sep 17 00:00:00 2001 From: lena Date: Tue, 22 Jun 2021 03:16:24 +0900 Subject: [PATCH 059/104] feat: add issue detail view model (#49) --- .../issue-tracker.xcodeproj/project.pbxproj | 4 +++ .../IssueDetailViewController.swift | 33 ++++++++++++++++--- .../IssueList/IssueDetailTableViewCell.swift | 16 ++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 9eb0f5069..5c31f3692 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ D0ADB6F8266F5BBE00E0762C /* OAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0ADB6F7266F5BBE00E0762C /* OAuthManager.swift */; }; D0DAAE28268071F00075E794 /* NewIssueViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DAAE27268071F00075E794 /* NewIssueViewModel.swift */; }; D0DAAE2A2680A8DE0075E794 /* AdditionalInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DAAE292680A8DE0075E794 /* AdditionalInfoViewController.swift */; }; + D0DAAE2C2680E2E20075E794 /* IssueDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DAAE2B2680E2E20075E794 /* IssueDetailViewModel.swift */; }; D0FD509E26708DDE008C6031 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E4266E095E00901C55 /* LoginViewController.swift */; }; D0FD50AA267096C0008C6031 /* MilestoneViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */; }; D0FD50BB26709F8B008C6031 /* MilestoneTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */; }; @@ -163,6 +164,7 @@ D0ADB6F7266F5BBE00E0762C /* OAuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthManager.swift; sourceTree = ""; }; D0DAAE27268071F00075E794 /* NewIssueViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewIssueViewModel.swift; sourceTree = ""; }; D0DAAE292680A8DE0075E794 /* AdditionalInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdditionalInfoViewController.swift; sourceTree = ""; }; + D0DAAE2B2680E2E20075E794 /* IssueDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueDetailViewModel.swift; sourceTree = ""; }; D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneViewController.swift; sourceTree = ""; }; D0FD50BA26709F8B008C6031 /* MilestoneTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MilestoneTableViewCell.swift; sourceTree = ""; }; D0FD50DB2671AA04008C6031 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = ""; }; @@ -249,6 +251,7 @@ B3E01192267A0B44001155D4 /* AddLabelViewModel.swift */, D04C694D267A35960056DD79 /* MilestoneViewModel.swift */, D0DAAE27268071F00075E794 /* NewIssueViewModel.swift */, + D0DAAE2B2680E2E20075E794 /* IssueDetailViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -710,6 +713,7 @@ B3B559E3266E095E00901C55 /* SceneDelegate.swift in Sources */, B3E01193267A0B44001155D4 /* AddLabelViewModel.swift in Sources */, B32FEAC62671DDA600BF37A1 /* MilestoneView.swift in Sources */, + D0DAAE2C2680E2E20075E794 /* IssueDetailViewModel.swift in Sources */, B3EADAC0267628FB0007C4B6 /* LabelsCollectionView.swift in Sources */, B3EADAC2267629190007C4B6 /* LabelsCollectionViewCell.swift in Sources */, D0DAAE2A2680A8DE0075E794 /* AdditionalInfoViewController.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index a52256755..1bd6f41b2 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -7,10 +7,14 @@ import UIKit import SnapKit +import RxSwift +import RxCocoa class IssueDetailViewController: UIViewController { private let cellReuseIdentifier = "IssueDetailCell" - private let data = [1, 2, 3, 4, 5, 6, 7, 8, 9] + private var viewModel = IssueDetailViewModel() + private let disposeBag = DisposeBag() + private var comment: [Comment] = [] private let tableView: UITableView = { let tableView = UITableView() @@ -38,7 +42,6 @@ class IssueDetailViewController: UIViewController { view.backgroundColor = .systemBackground navigationController?.navigationBar.prefersLargeTitles = true navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain, target: self, action: #selector(showIssueDetailInfo)) - navigationItem.title = "테스트 이슈 #2" tableView.dataSource = self tableView.delegate = self @@ -49,6 +52,8 @@ class IssueDetailViewController: UIViewController { view.addSubview(toolbar) setupAutolayout() + fetch() + bind() } override func viewWillAppear(_ animated: Bool) { @@ -75,7 +80,7 @@ class IssueDetailViewController: UIViewController { @objc private func scrollToNext() { guard let indexPath = tableView.indexPathForSelectedRow, - indexPath.row + 1 < data.count else { return } + indexPath.row + 1 < comment.count else { return } tableView.selectRow(at: IndexPath(row: indexPath.row + 1, section: indexPath.section), animated: true, scrollPosition: .bottom) } @@ -89,14 +94,32 @@ class IssueDetailViewController: UIViewController { } } +private extension IssueDetailViewController { + func fetch() { + viewModel.fetch() + } + + func bind() { + viewModel.subject.bind { detail in + self.navigationItem.title = detail?.data.title + guard let comment = detail?.data.comment else { return } + self.comment = comment + self.tableView.reloadData() + } + .disposed(by: disposeBag) + } +} extension IssueDetailViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return data.count + return comment.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) + guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as? IssueDetailTableViewCell else { + return UITableViewCell() + } cell.accessoryView = UIImageView(image: UIImage(systemName: "ellipsis")) + cell.configure(model: comment[indexPath.row]) return cell } } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueDetailTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueDetailTableViewCell.swift index 6913be46a..5cc73976d 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueDetailTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueDetailTableViewCell.swift @@ -29,7 +29,6 @@ class IssueDetailTableViewCell: UITableViewCell { private let profile: UIImageView = { let imageView = UIImageView() - imageView.image = UIImage(systemName: "bell") imageView.contentMode = .scaleAspectFit imageView.layer.masksToBounds = true imageView.layer.cornerRadius = imageView.frame.width / 2 @@ -83,4 +82,19 @@ class IssueDetailTableViewCell: UITableViewCell { maker.leading.trailing.top.bottom.equalToSuperview().inset(20) } } + + func configure(model: Comment) { + setupProfile(url: model.author.imageURL) + author.text = model.author.name + comment.text = model.content + } + + private func setupProfile(url: String) { + DispatchQueue.global().sync { + guard let imageData = try? Data(contentsOf: URL(string: url)!) else { return } + DispatchQueue.main.async { + self.profile.image = UIImage(data: imageData) + } + } + } } From 264e4fac06b7e4c35d0d0c9f4620c70f56db4a16 Mon Sep 17 00:00:00 2001 From: lena Date: Tue, 22 Jun 2021 18:17:11 +0900 Subject: [PATCH 060/104] feat: add keyboard notification to issue detail view (#83) --- .../IssueDetailViewController.swift | 69 ++++++++++--------- .../View/IssueList/ToolBarTextField.swift | 3 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index 1bd6f41b2..a6ba03ddb 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -10,11 +10,12 @@ import SnapKit import RxSwift import RxCocoa -class IssueDetailViewController: UIViewController { +final class IssueDetailViewController: UIViewController { private let cellReuseIdentifier = "IssueDetailCell" - private var viewModel = IssueDetailViewModel() + var viewModel: IssueDetailViewModel! private let disposeBag = DisposeBag() private var comment: [Comment] = [] + private var constaint: NSLayoutConstraint? private let tableView: UITableView = { let tableView = UITableView() @@ -23,18 +24,11 @@ class IssueDetailViewController: UIViewController { return tableView }() - private let toolbar: UIToolbar = { - let toolbar = UIToolbar(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 100))) - let textField = ToolBarTextField(frame: toolbar.bounds) - let upButton = UIBarButtonItem(image: UIImage(systemName: "chevron.up.circle"), - style: .plain, target: self, action: #selector(scrollToBefore)) - let downButton = UIBarButtonItem(image: UIImage(systemName: "chevron.down.circle"), - style: .plain, target: self, action: #selector(scrollToNext)) - let comment = UIBarButtonItem(customView: textField) - let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: comment, action: nil) - toolbar.setItems([upButton, downButton, space, comment], animated: false) - return toolbar - }() + private let textField = ToolBarTextField() + + deinit { + NotificationCenter.default.removeObserver(self) + } override func viewDidLoad() { super.viewDidLoad() @@ -49,11 +43,14 @@ class IssueDetailViewController: UIViewController { tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .none) view.addSubview(tableView) - view.addSubview(toolbar) + view.addSubview(textField) setupAutolayout() - fetch() + setupKeyboardNotification() + fetchData() bind() + + view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))) } override func viewWillAppear(_ animated: Bool) { @@ -61,28 +58,34 @@ class IssueDetailViewController: UIViewController { tabBarController?.tabBar.isHidden = true } - private func setupAutolayout() { - toolbar.snp.makeConstraints { maker in - maker.leading.trailing.equalToSuperview() - maker.bottom.equalTo(view.safeAreaLayoutGuide) - maker.height.equalTo(44) + private func setupKeyboardNotification() { + let center = NotificationCenter.default + center.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] noti in + guard let strongSelf = self else { return } + if let keyboardFrame = noti.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { + strongSelf.constaint?.constant = -(keyboardFrame.cgRectValue.height - strongSelf.bottomSafeAreaHeight) + print(keyboardFrame.cgRectValue.height) + } + } + center.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { [weak self] _ in + guard let strongSelf = self else { return } + strongSelf.constaint?.constant = 0 } } - @objc - private func scrollToBefore() { - guard let indexPath = tableView.indexPathForSelectedRow, - indexPath.row != 0 else { return } - tableView.selectRow(at: IndexPath(row: indexPath.row - 1, section: indexPath.section), - animated: true, scrollPosition: .top) + private func setupAutolayout() { + NSLayoutConstraint.activate([ + textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + textField.heightAnchor.constraint(equalToConstant: 44) + ]) + constaint = textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) + constaint?.isActive = true } @objc - private func scrollToNext() { - guard let indexPath = tableView.indexPathForSelectedRow, - indexPath.row + 1 < comment.count else { return } - tableView.selectRow(at: IndexPath(row: indexPath.row + 1, section: indexPath.section), - animated: true, scrollPosition: .bottom) + func handleTapGesture(recognizer: UITapGestureRecognizer) { + textField.resignFirstResponder() } @objc @@ -95,7 +98,7 @@ class IssueDetailViewController: UIViewController { } private extension IssueDetailViewController { - func fetch() { + func fetchData() { viewModel.fetch() } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift index fb3dcfc57..dd3e09b57 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift @@ -20,8 +20,9 @@ final class ToolBarTextField: UITextField { self.placeholder = "코멘트를 입력하세요" self.rightView = sendButton self.rightViewMode = .always - self.layer.cornerRadius = 10 + self.layer.cornerRadius = 15 self.layer.masksToBounds = true + self.backgroundColor = .systemGroupedBackground self.translatesAutoresizingMaskIntoConstraints = false } From 6cec208f77bfcbcdf24b0ae65a789b9a759a71ab Mon Sep 17 00:00:00 2001 From: lena Date: Tue, 22 Jun 2021 19:57:54 +0900 Subject: [PATCH 061/104] feat: add IssueDetailViewModel (#49) --- .../IssueDetailViewController.swift | 14 +++++------ .../ViewModel/IssueDetailViewModel.swift | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index a6ba03ddb..fc13f8e9b 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -15,7 +15,7 @@ final class IssueDetailViewController: UIViewController { var viewModel: IssueDetailViewModel! private let disposeBag = DisposeBag() private var comment: [Comment] = [] - private var constaint: NSLayoutConstraint? + private var textFieldHeightConstraint: NSLayoutConstraint? private let tableView: UITableView = { let tableView = UITableView() @@ -63,24 +63,24 @@ final class IssueDetailViewController: UIViewController { center.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] noti in guard let strongSelf = self else { return } if let keyboardFrame = noti.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { - strongSelf.constaint?.constant = -(keyboardFrame.cgRectValue.height - strongSelf.bottomSafeAreaHeight) + strongSelf.textFieldHeightConstraint?.constant = -(keyboardFrame.cgRectValue.height - strongSelf.bottomSafeAreaHeight) print(keyboardFrame.cgRectValue.height) } } center.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { [weak self] _ in guard let strongSelf = self else { return } - strongSelf.constaint?.constant = 0 + strongSelf.textFieldHeightConstraint?.constant = 0 } } private func setupAutolayout() { NSLayoutConstraint.activate([ - textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), - textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), + textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), textField.heightAnchor.constraint(equalToConstant: 44) ]) - constaint = textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) - constaint?.isActive = true + textFieldHeightConstraint = textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) + textFieldHeightConstraint?.isActive = true } @objc diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift new file mode 100644 index 000000000..7a8fd6bd9 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift @@ -0,0 +1,25 @@ +// +// IssueDetailViewModel.swift +// issue-tracker +// +// Created by Ador on 2021/06/22. +// + +import Foundation +import RxCocoa + +class IssueDetailViewModel { + var subject: BehaviorRelay = BehaviorRelay(value: nil) + let issueId: String + + init(id: String) { + self.issueId = id + } + + func fetch() { + guard let url = Endpoint(path: .issue).url(id: issueId) else { return } + NetworkManager().request(url: url, decodableType: IssueDetail.self) { [weak self] data in + self?.subject.accept(data) + } + } +} From 0304a11a7bfe45267d83b141a93d87cbb65616d4 Mon Sep 17 00:00:00 2001 From: lena Date: Tue, 22 Jun 2021 19:59:36 +0900 Subject: [PATCH 062/104] feat: add AdditionalInfoViewController MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 새로운 이슈 등록 시 추가정보(라벨, 마일스톤, 담당자) 선택하는 뷰 추가 --- .../AdditionalInfoViewController.swift | 68 +++++++++++++++++++ .../Controller/NewIssueViewController.swift | 9 ++- .../ViewModel/NewIssueViewModel.swift | 5 +- 3 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/Controller/AdditionalInfoViewController.swift diff --git a/iOS/issue-tracker/issue-tracker/Controller/AdditionalInfoViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AdditionalInfoViewController.swift new file mode 100644 index 000000000..c4a79b47d --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/Controller/AdditionalInfoViewController.swift @@ -0,0 +1,68 @@ +// +// AdditionalInfoViewController.swift +// issue-tracker +// +// Created by Ador on 2021/06/21. +// + +import UIKit + +class AdditionalInfoViewController: UITableViewController { + // temp + let data = ["enhancement", "bug", "feature"] + var setupSelectedData: ((String) -> Void)? + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .systemBackground + tableView.register(DetailTextStyleTableViewCell.self, forCellReuseIdentifier: "reuseIdentifier") + + self.navigationItem.leftBarButtonItem + = UIBarButtonItem(title: "취소", style: .done, target: self, action: #selector(didTapCancel)) + self.navigationItem.rightBarButtonItem + = UIBarButtonItem(title: "저장", style: .done, target: self, action: #selector(didTapSave)) + } + + // MARK: - Table view data source + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return data.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + cell.textLabel?.text = data[indexPath.row] + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let cell = tableView.cellForRow(at: indexPath) + cell?.accessoryType = .checkmark + } + + override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { + let selected = tableView.cellForRow(at: indexPath) + selected?.accessoryType = .none + } + + override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + return UIView() + } +} + +private extension AdditionalInfoViewController { + @objc + func didTapCancel() { + dismiss(animated: true) + } + + @objc + func didTapSave() { + guard let indexPath = tableView.indexPathForSelectedRow else { + didTapCancel() + return + } + setupSelectedData?(data[indexPath.row]) + didTapCancel() + } +} diff --git a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift index 2ea47b90f..308e039d5 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift @@ -34,7 +34,7 @@ class NewIssueViewController: UIViewController { let tableView = UITableView() tableView.rowHeight = 44.0 tableView.isScrollEnabled = false - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "NewIssueViewCell") + tableView.register(DetailTextStyleTableViewCell.self, forCellReuseIdentifier: "NewIssueViewCell") return tableView }() @@ -156,7 +156,12 @@ extension NewIssueViewController: UITableViewDataSource { extension NewIssueViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vc = AdditionalInfoViewController() - navigationController?.pushViewController(vc, animated: true) + vc.setupSelectedData = { data in + let cell = tableView.cellForRow(at: indexPath) + cell?.detailTextLabel?.text = data + } + let nav = UINavigationController(rootViewController: vc) + present(nav, animated: true) } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift index 77052c92e..0e09be0a5 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift @@ -10,10 +10,11 @@ import Foundation class NewIssueViewModel { var title: String? var content: String? - let url = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/issue" func post(completion: @escaping () -> Void) { - let url = URL(string: url)! + guard let url = Endpoint(path: .label).url() else { + return + } guard let title = title, let content = content, !content.isEmpty else { assertionFailure("issue post fail") return From 403a3f04b5a0a8cf3c41819e551aa192f5ef2e00 Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 22 Jun 2021 21:30:35 +0900 Subject: [PATCH 063/104] =?UTF-8?q?feat:=20=E2=9C=A8=20PatchIssue=20DTO=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 4 ++++ .../issue-tracker/Model/UserDTO/PatchIssue.swift | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index e45719963..027873e56 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997E266F90710091A44A /* AppleLoginButton.swift */; }; B34FF73E267AE9D8002D9C56 /* EstimatedLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */; }; B34FF741267B8800002D9C56 /* AddLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */; }; + B3926D6B2681B59800F72CEE /* PatchIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3926D6A2681B59800F72CEE /* PatchIssue.swift */; }; B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB57267840290060DC85 /* LabelListViewModel.swift */; }; B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB5A26784AA50060DC85 /* LabelList.swift */; }; B3B559E1266E095E00901C55 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B559E0266E095E00901C55 /* AppDelegate.swift */; }; @@ -113,6 +114,7 @@ B349997E266F90710091A44A /* AppleLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleLoginButton.swift; sourceTree = ""; }; B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EstimatedLabelView.swift; sourceTree = ""; }; B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLabelTableViewCell.swift; sourceTree = ""; }; + B3926D6A2681B59800F72CEE /* PatchIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchIssue.swift; sourceTree = ""; }; B3A5CB57267840290060DC85 /* LabelListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelListViewModel.swift; sourceTree = ""; }; B3A5CB5A26784AA50060DC85 /* LabelList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelList.swift; sourceTree = ""; }; B3B559DD266E095E00901C55 /* issue-tracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "issue-tracker.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -314,6 +316,7 @@ isa = PBXGroup; children = ( B3D7D0C3267735CC000F02F4 /* IssueList.swift */, + B3926D6A2681B59800F72CEE /* PatchIssue.swift */, B3A5CB5A26784AA50060DC85 /* LabelList.swift */, D03AF8F92677494F001C2CBF /* Comment.swift */, D03AF8F726774909001C2CBF /* MilestoneList.swift */, @@ -697,6 +700,7 @@ D0103BDE267B897E0079FC3D /* AddMilestoneViewController.swift in Sources */, D0A88E092674F888005877F6 /* IssueDetailTableViewCell.swift in Sources */, B3D7D0C4267735CC000F02F4 /* IssueList.swift in Sources */, + B3926D6B2681B59800F72CEE /* PatchIssue.swift in Sources */, D0ADB699266F0C1300E0762C /* NetworkManager.swift in Sources */, B3FBA7B9267335C30006E5E6 /* CancelButton.swift in Sources */, D03AF8FA2677494F001C2CBF /* Comment.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift new file mode 100644 index 000000000..54d4f56f8 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift @@ -0,0 +1,13 @@ +// +// PatchIssue.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/22. +// + +import Foundation + +struct PatchIssue: Encodable { + let issueNumber: [Int] + let isOpen: Bool +} From 5c2a3245c16413018b451d7779490627912b6da7 Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 22 Jun 2021 21:31:11 +0900 Subject: [PATCH 064/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=EB=B0=B0=ED=8F=AC=ED=95=98=EC=97=AC=20Endpoint=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/Endpoint.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index f841b6be2..48e55cdbe 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -8,8 +8,8 @@ import Foundation struct Endpoint { - let scheme: String = "https" - let host: String = "77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io" + let scheme: String = "http" + let host: String = "3.37.161.3" let port: Int = 8080 var path: Path @@ -17,6 +17,7 @@ struct Endpoint { var urlComponents = URLComponents() urlComponents.scheme = scheme urlComponents.host = host + urlComponents.port = port urlComponents.path = path.pathString if let id = id { urlComponents.path = path.pathString + "/" + String(id) From 69f68b327841f1a8634c501c02765331dd16125c Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 22 Jun 2021 21:32:22 +0900 Subject: [PATCH 065/104] =?UTF-8?q?feat:=20=E2=9C=A8=20NetworkManager?= =?UTF-8?q?=EC=9D=98=20patchRequest=20=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/NetworkManager.swift | 14 ++++++++++++++ .../ViewModel/IssueListViewModel.swift | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index f5a93fdda..50f7611e3 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -12,6 +12,7 @@ protocol Networkable { func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) func deleteRequest(url: URL, completion: @escaping () -> Void) + func patchRequest(url: URL, encodable: T, completion: @escaping () -> Void) } class NetworkManager: Networkable { @@ -55,4 +56,17 @@ class NetworkManager: Networkable { } } } + + func patchRequest(url: URL, encodable: T, completion: @escaping () -> Void) { + AF.request(url, method: .patch, parameters: encodable, encoder: JSONParameterEncoder.default) + .validate(statusCode: 200..<300) + .response { response in + switch response.result { + case .success: + completion() + case .failure(let error): + print(error) + } + } + } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift index 533e8ed3b..59329a8dc 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -7,12 +7,14 @@ import Foundation import RxCocoa +import RxSwift class IssueListViewModel { let issueList = BehaviorRelay<[Issue]>(value: []) let networkManager: Networkable var selectMode = BehaviorRelay(value: false) + var selectedCell = BehaviorRelay<[Issue]>(value: []) init(networkManager: Networkable) { self.networkManager = networkManager @@ -29,4 +31,11 @@ class IssueListViewModel { print("success") } } + + func patchIssue(issues: [Issue]) { + let encodableObject = PatchIssue(issueNumber: issues.map { $0.id! }, isOpen: false) + networkManager.patchRequest(url: Endpoint(path: .issue).url()!, encodable: encodableObject) { + print("success patchIssue") + } + } } From ef3ca6740dce35792ecc197aaf087bff385eea78 Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 22 Jun 2021 21:33:21 +0900 Subject: [PATCH 066/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20IssueToolbar=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=EC=A0=9C=EC=96=B4=20=EB=B3=80=EA=B2=BD=20(#7?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Controller/IssueToolbar.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift index ecd7b4150..c5b40c6e9 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueToolbar.swift @@ -9,19 +9,19 @@ import UIKit final class IssueToolbar: UIToolbar { - private let checkBoxBarButtonItem: UIBarButtonItem = { + private(set) var checkBoxBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.image = UIImage(systemName: "checkmark.circle") return item }() - private let closeIssueBarButtonItem: UIBarButtonItem = { + private(set) var closeIssueBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.image = UIImage(systemName: "archivebox") return item }() - private let labelBarButtonItem: UIBarButtonItem = { + private(set) var labelBarButtonItem: UIBarButtonItem = { var item = UIBarButtonItem() item.title = "이슈를 선택하세요" item.isEnabled = false From fd5e409e3e5f81a793d46ebfcc4fd3bd3bd97dc8 Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 22 Jun 2021 21:34:39 +0900 Subject: [PATCH 067/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Issue=20DTO?= =?UTF-8?q?=EC=9D=98=20Equatable=20=EC=B1=84=ED=83=9D=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/UserDTO/IssueList.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift index caa45e68e..0abaacf40 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift @@ -18,14 +18,18 @@ struct IssueDetail: Codable { } // MARK: - Issue -struct Issue: Codable { +struct Issue: Codable, Equatable { + static func == (lhs: Issue, rhs: Issue) -> Bool { + return lhs.id == rhs.id + } + let id: Int? let title: String let number: Int let open: Bool let createdTime: String let author: Author - let label: [IssueLabel] + let labels: [IssueLabel] let assignee: [Author]? let milestone: Milestone let comment: [Comment]? @@ -34,9 +38,9 @@ struct Issue: Codable { case id case title case number - case open = "is_open" + case open case createdTime = "created_time" - case author, label, assignee, milestone, comment + case author, labels, assignee, milestone, comment } } From 2c264acb63aea41c9b71618b1fd5d91e4c05143e Mon Sep 17 00:00:00 2001 From: zeke Date: Tue, 22 Jun 2021 21:35:57 +0900 Subject: [PATCH 068/104] =?UTF-8?q?feat:=20=E2=9C=A8=20ToolBar=EC=99=80=20?= =?UTF-8?q?SearchViewController=EC=9D=98=20=EB=B0=94=EC=9D=B8=EB=94=A9=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueListViewController.swift | 72 ++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 3f970b4e4..93955ef10 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -40,6 +40,8 @@ final class IssueListViewController: UIViewController { bindTableViewDelegate() bindButton() bindSelectMode() + bindIssueToolBar() + bindSearchController() issueListViewModel.fetchIssueList() } @@ -52,7 +54,7 @@ final class IssueListViewController: UIViewController { issueListViewModel.issueList .bind(to: issueTableView.rx.items) { tableView, _, issue in guard let cell = tableView.dequeueReusableCell(withIdentifier: IssueTableViewCell.identifier) as? IssueTableViewCell else { return UITableViewCell() } - cell.setupIssueCell(title: issue.title, description: "구현이 더 필요함", milestoneTitle: issue.milestone.title, relay: BehaviorRelay<[IssueLabel]>(value: issue.label)) + cell.setupIssueCell(title: issue.title, description: "구현이 더 필요함", milestoneTitle: issue.milestone.title, relay: BehaviorRelay<[IssueLabel]>(value: issue.labels)) return cell } .disposed(by: bag) @@ -63,6 +65,8 @@ final class IssueListViewController: UIViewController { .bind { [weak self] indexPath in guard let self = self, let cell = self.issueTableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } if self.issueListViewModel.selectMode.value { + let issue = self.issueListViewModel.issueList.value[indexPath.row] + self.issueListViewModel.selectedCell.accept(self.issueListViewModel.selectedCell.value + [issue]) cell.selectionStyle = .none cell.check() } else { @@ -77,6 +81,12 @@ final class IssueListViewController: UIViewController { .bind { [weak self] indexPath in guard let self = self, let cell = self.issueTableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } if self.issueListViewModel.selectMode.value { + let deselectedIssue = self.issueListViewModel.issueList.value[indexPath.row] + var selectedIssue = self.issueListViewModel.selectedCell.value + if let index = selectedIssue.firstIndex(where: { $0 == deselectedIssue }) { + selectedIssue.remove(at: index) + self.issueListViewModel.selectedCell.accept(selectedIssue) + } cell.uncheck() } } @@ -119,6 +129,56 @@ final class IssueListViewController: UIViewController { .disposed(by: bag) } + func bindIssueToolBar() { + issueListViewModel.selectedCell + .bind { [weak self] issues in + if issues.count == 0 { + self?.issueToolbar.labelBarButtonItem.title = "이슈를 선택하세요" + self?.issueToolbar.closeIssueBarButtonItem.isEnabled = false + } else { + self?.issueToolbar.labelBarButtonItem.title = "\(issues.count)개의 이슈가 선택됨" + self?.issueToolbar.closeIssueBarButtonItem.isEnabled = true + } + } + .disposed(by: bag) + + issueToolbar.checkBoxBarButtonItem.rx.tap + .bind { [weak self] _ in + guard let self = self else { return } + let rows = self.issueTableView.numberOfRows(inSection: 0) + for row in 0.. Date: Wed, 23 Jun 2021 11:18:56 +0900 Subject: [PATCH 069/104] feat: add badge, author to issue detail view --- .../IssueDetailViewController.swift | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index fc13f8e9b..ad2786dcc 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -11,12 +11,39 @@ import RxSwift import RxCocoa final class IssueDetailViewController: UIViewController { + + var viewModel: IssueDetailViewModel = IssueDetailViewModel() + private let cellReuseIdentifier = "IssueDetailCell" - var viewModel: IssueDetailViewModel! private let disposeBag = DisposeBag() private var comment: [Comment] = [] private var textFieldHeightConstraint: NSLayoutConstraint? + private let headerStackView: UIStackView = { + let stackView = UIStackView(frame: CGRect(origin: .zero, size: CGSize(width: 1, height: 44))) + stackView.axis = .vertical + stackView.alignment = .leading + stackView.spacing = 10 + return stackView + }() + + private let isOpened: PaddingLabel = { + let label = PaddingLabel(withInsets: 0, 0, 10, 10) + label.textAlignment = .center + label.backgroundColor = .systemPink + label.textColor = .white + label.layer.masksToBounds = true + label.layer.cornerRadius = 10 + label.snp.makeConstraints { $0.width.greaterThanOrEqualTo(50) } + return label + }() + + private let authorLabel: UILabel = { + let label = UILabel() + label.numberOfLines = 0 + return label + }() + private let tableView: UITableView = { let tableView = UITableView() tableView.rowHeight = 130 @@ -37,11 +64,17 @@ final class IssueDetailViewController: UIViewController { navigationController?.navigationBar.prefersLargeTitles = true navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain, target: self, action: #selector(showIssueDetailInfo)) + textField.textFieldDelegate = self tableView.dataSource = self tableView.delegate = self tableView.frame = view.bounds tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .none) + headerStackView.addArrangedSubview(isOpened) + headerStackView.addArrangedSubview(authorLabel) + + tableView.tableHeaderView = headerStackView + view.addSubview(tableView) view.addSubview(textField) @@ -74,6 +107,7 @@ final class IssueDetailViewController: UIViewController { } private func setupAutolayout() { + headerStackView.snp.makeConstraints { $0.leading.trailing.top.bottom.equalToSuperview().inset(20) } NSLayoutConstraint.activate([ textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), @@ -103,11 +137,14 @@ private extension IssueDetailViewController { } func bind() { - viewModel.subject.bind { detail in - self.navigationItem.title = detail?.data.title - guard let comment = detail?.data.comment else { return } - self.comment = comment - self.tableView.reloadData() + viewModel.subject.bind { [weak self] detail in + guard let detail = detail?.data else { return } + self?.navigationItem.title = detail.title + self?.isOpened.text = detail.isOpen ? "열림" : "닫힘" + self?.authorLabel.text = "\(detail.author.name)님이 작성했습니다." + guard let comment = detail.comment else { return } + self?.comment = comment + self?.tableView.reloadData() } .disposed(by: disposeBag) } @@ -132,3 +169,12 @@ extension IssueDetailViewController: UITableViewDelegate { UIView() } } + +extension IssueDetailViewController: ToolBarTextFieldDelegate { + func register() { + guard let comment = textField.text else { + return + } + viewModel.post(comment: comment) + } +} From e58da7b877660677e3a8686a710ba4c663552010 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 14:34:23 +0900 Subject: [PATCH 070/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=9D=98=20cornerRadius=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/LabelsCollectionViewCell.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iOS/issue-tracker/LabelsCollectionViewCell.swift b/iOS/issue-tracker/LabelsCollectionViewCell.swift index 40b00ab36..a09f246a1 100644 --- a/iOS/issue-tracker/LabelsCollectionViewCell.swift +++ b/iOS/issue-tracker/LabelsCollectionViewCell.swift @@ -16,7 +16,7 @@ final class LabelsCollectionViewCell: UICollectionViewCell { label.textAlignment = .center label.textColor = .white label.layer.masksToBounds = true - label.layer.cornerRadius = 15 + label.layer.cornerRadius = 10 return label }() @@ -41,5 +41,6 @@ final class LabelsCollectionViewCell: UICollectionViewCell { func configure(title: String, color: String) { label.text = title label.backgroundColor = UIColor.hexStringToUIColor(hex: color) + label.sizeToFit() } } From bc1c71d8885d4d0adf1f8c447c27ce08fe6a6275 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 14:35:39 +0900 Subject: [PATCH 071/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Milestone?= =?UTF-8?q?=EC=9D=98=20=ED=83=80=EC=9E=85=20=EC=98=B5=EC=85=94=EB=84=90?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift | 2 +- .../issue-tracker/View/IssueListViewController.swift | 4 ++-- .../issue-tracker/ViewModel/AddLabelViewModel.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift index 0abaacf40..7f427ea55 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift @@ -31,7 +31,7 @@ struct Issue: Codable, Equatable { let author: Author let labels: [IssueLabel] let assignee: [Author]? - let milestone: Milestone + let milestone: Milestone? let comment: [Comment]? enum CodingKeys: String, CodingKey { diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 93955ef10..e0680d5aa 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -54,7 +54,7 @@ final class IssueListViewController: UIViewController { issueListViewModel.issueList .bind(to: issueTableView.rx.items) { tableView, _, issue in guard let cell = tableView.dequeueReusableCell(withIdentifier: IssueTableViewCell.identifier) as? IssueTableViewCell else { return UITableViewCell() } - cell.setupIssueCell(title: issue.title, description: "구현이 더 필요함", milestoneTitle: issue.milestone.title, relay: BehaviorRelay<[IssueLabel]>(value: issue.labels)) + cell.setupIssueCell(title: issue.title, description: nil, milestoneTitle: issue.milestone?.title, relay: Observable>.just(BehaviorRelay(value: issue.labels))) return cell } .disposed(by: bag) @@ -174,7 +174,7 @@ final class IssueListViewController: UIViewController { searchController.rx.didDismiss .subscribe { [weak self] _ in - self?.issueListViewModel.fetchIssueList() +// self?.issueListViewModel.fetchIssueList() } .disposed(by: bag) } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift index 768dd49a8..d384b84f7 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift @@ -20,7 +20,7 @@ class AddLabelViewModel { } func postAddedLabel(completion: @escaping () -> Void) { - let encodableLabel = IssueLabel(id: nil, title: title.value, color: color.value, fontColor: fontColor, description: description.value) + let encodableLabel = IssueLabel(id: nil, title: title.value, color: color.value, fontColor: nil, description: description.value) networkManager.postRequest(url: Endpoint(path: .label).url()!, encodable: encodableLabel, completion: completion) } } From 889ecab3683fd2653f9bd117b7edc181af0c068d Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 14:36:52 +0900 Subject: [PATCH 072/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20tableViewCell,?= =?UTF-8?q?=20collectionViewCell=EC=9D=98=20=EC=82=AC=EC=9D=B4=EC=A6=88=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard | 2 +- .../issue-tracker/View/IssueList/LabelsCollectionView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard index 72b54dcc0..1a52c17ce 100644 --- a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard +++ b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard @@ -17,7 +17,7 @@ - + diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift index a6ba7698b..4d1b94b81 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift @@ -12,7 +12,7 @@ final class LabelsCollectionView: UICollectionView { private var labelsLayout: UICollectionViewFlowLayout = { var layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical - layout.estimatedItemSize = CGSize(width: 84, height: 22) + layout.estimatedItemSize = CGSize(width: 60, height: 20) layout.minimumLineSpacing = 10 layout.minimumInteritemSpacing = 10 return layout From d3fc0f161d10e41c683df6b24f9ca02f275783aa Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 14:37:18 +0900 Subject: [PATCH 073/104] =?UTF-8?q?chore:=20NetworkManager=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index 50f7611e3..f37a8a8e9 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -34,7 +34,7 @@ class NetworkManager: Networkable { func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) { AF.request(url, method: .post, parameters: encodable, encoder: JSONParameterEncoder.default, headers: httpHeaders) .validate(statusCode: 200..<300) - .responseData { response in + .response { response in switch response.result { case .success : completion() From c1e75e197cf6cfad0edb862056e3ef61855c042a Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 14:37:36 +0900 Subject: [PATCH 074/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Milestone=20DTO?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/UserDTO/MilestoneList.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift index 98088b6a1..7ec7532f3 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift @@ -8,7 +8,7 @@ import Foundation struct MilestoneList: Codable { - let milestone: [Milestone] + let data: [Milestone] } // MARK: - Milestone @@ -21,7 +21,9 @@ struct Milestone: Codable { let closedIssueCount, openedIssueCount: Int? enum CodingKeys: String, CodingKey { - case id, title, description, closedIssueCount, openedIssueCount + case id, title, description + case closedIssueCount = "closed_issue_count" + case openedIssueCount = "opened_issue_count" case createdTime = "created_time" case dueDate = "due_date" } From eb547db6b287083459f6f237559c0a84aa3023ac Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 14:38:13 +0900 Subject: [PATCH 075/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20tableViewCell?= =?UTF-8?q?=EC=97=90=20stackView=EC=B6=94=EA=B0=80=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EC=9E=AC=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueList/IssueTableViewCell.swift | 96 +++++++++++-------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index d644abf98..68afd0600 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -16,6 +16,15 @@ final class IssueTableViewCell: UITableViewCell { private var bag = DisposeBag() + var stackView: UIStackView = { + var stackView = UIStackView() + stackView.alignment = .leading + stackView.axis = .vertical + stackView.distribution = .fill + stackView.spacing = 16 + return stackView + }() + private var largeTitle: UILabel = { var label = UILabel() label.font = UIFont.boldSystemFont(ofSize: 22) @@ -33,11 +42,6 @@ final class IssueTableViewCell: UITableViewCell { return milestone }() - private var labelsCollectionView: LabelsCollectionView = { - var collectionView = LabelsCollectionView() - return collectionView - }() - private var checkBoxImageView: UIImageView = { var imageView = UIImageView() imageView.image = UIImage(systemName: "checkmark.circle.fill") @@ -46,6 +50,7 @@ final class IssueTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) + setUpStackView() addSubviews() setupAutolayout() checkBoxImageView.isHidden = true @@ -53,62 +58,71 @@ final class IssueTableViewCell: UITableViewCell { required init?(coder: NSCoder) { super.init(coder: coder) + setUpStackView() addSubviews() setupAutolayout() checkBoxImageView.isHidden = true } private func addSubviews() { - addSubview(labelsCollectionView) - addSubview(labelDescription) - addSubview(milestoneView) - addSubview(largeTitle) - addSubview(checkBoxImageView) + contentView.addSubview(stackView) + contentView.addSubview(checkBoxImageView) } - private func setupAutolayout() { - largeTitle.snp.makeConstraints { title in - title.top.equalTo(24) - title.leading.trailing.equalTo(16) - title.height.equalTo(28) - } - - labelDescription.snp.makeConstraints { label in - label.top.equalTo(largeTitle.snp.bottom).offset(16) - label.leading.trailing.equalToSuperview().inset(16) - label.height.equalTo(22) - } + private func setUpStackView() { + stackView.addArrangedSubview(largeTitle) + stackView.addArrangedSubview(labelDescription) + stackView.addArrangedSubview(milestoneView) + } - milestoneView.snp.makeConstraints { view in - view.top.equalTo(labelDescription.snp.bottom).offset(16) + private func setupAutolayout() { + stackView.snp.makeConstraints { view in + view.top.equalToSuperview().inset(24) view.leading.trailing.equalToSuperview().inset(16) - view.height.equalTo(22) } + } - labelsCollectionView.snp.makeConstraints { view in - view.top.equalTo(milestoneView.snp.bottom).offset(16) + func setUpCollectionView(view: UICollectionView) { + self.contentView.addSubview(view) + view.snp.makeConstraints { view in + view.top.equalTo(stackView.snp.bottom).offset(10) view.leading.trailing.equalToSuperview().inset(16) view.bottom.equalToSuperview() } - - checkBoxImageView.snp.makeConstraints { image in - image.top.equalToSuperview().inset(24) - image.trailing.equalToSuperview().inset(16) - image.width.height.equalTo(30) - } } - func setupIssueCell(title: String, description: String, milestoneTitle: String, relay: BehaviorRelay<[IssueLabel]>) { - self.largeTitle.text = title - self.labelDescription.text = description - self.milestoneView.setMilestoneTitle(title: milestoneTitle) + func setupIssueCell(title: String?, description: String?, milestoneTitle: String?, relay: Observable>) { + if let title = title { + self.largeTitle.text = title + largeTitle.sizeToFit() + } else { + largeTitle.isHidden = true + } + if let description = description { + self.labelDescription.text = description + labelDescription.sizeToFit() + } else { + labelDescription.isHidden = true + } + if let milestone = milestoneTitle { + self.milestoneView.setMilestoneTitle(title: milestone) + milestoneView.sizeToFit() + } else { + milestoneView.isHidden = true + } self.bindLabelCollectionView(relay: relay) } - func bindLabelCollectionView(relay: BehaviorRelay<[IssueLabel]>) { - relay.bind(to: labelsCollectionView.rx.items(cellIdentifier: LabelsCollectionViewCell.identifiers)) { index, model, cell in - guard let cell = UICollectionViewCell() as? LabelsCollectionViewCell else { return } - cell.configure(title: model.title, color: model.color) + func bindLabelCollectionView(relay: Observable>) { + let labelsCollectionView = LabelsCollectionView(frame: CGRect(x: 0, y: 0, width: contentView.frame.width, height: 100)) + self.setUpCollectionView(view: labelsCollectionView) + relay.subscribe { behaviorRelay in + behaviorRelay.bind(to: labelsCollectionView.rx.items) { collectionView, int, issueLabel in + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LabelsCollectionViewCell.identifiers, for: IndexPath(row: int, section: 0 )) as? LabelsCollectionViewCell else { return UICollectionViewCell() } + cell.configure(title: issueLabel.title, color: issueLabel.color) + return cell + } + } onCompleted: { } .disposed(by: bag) } From 11527c552b93cecfdb9b81c04279f019962d111a Mon Sep 17 00:00:00 2001 From: lena Date: Wed, 23 Jun 2021 14:45:45 +0900 Subject: [PATCH 076/104] chore: clean up code, add app AppTransportSecurity --- .../IssueDetailViewController.swift | 67 ++++++++++--------- .../Controller/ModalViewController.swift | 4 ++ iOS/issue-tracker/issue-tracker/Info.plist | 5 ++ .../Model/UserDTO/MilestoneList.swift | 10 ++- .../View/IssueList/ToolBarTextField.swift | 12 ++++ .../ViewModel/MilestoneViewModel.swift | 33 +++++++-- 6 files changed, 92 insertions(+), 39 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index ad2786dcc..d9e37adf4 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -59,15 +59,14 @@ final class IssueDetailViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .magenta - view.backgroundColor = .systemBackground - navigationController?.navigationBar.prefersLargeTitles = true + navigationItem.largeTitleDisplayMode = .always navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain, target: self, action: #selector(showIssueDetailInfo)) textField.textFieldDelegate = self tableView.dataSource = self tableView.delegate = self - tableView.frame = view.bounds tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: false, scrollPosition: .none) headerStackView.addArrangedSubview(isOpened) @@ -91,34 +90,8 @@ final class IssueDetailViewController: UIViewController { tabBarController?.tabBar.isHidden = true } - private func setupKeyboardNotification() { - let center = NotificationCenter.default - center.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] noti in - guard let strongSelf = self else { return } - if let keyboardFrame = noti.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { - strongSelf.textFieldHeightConstraint?.constant = -(keyboardFrame.cgRectValue.height - strongSelf.bottomSafeAreaHeight) - print(keyboardFrame.cgRectValue.height) - } - } - center.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { [weak self] _ in - guard let strongSelf = self else { return } - strongSelf.textFieldHeightConstraint?.constant = 0 - } - } - - private func setupAutolayout() { - headerStackView.snp.makeConstraints { $0.leading.trailing.top.bottom.equalToSuperview().inset(20) } - NSLayoutConstraint.activate([ - textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), - textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), - textField.heightAnchor.constraint(equalToConstant: 44) - ]) - textFieldHeightConstraint = textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) - textFieldHeightConstraint?.isActive = true - } - @objc - func handleTapGesture(recognizer: UITapGestureRecognizer) { + private func handleTapGesture(recognizer: UITapGestureRecognizer) { textField.resignFirstResponder() } @@ -140,7 +113,7 @@ private extension IssueDetailViewController { viewModel.subject.bind { [weak self] detail in guard let detail = detail?.data else { return } self?.navigationItem.title = detail.title - self?.isOpened.text = detail.isOpen ? "열림" : "닫힘" + self?.isOpened.text = detail.isOpen ? "Open" : "Closed" self?.authorLabel.text = "\(detail.author.name)님이 작성했습니다." guard let comment = detail.comment else { return } self?.comment = comment @@ -148,7 +121,35 @@ private extension IssueDetailViewController { } .disposed(by: disposeBag) } + + func setupKeyboardNotification() { + let center = NotificationCenter.default + center.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] noti in + guard let strongSelf = self else { return } + if let keyboardFrame = noti.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { + strongSelf.textFieldHeightConstraint?.constant = -(keyboardFrame.cgRectValue.height - strongSelf.bottomSafeAreaHeight) + } + } + center.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { [weak self] _ in + guard let strongSelf = self else { return } + strongSelf.textFieldHeightConstraint?.constant = 0 + } + } + + func setupAutolayout() { + headerStackView.snp.makeConstraints { $0.leading.trailing.top.bottom.equalToSuperview().inset(20) } + tableView.snp.makeConstraints { + $0.leading.trailing.top.bottom.equalTo(view.safeAreaLayoutGuide) } + NSLayoutConstraint.activate([ + textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), + textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), + textField.heightAnchor.constraint(equalToConstant: 44) + ]) + textFieldHeightConstraint = textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) + textFieldHeightConstraint?.isActive = true + } } + extension IssueDetailViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return comment.count @@ -156,7 +157,9 @@ extension IssueDetailViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as? IssueDetailTableViewCell else { - return UITableViewCell() + let cell = UITableViewCell() + cell.textLabel?.text = "아직 코멘트가 없습니다..." + return cell } cell.accessoryView = UIImageView(image: UIImage(systemName: "ellipsis")) cell.configure(model: comment[indexPath.row]) diff --git a/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift index 1bfc25edd..ca9dbeb7d 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/ModalViewController.swift @@ -42,6 +42,10 @@ private extension ModalViewController { } extension ModalViewController: UITableViewDataSource, UITableViewDelegate { + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return "이슈 상세 정보" + } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return labelText.count } diff --git a/iOS/issue-tracker/issue-tracker/Info.plist b/iOS/issue-tracker/issue-tracker/Info.plist index 5b531f7b2..53c6efd04 100644 --- a/iOS/issue-tracker/issue-tracker/Info.plist +++ b/iOS/issue-tracker/issue-tracker/Info.plist @@ -2,6 +2,11 @@ + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift index 98088b6a1..ef5a0984d 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/MilestoneList.swift @@ -8,7 +8,11 @@ import Foundation struct MilestoneList: Codable { - let milestone: [Milestone] + let data: [Milestone] + + enum CodingKeys: String, CodingKey { + case data + } } // MARK: - Milestone @@ -21,8 +25,10 @@ struct Milestone: Codable { let closedIssueCount, openedIssueCount: Int? enum CodingKeys: String, CodingKey { - case id, title, description, closedIssueCount, openedIssueCount + case id, title, description case createdTime = "created_time" case dueDate = "due_date" + case closedIssueCount = "closed_issue_count" + case openedIssueCount = "opened_issue_count" } } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift index dd3e09b57..0de353ccc 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/ToolBarTextField.swift @@ -7,10 +7,17 @@ import UIKit +protocol ToolBarTextFieldDelegate: AnyObject { + func register() +} + final class ToolBarTextField: UITextField { + weak var textFieldDelegate: ToolBarTextFieldDelegate? + private let sendButton: UIButton = { let button = UIButton() + button.addTarget(self, action: #selector(didTapSend), for: .touchUpInside) button.setImage(UIImage(systemName: "arrow.up.circle.fill"), for: .normal) return button }() @@ -29,4 +36,9 @@ final class ToolBarTextField: UITextField { required init?(coder: NSCoder) { super.init(coder: coder) } + + @objc + func didTapSend() { + textFieldDelegate?.register() + } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift index 0b174a65e..e1a747320 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift @@ -9,14 +9,37 @@ import Foundation import RxSwift class MilestoneViewModel { + private let networkManager = NetworkManager() var subject: Observable<[Milestone]> = Observable.just([]) - var model: [String: String] = ["제목": "", "설명": "", "완료일": ""] + var milestone: [String: String] = [:] + private var url: URL! { + return Endpoint(path: .milestone).url()! + } init() { - let urlString = "https://77b8f295-a324-4645-9ff3-3d93eaf7b630.mock.pstmn.io/milestone" - let url = URL(string: urlString)! - NetworkManager().request(url: url, decodableType: MilestoneList.self) { [weak self] data in - self?.subject = Observable.just(data.milestone) + fetch() + } + + private func fetch() { + networkManager.request(url: url, decodableType: MilestoneList.self) { [weak self] data in + self?.subject = Observable.just(data.data) + } + } + + func checkInput() { + // 타이틀 입력과 완료일 형식을 체크한다. + } + + func post() { + guard let title = milestone["title"] else { + fatalError() + } + let description = milestone["description"] + let dueDate = milestone["dueDate"] + let mile = Milestone(id: 1, title: title, description: description, createdTime: nil, dueDate: dueDate, closedIssueCount: nil, openedIssueCount: nil) + networkManager.postRequest(url: url, encodable: mile) { [weak self] in + // completion handler table view reload + self?.fetch() } } } From bd3001d8d2a2ca873484cda68a0378758b0819d8 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 16:31:53 +0900 Subject: [PATCH 077/104] =?UTF-8?q?feat:=20=E2=9C=A8=20LeftAlignedCollecti?= =?UTF-8?q?onViewFlowLayout=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 4 +++ .../View/IssueList/LabelsCollectionView.swift | 4 +-- .../LeftAlignedCollectionViewFlowLayout.swift | 29 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 iOS/issue-tracker/issue-tracker/View/IssueList/LeftAlignedCollectionViewFlowLayout.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index 027873e56..cdb6cbf3a 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ B349997F266F90710091A44A /* AppleLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B349997E266F90710091A44A /* AppleLoginButton.swift */; }; B34FF73E267AE9D8002D9C56 /* EstimatedLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */; }; B34FF741267B8800002D9C56 /* AddLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */; }; + B366CA41268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B366CA40268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift */; }; B3926D6B2681B59800F72CEE /* PatchIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3926D6A2681B59800F72CEE /* PatchIssue.swift */; }; B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB57267840290060DC85 /* LabelListViewModel.swift */; }; B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB5A26784AA50060DC85 /* LabelList.swift */; }; @@ -114,6 +115,7 @@ B349997E266F90710091A44A /* AppleLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleLoginButton.swift; sourceTree = ""; }; B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EstimatedLabelView.swift; sourceTree = ""; }; B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLabelTableViewCell.swift; sourceTree = ""; }; + B366CA40268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftAlignedCollectionViewFlowLayout.swift; sourceTree = ""; }; B3926D6A2681B59800F72CEE /* PatchIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchIssue.swift; sourceTree = ""; }; B3A5CB57267840290060DC85 /* LabelListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelListViewModel.swift; sourceTree = ""; }; B3A5CB5A26784AA50060DC85 /* LabelList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelList.swift; sourceTree = ""; }; @@ -218,6 +220,7 @@ B32FEAC42671DCD200BF37A1 /* IssueList */ = { isa = PBXGroup; children = ( + B366CA40268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift */, D04C6955267B2C560056DD79 /* ToolBarTextField.swift */, B3EADAC1267629190007C4B6 /* LabelsCollectionViewCell.swift */, B3EADABF267628FB0007C4B6 /* LabelsCollectionView.swift */, @@ -677,6 +680,7 @@ B32FEAC12671D9F600BF37A1 /* IssueListViewController.swift in Sources */, B32FEACA2671FD1600BF37A1 /* SelectBarButton.swift in Sources */, D0A88E0F26765325005877F6 /* UIKit+Extension.swift in Sources */, + B366CA41268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift in Sources */, B3F5275C2670C0EF002B0812 /* PaddingLabel.swift in Sources */, B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */, B32FEAC32671DCB100BF37A1 /* IssueTableViewCell.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift index 4d1b94b81..6c80f5f21 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift @@ -9,8 +9,8 @@ import UIKit final class LabelsCollectionView: UICollectionView { - private var labelsLayout: UICollectionViewFlowLayout = { - var layout = UICollectionViewFlowLayout() + private var labelsLayout: LeftAlignedCollectionViewFlowLayout = { + var layout = LeftAlignedCollectionViewFlowLayout() layout.scrollDirection = .vertical layout.estimatedItemSize = CGSize(width: 60, height: 20) layout.minimumLineSpacing = 10 diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/LeftAlignedCollectionViewFlowLayout.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/LeftAlignedCollectionViewFlowLayout.swift new file mode 100644 index 000000000..19c2d40a9 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/LeftAlignedCollectionViewFlowLayout.swift @@ -0,0 +1,29 @@ +// +// LeftAlignedCollectionViewFlowLayout.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/23. +// + +import UIKit + +class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout { + override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + let attributes = super.layoutAttributesForElements(in: rect) + + var leftMargin = sectionInset.left + var maxY: CGFloat = -1.0 + attributes?.forEach { layoutAttribute in + if layoutAttribute.frame.origin.y >= maxY { + leftMargin = sectionInset.left + } + + layoutAttribute.frame.origin.x = leftMargin + + leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing + maxY = max(layoutAttribute.frame.maxY, maxY) + } + + return attributes + } +} From edc6396d221ec00fca489207c191f0b8419237e5 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 16:32:23 +0900 Subject: [PATCH 078/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20collectionView?= =?UTF-8?q?=EB=A5=BC=20=EC=8A=A4=ED=83=9D=EB=B7=B0=EC=97=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueList/IssueTableViewCell.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index 68afd0600..9452f0021 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -78,16 +78,16 @@ final class IssueTableViewCell: UITableViewCell { private func setupAutolayout() { stackView.snp.makeConstraints { view in view.top.equalToSuperview().inset(24) - view.leading.trailing.equalToSuperview().inset(16) + view.leading.equalToSuperview().inset(16) + view.trailing.equalToSuperview().inset(200) } } func setUpCollectionView(view: UICollectionView) { - self.contentView.addSubview(view) + self.stackView.addArrangedSubview(view) view.snp.makeConstraints { view in - view.top.equalTo(stackView.snp.bottom).offset(10) - view.leading.trailing.equalToSuperview().inset(16) - view.bottom.equalToSuperview() + view.width.equalToSuperview() + view.height.equalTo(25) } } @@ -123,6 +123,7 @@ final class IssueTableViewCell: UITableViewCell { return cell } } onCompleted: { + self.layoutIfNeeded() } .disposed(by: bag) } From 53214909b24af5d7bb1d098e2ae4a5982830375d Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 16:32:37 +0900 Subject: [PATCH 079/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20cellSize=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard index 1a52c17ce..5740b00ec 100644 --- a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard +++ b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard @@ -17,7 +17,7 @@ - + From 7d6ec908b9142d2a7be612a5aa953893a16c050b Mon Sep 17 00:00:00 2001 From: lena Date: Wed, 23 Jun 2021 16:54:31 +0900 Subject: [PATCH 080/104] chore: add endpoint path --- .../Controller/AddMilestoneViewController.swift | 2 +- .../Controller/IssueDetailViewController.swift | 11 +++++------ .../issue-tracker/Model/Endpoint.swift | 1 + .../issue-tracker/Model/NetworkManager.swift | 2 +- .../issue-tracker/Model/UserDTO/Comment.swift | 5 +++++ .../View/IssueListViewController.swift | 4 +++- .../ViewModel/IssueDetailViewModel.swift | 17 ++++++++++------- .../ViewModel/IssueListViewModel.swift | 2 +- 8 files changed, 27 insertions(+), 17 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index afc788237..d8bf1f951 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -65,7 +65,7 @@ extension AddMilestoneViewController: UITableViewDataSource { .debounce(.milliseconds(500), scheduler: MainScheduler.instance) .subscribe(onNext: { text in let key = textLabel[indexPath.row] - self.viewModel.model[key] = text + self.viewModel.milestone[key] = text }) .disposed(by: disposeBag) } diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index d9e37adf4..3a0b9910b 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -79,7 +79,6 @@ final class IssueDetailViewController: UIViewController { setupAutolayout() setupKeyboardNotification() - fetchData() bind() view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))) @@ -102,18 +101,18 @@ final class IssueDetailViewController: UIViewController { controller.modalPresentationStyle = .custom present(controller, animated: true, completion: nil) } -} -private extension IssueDetailViewController { - func fetchData() { - viewModel.fetch() + func fetchData(id: Int) { + viewModel.fetch(id: id) } +} +private extension IssueDetailViewController { func bind() { viewModel.subject.bind { [weak self] detail in guard let detail = detail?.data else { return } self?.navigationItem.title = detail.title - self?.isOpened.text = detail.isOpen ? "Open" : "Closed" + self?.isOpened.text = detail.open ? "Open" : "Closed" self?.authorLabel.text = "\(detail.author.name)님이 작성했습니다." guard let comment = detail.comment else { return } self?.comment = comment diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index 48e55cdbe..78795a90b 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -30,6 +30,7 @@ struct Endpoint { case login = "/login" case label = "/label" case issue = "/issue" + case milestone = "/milestone" var pathString: String { return self.rawValue diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index f37a8a8e9..c8bb9eb49 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -56,7 +56,7 @@ class NetworkManager: Networkable { } } } - + func patchRequest(url: URL, encodable: T, completion: @escaping () -> Void) { AF.request(url, method: .patch, parameters: encodable, encoder: JSONParameterEncoder.default) .validate(statusCode: 200..<300) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/Comment.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/Comment.swift index fd0e48500..6acb4fc15 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/Comment.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/Comment.swift @@ -21,3 +21,8 @@ struct Comment: Codable { case content } } + +struct PostComment: Codable { + let writer: String + let comment: String +} diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index e0680d5aa..d56f0593f 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -64,14 +64,16 @@ final class IssueListViewController: UIViewController { issueTableView.rx.itemSelected .bind { [weak self] indexPath in guard let self = self, let cell = self.issueTableView.cellForRow(at: indexPath) as? IssueTableViewCell else { return } + let issue = self.issueListViewModel.issueList.value[indexPath.row] if self.issueListViewModel.selectMode.value { - let issue = self.issueListViewModel.issueList.value[indexPath.row] self.issueListViewModel.selectedCell.accept(self.issueListViewModel.selectedCell.value + [issue]) cell.selectionStyle = .none cell.check() } else { + guard let id = issue.id else { return } cell.selectionStyle = .none let controller = IssueDetailViewController() + controller.fetchData(id: id) self.navigationController?.pushViewController(controller, animated: true) } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift index 7a8fd6bd9..f01f31af4 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift @@ -10,16 +10,19 @@ import RxCocoa class IssueDetailViewModel { var subject: BehaviorRelay = BehaviorRelay(value: nil) - let issueId: String - init(id: String) { - self.issueId = id - } - - func fetch() { - guard let url = Endpoint(path: .issue).url(id: issueId) else { return } + func fetch(id: Int) { + guard let url = Endpoint(path: .issue).url(id: id) else { return } NetworkManager().request(url: url, decodableType: IssueDetail.self) { [weak self] data in self?.subject.accept(data) } } + + func post(comment: String) { + guard let url = Endpoint(path: .issue).url(id: 1) else { return } + let post = PostComment(writer: "Soo", comment: comment) + NetworkManager().postRequest(url: url, encodable: post) { + print("success") + } + } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift index 59329a8dc..23a220b92 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -31,7 +31,7 @@ class IssueListViewModel { print("success") } } - + func patchIssue(issues: [Issue]) { let encodableObject = PatchIssue(issueNumber: issues.map { $0.id! }, isOpen: false) networkManager.patchRequest(url: Endpoint(path: .issue).url()!, encodable: encodableObject) { From d63546dc6353bf0339260524eebbee8fe7604a42 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 17:43:38 +0900 Subject: [PATCH 081/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20AddLabelViewCon?= =?UTF-8?q?troller=EC=97=90=EC=84=9C=20=EB=92=A4=EB=A1=9C=EA=B0=80?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/AddLabelViewController.swift | 18 +++++++++++++----- .../Controller/LabelViewController.swift | 3 +++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index bebf055ce..7f78eefb5 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -21,6 +21,8 @@ final class AddLabelViewController: UIViewController { private var estimatedLabelView = EstimatedLabelView() private var bag = DisposeBag() private var saveButton = UIBarButtonItem(title: "저장", style: .plain, target: nil, action: nil) + private var cancelButton = UIBarButtonItem(title: "뒤로", style: .plain, target: nil, action: nil) + var reloadDataHandler: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() @@ -28,10 +30,7 @@ final class AddLabelViewController: UIViewController { self.addLabelViewModel = AddLabelViewModel(networkManager: NetworkManager()) navigationItem.title = "새로운 레이블" - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", - style: .plain, - target: self, - action: nil) + self.navigationItem.leftBarButtonItem = cancelButton self.navigationItem.rightBarButtonItem = saveButton tableView.dataSource = self @@ -39,6 +38,7 @@ final class AddLabelViewController: UIViewController { view.addSubview(estimatedLabelView) configureAutolayout() binding() + bindButton() } private func binding() { @@ -46,14 +46,22 @@ final class AddLabelViewController: UIViewController { .map { UIColor.hexStringToUIColor(hex: $0)} .bind(to: estimatedLabelView.getLabel().rx.backgroundColor) .disposed(by: bag) + } + func bindButton() { saveButton.rx.tap .subscribe { [weak self] _ in self?.addLabelViewModel.postAddedLabel(completion: { - self?.dismiss(animated: true, completion: nil) + self?.dismiss(animated: true, completion: self?.reloadDataHandler) }) } .disposed(by: bag) + + cancelButton.rx.tap + .subscribe { [weak self] _ in + self?.dismiss(animated: true, completion: nil) + } + .disposed(by: bag) } private func configureAutolayout() { diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 72f95f10d..24f665801 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -42,6 +42,9 @@ final class LabelViewController: UIViewController { @objc private func addLabelButtonTapped() { let viewController = AddLabelViewController() let navigationViewController = UINavigationController(rootViewController: viewController) + viewController.reloadDataHandler = { [weak self] in + self?.labelListViewModel.fetchLabelList() + } present(navigationViewController, animated: true, completion: nil) } From 3708dd074f507601a02b812ceab58f66296cb936 Mon Sep 17 00:00:00 2001 From: zeke Date: Wed, 23 Jun 2021 22:34:04 +0900 Subject: [PATCH 082/104] =?UTF-8?q?feat:=20=E2=9C=A8=20IssueTableViewCell?= =?UTF-8?q?=20=EC=88=98=1D=EC=A0=95=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/IssueList/IssueTableViewCell.swift | 33 +++++++++++++------ .../View/IssueList/LabelsCollectionView.swift | 2 +- .../View/IssueListViewController.swift | 18 +--------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index 9452f0021..b8a319c39 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -21,7 +21,7 @@ final class IssueTableViewCell: UITableViewCell { stackView.alignment = .leading stackView.axis = .vertical stackView.distribution = .fill - stackView.spacing = 16 + stackView.spacing = 10 return stackView }() @@ -42,6 +42,8 @@ final class IssueTableViewCell: UITableViewCell { return milestone }() + var labelsCollectionView = LabelsCollectionView() + private var checkBoxImageView: UIImageView = { var imageView = UIImageView() imageView.image = UIImage(systemName: "checkmark.circle.fill") @@ -56,6 +58,13 @@ final class IssueTableViewCell: UITableViewCell { checkBoxImageView.isHidden = true } + override func layoutSubviews() { + super.layoutSubviews() + contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 2, bottom: 0, right: 2)) + contentView.layer.cornerRadius = 20 + contentView.layer.borderWidth = 2 + } + required init?(coder: NSCoder) { super.init(coder: coder) setUpStackView() @@ -67,24 +76,29 @@ final class IssueTableViewCell: UITableViewCell { private func addSubviews() { contentView.addSubview(stackView) contentView.addSubview(checkBoxImageView) + contentView.backgroundColor = .systemYellow } private func setUpStackView() { stackView.addArrangedSubview(largeTitle) stackView.addArrangedSubview(labelDescription) stackView.addArrangedSubview(milestoneView) + stackView.addArrangedSubview(labelsCollectionView) } private func setupAutolayout() { stackView.snp.makeConstraints { view in - view.top.equalToSuperview().inset(24) - view.leading.equalToSuperview().inset(16) - view.trailing.equalToSuperview().inset(200) + view.edges.equalToSuperview().inset(10) + } + + checkBoxImageView.snp.makeConstraints { image in + image.top.equalToSuperview().inset(24) + image.trailing.equalToSuperview().inset(16) + image.width.height.equalTo(30) } } - func setUpCollectionView(view: UICollectionView) { - self.stackView.addArrangedSubview(view) + func setUpCollectionViewAutoLayout(view: UICollectionView) { view.snp.makeConstraints { view in view.width.equalToSuperview() view.height.equalTo(25) @@ -114,16 +128,15 @@ final class IssueTableViewCell: UITableViewCell { } func bindLabelCollectionView(relay: Observable>) { - let labelsCollectionView = LabelsCollectionView(frame: CGRect(x: 0, y: 0, width: contentView.frame.width, height: 100)) - self.setUpCollectionView(view: labelsCollectionView) + labelsCollectionView.dataSource = nil + self.setUpCollectionViewAutoLayout(view: labelsCollectionView) relay.subscribe { behaviorRelay in - behaviorRelay.bind(to: labelsCollectionView.rx.items) { collectionView, int, issueLabel in + behaviorRelay.bind(to: self.labelsCollectionView.rx.items) { collectionView, int, issueLabel in guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LabelsCollectionViewCell.identifiers, for: IndexPath(row: int, section: 0 )) as? LabelsCollectionViewCell else { return UICollectionViewCell() } cell.configure(title: issueLabel.title, color: issueLabel.color) return cell } } onCompleted: { - self.layoutIfNeeded() } .disposed(by: bag) } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift index 6c80f5f21..ed20478d1 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift @@ -22,7 +22,7 @@ final class LabelsCollectionView: UICollectionView { super.init(frame: frame, collectionViewLayout: labelsLayout) register(LabelsCollectionViewCell.self, forCellWithReuseIdentifier: LabelsCollectionViewCell.identifiers) isScrollEnabled = false - backgroundColor = .white + backgroundColor = .systemYellow } required init?(coder: NSCoder) { diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index d56f0593f..654a1af42 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -41,7 +41,6 @@ final class IssueListViewController: UIViewController { bindButton() bindSelectMode() bindIssueToolBar() - bindSearchController() issueListViewModel.fetchIssueList() } @@ -165,22 +164,6 @@ final class IssueListViewController: UIViewController { .disposed(by: bag) } - private func bindSearchController() { - searchController.searchBar.searchTextField.rx.text - .bind { [weak self] text in - guard let text = text?.lowercased(), let self = self else { return } - let filteredArr = self.issueListViewModel.issueList.value.filter { $0.title.localizedCaseInsensitiveContains(text) } - self.issueListViewModel.issueList.accept(filteredArr) - } - .disposed(by: bag) - - searchController.rx.didDismiss - .subscribe { [weak self] _ in -// self?.issueListViewModel.fetchIssueList() - } - .disposed(by: bag) - } - private func filterButtonTapped() { let controller = UINavigationController(rootViewController: IssueFilterViewController()) present(controller, animated: true) @@ -245,6 +228,7 @@ final class IssueListViewController: UIViewController { issueTableView.allowsMultipleSelection = true issueTableView.delegate = self issueTableView.tableFooterView = IssueTableFooterView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 300)) + issueTableView.separatorStyle = .none } } From 1bdfe375b922fd4ab82bbadc2855a86f923850af Mon Sep 17 00:00:00 2001 From: lena Date: Wed, 23 Jun 2021 23:57:45 +0900 Subject: [PATCH 083/104] feat: add github login (#15) --- .../Controller/LoginViewController.swift | 31 +++++++++++++++++++ .../issue-tracker/Login.storyboard | 28 +++++++++++++---- .../issue-tracker/Model/OAuthManager.swift | 20 +++++++----- .../Supporting Files/SceneDelegate.swift | 8 ++++- .../View/Login/GitHubLoginButton.swift | 3 +- .../issue-tracker/View/Login/LoginView.swift | 30 +++++++++--------- 6 files changed, 90 insertions(+), 30 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift index 7e71842d9..ff96b69a8 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift @@ -6,10 +6,41 @@ // import UIKit +import AuthenticationServices +@IBDesignable class LoginViewController: UIViewController { + @IBOutlet weak var contentView: LoginView! + @IBOutlet weak var githubLoginButton: GitHubLoginButton! + + private let authManager = OAuthManager(networkManager: NetworkManager()) + + @IBAction func didTapGithubLogin(_ sender: Any) { + let session = authManager.authenticate() + session.presentationContextProvider = self + session.start() + } + override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemGroupedBackground + + NotificationCenter.default.addObserver(self, selector: #selector(requestToken), name: Notification.Name.init("authorized"), object: nil) + } + + @objc func requestToken() { + authManager.requestJWTToken(completion: { + let st = UIStoryboard(name: "Main", bundle: nil) + let vc = st.instantiateViewController(withIdentifier: "tab") + vc.modalPresentationStyle = .fullScreen + self.present(vc, animated: true) + }) + } +} + +extension LoginViewController: ASWebAuthenticationPresentationContextProviding { + func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + return self.view.window ?? ASPresentationAnchor() } } diff --git a/iOS/issue-tracker/issue-tracker/Login.storyboard b/iOS/issue-tracker/issue-tracker/Login.storyboard index faed6a070..53d760a49 100644 --- a/iOS/issue-tracker/issue-tracker/Login.storyboard +++ b/iOS/issue-tracker/issue-tracker/Login.storyboard @@ -12,25 +12,41 @@ - + - + + - - - - + + + + + + + + + + + diff --git a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift index c9d8c92f4..c0d5e98c6 100644 --- a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift @@ -9,9 +9,10 @@ import Foundation import AuthenticationServices class OAuthManager { - private let cliendId = "" + private let cliendId = "4cccb9b4007d25b53a70" private lazy var authURL = URL(string: "https://github.com/login/oauth/authorize?client_id=\(cliendId)&scope=user:email")! - private let callbackUrlScheme = "issueTracker" + private let callbackUrlScheme = "issue-tracker" + private var code: String? var networkManager: Networkable @@ -28,18 +29,23 @@ class OAuthManager { print("An error occurred when attempting to sign in.") return } - print(code) - self.requestJWTToken(with: code) + self.code = code + NotificationCenter.default.post(name: Notification.Name.init("authorized"), object: self) }) return authenticationSession } - private func requestJWTToken(with code: String) { + func requestJWTToken(completion: @escaping () -> Void) { let query = URLQueryItem(name: "code", value: code) let endpoint = Endpoint(path: .login) guard let url = endpoint.url(queryItems: [query]) else { return } - networkManager.request(url: url, decodableType: [String: String].self) { (token) in - UserDefaults.standard.set(token, forKey: "token") + networkManager.request(url: url, decodableType: Token.self) { data in + print(data) + completion() } } } + +struct Token: Decodable { + let data: String +} diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift b/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift index b478923f7..ccd43030d 100644 --- a/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift +++ b/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift @@ -10,5 +10,11 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = scene as? UIWindowScene else { return } + window = UIWindow(windowScene: windowScene) + let storyboard = UIStoryboard(name: "Login", bundle: nil) + let vc = storyboard.instantiateViewController(withIdentifier: "LoginViewController") + window?.rootViewController = vc + } } diff --git a/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift b/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift index d96208ec7..7c1da7fd4 100644 --- a/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift +++ b/iOS/issue-tracker/issue-tracker/View/Login/GitHubLoginButton.swift @@ -8,7 +8,8 @@ import UIKit import SnapKit -class GitHubLoginButton: UIView { +@IBDesignable +class GitHubLoginButton: UIButton { var stackView: UIStackView = { var stackView = UIStackView() diff --git a/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift b/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift index dee4e711f..913985c65 100644 --- a/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift +++ b/iOS/issue-tracker/issue-tracker/View/Login/LoginView.swift @@ -34,8 +34,8 @@ class LoginView: UIView { return button }() - let appleLoginButton = AppleLoginButton() - let githubLoginButton = GitHubLoginButton() +// let appleLoginButton = AppleLoginButton() +// let githubLoginButton = GitHubLoginButton() let textField = IDPasswordTextField() override init(frame: CGRect) { @@ -54,8 +54,8 @@ class LoginView: UIView { func addSubViews() { addSubview(titleLabel) - addSubview(appleLoginButton) - addSubview(githubLoginButton) +// addSubview(appleLoginButton) +// addSubview(githubLoginButton) addSubview(textField) addSubview(login) addSubview(signUp) @@ -88,16 +88,16 @@ class LoginView: UIView { signUp.height.equalTo(21) } - githubLoginButton.snp.makeConstraints { button in - button.top.equalTo(login.snp.bottom).offset(175) - button.leading.trailing.equalTo(self).inset(16) - button.height.equalTo(56) - } - - appleLoginButton.snp.makeConstraints { button in - button.top.equalTo(githubLoginButton.snp.bottom).offset(14) - button.leading.trailing.equalTo(self).inset(16) - button.height.equalTo(56) - } +// githubLoginButton.snp.makeConstraints { button in +// button.top.equalTo(login.snp.bottom).offset(175) +// button.leading.trailing.equalTo(self).inset(16) +// button.height.equalTo(56) +// } +// +// appleLoginButton.snp.makeConstraints { button in +// button.top.equalTo(githubLoginButton.snp.bottom).offset(14) +// button.leading.trailing.equalTo(self).inset(16) +// button.height.equalTo(56) +// } } } From d98f1140506973e4c1971047f11e35089dfbd4f7 Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 24 Jun 2021 01:11:52 +0900 Subject: [PATCH 084/104] feat: reload data after adding new milestone --- .../Controller/AddMilestoneViewController.swift | 10 +++++++--- .../Controller/MilestoneViewController.swift | 4 ++-- .../Supporting Files/SceneDelegate.swift | 1 + .../ViewModel/MilestoneViewModel.swift | 14 ++++++++------ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index d8bf1f951..c639d7c14 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -11,7 +11,7 @@ import RxSwift class AddMilestoneViewController: UIViewController { private let disposeBag = DisposeBag() - private let viewModel: MilestoneViewModel! = MilestoneViewModel() + private let viewModel: MilestoneViewModel! = MilestoneViewModel.shared private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.register(AddMilestoneTableViewCell.self, forCellReuseIdentifier: AddMilestoneTableViewCell.reuseIdentifier) @@ -35,11 +35,14 @@ class AddMilestoneViewController: UIViewController { tableView.frame = view.bounds view.addSubview(tableView) + viewModel.completion = { + self.dismiss(animated: true) + } } @objc private func didTapSave() { - + viewModel.post() } } @@ -56,6 +59,7 @@ extension AddMilestoneViewController: UITableViewDataSource { cell.becomeFirstResponder() } let textLabel = ["제목", "설명", "완료일"] + let keys = ["title", "description", "dueDate"] cell.textLabel?.text = textLabel[indexPath.row] cell.bind { textField in textField.rx.text @@ -64,7 +68,7 @@ extension AddMilestoneViewController: UITableViewDataSource { .distinctUntilChanged() .debounce(.milliseconds(500), scheduler: MainScheduler.instance) .subscribe(onNext: { text in - let key = textLabel[indexPath.row] + let key = keys[indexPath.row] self.viewModel.milestone[key] = text }) .disposed(by: disposeBag) diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index 841b779f6..d6dd0ed9f 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -10,7 +10,7 @@ import RxSwift import RxCocoa class MilestoneViewController: UIViewController { - private let viewModel = MilestoneViewModel() + private let viewModel = MilestoneViewModel.shared private let disposeBag = DisposeBag() private let tableView: UITableView = { let tableView = UITableView() @@ -44,7 +44,7 @@ class MilestoneViewController: UIViewController { private extension MilestoneViewController { func setupObserver() { viewModel - .subject.asObservable() + .subject .subscribe(onNext: { [unowned self] _ in self.tableView.reloadData() }) diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift b/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift index ccd43030d..1b27288c0 100644 --- a/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift +++ b/iOS/issue-tracker/issue-tracker/Supporting Files/SceneDelegate.swift @@ -10,6 +10,7 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } window = UIWindow(windowScene: windowScene) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift index e1a747320..1def4524b 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift @@ -6,12 +6,14 @@ // import Foundation -import RxSwift +import RxCocoa class MilestoneViewModel { + static let shared = MilestoneViewModel() private let networkManager = NetworkManager() - var subject: Observable<[Milestone]> = Observable.just([]) + var subject: BehaviorRelay<[Milestone]> = BehaviorRelay<[Milestone]>(value: []) var milestone: [String: String] = [:] + var completion: (() -> Void)? private var url: URL! { return Endpoint(path: .milestone).url()! } @@ -20,9 +22,10 @@ class MilestoneViewModel { fetch() } - private func fetch() { + private func fetch(completion: (() -> Void)? = nil) { networkManager.request(url: url, decodableType: MilestoneList.self) { [weak self] data in - self?.subject = Observable.just(data.data) + self?.subject.accept(data.data) + completion?() } } @@ -38,8 +41,7 @@ class MilestoneViewModel { let dueDate = milestone["dueDate"] let mile = Milestone(id: 1, title: title, description: description, createdTime: nil, dueDate: dueDate, closedIssueCount: nil, openedIssueCount: nil) networkManager.postRequest(url: url, encodable: mile) { [weak self] in - // completion handler table view reload - self?.fetch() + self?.fetch(completion: self?.completion) } } } From 8477aed1861ba7532dcd6b8645c2fda45d8151f5 Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 24 Jun 2021 14:14:00 +0900 Subject: [PATCH 085/104] chore: remove achievement label in milestone view --- .../IssueDetailViewController.swift | 2 +- .../Controller/MilestoneViewController.swift | 2 +- .../Milestone/MilestoneTableViewCell.swift | 41 ++++++------------- 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift index 3a0b9910b..ab4fb4a03 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueDetailViewController.swift @@ -59,7 +59,7 @@ final class IssueDetailViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .magenta + view.backgroundColor = .systemBackground navigationItem.largeTitleDisplayMode = .always navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis"), style: .plain, target: self, action: #selector(showIssueDetailInfo)) diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index d6dd0ed9f..657e63764 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -14,7 +14,7 @@ class MilestoneViewController: UIViewController { private let disposeBag = DisposeBag() private let tableView: UITableView = { let tableView = UITableView() - tableView.rowHeight = 200 + tableView.rowHeight = 150 tableView.register(MilestoneTableViewCell.self, forCellReuseIdentifier: MilestoneTableViewCell.reuseId) return tableView }() diff --git a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift index 23a41ab08..b06215d14 100644 --- a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift @@ -14,21 +14,13 @@ class MilestoneTableViewCell: UITableViewCell { private let verticalStackView: UIStackView = { let stackView = UIStackView() - stackView.spacing = 10 + stackView.spacing = 5 stackView.axis = .vertical stackView.alignment = .fill stackView.distribution = .fillEqually return stackView }() - private let horizenStackView: UIStackView = { - let stackView = UIStackView() - stackView.axis = .horizontal - stackView.alignment = .top - stackView.distribution = .fill - return stackView - }() - private let issueLabelStackView: UIStackView = { let stackView = UIStackView() stackView.spacing = 5 @@ -45,13 +37,6 @@ class MilestoneTableViewCell: UITableViewCell { return label }() - private let achievementLabel: UILabel = { - let label = UILabel() - label.text = "50%" - label.textAlignment = .right - return label - }() - private let descriptionLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 17) @@ -61,7 +46,7 @@ class MilestoneTableViewCell: UITableViewCell { private let dueDateLabel: UILabel = { let label = UILabel() - label.font = .systemFont(ofSize: 17) + label.font = .systemFont(ofSize: 14) label.textColor = .systemGray return label }() @@ -70,19 +55,21 @@ class MilestoneTableViewCell: UITableViewCell { let label = PaddingLabel(withInsets: 5, 5, 10, 10) label.textAlignment = .center label.textColor = .white + label.font = .systemFont(ofSize: 14) label.layer.masksToBounds = true - label.layer.cornerRadius = 15 - label.backgroundColor = UIColor.hexStringToUIColor(hex: "#B1CAE5") + label.layer.cornerRadius = 8 + label.backgroundColor = .systemGreen return label }() private let closedIssue: PaddingLabel = { - let label = PaddingLabel(withInsets: 5, 5, 10, 10) + let label = PaddingLabel(withInsets: 10, 10, 10, 10) label.textAlignment = .center label.textColor = .white + label.font = .systemFont(ofSize: 14) label.layer.masksToBounds = true - label.layer.cornerRadius = 15 - label.backgroundColor = UIColor.hexStringToUIColor(hex: "#DFCD85") + label.layer.cornerRadius = 8 + label.backgroundColor = .systemRed return label }() @@ -96,9 +83,7 @@ class MilestoneTableViewCell: UITableViewCell { verticalStackView.addArrangedSubview(dueDateLabel) verticalStackView.addArrangedSubview(issueLabelStackView) - horizenStackView.addArrangedSubview(verticalStackView) - horizenStackView.addArrangedSubview(achievementLabel) - addSubview(horizenStackView) + addSubview(verticalStackView) } required init?(coder: NSCoder) { @@ -111,14 +96,12 @@ class MilestoneTableViewCell: UITableViewCell { verticalStackView.addArrangedSubview(dueDateLabel) verticalStackView.addArrangedSubview(issueLabelStackView) - horizenStackView.addArrangedSubview(verticalStackView) - horizenStackView.addArrangedSubview(achievementLabel) - addSubview(horizenStackView) + addSubview(verticalStackView) } override func layoutSubviews() { super.layoutSubviews() - horizenStackView.snp.makeConstraints { (maker) in + verticalStackView.snp.makeConstraints { (maker) in maker.edges.equalToSuperview().inset(20) } } From c4871ef8e71dcf495ac3530c7044e1dee5fd1140 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 14:22:38 +0900 Subject: [PATCH 086/104] =?UTF-8?q?feat:=20=E2=9C=A8=20CustomAlertView=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker.xcodeproj/project.pbxproj | 12 ++ .../failure.imageset/Contents.json | 21 +++ .../failure.imageset/redIcon.png | Bin 0 -> 18391 bytes .../success.imageset/Contents.json | 21 +++ .../success.imageset/greenIcon.png | Bin 0 -> 14413 bytes .../View/Alert/CustomAlertView.swift | 160 ++++++++++++++++++ 6 files changed, 214 insertions(+) create mode 100644 iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/failure.imageset/Contents.json create mode 100644 iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/failure.imageset/redIcon.png create mode 100644 iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/success.imageset/Contents.json create mode 100644 iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/success.imageset/greenIcon.png create mode 100644 iOS/issue-tracker/issue-tracker/View/Alert/CustomAlertView.swift diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index a6039f095..eee77c70f 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ B34FF73E267AE9D8002D9C56 /* EstimatedLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */; }; B34FF741267B8800002D9C56 /* AddLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */; }; B366CA41268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B366CA40268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift */; }; + B366CA43268370BA00C97190 /* CustomAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B366CA42268370BA00C97190 /* CustomAlertView.swift */; }; B3926D6B2681B59800F72CEE /* PatchIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3926D6A2681B59800F72CEE /* PatchIssue.swift */; }; B3A5CB58267840290060DC85 /* LabelListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB57267840290060DC85 /* LabelListViewModel.swift */; }; B3A5CB5B26784AA50060DC85 /* LabelList.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A5CB5A26784AA50060DC85 /* LabelList.swift */; }; @@ -119,6 +120,7 @@ B34FF73D267AE9D8002D9C56 /* EstimatedLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EstimatedLabelView.swift; sourceTree = ""; }; B34FF740267B8800002D9C56 /* AddLabelTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLabelTableViewCell.swift; sourceTree = ""; }; B366CA40268319AC00C97190 /* LeftAlignedCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftAlignedCollectionViewFlowLayout.swift; sourceTree = ""; }; + B366CA42268370BA00C97190 /* CustomAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAlertView.swift; sourceTree = ""; }; B3926D6A2681B59800F72CEE /* PatchIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchIssue.swift; sourceTree = ""; }; B3A5CB57267840290060DC85 /* LabelListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelListViewModel.swift; sourceTree = ""; }; B3A5CB5A26784AA50060DC85 /* LabelList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelList.swift; sourceTree = ""; }; @@ -215,6 +217,7 @@ 49903DAC266F558700D2A6DD /* View */ = { isa = PBXGroup; children = ( + B366CA44268370C100C97190 /* Alert */, B32FEAC42671DCD200BF37A1 /* IssueList */, D0FD50F12671B108008C6031 /* Label */, D0FD50ED2671B0E8008C6031 /* Milestone */, @@ -251,6 +254,14 @@ name = "Recovered References"; sourceTree = ""; }; + B366CA44268370C100C97190 /* Alert */ = { + isa = PBXGroup; + children = ( + B366CA42268370BA00C97190 /* CustomAlertView.swift */, + ); + path = Alert; + sourceTree = ""; + }; B3A5CB59267840310060DC85 /* ViewModel */ = { isa = PBXGroup; children = ( @@ -677,6 +688,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B366CA43268370BA00C97190 /* CustomAlertView.swift in Sources */, B3F527512670968F002B0812 /* LabelViewController.swift in Sources */, D0A88E0226732D21005877F6 /* NewIssueViewController.swift in Sources */, B3FBA7B726730B9A0006E5E6 /* IssueToolbar.swift in Sources */, diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/failure.imageset/Contents.json b/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/failure.imageset/Contents.json new file mode 100644 index 000000000..14be3e819 --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/failure.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "redIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/failure.imageset/redIcon.png b/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/failure.imageset/redIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca103a2e0a80ee9d25c6381cdc83b323ed1bf63 GIT binary patch literal 18391 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 literal 0 HcmV?d00001 diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/success.imageset/Contents.json b/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/success.imageset/Contents.json new file mode 100644 index 000000000..94fb84a5a --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/success.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "greenIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/success.imageset/greenIcon.png b/iOS/issue-tracker/issue-tracker/Supporting Files/Assets.xcassets/success.imageset/greenIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..73f6b6344adad9c63f1433bc2545bf8d17a0b5b1 GIT binary patch literal 14413 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 literal 0 HcmV?d00001 diff --git a/iOS/issue-tracker/issue-tracker/View/Alert/CustomAlertView.swift b/iOS/issue-tracker/issue-tracker/View/Alert/CustomAlertView.swift new file mode 100644 index 000000000..c6ed7588a --- /dev/null +++ b/iOS/issue-tracker/issue-tracker/View/Alert/CustomAlertView.swift @@ -0,0 +1,160 @@ +// +// PositiveAlertView.swift +// issue-tracker +// +// Created by 양준혁 on 2021/06/23. +// + +import UIKit +import SnapKit + +final class CustomAlertView: UIView { + + static let shared = CustomAlertView() + + private var contentView: ContentView = { + var view = ContentView() + return view + }() + + private var contentImage: UIImageView = { + var imageView = UIImageView() + imageView.image = UIImage(named: "success") + imageView.layer.masksToBounds = true + imageView.layer.borderWidth = 2 + imageView.layer.borderColor = UIColor.white.cgColor + imageView.layer.cornerRadius = 40 + return imageView + }() + + private var alertType: AlertType = .failure + private var handler: (() -> Void)? + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .clear + addSubview(contentView) + addSubview(contentImage) + setUpAutoLayout() + contentView.button.addTarget(self, action: #selector(dismiss), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + backgroundColor = .clear + addSubview(contentView) + addSubview(contentImage) + setUpAutoLayout() + contentView.button.addTarget(self, action: #selector(dismiss), for: .touchUpInside) + } + + private func setUpAutoLayout() { + contentView.snp.makeConstraints { view in + view.centerX.centerY.equalToSuperview() + view.leading.trailing.equalToSuperview().inset(50) + view.height.equalTo(250) + } + contentImage.snp.makeConstraints { image in + image.centerY.equalTo(contentView.snp.top) + image.centerX.equalToSuperview() + image.height.width.equalTo(80) + } + } + + func setUpAlertView(title: String, message: String, buttonTitle: String, alertType: AlertType, buttonHandler: (() -> Void)?) { + self.frame = UIScreen.main.bounds + switch alertType { + case .success: + contentImage.image = UIImage(named: "success") + contentView.button.backgroundColor = .systemGreen + case .failure: + contentImage.image = UIImage(named: "failure") + contentView.button.backgroundColor = .red + } + contentView.titleLabel.text = title + contentView.descriptionLabel.text = message + contentView.button.setTitle(buttonTitle, for: .normal) + + UIApplication.shared.connectedScenes + .filter({$0.activationState == .foregroundActive}) + .map({$0 as? UIWindowScene}) + .compactMap({$0}) + .first?.windows + .filter({$0.isKeyWindow}).first? + .addSubview(self) + + handler = buttonHandler + } + + @objc private func dismiss() { + self.removeFromSuperview() + handler?() + } +} + +enum AlertType { + case success + case failure +} + +final class ContentView: UIView { + var titleLabel: UILabel = { + var label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 26) + label.textAlignment = .center + return label + }() + + var descriptionLabel: UILabel = { + var label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.numberOfLines = 2 + label.textAlignment = .center + return label + }() + + var button: UIButton = { + var button = UIButton() + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(titleLabel) + addSubview(descriptionLabel) + addSubview(button) + backgroundColor = .white + layer.masksToBounds = true + layer.cornerRadius = 20 + setUpAutoLayout() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + addSubview(titleLabel) + addSubview(descriptionLabel) + addSubview(button) + backgroundColor = .white + layer.masksToBounds = true + layer.cornerRadius = 20 + setUpAutoLayout() + } + + private func setUpAutoLayout() { + titleLabel.snp.makeConstraints { title in + title.top.equalToSuperview().inset(60) + title.leading.trailing.equalToSuperview().inset(10) + title.height.equalTo(30) + } + descriptionLabel.snp.makeConstraints { label in + label.top.equalTo(titleLabel.snp.bottom).offset(20) + label.leading.trailing.equalToSuperview().inset(10) + label.height.equalTo(60) + } + button.snp.makeConstraints { button in + button.bottom.equalToSuperview().inset(10) + button.leading.trailing.equalToSuperview().inset(10) + button.height.equalTo(50) + } + } +} From 92bc9b620aca41827098be9968eb8a7ad356f945 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 14:26:15 +0900 Subject: [PATCH 087/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20Issue=20DTO=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Model/UserDTO/IssueList.swift | 2 +- .../View/IssueList/IssueTableViewCell.swift | 18 ++++++++---------- .../View/IssueListViewController.swift | 8 ++++---- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift index 7f427ea55..120d9fbe8 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/IssueList.swift @@ -29,7 +29,7 @@ struct Issue: Codable, Equatable { let open: Bool let createdTime: String let author: Author - let labels: [IssueLabel] + let labels: [IssueLabel]? let assignee: [Author]? let milestone: Milestone? let comment: [Comment]? diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index b8a319c39..69061e4a4 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -105,7 +105,7 @@ final class IssueTableViewCell: UITableViewCell { } } - func setupIssueCell(title: String?, description: String?, milestoneTitle: String?, relay: Observable>) { + func setupIssueCell(title: String?, description: String?, milestoneTitle: String?, issueLabels: [IssueLabel]?) { if let title = title { self.largeTitle.text = title largeTitle.sizeToFit() @@ -124,19 +124,17 @@ final class IssueTableViewCell: UITableViewCell { } else { milestoneView.isHidden = true } - self.bindLabelCollectionView(relay: relay) + self.bindLabelCollectionView(issueLabels: issueLabels) } - func bindLabelCollectionView(relay: Observable>) { + func bindLabelCollectionView(issueLabels: [IssueLabel]?) { + guard let labels = issueLabels else { return } labelsCollectionView.dataSource = nil self.setUpCollectionViewAutoLayout(view: labelsCollectionView) - relay.subscribe { behaviorRelay in - behaviorRelay.bind(to: self.labelsCollectionView.rx.items) { collectionView, int, issueLabel in - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LabelsCollectionViewCell.identifiers, for: IndexPath(row: int, section: 0 )) as? LabelsCollectionViewCell else { return UICollectionViewCell() } - cell.configure(title: issueLabel.title, color: issueLabel.color) - return cell - } - } onCompleted: { + Observable.just(labels).bind(to: self.labelsCollectionView.rx.items) { collectionView, int, issueLabel in + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LabelsCollectionViewCell.identifiers, for: IndexPath(row: int, section: 0 )) as? LabelsCollectionViewCell else { return UICollectionViewCell() } + cell.configure(title: issueLabel.title, color: issueLabel.color) + return cell } .disposed(by: bag) } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 654a1af42..a803d860f 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -53,7 +53,7 @@ final class IssueListViewController: UIViewController { issueListViewModel.issueList .bind(to: issueTableView.rx.items) { tableView, _, issue in guard let cell = tableView.dequeueReusableCell(withIdentifier: IssueTableViewCell.identifier) as? IssueTableViewCell else { return UITableViewCell() } - cell.setupIssueCell(title: issue.title, description: nil, milestoneTitle: issue.milestone?.title, relay: Observable>.just(BehaviorRelay(value: issue.labels))) + cell.setupIssueCell(title: issue.title, description: nil, milestoneTitle: issue.milestone?.title, issueLabels: issue.labels) return cell } .disposed(by: bag) @@ -186,6 +186,9 @@ final class IssueListViewController: UIViewController { private func cancelButtonTapped() { issueListViewModel.selectedCell.accept([]) + setupNavigationItem() + tabBarController?.tabBar.isHidden = false + issueToolbar.removeFromSuperview() guard let indexPath = issueTableView.indexPathsForSelectedRows else { return } for i in indexPath { issueTableView.deselectRow(at: i, animated: false) @@ -193,9 +196,6 @@ final class IssueListViewController: UIViewController { cell.uncheck() } } - setupNavigationItem() - tabBarController?.tabBar.isHidden = false - issueToolbar.removeFromSuperview() } private func setupAddIssueButtonAutolayout() { From a2a26b0854de78e19d3ac958473762720c6b7766 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 14:26:23 +0900 Subject: [PATCH 088/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20CustomAlertView?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20NetworkManag?= =?UTF-8?q?er=EC=88=98=EC=A0=95=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/NetworkManager.swift | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index c8bb9eb49..ced4db9ce 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -10,9 +10,9 @@ import Alamofire protocol Networkable { func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) - func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) - func deleteRequest(url: URL, completion: @escaping () -> Void) - func patchRequest(url: URL, encodable: T, completion: @escaping () -> Void) + func postRequest(url: URL, encodable: T, completion: @escaping (Result) -> Void) + func deleteRequest(url: URL, completion: @escaping (Result) -> Void) + func patchRequest(url: URL, encodable: T, completion: @escaping (Result) -> Void) } class NetworkManager: Networkable { @@ -31,42 +31,27 @@ class NetworkManager: Networkable { } } - func postRequest(url: URL, encodable: T, completion: @escaping () -> Void) { + func postRequest(url: URL, encodable: T, completion: @escaping (Result) -> Void) { AF.request(url, method: .post, parameters: encodable, encoder: JSONParameterEncoder.default, headers: httpHeaders) .validate(statusCode: 200..<300) .response { response in - switch response.result { - case .success : - completion() - case .failure(let error): - print(error) - } + completion(response.result) } } - func deleteRequest(url: URL, completion: @escaping () -> Void) { + func deleteRequest(url: URL, completion: @escaping (Result) -> Void) { AF.request(url, method: .delete) .validate(statusCode: 200..<300) .response { response in - switch response.result { - case .success: - completion() - case .failure(let error): - print(error) - } + completion(response.result) } } - func patchRequest(url: URL, encodable: T, completion: @escaping () -> Void) { + func patchRequest(url: URL, encodable: T, completion: @escaping (Result) -> Void) { AF.request(url, method: .patch, parameters: encodable, encoder: JSONParameterEncoder.default) .validate(statusCode: 200..<300) .response { response in - switch response.result { - case .success: - completion() - case .failure(let error): - print(error) - } + completion(response.result) } } } From 6a323a3623b15065d852bad20150101275d92ebe Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 14:28:22 +0900 Subject: [PATCH 089/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20CustomAlertView?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/AddLabelViewController.swift | 11 +++++++--- .../ViewModel/AddLabelViewModel.swift | 11 +++++++++- .../ViewModel/IssueDetailViewModel.swift | 9 ++++++-- .../ViewModel/IssueListViewModel.swift | 22 +++++++++++++++---- .../ViewModel/MilestoneViewModel.swift | 10 ++++++--- .../ViewModel/NewIssueViewModel.swift | 9 ++++++-- 6 files changed, 57 insertions(+), 15 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index 7f78eefb5..36b76e071 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -51,9 +51,14 @@ final class AddLabelViewController: UIViewController { func bindButton() { saveButton.rx.tap .subscribe { [weak self] _ in - self?.addLabelViewModel.postAddedLabel(completion: { - self?.dismiss(animated: true, completion: self?.reloadDataHandler) - }) + guard let viewModel = self?.addLabelViewModel else { return } + if viewModel.title.value == "" || viewModel.description.value == "" { + CustomAlertView.shared.setUpAlertView(title: "실패", message: "필수 입력란이 비었습니다. 다시 한번 확인해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } else { + viewModel.postAddedLabel { + self?.dismiss(animated: true, completion: nil) + } + } } .disposed(by: bag) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift index d384b84f7..b74b77d6a 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/AddLabelViewModel.swift @@ -21,6 +21,15 @@ class AddLabelViewModel { func postAddedLabel(completion: @escaping () -> Void) { let encodableLabel = IssueLabel(id: nil, title: title.value, color: color.value, fontColor: nil, description: description.value) - networkManager.postRequest(url: Endpoint(path: .label).url()!, encodable: encodableLabel, completion: completion) + networkManager.postRequest(url: Endpoint(path: .label).url()!, encodable: encodableLabel, completion: { result in + switch result { + case .success: + CustomAlertView.shared.setUpAlertView(title: "성공", message: "라벨이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { + completion() + }) + case .failure: + CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } + }) } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift index f01f31af4..7abaffb58 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueDetailViewModel.swift @@ -21,8 +21,13 @@ class IssueDetailViewModel { func post(comment: String) { guard let url = Endpoint(path: .issue).url(id: 1) else { return } let post = PostComment(writer: "Soo", comment: comment) - NetworkManager().postRequest(url: url, encodable: post) { - print("success") + NetworkManager().postRequest(url: url, encodable: post) { result in + switch result { + case .success: + CustomAlertView.shared.setUpAlertView(title: "성공", message: "라벨이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: nil) + case .failure: + CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } } } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift index 23a220b92..4e963fea6 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -27,15 +27,29 @@ class IssueListViewModel { } func deleteIssue(id: Int) { - networkManager.deleteRequest(url: Endpoint(path: .issue).url(id: 1)!) { - print("success") + networkManager.deleteRequest(url: Endpoint(path: .issue).url(id: 1)!) { result in + switch result { + case .success: + CustomAlertView.shared.setUpAlertView(title: "성공", message: "이슈가 성공적으로 삭제되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { + self.fetchIssueList() + }) + case .failure: + CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } } } func patchIssue(issues: [Issue]) { let encodableObject = PatchIssue(issueNumber: issues.map { $0.id! }, isOpen: false) - networkManager.patchRequest(url: Endpoint(path: .issue).url()!, encodable: encodableObject) { - print("success patchIssue") + networkManager.patchRequest(url: Endpoint(path: .issue).url()!, encodable: encodableObject) { result in + switch result { + case .success: + CustomAlertView.shared.setUpAlertView(title: "성공", message: "이슈가 성공적으로 닫혔습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { + self.fetchIssueList() + }) + case .failure: + CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } } } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift index e1a747320..c9d63141f 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift @@ -37,9 +37,13 @@ class MilestoneViewModel { let description = milestone["description"] let dueDate = milestone["dueDate"] let mile = Milestone(id: 1, title: title, description: description, createdTime: nil, dueDate: dueDate, closedIssueCount: nil, openedIssueCount: nil) - networkManager.postRequest(url: url, encodable: mile) { [weak self] in - // completion handler table view reload - self?.fetch() + networkManager.postRequest(url: url, encodable: mile) { result in + switch result { + case .success: + CustomAlertView.shared.setUpAlertView(title: "성공", message: "라벨이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { self.fetch() }) + case .failure: + CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } } } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift index 0e09be0a5..d36d617d2 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift @@ -20,8 +20,13 @@ class NewIssueViewModel { return } let newIssue = NewIssue(title: title, comment: content) - NetworkManager().postRequest(url: url, encodable: newIssue) { - completion() + NetworkManager().postRequest(url: url, encodable: newIssue) { result in + switch result { + case .success: + CustomAlertView.shared.setUpAlertView(title: "성공", message: "라벨이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: completion) + case .failure: + CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } } } } From 84e54463f5f951dcfce8c67dacd4c21c5bdf5855 Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 24 Jun 2021 14:32:21 +0900 Subject: [PATCH 090/104] chore: add bar button action --- .../Controller/AddMilestoneViewController.swift | 7 ++++++- .../Controller/IssueFilterViewController.swift | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index c639d7c14..84b9cc8fe 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -25,7 +25,7 @@ class AddMilestoneViewController: UIViewController { self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "뒤로", style: .plain, target: self, - action: nil) + action: #selector(didTapCancel)) self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", style: .plain, target: self, @@ -44,6 +44,11 @@ class AddMilestoneViewController: UIViewController { private func didTapSave() { viewModel.post() } + + @objc + private func didTapCancel() { + dismiss(animated: true) + } } extension AddMilestoneViewController: UITableViewDataSource { diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift index 13af37da2..392291521 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift @@ -32,7 +32,7 @@ class IssueFilterViewController: UIViewController { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "저장", style: .plain, target: self, - action: nil) + action: #selector(saveButtonTapped)) tableView.delegate = self tableView.dataSource = self tableView.frame = view.bounds @@ -44,6 +44,11 @@ class IssueFilterViewController: UIViewController { private func cancelButtonTapped() { dismiss(animated: true) } + + @objc + private func saveButtonTapped() { + dismiss(animated: true) + } } extension IssueFilterViewController: UITableViewDelegate { From d46779f1c25595bdd86c5f887e053ac6b4fd8b05 Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 24 Jun 2021 14:44:51 +0900 Subject: [PATCH 091/104] chore: add endpoint to github login --- iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard | 2 +- .../issue-tracker/Controller/LoginViewController.swift | 2 +- iOS/issue-tracker/issue-tracker/Model/Endpoint.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard index 5740b00ec..193ba2e9a 100644 --- a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard +++ b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard @@ -106,7 +106,7 @@ - + diff --git a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift index ff96b69a8..c754cbd7c 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift @@ -32,7 +32,7 @@ class LoginViewController: UIViewController { @objc func requestToken() { authManager.requestJWTToken(completion: { let st = UIStoryboard(name: "Main", bundle: nil) - let vc = st.instantiateViewController(withIdentifier: "tab") + let vc = st.instantiateViewController(withIdentifier: "main") vc.modalPresentationStyle = .fullScreen self.present(vc, animated: true) }) diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index 78795a90b..52e7efb84 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -27,7 +27,7 @@ struct Endpoint { } enum Path: String { - case login = "/login" + case login = "/user/login/oauth/githubios" case label = "/label" case issue = "/issue" case milestone = "/milestone" From 598d7f0f51d9f9f2eb00b5680082808fe25e9ad2 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 15:08:13 +0900 Subject: [PATCH 092/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20deleteIssue=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/ViewModel/IssueListViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift index 4e963fea6..6fd1d0552 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -27,7 +27,7 @@ class IssueListViewModel { } func deleteIssue(id: Int) { - networkManager.deleteRequest(url: Endpoint(path: .issue).url(id: 1)!) { result in + networkManager.deleteRequest(url: Endpoint(path: .issue).url(id: id)!) { result in switch result { case .success: CustomAlertView.shared.setUpAlertView(title: "성공", message: "이슈가 성공적으로 삭제되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { From 9eb3b83707a4f804b510a0686ca874c77b2e247c Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 15:51:27 +0900 Subject: [PATCH 093/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20IssueViewContro?= =?UTF-8?q?ller=EC=83=89=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20Endpoint?= =?UTF-8?q?=EC=9D=98=20url=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/View/IssueListViewController.swift | 5 +++++ .../issue-tracker/ViewModel/NewIssueViewModel.swift | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index a803d860f..a51a91cfc 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -42,6 +42,7 @@ final class IssueListViewController: UIViewController { bindSelectMode() bindIssueToolBar() issueListViewModel.fetchIssueList() + view.backgroundColor = .systemGroupedBackground } override func viewWillAppear(_ animated: Bool) { @@ -54,6 +55,7 @@ final class IssueListViewController: UIViewController { .bind(to: issueTableView.rx.items) { tableView, _, issue in guard let cell = tableView.dequeueReusableCell(withIdentifier: IssueTableViewCell.identifier) as? IssueTableViewCell else { return UITableViewCell() } cell.setupIssueCell(title: issue.title, description: nil, milestoneTitle: issue.milestone?.title, issueLabels: issue.labels) + cell.backgroundColor = .systemGroupedBackground return cell } .disposed(by: bag) @@ -175,6 +177,7 @@ final class IssueListViewController: UIViewController { navigationItem.title = "이슈 선택" navigationItem.searchController = nil tabBarController?.tabBar.isHidden = true + issueTableView.tableFooterView?.isHidden = true view.addSubview(issueToolbar) setupToolbarAutoulayout() } @@ -189,6 +192,7 @@ final class IssueListViewController: UIViewController { setupNavigationItem() tabBarController?.tabBar.isHidden = false issueToolbar.removeFromSuperview() + issueTableView.tableFooterView?.isHidden = false guard let indexPath = issueTableView.indexPathsForSelectedRows else { return } for i in indexPath { issueTableView.deselectRow(at: i, animated: false) @@ -224,6 +228,7 @@ final class IssueListViewController: UIViewController { } private func setupIssueTableView() { + issueTableView.backgroundColor = .systemGroupedBackground issueTableView.register(IssueTableViewCell.self, forCellReuseIdentifier: IssueTableViewCell.identifier) issueTableView.allowsMultipleSelection = true issueTableView.delegate = self diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift index d36d617d2..249ab19c9 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/NewIssueViewModel.swift @@ -12,7 +12,7 @@ class NewIssueViewModel { var content: String? func post(completion: @escaping () -> Void) { - guard let url = Endpoint(path: .label).url() else { + guard let url = Endpoint(path: .issue).url() else { return } guard let title = title, let content = content, !content.isEmpty else { From 71ace9def4a2c30064d0dd940671ba82eed25d2e Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 17:34:29 +0900 Subject: [PATCH 094/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/ViewModel/MilestoneViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift index e45b3af32..e1d487fa2 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift @@ -43,7 +43,7 @@ class MilestoneViewModel { networkManager.postRequest(url: url, encodable: mile) { result in switch result { case .success: - CustomAlertView.shared.setUpAlertView(title: "성공", message: "라벨이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { self.fetch() }) + CustomAlertView.shared.setUpAlertView(title: "성공", message: "마일스톤이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { self.fetch() }) case .failure: CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) } From 8512bc45b898965fbba588e50d3be93fde4f7b62 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 17:37:46 +0900 Subject: [PATCH 095/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20PatchIssue=20DT?= =?UTF-8?q?O=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Model/UserDTO/PatchIssue.swift | 9 +++++++++ .../View/IssueList/IssueTableViewCell.swift | 6 +++++- .../View/IssueListViewController.swift | 13 +++++++------ .../ViewModel/IssueListViewModel.swift | 5 +++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift index 54d4f56f8..1f4c2d53b 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift @@ -8,6 +8,15 @@ import Foundation struct PatchIssue: Encodable { + let data: PatchData +} + +struct PatchData: Encodable { let issueNumber: [Int] let isOpen: Bool + + enum CodingKeys: String, CodingKey { + case issueNumber = "issue_ids" + case isOpen = "is_open" + } } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift index 69061e4a4..e56f157e4 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/IssueTableViewCell.swift @@ -105,7 +105,7 @@ final class IssueTableViewCell: UITableViewCell { } } - func setupIssueCell(title: String?, description: String?, milestoneTitle: String?, issueLabels: [IssueLabel]?) { + func setupIssueCell(title: String?, description: String?, milestoneTitle: String?, issueLabels: [IssueLabel]?, isOpen: Bool) { if let title = title { self.largeTitle.text = title largeTitle.sizeToFit() @@ -124,6 +124,10 @@ final class IssueTableViewCell: UITableViewCell { } else { milestoneView.isHidden = true } + + if !isOpen { + contentView.backgroundColor = #colorLiteral(red: 0.649335742, green: 0.109275572, blue: 0.1304466426, alpha: 1) + } self.bindLabelCollectionView(issueLabels: issueLabels) } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index a51a91cfc..f771d61bb 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -54,8 +54,8 @@ final class IssueListViewController: UIViewController { issueListViewModel.issueList .bind(to: issueTableView.rx.items) { tableView, _, issue in guard let cell = tableView.dequeueReusableCell(withIdentifier: IssueTableViewCell.identifier) as? IssueTableViewCell else { return UITableViewCell() } - cell.setupIssueCell(title: issue.title, description: nil, milestoneTitle: issue.milestone?.title, issueLabels: issue.labels) cell.backgroundColor = .systemGroupedBackground + cell.setupIssueCell(title: issue.title, description: nil, milestoneTitle: issue.milestone?.title, issueLabels: issue.labels, isOpen: issue.open) return cell } .disposed(by: bag) @@ -245,15 +245,16 @@ extension IssueListViewController: UITableViewDelegate { success(true) } - let shareAction = UIContextualAction(style: .normal, title: "닫기") { _, _, success in - + let closeAction = UIContextualAction(style: .normal, title: "닫기") { [weak self] _, _, success in + guard let self = self else { return } + self.issueListViewModel.patchIssue(issues: [self.issueListViewModel.issueList.value[indexPath.row]]) success(true) } deleteAction.image = UIImage(systemName: "trash") - shareAction.image = UIImage(systemName: "archivebox") - shareAction.backgroundColor = #colorLiteral(red: 0.7988751531, green: 0.8300203681, blue: 0.9990373254, alpha: 1) + closeAction.image = UIImage(systemName: "archivebox") + closeAction.backgroundColor = #colorLiteral(red: 0.7988751531, green: 0.8300203681, blue: 0.9990373254, alpha: 1) - return UISwipeActionsConfiguration(actions: [shareAction, deleteAction]) + return UISwipeActionsConfiguration(actions: [closeAction, deleteAction]) } } diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift index 6fd1d0552..2c656318b 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -40,14 +40,15 @@ class IssueListViewModel { } func patchIssue(issues: [Issue]) { - let encodableObject = PatchIssue(issueNumber: issues.map { $0.id! }, isOpen: false) + let encodableObject = PatchIssue(data: PatchData(issueNumber: issues.map { $0.id! }, isOpen: false)) networkManager.patchRequest(url: Endpoint(path: .issue).url()!, encodable: encodableObject) { result in switch result { case .success: CustomAlertView.shared.setUpAlertView(title: "성공", message: "이슈가 성공적으로 닫혔습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { self.fetchIssueList() }) - case .failure: + case .failure(let error): + print(error) CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) } } From bdc017bea4b841cfd3d69c723ab5e37aa1543002 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 17:38:08 +0900 Subject: [PATCH 096/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EC=85=80=20?= =?UTF-8?q?=EA=B0=84=EA=B2=A9=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/LabelViewController.swift | 2 ++ .../Controller/MilestoneViewController.swift | 2 ++ .../issue-tracker/View/Label/LabelTableViewCell.swift | 11 +++++++++-- .../View/Milestone/MilestoneTableViewCell.swift | 7 +++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index 24f665801..f3f7f5be0 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -21,6 +21,7 @@ final class LabelViewController: UIViewController { super.viewDidLoad() seuptNavigationBar() labelTableView.register(LabelTableViewCell.self, forCellReuseIdentifier: LabelTableViewCell.identifier) + labelTableView.separatorStyle = .none addLabelButton.addTarget(self, action: #selector(addLabelButtonTapped), for: .touchUpInside) fetchLabel() bindTableView() @@ -34,6 +35,7 @@ final class LabelViewController: UIViewController { labelListViewModel.labelList.bind(to: labelTableView.rx.items) { tableView, _, element in guard let cell = tableView.dequeueReusableCell(withIdentifier: LabelTableViewCell.identifier) as? LabelTableViewCell else { return UITableViewCell()} cell.setupLabelCell(title: element.title, description: element.description!, color: element.color) + cell.contentView.backgroundColor = .systemYellow return cell } .disposed(by: bag) diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index 657e63764..ee5026c55 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -16,6 +16,7 @@ class MilestoneViewController: UIViewController { let tableView = UITableView() tableView.rowHeight = 150 tableView.register(MilestoneTableViewCell.self, forCellReuseIdentifier: MilestoneTableViewCell.reuseId) + tableView.separatorStyle = .none return tableView }() @@ -59,6 +60,7 @@ private extension MilestoneViewController { .items(cellIdentifier: MilestoneTableViewCell.reuseId, cellType: MilestoneTableViewCell.self)) { _, milestone, cell in cell.configure(with: milestone) + cell.contentView.backgroundColor = .systemYellow } .disposed(by: disposeBag) } diff --git a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift index 4ca176425..06ab098c8 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift @@ -37,10 +37,17 @@ final class LabelTableViewCell: UITableViewCell { addSubviews() setupAutolayout() } + + override func layoutSubviews() { + super.layoutSubviews() + contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 2, bottom: 0, right: 2)) + contentView.layer.cornerRadius = 20 + contentView.layer.borderWidth = 2 + } private func addSubviews() { - addSubview(labelView) - addSubview(labelDescription) + contentView.addSubview(labelView) + contentView.addSubview(labelDescription) } private func setupAutolayout() { diff --git a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift index b06215d14..68cf3a99c 100644 --- a/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Milestone/MilestoneTableViewCell.swift @@ -83,7 +83,7 @@ class MilestoneTableViewCell: UITableViewCell { verticalStackView.addArrangedSubview(dueDateLabel) verticalStackView.addArrangedSubview(issueLabelStackView) - addSubview(verticalStackView) + contentView.addSubview(verticalStackView) } required init?(coder: NSCoder) { @@ -96,7 +96,7 @@ class MilestoneTableViewCell: UITableViewCell { verticalStackView.addArrangedSubview(dueDateLabel) verticalStackView.addArrangedSubview(issueLabelStackView) - addSubview(verticalStackView) + contentView.addSubview(verticalStackView) } override func layoutSubviews() { @@ -104,6 +104,9 @@ class MilestoneTableViewCell: UITableViewCell { verticalStackView.snp.makeConstraints { (maker) in maker.edges.equalToSuperview().inset(20) } + contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 2, bottom: 0, right: 2)) + contentView.layer.cornerRadius = 20 + contentView.layer.borderWidth = 2 } func configure(with milestone: Milestone) { From 3aee0594fa49614ad910648629ed5f7d6b59e815 Mon Sep 17 00:00:00 2001 From: zeke Date: Thu, 24 Jun 2021 18:36:23 +0900 Subject: [PATCH 097/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20clear=20white?= =?UTF-8?q?=20space=20and=20modify=20PatchIssue=20DTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Controller/AddMilestoneViewController.swift | 2 +- .../issue-tracker/Model/UserDTO/PatchIssue.swift | 4 ---- .../issue-tracker/View/IssueList/LabelsCollectionView.swift | 2 +- .../issue-tracker/View/Label/LabelTableViewCell.swift | 2 +- .../issue-tracker/ViewModel/IssueListViewModel.swift | 2 +- .../issue-tracker/ViewModel/MilestoneViewModel.swift | 2 +- 6 files changed, 5 insertions(+), 9 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index 84b9cc8fe..aba90a9e8 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -44,7 +44,7 @@ class AddMilestoneViewController: UIViewController { private func didTapSave() { viewModel.post() } - + @objc private func didTapCancel() { dismiss(animated: true) diff --git a/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift b/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift index 1f4c2d53b..a2b18d049 100644 --- a/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift +++ b/iOS/issue-tracker/issue-tracker/Model/UserDTO/PatchIssue.swift @@ -8,10 +8,6 @@ import Foundation struct PatchIssue: Encodable { - let data: PatchData -} - -struct PatchData: Encodable { let issueNumber: [Int] let isOpen: Bool diff --git a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift index ed20478d1..a183e1670 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueList/LabelsCollectionView.swift @@ -22,7 +22,7 @@ final class LabelsCollectionView: UICollectionView { super.init(frame: frame, collectionViewLayout: labelsLayout) register(LabelsCollectionViewCell.self, forCellWithReuseIdentifier: LabelsCollectionViewCell.identifiers) isScrollEnabled = false - backgroundColor = .systemYellow + backgroundColor = .clear } required init?(coder: NSCoder) { diff --git a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift index 06ab098c8..20f02c3a5 100644 --- a/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift +++ b/iOS/issue-tracker/issue-tracker/View/Label/LabelTableViewCell.swift @@ -37,7 +37,7 @@ final class LabelTableViewCell: UITableViewCell { addSubviews() setupAutolayout() } - + override func layoutSubviews() { super.layoutSubviews() contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 2, bottom: 0, right: 2)) diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift index 2c656318b..453a49fa8 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -40,7 +40,7 @@ class IssueListViewModel { } func patchIssue(issues: [Issue]) { - let encodableObject = PatchIssue(data: PatchData(issueNumber: issues.map { $0.id! }, isOpen: false)) + let encodableObject = PatchIssue(issueNumber: issues.map { $0.id! }, isOpen: false) networkManager.patchRequest(url: Endpoint(path: .issue).url()!, encodable: encodableObject) { result in switch result { case .success: diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift index e1d487fa2..cc6551d57 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift @@ -46,7 +46,7 @@ class MilestoneViewModel { CustomAlertView.shared.setUpAlertView(title: "성공", message: "마일스톤이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { self.fetch() }) case .failure: CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) - } + } } } } From d495b8ce28eafa6b589a77bf096420c3de044c5c Mon Sep 17 00:00:00 2001 From: lena Date: Thu, 24 Jun 2021 23:25:12 +0900 Subject: [PATCH 098/104] feat: add func to delete milestone --- .../AddMilestoneViewController.swift | 2 +- .../Controller/MilestoneViewController.swift | 4 ++- .../ViewModel/MilestoneViewModel.swift | 35 +++++++++++++++---- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift index aba90a9e8..f2d326925 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddMilestoneViewController.swift @@ -35,7 +35,7 @@ class AddMilestoneViewController: UIViewController { tableView.frame = view.bounds view.addSubview(tableView) - viewModel.completion = { + viewModel.dismissCompletion = { self.dismiss(animated: true) } } diff --git a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift index ee5026c55..799079fbc 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/MilestoneViewController.swift @@ -68,7 +68,9 @@ private extension MilestoneViewController { extension MilestoneViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - let delete = UIContextualAction(style: .destructive, title: "삭제", handler: { _, _, completion in + let delete = UIContextualAction(style: .destructive, title: "삭제", handler: { [unowned self] _, _, completion in + let id = viewModel.subject.value[indexPath.row].id + viewModel.delete(id: id) completion(true) }) delete.image = UIImage(systemName: "trash") diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift index cc6551d57..2239ef3b7 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/MilestoneViewModel.swift @@ -13,7 +13,7 @@ class MilestoneViewModel { private let networkManager = NetworkManager() var subject: BehaviorRelay<[Milestone]> = BehaviorRelay<[Milestone]>(value: []) var milestone: [String: String] = [:] - var completion: (() -> Void)? + var dismissCompletion: (() -> Void)? private var url: URL! { return Endpoint(path: .milestone).url()! } @@ -29,11 +29,11 @@ class MilestoneViewModel { } } - func checkInput() { - // 타이틀 입력과 완료일 형식을 체크한다. - } - func post() { + guard checkInput() else { + CustomAlertView.shared.setUpAlertView(title: "실패", message: "제목을 반드시 입력해주세요!", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + return + } guard let title = milestone["title"] else { fatalError() } @@ -43,10 +43,33 @@ class MilestoneViewModel { networkManager.postRequest(url: url, encodable: mile) { result in switch result { case .success: - CustomAlertView.shared.setUpAlertView(title: "성공", message: "마일스톤이 등록되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { self.fetch() }) + CustomAlertView.shared.setUpAlertView(title: "성공", + message: "마일스톤이 등록되었습니다.", + buttonTitle: "확인", + alertType: .success, + buttonHandler: { [weak self] in + self?.fetch() + self?.dismissCompletion?() + }) case .failure: CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) } } } + + func delete(id: Int) { + guard let url = Endpoint(path: .milestone).url(id: id) else { return } + networkManager.deleteRequest(url: url) { [weak self] _ in + self?.fetch() + self?.dismissCompletion?() + } + } + + private func checkInput() -> Bool { + // 타이틀 입력 체크 + guard let title = milestone["title"], !title.isEmpty, title != "" else { + return false + } + return true + } } From 08d532dcfab5174577dcbdfbab9d634589aa26a3 Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 25 Jun 2021 00:14:32 +0900 Subject: [PATCH 099/104] =?UTF-8?q?feat:=20=E2=9C=A8=20=EB=9D=BC=EB=B2=A8?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/AddLabelViewController.swift | 6 ++++ .../IssueFilterViewController.swift | 2 +- .../Controller/LabelViewController.swift | 30 ++++++++++++++++++- .../ViewModel/LabelListViewModel.swift | 14 +++++++++ 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift index 36b76e071..ed48ee62c 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/AddLabelViewController.swift @@ -10,6 +10,10 @@ import RxSwift import RxCocoa import SnapKit +protocol AddLabelViewControllerDelegate: AnyObject { + func fetchData() +} + final class AddLabelViewController: UIViewController { private var addLabelViewModel: AddLabelViewModel! private lazy var tableView: UITableView = { @@ -23,6 +27,7 @@ final class AddLabelViewController: UIViewController { private var saveButton = UIBarButtonItem(title: "저장", style: .plain, target: nil, action: nil) private var cancelButton = UIBarButtonItem(title: "뒤로", style: .plain, target: nil, action: nil) var reloadDataHandler: (() -> Void)? + weak var delegate: AddLabelViewControllerDelegate? override func viewDidLoad() { super.viewDidLoad() @@ -57,6 +62,7 @@ final class AddLabelViewController: UIViewController { } else { viewModel.postAddedLabel { self?.dismiss(animated: true, completion: nil) + self?.delegate?.fetchData() } } } diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift index 392291521..281744c3d 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift @@ -44,7 +44,7 @@ class IssueFilterViewController: UIViewController { private func cancelButtonTapped() { dismiss(animated: true) } - + @objc private func saveButtonTapped() { dismiss(animated: true) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index f3f7f5be0..c33a2557b 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -23,8 +23,8 @@ final class LabelViewController: UIViewController { labelTableView.register(LabelTableViewCell.self, forCellReuseIdentifier: LabelTableViewCell.identifier) labelTableView.separatorStyle = .none addLabelButton.addTarget(self, action: #selector(addLabelButtonTapped), for: .touchUpInside) - fetchLabel() bindTableView() + labelTableView.delegate = self } private func fetchLabel() { @@ -43,6 +43,7 @@ final class LabelViewController: UIViewController { @objc private func addLabelButtonTapped() { let viewController = AddLabelViewController() + viewController.delegate = self let navigationViewController = UINavigationController(rootViewController: viewController) viewController.reloadDataHandler = { [weak self] in self?.labelListViewModel.fetchLabelList() @@ -56,3 +57,30 @@ final class LabelViewController: UIViewController { navigationItem.rightBarButtonItem = UIBarButtonItem(customView: addLabelButton) } } + +extension LabelViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + let deleteAction = UIContextualAction(style: .destructive, title: "삭제") { [weak self] _, _, success in + guard let self = self else { return } + self.labelListViewModel.deleteLabel(id: self.labelListViewModel.labelList.value[indexPath.row].id!) + success(true) + } + + let closeAction = UIContextualAction(style: .normal, title: "수정") { [weak self] _, _, success in + + success(true) + } + + deleteAction.image = UIImage(systemName: "trash") + closeAction.image = UIImage(systemName: "archivebox") + closeAction.backgroundColor = #colorLiteral(red: 0.7988751531, green: 0.8300203681, blue: 0.9990373254, alpha: 1) + + return UISwipeActionsConfiguration(actions: [closeAction, deleteAction]) + } +} + +extension LabelViewController: AddLabelViewControllerDelegate { + func fetchData() { + fetchLabel() + } +} diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift index ec75f3f95..e581f082c 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/LabelListViewModel.swift @@ -22,4 +22,18 @@ class LabelListViewModel { self?.labelList.accept(label.data) } } + + func deleteLabel(id: Int) { + networkManager.deleteRequest(url: Endpoint(path: .label).url(id: id)!) { result in + switch result { + case .success: + CustomAlertView.shared.setUpAlertView(title: "성공", message: "라벨이 성공적으로 삭제되었습니다.", buttonTitle: "확인", alertType: .success, buttonHandler: { + self.fetchLabelList() + }) + case .failure(let error): + print(error) + CustomAlertView.shared.setUpAlertView(title: "실패", message: "서버가 불안정합니다. 다시 시도해주세요.", buttonTitle: "확인", alertType: .failure, buttonHandler: nil) + } + } + } } From 00115e307ed308ef723fc0756919db51d316497d Mon Sep 17 00:00:00 2001 From: zeke Date: Fri, 25 Jun 2021 01:01:59 +0900 Subject: [PATCH 100/104] =?UTF-8?q?chore:=20=F0=9F=94=A7=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=ED=99=94=EB=A9=B4=20=EC=A0=84=ED=99=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../issue-tracker/Controller/LoginViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift index c754cbd7c..39446a8e9 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LoginViewController.swift @@ -34,7 +34,9 @@ class LoginViewController: UIViewController { let st = UIStoryboard(name: "Main", bundle: nil) let vc = st.instantiateViewController(withIdentifier: "main") vc.modalPresentationStyle = .fullScreen - self.present(vc, animated: true) + DispatchQueue.main.async { + self.present(vc, animated: true) + } }) } } From a9cd18d4f9064147556f9ffb3dc148b4a3111ae3 Mon Sep 17 00:00:00 2001 From: lena Date: Fri, 25 Jun 2021 11:16:19 +0900 Subject: [PATCH 101/104] feat: add filter open, closed issues --- iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj | 2 +- .../Controller/IssueFilterViewController.swift | 8 ++++++++ .../issue-tracker/View/IssueListViewController.swift | 7 +++++-- .../issue-tracker/ViewModel/IssueListViewModel.swift | 5 +++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj index eee77c70f..c1c488c64 100644 --- a/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj +++ b/iOS/issue-tracker/issue-tracker.xcodeproj/project.pbxproj @@ -395,13 +395,13 @@ children = ( D0A88E0C26762D51005877F6 /* ModalViewController.swift */, B32FEAC02671D9F600BF37A1 /* IssueListViewController.swift */, + D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */, B3B559E4266E095E00901C55 /* LoginViewController.swift */, B3F527502670968F002B0812 /* LabelViewController.swift */, D0FD50A9267096C0008C6031 /* MilestoneViewController.swift */, 491F08932679E5410081C5C5 /* AddLabelViewController.swift */, D0A88E0126732D21005877F6 /* NewIssueViewController.swift */, D0A88E03267334A6005877F6 /* IssueDetailViewController.swift */, - D03AF8F32677253C001C2CBF /* IssueFilterViewController.swift */, D0103BDD267B897E0079FC3D /* AddMilestoneViewController.swift */, D0DAAE292680A8DE0075E794 /* AdditionalInfoViewController.swift */, ); diff --git a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift index 281744c3d..5ae2954a4 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/IssueFilterViewController.swift @@ -9,6 +9,7 @@ import UIKit class IssueFilterViewController: UIViewController { + var viewModel: IssueListViewModel! private let cellReuseIdentifier = "IssueFilterTableViewCell" private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) @@ -47,6 +48,13 @@ class IssueFilterViewController: UIViewController { @objc private func saveButtonTapped() { + let indexPath = tableView.indexPathForSelectedRow + if indexPath == [0, 0] { + viewModel.fetchIssueList(filterBy: "open") + } + if indexPath == [0, 4] { + viewModel.fetchIssueList(filterBy: "closed") + } dismiss(animated: true) } } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index f771d61bb..3ffc400c8 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -24,6 +24,7 @@ final class IssueListViewController: UIViewController { var searchController = UISearchController(searchResultsController: nil) searchController.searchBar.setImage(UIImage(systemName: "mic.fill"), for: .bookmark, state: .normal) searchController.searchBar.showsBookmarkButton = true + searchController.obscuresBackgroundDuringPresentation = false return searchController }() @@ -167,8 +168,10 @@ final class IssueListViewController: UIViewController { } private func filterButtonTapped() { - let controller = UINavigationController(rootViewController: IssueFilterViewController()) - present(controller, animated: true) + let controller = IssueFilterViewController() + controller.viewModel = issueListViewModel + let navigation = UINavigationController(rootViewController: controller) + present(navigation, animated: true) } private func selectButtonTapped() { diff --git a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift index 453a49fa8..516cadba8 100644 --- a/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift +++ b/iOS/issue-tracker/issue-tracker/ViewModel/IssueListViewModel.swift @@ -20,8 +20,9 @@ class IssueListViewModel { self.networkManager = networkManager } - func fetchIssueList() { - networkManager.request(url: Endpoint(path: .issue).url()!, decodableType: IssueList.self) { issueList in + func fetchIssueList(filterBy string: String? = nil) { + let query = URLQueryItem(name: "is", value: string) + networkManager.request(url: Endpoint(path: .issue).url(queryItems: [query])!, decodableType: IssueList.self) { issueList in self.issueList.accept(issueList.data) } } From 8b05b742f54a2415944858a1c8cb7fee2da2a957 Mon Sep 17 00:00:00 2001 From: lena Date: Fri, 25 Jun 2021 15:49:22 +0900 Subject: [PATCH 102/104] feat: add jwt token in http header --- .../issue-tracker/Base.lproj/Main.storyboard | 4 ++-- iOS/issue-tracker/issue-tracker/Login.storyboard | 5 +++++ .../issue-tracker/Model/Endpoint.swift | 9 +++++---- .../issue-tracker/Model/NetworkManager.swift | 15 ++++++++++++--- .../issue-tracker/Model/OAuthManager.swift | 6 ++++-- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard index 193ba2e9a..71140a572 100644 --- a/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard +++ b/iOS/issue-tracker/issue-tracker/Base.lproj/Main.storyboard @@ -71,7 +71,7 @@ - + @@ -81,7 +81,7 @@ - + diff --git a/iOS/issue-tracker/issue-tracker/Login.storyboard b/iOS/issue-tracker/issue-tracker/Login.storyboard index 53d760a49..a70bcaf41 100644 --- a/iOS/issue-tracker/issue-tracker/Login.storyboard +++ b/iOS/issue-tracker/issue-tracker/Login.storyboard @@ -53,6 +53,11 @@ + + + + + diff --git a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift index 52e7efb84..c9c867d7a 100644 --- a/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift +++ b/iOS/issue-tracker/issue-tracker/Model/Endpoint.swift @@ -27,10 +27,11 @@ struct Endpoint { } enum Path: String { - case login = "/user/login/oauth/githubios" - case label = "/label" - case issue = "/issue" - case milestone = "/milestone" + case login = "/api/user/login/oauth/githubios" + case label = "/api/label" + case issue = "/api/issue" + case milestone = "/api/milestone" + case user = "/api/user" var pathString: String { return self.rawValue diff --git a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift index ced4db9ce..7cbe27053 100644 --- a/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/NetworkManager.swift @@ -16,7 +16,16 @@ protocol Networkable { } class NetworkManager: Networkable { - private let httpHeaders: HTTPHeaders = ["Content-Type": "application/json", "Accept": "application/json"] + private lazy var httpHeaders: HTTPHeaders = ["Content-Type": "application/json", + "Accept": "application/json", + "Authorization": getToken()] + + func getToken() -> String { + guard let token = UserDefaults.standard.string(forKey: "token") else { + return "" + } + return token + } func request(url: URL, decodableType: T.Type, completion: @escaping (T) -> Void) { AF.request(url, method: .get, headers: httpHeaders) @@ -40,7 +49,7 @@ class NetworkManager: Networkable { } func deleteRequest(url: URL, completion: @escaping (Result) -> Void) { - AF.request(url, method: .delete) + AF.request(url, method: .delete, headers: httpHeaders) .validate(statusCode: 200..<300) .response { response in completion(response.result) @@ -48,7 +57,7 @@ class NetworkManager: Networkable { } func patchRequest(url: URL, encodable: T, completion: @escaping (Result) -> Void) { - AF.request(url, method: .patch, parameters: encodable, encoder: JSONParameterEncoder.default) + AF.request(url, method: .patch, parameters: encodable, encoder: JSONParameterEncoder.default, headers: httpHeaders) .validate(statusCode: 200..<300) .response { response in completion(response.result) diff --git a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift index c0d5e98c6..51f74f723 100644 --- a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift @@ -10,7 +10,7 @@ import AuthenticationServices class OAuthManager { private let cliendId = "4cccb9b4007d25b53a70" - private lazy var authURL = URL(string: "https://github.com/login/oauth/authorize?client_id=\(cliendId)&scope=user:email")! + private lazy var authURL = URL(string: "https://github.com/login/oauth/authorize?client_id=\(cliendId)")! private let callbackUrlScheme = "issue-tracker" private var code: String? @@ -40,7 +40,9 @@ class OAuthManager { let endpoint = Endpoint(path: .login) guard let url = endpoint.url(queryItems: [query]) else { return } networkManager.request(url: url, decodableType: Token.self) { data in - print(data) + let token = data.data + print(token) + UserDefaults.standard.setValue(token, forKey: "token") completion() } } From 2c0dd4aa7d959381431afa5b257f841b5a7e7e07 Mon Sep 17 00:00:00 2001 From: lena Date: Fri, 25 Jun 2021 16:00:32 +0900 Subject: [PATCH 103/104] feat: add delegate to refresh issue list --- .../issue-tracker/Controller/NewIssueViewController.swift | 6 ++++++ .../issue-tracker/View/IssueListViewController.swift | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift index 308e039d5..0e59bf6e0 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/NewIssueViewController.swift @@ -10,12 +10,17 @@ import SnapKit import MarkdownKit import RxSwift +protocol NewIssueViewDelegate: AnyObject { + func refresh() +} + class NewIssueViewController: UIViewController { private let additionalInfo = ["레이블", "마일스톤", "담당자"] private let cellReuseIdentifier = "NewIssueViewCell" private let markdownParser = MarkdownParser() private var viewModel = NewIssueViewModel() private let disposeBag = DisposeBag() + weak var delegate: NewIssueViewDelegate? private let subject: UILabel = { let label = UILabel() @@ -121,6 +126,7 @@ class NewIssueViewController: UIViewController { return } viewModel.post { [weak self] in + self?.delegate?.refresh() self?.navigationController?.popViewController(animated: true) } } diff --git a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift index 3ffc400c8..282f1594c 100644 --- a/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift +++ b/iOS/issue-tracker/issue-tracker/View/IssueListViewController.swift @@ -187,6 +187,7 @@ final class IssueListViewController: UIViewController { private func addIssueButtonTapped() { let controller = NewIssueViewController() + controller.delegate = self navigationController?.pushViewController(controller, animated: true) } @@ -261,3 +262,9 @@ extension IssueListViewController: UITableViewDelegate { return UISwipeActionsConfiguration(actions: [closeAction, deleteAction]) } } + +extension IssueListViewController: NewIssueViewDelegate { + func refresh() { + issueListViewModel.fetchIssueList() + } +} From 7bf7475f100b85b045e8dc5c37022290d3ef8005 Mon Sep 17 00:00:00 2001 From: lena Date: Fri, 2 Jul 2021 00:43:35 +0900 Subject: [PATCH 104/104] chore: delete print --- .../issue-tracker/Controller/LabelViewController.swift | 3 +-- iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift index c33a2557b..d8fe8384e 100644 --- a/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift +++ b/iOS/issue-tracker/issue-tracker/Controller/LabelViewController.swift @@ -66,8 +66,7 @@ extension LabelViewController: UITableViewDelegate { success(true) } - let closeAction = UIContextualAction(style: .normal, title: "수정") { [weak self] _, _, success in - + let closeAction = UIContextualAction(style: .normal, title: "수정") {_, _, success in success(true) } diff --git a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift index 51f74f723..d545e1aee 100644 --- a/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift +++ b/iOS/issue-tracker/issue-tracker/Model/OAuthManager.swift @@ -41,7 +41,6 @@ class OAuthManager { guard let url = endpoint.url(queryItems: [query]) else { return } networkManager.request(url: url, decodableType: Token.self) { data in let token = data.data - print(token) UserDefaults.standard.setValue(token, forKey: "token") completion() }