From 5a7c360276c73f70d3b33b385caa455bf14ab881 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Thu, 20 Dec 2018 18:30:13 +0200 Subject: [PATCH 01/48] Adding projects --- App/Entities/Project.swift | 23 ++++++++ Delivery/macOS/App/AppWireframe.swift | 2 +- .../Screens/Projects/Projects.storyboard | 57 +++++++++++++++++++ .../Screens/Projects/ProjectsPresenter.swift | 21 +++++++ .../Projects/ProjectsViewController.swift | 13 +++++ Delivery/macOS/Screens/Tasks/Tasks.storyboard | 38 ++++++------- .../macOS/Screens/Tasks/TasksPresenter.swift | 3 + .../Screens/Tasks/TasksViewController.swift | 12 +++- Jirassic.xcodeproj/project.pbxproj | 32 +++++++++++ 9 files changed, 180 insertions(+), 21 deletions(-) create mode 100644 App/Entities/Project.swift create mode 100644 Delivery/macOS/Screens/Projects/Projects.storyboard create mode 100644 Delivery/macOS/Screens/Projects/ProjectsPresenter.swift create mode 100644 Delivery/macOS/Screens/Projects/ProjectsViewController.swift diff --git a/App/Entities/Project.swift b/App/Entities/Project.swift new file mode 100644 index 0000000..fd91843 --- /dev/null +++ b/App/Entities/Project.swift @@ -0,0 +1,23 @@ +// +// Project.swift +// Jirassic +// +// Created by Cristian Baluta on 20/12/2018. +// Copyright © 2018 Imagin soft. All rights reserved. +// + +import Foundation + +struct Project { + + var lastModifiedDate: Date? + var title: String + var jiraBaseUrl: String? + var jiraUser: String? + var jiraProject: String? + var jiraIssue: String? + var gitBaseUrl: String? + /// Git commits with this prefix are considered part of the project automatically + var taskNumberPrefix: String? + var objectId: String? +} diff --git a/Delivery/macOS/App/AppWireframe.swift b/Delivery/macOS/App/AppWireframe.swift index 07902ab..c1d12ac 100644 --- a/Delivery/macOS/App/AppWireframe.swift +++ b/Delivery/macOS/App/AppWireframe.swift @@ -9,7 +9,7 @@ import Cocoa enum SplitViewColumn: Int { - case calendar = 0 + case projects = 0 case tasks = 1 } diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard new file mode 100644 index 0000000..b7a5204 --- /dev/null +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift new file mode 100644 index 0000000..c526536 --- /dev/null +++ b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift @@ -0,0 +1,21 @@ +// +// ProjectsPresenter.swift +// Jirassic +// +// Created by Cristian Baluta on 20/12/2018. +// Copyright © 2018 Imagin soft. All rights reserved. +// + +import Foundation + +protocol ProjectsPresenterInput { + +} + +protocol ProjectsPresenterOutput { + +} + +class ProjectsPresenter { + +} diff --git a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift new file mode 100644 index 0000000..bb82b1b --- /dev/null +++ b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift @@ -0,0 +1,13 @@ +// +// ProjectsViewController.swift +// Jirassic +// +// Created by Cristian Baluta on 20/12/2018. +// Copyright © 2018 Imagin soft. All rights reserved. +// + +import Cocoa + +class ProjectsViewController: NSViewController { + +} diff --git a/Delivery/macOS/Screens/Tasks/Tasks.storyboard b/Delivery/macOS/Screens/Tasks/Tasks.storyboard index c6ad531..57b6330 100644 --- a/Delivery/macOS/Screens/Tasks/Tasks.storyboard +++ b/Delivery/macOS/Screens/Tasks/Tasks.storyboard @@ -1,8 +1,8 @@ - + - + @@ -39,23 +39,23 @@ - + - + - + - + - + @@ -69,11 +69,11 @@ - + - + @@ -92,11 +92,11 @@ - + - + @@ -121,11 +121,11 @@ - - + - + - + @@ -156,12 +156,12 @@ - + - + @@ -316,7 +316,7 @@ - + diff --git a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift index a3c8722..92dd8fd 100644 --- a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift @@ -29,6 +29,7 @@ protocol TasksPresenterOutput: class { func showWarning (_ show: Bool) func showMessage (_ message: MessageViewModel) func showCalendar (_ weeks: [Week]) + func showProjects (_ projects: [Project]) func showTasks (_ tasks: [Task]) func showReports (_ reports: [Report], numberOfDays: Int, type: ListType) func removeTasksController() @@ -209,6 +210,8 @@ extension TasksPresenter: TasksInteractorOutput { let day = lastSelectedDay ?? Day(dateStart: Date(), dateEnd: nil) ui.showCalendar(weeks) ui.selectDay(day) + + ui.showProjects([]) } func tasksDidLoad (_ tasks: [Task]) { diff --git a/Delivery/macOS/Screens/Tasks/TasksViewController.swift b/Delivery/macOS/Screens/Tasks/TasksViewController.swift index 5325d44..a3f1be3 100644 --- a/Delivery/macOS/Screens/Tasks/TasksViewController.swift +++ b/Delivery/macOS/Screens/Tasks/TasksViewController.swift @@ -136,7 +136,17 @@ extension TasksViewController: TasksPresenterOutput { self.presenter?.messageButtonDidPress() } } - + + func showProjects (_ projects: [Project]) { + + var frame = splitView!.subviews[SplitViewColumn.projects.rawValue].frame + frame.origin = NSPoint.zero + let projectsView = ProjectsViewController.instantiateFromStoryboard("Projects") + projectsView.view.frame = frame + splitView.subviews[SplitViewColumn.projects.rawValue].addSubview(projectsView.view) + projectsView.view.constrainToSuperview() + } + func showCalendar (_ weeks: [Week]) { calendarScrollView.weeks = weeks diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index de65c6a..f534547 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -449,6 +449,14 @@ 40FCE42C1DFC3F8700D4FD45 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE42B1DFC3F8700D4FD45 /* WelcomeViewController.swift */; }; 40FCE42E1DFC576C00D4FD45 /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 40FCE42D1DFC576C00D4FD45 /* Welcome.storyboard */; }; 40FCE4301DFD7C8D00D4FD45 /* Animatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE42F1DFD7C8D00D4FD45 /* Animatable.swift */; }; + 5623914A21CBC6A10031B882 /* ProjectsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914921CBC6A10031B882 /* ProjectsViewController.swift */; }; + 5623914B21CBC6A10031B882 /* ProjectsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914921CBC6A10031B882 /* ProjectsViewController.swift */; }; + 5623914D21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */; }; + 5623914E21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */; }; + 5623915021CBC6CD0031B882 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; + 5623915121CBC6CD0031B882 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; + 5623915321CBC9750031B882 /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5623915221CBC9750031B882 /* Projects.storyboard */; }; + 5623915421CBC9750031B882 /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5623915221CBC9750031B882 /* Projects.storyboard */; }; 564E55F3202883DB00CE4C76 /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */; }; 564E55F4202883DB00CE4C76 /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */; }; 564E55F6202884DE00CE4C76 /* WorklogsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */; }; @@ -791,6 +799,10 @@ 40FCE42B1DFC3F8700D4FD45 /* WelcomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = ""; }; 40FCE42D1DFC576C00D4FD45 /* Welcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Welcome.storyboard; sourceTree = ""; }; 40FCE42F1DFD7C8D00D4FD45 /* Animatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animatable.swift; sourceTree = ""; }; + 5623914921CBC6A10031B882 /* ProjectsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsViewController.swift; sourceTree = ""; }; + 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsPresenter.swift; sourceTree = ""; }; + 5623914F21CBC6CD0031B882 /* Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.swift; sourceTree = ""; }; + 5623915221CBC9750031B882 /* Projects.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Projects.storyboard; sourceTree = ""; }; 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsViewController.swift; sourceTree = ""; }; 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsPresenter.swift; sourceTree = ""; }; 564E55F82028857300CE4C76 /* Worklogs.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Worklogs.storyboard; sourceTree = ""; }; @@ -1343,6 +1355,7 @@ 4065D31E1DD3B44200B73201 /* Week.swift */, 28A2839920382BBB00DDCB63 /* GitCommit.swift */, 56ADBF6921C3F625008350A6 /* GitUser.swift */, + 5623914F21CBC6CD0031B882 /* Project.swift */, ); path = Entities; sourceTree = ""; @@ -1523,6 +1536,7 @@ 5685C7621DE8721100CA545E /* Account */, 4065D3721DD3B44200B73201 /* Settings */, 564E55F12028839F00CE4C76 /* Worklogs */, + 5623914821CBC66C0031B882 /* Projects */, 2845B142206AE342006EFB3B /* Calendar */, 4065D3761DD3B44200B73201 /* Tasks */, ); @@ -1653,6 +1667,16 @@ path = External; sourceTree = ""; }; + 5623914821CBC66C0031B882 /* Projects */ = { + isa = PBXGroup; + children = ( + 5623914921CBC6A10031B882 /* ProjectsViewController.swift */, + 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */, + 5623915221CBC9750031B882 /* Projects.storyboard */, + ); + path = Projects; + sourceTree = ""; + }; 564E55F12028839F00CE4C76 /* Worklogs */ = { isa = PBXGroup; children = ( @@ -2009,6 +2033,7 @@ 28B116BB21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, 287B358A20FBAFD60022F43E /* WizardCalendarView.xib in Resources */, 280D71C11ED608D6005D2689 /* Welcome.storyboard in Resources */, + 5623915321CBC9750031B882 /* Projects.storyboard in Resources */, 280D71C21ED608D6005D2689 /* Login.storyboard in Resources */, 56D90CFE20876F3C00F24442 /* WizardGitView.xib in Resources */, 280D71C31ED608D6005D2689 /* Settings.storyboard in Resources */, @@ -2065,6 +2090,7 @@ 406384891DE388C5004795A4 /* Tasks.storyboard in Resources */, 28FE18A9207B36CB00DF796E /* CocoaHookupCell.xib in Resources */, 569C4C77202319FC0049FBF1 /* GitCell.xib in Resources */, + 5623915421CBC9750031B882 /* Projects.storyboard in Resources */, 28E896631E830D6700722032 /* Placeholder.storyboard in Resources */, 56D90D0220876F4900F24442 /* WizardJiraView.xib in Resources */, 4065D3CE1DD3B44200B73201 /* Main.storyboard in Resources */, @@ -2162,6 +2188,7 @@ 56ADBF6D21C3F94D008350A6 /* GitUserParser.swift in Sources */, 280D71351ED6043D005D2689 /* StringIdGenerator.swift in Sources */, 280D71361ED6043D005D2689 /* StringArray.swift in Sources */, + 5623914D21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */, 287195362027CAD9001C237E /* TimeInteractor.swift in Sources */, 280D71371ED6043D005D2689 /* ViewAutolayout.swift in Sources */, 285465A7217858790052CB6A /* StoreView.swift in Sources */, @@ -2203,6 +2230,7 @@ 2892E811208E6275004E5298 /* LocalPreferences.swift in Sources */, 280D715D1ED6043D005D2689 /* AppLauncher.swift in Sources */, 280D715E1ED6043D005D2689 /* Versioning.swift in Sources */, + 5623914A21CBC6A10031B882 /* ProjectsViewController.swift in Sources */, 2845B14D206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */, 2845B164206AE468006EFB3B /* TaskCellPresenter.swift in Sources */, 280D715F1ED6043D005D2689 /* AppTheme.swift in Sources */, @@ -2278,6 +2306,7 @@ 284192D72018841700E64A9A /* JProject.swift in Sources */, 280D719E1ED6043D005D2689 /* SqliteRepository+Tasks.swift in Sources */, 2845154E20A6547E009F3EE1 /* KeychainWrapper.swift in Sources */, + 5623915021CBC6CD0031B882 /* Project.swift in Sources */, 2892B2A71F094C800085BAC2 /* JWorkAttribute.swift in Sources */, 2845B166206AE468006EFB3B /* TasksHeaderView.swift in Sources */, 28DCA4552018B6E700DFAE29 /* JProjectIssue.swift in Sources */, @@ -2370,6 +2399,7 @@ 4065D3A71DD3B44200B73201 /* ViewController.swift in Sources */, 4065D3BB1DD3B44200B73201 /* UserInteractor.swift in Sources */, 4065D3E31DD3B44200B73201 /* CellProtocol.swift in Sources */, + 5623914B21CBC6A10031B882 /* ProjectsViewController.swift in Sources */, 2812F61F2145031B008EE81E /* IAPHelper.swift in Sources */, 4065D3A61DD3B44200B73201 /* ViewAutolayout.swift in Sources */, 28CBB597204554A2006F9D3A /* ParseGitBranch.swift in Sources */, @@ -2395,6 +2425,7 @@ 4065D3BA1DD3B44200B73201 /* RegisterUserInteractor.swift in Sources */, 28FE18A6207B369800DF796E /* CocoaHookupCell.swift in Sources */, 4065D3B41DD3B44200B73201 /* TaskTypeEstimator.swift in Sources */, + 5623914E21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */, 285465A8217858790052CB6A /* StoreView.swift in Sources */, 4065D3D91DD3B44200B73201 /* UserNotifications.swift in Sources */, 569C4C6E202319CA0049FBF1 /* BrowserPresenter.swift in Sources */, @@ -2453,6 +2484,7 @@ 56D90CFC20876F2B00F24442 /* WizardGitView.swift in Sources */, 565E19D020A5E007003A5E2A /* RCLog.swift in Sources */, 40FCE42C1DFC3F8700D4FD45 /* WelcomeViewController.swift in Sources */, + 5623915121CBC6CD0031B882 /* Project.swift in Sources */, 28EE1F6520EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */, 2845B16B206AE468006EFB3B /* NonTaskCell.swift in Sources */, 4065D39D1DD3B44200B73201 /* Report.swift in Sources */, From f9b9363a7f293f5ca4c7634c4ddb1ae97bc964c0 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Thu, 31 Jan 2019 09:13:29 +0200 Subject: [PATCH 02/48] ... --- .../Screens/Projects/Projects.storyboard | 78 ++++++++++++++----- .../Projects/ProjectsViewController.swift | 30 +++++++ 2 files changed, 89 insertions(+), 19 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard index b7a5204..9ae876b 100644 --- a/Delivery/macOS/Screens/Projects/Projects.storyboard +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -1,8 +1,8 @@ - + - + @@ -10,10 +10,58 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -25,29 +73,21 @@ - - + + + + + + + + diff --git a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift index bb82b1b..906c19d 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift @@ -9,5 +9,35 @@ import Cocoa class ProjectsViewController: NSViewController { + @IBOutlet private weak var scrollView: NSScrollView! + @IBOutlet private weak var tableView: NSTableView! + var projects = ["Bosch Ebike", "Electric castle"] +} + +extension ProjectsViewController: NSTableViewDataSource { + + func numberOfRows (in aTableView: NSTableView) -> Int { + return projects.count + } +} + +extension ProjectsViewController: NSTableViewDelegate { + + func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { + + if (tableColumn?.identifier)?.rawValue == "isSelected" { + return nil + } + if (tableColumn?.identifier)?.rawValue == "name" { + return projects[row] + } + return nil + } + func tableView(_ tableView: NSTableView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, row: Int) { + + if (tableColumn?.identifier)?.rawValue == "isSelected" { + + } + } } From 8538238d0b8cbd3a9d4923d93ff8f0c963b97a3f Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 23 Dec 2019 10:35:28 +0200 Subject: [PATCH 03/48] Remove animations --- Delivery/macOS/Animations/NoAnimation.swift | 21 +++++++++++++++++++++ Delivery/macOS/App/AppWireframe.swift | 8 ++++---- Jirassic.xcodeproj/project.pbxproj | 7 ++++++- 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 Delivery/macOS/Animations/NoAnimation.swift diff --git a/Delivery/macOS/Animations/NoAnimation.swift b/Delivery/macOS/Animations/NoAnimation.swift new file mode 100644 index 0000000..c729ce0 --- /dev/null +++ b/Delivery/macOS/Animations/NoAnimation.swift @@ -0,0 +1,21 @@ +// +// NoAnimation.swift +// Jirassic +// +// Created by Cristian Baluta on 17/11/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa + +class NoAnimation: NSObject { + + var animationReachedMiddle: (() -> ())? + var animationFinished: (() -> ())? + weak var layer: CALayer? + + func startWithLayer (_ layer: CALayer) { + self.animationReachedMiddle!() + self.animationFinished!() + } +} diff --git a/Delivery/macOS/App/AppWireframe.swift b/Delivery/macOS/App/AppWireframe.swift index 8b9cea9..8176364 100644 --- a/Delivery/macOS/App/AppWireframe.swift +++ b/Delivery/macOS/App/AppWireframe.swift @@ -256,7 +256,7 @@ extension AppWireframe { func flipToTasksController() { let tasksController = self.tasksViewController - let flip = FlipAnimation() + let flip = NoAnimation() flip.animationReachedMiddle = { self.removeCurrentController() self.addController(tasksController) @@ -269,7 +269,7 @@ extension AppWireframe { func flipToSettingsController() { let settingsController = self.settingsViewController - let flip = FlipAnimation() + let flip = NoAnimation() flip.animationReachedMiddle = { self.removeCurrentController() self.removePlaceholder() @@ -284,7 +284,7 @@ extension AppWireframe { func flipToLoginController() { let loginController = self.loginViewController - let flip = FlipAnimation() + let flip = NoAnimation() flip.animationReachedMiddle = { self.removeController(self.currentController!) self.addController(loginController) @@ -297,7 +297,7 @@ extension AppWireframe { func flipToWizardController() { let wizardController = self.wizardViewController - let flip = FlipAnimation() + let flip = NoAnimation() flip.animationReachedMiddle = { self.removeCurrentController() self.removePlaceholder() diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 97cc38f..703bee9 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -158,6 +158,8 @@ 28279AD521C6245700376304 /* GitUsersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28279AD421C6245700376304 /* GitUsersViewController.swift */; }; 28279AD621C6245700376304 /* GitUsersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28279AD421C6245700376304 /* GitUsersViewController.swift */; }; 28279E9C1E8300E200EAF9FC /* PlaceholderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28279E9B1E8300E200EAF9FC /* PlaceholderViewController.swift */; }; + 2828B04E2381D04E0005B188 /* NoAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2828B04D2381D04E0005B188 /* NoAnimation.swift */; }; + 2828B04F2381D04E0005B188 /* NoAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2828B04D2381D04E0005B188 /* NoAnimation.swift */; }; 284192D72018841700E64A9A /* JProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 284192D62018841700E64A9A /* JProject.swift */; }; 284192D82018841700E64A9A /* JProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 284192D62018841700E64A9A /* JProject.swift */; }; 284192DA2018855B00E64A9A /* JiraRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 284192D92018855B00E64A9A /* JiraRepository+Projects.swift */; }; @@ -458,7 +460,6 @@ 5623914B21CBC6A10031B882 /* ProjectsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914921CBC6A10031B882 /* ProjectsViewController.swift */; }; 5623914D21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */; }; 5623914E21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */; }; - 5623915021CBC6CD0031B882 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; 5623915121CBC6CD0031B882 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; 5623915321CBC9750031B882 /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5623915221CBC9750031B882 /* Projects.storyboard */; }; 5623915421CBC9750031B882 /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5623915221CBC9750031B882 /* Projects.storyboard */; }; @@ -595,6 +596,7 @@ 2823C9341E4F69970055D036 /* Versioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Versioning.swift; sourceTree = ""; }; 28279AD421C6245700376304 /* GitUsersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitUsersViewController.swift; sourceTree = ""; }; 28279E9B1E8300E200EAF9FC /* PlaceholderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaceholderViewController.swift; sourceTree = ""; }; + 2828B04D2381D04E0005B188 /* NoAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoAnimation.swift; sourceTree = ""; }; 284192D62018841700E64A9A /* JProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JProject.swift; sourceTree = ""; }; 284192D92018855B00E64A9A /* JiraRepository+Projects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JiraRepository+Projects.swift"; sourceTree = ""; }; 284192DC2018A8B200E64A9A /* ModuleJiraTempo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleJiraTempo.swift; sourceTree = ""; }; @@ -1463,6 +1465,7 @@ isa = PBXGroup; children = ( 4065D3521DD3B44200B73201 /* FlipAnimation.swift */, + 2828B04D2381D04E0005B188 /* NoAnimation.swift */, 40FCE42F1DFD7C8D00D4FD45 /* Animatable.swift */, ); path = Animations; @@ -2273,6 +2276,7 @@ 287B358720FBAFC40022F43E /* WizardCalendarView.swift in Sources */, 280D71B71ED6043D005D2689 /* RepositoryInteractor.swift in Sources */, 280300D61EDB5ECA000A763E /* StatisticsInteractor.swift in Sources */, + 2828B04E2381D04E0005B188 /* NoAnimation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2298,6 +2302,7 @@ 28279AD321BBF09900376304 /* Jirassic.xcdatamodeld in Sources */, 56ADBF6E21C3F94D008350A6 /* GitUserParser.swift in Sources */, 2898D3B22185959300CF5AD4 /* EditableTimeBox.swift in Sources */, + 2828B04F2381D04E0005B188 /* NoAnimation.swift in Sources */, 4065D39F1DD3B44200B73201 /* Task.swift in Sources */, 4065D3CA1DD3B44200B73201 /* AppWireframe.swift in Sources */, 569C4C5F202319630049FBF1 /* JitCell.swift in Sources */, From 343460b7bbcf0d420cc3e13493d0c630349b701e Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Sun, 29 Dec 2019 10:57:55 +0200 Subject: [PATCH 04/48] Redesign Redesign main and tasks screen and add projects screen --- App/Entities/Project.swift | 8 +- App/Entities/Task.swift | 4 +- App/Extensions/DateExtension.swift | 32 +- App/Extensions/TableViewCell.swift | 2 +- App/Tasks/CloseDay.swift | 2 +- App/Tasks/ReadDaysInteractor.swift | 134 ++-- App/Tasks/TaskTypeSelection.swift | 2 +- Delivery/macOS/App/AppDelegate.swift | 2 +- Delivery/macOS/App/AppWireframe.swift | 107 ++-- .../macOS/Components/Components.storyboard | 292 +-------- .../Components/GitUsersViewController.swift | 29 +- .../Screens/Account/LoginPresenter.swift | 4 +- .../Screens/Calendar/Calendar.storyboard | 66 ++ .../Calendar/CalendarDayCellView.swift | 107 ++++ .../Screens/Calendar/CalendarInteractor.swift | 41 ++ .../Screens/Calendar/CalendarPresenter.swift | 89 +++ .../Screens/Calendar/CalendarScrollView.swift | 155 ----- .../Calendar/CalendarViewController.swift | 94 +++ .../Main}/Base.lproj/Main.storyboard | 169 ++++- .../macOS/Screens/Main/MainPresenter.swift | 90 +++ .../Screens/Main/MainViewController.swift | 287 +++++++++ .../Onboarding/WizardCalendarView.swift | 8 +- .../Screens/Onboarding/WizardGitView.swift | 52 +- .../Onboarding/WizardViewController.swift | 2 +- .../Projects/ProjectDetailsPresenter.swift | 69 ++ .../ProjectDetailsViewController.swift | 132 ++++ .../Screens/Projects/Projects.storyboard | 399 ++++++++++-- .../Screens/Projects/ProjectsInteractor.swift | 67 ++ .../Projects/ProjectsListViewController.swift | 80 +++ .../Screens/Projects/ProjectsPresenter.swift | 60 +- .../Projects/ProjectsViewController.swift | 74 ++- .../MonthReports/MonthReportsHeaderView.swift | 0 .../MonthReports/MonthReportsHeaderView.xib | 0 .../macOS/Screens/Reports/Reports.storyboard | 35 ++ .../Reports/ReportsDataSource.swift | 3 + .../Screens/Reports/ReportsInteractor.swift | 63 ++ .../Screens/Reports/ReportsPresenter.swift | 127 ++++ .../Reports/ReportsViewController.swift | 88 +++ .../cells}/HeaderView/ReportsHeaderView.swift | 0 .../cells}/HeaderView/ReportsHeaderView.xib | 0 .../cells}/ReportCell/ReportCell.swift | 0 .../cells}/ReportCell/ReportCell.xib | 0 .../ReportCell/ReportCellPresenter.swift | 0 ...enter.swift => CalendarAppPresenter.swift} | 10 +- .../Input/Calendar/CalendarCell.swift | 8 +- .../Screens/Settings/Input/Git/GitCell.swift | 66 +- .../Screens/Settings/Input/Git/GitCell.xib | 87 +-- .../Settings/Input/Git/GitPresenter.swift | 51 -- .../Screens/Settings/Settings.storyboard | 21 +- .../Settings/SettingsViewController.swift | 2 +- .../AllTasks/HeaderView/TasksHeaderView.swift | 72 --- .../AllTasks/HeaderView/TasksHeaderView.xib | 96 --- Delivery/macOS/Screens/Tasks/DataSource.swift | 7 +- .../Tasks}/EditableTimeBox.swift | 9 +- .../Tasks}/NewTaskViewController.swift | 0 Delivery/macOS/Screens/Tasks/Tasks.storyboard | 595 ++++++++++-------- .../{AllTasks => }/TasksDataSource.swift | 57 +- .../macOS/Screens/Tasks/TasksInteractor.swift | 27 +- .../macOS/Screens/Tasks/TasksPresenter.swift | 151 +---- .../macOS/Screens/Tasks/TasksScrollView.swift | 96 +-- .../Screens/Tasks/TasksViewController.swift | 275 +++----- .../Tasks}/TimeBox.swift | 27 +- .../Tasks}/TimeBoxViewController.swift | 0 .../{AllTasks => cells}/CellProtocol.swift | 0 .../cells/CloseDayCell/CloseDayCell.swift | 27 + .../Tasks/cells/CloseDayCell/CloseDayCell.xib | 60 ++ .../cells/ClosedDayCell/ClosedDayCell.swift | 42 ++ .../cells/ClosedDayCell/ClosedDayCell.xib | 55 ++ .../NonTaskCell/NonTaskCell.swift | 17 +- .../NonTaskCell/NonTaskCell.xib | 57 +- .../TaskCell/TaskCell.swift | 17 +- .../{AllTasks => cells}/TaskCell/TaskCell.xib | 111 +++- .../TaskCell/TaskCellPresenter.swift | 3 + .../TaskCell/TaskCellTests.swift | 0 .../Screens/Worklogs/Worklogs.storyboard | 71 +-- .../Screens/Worklogs/WorklogsPresenter.swift | 26 +- .../Worklogs/WorklogsViewController.swift | 4 +- Jirassic.xcodeproj/project.pbxproj | 260 ++++++-- 78 files changed, 3436 insertions(+), 1946 deletions(-) create mode 100644 Delivery/macOS/Screens/Calendar/Calendar.storyboard create mode 100644 Delivery/macOS/Screens/Calendar/CalendarDayCellView.swift create mode 100644 Delivery/macOS/Screens/Calendar/CalendarInteractor.swift create mode 100644 Delivery/macOS/Screens/Calendar/CalendarPresenter.swift delete mode 100644 Delivery/macOS/Screens/Calendar/CalendarScrollView.swift create mode 100644 Delivery/macOS/Screens/Calendar/CalendarViewController.swift rename Delivery/macOS/{ => Screens/Main}/Base.lproj/Main.storyboard (78%) create mode 100644 Delivery/macOS/Screens/Main/MainPresenter.swift create mode 100644 Delivery/macOS/Screens/Main/MainViewController.swift create mode 100644 Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift create mode 100644 Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift create mode 100644 Delivery/macOS/Screens/Projects/ProjectsInteractor.swift create mode 100644 Delivery/macOS/Screens/Projects/ProjectsListViewController.swift rename Delivery/macOS/Screens/{Tasks => Reports}/MonthReports/MonthReportsHeaderView.swift (100%) rename Delivery/macOS/Screens/{Tasks => Reports}/MonthReports/MonthReportsHeaderView.xib (100%) create mode 100644 Delivery/macOS/Screens/Reports/Reports.storyboard rename Delivery/macOS/Screens/{Tasks => }/Reports/ReportsDataSource.swift (93%) create mode 100644 Delivery/macOS/Screens/Reports/ReportsInteractor.swift create mode 100644 Delivery/macOS/Screens/Reports/ReportsPresenter.swift create mode 100644 Delivery/macOS/Screens/Reports/ReportsViewController.swift rename Delivery/macOS/Screens/{Tasks/Reports => Reports/cells}/HeaderView/ReportsHeaderView.swift (100%) rename Delivery/macOS/Screens/{Tasks/Reports => Reports/cells}/HeaderView/ReportsHeaderView.xib (100%) rename Delivery/macOS/Screens/{Tasks/Reports => Reports/cells}/ReportCell/ReportCell.swift (100%) rename Delivery/macOS/Screens/{Tasks/Reports => Reports/cells}/ReportCell/ReportCell.xib (100%) rename Delivery/macOS/Screens/{Tasks/Reports => Reports/cells}/ReportCell/ReportCellPresenter.swift (100%) rename Delivery/macOS/Screens/Settings/Input/Calendar/{CalendarPresenter.swift => CalendarAppPresenter.swift} (92%) delete mode 100644 Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.swift delete mode 100644 Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.xib rename Delivery/macOS/{Components => Screens/Tasks}/EditableTimeBox.swift (90%) rename Delivery/macOS/{Components => Screens/Tasks}/NewTaskViewController.swift (100%) rename Delivery/macOS/Screens/Tasks/{AllTasks => }/TasksDataSource.swift (68%) rename Delivery/macOS/{Components => Screens/Tasks}/TimeBox.swift (74%) rename Delivery/macOS/{Components => Screens/Tasks}/TimeBoxViewController.swift (100%) rename Delivery/macOS/Screens/Tasks/{AllTasks => cells}/CellProtocol.swift (100%) create mode 100644 Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.swift create mode 100644 Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.xib create mode 100644 Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.swift create mode 100644 Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.xib rename Delivery/macOS/Screens/Tasks/{AllTasks => cells}/NonTaskCell/NonTaskCell.swift (94%) rename Delivery/macOS/Screens/Tasks/{AllTasks => cells}/NonTaskCell/NonTaskCell.xib (72%) rename Delivery/macOS/Screens/Tasks/{AllTasks => cells}/TaskCell/TaskCell.swift (96%) rename Delivery/macOS/Screens/Tasks/{AllTasks => cells}/TaskCell/TaskCell.xib (59%) rename Delivery/macOS/Screens/Tasks/{AllTasks => cells}/TaskCell/TaskCellPresenter.swift (95%) rename Delivery/macOS/Screens/Tasks/{AllTasks => cells}/TaskCell/TaskCellTests.swift (100%) diff --git a/App/Entities/Project.swift b/App/Entities/Project.swift index fd91843..f79b75e 100644 --- a/App/Entities/Project.swift +++ b/App/Entities/Project.swift @@ -9,15 +9,17 @@ import Foundation struct Project { - + + var objectId: String? var lastModifiedDate: Date? var title: String var jiraBaseUrl: String? var jiraUser: String? var jiraProject: String? var jiraIssue: String? - var gitBaseUrl: String? + + var gitBaseUrls: [String] + var gitUsers: [String] /// Git commits with this prefix are considered part of the project automatically var taskNumberPrefix: String? - var objectId: String? } diff --git a/App/Entities/Task.swift b/App/Entities/Task.swift index 63316c9..caed9bf 100644 --- a/App/Entities/Task.swift +++ b/App/Entities/Task.swift @@ -28,9 +28,9 @@ enum TaskType: Int { case .startDay: return "Working day started" case .endDay: return "Working day ended" case .scrum: return "Scrum meeting" - case .lunch: return "Lunch break" + case .lunch: return "Lunch break 🥑🥑🥑" case .meeting: return "Meeting" - case .waste: return "Social & Media" + case .waste: return "Social & Media 👤👤👤" case .learning: return "Learning session" case .coderev: return "Reviewing and fixing code" case .calendar: return "Calendar event" diff --git a/App/Extensions/DateExtension.swift b/App/Extensions/DateExtension.swift index 8ed86b3..a27be12 100644 --- a/App/Extensions/DateExtension.swift +++ b/App/Extensions/DateExtension.swift @@ -12,6 +12,7 @@ let ymdUnitFlags: Set = [.year, .month, .day] let ymdhmsUnitFlags: Set = [.year, .month, .weekday, .day, .hour, .minute, .second] let gregorian = Calendar(identifier: Calendar.Identifier.gregorian) +/// Instantiate a date extension Date { init (hour: Int, minute: Int, second: Int = 0) { @@ -51,6 +52,7 @@ extension Date { } } +/// Format the date extension Date { func HHmmddMM() -> String { @@ -90,6 +92,12 @@ extension Date { return f.string(from: self) } + func E() -> String { + let f = DateFormatter() + f.dateFormat = "E" + return f.string(from: self) + } + // eg. Thursday, February 01 func EEEEMMMMdd() -> String { let f = DateFormatter() @@ -139,6 +147,12 @@ extension Date { f.dateFormat = "YYYY" return f.string(from: self) } + + func MMMyyyy() -> String { + let f = DateFormatter() + f.dateFormat = "MMM yyyy" + return f.string(from: self) + } } extension Date { @@ -182,10 +196,8 @@ extension Date { } func daysInMonth() -> Int { - - let daysRange = gregorian.range(of: Calendar.Component.day, in: Calendar.Component.month, for: self) - - return daysRange!.count as Int + let daysRange = gregorian.range(of: .day, in: .month, for: self) + return daysRange?.count ?? 0 } func components() -> (hour: Int, minute: Int) { @@ -234,10 +246,22 @@ extension Date { return gregorian.date(from: comps)! } + func dateByUpdating (day: Int) -> Date { + + var comps = gregorian.dateComponents(ymdhmsUnitFlags, from: self) + comps.day = day + + return gregorian.date(from: comps)! + } + func dateByKeepingTime() -> Date { let comps = gregorian.dateComponents(ymdhmsUnitFlags, from: self) return Date().dateByUpdating(hour: comps.hour!, minute: comps.minute!) } + + func dateByAddingMonths(_ numberOfMonths: Int) -> Date { + return Calendar.current.date(byAdding: .month, value: numberOfMonths, to: self) ?? self + } static func parseHHmm (_ hhmm: String) -> (hour: Int, min: Int) { let hm = hhmm.components(separatedBy: ":") diff --git a/App/Extensions/TableViewCell.swift b/App/Extensions/TableViewCell.swift index f537b0d..eb6437e 100644 --- a/App/Extensions/TableViewCell.swift +++ b/App/Extensions/TableViewCell.swift @@ -45,7 +45,7 @@ extension TableViewCell { // return UIStoryboard(name: name, bundle: nil).instantiateViewControllerWithIdentifier(self.className) as! T #else guard let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: className), owner: self) as? T else { - fatalError("Cell \(className) might not be registered in thsi tableView") + fatalError("Cell \(className) might not be registered in this tableView") } return cell #endif diff --git a/App/Tasks/CloseDay.swift b/App/Tasks/CloseDay.swift index 1df9c78..7ee7e23 100644 --- a/App/Tasks/CloseDay.swift +++ b/App/Tasks/CloseDay.swift @@ -9,7 +9,7 @@ import Foundation import RCLog -class CloseDay { +class CloseDayInteractor { func close (with tasks: [Task]) { diff --git a/App/Tasks/ReadDaysInteractor.swift b/App/Tasks/ReadDaysInteractor.swift index 61a28be..4eb8660 100644 --- a/App/Tasks/ReadDaysInteractor.swift +++ b/App/Tasks/ReadDaysInteractor.swift @@ -19,7 +19,7 @@ class ReadDaysInteractor: RepositoryInteractor { /// @parameters /// completion block will be called once with local tasks and once with updated tasks if remote had any changes to download func queryAll (_ completion: @escaping (_ weeks: [Week]) -> Void) { - query(startingDate: Date(timeIntervalSince1970: 0), completion: completion) + query(startingDate: Date(timeIntervalSince1970: 0), endingDate: Date(), completion: completion) } /// Query all startDay and endDay objects from the local repository @@ -27,31 +27,32 @@ class ReadDaysInteractor: RepositoryInteractor { /// @parameters /// startingDate - Query between this date and current date /// completion block - will be called once with local tasks and once with updated tasks if remote had any changes to download - func query (startingDate: Date, completion: @escaping (_ weeks: [Week]) -> Void) { + func query (startingDate: Date, endingDate: Date, completion: @escaping (_ weeks: [Week]) -> Void) { - queryLocalTasks(startDate: startingDate, endDate: Date()) { [weak self] (tasks: [Task]) in + queryLocalTasks(startDate: startingDate, endDate: endingDate) { [weak self] (tasks: [Task]) in - guard let _self = self else { + guard let self = self else { return } - _self.tasks = tasks - completion(_self.weeks()) + self.tasks = tasks + completion(self.createWeeks()) - if let remoteRepository = _self.remoteRepository { + guard let remoteRepository = self.remoteRepository else { + return + } - let sync = RCSync(localRepository: _self.repository, remoteRepository: remoteRepository) - sync.start { [weak self] hasIncomingChanges in - - guard let _self = self, hasIncomingChanges else { - return - } - // Delete dusplicate start day - RemoveDuplicate(repository: _self.repository, remoteRepository: _self.remoteRepository, date: Date()).execute() - // Fetch again the local tasks if they were updated - _self.queryLocalTasks(startDate: startingDate, endDate: Date()) { (tasks: [Task]) in - _self.tasks = tasks - completion(_self.weeks()) - } + let sync = RCSync(localRepository: self.repository, remoteRepository: remoteRepository) + sync.start { [weak self] hasIncomingChanges in + + guard let self = self, hasIncomingChanges else { + return + } + // Delete dusplicate start day + RemoveDuplicate(repository: self.repository, remoteRepository: self.remoteRepository, date: Date()).execute() + // Fetch again the local tasks if they were updated + self.queryLocalTasks(startDate: startingDate, endDate: Date()) { (tasks: [Task]) in + self.tasks = tasks + completion(self.createWeeks()) } } } @@ -71,50 +72,75 @@ class ReadDaysInteractor: RepositoryInteractor { }) } - private func weeks() -> [Week] { + private func createWeeks() -> [Week] { - var objects = [Week]() + var weeks = [Week]() var referenceDate = Date.distantFuture for task in tasks { if !task.endDate.isSameWeekAs(referenceDate) { referenceDate = task.endDate - let obj = Week(date: task.endDate) - obj.days = days(ofWeek: obj) - objects.append(obj) + let week = Week(date: task.endDate) + week.days = createDaysFromAscendingTasks(ofWeek: week) + weeks.append(week) } } - return objects + return weeks } - private func days() -> [Day] { - - var objects = [Day]() - var obj: Day? - var referenceDate = Date.distantFuture - - for task in tasks { - if task.endDate.isSameDayAs(referenceDate) { - if task.taskType == .endDay { - let tempObj = objects.removeLast() - obj = Day(dateStart: tempObj.dateStart, dateEnd: task.endDate) - objects.append(obj!) - } - } else { - referenceDate = task.endDate - obj = Day(dateStart: task.endDate, dateEnd: nil) - objects.append(obj!) - } - } - - return objects - } +// private func createDays() -> [Day] { +// +// var objects = [Day]() +// var obj: Day? +// var referenceDate = Date.distantFuture +// +// for task in tasks { +// if task.endDate.isSameDayAs(referenceDate) { +// if task.taskType == .endDay { +// let tempObj = objects.removeLast() +// obj = Day(dateStart: tempObj.dateStart, dateEnd: task.endDate) +// objects.append(obj!) +// } +// } else { +// referenceDate = task.endDate +// obj = Day(dateStart: task.endDate, dateEnd: nil) +// objects.append(obj!) +// } +// } +// +// return objects +// } - private func sorted (tasks: [Task]) -> [Task] { - return tasks.sorted { (task1: Task, task2: Task) -> Bool in - return task1.endDate.compare(task2.endDate) == .orderedDescending + private func createDaysFromAscendingTasks (ofWeek week: Week) -> [Day] { + + var days = [Day]() + var activeDay: Day? + var referenceDate = Date.distantFuture + + for task in tasks { + guard task.endDate.isSameWeekAs(week.date) else { + continue + } + switch task.taskType { + case .startDay: + // New day found + referenceDate = task.endDate + activeDay = Day(dateStart: referenceDate, dateEnd: nil) + days.append(activeDay!) + case .endDay: + // End of day found. + guard task.endDate.isSameDayAs(referenceDate) else { + continue + } + let tempDay = days.removeLast() + activeDay = Day(dateStart: tempDay.dateStart, dateEnd: task.endDate) + days.append(activeDay!) + default: break + } } + + return days } private func days (ofWeek week: Week) -> [Day] { @@ -151,4 +177,10 @@ class ReadDaysInteractor: RepositoryInteractor { return objects } + + private func sorted (tasks: [Task]) -> [Task] { + return tasks.sorted { (task1: Task, task2: Task) -> Bool in + return task1.endDate.compare(task2.endDate) == .orderedAscending + } + } } diff --git a/App/Tasks/TaskTypeSelection.swift b/App/Tasks/TaskTypeSelection.swift index 64df7f1..671fd0b 100644 --- a/App/Tasks/TaskTypeSelection.swift +++ b/App/Tasks/TaskTypeSelection.swift @@ -22,6 +22,6 @@ class TaskTypeSelection { if let type = ListType(rawValue: UserDefaults.standard.integer(forKey: kLastSelectedTabKey)) { return type } - return ListType.allTasks + return ListType.tasks } } diff --git a/Delivery/macOS/App/AppDelegate.swift b/Delivery/macOS/App/AppDelegate.swift index a13178e..dfe606f 100644 --- a/Delivery/macOS/App/AppDelegate.swift +++ b/Delivery/macOS/App/AppDelegate.swift @@ -229,7 +229,7 @@ extension AppDelegate { popover.contentViewController = appWireframe.appViewController popover.animates = true appWireframe.removeCurrentController() - _ = appWireframe.presentTasksController() + _ = appWireframe.presentMainController() appWireframe.showPopover(popover, fromIcon: menu.iconView) } diff --git a/Delivery/macOS/App/AppWireframe.swift b/Delivery/macOS/App/AppWireframe.swift index 8176364..66fd8cc 100644 --- a/Delivery/macOS/App/AppWireframe.swift +++ b/Delivery/macOS/App/AppWireframe.swift @@ -8,11 +8,6 @@ import Cocoa -enum SplitViewColumn: Int { - case projects = 0 - case tasks = 1 -} - class AppWireframe { private var _appViewController: AppViewController? @@ -30,7 +25,7 @@ class AppWireframe { return _appViewController! } - private var welcomeViewController: WelcomeViewController { + private func createWelcomeViewController() -> WelcomeViewController { let controller = WelcomeViewController.instantiateFromStoryboard("Welcome") controller.appWireframe = self @@ -38,7 +33,7 @@ class AppWireframe { return controller } - private var wizardViewController: WizardViewController { + private func createWizardViewController() -> WizardViewController { let controller = WizardViewController.instantiateFromStoryboard("Welcome") controller.appWireframe = self @@ -46,7 +41,7 @@ class AppWireframe { return controller } - private var loginViewController: LoginViewController { + private func createLoginViewController() -> LoginViewController { let controller = LoginViewController.instantiateFromStoryboard("Login") let presenter = LoginPresenter() @@ -57,23 +52,36 @@ class AppWireframe { return controller } - private var tasksViewController: TasksViewController { + private func createMainViewController() -> MainViewController { - let controller = TasksViewController.instantiateFromStoryboard("Tasks") - let presenter = TasksPresenter() - let interactor = TasksInteractor() + let controller = MainViewController.instantiateFromStoryboard("Main") + let presenter = MainPresenter() presenter.ui = controller - presenter.interactor = interactor presenter.appWireframe = self - interactor.presenter = presenter controller.presenter = presenter controller.appWireframe = self return controller } - private var taskSuggestionViewController: TaskSuggestionViewController { +// private var tasksViewController: TasksViewController { +// +// let controller = TasksViewController.instantiateFromStoryboard("Tasks") +// let presenter = TasksPresenter() +// let interactor = TasksInteractor() +// +// presenter.ui = controller +// presenter.interactor = interactor +// presenter.appWireframe = self +// interactor.presenter = presenter +// controller.presenter = presenter +// controller.appWireframe = self +// +// return controller +// } + + private func createTaskSuggestionViewController() -> TaskSuggestionViewController { let controller = TaskSuggestionViewController.instantiateFromStoryboard("Tasks") let presenter = TaskSuggestionPresenter() @@ -84,7 +92,7 @@ class AppWireframe { return controller } - private var settingsViewController: SettingsViewController { + private func createSettingsViewController() -> SettingsViewController { let controller = SettingsViewController.instantiateFromStoryboard("Settings") let presenter = SettingsPresenter() @@ -100,11 +108,11 @@ class AppWireframe { return controller } - private var placeholderViewController: PlaceholderViewController { + private func createPlaceholderViewController() -> PlaceholderViewController { return PlaceholderViewController.instantiateFromStoryboard("Placeholder") } - - private var worklogsViewController: WorklogsViewController { + + func createWorklogsViewController() -> WorklogsViewController { let controller = WorklogsViewController.instantiateFromStoryboard("Worklogs") let presenter = WorklogsPresenter() @@ -115,7 +123,6 @@ class AppWireframe { return controller } - } extension AppWireframe { @@ -158,7 +165,7 @@ extension AppWireframe { func presentWelcomeController() -> WelcomeViewController { appViewController.view.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: 560, height: 500)) - let controller = self.welcomeViewController + let controller = createWelcomeViewController() addController(controller) currentController = controller @@ -168,7 +175,7 @@ extension AppWireframe { func presentWizardController() -> WizardViewController { appViewController.view.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: 560, height: 500)) - let controller = self.wizardViewController + let controller = createWizardViewController() addController(controller) currentController = controller @@ -177,17 +184,17 @@ extension AppWireframe { func presentLoginController() -> LoginViewController { - let controller = self.loginViewController + let controller = createLoginViewController() addController(controller) currentController = controller return controller } - func presentTasksController() -> TasksViewController { + func presentMainController() -> MainViewController { - appViewController.view.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: 560, height: 500)) - let controller = self.tasksViewController + appViewController.view.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: 560, height: 600)) + let controller = createMainViewController() addController(controller) currentController = controller @@ -197,7 +204,7 @@ extension AppWireframe { func presentTaskSuggestionController (startSleepDate: Date?, endSleepDate: Date) -> TaskSuggestionViewController { appViewController.view.frame = CGRect(origin: CGPoint.zero, size: CGSize(width: 450, height: 150)) - let controller = self.taskSuggestionViewController + let controller = createTaskSuggestionViewController() controller.startSleepDate = startSleepDate controller.endSleepDate = endSleepDate addController(controller) @@ -207,16 +214,16 @@ extension AppWireframe { } // Placeholder - func presentPlaceholder (_ message: MessageViewModel, intoSplitView splitView: NSSplitView) -> PlaceholderViewController { + func presentPlaceholder (_ message: MessageViewModel, in view: NSView) -> PlaceholderViewController { var controller = _placeholderViewController if controller == nil { - controller = self.placeholderViewController + controller = createPlaceholderViewController() appViewController.addChild(controller!) _placeholderViewController = controller } - splitView.subviews[SplitViewColumn.tasks.rawValue].addSubview(controller!.view) + view.addSubview(controller!.view) controller!.view.constrainToSuperview() controller!.viewModel = message @@ -233,7 +240,7 @@ extension AppWireframe { // EndDay func presentEndDayController (date: Date, tasks: [Task]) -> WorklogsViewController { - let controller = self.worklogsViewController + let controller = createWorklogsViewController() controller.date = date controller.tasks = tasks addController(controller) @@ -253,14 +260,14 @@ extension AppWireframe { extension AppWireframe { - func flipToTasksController() { + func flipToMainController() { - let tasksController = self.tasksViewController + let mainController = createMainViewController() let flip = NoAnimation() flip.animationReachedMiddle = { self.removeCurrentController() - self.addController(tasksController) - self.currentController = tasksController + self.addController(mainController) + self.currentController = mainController } flip.animationFinished = {} flip.startWithLayer(layerToAnimate()) @@ -268,22 +275,26 @@ extension AppWireframe { func flipToSettingsController() { - let settingsController = self.settingsViewController - let flip = NoAnimation() - flip.animationReachedMiddle = { - self.removeCurrentController() - self.removePlaceholder() - self.removeEndDayController() - self.addController(settingsController) - self.currentController = settingsController - } - flip.animationFinished = {} - flip.startWithLayer(layerToAnimate()) + let settingsController = createSettingsViewController() + let window = NSWindow(contentViewController: settingsController) + window.title = "Jirassic settings" + window.level = .popUpMenu + window.makeKeyAndOrderFront(nil) +// let flip = NoAnimation() +// flip.animationReachedMiddle = { +// self.removeCurrentController() +// self.removePlaceholder() +// self.removeEndDayController() +// self.addController(settingsController) +// self.currentController = settingsController +// } +// flip.animationFinished = {} +// flip.startWithLayer(layerToAnimate()) } func flipToLoginController() { - let loginController = self.loginViewController + let loginController = createLoginViewController() let flip = NoAnimation() flip.animationReachedMiddle = { self.removeController(self.currentController!) @@ -296,7 +307,7 @@ extension AppWireframe { func flipToWizardController() { - let wizardController = self.wizardViewController + let wizardController = createWizardViewController() let flip = NoAnimation() flip.animationReachedMiddle = { self.removeCurrentController() diff --git a/Delivery/macOS/Components/Components.storyboard b/Delivery/macOS/Components/Components.storyboard index 5fb6d6b..b70979a 100644 --- a/Delivery/macOS/Components/Components.storyboard +++ b/Delivery/macOS/Components/Components.storyboard @@ -1,291 +1,11 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Usually Jira task ids which are of form LETTER-NUMBER, but can be anything really, important is that same task should have same id - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -302,14 +22,14 @@ - + - + @@ -321,7 +41,7 @@ - + @@ -358,7 +78,7 @@ - + diff --git a/Delivery/macOS/Components/GitUsersViewController.swift b/Delivery/macOS/Components/GitUsersViewController.swift index 4933a80..7fc3ec8 100644 --- a/Delivery/macOS/Components/GitUsersViewController.swift +++ b/Delivery/macOS/Components/GitUsersViewController.swift @@ -7,7 +7,6 @@ // import Cocoa -import RCPreferences class GitUsersViewController: NSViewController { @@ -15,16 +14,19 @@ class GitUsersViewController: NSViewController { @IBOutlet private weak var tableView: NSTableView! @IBOutlet private weak var doneButton: NSButton! - var onDone: (() -> Void)? - - private let pref = RCPreferences() private let gitModule = ModuleGitLogs() - private var users: [GitUser] = [] + private var gitUsers: [GitUser] = [] + var selectedUsers: [String] = [] { + didSet { + tableView.reloadData() + } + } + var onDone: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() gitModule.fetchUsers { [weak self] users in - self?.users = users + self?.gitUsers = users self?.tableView.reloadData() } tableView.headerView = nil @@ -38,7 +40,7 @@ class GitUsersViewController: NSViewController { extension GitUsersViewController: NSTableViewDataSource { func numberOfRows (in aTableView: NSTableView) -> Int { - return users.count + return gitUsers.count } } @@ -46,11 +48,10 @@ extension GitUsersViewController: NSTableViewDelegate { func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { - let user = users[row] + let user = gitUsers[row] if (tableColumn?.identifier)?.rawValue == "isSelected" { - let allowedAuthors: [String] = pref.string(.settingsGitAuthors).split(separator: ",").map { String($0) } - let isSelected = allowedAuthors.contains(user.email) + let isSelected = selectedUsers.contains(user.email) return NSNumber(booleanLiteral: isSelected) } if (tableColumn?.identifier)?.rawValue == "email" { @@ -61,19 +62,17 @@ extension GitUsersViewController: NSTableViewDelegate { func tableView(_ tableView: NSTableView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, row: Int) { - let user = users[row] + let user = gitUsers[row] if (tableColumn?.identifier)?.rawValue == "isSelected" { - var allowedAuthors: [String] = pref.string(.settingsGitAuthors).split(separator: ",").map { String($0) } guard let isSelected = (object as? NSNumber)?.boolValue else { return } if isSelected { - allowedAuthors.append(user.email) + selectedUsers.append(user.email) } else { - allowedAuthors = allowedAuthors.filter({$0 != user.email}) + selectedUsers = selectedUsers.filter({$0 != user.email}) } - pref.set(allowedAuthors.joined(separator: ","), forKey: .settingsGitAuthors) } } } diff --git a/Delivery/macOS/Screens/Account/LoginPresenter.swift b/Delivery/macOS/Screens/Account/LoginPresenter.swift index f0f97a4..24a5244 100644 --- a/Delivery/macOS/Screens/Account/LoginPresenter.swift +++ b/Delivery/macOS/Screens/Account/LoginPresenter.swift @@ -35,7 +35,7 @@ extension LoginPresenter: LoginPresenterInput { let login = UserInteractor(repository: repository, remoteRepository: remoteRepository) login.onLoginSuccess = { self.userInterface?.showLoadingIndicator(false) - _ = self.appWireframe?.presentTasksController() + _ = self.appWireframe?.presentMainController() } login.onLoginFailure = { self.userInterface?.showLoadingIndicator(false) @@ -45,6 +45,6 @@ extension LoginPresenter: LoginPresenterInput { } func cancelScreen() { - _ = appWireframe?.presentTasksController() + _ = appWireframe?.presentMainController() } } diff --git a/Delivery/macOS/Screens/Calendar/Calendar.storyboard b/Delivery/macOS/Screens/Calendar/Calendar.storyboard new file mode 100644 index 0000000..14469b2 --- /dev/null +++ b/Delivery/macOS/Screens/Calendar/Calendar.storyboard @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delivery/macOS/Screens/Calendar/CalendarDayCellView.swift b/Delivery/macOS/Screens/Calendar/CalendarDayCellView.swift new file mode 100644 index 0000000..200d460 --- /dev/null +++ b/Delivery/macOS/Screens/Calendar/CalendarDayCellView.swift @@ -0,0 +1,107 @@ +// +// CalendarDayView.swift +// Jirassic +// +// Created by Cristian Baluta on 25/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa + +class CalendarDayCellView: NSView { + + private var dayText: NSTextField + private var weekdayText: NSTextField + private var bulletText: NSTextField + private var backgroundView: NSBox + + override init(frame frameRect: NSRect) { + + let w = frameRect.size.width + let h = frameRect.size.height + + backgroundView = NSBox(frame: NSRect(x: 0, y: 0, width: w, height: h)) + backgroundView.boxType = .custom + backgroundView.borderType = .noBorder + backgroundView.cornerRadius = 10 + backgroundView.fillColor = .darkGray + + dayText = NSTextField(frame: NSRect(x: -4, y: 0, width: w+8, height: w)) + dayText.font = NSFont.systemFont(ofSize: 9) + dayText.alignment = .center + dayText.backgroundColor = NSColor.clear + dayText.isBordered = false + dayText.isEditable = false + + weekdayText = NSTextField(frame: NSRect(x: 0, y: w, width: w, height: w)) + weekdayText.font = NSFont.systemFont(ofSize: 9) + weekdayText.alignment = .center + weekdayText.backgroundColor = NSColor.clear + weekdayText.isBordered = false + weekdayText.isEditable = false + + bulletText = NSTextField(frame: NSRect(x: 0, y: -10, width: w, height: w)) + bulletText.font = NSFont.systemFont(ofSize: 9) + bulletText.alignment = .center + bulletText.backgroundColor = NSColor.clear + bulletText.isBordered = false + bulletText.isEditable = false + + super.init(frame: frameRect) + self.addSubview(backgroundView) + self.addSubview(dayText) + self.addSubview(weekdayText) + self.addSubview(bulletText) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + var day: Int = 0 { + didSet { + dayText.stringValue = "\(day)" + } + } + + var weekday: String = "" { + didSet { + weekdayText.stringValue = weekday + } + } + + var isSelected: Bool = false { + didSet { +// backgroundView.isHidden = !isSelected + backgroundView.fillColor = isSelected ? .darkGray : .clear + dayText.font = isSelected ? NSFont.boldSystemFont(ofSize: 10) : NSFont.systemFont(ofSize: 9) + dayText.textColor = isSelected ? .white : .labelColor + weekdayText.textColor = isSelected ? .white : .labelColor + } + } + + var isStarted: Bool = false { + didSet { + dayText.alphaValue = isStarted ? 1.0 : 0.4 + weekdayText.alphaValue = isStarted ? 1.0 : 0.4 + } + } + + var isToday: Bool = false { + didSet { + backgroundView.borderType = isToday ? .lineBorder : .noBorder + } + } + + var isEnded: Bool = false { + didSet { + bulletText.stringValue = isEnded ? "•" : "" + } + } + + var onClick: (() -> Void)? + + override func mouseDown(with event: NSEvent) { + onClick?() + } +} diff --git a/Delivery/macOS/Screens/Calendar/CalendarInteractor.swift b/Delivery/macOS/Screens/Calendar/CalendarInteractor.swift new file mode 100644 index 0000000..ffe0e36 --- /dev/null +++ b/Delivery/macOS/Screens/Calendar/CalendarInteractor.swift @@ -0,0 +1,41 @@ +// +// CalendarInteractor.swift +// Jirassic +// +// Created by Cristian Baluta on 24/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Foundation +import RCLog + +protocol CalendarInteractorInput: class { + + func loadCalendar(date: Date) +} + +protocol CalendarInteractorOutput: class { + + func calendarDidLoad (_ weeks: [Week]) +} + +class CalendarInteractor { + + weak var presenter: CalendarInteractorOutput? + private let daysReader: ReadDaysInteractor! + + init() { + daysReader = ReadDaysInteractor(repository: localRepository, remoteRepository: remoteRepository) + } +} + +extension CalendarInteractor: CalendarInteractorInput { + + func loadCalendar(date: Date) { + + daysReader.query(startingDate: date.startOfMonth(), endingDate: date.endOfMonth()) { [weak self] weeks in + self?.presenter?.calendarDidLoad(weeks) + } + } + +} diff --git a/Delivery/macOS/Screens/Calendar/CalendarPresenter.swift b/Delivery/macOS/Screens/Calendar/CalendarPresenter.swift new file mode 100644 index 0000000..88f7591 --- /dev/null +++ b/Delivery/macOS/Screens/Calendar/CalendarPresenter.swift @@ -0,0 +1,89 @@ +// +// CalendarPresenter.swift +// Jirassic +// +// Created by Cristian Baluta on 24/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Foundation + +protocol CalendarPresenterInput: class { + func reloadData() + func goPrevMonth() + func goNextMonth() + var selectedMonth: Date { get } +} + +protocol CalendarPresenterOutput: class { + func addCell (at index: Int, day: Day, isStarted: Bool) + func clearCells() + func showMonthName (_ name: String) +} + +class CalendarPresenter { + + weak var appWireframe: AppWireframe? + weak var ui: CalendarPresenterOutput? + var interactor: CalendarInteractorInput? + var selectedMonth: Date = Date() { + didSet { + reloadData() + } + } +} + +extension CalendarPresenter: CalendarPresenterInput { + + func reloadData() { + ui!.showMonthName(selectedMonth.MMMyyyy()) + ui!.clearCells() + interactor!.loadCalendar(date: selectedMonth) + } + + func goPrevMonth() { + selectedMonth = selectedMonth.dateByAddingMonths(-1) + } + + func goNextMonth() { + selectedMonth = selectedMonth.dateByAddingMonths(1) + } +} + +extension CalendarPresenter: CalendarInteractorOutput { + + func calendarDidLoad(_ weeks: [Week]) { + DispatchQueue.main.async { + let existingDays = weeks.flatMap({$0.days}) + let firstDate = existingDays.first?.dateStart ?? self.selectedMonth + var days = [(Day, Bool)]() + for i in 1...firstDate.daysInMonth() { + if let day = existingDays.filter({$0.dateStart.day() == i}).first { + days.append((day, true)) + } else { + let day = Day(dateStart: firstDate.dateByUpdating(day: i), dateEnd: nil) + days.append((day, false)) + } + } + var i = 0 + var isWeekend = false + for day in days { + if day.0.dateStart.day() == 1 && day.0.dateStart.isWeekend() { + isWeekend = false + continue + } + if isWeekend && day.0.dateStart.isWeekend() { + isWeekend = false + continue + } + if day.0.dateStart.isWeekend() && !day.0.dateStart.isToday() { + i += 1 + isWeekend = true + } else { + self.ui!.addCell(at: i, day: day.0, isStarted: day.1) + i += 1 + } + } + } + } +} diff --git a/Delivery/macOS/Screens/Calendar/CalendarScrollView.swift b/Delivery/macOS/Screens/Calendar/CalendarScrollView.swift deleted file mode 100644 index 3f57ed8..0000000 --- a/Delivery/macOS/Screens/Calendar/CalendarScrollView.swift +++ /dev/null @@ -1,155 +0,0 @@ -// -// DatesScrollView.swift -// Jirassic -// -// Created by Baluta Cristian on 30/12/15. -// Copyright © 2015 Cristian Baluta. All rights reserved. -// - -import Cocoa - -class CalendarScrollView: NSScrollView { - - @IBOutlet fileprivate var outlineView: NSOutlineView? - var _weeks = [Week]() - var weeks: [Week] { - get { - return _weeks - } - set { - _weeks = newValue - guard let firstWeek = _weeks.first else { - return - } - let now = Date() - if firstWeek.date.isSameWeekAs(now) { - if let firstDay = firstWeek.days.first { - if !firstDay.dateStart.isSameDayAs(now) { - _weeks[0].days.insert(Day(dateStart: now, dateEnd: nil), at: 0) - } - } - } else { - let week = Week(date: now) - week.days.append( Day(dateStart: now, dateEnd: nil) ) - _weeks.insert(week, at: 0) - } - } - } - var didSelectDay: ((_ day: Day) -> ())? - var selectedDay: Day? - - override func awakeFromNib() { - super.awakeFromNib() - outlineView?.dataSource = self - outlineView?.delegate = self - } - - func reloadData() { - self.outlineView?.reloadData() - self.outlineView?.expandItem(nil, expandChildren: true) - } - - func selectDay (_ dayToSelect: Day) { - - var i = -1 - for week in weeks { - i += 1 - for day in week.days { - i += 1 - if day.dateStart.isSameDayAs(dayToSelect.dateStart) { - let indexSet = IndexSet(integer: i) - outlineView?.selectRowIndexes(indexSet, byExtendingSelection: true) - break - } - } - } - } -} - -extension CalendarScrollView: NSOutlineViewDataSource { - - func outlineView (_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { - - if let item: AnyObject = item as AnyObject? { - switch item { - case let week as Week: - return week.days[index] - default: - return self - } - } else { - return weeks[index] - } - } - - func outlineView (_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { - - switch item { - case let week as Week: - return week.days.count > 0 - default: - return false - } - } - - func outlineView (_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { - - if let item: AnyObject = item as AnyObject? { - switch item { - case let week as Week: - return week.days.count - default: - return 0 - } - } else { - return weeks.count - } - } -} - -extension CalendarScrollView: NSOutlineViewDelegate { - - func outlineView (_ outlineView: NSOutlineView, viewFor viewForTableColumn: NSTableColumn?, item: Any) -> NSView? { - - switch item { - case let week as Week: - - let view = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "HeaderCell"), owner: self) as! NSTableCellView - if let textField = view.textField { - textField.font = NSFont.boldSystemFont(ofSize: 14) - textField.stringValue = week.date.weekInterval() - } - return view - - case let day as Day: - - let view = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "DataCell"), owner: self) as! NSTableCellView - if let textField = view.textField { - textField.font = NSFont.boldSystemFont(ofSize: 12) - textField.textColor = day.dateEnd == nil ? NSColor.lightGray : NSColor.darkGray - textField.stringValue = day.dateStart.isToday() ? "Today" : day.dateStart.ddEEE() - } - - return view - - default: - return nil - } - } - - func outlineView (_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool { - return item is Week - } - - func outlineViewSelectionDidChange (_ notification: Notification) { - - if let outlineView = notification.object as? NSOutlineView { - let selectedRow = outlineView.selectedRow - - if let selectedObject = outlineView.item(atRow: selectedRow) as? Day { - selectedDay = selectedObject - didSelectDay?(selectedObject) - } - } - } -} diff --git a/Delivery/macOS/Screens/Calendar/CalendarViewController.swift b/Delivery/macOS/Screens/Calendar/CalendarViewController.swift new file mode 100644 index 0000000..921508e --- /dev/null +++ b/Delivery/macOS/Screens/Calendar/CalendarViewController.swift @@ -0,0 +1,94 @@ +// +// CalendarViewController.swift +// Jirassic +// +// Created by Cristian Baluta on 24/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa + +class CalendarViewController: NSViewController { + + @IBOutlet var monthTextField: NSTextField! + + weak var appWireframe: AppWireframe? + var presenter: CalendarPresenterInput? + + var cells: [CalendarDayCellView] = [] + + var didChangeDay: ((_ day: Day) -> Void)? + var didChangeMonth: ((_ date: Date) -> Void)? + + private var day: Day? = Day(dateStart: Date(), dateEnd: nil) + var selectedDay: Day? { + get { + return day + } + set { + day = newValue + if let d = day { + selectCell(day: d) + } + } + } + + override func viewDidAppear() { + super.viewDidAppear() + reloadData() + } + + func reloadData() { + presenter!.reloadData() + didChangeDay?(day!) + } + + private func selectCell (day: Day) { + for cell in cells { + cell.isSelected = cell.day == day.dateStart.day() + } + } + + @IBAction func handlePrevMonth (_ sender: NSButton) { + presenter!.goPrevMonth() + didChangeMonth?(presenter!.selectedMonth) + } + + @IBAction func handleNextMonth (_ sender: NSButton) { + presenter!.goNextMonth() + didChangeMonth?(presenter!.selectedMonth) + } +} + +extension CalendarViewController: CalendarPresenterOutput { + + func addCell (at index: Int, day: Day, isStarted: Bool) { + let w = 20 + let y = 4 + + let cell = CalendarDayCellView(frame: NSRect(x: index*w, y: y, width: w, height: w+w+6)) + cell.day = day.dateStart.day() + cell.weekday = "\(day.dateStart.E())" + cell.isSelected = day.dateStart.isSameDayAs(selectedDay?.dateStart ?? Date()) + cell.isStarted = isStarted + cell.isEnded = day.dateEnd != nil + cell.isToday = day.dateStart.isToday() + cell.onClick = { + self.selectCell(day: day) + self.didChangeDay?(day) + } + self.view.addSubview(cell) + cells.append(cell) + } + + func clearCells() { + for cell in cells { + cell.removeFromSuperview() + } + cells = [] + } + + func showMonthName (_ name: String) { + monthTextField.stringValue = name + } +} diff --git a/Delivery/macOS/Base.lproj/Main.storyboard b/Delivery/macOS/Screens/Main/Base.lproj/Main.storyboard similarity index 78% rename from Delivery/macOS/Base.lproj/Main.storyboard rename to Delivery/macOS/Screens/Main/Base.lproj/Main.storyboard index 9146e98..1cd7d0c 100644 --- a/Delivery/macOS/Base.lproj/Main.storyboard +++ b/Delivery/macOS/Screens/Main/Base.lproj/Main.storyboard @@ -1,7 +1,8 @@ - + - + + @@ -660,5 +661,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delivery/macOS/Screens/Main/MainPresenter.swift b/Delivery/macOS/Screens/Main/MainPresenter.swift new file mode 100644 index 0000000..f2aee1e --- /dev/null +++ b/Delivery/macOS/Screens/Main/MainPresenter.swift @@ -0,0 +1,90 @@ +// +// MainPresenter.swift +// Jirassic +// +// Created by Cristian Baluta on 24/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Foundation +import RCPreferences + +enum ListType: Int { + + case tasks = 0 + case reports = 1 + case projects = 2 +} + +protocol MainPresenterInput: class { + + func viewDidAppear() + func select(listType: ListType) +} + +protocol MainPresenterOutput: class { + + func showWarning (_ show: Bool) +// func showMessage (_ message: MessageViewModel) + func showCalendar() + func showTasks() + func showReports() + func showProjects() + func removeCalendar() + func removeTasks() + func removeReports() + func removeProjects() + func select(listType: ListType) +} + +class MainPresenter { + + weak var appWireframe: AppWireframe? + weak var ui: MainPresenterOutput? + + private var selectedListType = ListType.tasks + private let pref = RCPreferences() + private var extensions = ExtensionsInteractor() + private var lastSelectedDay: Day? +} + +extension MainPresenter: MainPresenterInput { + + func viewDidAppear() { + ui!.showWarning(false) + ui!.showCalendar() + let lastType = TaskTypeSelection().lastType() + select(listType: lastType) + ui!.select(listType: lastType) + // Check for compatibility of components + extensions.getVersions { [weak self] (versions) in + guard let userInterface = self?.ui else { + return + } + let compatibility = Versioning(versions: versions) + if compatibility.shellScript.available { + userInterface.showWarning(!compatibility.jirassic.compatible || !compatibility.jit.compatible) + } else { + userInterface.showWarning(false) + } + } + } + + func select(listType: ListType) { + TaskTypeSelection().setType(listType) + ui!.removeTasks() + ui!.removeReports() + ui!.removeProjects() + switch listType { + case .tasks: + ui!.showCalendar() + ui!.showTasks() + case .reports: + ui!.showCalendar() + ui!.showReports() + case .projects: + ui!.removeCalendar() + ui!.showProjects() + } + } +} diff --git a/Delivery/macOS/Screens/Main/MainViewController.swift b/Delivery/macOS/Screens/Main/MainViewController.swift new file mode 100644 index 0000000..e2f7f5e --- /dev/null +++ b/Delivery/macOS/Screens/Main/MainViewController.swift @@ -0,0 +1,287 @@ +// +// MainViewController.swift +// Jirassic +// +// Created by Cristian Baluta on 24/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa +import RCPreferences +import RCLog + +class MainViewController: NSViewController { + + @IBOutlet private var splitView: NSSplitView! + @IBOutlet private var splitTopView: NSView! + @IBOutlet private var splitBottomView: NSView! + @IBOutlet private var splitTopViewHeightConstraint: NSLayoutConstraint! + @IBOutlet private var listSegmentedControl: NSSegmentedControl! + @IBOutlet private var syncIndicator: NSProgressIndicator! + @IBOutlet private var butRefresh: NSButton! + @IBOutlet private var butSettings: NSButton! + @IBOutlet private var butWarning: NSButton! + @IBOutlet private var butWarningRightConstraint: NSLayoutConstraint! + @IBOutlet private var butQuit: NSButton! + @IBOutlet private var butMinimize: NSButton! + + private var calendarViewController: CalendarViewController? + private var tasksViewController: TasksViewController? + private var reportsViewController: ReportsViewController? + private var projectsViewController: ProjectsViewController? + + weak var appWireframe: AppWireframe? + var presenter: MainPresenterInput? + + override func awakeFromNib() { + super.awakeFromNib() + createLayer() + } + + override func viewDidLoad() { + super.viewDidLoad() + registerForNotifications() + hideControls(false) + } + + override func viewDidAppear() { + super.viewDidAppear() + presenter!.viewDidAppear() + } + + deinit { + RCLog(self) + NotificationCenter.default.removeObserver(self) + } + + private func hideControls (_ hide: Bool) { + butSettings.isHidden = hide + butRefresh.isHidden = remoteRepository == nil ? true : hide + butWarning.isHidden = hide + butQuit.isHidden = hide + butMinimize.isHidden = hide + listSegmentedControl.isHidden = hide + } +} + +extension MainViewController: Animatable { + + func createLayer() { + view.layer = CALayer() + view.wantsLayer = true + } +} + +extension MainViewController { + + @IBAction func handleSegmentedControl (_ sender: NSSegmentedControl) { + let listType = ListType(rawValue: sender.selectedSegment)! + presenter!.select(listType: listType) + } + + @IBAction func handleRefreshButton (_ sender: NSButton) { +// presenter!.syncData() + } + + @IBAction func handleSettingsButton (_ sender: NSButton) { + appWireframe!.flipToSettingsController() + } + + @IBAction func handleWarningButton (_ sender: NSButton) { + RCPreferences().set(SettingsTab.input.rawValue, forKey: .settingsActiveTab) + appWireframe!.flipToSettingsController() + } + + @IBAction func handleQuitAppButton (_ sender: NSButton) { + NSApplication.shared.terminate(nil) + } + + @IBAction func handleMinimizeAppButton (_ sender: NSButton) { + AppDelegate.sharedApp().menu.triggerClose() + } +} + +extension MainViewController: MainPresenterOutput { + + func select(listType: ListType) { + listSegmentedControl!.selectedSegment = listType.rawValue + } + + func showLoadingIndicator (_ show: Bool) { + + butRefresh.isHidden = remoteRepository == nil ? true : show + butWarningRightConstraint.constant = butRefresh.isHidden ? 0 : 22 +// if show { +// loadingTasksIndicator.isHidden = false +// loadingTasksIndicator.startAnimation(nil) +// } else { +// loadingTasksIndicator.stopAnimation(nil) +// loadingTasksIndicator.isHidden = true +// } + } + + func showWarning (_ show: Bool) { + butWarning.isHidden = !show + } + + func showMessage (_ message: MessageViewModel) { + + let controller = appWireframe!.presentPlaceholder(message, in: self.view) + controller.didPressButton = { +// self.presenter?.messageButtonDidPress() + } + } + + func showCalendar() { + + guard calendarViewController == nil else { + return + } + splitTopViewHeightConstraint.constant = 90 + + let controller = CalendarViewController.instantiateFromStoryboard("Calendar") + let presenter = CalendarPresenter() + let interactor = CalendarInteractor() + + presenter.ui = controller + presenter.interactor = interactor + interactor.presenter = presenter + controller.presenter = presenter + controller.appWireframe = appWireframe + + splitTopView.addSubview(controller.view) + self.addChild(controller) + controller.view.constrainToSuperview() + + calendarViewController = controller + calendarViewController!.didChangeDay = { [weak self] day in + guard let controller = self?.tasksViewController else { + return + } + controller.presenter?.reloadTasksOnDay(day) + } + calendarViewController!.didChangeMonth = { [weak self] date in + guard let controller = self?.reportsViewController else { + return + } + controller.presenter?.reloadReportsInMonth(date) + } + } + + func showTasks() { + + let controller = TasksViewController.instantiateFromStoryboard("Tasks") + let presenter = TasksPresenter() + let interactor = TasksInteractor() + + presenter.ui = controller + presenter.interactor = interactor + presenter.appWireframe = appWireframe + interactor.presenter = presenter + controller.presenter = presenter + controller.appWireframe = appWireframe + + splitBottomView.addSubview(controller.view) + self.addChild(controller) + controller.view.constrainToSuperview() + + tasksViewController = controller + + guard let selectedDay = calendarViewController!.selectedDay else { + return + } + controller.presenter?.reloadTasksOnDay(selectedDay) + } + + func showReports () { + + let controller = ReportsViewController.instantiateFromStoryboard("Reports") + let presenter = ReportsPresenter() + let interactor = ReportsInteractor() + + presenter.ui = controller + presenter.interactor = interactor + presenter.appWireframe = appWireframe + interactor.presenter = presenter + controller.presenter = presenter + controller.appWireframe = appWireframe + + splitBottomView.addSubview(controller.view) + self.addChild(controller) + controller.view.constrainToSuperview() + + reportsViewController = controller + } + + func showProjects() { + + let controller = ProjectsViewController.instantiateFromStoryboard("Projects") + let presenter = ProjectsPresenter() + let interactor = ProjectsInteractor() + + presenter.ui = controller + presenter.interactor = interactor + interactor.presenter = presenter + controller.presenter = presenter + controller.appWireframe = appWireframe + + splitBottomView.addSubview(controller.view) + self.addChild(controller) + controller.view.constrainToSuperview() + + projectsViewController = controller + } + + func removeCalendar() { + guard let controller = calendarViewController else { + return + } + controller.removeFromSuperview() + controller.removeFromParent() + calendarViewController = nil + splitTopViewHeightConstraint.constant = 10 + } + func removeTasks() { + guard let controller = tasksViewController else { + return + } + controller.removeFromSuperview() + controller.removeFromParent() + tasksViewController = nil + } + func removeReports() { + guard let controller = reportsViewController else { + return + } + controller.removeFromSuperview() + controller.removeFromParent() + reportsViewController = nil + } + func removeProjects() { + guard let controller = projectsViewController else { + return + } + controller.removeFromSuperview() + controller.removeFromParent() + projectsViewController = nil + } + + func tasksDidClear() { + calendarViewController?.reloadData() + } +} + +extension MainViewController { + + func registerForNotifications() { + + NotificationCenter.default.addObserver(self, + selector: #selector(MainViewController.handleNewTaskAdded(_:)), + name: NSNotification.Name(rawValue: kNewTaskWasAddedNotification), + object: nil) + } + + @objc func handleNewTaskAdded (_ notif: Notification) { +// presenter!.reloadData() + } +} diff --git a/Delivery/macOS/Screens/Onboarding/WizardCalendarView.swift b/Delivery/macOS/Screens/Onboarding/WizardCalendarView.swift index fc2c493..c1174bf 100644 --- a/Delivery/macOS/Screens/Onboarding/WizardCalendarView.swift +++ b/Delivery/macOS/Screens/Onboarding/WizardCalendarView.swift @@ -17,12 +17,12 @@ class WizardCalendarView: NSView { var onSkip: (() -> Void)? private let pref = RCPreferences() private var calendarsButtons = [NSButton]() - private var presenter: CalendarPresenterInput = CalendarPresenter() + private var presenter: CalendarAppPresenterInput = CalendarAppPresenter() override func awakeFromNib() { super.awakeFromNib() - (presenter as! CalendarPresenter).userInterface = self - (presenter as! CalendarPresenter).refresh() + (presenter as! CalendarAppPresenter).userInterface = self + (presenter as! CalendarAppPresenter).refresh() } func save() { @@ -56,7 +56,7 @@ class WizardCalendarView: NSView { } } -extension WizardCalendarView: CalendarPresenterOutput { +extension WizardCalendarView: CalendarAppPresenterOutput { func enable (_ enabled: Bool) { butAuthorize.isHidden = enabled diff --git a/Delivery/macOS/Screens/Onboarding/WizardGitView.swift b/Delivery/macOS/Screens/Onboarding/WizardGitView.swift index 2202ef0..fd5efd0 100644 --- a/Delivery/macOS/Screens/Onboarding/WizardGitView.swift +++ b/Delivery/macOS/Screens/Onboarding/WizardGitView.swift @@ -17,8 +17,6 @@ class WizardGitView: NSView { @IBOutlet var butSkip: NSButton! var onSkip: (() -> Void)? private let pref = RCPreferences() - private var emailClickGestureRecognizer: NSClickGestureRecognizer? - private var gitUsersPopover: NSPopover? var presenter: GitPresenterInput = GitPresenter() @@ -28,24 +26,10 @@ class WizardGitView: NSView { pref.set(true, forKey: .enableGit) (presenter as! GitPresenter).userInterface = self presenter.isShellScriptInstalled = true - - let emailClickGestureRecognizer = NSClickGestureRecognizer(target: self, action: #selector(GitCell.emailTextFieldClicked)) - emailsTextField.addGestureRecognizer(emailClickGestureRecognizer) - self.emailClickGestureRecognizer = emailClickGestureRecognizer - } - - deinit { - if let gesture = emailClickGestureRecognizer { - emailsTextField.removeGestureRecognizer(gesture) - } } func save() { - presenter.save(emails: emailsTextField.stringValue, paths: pathsTextField.stringValue) - } - - @IBAction func handlePickButton (_ sender: NSButton) { - presenter.pickPath() + } @IBAction func handleSkipButton (_ sender: NSButton) { @@ -56,23 +40,6 @@ class WizardGitView: NSView { save() onSkip?() } - - @objc func emailTextFieldClicked() { - guard gitUsersPopover == nil else { - return - } - let popover = NSPopover() - let view = GitUsersViewController.instantiateFromStoryboard("Components") - view.onDone = { - self.gitUsersPopover?.performClose(nil) - self.gitUsersPopover = nil - self.presenter.isShellScriptInstalled = true - } - popover.contentViewController = view - let rect = CGRect(origin: CGPoint(x: emailsTextField.frame.origin.x, y: emailsTextField.frame.origin.y), size: emailsTextField.frame.size) - popover.show(relativeTo: rect, of: self, preferredEdge: NSRectEdge.minY) - gitUsersPopover = popover - } } extension WizardGitView: GitPresenterOutput { @@ -83,21 +50,4 @@ extension WizardGitView: GitPresenterOutput { func setButInstall (enabled: Bool) {} func setButPurchase(enabled: Bool) {} func setButEnable (on: Bool?, enabled: Bool?) {} - func setPaths (_ paths: String?, enabled: Bool?) { - if let paths = paths { - pathsTextField.stringValue = paths - } - if let enabled = enabled { - pathsTextField.isEnabled = enabled - butPick.isEnabled = enabled - } - } - func setEmails (_ emails: String?, enabled: Bool?) { - if let emails = emails { - emailsTextField.stringValue = emails - } - if let enabled = enabled { - emailsTextField.isEnabled = enabled - } - } } diff --git a/Delivery/macOS/Screens/Onboarding/WizardViewController.swift b/Delivery/macOS/Screens/Onboarding/WizardViewController.swift index 5e5d037..1745ce8 100644 --- a/Delivery/macOS/Screens/Onboarding/WizardViewController.swift +++ b/Delivery/macOS/Screens/Onboarding/WizardViewController.swift @@ -174,7 +174,7 @@ class WizardViewController: NSViewController { @IBAction func handleSkipButton (_ sender: NSButton) { let stepsToSave: [Int] = WizardStep.allCases.map({ $0.rawValue }) pref.set(stepsToSave, forKey: .wizardSteps) - appWireframe!.flipToTasksController() + appWireframe!.flipToMainController() } @IBAction func handleQuitAppButton (_ sender: NSButton) { diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift new file mode 100644 index 0000000..7395783 --- /dev/null +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -0,0 +1,69 @@ +// +// ProjectDetailsPresenter.swift +// Jirassic +// +// Created by Cristian Baluta on 28/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Foundation +import RCPreferences + +protocol ProjectDetailsPresenterInput: class { + + func didPickUrl (_ url: URL) + func save (emails: String, paths: String) +} + +protocol ProjectDetailsPresenterOutput: class { + + func pickPath() + func setPaths (_ paths: String?, enabled: Bool?) + func setEmails (_ emails: String?, enabled: Bool?) +} + +class ProjectDetailsPresenter { + + weak var ui: ProjectDetailsPresenterOutput? + private let localPreferences = RCPreferences() + +} + +extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { + + func reloadData() { + + ui?.setPaths("", + enabled: localPreferences.bool(.enableGit)) + + ui?.setEmails("", + enabled: localPreferences.bool(.enableGit)) + + } + + func didPickUrl (_ url: URL) { + + var path = url.absoluteString + path = path.replacingOccurrences(of: "file://", with: "") + path.removeLast() + // TODO: Validate if the picked project is a git project + +// let existingPaths = wself.localPreferences.string(.settingsGitPaths) +// let updatedPaths = existingPaths == "" ? path : (existingPaths + "," + path) +// savePaths(updatedPaths) +// ui.setPaths(updatedPaths, enabled: localPreferences.bool(.enableGit)) + } + + func save (emails: String, paths: String) { + saveEmails(emails) + savePaths(paths) + } + + private func saveEmails (_ emails: String) { + + } + + private func savePaths (_ paths: String) { + + } +} diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift new file mode 100644 index 0000000..9c294cf --- /dev/null +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -0,0 +1,132 @@ +// +// ProjectDetailsViewController.swift +// Jirassic +// +// Created by Cristian Baluta on 24/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa + +class ProjectDetailsViewController: NSViewController { + + @IBOutlet private var baseUrlTextField: NSTextField! + @IBOutlet private var userTextField: NSTextField! + @IBOutlet private var passwordTextField: NSTextField! + @IBOutlet private var errorTextField: NSTextField! + @IBOutlet private var projectNamePopup: NSPopUpButton! + @IBOutlet private var projectIssueNamePopup: NSPopUpButton! + @IBOutlet private var progressIndicator: NSProgressIndicator! + @IBOutlet private var emailsTextField: NSTextField! + @IBOutlet private var pathsTextField: NSTextField! + @IBOutlet private var butPick: NSButton! + + var project: Project? { + didSet { + reloadData() + } + } + + var presenter: ProjectDetailsPresenterInput? + + private var emailClickGestureRecognizer: NSClickGestureRecognizer? + private var gitUsersPopover: NSPopover? + + override func viewDidLoad() { + super.viewDidLoad() + + emailsTextField.delegate = self + pathsTextField.delegate = self + + let gesture = NSClickGestureRecognizer(target: self, action: #selector(ProjectDetailsViewController.emailTextFieldClicked)) + emailsTextField.addGestureRecognizer(gesture) + self.emailClickGestureRecognizer = gesture + } + + deinit { + if let gesture = emailClickGestureRecognizer { + emailsTextField.removeGestureRecognizer(gesture) + } + } + + func reloadData() { + + baseUrlTextField.stringValue = project?.jiraBaseUrl ?? "" + userTextField.stringValue = project?.jiraUser ?? "" + } + + + @IBAction func handlePickerButton (_ sender: NSButton) { + pickPath() + } + + @IBAction func handleSaveButton (_ sender: NSButton) { + + } + + @objc func emailTextFieldClicked() { + guard gitUsersPopover == nil else { + return + } + let popover = NSPopover() + let view = GitUsersViewController.instantiateFromStoryboard("Components") + view.onDone = { + self.emailsTextField.stringValue = view.selectedUsers.joined(separator: ",") + self.gitUsersPopover?.performClose(nil) + self.gitUsersPopover = nil + } + popover.contentViewController = view + let rect = CGRect(origin: CGPoint(x: emailsTextField.frame.width/2, y: 0), + size: emailsTextField.frame.size) + popover.show(relativeTo: rect, of: emailsTextField, preferredEdge: NSRectEdge.minY) + gitUsersPopover = popover + // Set emails after popover is presented + view.selectedUsers = ["cristi.baluta@gmail.com"] + } +} + +extension ProjectDetailsViewController: ProjectDetailsPresenterOutput { + + func pickPath() { + + let panel = NSOpenPanel() + panel.canChooseFiles = false + panel.canChooseDirectories = true + panel.allowsMultipleSelection = false + panel.message = "Select the root of the git project you want to track" + panel.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow))) + panel.begin { [weak self] result in + + guard let url = panel.urls.first, result.rawValue == NSFileHandlingPanelOKButton else { + return + } + self?.presenter?.didPickUrl(url) + } + } + + func setPaths (_ paths: String?, enabled: Bool?) { + if let paths = paths { + pathsTextField.stringValue = paths + } + if let enabled = enabled { + pathsTextField.isEnabled = enabled + butPick.isEnabled = enabled + } + } + + func setEmails (_ emails: String?, enabled: Bool?) { + if let emails = emails { + emailsTextField.stringValue = emails + } + if let enabled = enabled { + emailsTextField.isEnabled = enabled + } + } +} + +extension ProjectDetailsViewController: NSTextFieldDelegate { + + func controlTextDidEndEditing(_ obj: Notification) { +// save() + } +} diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard index 9ae876b..84cc856 100644 --- a/Delivery/macOS/Screens/Projects/Projects.storyboard +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -1,42 +1,42 @@ - + - + - + - + - + - - + + - + - - - - + + + + - + - + @@ -53,45 +53,374 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSAllRomanInputSourcesLocaleIdentifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + - + - + + + + diff --git a/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift new file mode 100644 index 0000000..abf3b0c --- /dev/null +++ b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift @@ -0,0 +1,67 @@ +// +// ProjectsInteractor.swift +// Jirassic +// +// Created by Cristian Baluta on 23/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Foundation + +protocol ProjectsInteractorInput: class { + + func reloadProjects() + func addProject() + func removeProject (_ project: Project) +} + +protocol ProjectsInteractorOutput: class { + + func projectsDidLoad (_ projects: [Project]) +} + +class ProjectsInteractor { + + weak var presenter: ProjectsPresenter? + +} + +extension ProjectsInteractor: ProjectsInteractorInput { + + func addProject() { +// presenter?.projectsDidLoad([ +// +// ]) + } + + func removeProject (_ project: Project) { + + } + + func reloadProjects() { +// presenter?.projectsDidLoad([]) +// return; + presenter?.projectsDidLoad([ + Project(objectId: nil, + lastModifiedDate: nil, + title: "BoschEbike", + jiraBaseUrl: "https://jira.fortech.ro", + jiraUser: "cristianbal", + jiraProject: "BOSCH", + jiraIssue: "BOSCH-100", + gitBaseUrls: [], + gitUsers: ["cristi.baluta@gmail.com", "cristianbal@fortech.ro"], + taskNumberPrefix: "APP"), + Project(objectId: nil, + lastModifiedDate: nil, + title: "Terma", + jiraBaseUrl: "https://jira.fortech.ro", + jiraUser: "cristianbal", + jiraProject: "BOSCH", + jiraIssue: "BOSCH-100", + gitBaseUrls: [], + gitUsers: [], + taskNumberPrefix: "TERMA") + ]) + } +} diff --git a/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift new file mode 100644 index 0000000..2fe1c7f --- /dev/null +++ b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift @@ -0,0 +1,80 @@ +// +// ProjectsListViewController.swift +// Jirassic +// +// Created by Cristian Baluta on 28/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa + +class ProjectsListViewController: NSViewController { + + @IBOutlet private weak var scrollView: NSScrollView! + @IBOutlet private weak var tableView: NSTableView! + @IBOutlet private weak var butAdd: NSButton! + @IBOutlet private weak var butRemove: NSButton! + + private var selectedProject: Project? + + var projects = [Project]() { + didSet { + tableView.reloadData() + } + } + var didSelectProject: ((Project) -> Void)? + var didSelectAddProject: (() -> Void)? + var didSelectRemoveProject: ((Project) -> Void)? + + override func viewDidLoad() { + super.viewDidLoad() + scrollView.focusRingType = .none + tableView.focusRingType = .none + } + + @IBAction func handleAddButton (_ sender: NSButton) { + didSelectAddProject?() + } + + @IBAction func handleRemoveButton (_ sender: NSButton) { + guard let project = selectedProject else { + return + } + didSelectRemoveProject?(project) + } +} + +extension ProjectsListViewController: NSTableViewDataSource { + + func numberOfRows (in aTableView: NSTableView) -> Int { + return projects.count + } +} + +extension ProjectsListViewController: NSTableViewDelegate { + + func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { + + let project = projects[row] + if (tableColumn?.identifier)?.rawValue == "isSelected" { + return nil + } + if (tableColumn?.identifier)?.rawValue == "name" { + return project.title + } + return nil + } + + func tableView(_ tableView: NSTableView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, row: Int) { + + if (tableColumn?.identifier)?.rawValue == "isSelected" { + + } + } + + func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { + selectedProject = projects[row] + didSelectProject?(projects[row]) + return true + } +} diff --git a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift index c526536..70c381e 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift @@ -8,14 +8,68 @@ import Foundation -protocol ProjectsPresenterInput { +protocol ProjectsPresenterInput: class { + func reloadProjects() + func addProject() + func removeProject (_ project: Project) +} +protocol ProjectsPresenterOutput: class { + func projectsDidLoad(_ projects: [Project]) + func showMessage (_ message: MessageViewModel) + func hideMessage() } -protocol ProjectsPresenterOutput { +class ProjectsPresenter { + + weak var appWireframe: AppWireframe? + weak var ui: ProjectsPresenterOutput? + var interactor: ProjectsInteractorInput? + + private var projects = [Project]() +} +extension ProjectsPresenter: ProjectsPresenterInput { + + func reloadProjects() { + interactor!.reloadProjects() + } + + func addProject() { + ui!.hideMessage() +// interactor!.addProject() + let newProject = Project(objectId: nil, + lastModifiedDate: nil, + title: "Project \(projects.count + 1)", + jiraBaseUrl: "https://", + jiraUser: nil, + jiraProject: nil, + jiraIssue: nil, + gitBaseUrls: [], + gitUsers: [], + taskNumberPrefix: nil) + projects.append(newProject) + projectsDidLoad(projects) + } + + func removeProject (_ project: Project) { +// interactor!.removeProject(project) + let newProjects = projects.filter( { $0.title != project.title } ) + projectsDidLoad(newProjects) + } } -class ProjectsPresenter { +extension ProjectsPresenter: ProjectsInteractorOutput { + func projectsDidLoad(_ projects: [Project]) { + self.projects = projects + if projects.count == 0 { + ui!.showMessage(( + title: "No projects", + message: "Add your first project!", + buttonTitle: "Add")) + } else { + ui!.projectsDidLoad(projects) + } + } } diff --git a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift index 906c19d..2dabd21 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift @@ -8,36 +8,72 @@ import Cocoa -class ProjectsViewController: NSViewController { - @IBOutlet private weak var scrollView: NSScrollView! - @IBOutlet private weak var tableView: NSTableView! - var projects = ["Bosch Ebike", "Electric castle"] -} - -extension ProjectsViewController: NSTableViewDataSource { +class ProjectsViewController: NSSplitViewController { + + weak var appWireframe: AppWireframe? + var presenter: ProjectsPresenterInput? + + override func viewDidLoad() { + super.viewDidLoad() + self.view.focusRingType = .none + self.splitViewItems.first?.minimumThickness = 120 + + presenter!.reloadProjects() + } - func numberOfRows (in aTableView: NSTableView) -> Int { - return projects.count + private func setupControllers() { + + guard self.splitViewItems.count == 0 else { + return + } + let c1 = ProjectsListViewController.instantiateFromStoryboard("Projects") + let c2 = ProjectDetailsViewController.instantiateFromStoryboard("Projects") + let c2Presenter = ProjectDetailsPresenter() + c2.presenter = c2Presenter + c2Presenter.ui = c2 + let s1 = NSSplitViewItem(viewController: c1) + s1.minimumThickness = 120 + let s2 = NSSplitViewItem(viewController: c2) + self.splitViewItems = [s1, s2] } } -extension ProjectsViewController: NSTableViewDelegate { +extension ProjectsViewController: ProjectsPresenterOutput { - func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { + func showMessage (_ message: MessageViewModel) { - if (tableColumn?.identifier)?.rawValue == "isSelected" { - return nil - } - if (tableColumn?.identifier)?.rawValue == "name" { - return projects[row] + self.splitViewItems = [] + + let controller = appWireframe!.presentPlaceholder(message, in: self.view) + controller.didPressButton = { + self.presenter?.addProject() } - return nil } - func tableView(_ tableView: NSTableView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, row: Int) { + func hideMessage() { + appWireframe!.removePlaceholder() + } + + func projectsDidLoad(_ projects: [Project]) { - if (tableColumn?.identifier)?.rawValue == "isSelected" { + setupControllers() + + guard let controller = self.splitViewItems.first?.viewController as? ProjectsListViewController else { + return + } + controller.projects = projects + controller.didSelectProject = { project in + guard let controller = self.splitViewItems.last?.viewController as? ProjectDetailsViewController else { + return + } + controller.project = project + } + controller.didSelectAddProject = { + self.presenter?.addProject() + } + controller.didSelectRemoveProject = { project in + self.presenter?.removeProject(project) } } } diff --git a/Delivery/macOS/Screens/Tasks/MonthReports/MonthReportsHeaderView.swift b/Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.swift similarity index 100% rename from Delivery/macOS/Screens/Tasks/MonthReports/MonthReportsHeaderView.swift rename to Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.swift diff --git a/Delivery/macOS/Screens/Tasks/MonthReports/MonthReportsHeaderView.xib b/Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.xib similarity index 100% rename from Delivery/macOS/Screens/Tasks/MonthReports/MonthReportsHeaderView.xib rename to Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.xib diff --git a/Delivery/macOS/Screens/Reports/Reports.storyboard b/Delivery/macOS/Screens/Reports/Reports.storyboard new file mode 100644 index 0000000..e5729d0 --- /dev/null +++ b/Delivery/macOS/Screens/Reports/Reports.storyboard @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delivery/macOS/Screens/Tasks/Reports/ReportsDataSource.swift b/Delivery/macOS/Screens/Reports/ReportsDataSource.swift similarity index 93% rename from Delivery/macOS/Screens/Tasks/Reports/ReportsDataSource.swift rename to Delivery/macOS/Screens/Reports/ReportsDataSource.swift index e5f97e2..0392fe4 100644 --- a/Delivery/macOS/Screens/Tasks/Reports/ReportsDataSource.swift +++ b/Delivery/macOS/Screens/Reports/ReportsDataSource.swift @@ -20,6 +20,9 @@ class ReportsDataSource: NSObject, TasksAndReportsDataSource { } var didClickAddRow: ((_ row: Int) -> Void)? var didClickRemoveRow: ((_ row: Int) -> Void)? + var didClickCloseDay: ((_ tasks: [Task]) -> Void)? + var didClickSaveWorklogs: (() -> Void)? + var didClickSetupJira: (() -> Void)? private var tempCell: ReportCell? let numberOfDays: Int var reports: [Report] diff --git a/Delivery/macOS/Screens/Reports/ReportsInteractor.swift b/Delivery/macOS/Screens/Reports/ReportsInteractor.swift new file mode 100644 index 0000000..9507d1f --- /dev/null +++ b/Delivery/macOS/Screens/Reports/ReportsInteractor.swift @@ -0,0 +1,63 @@ +// +// ReportsInteractor.swift +// Jirassic +// +// Created by Cristian Baluta on 27/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Foundation +import RCPreferences +import RCLog + +protocol ReportsInteractorInput: class { + + func reloadTasks (inDay day: Day) + func reloadTasks (inMonth day: Day) +} + +protocol ReportsInteractorOutput: class { + + func tasksDidLoad (_ tasks: [Task]) +} + +class ReportsInteractor { + + weak var presenter: ReportsInteractorOutput? + + private let tasksReader: ReadTasksInteractor! + private let moduleGit = ModuleGitLogs() + private let moduleCalendar = ModuleCalendar() + private let pref = RCPreferences() + private var currentTasks = [Task]() + private var currentDateStart: Date? + + init() { + tasksReader = ReadTasksInteractor(repository: localRepository, remoteRepository: remoteRepository) + } +} + +extension ReportsInteractor: ReportsInteractorInput { + + func reloadTasks (inDay day: Day) { + + let dateStart = day.dateStart + let dateEnd = day.dateEnd ?? dateStart.endOfDay() + currentDateStart = dateStart + reloadTasks(dateStart: dateStart, dateEnd: dateEnd) + } + + func reloadTasks (inMonth day: Day) { + + let dateStart = day.dateStart.startOfMonth() + let dateEnd = dateStart.endOfMonth() + currentDateStart = dateStart + reloadTasks(dateStart: dateStart, dateEnd: dateEnd) + } + + private func reloadTasks (dateStart: Date, dateEnd: Date) { + + + } + +} diff --git a/Delivery/macOS/Screens/Reports/ReportsPresenter.swift b/Delivery/macOS/Screens/Reports/ReportsPresenter.swift new file mode 100644 index 0000000..0eeb64a --- /dev/null +++ b/Delivery/macOS/Screens/Reports/ReportsPresenter.swift @@ -0,0 +1,127 @@ +// +// ReportsPresenter.swift +// Jirassic +// +// Created by Cristian Baluta on 27/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa +import RCPreferences + +protocol ReportsPresenterInput: class { + + func viewDidLoad() + func reloadData() + func reloadReportsOnDay (_ day: Day) + func reloadReportsInMonth (_ date: Date) + func updateNoTasksState() +} + +protocol ReportsPresenterOutput: class { + + func showLoadingIndicator (_ show: Bool) + func showMessage (_ message: MessageViewModel) + func showReports (_ reports: [Report], numberOfDays: Int, type: ListType) +} + +class ReportsPresenter { + + weak var appWireframe: AppWireframe? + weak var ui: ReportsPresenterOutput? + var interactor: ReportsInteractorInput? + + private var currentTasks = [Task]() + private var currentReports = [Report]() + private let pref = RCPreferences() + private var extensions = ExtensionsInteractor() + private var lastSelectedDay: Day? +} + +extension ReportsPresenter: ReportsPresenterInput { + + func viewDidLoad() { + ui!.showLoadingIndicator(true) + reloadData() + } + + func reloadData() { + ui!.showLoadingIndicator(true) + } + + func reloadReportsOnDay (_ day: Day) { + + } + + func reloadReportsInMonth (_ date: Date) { + + } + + func reloadTasksOnDay (_ day: Day, listType: ListType) { + ui!.showLoadingIndicator(true) + lastSelectedDay = day + interactor!.reloadTasks(inDay: day) + } + + func updateNoTasksState() { + + if currentTasks.count == 1 { + ui!.showMessage(( + title: "No task yet", + message: "Go to 'Tasks' tab and log some work first!", + buttonTitle: nil)) + } else { + appWireframe!.removePlaceholder() + } + } + + func messageButtonDidPress() { + + if currentTasks.count == 0 { + startDay() + } + } + + func startDay() { + + let task = Task(endDate: Date(), type: .startDay) + let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) + saveInteractor.saveTask(task, allowSyncing: true, completion: { [weak self] savedTask in + self?.reloadData() + }) + ModuleHookup().insert(task: task) + } +} + +extension ReportsPresenter: ReportsInteractorOutput { + + func tasksDidLoad (_ tasks: [Task]) { + + guard let ui = self.ui else { + return + } + ui.showLoadingIndicator(false) + currentTasks = tasks + + let settings = SettingsInteractor().getAppSettings() + let targetHoursInDay = pref.bool(.enableRoundingDay) + ? TimeInteractor(settings: settings).workingDayLength() + : nil + let reportInteractor = CreateMonthReport() + let reports = reportInteractor.reports(fromTasks: currentTasks, targetHoursInDay: targetHoursInDay, roundHours: true) + currentReports = reports.byTasks +// ui.showReports(currentReports, numberOfDays: reports.byDays.count) + + +// let settings = SettingsInteractor().getAppSettings() +// let targetHoursInDay = pref.bool(.enableRoundingDay) +// ? TimeInteractor(settings: settings).workingDayLength() +// : nil +// let reportInteractor = CreateReport() +// let reports = reportInteractor.reports(fromTasks: currentTasks, targetHoursInDay: targetHoursInDay) +// currentReports = reports.reversed() +// ui.showReports(currentReports, numberOfDays: 1, type: selectedListType) + + updateNoTasksState() + } +} diff --git a/Delivery/macOS/Screens/Reports/ReportsViewController.swift b/Delivery/macOS/Screens/Reports/ReportsViewController.swift new file mode 100644 index 0000000..f71a5dd --- /dev/null +++ b/Delivery/macOS/Screens/Reports/ReportsViewController.swift @@ -0,0 +1,88 @@ +// +// ReportsViewController.swift +// Jirassic +// +// Created by Cristian Baluta on 27/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa +import RCLog + +class ReportsViewController: NSViewController { + + @IBOutlet private var loadingTasksIndicator: NSProgressIndicator! + private var tasksScrollView: TasksScrollView? + + weak var appWireframe: AppWireframe? + var presenter: ReportsPresenterInput? + /// Property to keep a reference to the active cell rect + private var rectToDisplayPopoverAt: NSRect? + private var activePopover: NSPopover? + + override func viewDidLoad() { + super.viewDidLoad() + presenter!.viewDidLoad() + } + + deinit { + RCLog(self) + } +} + +extension ReportsViewController: ReportsPresenterOutput { + + func showLoadingIndicator (_ show: Bool) { + + if show { + loadingTasksIndicator.isHidden = false + loadingTasksIndicator.startAnimation(nil) + } else { + loadingTasksIndicator.stopAnimation(nil) + loadingTasksIndicator.isHidden = true + } + } + + func showMessage (_ message: MessageViewModel) { + + let controller = appWireframe!.presentPlaceholder(message, in: self.view) + controller.didPressButton = { +// self.presenter?.messageButtonDidPress() + } + } + + func showReports (_ reports: [Report], numberOfDays: Int, type: ListType) { + + let dataSource = ReportsDataSource(reports: reports, numberOfDays: numberOfDays) + let scrollView = TasksScrollView(dataSource: dataSource, listType: type) + self.view.addSubview(scrollView) + scrollView.constrainToSuperview() + scrollView.reloadData() +// scrollView.didChangeSettings = { [weak self] in +// +// } +// scrollView.didClickCopyMonthlyReport = { asHtml in +// var string = "" +// let interactor = CreateMonthReport() +// if asHtml { +// string = interactor.htmlReports(dataSource.reports) +// } else { +// let joined = interactor.joinReports(dataSource.reports) +// string = joined.notes + "\n\n" + joined.totalDuration.secToHoursAndMin +// } +// NSPasteboard.general.clearContents() +// NSPasteboard.general.writeObjects([string as NSPasteboardWriting]) +// } + + tasksScrollView = scrollView + } + + func removeTasksController() { + + if tasksScrollView != nil { + tasksScrollView?.removeFromSuperview() + tasksScrollView = nil + } + } + +} diff --git a/Delivery/macOS/Screens/Tasks/Reports/HeaderView/ReportsHeaderView.swift b/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.swift similarity index 100% rename from Delivery/macOS/Screens/Tasks/Reports/HeaderView/ReportsHeaderView.swift rename to Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.swift diff --git a/Delivery/macOS/Screens/Tasks/Reports/HeaderView/ReportsHeaderView.xib b/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.xib similarity index 100% rename from Delivery/macOS/Screens/Tasks/Reports/HeaderView/ReportsHeaderView.xib rename to Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.xib diff --git a/Delivery/macOS/Screens/Tasks/Reports/ReportCell/ReportCell.swift b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift similarity index 100% rename from Delivery/macOS/Screens/Tasks/Reports/ReportCell/ReportCell.swift rename to Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift diff --git a/Delivery/macOS/Screens/Tasks/Reports/ReportCell/ReportCell.xib b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.xib similarity index 100% rename from Delivery/macOS/Screens/Tasks/Reports/ReportCell/ReportCell.xib rename to Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.xib diff --git a/Delivery/macOS/Screens/Tasks/Reports/ReportCell/ReportCellPresenter.swift b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCellPresenter.swift similarity index 100% rename from Delivery/macOS/Screens/Tasks/Reports/ReportCell/ReportCellPresenter.swift rename to Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCellPresenter.swift diff --git a/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarPresenter.swift b/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarAppPresenter.swift similarity index 92% rename from Delivery/macOS/Screens/Settings/Input/Calendar/CalendarPresenter.swift rename to Delivery/macOS/Screens/Settings/Input/Calendar/CalendarAppPresenter.swift index c7ddd11..a4fbbb7 100644 --- a/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarPresenter.swift +++ b/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarAppPresenter.swift @@ -10,7 +10,7 @@ import Foundation import Cocoa import RCPreferences -protocol CalendarPresenterInput: class { +protocol CalendarAppPresenterInput: class { func enable (_ enabled: Bool) func enableCalendar (_ calendarTitle: String) @@ -19,7 +19,7 @@ protocol CalendarPresenterInput: class { func authorize() } -protocol CalendarPresenterOutput: class { +protocol CalendarAppPresenterOutput: class { func enable (_ enabled: Bool) func setStatusImage (_ imageName: NSImage.Name) @@ -29,14 +29,14 @@ protocol CalendarPresenterOutput: class { func setCalendars (_ calendars: [String], selected: [String]) } -class CalendarPresenter { +class CalendarAppPresenter { - weak var userInterface: CalendarPresenterOutput? + weak var userInterface: CalendarAppPresenterOutput? private let calendarModule = ModuleCalendar() private let pref = RCPreferences() } -extension CalendarPresenter: CalendarPresenterInput { +extension CalendarAppPresenter: CalendarAppPresenterInput { func enable (_ enabled: Bool) { pref.set(enabled, forKey: .enableCalendar) diff --git a/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarCell.swift b/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarCell.swift index 76ea46f..fd76908 100644 --- a/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarCell.swift +++ b/Delivery/macOS/Screens/Settings/Input/Calendar/CalendarCell.swift @@ -20,11 +20,11 @@ class CalendarCell: NSTableRowView { @IBOutlet private var scrollView: NSScrollView! private var calendarsButtons = [NSButton]() - private var presenter: CalendarPresenterInput = CalendarPresenter() + private var presenter: CalendarAppPresenterInput = CalendarAppPresenter() override func awakeFromNib() { super.awakeFromNib() - (presenter as! CalendarPresenter).userInterface = self + (presenter as! CalendarAppPresenter).userInterface = self presenter.refresh() } @@ -57,7 +57,7 @@ class CalendarCell: NSTableRowView { } } -extension CalendarCell: CalendarPresenterOutput { +extension CalendarCell: CalendarAppPresenterOutput { func enable (_ enabled: Bool) { for but in calendarsButtons { @@ -81,7 +81,7 @@ extension CalendarCell: CalendarPresenterOutput { statusImageView.isHidden = authorized butAuthorize.isHidden = authorized butEnable.isHidden = !authorized - butEnable.state = enabled ? NSControl.StateValue.on : NSControl.StateValue.off + butEnable.state = enabled ? .on : .off } func setCalendars (_ calendars: [String], selected: [String]) { diff --git a/Delivery/macOS/Screens/Settings/Input/Git/GitCell.swift b/Delivery/macOS/Screens/Settings/Input/Git/GitCell.swift index 1415d19..085398b 100644 --- a/Delivery/macOS/Screens/Settings/Input/Git/GitCell.swift +++ b/Delivery/macOS/Screens/Settings/Input/Git/GitCell.swift @@ -10,44 +10,27 @@ import Cocoa class GitCell: NSTableRowView, Saveable { - static let height = CGFloat(195) + static let height = CGFloat(120) @IBOutlet private var statusImageView: NSImageView! @IBOutlet private var butEnable: NSButton! @IBOutlet private var statusTextField: NSTextField! @IBOutlet private var descriptionTextField: NSTextField! - @IBOutlet private var emailsTextField: NSTextField! - @IBOutlet private var pathsTextField: NSTextField! @IBOutlet private var butInstall: NSButton! @IBOutlet private var butPurchase: NSButton! - @IBOutlet private var butPick: NSButton! var presenter: GitPresenterInput = GitPresenter() var onPurchasePressed: (() -> Void)? - private var emailClickGestureRecognizer: NSClickGestureRecognizer? - private var gitUsersPopover: NSPopover? override func awakeFromNib() { super.awakeFromNib() (presenter as! GitPresenter).userInterface = self butEnable.isHidden = true butPurchase.isHidden = true - emailsTextField.delegate = self - pathsTextField.delegate = self - - let emailClickGestureRecognizer = NSClickGestureRecognizer(target: self, action: #selector(GitCell.emailTextFieldClicked)) - emailsTextField.addGestureRecognizer(emailClickGestureRecognizer) - self.emailClickGestureRecognizer = emailClickGestureRecognizer - } - - deinit { - if let gesture = emailClickGestureRecognizer { - emailsTextField.removeGestureRecognizer(gesture) - } } func save() { - presenter.save(emails: emailsTextField.stringValue, paths: pathsTextField.stringValue) + } @IBAction func handleInstallButton (_ sender: NSButton) { @@ -65,34 +48,6 @@ class GitCell: NSTableRowView, Saveable { @IBAction func handleEnableButton (_ sender: NSButton) { presenter.enableGit(sender.state == .on) } - - @IBAction func handlePickButton (_ sender: NSButton) { - presenter.pickPath() - } - - @objc func emailTextFieldClicked() { - guard gitUsersPopover == nil else { - return - } - let popover = NSPopover() - let view = GitUsersViewController.instantiateFromStoryboard("Components") - view.onDone = { - self.gitUsersPopover?.performClose(nil) - self.gitUsersPopover = nil - self.presenter.isShellScriptInstalled = true - } - popover.contentViewController = view - let rect = CGRect(origin: CGPoint(x: emailsTextField.frame.origin.x, y: GitCell.height-45), size: emailsTextField.frame.size) - popover.show(relativeTo: rect, of: self, preferredEdge: NSRectEdge.minY) - gitUsersPopover = popover - } -} - -extension GitCell: NSTextFieldDelegate { - - func controlTextDidEndEditing(_ obj: Notification) { - save() - } } extension GitCell: GitPresenterOutput { @@ -118,21 +73,4 @@ extension GitCell: GitPresenterOutput { } butEnable.isHidden = enabled == false } - func setPaths (_ paths: String?, enabled: Bool?) { - if let paths = paths { - pathsTextField.stringValue = paths - } - if let enabled = enabled { - pathsTextField.isEnabled = enabled - butPick.isEnabled = enabled - } - } - func setEmails (_ emails: String?, enabled: Bool?) { - if let emails = emails { - emailsTextField.stringValue = emails - } - if let enabled = enabled { - emailsTextField.isEnabled = enabled - } - } } diff --git a/Delivery/macOS/Screens/Settings/Input/Git/GitCell.xib b/Delivery/macOS/Screens/Settings/Input/Git/GitCell.xib index 2b07f9a..9db52ae 100644 --- a/Delivery/macOS/Screens/Settings/Input/Git/GitCell.xib +++ b/Delivery/macOS/Screens/Settings/Input/Git/GitCell.xib @@ -1,8 +1,8 @@ - + - + @@ -10,17 +10,17 @@ - + - + - + - + @@ -28,7 +28,7 @@ - + @@ -36,68 +36,26 @@ - - - - - - - - - - - - - - - - - - + - + Commits made with Git are loaded in Jirassic on demand, meaning that they don't get saved to local database or synced over iCloud. If you wish to save them use Jit - - - - - - - - - - - - - - - - - + - + @@ -46,7 +47,7 @@ - + @@ -77,7 +78,7 @@ - + @@ -87,7 +88,7 @@ - + @@ -118,7 +119,7 @@ - + diff --git a/Delivery/macOS/Screens/Settings/SettingsViewController.swift b/Delivery/macOS/Screens/Settings/SettingsViewController.swift index 5d873ba..9332b2c 100644 --- a/Delivery/macOS/Screens/Settings/SettingsViewController.swift +++ b/Delivery/macOS/Screens/Settings/SettingsViewController.swift @@ -93,7 +93,7 @@ class SettingsViewController: NSViewController { extension SettingsViewController { @IBAction func handleSaveButton (_ sender: NSButton) { - appWireframe!.flipToTasksController() + appWireframe!.flipToMainController() } @IBAction func handleBackupButton (_ sender: NSButton) { diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.swift b/Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.swift deleted file mode 100644 index 99b6ff0..0000000 --- a/Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// FooterCell.swift -// Jirassic -// -// Created by Cristian Baluta on 30/01/2018. -// Copyright © 2018 Imagin soft. All rights reserved. -// - -import Cocoa -import RCPreferences - -class TasksHeaderView: NSTableHeaderView { - - @IBOutlet private var backgroundView: NSVisualEffectView! - @IBOutlet private var butAdd: NSButton! - @IBOutlet private var butCloseDay: NSButton! - @IBOutlet private var butWorklogs: NSButton! - @IBOutlet private var butJiraSetup: NSButton! - - private unowned let appWireframe = AppDelegate.sharedApp().appWireframe - private var store = Store.shared - private var moduleJira = ModuleJiraTempo() - private let pref = RCPreferences() - var didClickAddTask: (() -> Void)? - var didClickCloseDay: (() -> Void)? - var didClickSaveWorklogs: (() -> Void)? - var isDayEnded: Bool = false { - didSet { - let isJiraAvailable = store.isJiraTempoPurchased && moduleJira.isConfigured && moduleJira.isProjectConfigured - self.butAdd.isHidden = isDayEnded - self.butCloseDay.isHidden = isDayEnded - self.butWorklogs.isHidden = !isDayEnded - self.butWorklogs.isEnabled = isJiraAvailable - self.butJiraSetup.isHidden = isDayEnded ? isJiraAvailable : true - } - } - - override func awakeFromNib() { - super.awakeFromNib() - if #available(OSX 10.14, *) { - // In OS14 there is already a default blurry background - backgroundView.isHidden = true - } - } - - override func headerRect(ofColumn column: Int) -> NSRect { - // This will prevent for a label to appear in the middle of the header - return NSRect.zero - } - - // Overriding this in OS14 removes default blurry background and the custom one adds ugly edges to buttons -// override func draw (_ dirtyRect: NSRect) { -// } - - @IBAction func handleAddButton (_ sender: NSButton) { - didClickAddTask?() - } - - @IBAction func handleCloseDayButton (_ sender: NSButton) { - didClickCloseDay?() - } - - @IBAction func handleWorklogsButton (_ sender: NSButton) { - didClickSaveWorklogs?() - } - - @IBAction func handleJiraSetupButton (_ sender: NSButton) { - pref.set(SettingsTab.output.rawValue, forKey: .settingsActiveTab) - appWireframe.flipToSettingsController() - } - -} diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.xib b/Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.xib deleted file mode 100644 index 0eae4ee..0000000 --- a/Delivery/macOS/Screens/Tasks/AllTasks/HeaderView/TasksHeaderView.xib +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Delivery/macOS/Screens/Tasks/DataSource.swift b/Delivery/macOS/Screens/Tasks/DataSource.swift index 615b0e8..25346f4 100644 --- a/Delivery/macOS/Screens/Tasks/DataSource.swift +++ b/Delivery/macOS/Screens/Tasks/DataSource.swift @@ -10,10 +10,13 @@ import Cocoa protocol TasksAndReportsDataSource { var tableView: NSTableView! {get set} - var didClickAddRow: ((_ row: Int) -> Void)? {get set} - var didClickRemoveRow: ((_ row: Int) -> Void)? {get set} func addTask (_ task: Task, at row: Int) func removeTask (at row: Int) + var didClickAddRow: ((_ row: Int) -> Void)? {get set} + var didClickRemoveRow: ((_ row: Int) -> Void)? {get set} + var didClickCloseDay: ((_ tasks: [Task]) -> Void)? {get set} + var didClickSaveWorklogs: (() -> Void)? {get set} + var didClickSetupJira: (() -> Void)? {get set} } typealias DataSource = NSTableViewDataSource & NSTableViewDelegate & TasksAndReportsDataSource diff --git a/Delivery/macOS/Components/EditableTimeBox.swift b/Delivery/macOS/Screens/Tasks/EditableTimeBox.swift similarity index 90% rename from Delivery/macOS/Components/EditableTimeBox.swift rename to Delivery/macOS/Screens/Tasks/EditableTimeBox.swift index 2824c94..a904e62 100644 --- a/Delivery/macOS/Components/EditableTimeBox.swift +++ b/Delivery/macOS/Screens/Tasks/EditableTimeBox.swift @@ -21,6 +21,7 @@ class EditableTimeBox: TimeBox { override func awakeFromNib() { super.awakeFromNib() timeTextField?.delegate = self +// backgroundBox?.fillColor = .darkGray } } @@ -29,8 +30,8 @@ extension EditableTimeBox: NSTextFieldDelegate { public func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool { isEditing = true partialValue = stringValue - self.borderColor = NSColor.darkGray - timeTextField?.textColor = NSColor.black +// backgroundBox?.borderColor = .darkGray + timeTextField?.textColor = .black return true } @@ -40,8 +41,8 @@ extension EditableTimeBox: NSTextFieldDelegate { wasEdited = false didEndEditing?() } - self.borderColor = NSColor.white - timeTextField?.textColor = NSColor.darkGray +// backgroundBox?.borderColor = .white + timeTextField?.textColor = .darkGray return true } diff --git a/Delivery/macOS/Components/NewTaskViewController.swift b/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift similarity index 100% rename from Delivery/macOS/Components/NewTaskViewController.swift rename to Delivery/macOS/Screens/Tasks/NewTaskViewController.swift diff --git a/Delivery/macOS/Screens/Tasks/Tasks.storyboard b/Delivery/macOS/Screens/Tasks/Tasks.storyboard index 57b6330..7bcbd65 100644 --- a/Delivery/macOS/Screens/Tasks/Tasks.storyboard +++ b/Delivery/macOS/Screens/Tasks/Tasks.storyboard @@ -1,284 +1,21 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -308,7 +45,7 @@ - + @@ -316,12 +53,12 @@ - + - + @@ -335,13 +72,13 @@ - + - + @@ -377,10 +114,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Usually Jira task ids which are of form LETTER-NUMBER, but can be anything really, important is that same task should have same id + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/TasksDataSource.swift b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift similarity index 68% rename from Delivery/macOS/Screens/Tasks/AllTasks/TasksDataSource.swift rename to Delivery/macOS/Screens/Tasks/TasksDataSource.swift index b33a5ba..6563bb5 100644 --- a/Delivery/macOS/Screens/Tasks/AllTasks/TasksDataSource.swift +++ b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift @@ -9,22 +9,29 @@ import Cocoa let kNonTaskCellHeight = CGFloat(40.0) -let kTaskCellHeight = CGFloat(90.0) +let kTaskCellHeight = CGFloat(40.0) +let kCloseDayCellHeight = CGFloat(90.0) let kGapBetweenCells = CGFloat(16.0) let kCellLeftPadding = CGFloat(10.0) class TasksDataSource: NSObject, TasksAndReportsDataSource { - var tableView: NSTableView! { + var didClickAddRow: ((_ row: Int) -> Void)? + var didClickRemoveRow: ((_ row: Int) -> Void)? + var didClickCloseDay: ((_ tasks: [Task]) -> Void)? + var didClickSaveWorklogs: (() -> Void)? + var didClickSetupJira: (() -> Void)? + + internal var tableView: NSTableView! { didSet { TaskCell.register(in: tableView) NonTaskCell.register(in: tableView) + CloseDayCell.register(in: tableView) + ClosedDayCell.register(in: tableView) } } - var tasks: [Task] - var didClickAddRow: ((_ row: Int) -> Void)? - var didClickRemoveRow: ((_ row: Int) -> Void)? - var isDayEnded: Bool { + private var tasks: [Task] + private var isDayEnded: Bool { return self.tasks.contains(where: { $0.taskType == .endDay }) } @@ -35,10 +42,10 @@ class TasksDataSource: NSObject, TasksAndReportsDataSource { private func cellForTaskType (_ taskType: TaskType) -> CellProtocol { switch taskType { - case TaskType.issue, TaskType.gitCommit: - return TaskCell.instantiate(in: self.tableView) + case .issue, .gitCommit: + return TaskCell.instantiate(in: tableView) default: - return NonTaskCell.instantiate(in: self.tableView) + return NonTaskCell.instantiate(in: tableView) } } @@ -54,11 +61,14 @@ class TasksDataSource: NSObject, TasksAndReportsDataSource { extension TasksDataSource: NSTableViewDataSource { func numberOfRows (in aTableView: NSTableView) -> Int { - return tasks.count + return tasks.count > 0 ? tasks.count + 1 : 0 } func tableView (_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + guard row < tasks.count else { + return kCloseDayCellHeight + } let theData = tasks[row] switch theData.taskType { case TaskType.issue, TaskType.gitCommit: @@ -72,10 +82,31 @@ extension TasksDataSource: NSTableViewDataSource { extension TasksDataSource: NSTableViewDelegate { func tableView (_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - + + guard row < tasks.count else { + if self.isDayEnded { + let cell = ClosedDayCell.instantiate(in: tableView) + cell.didClickSaveWorklogs = { + self.didClickSaveWorklogs?() + } + cell.didClickSetupJira = { + self.didClickSetupJira?() + } + return cell + } else { + let cell = CloseDayCell.instantiate(in: tableView) + cell.didClickAddTask = { + self.didClickAddRow?(self.tasks.count - 1) + } + cell.didClickCloseDay = { + self.didClickCloseDay?(self.tasks) + } + return cell + } + } + var theData = tasks[row] let thePreviousData: Task? = row == 0 ? nil : tasks[row-1] - var cell: CellProtocol = self.cellForTaskType(theData.taskType) TaskCellPresenter(cell: cell).present(previousTask: thePreviousData, currentTask: theData) @@ -95,7 +126,7 @@ extension TasksDataSource: NSTableViewDelegate { } cell.didClickRemoveCell = { [weak self] (cell: CellProtocol) in // Ugly hack to find the row number from which the action came - tableView.enumerateAvailableRowViews({ (rowView, rowIndex) -> Void in + tableView.enumerateAvailableRowViews( { (rowView, rowIndex) -> Void in if rowView.subviews.first! == cell as! NSTableRowView { self?.didClickRemoveRow!(rowIndex) return diff --git a/Delivery/macOS/Screens/Tasks/TasksInteractor.swift b/Delivery/macOS/Screens/Tasks/TasksInteractor.swift index 3a91da0..b72a28f 100644 --- a/Delivery/macOS/Screens/Tasks/TasksInteractor.swift +++ b/Delivery/macOS/Screens/Tasks/TasksInteractor.swift @@ -12,22 +12,19 @@ import RCLog protocol TasksInteractorInput: class { - func reloadCalendar() func reloadTasks (inDay day: Day) func reloadTasks (inMonth day: Day) } protocol TasksInteractorOutput: class { - func calendarDidLoad (_ weeks: [Week]) func tasksDidLoad (_ tasks: [Task]) } class TasksInteractor { - weak var presenter: TasksPresenter? + weak var presenter: TasksInteractorOutput? - private let daysReader: ReadDaysInteractor! private let tasksReader: ReadTasksInteractor! private let moduleGit = ModuleGitLogs() private let moduleCalendar = ModuleCalendar() @@ -36,28 +33,12 @@ class TasksInteractor { private var currentDateStart: Date? init() { - daysReader = ReadDaysInteractor(repository: localRepository, remoteRepository: remoteRepository) tasksReader = ReadTasksInteractor(repository: localRepository, remoteRepository: remoteRepository) } } extension TasksInteractor: TasksInteractorInput { - - func reloadCalendar() { - - let startRequestDate = Date() - daysReader.query(startingDate: Date(timeIntervalSinceNow: -12.monthsToSec).endOfDay()) { [weak self] weeks in - let endRequestDate = Date() - RCLog(endRequestDate.timeIntervalSince(startRequestDate)) - DispatchQueue.main.async { - guard let wself = self else { - return - } - wself.presenter?.calendarDidLoad(weeks) - } - } - } - + func reloadTasks (inDay day: Day) { let dateStart = day.dateStart @@ -65,7 +46,7 @@ extension TasksInteractor: TasksInteractorInput { currentDateStart = dateStart reloadTasks(dateStart: dateStart, dateEnd: dateEnd) } - + func reloadTasks (inMonth day: Day) { let dateStart = day.dateStart.startOfMonth() @@ -73,7 +54,7 @@ extension TasksInteractor: TasksInteractorInput { currentDateStart = dateStart reloadTasks(dateStart: dateStart, dateEnd: dateEnd) } - + private func reloadTasks (dateStart: Date, dateEnd: Date) { self.currentTasks = [] diff --git a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift index 39b21a1..25b19e6 100644 --- a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift @@ -7,43 +7,28 @@ // import Cocoa -import RCPreferences protocol TasksPresenterInput: class { - func initUI() - func syncData() - func reloadData() - func reloadTasksOnDay (_ day: Day, listType: ListType) + func reloadLastSelectedDay() + func reloadTasksOnDay (_ day: Day) func updateNoTasksState() - func messageButtonDidPress() - func startDay() - func closeDay (shouldSaveToJira: Bool) + func closeDay (showWorklogs: Bool) func insertTaskWithData (_ taskData: TaskCreationData) func insertTask (after row: Int) func removeTask (at row: Int) + func didClickSaveWorklogs() } protocol TasksPresenterOutput: class { func showLoadingIndicator (_ show: Bool) - func showWarning (_ show: Bool) func showMessage (_ message: MessageViewModel) - func showCalendar (_ weeks: [Week]) - func showProjects (_ projects: [Project]) func showTasks (_ tasks: [Task]) - func showReports (_ reports: [Report], numberOfDays: Int, type: ListType) - func removeTasksController() - func selectDay (_ day: Day) func presentNewTaskController (date: Date) func presentEndDayController (date: Date, tasks: [Task]) -} - -enum ListType: Int { - - case allTasks = 0 - case report = 1 - case monthlyReports = 2 + func removeTasks() + func removeEndDayController() } class TasksPresenter { @@ -53,54 +38,21 @@ class TasksPresenter { var interactor: TasksInteractorInput? private var currentTasks = [Task]() - private var currentReports = [Report]() - private var selectedListType = ListType.allTasks - private let pref = RCPreferences() - private var extensions = ExtensionsInteractor() - private var lastSelectedDay: Day? + private var lastSelectedDay: Day = Day(dateStart: Date(), dateEnd: nil) } extension TasksPresenter: TasksPresenterInput { - func initUI() { - ui!.showWarning(false) - ui!.showLoadingIndicator(false) - reloadData() - extensions.getVersions { [weak self] (versions) in - guard let userInterface = self?.ui else { - return - } - let compatibility = Versioning(versions: versions) - if compatibility.shellScript.available { - userInterface.showWarning(!compatibility.jirassic.compatible || !compatibility.jit.compatible) - } else { - userInterface.showWarning(false) - } - } -// updateNoTasksState() + func reloadLastSelectedDay() { + reloadTasksOnDay(lastSelectedDay) } - func syncData() { - reloadData() - } - - func reloadData() { - ui!.removeTasksController() - ui!.showLoadingIndicator(true) - interactor!.reloadCalendar() - } - - func reloadTasksOnDay (_ day: Day, listType: ListType) { - ui!.removeTasksController() - ui!.showLoadingIndicator(true) + func reloadTasksOnDay (_ day: Day) { lastSelectedDay = day - selectedListType = listType - switch selectedListType { - case .allTasks, .report: - interactor!.reloadTasks(inDay: day) - case .monthlyReports: - interactor!.reloadTasks(inMonth: day) - } + ui!.removeTasks() + ui!.removeEndDayController() + ui!.showLoadingIndicator(true) + interactor!.reloadTasks(inDay: day) } func updateNoTasksState() { @@ -110,12 +62,6 @@ extension TasksPresenter: TasksPresenterInput { title: "Good morning!", message: "Ready to start working today?", buttonTitle: "Start day")) - } - else if currentTasks.count == 1, selectedListType == .report { - ui!.showMessage(( - title: "No task yet", - message: "Go to 'All tasks' tab and log some work first!", - buttonTitle: nil)) } else { appWireframe!.removePlaceholder() } @@ -135,22 +81,28 @@ extension TasksPresenter: TasksPresenterInput { let task = Task(endDate: Date(), type: .startDay) let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) saveInteractor.saveTask(task, allowSyncing: true, completion: { [weak self] savedTask in - self?.reloadData() + self?.reloadLastSelectedDay() }) ModuleHookup().insert(task: task) } - - func closeDay (shouldSaveToJira: Bool) { + + func closeDay (showWorklogs: Bool) { - let closeDay = CloseDay() + let closeDay = CloseDayInteractor() closeDay.close(with: currentTasks) - if shouldSaveToJira { - // Reload data will be called after save with success - ui!.presentEndDayController(date: lastSelectedDay?.dateStart ?? Date(), tasks: currentTasks) + + if showWorklogs { + didClickSaveWorklogs() } else { - reloadData() + reloadLastSelectedDay() } } + + func didClickSaveWorklogs() { + // Reload data will be called after save with success + ui!.removeTasks() + ui!.presentEndDayController(date: lastSelectedDay.dateStart, tasks: currentTasks) + } func insertTaskWithData (_ taskData: TaskCreationData) { @@ -191,64 +143,23 @@ extension TasksPresenter: TasksPresenterInput { let deleteInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) deleteInteractor.deleteTask(task) updateNoTasksState() + if currentTasks.count == 0 { - let reader = ReadDaysInteractor(repository: localRepository, remoteRepository: nil) - reader.queryAll { [weak self] (weeks) in - self?.ui?.showCalendar(weeks) - } + } } } extension TasksPresenter: TasksInteractorOutput { - func calendarDidLoad (_ weeks: [Week]) { - - guard let ui = self.ui else { - return - } - ui.showLoadingIndicator(false) - let day = lastSelectedDay ?? Day(dateStart: Date(), dateEnd: nil) - ui.showCalendar(weeks) - ui.selectDay(day) - - ui.showProjects([]) - } - func tasksDidLoad (_ tasks: [Task]) { guard let ui = self.ui else { return } ui.showLoadingIndicator(false) - ui.removeTasksController() currentTasks = tasks - - switch selectedListType { - case .allTasks: - ui.showTasks(currentTasks) - - case .report: - let settings = SettingsInteractor().getAppSettings() - let targetHoursInDay = pref.bool(.enableRoundingDay) - ? TimeInteractor(settings: settings).workingDayLength() - : nil - let reportInteractor = CreateReport() - let reports = reportInteractor.reports(fromTasks: currentTasks, targetHoursInDay: targetHoursInDay) - currentReports = reports.reversed() - ui.showReports(currentReports, numberOfDays: 1, type: selectedListType) - - case .monthlyReports: - let settings = SettingsInteractor().getAppSettings() - let targetHoursInDay = pref.bool(.enableRoundingDay) - ? TimeInteractor(settings: settings).workingDayLength() - : nil - let reportInteractor = CreateMonthReport() - let reports = reportInteractor.reports(fromTasks: currentTasks, targetHoursInDay: targetHoursInDay, roundHours: true) - currentReports = reports.byTasks - ui.showReports(currentReports, numberOfDays: reports.byDays.count, type: selectedListType) - break - } + ui.showTasks(currentTasks) updateNoTasksState() } } diff --git a/Delivery/macOS/Screens/Tasks/TasksScrollView.swift b/Delivery/macOS/Screens/Tasks/TasksScrollView.swift index 57f215a..ac683f3 100644 --- a/Delivery/macOS/Screens/Tasks/TasksScrollView.swift +++ b/Delivery/macOS/Screens/Tasks/TasksScrollView.swift @@ -19,8 +19,10 @@ class TasksScrollView: NSScrollView { var didClickAddRow: ((_ row: Int, _ rect: CGRect?) -> Void)? var didClickRemoveRow: ((_ row: Int) -> Void)? var didClickCloseDay: ((_ tasks: [Task], _ shouldSaveToJira: Bool) -> Void)? - var didClickCopyMonthlyReport: ((_ asHtml: Bool) -> Void)? - var didChangeSettings: (() -> Void)? + var didClickSaveWorklogs: (() -> Void)? + var didClickSetupJira: (() -> Void)? +// var didClickCopyMonthlyReport: ((_ asHtml: Bool) -> Void)? +// var didChangeSettings: (() -> Void)? required init?(coder: NSCoder) { super.init(coder: coder) @@ -47,14 +49,22 @@ class TasksScrollView: NSScrollView { self.dataSource.tableView = tableView tableView.dataSource = self.dataSource tableView.delegate = self.dataSource - addHeader() - self.dataSource.didClickAddRow = { [weak self] row in - self?.didClickAddRow!(row, nil) - } - self.dataSource.didClickRemoveRow = { [weak self] row in - self?.didClickRemoveRow!(row) - } +// self.dataSource.didClickAddRow = { [weak self] row in +// self?.didClickAddRow!(row, nil) +// } +// self.dataSource.didClickRemoveRow = { [weak self] row in +// self?.didClickRemoveRow!(row) +// } +// self.dataSource.didClickCloseDay = { [weak self] tasks in +// self?.didClickCloseDay!(tasks, false) +// } +// self.dataSource.didClickSaveWorklogs = { [weak self] in +// self?.didClickSaveWorklogs!() +// } +// self.dataSource.didClickSetupJira = { [weak self] in +// self?.didClickSetupJira!() +// } } private func setupTableView() { @@ -76,76 +86,9 @@ class TasksScrollView: NSScrollView { self.documentView = tableView! } - private func addHeader() { - - if let dataSource = self.dataSource as? TasksDataSource { - - guard dataSource.tasks.count > 0 else { - tableView.headerView = nil - return - } - guard tableView.headerView == nil else { - return - } - let headerView = TasksHeaderView.instantiateFromXib() - headerView.didClickCloseDay = { [weak self] in - self?.didClickCloseDay!(dataSource.tasks, false) - } - headerView.didClickAddTask = { [weak self] in - if let wself = self { - wself.didClickAddRow!(wself.dataSource.numberOfRows!(in: wself.tableView) - 1, - CGRect(x: 60, y: headerView.frame.size.height + wself.contentView.documentVisibleRect.origin.y, width: 1, height: 1)) - } - } - headerView.didClickSaveWorklogs = { [weak self] in - self?.didClickCloseDay!(dataSource.tasks, true) - } - headerView.isDayEnded = dataSource.isDayEnded - - tableView.headerView = headerView - } - else if let dataSource = self.dataSource as? ReportsDataSource { - - guard dataSource.reports.count > 0 else { - tableView.headerView = nil - return - } - guard tableView.headerView == nil else { - return - } - let settings = SettingsInteractor().getAppSettings() - let workingDayLength = TimeInteractor(settings: settings).workingDayLength() - let totalTime = StatisticsInteractor().duration(of: dataSource.reports) - - var headerView: ReportsHeaderView! - switch listType! { - case .report: - headerView = ReportsHeaderView.instantiateFromXib() - case .monthlyReports: - let monthHeaderView = MonthReportsHeaderView.instantiateFromXib() - monthHeaderView.numberOfDays = dataSource.numberOfDays - monthHeaderView.didClickCopyAll = { asHtml in - self.didClickCopyMonthlyReport?(asHtml) - } - headerView = monthHeaderView - default: - break - } - headerView.workdayTime = workingDayLength.secToPercent - headerView.workedTime = pref.bool(.usePercents) - ? String(describing: totalTime.secToPercent) - : totalTime.secToHoursAndMin - headerView.didChangeSettings = { [weak self] in - self?.didChangeSettings!() - } - tableView.headerView = headerView - } - } - func addTask (_ task: Task, at row: Int) { if let dataSource = tableView.dataSource as? TasksDataSource { dataSource.addTask(task, at: row) - addHeader() } } @@ -154,7 +97,6 @@ class TasksScrollView: NSScrollView { dataSource.removeTask(at: row) let rowsToRemoveFromTable = IndexSet(integer: row) tableView.removeRows(at: rowsToRemoveFromTable, withAnimation: .effectFade) - addHeader() } } diff --git a/Delivery/macOS/Screens/Tasks/TasksViewController.swift b/Delivery/macOS/Screens/Tasks/TasksViewController.swift index 9a8b9fc..dee746f 100644 --- a/Delivery/macOS/Screens/Tasks/TasksViewController.swift +++ b/Delivery/macOS/Screens/Tasks/TasksViewController.swift @@ -7,117 +7,34 @@ // import Cocoa -import RCPreferences import RCLog class TasksViewController: NSViewController { - @IBOutlet private var splitView: NSSplitView! - @IBOutlet private var calendarScrollView: CalendarScrollView! - private var tasksScrollView: TasksScrollView? - @IBOutlet private var listSegmentedControl: NSSegmentedControl! - @IBOutlet private var butRefresh: NSButton! - @IBOutlet private var butSettings: NSButton! - @IBOutlet private var butWarning: NSButton! - @IBOutlet private var butWarningRightConstraint: NSLayoutConstraint! - @IBOutlet private var butQuit: NSButton! - @IBOutlet private var butMinimize: NSButton! @IBOutlet private var loadingTasksIndicator: NSProgressIndicator! - @IBOutlet private var syncIndicator: NSProgressIndicator! - - weak var appWireframe: AppWireframe? - var presenter: TasksPresenterInput? + private var tasksScrollView: TasksScrollView? + private var worklogsViewController: WorklogsViewController? /// Property to keep a reference to the active cell rect private var rectToDisplayPopoverAt: NSRect? private var activePopover: NSPopover? - - override func awakeFromNib() { - super.awakeFromNib() - createLayer() - } + + weak var appWireframe: AppWireframe? + var presenter: TasksPresenterInput? override func viewDidLoad() { super.viewDidLoad() - - registerForNotifications() - listSegmentedControl!.selectedSegment = TaskTypeSelection().lastType().rawValue - hideControls(false) - - calendarScrollView!.didSelectDay = { [weak self] (day: Day) in - guard let strongSelf = self else { - return - } - let selectedListType = ListType(rawValue: strongSelf.listSegmentedControl!.selectedSegment)! - strongSelf.presenter!.reloadTasksOnDay(day, listType: selectedListType) - } + presenter!.reloadLastSelectedDay() } - override func viewDidAppear() { - super.viewDidAppear() - presenter!.initUI() - } - deinit { RCLog(self) - NotificationCenter.default.removeObserver(self) - } - - private func hideControls (_ hide: Bool) { - butSettings.isHidden = hide - butRefresh.isHidden = remoteRepository == nil ? true : hide - butWarning.isHidden = hide - butQuit.isHidden = hide - butMinimize.isHidden = hide - listSegmentedControl.isHidden = hide - } -} - -extension TasksViewController: Animatable { - - func createLayer() { - view.layer = CALayer() - view.wantsLayer = true - } -} - -extension TasksViewController { - - @IBAction func handleSegmentedControl (_ sender: NSSegmentedControl) { - let listType = ListType(rawValue: sender.selectedSegment)! - TaskTypeSelection().setType(listType) - if let selectedDay = calendarScrollView!.selectedDay { - presenter!.reloadTasksOnDay(selectedDay, listType: listType) - } } - - @IBAction func handleRefreshButton (_ sender: NSButton) { - presenter!.syncData() - } - - @IBAction func handleSettingsButton (_ sender: NSButton) { - appWireframe!.flipToSettingsController() - } - - @IBAction func handleWarningButton (_ sender: NSButton) { - RCPreferences().set(SettingsTab.input.rawValue, forKey: .settingsActiveTab) - appWireframe!.flipToSettingsController() - } - - @IBAction func handleQuitAppButton (_ sender: NSButton) { - NSApplication.shared.terminate(nil) - } - - @IBAction func handleMinimizeAppButton (_ sender: NSButton) { - AppDelegate.sharedApp().menu.triggerClose() - } } extension TasksViewController: TasksPresenterOutput { func showLoadingIndicator (_ show: Bool) { - butRefresh.isHidden = remoteRepository == nil ? true : show - butWarningRightConstraint.constant = butRefresh.isHidden ? 0 : 22 if show { loadingTasksIndicator.isHidden = false loadingTasksIndicator.startAnimation(nil) @@ -127,99 +44,70 @@ extension TasksViewController: TasksPresenterOutput { } } - func showWarning (_ show: Bool) { - butWarning.isHidden = !show - } - func showMessage (_ message: MessageViewModel) { - let controller = appWireframe!.presentPlaceholder(message, intoSplitView: splitView!) + let controller = appWireframe!.presentPlaceholder(message, in: self.view) controller.didPressButton = { - self.presenter?.messageButtonDidPress() +// self.presenter?.messageButtonDidPress() } } - - func showProjects (_ projects: [Project]) { - - var frame = splitView!.subviews[SplitViewColumn.projects.rawValue].frame - frame.origin = NSPoint.zero - let projectsView = ProjectsViewController.instantiateFromStoryboard("Projects") - projectsView.view.frame = frame - splitView.subviews[SplitViewColumn.projects.rawValue].addSubview(projectsView.view) - projectsView.view.constrainToSuperview() - } - - func showCalendar (_ weeks: [Week]) { - - calendarScrollView.weeks = weeks - calendarScrollView.reloadData() - } func showTasks (_ tasks: [Task]) { let dataSource = TasksDataSource(tasks: tasks) - - var frame = splitView!.subviews[SplitViewColumn.tasks.rawValue].frame - frame.origin = NSPoint.zero - let scrollView = TasksScrollView(dataSource: dataSource, listType: .allTasks) - scrollView.frame = frame - splitView.subviews[SplitViewColumn.tasks.rawValue].addSubview(scrollView) - scrollView.constrainToSuperview() - scrollView.didClickAddRow = { [weak self] (row, rect) in + dataSource.didClickAddRow = { [weak self] row in RCLogO("Add item after row \(row)") if row >= 0 { - self?.rectToDisplayPopoverAt = rect ?? self?.tasksScrollView?.frameOfCell(atRow: row) + self?.rectToDisplayPopoverAt = self?.tasksScrollView?.frameOfCell(atRow: row) self?.presenter!.insertTask(after: row) } } - scrollView.didClickRemoveRow = { [weak self] row in + dataSource.didClickRemoveRow = { [weak self] row in RCLogO("Remove item at row \(row)") if row >= 0 { self?.presenter!.removeTask(at: row) self?.tasksScrollView!.removeTask(at: row) } } - scrollView.didClickCloseDay = { [weak self] (tasks, shouldSaveToJira) in - self?.presenter!.closeDay(shouldSaveToJira: shouldSaveToJira) + dataSource.didClickCloseDay = { [weak self] tasks in + self?.presenter!.closeDay(showWorklogs: true) } - - scrollView.reloadData() - tasksScrollView = scrollView - } - - func showReports (_ reports: [Report], numberOfDays: Int, type: ListType) { - - let dataSource = ReportsDataSource(reports: reports, numberOfDays: numberOfDays) - - var frame = splitView.subviews[SplitViewColumn.tasks.rawValue].frame - frame.origin = NSPoint.zero - let scrollView = TasksScrollView(dataSource: dataSource, listType: type) - scrollView.frame = frame - splitView!.subviews[SplitViewColumn.tasks.rawValue].addSubview(scrollView) - scrollView.constrainToSuperview() - scrollView.reloadData() - scrollView.didChangeSettings = { [weak self] in - if let strongSelf = self { - strongSelf.handleSegmentedControl(strongSelf.listSegmentedControl) - } + dataSource.didClickSaveWorklogs = { [weak self] in + self?.presenter!.didClickSaveWorklogs() } - scrollView.didClickCopyMonthlyReport = { asHtml in - var string = "" - let interactor = CreateMonthReport() - if asHtml { - string = interactor.htmlReports(dataSource.reports) - } else { - let joined = interactor.joinReports(dataSource.reports) - string = joined.notes + "\n\n" + joined.totalDuration.secToHoursAndMin - } - NSPasteboard.general.clearContents() - NSPasteboard.general.writeObjects([string as NSPasteboardWriting]) + dataSource.didClickSetupJira = { [weak self] in + self?.appWireframe!.flipToSettingsController() } + let scrollView = TasksScrollView(dataSource: dataSource, listType: .tasks) + self.view.addSubview(scrollView) + scrollView.constrainToSuperview() +// scrollView.didClickAddRow = { [weak self] (row, rect) in +// RCLogO("Add item after row \(row)") +// if row >= 0 { +// self?.rectToDisplayPopoverAt = rect ?? self?.tasksScrollView?.frameOfCell(atRow: row) +// self?.presenter!.insertTask(after: row) +// } +// } +// scrollView.didClickRemoveRow = { [weak self] row in +// RCLogO("Remove item at row \(row)") +// if row >= 0 { +// self?.presenter!.removeTask(at: row) +// self?.tasksScrollView!.removeTask(at: row) +// } +// } +// scrollView.didClickCloseDay = { [weak self] (tasks, shouldSaveToJira) in +// self?.presenter!.closeDay(shouldSaveToJira: shouldSaveToJira) +// } +// scrollView.didClickSaveWorklogs = { [weak self] in +// +// } + + scrollView.reloadData() tasksScrollView = scrollView } - func removeTasksController() { + func removeTasks() { if tasksScrollView != nil { tasksScrollView?.removeFromSuperview() @@ -227,21 +115,18 @@ extension TasksViewController: TasksPresenterOutput { } } - func selectDay (_ day: Day) { - calendarScrollView.selectDay(day) - } - func presentNewTaskController (date: Date) { let popover = NSPopover() - let controller = NewTaskViewController.instantiateFromStoryboard("Components") + let controller = NewTaskViewController.instantiateFromStoryboard("Tasks") controller.onSave = { [weak self] (taskData: TaskCreationData) -> Void in - if let strongSelf = self { - strongSelf.presenter!.insertTaskWithData(taskData) - strongSelf.presenter!.updateNoTasksState() - strongSelf.presenter!.reloadData() - popover.performClose(nil) + guard let self = self else { + return } + self.presenter?.insertTaskWithData(taskData) + self.presenter!.updateNoTasksState() + self.presenter!.reloadLastSelectedDay() + popover.performClose(nil) } controller.onCancel = { [weak self] in if let strongSelf = self { @@ -251,50 +136,44 @@ extension TasksViewController: TasksPresenterOutput { } } popover.contentViewController = controller - popover.show(relativeTo: rectToDisplayPopoverAt!, of: self.tasksScrollView!.view(), preferredEdge: NSRectEdge.maxY) + popover.show(relativeTo: rectToDisplayPopoverAt!, + of: self.view, + preferredEdge: NSRectEdge.maxY) // Add data after popover is presented controller.dateStart = nil// TODO Add scrum start date when around scrum date controller.dateEnd = date activePopover = popover } - + func presentEndDayController (date: Date, tasks: [Task]) { - - splitView.isHidden = true - appWireframe!.removePlaceholder() - hideControls(true) - - let controller = appWireframe!.presentEndDayController(date: date, tasks: tasks) + + let controller = appWireframe!.createWorklogsViewController() + controller.date = date + controller.tasks = tasks + self.view.addSubview(controller.view) + controller.view.constrainToSuperview() controller.onSave = { [weak self] in - if let strongSelf = self { - strongSelf.appWireframe!.removeEndDayController() - strongSelf.splitView.isHidden = false - strongSelf.hideControls(false) - strongSelf.presenter!.reloadData() + guard let self = self else { + return } + self.removeEndDayController() + self.presenter!.reloadLastSelectedDay() } controller.onCancel = { [weak self] in - if let strongSelf = self { - strongSelf.appWireframe!.removeEndDayController() - strongSelf.splitView.isHidden = false - strongSelf.hideControls(false) - strongSelf.presenter!.updateNoTasksState() + guard let self = self else { + return } + self.removeEndDayController() + self.presenter!.reloadLastSelectedDay() + } + worklogsViewController = controller + } + + func removeEndDayController() { + + if worklogsViewController != nil { + worklogsViewController?.removeFromSuperview() + worklogsViewController = nil } } -} - -extension TasksViewController { - - func registerForNotifications() { - - NotificationCenter.default.addObserver(self, - selector: #selector(TasksViewController.handleNewTaskAdded(_:)), - name: NSNotification.Name(rawValue: kNewTaskWasAddedNotification), - object: nil) - } - - @objc func handleNewTaskAdded (_ notif: Notification) { - presenter!.reloadData() - } } diff --git a/Delivery/macOS/Components/TimeBox.swift b/Delivery/macOS/Screens/Tasks/TimeBox.swift similarity index 74% rename from Delivery/macOS/Components/TimeBox.swift rename to Delivery/macOS/Screens/Tasks/TimeBox.swift index 69445cc..cf81c92 100644 --- a/Delivery/macOS/Components/TimeBox.swift +++ b/Delivery/macOS/Screens/Tasks/TimeBox.swift @@ -10,6 +10,7 @@ import Cocoa class TimeBox: NSBox { + internal var backgroundBox: NSBox? internal var timeTextField: NSTextField? var stringValue: String { @@ -24,10 +25,10 @@ class TimeBox: NSBox { didSet { let isDark = self.isDark if #available(OSX 10.14, *) { - self.fillColor = isDark ? NSColor.white : NSColor.darkGray - self.timeTextField?.textColor = isDark ? NSColor.darkGray : NSColor.white +// self.fillColor = .clear //isDark ? .white : .darkGray + self.timeTextField?.textColor = .labelColor //isDark ? .darkGray : .white } else { - self.fillColor = isDark ? NSColor.darkGray : NSColor.white +// self.fillColor = isDark ? NSColor.darkGray : NSColor.white // self.borderColor = isDark ? NSColor.clear : NSColor.white self.timeTextField?.textColor = isDark ? NSColor.white : NSColor.darkGray // self.timeTextField?.backgroundColor = isDark ? NSColor.darkGray : NSColor.darkGray @@ -38,6 +39,8 @@ class TimeBox: NSBox { init() { super.init(frame: NSRect.zero) + self.wantsLayer = true + self.layer?.backgroundColor = NSColor.clear.cgColor stringValue = "" } @@ -48,13 +51,19 @@ class TimeBox: NSBox { override func awakeFromNib() { super.awakeFromNib() - self.borderType = .noBorder + backgroundBox = NSBox() + backgroundBox?.borderType = .noBorder + backgroundBox?.cornerRadius = 7 + backgroundBox?.boxType = .custom + backgroundBox?.fillColor = .clear +// self.addSubview(backgroundBox!) +// backgroundBox?.constrainToSuperview() timeTextField = NSTextField() - timeTextField?.font = NSFont.boldSystemFont(ofSize: 10) - timeTextField?.textColor = NSColor.darkGray - timeTextField?.backgroundColor = NSColor.clear - timeTextField?.drawsBackground = false + timeTextField?.font = NSFont.systemFont(ofSize: 10) + timeTextField?.textColor = .darkGray + timeTextField?.backgroundColor = .clear +// timeTextField?.drawsBackground = false timeTextField?.alignment = .center timeTextField?.focusRingType = .none timeTextField?.placeholderString = "00:00" @@ -68,7 +77,7 @@ class TimeBox: NSBox { self.addConstraints(NSLayoutConstraint.constraints( withVisualFormat: "H:|-(-5)-[view]-(-5)-|", options: [], metrics: nil, views: viewsDictionary)) self.addConstraints(NSLayoutConstraint.constraints( - withVisualFormat: "V:|-(-3)-[view]-(-5)-|", options: [], metrics: nil, views: viewsDictionary)) + withVisualFormat: "V:|-(-3)-[view]-(-6)-|", options: [], metrics: nil, views: viewsDictionary)) } override func mouseDown(with event: NSEvent) { diff --git a/Delivery/macOS/Components/TimeBoxViewController.swift b/Delivery/macOS/Screens/Tasks/TimeBoxViewController.swift similarity index 100% rename from Delivery/macOS/Components/TimeBoxViewController.swift rename to Delivery/macOS/Screens/Tasks/TimeBoxViewController.swift diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/CellProtocol.swift b/Delivery/macOS/Screens/Tasks/cells/CellProtocol.swift similarity index 100% rename from Delivery/macOS/Screens/Tasks/AllTasks/CellProtocol.swift rename to Delivery/macOS/Screens/Tasks/cells/CellProtocol.swift diff --git a/Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.swift b/Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.swift new file mode 100644 index 0000000..c51836e --- /dev/null +++ b/Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.swift @@ -0,0 +1,27 @@ +// +// CloseDayCell.swift +// Jirassic +// +// Created by Cristian Baluta on 25/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa + +class CloseDayCell: NSTableRowView { + + @IBOutlet private var butAdd: NSButton! + @IBOutlet private var butCloseDay: NSButton! + + var didClickAddTask: (() -> Void)? + var didClickCloseDay: (() -> Void)? + + @IBAction func handleAddButton (_ sender: NSButton) { + didClickAddTask?() + } + + @IBAction func handleCloseDayButton (_ sender: NSButton) { + didClickCloseDay?() + } + +} diff --git a/Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.xib b/Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.xib new file mode 100644 index 0000000..4480ce8 --- /dev/null +++ b/Delivery/macOS/Screens/Tasks/cells/CloseDayCell/CloseDayCell.xib @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.swift b/Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.swift new file mode 100644 index 0000000..d09ee64 --- /dev/null +++ b/Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.swift @@ -0,0 +1,42 @@ +// +// ClosedDayCell.swift +// Jirassic +// +// Created by Cristian Baluta on 25/12/2019. +// Copyright © 2019 Imagin soft. All rights reserved. +// + +import Cocoa +import RCPreferences + +class ClosedDayCell: NSTableRowView { + + @IBOutlet private var butSaveWorklogs: NSButton! + @IBOutlet private var butSetupJira: NSButton! + + private unowned let appWireframe = AppDelegate.sharedApp().appWireframe + private var store = Store.shared + private var moduleJira = ModuleJiraTempo() + private let pref = RCPreferences() + + var didClickSaveWorklogs: (() -> Void)? + var didClickSetupJira: (() -> Void)? + + override func awakeFromNib() { + super.awakeFromNib() + let isJiraAvailable = store.isJiraTempoPurchased && + moduleJira.isConfigured && + moduleJira.isProjectConfigured + butSaveWorklogs.isEnabled = isJiraAvailable + butSetupJira.isHidden = isJiraAvailable + } + + @IBAction func handleSaveWorklogsButton (_ sender: NSButton) { + didClickSaveWorklogs?() + } + + @IBAction func handleSetupJiraButton (_ sender: NSButton) { + didClickSetupJira?() + } + +} diff --git a/Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.xib b/Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.xib new file mode 100644 index 0000000..e2fe486 --- /dev/null +++ b/Delivery/macOS/Screens/Tasks/cells/ClosedDayCell/ClosedDayCell.xib @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/NonTaskCell/NonTaskCell.swift b/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.swift similarity index 94% rename from Delivery/macOS/Screens/Tasks/AllTasks/NonTaskCell/NonTaskCell.swift rename to Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.swift index 7fffb7d..2df510a 100644 --- a/Delivery/macOS/Screens/Tasks/AllTasks/NonTaskCell/NonTaskCell.swift +++ b/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.swift @@ -14,11 +14,12 @@ class NonTaskCell: NSTableRowView, CellProtocol { @IBOutlet private var statusImageWidthContraint: NSLayoutConstraint! @IBOutlet private var dateStartTextField: TimeBox! @IBOutlet private var dateStartTextFieldLeadingContraint: NSLayoutConstraint! - @IBOutlet private var dateEndTextField: TimeBox! - @IBOutlet private var notesTextField: NSTextField! - @IBOutlet private var notesTextFieldTrailingContraint: NSLayoutConstraint! - @IBOutlet private var butRemove: NSButton! - @IBOutlet private var butAdd: NSButton! + @IBOutlet private var bullet: NSTextField! + @IBOutlet private var dateEndTextField: TimeBox! + @IBOutlet private var notesTextField: NSTextField! + @IBOutlet private var notesTextFieldTrailingContraint: NSLayoutConstraint! + @IBOutlet private var butRemove: NSButton! + @IBOutlet private var butAdd: NSButton! private var isEditing = false private var wasEdited = false @@ -53,10 +54,12 @@ class NonTaskCell: NSTableRowView, CellProtocol { if let dateStart = newValue.dateStart { self.dateStartTextField.stringValue = dateStart.HHmm() self.dateStartTextField.isHidden = false - self.dateStartTextFieldLeadingContraint.constant = 14 + self.bullet.isHidden = false + self.dateStartTextFieldLeadingContraint.constant = 20 } else { self.dateStartTextField.isHidden = true - self.dateStartTextFieldLeadingContraint.constant = 14 - 36 - 4 + self.bullet.isHidden = true + self.dateStartTextFieldLeadingContraint.constant = 20 - 36 - 8 } self.dateEndTextField.stringValue = newValue.dateEnd.HHmm() self.notesTextField.stringValue = newValue.notes ?? "" diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/NonTaskCell/NonTaskCell.xib b/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.xib similarity index 72% rename from Delivery/macOS/Screens/Tasks/AllTasks/NonTaskCell/NonTaskCell.xib rename to Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.xib index dc7be91..3cc04e7 100644 --- a/Delivery/macOS/Screens/Tasks/AllTasks/NonTaskCell/NonTaskCell.xib +++ b/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.xib @@ -1,8 +1,8 @@ - + - + @@ -14,7 +14,7 @@ - + @@ -24,26 +24,37 @@ - - - + + + + + + + + + + + + + + - - + + - - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/TaskCell/TaskCellPresenter.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift similarity index 95% rename from Delivery/macOS/Screens/Tasks/AllTasks/TaskCell/TaskCellPresenter.swift rename to Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift index 7d59588..aa332eb 100644 --- a/Delivery/macOS/Screens/Tasks/AllTasks/TaskCell/TaskCellPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift @@ -49,6 +49,9 @@ class TaskCellPresenter: NSObject { // The codereview notes is a list of tasks that were reviewed if theTask.taskType == .coderev && theTask.notes != nil && theTask.notes != "" { notes = "\(theTask.taskType.defaultNotes): \(notes)" + } + if theTask.taskType == .lunch { + notes = theTask.taskType.defaultNotes } cell.data = ( dateStart: theTask.startDate, diff --git a/Delivery/macOS/Screens/Tasks/AllTasks/TaskCell/TaskCellTests.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellTests.swift similarity index 100% rename from Delivery/macOS/Screens/Tasks/AllTasks/TaskCell/TaskCellTests.swift rename to Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellTests.swift diff --git a/Delivery/macOS/Screens/Worklogs/Worklogs.storyboard b/Delivery/macOS/Screens/Worklogs/Worklogs.storyboard index 7c5e649..f8df507 100644 --- a/Delivery/macOS/Screens/Worklogs/Worklogs.storyboard +++ b/Delivery/macOS/Screens/Worklogs/Worklogs.storyboard @@ -1,8 +1,8 @@ - + - + @@ -15,13 +15,13 @@ - + - + - - - - - - - - - - - - + @@ -84,7 +73,10 @@ Gw - + + + + @@ -92,24 +84,24 @@ Gw - + - + - + - + - + - - + + @@ -120,7 +112,7 @@ Gw - + @@ -134,7 +126,7 @@ Gw - + @@ -161,7 +153,7 @@ Gw - + @@ -170,14 +162,13 @@ Gw + - - - + + - - + @@ -188,22 +179,22 @@ Gw - + - - + + + - + - diff --git a/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift b/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift index 60a7f27..40f75b9 100644 --- a/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift +++ b/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift @@ -76,21 +76,21 @@ extension WorklogsPresenter: WorklogsPresenterInput { let duration = isRoundingEnabled ? workdayLength : workedLength moduleJira.postWorklog(worklog: worklog, duration: duration, date: date!, success: { [weak self] in - DispatchQueue.main.async { - if let userInterface = self?.userInterface { - userInterface.showProgressIndicator(false) - userInterface.showJiraMessage("Worklogs saved to Jira", isError: false) - userInterface.saveSuccess() - } + DispatchQueue.main.async { + if let userInterface = self?.userInterface { + userInterface.showProgressIndicator(false) + userInterface.showJiraMessage("Worklogs saved to Jira", isError: false) + userInterface.saveSuccess() } - }, failure: { [weak self] error in - - DispatchQueue.main.async { - if let userInterface = self?.userInterface { - userInterface.showProgressIndicator(false) - userInterface.showJiraMessage(error.localizedDescription, isError: true) - } + } + }, failure: { [weak self] error in + + DispatchQueue.main.async { + if let userInterface = self?.userInterface { + userInterface.showProgressIndicator(false) + userInterface.showJiraMessage(error.localizedDescription, isError: true) } + } }) } diff --git a/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift b/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift index c568c27..6f86b86 100644 --- a/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift +++ b/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift @@ -10,7 +10,6 @@ import Cocoa class WorklogsViewController: NSViewController { - @IBOutlet private var dateTextField: NSTextField! @IBOutlet private var durationTextField: NSTextField! @IBOutlet private var worklogTextView: NSTextView! @IBOutlet private var progressIndicator: NSProgressIndicator! @@ -21,14 +20,13 @@ class WorklogsViewController: NSViewController { var onSave: (() -> Void)? var onCancel: (() -> Void)? var presenter: WorklogsPresenterInput? + weak var appWireframe: AppWireframe? var date: Date? var tasks: [Task]? - weak var appWireframe: AppWireframe? override func viewDidLoad() { super.viewDidLoad() - dateTextField.stringValue = date!.EEEEMMMdd() jiraErrorTextField.stringValue = "" worklogTextView.drawsBackground = false diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 703bee9..4f40cf2 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -111,7 +111,6 @@ 280D71781ED6043D005D2689 /* NewTaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3711DD3B44200B73201 /* NewTaskViewController.swift */; }; 280D717A1ED6043D005D2689 /* TasksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3851DD3B44200B73201 /* TasksViewController.swift */; }; 280D717B1ED6043D005D2689 /* TasksPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3831DD3B44200B73201 /* TasksPresenter.swift */; }; - 280D717C1ED6043D005D2689 /* CalendarScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */; }; 280D717D1ED6043D005D2689 /* TasksScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3841DD3B44200B73201 /* TasksScrollView.swift */; }; 280D717E1ED6043D005D2689 /* TasksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */; }; 280D71811ED6043D005D2689 /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F2811E586426002BE564 /* DataSource.swift */; }; @@ -136,7 +135,6 @@ 280D71B71ED6043D005D2689 /* RepositoryInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3951DD3B44200B73201 /* RepositoryInteractor.swift */; }; 280D71BC1ED60683005D2689 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 28577EB91E8AF379002B07FD /* libsqlite3.tbd */; }; 280D71BE1ED6069A005D2689 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4055B1381E0D82A900279430 /* ServiceManagement.framework */; }; - 280D71BF1ED608D6005D2689 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4065D3591DD3B44200B73201 /* Main.storyboard */; }; 280D71C01ED608D6005D2689 /* Placeholder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28E896621E830D6700722032 /* Placeholder.storyboard */; }; 280D71C11ED608D6005D2689 /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 40FCE42D1DFC576C00D4FD45 /* Welcome.storyboard */; }; 280D71C21ED608D6005D2689 /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5685C7641DE8721100CA545E /* Login.storyboard */; }; @@ -188,10 +186,6 @@ 2845B163206AE468006EFB3B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B156206AE467006EFB3B /* TaskCell.xib */; }; 2845B164206AE468006EFB3B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */; }; 2845B165206AE468006EFB3B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */; }; - 2845B166206AE468006EFB3B /* TasksHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B159206AE467006EFB3B /* TasksHeaderView.swift */; }; - 2845B167206AE468006EFB3B /* TasksHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B159206AE467006EFB3B /* TasksHeaderView.swift */; }; - 2845B168206AE468006EFB3B /* TasksHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */; }; - 2845B169206AE468006EFB3B /* TasksHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */; }; 2845B16A206AE468006EFB3B /* NonTaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* NonTaskCell.swift */; }; 2845B16B206AE468006EFB3B /* NonTaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* NonTaskCell.swift */; }; 2845B16C206AE468006EFB3B /* NonTaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15D206AE467006EFB3B /* NonTaskCell.xib */; }; @@ -217,6 +211,49 @@ 28577EB71E8AF08F002B07FD /* SQLTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EB41E8AF08F002B07FD /* SQLTable.swift */; }; 28577EB81E8AF08F002B07FD /* SQLTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EB41E8AF08F002B07FD /* SQLTable.swift */; }; 28577EBA1E8AF379002B07FD /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 28577EB91E8AF379002B07FD /* libsqlite3.tbd */; }; + 286348F523B136B3009F81D7 /* ProjectsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286348F423B136B3009F81D7 /* ProjectsInteractor.swift */; }; + 286348F623B136B3009F81D7 /* ProjectsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286348F423B136B3009F81D7 /* ProjectsInteractor.swift */; }; + 286348F923B22775009F81D7 /* ProjectDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286348F823B22775009F81D7 /* ProjectDetailsViewController.swift */; }; + 286348FA23B22775009F81D7 /* ProjectDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286348F823B22775009F81D7 /* ProjectDetailsViewController.swift */; }; + 286348FB23B23748009F81D7 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; + 286348FC23B237B1009F81D7 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; + 286348FD23B237B2009F81D7 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; + 2863490023B24335009F81D7 /* Calendar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 286348FF23B24335009F81D7 /* Calendar.storyboard */; }; + 2863490123B24335009F81D7 /* Calendar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 286348FF23B24335009F81D7 /* Calendar.storyboard */; }; + 2863490323B24800009F81D7 /* CalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863490223B247FF009F81D7 /* CalendarViewController.swift */; }; + 2863490423B24800009F81D7 /* CalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863490223B247FF009F81D7 /* CalendarViewController.swift */; }; + 2863490623B24C22009F81D7 /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863490523B24C22009F81D7 /* CalendarPresenter.swift */; }; + 2863490723B24C22009F81D7 /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863490523B24C22009F81D7 /* CalendarPresenter.swift */; }; + 2863490923B24C34009F81D7 /* CalendarInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863490823B24C34009F81D7 /* CalendarInteractor.swift */; }; + 2863490A23B24C34009F81D7 /* CalendarInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863490823B24C34009F81D7 /* CalendarInteractor.swift */; }; + 2863490E23B29979009F81D7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2863490C23B29979009F81D7 /* Main.storyboard */; }; + 2863490F23B29979009F81D7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2863490C23B29979009F81D7 /* Main.storyboard */; }; + 2863491123B299C8009F81D7 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491023B299C8009F81D7 /* MainViewController.swift */; }; + 2863491223B299C8009F81D7 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491023B299C8009F81D7 /* MainViewController.swift */; }; + 2863491423B299DB009F81D7 /* MainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491323B299DB009F81D7 /* MainPresenter.swift */; }; + 2863491523B299DB009F81D7 /* MainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491323B299DB009F81D7 /* MainPresenter.swift */; }; + 2863491723B354A0009F81D7 /* CalendarDayCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491623B354A0009F81D7 /* CalendarDayCellView.swift */; }; + 2863491823B354A0009F81D7 /* CalendarDayCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491623B354A0009F81D7 /* CalendarDayCellView.swift */; }; + 2863491B23B393CF009F81D7 /* CloseDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491A23B393CF009F81D7 /* CloseDayCell.swift */; }; + 2863491C23B393CF009F81D7 /* CloseDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863491A23B393CF009F81D7 /* CloseDayCell.swift */; }; + 2863491E23B393E1009F81D7 /* CloseDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2863491D23B393E1009F81D7 /* CloseDayCell.xib */; }; + 2863491F23B393E1009F81D7 /* CloseDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2863491D23B393E1009F81D7 /* CloseDayCell.xib */; }; + 2863492223B3A95A009F81D7 /* ClosedDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492123B3A95A009F81D7 /* ClosedDayCell.swift */; }; + 2863492323B3A95A009F81D7 /* ClosedDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492123B3A95A009F81D7 /* ClosedDayCell.swift */; }; + 2863492523B3A966009F81D7 /* ClosedDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2863492423B3A966009F81D7 /* ClosedDayCell.xib */; }; + 2863492623B3A966009F81D7 /* ClosedDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2863492423B3A966009F81D7 /* ClosedDayCell.xib */; }; + 2863492923B5EF87009F81D7 /* Reports.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2863492823B5EF87009F81D7 /* Reports.storyboard */; }; + 2863492A23B5EF87009F81D7 /* Reports.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2863492823B5EF87009F81D7 /* Reports.storyboard */; }; + 2863492C23B5EF9C009F81D7 /* ReportsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492B23B5EF9B009F81D7 /* ReportsViewController.swift */; }; + 2863492D23B5EF9C009F81D7 /* ReportsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492B23B5EF9B009F81D7 /* ReportsViewController.swift */; }; + 2863492F23B5EFD1009F81D7 /* ReportsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */; }; + 2863493023B5EFD1009F81D7 /* ReportsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */; }; + 2863493223B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */; }; + 2863493323B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */; }; + 2863493523B790B1009F81D7 /* ProjectsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */; }; + 2863493623B790B1009F81D7 /* ProjectsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */; }; + 2863493823B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */; }; + 2863493923B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */; }; 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 286BC00421909F85004D4CDD /* CloseDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDay.swift */; }; @@ -353,8 +390,8 @@ 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F5D20EE8D1000C5C1D6 /* CalendarCell.swift */; }; 28EE1F6120EE8D6100C5C1D6 /* CalendarCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */; }; 28EE1F6220EE8D6100C5C1D6 /* CalendarCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */; }; - 28EE1F6420EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */; }; - 28EE1F6520EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */; }; + 28EE1F6420EE92DA00C5C1D6 /* CalendarAppPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F6320EE92DA00C5C1D6 /* CalendarAppPresenter.swift */; }; + 28EE1F6520EE92DA00C5C1D6 /* CalendarAppPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F6320EE92DA00C5C1D6 /* CalendarAppPresenter.swift */; }; 28FB265120224E3A00AEA38D /* JiraTempoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265020224E3A00AEA38D /* JiraTempoCell.swift */; }; 28FB265220224E3A00AEA38D /* JiraTempoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265020224E3A00AEA38D /* JiraTempoCell.swift */; }; 28FB265420224E5B00AEA38D /* JiraTempoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28FB265320224E5B00AEA38D /* JiraTempoCell.xib */; }; @@ -406,7 +443,6 @@ 4065D3C81DD3B44200B73201 /* FlipAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3521DD3B44200B73201 /* FlipAnimation.swift */; }; 4065D3C91DD3B44200B73201 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3541DD3B44200B73201 /* AppDelegate.swift */; }; 4065D3CA1DD3B44200B73201 /* AppWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3551DD3B44200B73201 /* AppWireframe.swift */; }; - 4065D3CE1DD3B44200B73201 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4065D3591DD3B44200B73201 /* Main.storyboard */; }; 4065D3CF1DD3B44200B73201 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4065D35B1DD3B44200B73201 /* Images.xcassets */; }; 4065D3D31DD3B44200B73201 /* jirassic.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 4065D3611DD3B44200B73201 /* jirassic.sdef */; }; 4065D3D61DD3B44200B73201 /* MenuBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3651DD3B44200B73201 /* MenuBarController.swift */; }; @@ -418,7 +454,6 @@ 4065D3DF1DD3B44200B73201 /* SettingsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3731DD3B44200B73201 /* SettingsInteractor.swift */; }; 4065D3E01DD3B44200B73201 /* SettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3741DD3B44200B73201 /* SettingsPresenter.swift */; }; 4065D3E11DD3B44200B73201 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3751DD3B44200B73201 /* SettingsViewController.swift */; }; - 4065D3E21DD3B44200B73201 /* CalendarScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */; }; 4065D3E31DD3B44200B73201 /* CellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3781DD3B44200B73201 /* CellProtocol.swift */; }; 4065D3EE1DD3B44200B73201 /* TasksPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3831DD3B44200B73201 /* TasksPresenter.swift */; }; 4065D3EF1DD3B44200B73201 /* TasksScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3841DD3B44200B73201 /* TasksScrollView.swift */; }; @@ -612,8 +647,6 @@ 2845B155206AE467006EFB3B /* TaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell.swift; sourceTree = ""; }; 2845B156206AE467006EFB3B /* TaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TaskCell.xib; sourceTree = ""; }; 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCellPresenter.swift; sourceTree = ""; }; - 2845B159206AE467006EFB3B /* TasksHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksHeaderView.swift; sourceTree = ""; }; - 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TasksHeaderView.xib; sourceTree = ""; }; 2845B15C206AE467006EFB3B /* NonTaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonTaskCell.swift; sourceTree = ""; }; 2845B15D206AE467006EFB3B /* NonTaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NonTaskCell.xib; sourceTree = ""; }; 285465A1216C84E10052CB6A /* CreateMonthReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateMonthReport.swift; sourceTree = ""; }; @@ -627,6 +660,26 @@ 28577EB41E8AF08F002B07FD /* SQLTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLTable.swift; sourceTree = ""; }; 28577EB91E8AF379002B07FD /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 28577EBB1E8AFB1B002B07FD /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + 286348F423B136B3009F81D7 /* ProjectsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsInteractor.swift; sourceTree = ""; }; + 286348F823B22775009F81D7 /* ProjectDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsViewController.swift; sourceTree = ""; }; + 286348FF23B24335009F81D7 /* Calendar.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Calendar.storyboard; sourceTree = ""; }; + 2863490223B247FF009F81D7 /* CalendarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewController.swift; sourceTree = ""; }; + 2863490523B24C22009F81D7 /* CalendarPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarPresenter.swift; sourceTree = ""; }; + 2863490823B24C34009F81D7 /* CalendarInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarInteractor.swift; sourceTree = ""; }; + 2863490D23B29979009F81D7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 2863491023B299C8009F81D7 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; + 2863491323B299DB009F81D7 /* MainPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainPresenter.swift; sourceTree = ""; }; + 2863491623B354A0009F81D7 /* CalendarDayCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDayCellView.swift; sourceTree = ""; }; + 2863491A23B393CF009F81D7 /* CloseDayCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDayCell.swift; sourceTree = ""; }; + 2863491D23B393E1009F81D7 /* CloseDayCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CloseDayCell.xib; sourceTree = ""; }; + 2863492123B3A95A009F81D7 /* ClosedDayCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedDayCell.swift; sourceTree = ""; }; + 2863492423B3A966009F81D7 /* ClosedDayCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ClosedDayCell.xib; sourceTree = ""; }; + 2863492823B5EF87009F81D7 /* Reports.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Reports.storyboard; sourceTree = ""; }; + 2863492B23B5EF9B009F81D7 /* ReportsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsViewController.swift; sourceTree = ""; }; + 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsPresenter.swift; sourceTree = ""; }; + 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsInteractor.swift; sourceTree = ""; }; + 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsListViewController.swift; sourceTree = ""; }; + 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsPresenter.swift; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; 286BC00321909F85004D4CDD /* CloseDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDay.swift; sourceTree = ""; }; 287195142022E1E2001C237E /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; @@ -694,7 +747,7 @@ 28EDE9381E59EC1500B360A4 /* TasksView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksView.swift; sourceTree = ""; }; 28EE1F5D20EE8D1000C5C1D6 /* CalendarCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarCell.swift; sourceTree = ""; }; 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CalendarCell.xib; sourceTree = ""; }; - 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarPresenter.swift; sourceTree = ""; }; + 28EE1F6320EE92DA00C5C1D6 /* CalendarAppPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarAppPresenter.swift; sourceTree = ""; }; 28FB265020224E3A00AEA38D /* JiraTempoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JiraTempoCell.swift; sourceTree = ""; }; 28FB265320224E5B00AEA38D /* JiraTempoCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JiraTempoCell.xib; sourceTree = ""; }; 28FB265720224E9B00AEA38D /* HookupCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HookupCell.xib; sourceTree = ""; }; @@ -757,7 +810,6 @@ 4065D3521DD3B44200B73201 /* FlipAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlipAnimation.swift; sourceTree = ""; }; 4065D3541DD3B44200B73201 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 4065D3551DD3B44200B73201 /* AppWireframe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppWireframe.swift; sourceTree = ""; }; - 4065D35A1DD3B44200B73201 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 4065D35B1DD3B44200B73201 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 4065D35C1DD3B44200B73201 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4065D3611DD3B44200B73201 /* jirassic.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = jirassic.sdef; sourceTree = ""; }; @@ -770,7 +822,6 @@ 4065D3731DD3B44200B73201 /* SettingsInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsInteractor.swift; sourceTree = ""; }; 4065D3741DD3B44200B73201 /* SettingsPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsPresenter.swift; sourceTree = ""; }; 4065D3751DD3B44200B73201 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; - 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarScrollView.swift; sourceTree = ""; }; 4065D3781DD3B44200B73201 /* CellProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellProtocol.swift; sourceTree = ""; }; 4065D3831DD3B44200B73201 /* TasksPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksPresenter.swift; sourceTree = ""; }; 4065D3841DD3B44200B73201 /* TasksScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksScrollView.swift; sourceTree = ""; }; @@ -895,10 +946,6 @@ 2801388D205F86250051B532 /* Components */ = { isa = PBXGroup; children = ( - 2801388E205F86460051B532 /* TimeBox.swift */, - 2898D3B02185959300CF5AD4 /* EditableTimeBox.swift */, - 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */, - 4065D3711DD3B44200B73201 /* NewTaskViewController.swift */, 28279AD421C6245700376304 /* GitUsersViewController.swift */, 2898D3AD2184ED3000CF5AD4 /* Components.storyboard */, ); @@ -939,20 +986,23 @@ path = Keychain; sourceTree = ""; }; - 2845B141206AE2A4006EFB3B /* Reports */ = { + 2845B141206AE2A4006EFB3B /* cells */ = { isa = PBXGroup; children = ( - 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */, 287B943B21A690C100FFC4A5 /* HeaderView */, 2845B143206AE3A8006EFB3B /* ReportCell */, ); - path = Reports; + path = cells; sourceTree = ""; }; 2845B142206AE342006EFB3B /* Calendar */ = { isa = PBXGroup; children = ( - 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */, + 2863490223B247FF009F81D7 /* CalendarViewController.swift */, + 2863490523B24C22009F81D7 /* CalendarPresenter.swift */, + 2863490823B24C34009F81D7 /* CalendarInteractor.swift */, + 2863491623B354A0009F81D7 /* CalendarDayCellView.swift */, + 286348FF23B24335009F81D7 /* Calendar.storyboard */, ); path = Calendar; sourceTree = ""; @@ -978,15 +1028,6 @@ path = TaskCell; sourceTree = ""; }; - 2845B158206AE467006EFB3B /* HeaderView */ = { - isa = PBXGroup; - children = ( - 2845B159206AE467006EFB3B /* TasksHeaderView.swift */, - 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */, - ); - path = HeaderView; - sourceTree = ""; - }; 2845B15B206AE467006EFB3B /* NonTaskCell */ = { isa = PBXGroup; children = ( @@ -996,16 +1037,16 @@ path = NonTaskCell; sourceTree = ""; }; - 2845B16F206C2888006EFB3B /* AllTasks */ = { + 2845B16F206C2888006EFB3B /* cells */ = { isa = PBXGroup; children = ( - 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */, 4065D3781DD3B44200B73201 /* CellProtocol.swift */, - 2845B158206AE467006EFB3B /* HeaderView */, + 2863492023B3A941009F81D7 /* ClosedDayCell */, + 2863491923B393A3009F81D7 /* CloseDayCell */, 2845B15B206AE467006EFB3B /* NonTaskCell */, 2845B153206AE467006EFB3B /* TaskCell */, ); - path = AllTasks; + path = cells; sourceTree = ""; }; 285465A5217858120052CB6A /* Store */ = { @@ -1035,6 +1076,55 @@ path = sqlite; sourceTree = ""; }; + 286348FE23B237D9009F81D7 /* Projects */ = { + isa = PBXGroup; + children = ( + ); + path = Projects; + sourceTree = ""; + }; + 2863490B23B2982A009F81D7 /* Main */ = { + isa = PBXGroup; + children = ( + 2863490C23B29979009F81D7 /* Main.storyboard */, + 2863491023B299C8009F81D7 /* MainViewController.swift */, + 2863491323B299DB009F81D7 /* MainPresenter.swift */, + ); + path = Main; + sourceTree = ""; + }; + 2863491923B393A3009F81D7 /* CloseDayCell */ = { + isa = PBXGroup; + children = ( + 2863491A23B393CF009F81D7 /* CloseDayCell.swift */, + 2863491D23B393E1009F81D7 /* CloseDayCell.xib */, + ); + path = CloseDayCell; + sourceTree = ""; + }; + 2863492023B3A941009F81D7 /* ClosedDayCell */ = { + isa = PBXGroup; + children = ( + 2863492123B3A95A009F81D7 /* ClosedDayCell.swift */, + 2863492423B3A966009F81D7 /* ClosedDayCell.xib */, + ); + path = ClosedDayCell; + sourceTree = ""; + }; + 2863492723B5EF31009F81D7 /* Reports */ = { + isa = PBXGroup; + children = ( + 2863492823B5EF87009F81D7 /* Reports.storyboard */, + 2863492B23B5EF9B009F81D7 /* ReportsViewController.swift */, + 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */, + 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */, + 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */, + 2845B141206AE2A4006EFB3B /* cells */, + 2898D3A62181905600CF5AD4 /* MonthReports */, + ); + path = Reports; + sourceTree = ""; + }; 28667B591FCADF4E007B98E3 /* Modules */ = { isa = PBXGroup; children = ( @@ -1156,7 +1246,7 @@ isa = PBXGroup; children = ( 28EE1F5D20EE8D1000C5C1D6 /* CalendarCell.swift */, - 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */, + 28EE1F6320EE92DA00C5C1D6 /* CalendarAppPresenter.swift */, 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */, ); path = Calendar; @@ -1262,6 +1352,7 @@ 40FCE4281DFC1CB400D4FD45 /* TaskSuggestionTests.swift */, ); name = TaskSuggestion; + path = ..; sourceTree = ""; }; 405B15641DEF66EE0009871C /* Resources */ = { @@ -1312,6 +1403,7 @@ 4065D31F1DD3B44200B73201 /* Extensions */, 4065D3271DD3B44200B73201 /* Notifications */, 4065D32A1DD3B44200B73201 /* Reports */, + 286348FE23B237D9009F81D7 /* Projects */, 4065D32D1DD3B44200B73201 /* Tasks */, 280300D41EDB5E6D000A763E /* Statistics */, 4065D3371DD3B44200B73201 /* Time */, @@ -1474,7 +1566,6 @@ 4065D3531DD3B44200B73201 /* App */ = { isa = PBXGroup; children = ( - 4065D3591DD3B44200B73201 /* Main.storyboard */, 4065D3541DD3B44200B73201 /* AppDelegate.swift */, 4065D3551DD3B44200B73201 /* AppWireframe.swift */, 405B15651DEF75660009871C /* AppViewController.swift */, @@ -1509,15 +1600,16 @@ 4065D36B1DD3B44200B73201 /* Screens */ = { isa = PBXGroup; children = ( + 2863490B23B2982A009F81D7 /* Main */, 28279E9A1E8300E200EAF9FC /* Placeholder */, 287B358520FBAD160022F43E /* Onboarding */, - 405B155F1DEF3CE20009871C /* TaskSuggestion */, 5685C7621DE8721100CA545E /* Account */, 4065D3721DD3B44200B73201 /* Settings */, 564E55F12028839F00CE4C76 /* Worklogs */, - 5623914821CBC66C0031B882 /* Projects */, 2845B142206AE342006EFB3B /* Calendar */, 4065D3761DD3B44200B73201 /* Tasks */, + 2863492723B5EF31009F81D7 /* Reports */, + 5623914821CBC66C0031B882 /* Projects */, ); path = Screens; sourceTree = ""; @@ -1547,10 +1639,14 @@ 566B9FA6217DE67700EAF324 /* TasksInteractor.swift */, 4065D3841DD3B44200B73201 /* TasksScrollView.swift */, 28EDE9381E59EC1500B360A4 /* TasksView.swift */, + 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */, 28A5F2811E586426002BE564 /* DataSource.swift */, - 2845B16F206C2888006EFB3B /* AllTasks */, - 2845B141206AE2A4006EFB3B /* Reports */, - 2898D3A62181905600CF5AD4 /* MonthReports */, + 4065D3711DD3B44200B73201 /* NewTaskViewController.swift */, + 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */, + 2898D3B02185959300CF5AD4 /* EditableTimeBox.swift */, + 2801388E205F86460051B532 /* TimeBox.swift */, + 405B155F1DEF3CE20009871C /* TaskSuggestion */, + 2845B16F206C2888006EFB3B /* cells */, ); path = Tasks; sourceTree = ""; @@ -1647,6 +1743,10 @@ children = ( 5623914921CBC6A10031B882 /* ProjectsViewController.swift */, 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */, + 286348F423B136B3009F81D7 /* ProjectsInteractor.swift */, + 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */, + 286348F823B22775009F81D7 /* ProjectDetailsViewController.swift */, + 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */, 5623915221CBC9750031B882 /* Projects.storyboard */, ); path = Projects; @@ -1675,9 +1775,9 @@ children = ( 5685C76B1DE8724400CA545E /* AccountViewController.swift */, 5685C7631DE8721100CA545E /* CloudKitLoginViewController.swift */, - 5685C7641DE8721100CA545E /* Login.storyboard */, 5685C7651DE8721100CA545E /* LoginPresenter.swift */, 5685C7661DE8721100CA545E /* LoginViewController.swift */, + 5685C7641DE8721100CA545E /* Login.storyboard */, ); path = Account; sourceTree = ""; @@ -1971,9 +2071,10 @@ 28A283A82038AFC500DDCB63 /* JirassicCell.xib in Resources */, 288BB6812087152B00CF720A /* WizardAppleScriptView.xib in Resources */, 280D71C91ED60908005D2689 /* jirassic.sdef in Resources */, + 2863490E23B29979009F81D7 /* Main.storyboard in Resources */, 2892E80B208D9B42004E5298 /* InputsScrollView.xib in Resources */, 28EE1F6120EE8D6100C5C1D6 /* CalendarCell.xib in Resources */, - 280D71BF1ED608D6005D2689 /* Main.storyboard in Resources */, + 2863490023B24335009F81D7 /* Calendar.storyboard in Resources */, 2898D3AE2184ED3000CF5AD4 /* Components.storyboard in Resources */, 280D71C01ED608D6005D2689 /* Placeholder.storyboard in Resources */, 280C1D441ED74EF900C126A1 /* ShellSupport.scpt in Resources */, @@ -1981,6 +2082,7 @@ 28B116BB21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, 287B358A20FBAFD60022F43E /* WizardCalendarView.xib in Resources */, 280D71C11ED608D6005D2689 /* Welcome.storyboard in Resources */, + 2863491E23B393E1009F81D7 /* CloseDayCell.xib in Resources */, 5623915321CBC9750031B882 /* Projects.storyboard in Resources */, 280D71C21ED608D6005D2689 /* Login.storyboard in Resources */, 56D90CFE20876F3C00F24442 /* WizardGitView.xib in Resources */, @@ -1988,6 +2090,7 @@ 280D71C41ED608D6005D2689 /* Tasks.storyboard in Resources */, 564E55F92028857300CE4C76 /* Worklogs.storyboard in Resources */, 2845B162206AE468006EFB3B /* TaskCell.xib in Resources */, + 2863492523B3A966009F81D7 /* ClosedDayCell.xib in Resources */, 56D90D0120876F4900F24442 /* WizardJiraView.xib in Resources */, 280C1D431ED74EF900C126A1 /* BrowserSupport.scpt in Resources */, 28B116B821AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */, @@ -1996,11 +2099,11 @@ 2845B14B206AE3A8006EFB3B /* ReportCell.xib in Resources */, 28FE18A8207B36CB00DF796E /* CocoaHookupCell.xib in Resources */, 569C4C76202319FC0049FBF1 /* GitCell.xib in Resources */, - 2845B168206AE468006EFB3B /* TasksHeaderView.xib in Resources */, 280D71C81ED608D6005D2689 /* Images.xcassets in Resources */, 285465AD217858AF0052CB6A /* StoreView.xib in Resources */, 2845B16C206AE468006EFB3B /* NonTaskCell.xib in Resources */, 569C4C73202319EE0049FBF1 /* JitCell.xib in Resources */, + 2863492923B5EF87009F81D7 /* Reports.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2016,6 +2119,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2863490123B24335009F81D7 /* Calendar.storyboard in Resources */, 2898D3AF2184ED3000CF5AD4 /* Components.storyboard in Resources */, 569C4C7A20231A0B0049FBF1 /* BrowserCell.xib in Resources */, 288BB6822087152B00CF720A /* WizardAppleScriptView.xib in Resources */, @@ -2027,13 +2131,16 @@ 28FB265920224E9B00AEA38D /* HookupCell.xib in Resources */, 40FCE42E1DFC576C00D4FD45 /* Welcome.storyboard in Resources */, 5685C7681DE8721100CA545E /* Login.storyboard in Resources */, + 2863492A23B5EF87009F81D7 /* Reports.storyboard in Resources */, 569C4C71202319DE0049FBF1 /* ShellCell.xib in Resources */, 28B116B921AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */, 2892E80C208D9B42004E5298 /* InputsScrollView.xib in Resources */, + 2863490F23B29979009F81D7 /* Main.storyboard in Resources */, 287B358B20FBAFD60022F43E /* WizardCalendarView.xib in Resources */, 4065D3CF1DD3B44200B73201 /* Images.xcassets in Resources */, 56CD22BF1E72F89700F9CDB8 /* BuildScript.sh in Resources */, 28FB265520224E5B00AEA38D /* JiraTempoCell.xib in Resources */, + 2863492623B3A966009F81D7 /* ClosedDayCell.xib in Resources */, 2845B14C206AE3A8006EFB3B /* ReportCell.xib in Resources */, 406384891DE388C5004795A4 /* Tasks.storyboard in Resources */, 28FE18A9207B36CB00DF796E /* CocoaHookupCell.xib in Resources */, @@ -2041,14 +2148,13 @@ 5623915421CBC9750031B882 /* Projects.storyboard in Resources */, 28E896631E830D6700722032 /* Placeholder.storyboard in Resources */, 56D90D0220876F4900F24442 /* WizardJiraView.xib in Resources */, - 4065D3CE1DD3B44200B73201 /* Main.storyboard in Resources */, 2845B163206AE468006EFB3B /* TaskCell.xib in Resources */, 4065D3D31DD3B44200B73201 /* jirassic.sdef in Resources */, 40E092411DE385E4001EF5DA /* Settings.storyboard in Resources */, 28B116BC21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, 56D90CFF20876F3C00F24442 /* WizardGitView.xib in Resources */, 564E55FA2028857300CE4C76 /* Worklogs.storyboard in Resources */, - 2845B169206AE468006EFB3B /* TasksHeaderView.xib in Resources */, + 2863491F23B393E1009F81D7 /* CloseDayCell.xib in Resources */, 28A283A92038AFC500DDCB63 /* JirassicCell.xib in Resources */, 28EE1F6220EE8D6100C5C1D6 /* CalendarCell.xib in Resources */, ); @@ -2075,6 +2181,7 @@ 280D70DA1ECFE83F005D2689 /* CreateReport.swift in Sources */, 280D70D71ECFE817005D2689 /* DateExtension.swift in Sources */, 280D70F01ECFE8CC005D2689 /* Repository.swift in Sources */, + 286348FC23B237B1009F81D7 /* Project.swift in Sources */, 280D70F61ECFE9B3005D2689 /* RegisterUserInteractor.swift in Sources */, 280D70DE1ECFE870005D2689 /* UserInteractor.swift in Sources */, 280D70E11ECFE88F005D2689 /* CoreDataRepository+Settings.swift in Sources */, @@ -2118,10 +2225,11 @@ 280D712C1ED6043D005D2689 /* Day.swift in Sources */, 2892B29B1F094A760085BAC2 /* JiraRepository+Reports.swift in Sources */, 280D712D1ED6043D005D2689 /* Report.swift in Sources */, - 28EE1F6420EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */, + 28EE1F6420EE92DA00C5C1D6 /* CalendarAppPresenter.swift in Sources */, 280D712E1ED6043D005D2689 /* Settings.swift in Sources */, 2898D3AB2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, 280D712F1ED6043D005D2689 /* Task.swift in Sources */, + 2863491B23B393CF009F81D7 /* CloseDayCell.swift in Sources */, 287195312025C360001C237E /* CSettings.swift in Sources */, 56ADBF6A21C3F625008350A6 /* GitUser.swift in Sources */, 287195302025C35C001C237E /* CoreDataRepository+User.swift in Sources */, @@ -2130,6 +2238,7 @@ 566B9FA7217DE67800EAF324 /* TasksInteractor.swift in Sources */, 288BB67E2085252900CF720A /* WizardViewController.swift in Sources */, 28FB265E2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */, + 2863492F23B5EFD1009F81D7 /* ReportsPresenter.swift in Sources */, 280D71331ED6043D005D2689 /* DateExtension.swift in Sources */, 56ADBF6D21C3F94D008350A6 /* GitUserParser.swift in Sources */, 280D71351ED6043D005D2689 /* StringIdGenerator.swift in Sources */, @@ -2153,6 +2262,7 @@ 280D713F1ED6043D005D2689 /* ReadDaysInteractor.swift in Sources */, 280D71411ED6043D005D2689 /* ReadTasksInteractor.swift in Sources */, 280D71431ED6043D005D2689 /* TaskFinder.swift in Sources */, + 286348F523B136B3009F81D7 /* ProjectsInteractor.swift in Sources */, 280D71451ED6043D005D2689 /* TaskInteractor.swift in Sources */, 285465A2216C84E10052CB6A /* CreateMonthReport.swift in Sources */, 56D90CF820876F1100F24442 /* WizardJiraView.swift in Sources */, @@ -2177,17 +2287,23 @@ 5623914A21CBC6A10031B882 /* ProjectsViewController.swift in Sources */, 2845B14D206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */, 2845B164206AE468006EFB3B /* TaskCellPresenter.swift in Sources */, + 2863491123B299C8009F81D7 /* MainViewController.swift in Sources */, 280D715F1ED6043D005D2689 /* AppTheme.swift in Sources */, 28CBB596204554A2006F9D3A /* ParseGitBranch.swift in Sources */, + 286348F923B22775009F81D7 /* ProjectDetailsViewController.swift in Sources */, 280D71601ED6043D005D2689 /* MenuBarController.swift in Sources */, 280D71611ED6043D005D2689 /* MenuBarIconView.swift in Sources */, 569C4C67202319930049FBF1 /* GitPresenter.swift in Sources */, 2871951B2022ECF7001C237E /* OutputsScrollView.swift in Sources */, + 2863491423B299DB009F81D7 /* MainPresenter.swift in Sources */, + 2863490623B24C22009F81D7 /* CalendarPresenter.swift in Sources */, 280D71621ED6043D005D2689 /* FlipAnimation.swift in Sources */, 280D71631ED6043D005D2689 /* Animatable.swift in Sources */, + 2863490323B24800009F81D7 /* CalendarViewController.swift in Sources */, 564E55F3202883DB00CE4C76 /* WorklogsViewController.swift in Sources */, 280D71641ED6043D005D2689 /* PlaceholderViewController.swift in Sources */, 28FE1887207A520B00DF796E /* NewTaskCommand.swift in Sources */, + 2863491723B354A0009F81D7 /* CalendarDayCellView.swift in Sources */, 280D71661ED6043D005D2689 /* WelcomeViewController.swift in Sources */, 280D71681ED6043D005D2689 /* TaskSuggestionViewController.swift in Sources */, 280D71691ED6043D005D2689 /* TaskSuggestionPresenter.swift in Sources */, @@ -2209,11 +2325,13 @@ 2845B13F2068351F006EFB3B /* TableViewCell.swift in Sources */, 288BB6872087169F00CF720A /* ViewXib.swift in Sources */, 28A283972037975600DDCB63 /* Saveable.swift in Sources */, + 2863493523B790B1009F81D7 /* ProjectsListViewController.swift in Sources */, 280D71711ED6043D005D2689 /* ExtensionsInstallerInteractor.swift in Sources */, 280D71721ED6043D005D2689 /* AppleScript.swift in Sources */, 28A283A52038AFB100DDCB63 /* JirassicCell.swift in Sources */, 280D71731ED6043D005D2689 /* SandboxedAppleScript.swift in Sources */, 280D71741ED6043D005D2689 /* SettingsViewController.swift in Sources */, + 2863493823B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */, 280D71751ED6043D005D2689 /* SettingsPresenter.swift in Sources */, 280D71761ED6043D005D2689 /* SettingsInteractor.swift in Sources */, 280D71781ED6043D005D2689 /* NewTaskViewController.swift in Sources */, @@ -2225,7 +2343,6 @@ 280D717B1ED6043D005D2689 /* TasksPresenter.swift in Sources */, 2818848121A4A2F800B33B9C /* Jirassic.xcdatamodeld in Sources */, 2845B14F206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */, - 280D717C1ED6043D005D2689 /* CalendarScrollView.swift in Sources */, 280D717D1ED6043D005D2689 /* TasksScrollView.swift in Sources */, 280D717E1ED6043D005D2689 /* TasksDataSource.swift in Sources */, 28A2839F20385BE000DDCB63 /* GitCommitsParser.swift in Sources */, @@ -2241,15 +2358,17 @@ 569C4C64202319870049FBF1 /* GitCell.swift in Sources */, 569C4C6D202319CA0049FBF1 /* BrowserPresenter.swift in Sources */, 280D718D1ED6043D005D2689 /* InternalNotifications.swift in Sources */, + 286348FB23B23748009F81D7 /* Project.swift in Sources */, 280D718E1ED6043D005D2689 /* UserNotifications.swift in Sources */, 280D718F1ED6043D005D2689 /* SleepNotifications.swift in Sources */, + 2863490923B24C34009F81D7 /* CalendarInteractor.swift in Sources */, 2892B2981F094A170085BAC2 /* JiraRepository.swift in Sources */, 280D71901ED6043D005D2689 /* BrowserNotification.swift in Sources */, 280D719D1ED6043D005D2689 /* SqliteRepository.swift in Sources */, 284192D72018841700E64A9A /* JProject.swift in Sources */, 280D719E1ED6043D005D2689 /* SqliteRepository+Tasks.swift in Sources */, + 2863492223B3A95A009F81D7 /* ClosedDayCell.swift in Sources */, 2892B2A71F094C800085BAC2 /* JWorkAttribute.swift in Sources */, - 2845B166206AE468006EFB3B /* TasksHeaderView.swift in Sources */, 28DCA4552018B6E700DFAE29 /* JProjectIssue.swift in Sources */, 280D719F1ED6043D005D2689 /* SqliteRepository+User.swift in Sources */, 28EE1F5E20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, @@ -2265,11 +2384,13 @@ 280D71A61ED6043D005D2689 /* SQLiteSchema.swift in Sources */, 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */, 28279AD121BBF08C00376304 /* InMemoryCoreDataRepository.swift in Sources */, + 2863493223B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */, 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, 569C4C5E202319630049FBF1 /* JitCell.swift in Sources */, 28A2839A20382BBB00DDCB63 /* GitCommit.swift in Sources */, 284192DA2018855B00E64A9A /* JiraRepository+Projects.swift in Sources */, 565E19F020A5E336003A5E2A /* RCSync.swift in Sources */, + 2863492C23B5EF9C009F81D7 /* ReportsViewController.swift in Sources */, 280D71B61ED6043D005D2689 /* Repository.swift in Sources */, 564E55F6202884DE00CE4C76 /* WorklogsPresenter.swift in Sources */, 287195212022FD87001C237E /* InputsTableViewDataSource.swift in Sources */, @@ -2304,6 +2425,7 @@ 2898D3B22185959300CF5AD4 /* EditableTimeBox.swift in Sources */, 2828B04F2381D04E0005B188 /* NoAnimation.swift in Sources */, 4065D39F1DD3B44200B73201 /* Task.swift in Sources */, + 2863491223B299C8009F81D7 /* MainViewController.swift in Sources */, 4065D3CA1DD3B44200B73201 /* AppWireframe.swift in Sources */, 569C4C5F202319630049FBF1 /* JitCell.swift in Sources */, 4065D3C91DD3B44200B73201 /* AppDelegate.swift in Sources */, @@ -2312,6 +2434,7 @@ 287195372027CAD9001C237E /* TimeInteractor.swift in Sources */, 287195192022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, 28577EAD1E8ADC53002B07FD /* SSettings.swift in Sources */, + 2863493323B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */, 28577EB51E8AF08F002B07FD /* SQLiteDB.swift in Sources */, 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, 286BC00521909F85004D4CDD /* CloseDay.swift in Sources */, @@ -2331,10 +2454,12 @@ 2812F6212145031B008EE81E /* Store.swift in Sources */, 4065D3F91DD3B44200B73201 /* Repository.swift in Sources */, 4065D3B21DD3B44200B73201 /* TaskInteractor.swift in Sources */, + 2863490723B24C22009F81D7 /* CalendarPresenter.swift in Sources */, 4065D3A21DD3B44200B73201 /* Week.swift in Sources */, 28AFE7311E9A594500BAAD8C /* UserDefaults+token.swift in Sources */, 4065D3F01DD3B44200B73201 /* TasksViewController.swift in Sources */, 5685C76A1DE8721100CA545E /* LoginViewController.swift in Sources */, + 2863491C23B393CF009F81D7 /* CloseDayCell.swift in Sources */, 569C4C592023193B0049FBF1 /* ShellCell.swift in Sources */, 2845B14E206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */, 2823C9351E4F69970055D036 /* Versioning.swift in Sources */, @@ -2345,6 +2470,7 @@ 5623914B21CBC6A10031B882 /* ProjectsViewController.swift in Sources */, 2812F61F2145031B008EE81E /* IAPHelper.swift in Sources */, 4065D3A61DD3B44200B73201 /* ViewAutolayout.swift in Sources */, + 2863490A23B24C34009F81D7 /* CalendarInteractor.swift in Sources */, 28CBB597204554A2006F9D3A /* ParseGitBranch.swift in Sources */, 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */, 40FCE4271DF7646F00D4FD45 /* SandboxedAppleScript.swift in Sources */, @@ -2359,10 +2485,12 @@ 288BB6852087157F00CF720A /* WizardAppleScriptView.swift in Sources */, 2871951F2022FD14001C237E /* InputsScrollView.swift in Sources */, 4065D3E11DD3B44200B73201 /* SettingsViewController.swift in Sources */, + 2863492323B3A95A009F81D7 /* ClosedDayCell.swift in Sources */, 4065D3C81DD3B44200B73201 /* FlipAnimation.swift in Sources */, 4065D39E1DD3B44200B73201 /* Settings.swift in Sources */, 28A283B1203CB1A100DDCB63 /* MergeTasksInteractor.swift in Sources */, 2892B29C1F094A760085BAC2 /* JiraRepository+Reports.swift in Sources */, + 2863490423B24800009F81D7 /* CalendarViewController.swift in Sources */, 2845B1402068351F006EFB3B /* TableViewCell.swift in Sources */, 28C9C6221EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */, 284192DE2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, @@ -2371,9 +2499,11 @@ 4065D3B41DD3B44200B73201 /* TaskTypeEstimator.swift in Sources */, 5623914E21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */, 285465A8217858790052CB6A /* StoreView.swift in Sources */, + 286348FA23B22775009F81D7 /* ProjectDetailsViewController.swift in Sources */, 4065D3D91DD3B44200B73201 /* UserNotifications.swift in Sources */, 569C4C6E202319CA0049FBF1 /* BrowserPresenter.swift in Sources */, 4065D3A51DD3B44200B73201 /* StringIdGenerator.swift in Sources */, + 2863493923B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */, 2845B150206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */, 28577EAB1E8ADC53002B07FD /* SqliteRepository.swift in Sources */, 28A283982037975600DDCB63 /* Saveable.swift in Sources */, @@ -2387,12 +2517,14 @@ 566B9FA8217DE67800EAF324 /* TasksInteractor.swift in Sources */, 4065D3B01DD3B44200B73201 /* TaskFinder.swift in Sources */, 28A283952037874600DDCB63 /* ModuleGitLogs.swift in Sources */, + 2863491823B354A0009F81D7 /* CalendarDayCellView.swift in Sources */, 284192D82018841700E64A9A /* JProject.swift in Sources */, 56D90CF920876F1100F24442 /* WizardJiraView.swift in Sources */, 28279AD221BBF08E00376304 /* InMemoryCoreDataRepository.swift in Sources */, 28EDE9391E59EC1500B360A4 /* TasksView.swift in Sources */, 28A283AC203A93AB00DDCB63 /* GitBranchParser.swift in Sources */, 40FCE4301DFD7C8D00D4FD45 /* Animatable.swift in Sources */, + 2863491523B299DB009F81D7 /* MainPresenter.swift in Sources */, 4065D3F21DD3B44200B73201 /* CloudKitRepository.swift in Sources */, 4065D3AE1DD3B44200B73201 /* ReadDaysInteractor.swift in Sources */, 285465A3216C84E10052CB6A /* CreateMonthReport.swift in Sources */, @@ -2400,6 +2532,7 @@ 28A283A020385BE000DDCB63 /* GitCommitsParser.swift in Sources */, 4065D3DA1DD3B44200B73201 /* SleepNotifications.swift in Sources */, 280F507D1EC868B0007416AB /* StringArray.swift in Sources */, + 2863492D23B5EF9C009F81D7 /* ReportsViewController.swift in Sources */, 569C4C68202319930049FBF1 /* GitPresenter.swift in Sources */, 28FB265C20224EA700AEA38D /* HookupCell.swift in Sources */, 2845B13A206703C5006EFB3B /* Keychain.swift in Sources */, @@ -2412,7 +2545,7 @@ 4051D5F91E0EA48A002042BB /* AppLauncher.swift in Sources */, 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, 56ADBF6B21C3F625008350A6 /* GitUser.swift in Sources */, - 2845B167206AE468006EFB3B /* TasksHeaderView.swift in Sources */, + 286348F623B136B3009F81D7 /* ProjectsInteractor.swift in Sources */, 28AA500A1EDCD51300AAF03D /* CoreDataRepository+Settings.swift in Sources */, 280D70B11ECC09D9005D2689 /* AppTheme.swift in Sources */, 5685C7671DE8721100CA545E /* CloudKitLoginViewController.swift in Sources */, @@ -2423,21 +2556,22 @@ 28577EB11E8ADC53002B07FD /* SUser.swift in Sources */, 4065D3DE1DD3B44200B73201 /* NewTaskViewController.swift in Sources */, 4065D39C1DD3B44200B73201 /* Day.swift in Sources */, + 2863493023B5EFD1009F81D7 /* ReportsPresenter.swift in Sources */, 56D90CFC20876F2B00F24442 /* WizardGitView.swift in Sources */, 40FCE42C1DFC3F8700D4FD45 /* WelcomeViewController.swift in Sources */, 5623915121CBC6CD0031B882 /* Project.swift in Sources */, - 28EE1F6520EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */, + 28EE1F6520EE92DA00C5C1D6 /* CalendarAppPresenter.swift in Sources */, 2845B16B206AE468006EFB3B /* NonTaskCell.swift in Sources */, 4065D39D1DD3B44200B73201 /* Report.swift in Sources */, 40FCE4251DF7646F00D4FD45 /* AppleScript.swift in Sources */, 28A928781E8F78580022AB55 /* SQLiteSchema.swift in Sources */, + 2863493623B790B1009F81D7 /* ProjectsListViewController.swift in Sources */, 28FE18AC207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */, 4065D3AF1DD3B44200B73201 /* ReadTasksInteractor.swift in Sources */, 565E19F120A5E336003A5E2A /* RCSync.swift in Sources */, 28AA50091EDCD51300AAF03D /* CoreDataRepository+Tasks.swift in Sources */, 287195162022E1E2001C237E /* HookupPresenter.swift in Sources */, 2892E812208E6275004E5298 /* LocalPreferences.swift in Sources */, - 4065D3E21DD3B44200B73201 /* CalendarScrollView.swift in Sources */, 4065D3AC1DD3B44200B73201 /* CreateReport.swift in Sources */, 2845B161206AE468006EFB3B /* TaskCell.swift in Sources */, 28FB265F2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */, @@ -2512,6 +2646,7 @@ 280D71021ED35A25005D2689 /* UserDefaults+token.swift in Sources */, 4065D4281DD4574D00B73201 /* User.swift in Sources */, 4065D4311DD457B000B73201 /* DateExtension.swift in Sources */, + 286348FD23B237B2009F81D7 /* Project.swift in Sources */, 28577EB01E8ADC53002B07FD /* STask.swift in Sources */, 4065D4291DD4575B00B73201 /* Week.swift in Sources */, 280D71031ED35A3E005D2689 /* StringArray.swift in Sources */, @@ -2539,6 +2674,14 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 2863490C23B29979009F81D7 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 2863490D23B29979009F81D7 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; 28E896731E83770D00722032 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( @@ -2563,15 +2706,6 @@ name = Main.storyboard; sourceTree = ""; }; - 4065D3591DD3B44200B73201 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4065D35A1DD3B44200B73201 /* Base */, - ); - name = Main.storyboard; - path = ..; - sourceTree = ""; - }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ From 5214dd059a48029cb292bdf2a827782b4ab5a952 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 30 Dec 2019 09:35:31 +0200 Subject: [PATCH 05/48] Remove NonTaskCell --- App/Tasks/TaskTypeEstimator.swift | 4 +- .../Reports/cells/ReportCell/ReportCell.swift | 2 +- .../macOS/Screens/Tasks/TasksDataSource.swift | 48 ++-- .../Screens/Tasks/cells/CellProtocol.swift | 2 +- .../Tasks/cells/NonTaskCell/NonTaskCell.swift | 247 ----------------- .../Tasks/cells/NonTaskCell/NonTaskCell.xib | 134 ---------- .../Tasks/cells/TaskCell/TaskCell.swift | 217 +++++++-------- .../Screens/Tasks/cells/TaskCell/TaskCell.xib | 250 +++++------------ .../Tasks/cells/TaskCell/TaskCellTests.swift | 39 --- .../Tasks/cells/TaskCell/TaskCell_.swift | 253 ++++++++++++++++++ Jirassic.xcodeproj/project.pbxproj | 54 ++-- 11 files changed, 458 insertions(+), 792 deletions(-) delete mode 100644 Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.swift delete mode 100644 Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.xib delete mode 100644 Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellTests.swift create mode 100644 Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell_.swift diff --git a/App/Tasks/TaskTypeEstimator.swift b/App/Tasks/TaskTypeEstimator.swift index dc59426..97b0ebd 100644 --- a/App/Tasks/TaskTypeEstimator.swift +++ b/App/Tasks/TaskTypeEstimator.swift @@ -16,7 +16,7 @@ class TaskTypeEstimator { func taskTypeAroundDate (_ date: Date, withSettings settings: Settings) -> TaskType { // Check if the date is around scrum time - var settingsScrumTime = gregorian.dateComponents(ymdhmsUnitFlags, from: settings.settingsTracking.scrumTime) + let settingsScrumTime = gregorian.dateComponents(ymdhmsUnitFlags, from: settings.settingsTracking.scrumTime) var comps = gregorian.dateComponents(ymdhmsUnitFlags, from: date) comps.hour = settingsScrumTime.hour @@ -30,7 +30,7 @@ class TaskTypeEstimator { // Check if the date is around lunch break - var settingsLunchTime = gregorian.dateComponents(ymdhmsUnitFlags, from: settings.settingsTracking.lunchTime) + let settingsLunchTime = gregorian.dateComponents(ymdhmsUnitFlags, from: settings.settingsTracking.lunchTime) comps.hour = settingsLunchTime.hour comps.minute = settingsLunchTime.minute diff --git a/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift index 94a5b68..35da83f 100644 --- a/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift +++ b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift @@ -19,7 +19,7 @@ class ReportCell: NSTableRowView, CellProtocol { fileprivate var trackingArea: NSTrackingArea? fileprivate var bgColor: NSColor = NSColor.clear - var didEndEditingCell: ((_ cell: CellProtocol) -> ())? + var didClickEditCell: ((_ cell: CellProtocol) -> ())? var didClickRemoveCell: ((_ cell: CellProtocol) -> ())? var didClickAddCell: ((_ cell: CellProtocol) -> ())? var didCopyContentCell: ((_ cell: CellProtocol) -> ())? diff --git a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift index 6563bb5..6452ed9 100644 --- a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift +++ b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift @@ -8,7 +8,6 @@ import Cocoa -let kNonTaskCellHeight = CGFloat(40.0) let kTaskCellHeight = CGFloat(40.0) let kCloseDayCellHeight = CGFloat(90.0) let kGapBetweenCells = CGFloat(16.0) @@ -25,7 +24,6 @@ class TasksDataSource: NSObject, TasksAndReportsDataSource { internal var tableView: NSTableView! { didSet { TaskCell.register(in: tableView) - NonTaskCell.register(in: tableView) CloseDayCell.register(in: tableView) ClosedDayCell.register(in: tableView) } @@ -39,16 +37,6 @@ class TasksDataSource: NSObject, TasksAndReportsDataSource { self.tasks = tasks } - private func cellForTaskType (_ taskType: TaskType) -> CellProtocol { - - switch taskType { - case .issue, .gitCommit: - return TaskCell.instantiate(in: tableView) - default: - return NonTaskCell.instantiate(in: tableView) - } - } - func addTask (_ task: Task, at row: Int) { tasks.insert(task, at: row) } @@ -69,13 +57,7 @@ extension TasksDataSource: NSTableViewDataSource { guard row < tasks.count else { return kCloseDayCellHeight } - let theData = tasks[row] - switch theData.taskType { - case TaskType.issue, TaskType.gitCommit: - return kTaskCellHeight - default: - return kNonTaskCellHeight - } + return kTaskCellHeight } } @@ -107,22 +89,22 @@ extension TasksDataSource: NSTableViewDelegate { var theData = tasks[row] let thePreviousData: Task? = row == 0 ? nil : tasks[row-1] - var cell: CellProtocol = self.cellForTaskType(theData.taskType) + var cell: CellProtocol = TaskCell.instantiate(in: tableView) TaskCellPresenter(cell: cell).present(previousTask: thePreviousData, currentTask: theData) - cell.didEndEditingCell = { [weak self] (cell: CellProtocol) in - let updatedData = cell.data - theData.taskNumber = updatedData.taskNumber - theData.notes = updatedData.notes - theData.startDate = updatedData.dateStart - theData.endDate = updatedData.dateEnd - // Save to local variable - self?.tasks[row] = theData - // Save to db and server - let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) - saveInteractor.saveTask(theData, allowSyncing: true, completion: { savedTask in - tableView.reloadData(forRowIndexes: [row], columnIndexes: [0]) - }) + cell.didClickEditCell = { [weak self] (cell: CellProtocol) in +// let updatedData = cell.data +// theData.taskNumber = updatedData.taskNumber +// theData.notes = updatedData.notes +// theData.startDate = updatedData.dateStart +// theData.endDate = updatedData.dateEnd +// // Save to local variable +// self?.tasks[row] = theData +// // Save to db and server +// let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) +// saveInteractor.saveTask(theData, allowSyncing: true, completion: { savedTask in +// tableView.reloadData(forRowIndexes: [row], columnIndexes: [0]) +// }) } cell.didClickRemoveCell = { [weak self] (cell: CellProtocol) in // Ugly hack to find the row number from which the action came diff --git a/Delivery/macOS/Screens/Tasks/cells/CellProtocol.swift b/Delivery/macOS/Screens/Tasks/cells/CellProtocol.swift index dcc7f29..05168e7 100644 --- a/Delivery/macOS/Screens/Tasks/cells/CellProtocol.swift +++ b/Delivery/macOS/Screens/Tasks/cells/CellProtocol.swift @@ -20,7 +20,7 @@ protocol CellProtocol { var color: NSColor {get set} var timeToolTip: String? {get set} - var didEndEditingCell: ((_ cell: CellProtocol) -> ())? {get set} + var didClickEditCell: ((_ cell: CellProtocol) -> ())? {get set} var didClickRemoveCell: ((_ cell: CellProtocol) -> ())? {get set} var didClickAddCell: ((_ cell: CellProtocol) -> ())? {get set} var didCopyContentCell: ((_ cell: CellProtocol) -> ())? {get set} diff --git a/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.swift b/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.swift deleted file mode 100644 index 2df510a..0000000 --- a/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.swift +++ /dev/null @@ -1,247 +0,0 @@ -// -// NonTaskCell.swift -// Jirassic -// -// Created by Baluta Cristian on 07/05/15. -// Copyright (c) 2015 Cristian Baluta. All rights reserved. -// - -import Cocoa - -class NonTaskCell: NSTableRowView, CellProtocol { - - @IBOutlet var statusImage: NSImageView? - @IBOutlet private var statusImageWidthContraint: NSLayoutConstraint! - @IBOutlet private var dateStartTextField: TimeBox! - @IBOutlet private var dateStartTextFieldLeadingContraint: NSLayoutConstraint! - @IBOutlet private var bullet: NSTextField! - @IBOutlet private var dateEndTextField: TimeBox! - @IBOutlet private var notesTextField: NSTextField! - @IBOutlet private var notesTextFieldTrailingContraint: NSLayoutConstraint! - @IBOutlet private var butRemove: NSButton! - @IBOutlet private var butAdd: NSButton! - - private var isEditing = false - private var wasEdited = false - private var trackingArea: NSTrackingArea? - private var activeTimeboxPopover: NSPopover? - - var didEndEditingCell: ((_ cell: CellProtocol) -> ())? - var didClickRemoveCell: ((_ cell: CellProtocol) -> ())? - var didClickAddCell: ((_ cell: CellProtocol) -> ())? - var didCopyContentCell: ((_ cell: CellProtocol) -> ())? - - private var _data: TaskCreationData? - var data: TaskCreationData { - get { - if let dateStart = _data?.dateStart { - let newHM = Date.parseHHmm(self.dateStartTextField.stringValue) - let date = dateStart.dateByUpdating(hour: newHM.hour, minute: newHM.min) - _data?.dateStart = date - } - let newHM = Date.parseHHmm(self.dateEndTextField.stringValue) - let dateEnd = _data!.dateEnd.dateByUpdating(hour: newHM.hour, minute: newHM.min) - _data?.dateEnd = dateEnd - - if self.notesTextField.stringValue != "" { - _data?.notes = self.notesTextField.stringValue - } - - return _data! - } - set { - _data = newValue - if let dateStart = newValue.dateStart { - self.dateStartTextField.stringValue = dateStart.HHmm() - self.dateStartTextField.isHidden = false - self.bullet.isHidden = false - self.dateStartTextFieldLeadingContraint.constant = 20 - } else { - self.dateStartTextField.isHidden = true - self.bullet.isHidden = true - self.dateStartTextFieldLeadingContraint.constant = 20 - 36 - 8 - } - self.dateEndTextField.stringValue = newValue.dateEnd.HHmm() - self.notesTextField.stringValue = newValue.notes ?? "" - } - } - var duration: String { - get { - return "" - } - set { - fatalError("Not available") - } - } - var isDark: Bool = false { - didSet { - dateStartTextField.isDark = isDark - dateEndTextField.isDark = isDark - } - } - var isEditable: Bool = true { - didSet { - notesTextField.isEditable = isEditable - } - } - var isRemovable: Bool = true - var isIgnored: Bool = false { - didSet { - self.notesTextField!.alphaValue = isIgnored ? 0.5 : 1.0 - } - } - var color: NSColor = NSColor.black { - didSet { - self.notesTextField!.textColor = color - } - } - var timeToolTip: String? { - didSet { - dateStartTextField.toolTip = timeToolTip - dateEndTextField.toolTip = timeToolTip - } - } - - override func awakeFromNib() { - super.awakeFromNib() - - butRemove.isHidden = true - butAdd.isHidden = true - butRemove.wantsLayer = true - dateStartTextField.onClick = { - self.createTimeboxPopover(timebox: self.dateStartTextField) - } - dateEndTextField.onClick = { - self.createTimeboxPopover(timebox: self.dateEndTextField) - } - if AppDelegate.sharedApp().theme.isDark { - notesTextField!.textColor = NSColor.white - } - } - - override func drawBackground (in dirtyRect: NSRect) { - - NSColor(calibratedWhite: 1.0, alpha: 0.0).setFill() - let selectionPath = NSBezierPath(roundedRect: dirtyRect, xRadius: 0, yRadius: 0) - selectionPath.fill() - } - - private func createTimeboxPopover (timebox: TimeBox) { - guard activeTimeboxPopover == nil, isEditable else { - return - } - let popover = NSPopover() - let view = TimeBoxViewController.instantiateFromStoryboard("Components") - view.didSave = { - let hasChanges = timebox.stringValue != view.stringValue - timebox.stringValue = view.stringValue - popover.performClose(nil) - if hasChanges { - self.didEndEditingCell?(self) - } - } - view.didCancel = { - popover.performClose(nil) - self.activeTimeboxPopover = nil - } - popover.contentViewController = view - popover.show(relativeTo: timebox.frame, of: self, preferredEdge: NSRectEdge.maxY) - view.stringValue = timebox.stringValue - - activeTimeboxPopover = popover - } -} - -extension NonTaskCell { - - @IBAction func handleRemoveButton (_ sender: NSButton) { - didClickRemoveCell?(self) - } - - @IBAction func handleAddButton (_ sender: NSButton) { - didClickAddCell?(self) - } -} - -extension NonTaskCell { - - override func mouseEntered (with theEvent: NSEvent) { - super.mouseEntered(with: theEvent) - - self.butRemove.isHidden = false - self.butAdd.isHidden = false - self.notesTextFieldTrailingContraint.constant = 80 - self.setNeedsDisplay(self.frame) - } - -// override func mouseMoved(with event: NSEvent) { -// -// let locationInWindow = event.locationInWindow -// let locationInView = self.convert(locationInWindow, from: nil) -// RCLog(locationInView) -// if dateStartTextField.frame.contains(locationInView) { -// dateStartTextField.font = NSFont.systemFont(ofSize: 14) -// } -// } - - override func mouseExited (with theEvent: NSEvent) { - super.mouseExited(with: theEvent) - - self.butRemove.isHidden = true - self.butAdd.isHidden = true - self.notesTextFieldTrailingContraint.constant = 10 - self.setNeedsDisplay(self.frame) - } - - func ensureTrackingArea() { - - if trackingArea == nil { - trackingArea = NSTrackingArea(rect: NSZeroRect, - options: [ - NSTrackingArea.Options.inVisibleRect, - NSTrackingArea.Options.activeAlways, - NSTrackingArea.Options.mouseEnteredAndExited - ], - owner: self, - userInfo: nil) - } - } - - override func updateTrackingAreas() { - super.updateTrackingAreas() - - self.ensureTrackingArea() - if !self.trackingAreas.contains(self.trackingArea!) { - self.addTrackingArea(self.trackingArea!) - } - } -} - -extension NonTaskCell: NSTextFieldDelegate { - - public func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool { - isEditing = true - return true - } - - public func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool { - if wasEdited { - wasEdited = false - didEndEditingCell?(self) - } - return true - } - - public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { - // Detect Enter key - if wasEdited && commandSelector == #selector(NSResponder.insertNewline(_:)) { - didEndEditingCell?(self) - wasEdited = false - } - return false - } - - func controlTextDidChange (_ obj: Notification) { - wasEdited = true - } -} diff --git a/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.xib b/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.xib deleted file mode 100644 index 3cc04e7..0000000 --- a/Delivery/macOS/Screens/Tasks/cells/NonTaskCell/NonTaskCell.xib +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift index 0f94a9a..d0dc99b 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift @@ -1,84 +1,96 @@ // -// TaskCell.swift +// NonTaskCell.swift // Jirassic // -// Created by Baluta Cristian on 28/03/15. +// Created by Baluta Cristian on 07/05/15. // Copyright (c) 2015 Cristian Baluta. All rights reserved. // import Cocoa class TaskCell: NSTableRowView, CellProtocol { - - @IBOutlet var statusImage: NSImageView? - @IBOutlet private var dateEndTextField: TimeBox! - @IBOutlet private var issueNrTextField: NSTextField! + + @IBOutlet var statusImage: NSImageView? + @IBOutlet private var statusImageWidthContraint: NSLayoutConstraint! + @IBOutlet private var dateStartTextField: TimeBox! + @IBOutlet private var dateStartTextFieldLeadingContraint: NSLayoutConstraint! + @IBOutlet private var bullet: NSTextField! + @IBOutlet private var dateEndTextField: TimeBox! @IBOutlet private var notesTextField: NSTextField! - @IBOutlet private var notesTextFieldRightConstrain: NSLayoutConstraint! - + @IBOutlet private var notesTextFieldTrailingContraint: NSLayoutConstraint! + @IBOutlet private var butRemove: NSButton! @IBOutlet private var butAdd: NSButton! - @IBOutlet private var butRemove: NSButton! - @IBOutlet private var butRemoveWidthConstraint: NSLayoutConstraint! + @IBOutlet private var butAEdit: NSButton! - private var isEditing = false - private var wasEdited = false - private var mouseInside = false - private var trackingArea: NSTrackingArea? + private var isEditing = false + private var wasEdited = false + private var trackingArea: NSTrackingArea? private var activeTimeboxPopover: NSPopover? - var didEndEditingCell: ((_ cell: CellProtocol) -> ())? + var didClickEditCell: ((_ cell: CellProtocol) -> ())? var didClickRemoveCell: ((_ cell: CellProtocol) -> ())? var didClickAddCell: ((_ cell: CellProtocol) -> ())? var didCopyContentCell: ((_ cell: CellProtocol) -> ())? - - // Sets data to the cell - var _dateEnd = Date() - var _dateEndHHmm = "" + + private var _data: TaskCreationData? var data: TaskCreationData { get { - let hm = Date.parseHHmm(self.dateEndTextField!.stringValue) - let date = _dateEnd.dateByUpdating(hour: hm.hour, minute: hm.min) - return ( - dateStart: nil, - dateEnd: date, - taskNumber: self.issueNrTextField.stringValue, - notes: self.notesTextField.stringValue, - taskType: .issue - ) + if let dateStart = _data?.dateStart { + let newHM = Date.parseHHmm(self.dateStartTextField.stringValue) + let date = dateStart.dateByUpdating(hour: newHM.hour, minute: newHM.min) + _data?.dateStart = date + } + let newHM = Date.parseHHmm(self.dateEndTextField.stringValue) + let dateEnd = _data!.dateEnd.dateByUpdating(hour: newHM.hour, minute: newHM.min) + _data?.dateEnd = dateEnd + + if self.notesTextField.stringValue != "" { + _data?.notes = self.notesTextField.stringValue + } + + return _data! } - set { - _dateEnd = newValue.dateEnd - _dateEndHHmm = _dateEnd.HHmm() - dateEndTextField.stringValue = _dateEndHHmm - issueNrTextField.stringValue = newValue.taskNumber ?? "" - notesTextField.stringValue = newValue.notes ?? "" + set { + _data = newValue + if let dateStart = newValue.dateStart { + self.dateStartTextField.stringValue = dateStart.HHmm() + self.dateStartTextField.isHidden = false + self.bullet.isHidden = false + self.dateStartTextFieldLeadingContraint.constant = 20 + } else { + self.dateStartTextField.isHidden = true + self.bullet.isHidden = true + self.dateStartTextFieldLeadingContraint.constant = 20 - 36 - 8 + } + self.dateEndTextField.stringValue = newValue.dateEnd.HHmm() + self.notesTextField.stringValue = (newValue.taskNumber ?? "") + " - " + (newValue.notes ?? "") } } - var duration: String { - get { - return "" - } + var duration: String { + get { + return "" + } set { fatalError("Not available") } } var isDark: Bool = false { didSet { + dateStartTextField.isDark = isDark dateEndTextField.isDark = isDark } } var isEditable: Bool = true { didSet { - issueNrTextField.isEditable = isEditable notesTextField.isEditable = isEditable } } - var isRemovable: Bool = true { + var isRemovable: Bool = true + var isIgnored: Bool = false { didSet { - butRemove.isEnabled = isRemovable + self.notesTextField!.alphaValue = isIgnored ? 0.5 : 1.0 } } - var isIgnored: Bool = false var color: NSColor = NSColor.black { didSet { self.notesTextField!.textColor = color @@ -86,34 +98,47 @@ class TaskCell: NSTableRowView, CellProtocol { } var timeToolTip: String? { didSet { - self.toolTip = nil + dateStartTextField.toolTip = timeToolTip dateEndTextField.toolTip = timeToolTip } } - + override func awakeFromNib() { super.awakeFromNib() - showMouseOverControls(false) - notesTextFieldRightConstrain.constant = 0 - dateEndTextField.onClick = { [weak self] in - if let wself = self { - wself.createTimeboxPopover(timebox: wself.dateEndTextField) - } + + butRemove.isHidden = true + butAdd.isHidden = true + butRemove.wantsLayer = true + dateStartTextField.onClick = { + self.createTimeboxPopover(timebox: self.dateStartTextField) + } + dateEndTextField.onClick = { + self.createTimeboxPopover(timebox: self.dateEndTextField) + } + if AppDelegate.sharedApp().theme.isDark { + notesTextField!.textColor = NSColor.white } } + + override func drawBackground (in dirtyRect: NSRect) { + + NSColor(calibratedWhite: 1.0, alpha: 0.0).setFill() + let selectionPath = NSBezierPath(roundedRect: dirtyRect, xRadius: 0, yRadius: 0) + selectionPath.fill() + } private func createTimeboxPopover (timebox: TimeBox) { - guard activeTimeboxPopover == nil else { + guard activeTimeboxPopover == nil, isEditable else { return } let popover = NSPopover() - let view = TimeBoxViewController.instantiateFromStoryboard("Components") + let view = TimeBoxViewController.instantiateFromStoryboard("Tasks") view.didSave = { let hasChanges = timebox.stringValue != view.stringValue timebox.stringValue = view.stringValue popover.performClose(nil) if hasChanges { - self.didEndEditingCell?(self) + self.didClickEditCell?(self) } } view.didCancel = { @@ -137,79 +162,50 @@ extension TaskCell { @IBAction func handleAddButton (_ sender: NSButton) { didClickAddCell?(self) } + + @IBAction func handleEditButton (_ sender: NSButton) { + didClickEditCell?(self) + } } +/// Mouse tracking extension TaskCell { -/* - override func drawBackground (in dirtyRect: NSRect) { - - let width = dirtyRect.size.width - kCellLeftPadding * 2 - let height = dirtyRect.size.height - kGapBetweenCells - 2 - - if isEditing { - let selectionRect = NSRect(x: kCellLeftPadding, y: 2, width: width, height: height) - NSColor.red.setStroke() - let selectionPath = NSBezierPath(roundedRect: selectionRect, xRadius: 6, yRadius: 6) - selectionPath.stroke() - } - else if self.mouseInside { - notesTextFieldRightConstrain!.constant = isRemovable ? 36 : 0 - butRemoveWidthConstraint.constant = isRemovable ? 16 : 0 - let selectionRect = NSRect(x: kCellLeftPadding, y: 2, width: width, height: height) - //NSColor(calibratedWhite: 1.0, alpha: 0.0).setFill() - AppDelegate.sharedApp().theme.highlightLineColor.setStroke() -// NSColor(calibratedRed: 0.3, green: 0.1, blue: 0.1, alpha: 1.0).setStroke() - let selectionPath = NSBezierPath(roundedRect: selectionRect, xRadius: 6, yRadius: 6) -// selectionPath.fill() - selectionPath.stroke() - } - else { - notesTextFieldRightConstrain!.constant = 0 - let selectionRect = NSRect(x: kCellLeftPadding, y: 2, width: width, height: height) -// NSColor(calibratedWhite: 1.0, alpha: 1.0).setFill() - AppDelegate.sharedApp().theme.lineColor.setStroke() - let selectionPath = NSBezierPath(roundedRect: selectionRect, xRadius: 6, yRadius: 6) -// selectionPath.fill() - selectionPath.stroke() - } - } - */ + override func mouseEntered (with theEvent: NSEvent) { super.mouseEntered(with: theEvent) - self.mouseInside = true - self.showMouseOverControls(self.mouseInside) + + self.butRemove.isHidden = false + self.butAdd.isHidden = false + self.notesTextFieldTrailingContraint.constant = 80 self.setNeedsDisplay(self.frame) } - override func mouseExited (with theEvent: NSEvent) { + override func mouseExited (with theEvent: NSEvent) { super.mouseExited(with: theEvent) - self.mouseInside = false - self.showMouseOverControls(self.mouseInside) + + self.butRemove.isHidden = true + self.butAdd.isHidden = true + self.notesTextFieldTrailingContraint.constant = 10 self.setNeedsDisplay(self.frame) } - func showMouseOverControls (_ show: Bool) { - butRemove.isHidden = !show - butAdd.isHidden = !show - //line1.isHidden = !show - //line2.isHidden = !show - } - func ensureTrackingArea() { + if trackingArea == nil { - trackingArea = NSTrackingArea( - rect: NSZeroRect, - options: [NSTrackingArea.Options.inVisibleRect, - NSTrackingArea.Options.activeAlways, - NSTrackingArea.Options.mouseEnteredAndExited], + trackingArea = NSTrackingArea(rect: NSZeroRect, + options: [ + NSTrackingArea.Options.inVisibleRect, + NSTrackingArea.Options.activeAlways, + NSTrackingArea.Options.mouseEnteredAndExited + ], owner: self, - userInfo: nil - ) + userInfo: nil) } } override func updateTrackingAreas() { super.updateTrackingAreas() + self.ensureTrackingArea() if !self.trackingAreas.contains(self.trackingArea!) { self.addTrackingArea(self.trackingArea!) @@ -221,16 +217,13 @@ extension TaskCell: NSTextFieldDelegate { public func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool { isEditing = true - self.setNeedsDisplay(self.frame) return true } public func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool { if wasEdited { wasEdited = false - isEditing = false - didEndEditingCell?(self) - self.setNeedsDisplay(self.frame) + didClickEditCell?(self) } return true } @@ -238,10 +231,8 @@ extension TaskCell: NSTextFieldDelegate { public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { // Detect Enter key if wasEdited && commandSelector == #selector(NSResponder.insertNewline(_:)) { + didClickEditCell?(self) wasEdited = false - isEditing = false - didEndEditingCell?(self) - self.setNeedsDisplay(self.frame) } return false } diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib index 653c6e1..3a0cbe7 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib @@ -9,243 +9,121 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + + + - + - - - - - - + + + - + - + - + + + + + + + + + + + + - - + + - - - + - - + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + - + diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellTests.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellTests.swift deleted file mode 100644 index c73484b..0000000 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellTests.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// TaskCellTests.swift -// Jirassic -// -// Created by Baluta Cristian on 17/09/15. -// Copyright © 2015 Cristian Baluta. All rights reserved. -// - -import XCTest -@testable import Jirassic_no_cloud - -class TaskCellTests: XCTestCase { - -// func testSettingData() { -// -// let _cell = cell() -// RCLog(_cell) -// _cell.data = (dateStart: "dateStart", dateEnd: "dateEnd", issue: "IOS-01", notes: "notes") -// -// XCTAssert(_cell.data.dateStart == "dateStart", "") -// XCTAssert(_cell.data.dateEnd == "dateEnd", "") -// XCTAssert(_cell.data.issue == "IOS-01", "") -// XCTAssert(_cell.data.notes == "notes", "") -// } -// -// func cell() -> TaskCell { -// var views: NSArray? -// NSBundle.mainBundle().loadNibNamed("TaskCell", owner: self, topLevelObjects: &views) -// RCLog(views) -// RCLog(views?.objectAtIndex(0)) -// RCLog(views?.objectAtIndex(1)) -// if let c = views?.objectAtIndex(0) as? TaskCell { -// return c -// } else if let c = views?.objectAtIndex(1) as? TaskCell { -// return c -// } -// return TaskCell() -// } -} diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell_.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell_.swift new file mode 100644 index 0000000..535fd3e --- /dev/null +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell_.swift @@ -0,0 +1,253 @@ +// +// TaskCell.swift +// Jirassic +// +// Created by Baluta Cristian on 28/03/15. +// Copyright (c) 2015 Cristian Baluta. All rights reserved. +// + +import Cocoa +/* +class TaskCell2: NSTableRowView { + + @IBOutlet var statusImage: NSImageView? + @IBOutlet private var dateEndTextField: TimeBox! + @IBOutlet private var issueNrTextField: NSTextField! + @IBOutlet private var notesTextField: NSTextField! + @IBOutlet private var notesTextFieldRightConstrain: NSLayoutConstraint! + + @IBOutlet private var butAdd: NSButton! + @IBOutlet private var butRemove: NSButton! + @IBOutlet private var butRemoveWidthConstraint: NSLayoutConstraint! + + private var isEditing = false + private var wasEdited = false + private var mouseInside = false + private var trackingArea: NSTrackingArea? + private var activeTimeboxPopover: NSPopover? + + var didEndEditingCell: ((_ cell: CellProtocol) -> ())? + var didClickRemoveCell: ((_ cell: CellProtocol) -> ())? + var didClickAddCell: ((_ cell: CellProtocol) -> ())? + var didCopyContentCell: ((_ cell: CellProtocol) -> ())? + + // Sets data to the cell + var _dateEnd = Date() + var _dateEndHHmm = "" + var data: TaskCreationData { + get { + let hm = Date.parseHHmm(self.dateEndTextField!.stringValue) + let date = _dateEnd.dateByUpdating(hour: hm.hour, minute: hm.min) + return ( + dateStart: nil, + dateEnd: date, + taskNumber: self.issueNrTextField.stringValue, + notes: self.notesTextField.stringValue, + taskType: .issue + ) + } + set { + _dateEnd = newValue.dateEnd + _dateEndHHmm = _dateEnd.HHmm() + dateEndTextField.stringValue = _dateEndHHmm + issueNrTextField.stringValue = newValue.taskNumber ?? "" + notesTextField.stringValue = newValue.notes ?? "" + } + } + var duration: String { + get { + return "" + } + set { + fatalError("Not available") + } + } + var isDark: Bool = false { + didSet { + dateEndTextField.isDark = isDark + } + } + var isEditable: Bool = true { + didSet { + issueNrTextField.isEditable = isEditable + notesTextField.isEditable = isEditable + } + } + var isRemovable: Bool = true { + didSet { + butRemove.isEnabled = isRemovable + } + } + var isIgnored: Bool = false + var color: NSColor = NSColor.black { + didSet { + self.notesTextField!.textColor = color + } + } + var timeToolTip: String? { + didSet { + self.toolTip = nil + dateEndTextField.toolTip = timeToolTip + } + } + + override func awakeFromNib() { + super.awakeFromNib() + showMouseOverControls(false) + notesTextFieldRightConstrain.constant = 0 + dateEndTextField.onClick = { [weak self] in + if let wself = self { + wself.createTimeboxPopover(timebox: wself.dateEndTextField) + } + } + } + + private func createTimeboxPopover (timebox: TimeBox) { + guard activeTimeboxPopover == nil else { + return + } + let popover = NSPopover() + let view = TimeBoxViewController.instantiateFromStoryboard("Tasks") + view.didSave = { + let hasChanges = timebox.stringValue != view.stringValue + timebox.stringValue = view.stringValue + popover.performClose(nil) + if hasChanges { + self.didEndEditingCell?(self) + } + } + view.didCancel = { + popover.performClose(nil) + self.activeTimeboxPopover = nil + } + popover.contentViewController = view + popover.show(relativeTo: timebox.frame, of: self, preferredEdge: NSRectEdge.maxY) + view.stringValue = timebox.stringValue + + activeTimeboxPopover = popover + } +} + +extension TaskCell2 { + + @IBAction func handleRemoveButton (_ sender: NSButton) { + didClickRemoveCell?(self) + } + + @IBAction func handleAddButton (_ sender: NSButton) { + didClickAddCell?(self) + } +} + +extension TaskCell2 { +/* + override func drawBackground (in dirtyRect: NSRect) { + + let width = dirtyRect.size.width - kCellLeftPadding * 2 + let height = dirtyRect.size.height - kGapBetweenCells - 2 + + if isEditing { + let selectionRect = NSRect(x: kCellLeftPadding, y: 2, width: width, height: height) + NSColor.red.setStroke() + let selectionPath = NSBezierPath(roundedRect: selectionRect, xRadius: 6, yRadius: 6) + selectionPath.stroke() + } + else if self.mouseInside { + notesTextFieldRightConstrain!.constant = isRemovable ? 36 : 0 + butRemoveWidthConstraint.constant = isRemovable ? 16 : 0 + let selectionRect = NSRect(x: kCellLeftPadding, y: 2, width: width, height: height) + //NSColor(calibratedWhite: 1.0, alpha: 0.0).setFill() + AppDelegate.sharedApp().theme.highlightLineColor.setStroke() +// NSColor(calibratedRed: 0.3, green: 0.1, blue: 0.1, alpha: 1.0).setStroke() + let selectionPath = NSBezierPath(roundedRect: selectionRect, xRadius: 6, yRadius: 6) +// selectionPath.fill() + selectionPath.stroke() + } + else { + notesTextFieldRightConstrain!.constant = 0 + let selectionRect = NSRect(x: kCellLeftPadding, y: 2, width: width, height: height) +// NSColor(calibratedWhite: 1.0, alpha: 1.0).setFill() + AppDelegate.sharedApp().theme.lineColor.setStroke() + let selectionPath = NSBezierPath(roundedRect: selectionRect, xRadius: 6, yRadius: 6) +// selectionPath.fill() + selectionPath.stroke() + } + } + */ + override func mouseEntered (with theEvent: NSEvent) { + super.mouseEntered(with: theEvent) + self.mouseInside = true + self.showMouseOverControls(self.mouseInside) + self.setNeedsDisplay(self.frame) + } + + override func mouseExited (with theEvent: NSEvent) { + super.mouseExited(with: theEvent) + self.mouseInside = false + self.showMouseOverControls(self.mouseInside) + self.setNeedsDisplay(self.frame) + } + + func showMouseOverControls (_ show: Bool) { + butRemove.isHidden = !show + butAdd.isHidden = !show + //line1.isHidden = !show + //line2.isHidden = !show + } + + func ensureTrackingArea() { + if trackingArea == nil { + trackingArea = NSTrackingArea( + rect: NSZeroRect, + options: [NSTrackingArea.Options.inVisibleRect, + NSTrackingArea.Options.activeAlways, + NSTrackingArea.Options.mouseEnteredAndExited], + owner: self, + userInfo: nil + ) + } + } + + override func updateTrackingAreas() { + super.updateTrackingAreas() + self.ensureTrackingArea() + if !self.trackingAreas.contains(self.trackingArea!) { + self.addTrackingArea(self.trackingArea!) + } + } +} + +extension TaskCell2: NSTextFieldDelegate { + + public func control(_ control: NSControl, textShouldBeginEditing fieldEditor: NSText) -> Bool { + isEditing = true + self.setNeedsDisplay(self.frame) + return true + } + + public func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool { + if wasEdited { + wasEdited = false + isEditing = false + didEndEditingCell?(self) + self.setNeedsDisplay(self.frame) + } + return true + } + + public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { + // Detect Enter key + if wasEdited && commandSelector == #selector(NSResponder.insertNewline(_:)) { + wasEdited = false + isEditing = false + didEndEditingCell?(self) + self.setNeedsDisplay(self.frame) + } + return false + } + + func controlTextDidChange (_ obj: Notification) { + wasEdited = true + } +} +*/ diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 4f40cf2..70b70b4 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -180,17 +180,14 @@ 2845B150206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */; }; 2845B151206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */; }; 2845B152206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */; }; - 2845B160206AE468006EFB3B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell.swift */; }; - 2845B161206AE468006EFB3B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell.swift */; }; - 2845B162206AE468006EFB3B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B156206AE467006EFB3B /* TaskCell.xib */; }; - 2845B163206AE468006EFB3B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B156206AE467006EFB3B /* TaskCell.xib */; }; + 2845B160206AE468006EFB3B /* TaskCell_.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell_.swift */; }; + 2845B161206AE468006EFB3B /* TaskCell_.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell_.swift */; }; 2845B164206AE468006EFB3B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */; }; 2845B165206AE468006EFB3B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */; }; - 2845B16A206AE468006EFB3B /* NonTaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* NonTaskCell.swift */; }; - 2845B16B206AE468006EFB3B /* NonTaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* NonTaskCell.swift */; }; - 2845B16C206AE468006EFB3B /* NonTaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15D206AE467006EFB3B /* NonTaskCell.xib */; }; - 2845B16D206AE468006EFB3B /* NonTaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15D206AE467006EFB3B /* NonTaskCell.xib */; }; - 2845B16E206AE46F006EFB3B /* TaskCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B154206AE467006EFB3B /* TaskCellTests.swift */; }; + 2845B16A206AE468006EFB3B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* TaskCell.swift */; }; + 2845B16B206AE468006EFB3B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* TaskCell.swift */; }; + 2845B16C206AE468006EFB3B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15D206AE467006EFB3B /* TaskCell.xib */; }; + 2845B16D206AE468006EFB3B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15D206AE467006EFB3B /* TaskCell.xib */; }; 285465A2216C84E10052CB6A /* CreateMonthReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A1216C84E10052CB6A /* CreateMonthReport.swift */; }; 285465A3216C84E10052CB6A /* CreateMonthReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A1216C84E10052CB6A /* CreateMonthReport.swift */; }; 285465A4216C84E10052CB6A /* CreateMonthReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A1216C84E10052CB6A /* CreateMonthReport.swift */; }; @@ -643,12 +640,10 @@ 2845B146206AE3A8006EFB3B /* ReportCellPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportCellPresenter.swift; sourceTree = ""; }; 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportsDataSource.swift; sourceTree = ""; }; 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportsHeaderView.swift; sourceTree = ""; }; - 2845B154206AE467006EFB3B /* TaskCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCellTests.swift; sourceTree = ""; }; - 2845B155206AE467006EFB3B /* TaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell.swift; sourceTree = ""; }; - 2845B156206AE467006EFB3B /* TaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TaskCell.xib; sourceTree = ""; }; + 2845B155206AE467006EFB3B /* TaskCell_.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell_.swift; sourceTree = ""; }; 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCellPresenter.swift; sourceTree = ""; }; - 2845B15C206AE467006EFB3B /* NonTaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonTaskCell.swift; sourceTree = ""; }; - 2845B15D206AE467006EFB3B /* NonTaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NonTaskCell.xib; sourceTree = ""; }; + 2845B15C206AE467006EFB3B /* TaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell.swift; sourceTree = ""; }; + 2845B15D206AE467006EFB3B /* TaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TaskCell.xib; sourceTree = ""; }; 285465A1216C84E10052CB6A /* CreateMonthReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateMonthReport.swift; sourceTree = ""; }; 285465A6217858790052CB6A /* StoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreView.swift; sourceTree = ""; }; 285465AC217858AF0052CB6A /* StoreView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StoreView.xib; sourceTree = ""; }; @@ -1020,30 +1015,20 @@ 2845B153206AE467006EFB3B /* TaskCell */ = { isa = PBXGroup; children = ( - 2845B155206AE467006EFB3B /* TaskCell.swift */, - 2845B156206AE467006EFB3B /* TaskCell.xib */, - 2845B154206AE467006EFB3B /* TaskCellTests.swift */, + 2845B15C206AE467006EFB3B /* TaskCell.swift */, + 2845B155206AE467006EFB3B /* TaskCell_.swift */, 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */, + 2845B15D206AE467006EFB3B /* TaskCell.xib */, ); path = TaskCell; sourceTree = ""; }; - 2845B15B206AE467006EFB3B /* NonTaskCell */ = { - isa = PBXGroup; - children = ( - 2845B15C206AE467006EFB3B /* NonTaskCell.swift */, - 2845B15D206AE467006EFB3B /* NonTaskCell.xib */, - ); - path = NonTaskCell; - sourceTree = ""; - }; 2845B16F206C2888006EFB3B /* cells */ = { isa = PBXGroup; children = ( 4065D3781DD3B44200B73201 /* CellProtocol.swift */, 2863492023B3A941009F81D7 /* ClosedDayCell */, 2863491923B393A3009F81D7 /* CloseDayCell */, - 2845B15B206AE467006EFB3B /* NonTaskCell */, 2845B153206AE467006EFB3B /* TaskCell */, ); path = cells; @@ -2089,7 +2074,6 @@ 280D71C31ED608D6005D2689 /* Settings.storyboard in Resources */, 280D71C41ED608D6005D2689 /* Tasks.storyboard in Resources */, 564E55F92028857300CE4C76 /* Worklogs.storyboard in Resources */, - 2845B162206AE468006EFB3B /* TaskCell.xib in Resources */, 2863492523B3A966009F81D7 /* ClosedDayCell.xib in Resources */, 56D90D0120876F4900F24442 /* WizardJiraView.xib in Resources */, 280C1D431ED74EF900C126A1 /* BrowserSupport.scpt in Resources */, @@ -2101,7 +2085,7 @@ 569C4C76202319FC0049FBF1 /* GitCell.xib in Resources */, 280D71C81ED608D6005D2689 /* Images.xcassets in Resources */, 285465AD217858AF0052CB6A /* StoreView.xib in Resources */, - 2845B16C206AE468006EFB3B /* NonTaskCell.xib in Resources */, + 2845B16C206AE468006EFB3B /* TaskCell.xib in Resources */, 569C4C73202319EE0049FBF1 /* JitCell.xib in Resources */, 2863492923B5EF87009F81D7 /* Reports.storyboard in Resources */, ); @@ -2124,7 +2108,7 @@ 569C4C7A20231A0B0049FBF1 /* BrowserCell.xib in Resources */, 288BB6822087152B00CF720A /* WizardAppleScriptView.xib in Resources */, 569C4C74202319EE0049FBF1 /* JitCell.xib in Resources */, - 2845B16D206AE468006EFB3B /* NonTaskCell.xib in Resources */, + 2845B16D206AE468006EFB3B /* TaskCell.xib in Resources */, 2871952920241A6C001C237E /* TrackingView.xib in Resources */, 2892E80F208D9DD0004E5298 /* OutputsScrollView.xib in Resources */, 285465AE217858AF0052CB6A /* StoreView.xib in Resources */, @@ -2148,7 +2132,6 @@ 5623915421CBC9750031B882 /* Projects.storyboard in Resources */, 28E896631E830D6700722032 /* Placeholder.storyboard in Resources */, 56D90D0220876F4900F24442 /* WizardJiraView.xib in Resources */, - 2845B163206AE468006EFB3B /* TaskCell.xib in Resources */, 4065D3D31DD3B44200B73201 /* jirassic.sdef in Resources */, 40E092411DE385E4001EF5DA /* Settings.storyboard in Resources */, 28B116BC21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, @@ -2269,7 +2252,7 @@ 2801388F205F86460051B532 /* TimeBox.swift in Sources */, 280D71471ED6043D005D2689 /* TaskTypeEstimator.swift in Sources */, 280D71491ED6043D005D2689 /* TaskTypeSelection.swift in Sources */, - 2845B160206AE468006EFB3B /* TaskCell.swift in Sources */, + 2845B160206AE468006EFB3B /* TaskCell_.swift in Sources */, 28FE18AB207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */, 280D714A1ED6043D005D2689 /* PredictiveTimeTyping.swift in Sources */, 280D714C1ED6043D005D2689 /* RegisterUserInteractor.swift in Sources */, @@ -2352,7 +2335,7 @@ 280D71821ED6043D005D2689 /* CellProtocol.swift in Sources */, 280D71831ED6043D005D2689 /* TasksView.swift in Sources */, 2898D3A82181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */, - 2845B16A206AE468006EFB3B /* NonTaskCell.swift in Sources */, + 2845B16A206AE468006EFB3B /* TaskCell.swift in Sources */, 2845B1352066C6E6006EFB3B /* AppleScriptProtocol.swift in Sources */, 569C4C582023193B0049FBF1 /* ShellCell.swift in Sources */, 569C4C64202319870049FBF1 /* GitCell.swift in Sources */, @@ -2561,7 +2544,7 @@ 40FCE42C1DFC3F8700D4FD45 /* WelcomeViewController.swift in Sources */, 5623915121CBC6CD0031B882 /* Project.swift in Sources */, 28EE1F6520EE92DA00C5C1D6 /* CalendarAppPresenter.swift in Sources */, - 2845B16B206AE468006EFB3B /* NonTaskCell.swift in Sources */, + 2845B16B206AE468006EFB3B /* TaskCell.swift in Sources */, 4065D39D1DD3B44200B73201 /* Report.swift in Sources */, 40FCE4251DF7646F00D4FD45 /* AppleScript.swift in Sources */, 28A928781E8F78580022AB55 /* SQLiteSchema.swift in Sources */, @@ -2573,7 +2556,7 @@ 287195162022E1E2001C237E /* HookupPresenter.swift in Sources */, 2892E812208E6275004E5298 /* LocalPreferences.swift in Sources */, 4065D3AC1DD3B44200B73201 /* CreateReport.swift in Sources */, - 2845B161206AE468006EFB3B /* TaskCell.swift in Sources */, + 2845B161206AE468006EFB3B /* TaskCell_.swift in Sources */, 28FB265F2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */, 287195222022FD87001C237E /* InputsTableViewDataSource.swift in Sources */, 28577EB71E8AF08F002B07FD /* SQLTable.swift in Sources */, @@ -2605,7 +2588,6 @@ 56D069E1216CABCB000D051D /* CreateMonthReportTests.swift in Sources */, 287558451EFE5969009A2503 /* ReadDaysInteractorTests.swift in Sources */, 4065D4241DD4562900B73201 /* CreateReportTests.swift in Sources */, - 2845B16E206AE46F006EFB3B /* TaskCellTests.swift in Sources */, 4073184F1DE9B1B40046F409 /* ComputerWakeUpInteractorTests.swift in Sources */, 28A283AE203C069F00DDCB63 /* GitBranchParserTests.swift in Sources */, 4065D4271DD4562900B73201 /* TaskTypeEstimatorTests.swift in Sources */, From 32d08ed8c267a5fbb7b9c59bc4f88461b0c09a68 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 30 Dec 2019 10:21:17 +0200 Subject: [PATCH 06/48] Show reports --- Delivery/macOS/External/AppleScript.swift | 2 +- .../macOS/Modules/Hookup/ModuleHookup.swift | 2 +- .../Screens/Main/MainViewController.swift | 2 +- .../Screens/Reports/ReportsInteractor.swift | 63 ------------------- .../Screens/Reports/ReportsPresenter.swift | 54 ++++++++-------- .../Reports/ReportsViewController.swift | 6 +- .../macOS/Screens/Tasks/TasksInteractor.swift | 8 +-- .../AppleScriptCommands/NewTaskCommand.swift | 2 +- Jirassic.xcodeproj/project.pbxproj | 32 +++++----- .../xcschemes/Jirassic AppStore.xcscheme | 28 ++++----- .../xcschemes/Jirassic iOS.xcscheme | 10 +-- .../xcschemes/Jirassic macOS.xcscheme | 28 ++++----- .../xcschemes/JirassicLauncher.xcscheme | 10 +-- .../xcschemes/jirassic-cmd.xcscheme | 10 +-- 14 files changed, 84 insertions(+), 173 deletions(-) delete mode 100644 Delivery/macOS/Screens/Reports/ReportsInteractor.swift diff --git a/Delivery/macOS/External/AppleScript.swift b/Delivery/macOS/External/AppleScript.swift index e9df4e2..2bfe83a 100644 --- a/Delivery/macOS/External/AppleScript.swift +++ b/Delivery/macOS/External/AppleScript.swift @@ -96,7 +96,7 @@ class AppleScript: AppleScriptProtocol { let validJson = rawJson.replacingOccurrences(of: "'", with: "\"") var dict: [String: String] = [:] if let data = validJson.data(using: String.Encoding.utf8) { - if let d = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String] { + if let d = ((try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String]) as [String : String]??) { if let _d = d { dict = _d } diff --git a/Delivery/macOS/Modules/Hookup/ModuleHookup.swift b/Delivery/macOS/Modules/Hookup/ModuleHookup.swift index 5611583..f35a2a6 100644 --- a/Delivery/macOS/Modules/Hookup/ModuleHookup.swift +++ b/Delivery/macOS/Modules/Hookup/ModuleHookup.swift @@ -48,7 +48,7 @@ class ModuleHookup { completion?(false) return } - guard let jdict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String], + guard let jdict = ((try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String]) as [String : String]??), let dict = jdict else { completion?(false) return diff --git a/Delivery/macOS/Screens/Main/MainViewController.swift b/Delivery/macOS/Screens/Main/MainViewController.swift index e2f7f5e..ade87ff 100644 --- a/Delivery/macOS/Screens/Main/MainViewController.swift +++ b/Delivery/macOS/Screens/Main/MainViewController.swift @@ -197,7 +197,7 @@ extension MainViewController: MainPresenterOutput { let controller = ReportsViewController.instantiateFromStoryboard("Reports") let presenter = ReportsPresenter() - let interactor = ReportsInteractor() + let interactor = TasksInteractor() presenter.ui = controller presenter.interactor = interactor diff --git a/Delivery/macOS/Screens/Reports/ReportsInteractor.swift b/Delivery/macOS/Screens/Reports/ReportsInteractor.swift deleted file mode 100644 index 9507d1f..0000000 --- a/Delivery/macOS/Screens/Reports/ReportsInteractor.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// ReportsInteractor.swift -// Jirassic -// -// Created by Cristian Baluta on 27/12/2019. -// Copyright © 2019 Imagin soft. All rights reserved. -// - -import Foundation -import RCPreferences -import RCLog - -protocol ReportsInteractorInput: class { - - func reloadTasks (inDay day: Day) - func reloadTasks (inMonth day: Day) -} - -protocol ReportsInteractorOutput: class { - - func tasksDidLoad (_ tasks: [Task]) -} - -class ReportsInteractor { - - weak var presenter: ReportsInteractorOutput? - - private let tasksReader: ReadTasksInteractor! - private let moduleGit = ModuleGitLogs() - private let moduleCalendar = ModuleCalendar() - private let pref = RCPreferences() - private var currentTasks = [Task]() - private var currentDateStart: Date? - - init() { - tasksReader = ReadTasksInteractor(repository: localRepository, remoteRepository: remoteRepository) - } -} - -extension ReportsInteractor: ReportsInteractorInput { - - func reloadTasks (inDay day: Day) { - - let dateStart = day.dateStart - let dateEnd = day.dateEnd ?? dateStart.endOfDay() - currentDateStart = dateStart - reloadTasks(dateStart: dateStart, dateEnd: dateEnd) - } - - func reloadTasks (inMonth day: Day) { - - let dateStart = day.dateStart.startOfMonth() - let dateEnd = dateStart.endOfMonth() - currentDateStart = dateStart - reloadTasks(dateStart: dateStart, dateEnd: dateEnd) - } - - private func reloadTasks (dateStart: Date, dateEnd: Date) { - - - } - -} diff --git a/Delivery/macOS/Screens/Reports/ReportsPresenter.swift b/Delivery/macOS/Screens/Reports/ReportsPresenter.swift index 0eeb64a..dc83d1f 100644 --- a/Delivery/macOS/Screens/Reports/ReportsPresenter.swift +++ b/Delivery/macOS/Screens/Reports/ReportsPresenter.swift @@ -12,10 +12,9 @@ import RCPreferences protocol ReportsPresenterInput: class { func viewDidLoad() - func reloadData() + func reloadLastSelectedMonth() func reloadReportsOnDay (_ day: Day) func reloadReportsInMonth (_ date: Date) - func updateNoTasksState() } protocol ReportsPresenterOutput: class { @@ -23,56 +22,55 @@ protocol ReportsPresenterOutput: class { func showLoadingIndicator (_ show: Bool) func showMessage (_ message: MessageViewModel) func showReports (_ reports: [Report], numberOfDays: Int, type: ListType) + func removeReports() } class ReportsPresenter { weak var appWireframe: AppWireframe? weak var ui: ReportsPresenterOutput? - var interactor: ReportsInteractorInput? + var interactor: TasksInteractorInput? private var currentTasks = [Task]() private var currentReports = [Report]() private let pref = RCPreferences() private var extensions = ExtensionsInteractor() private var lastSelectedDay: Day? + private var lastSelectedMonth: Date = Date() + + + func updateNoTasksState() { + + if currentTasks.count == 1 { + ui!.showMessage(( + title: "No task yet", + message: "Go to 'Tasks' tab and log some work first!", + buttonTitle: nil)) + } else { + appWireframe!.removePlaceholder() + } + } } extension ReportsPresenter: ReportsPresenterInput { func viewDidLoad() { ui!.showLoadingIndicator(true) - reloadData() + reloadLastSelectedMonth() } - func reloadData() { - ui!.showLoadingIndicator(true) + func reloadLastSelectedMonth() { + reloadReportsInMonth(lastSelectedMonth) } func reloadReportsOnDay (_ day: Day) { - + // TODO } func reloadReportsInMonth (_ date: Date) { - - } - - func reloadTasksOnDay (_ day: Day, listType: ListType) { + ui!.removeReports() ui!.showLoadingIndicator(true) - lastSelectedDay = day - interactor!.reloadTasks(inDay: day) - } - - func updateNoTasksState() { - - if currentTasks.count == 1 { - ui!.showMessage(( - title: "No task yet", - message: "Go to 'Tasks' tab and log some work first!", - buttonTitle: nil)) - } else { - appWireframe!.removePlaceholder() - } + interactor!.reloadTasks(inMonth: date) } func messageButtonDidPress() { @@ -87,13 +85,13 @@ extension ReportsPresenter: ReportsPresenterInput { let task = Task(endDate: Date(), type: .startDay) let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) saveInteractor.saveTask(task, allowSyncing: true, completion: { [weak self] savedTask in - self?.reloadData() + self?.reloadLastSelectedMonth() }) ModuleHookup().insert(task: task) } } -extension ReportsPresenter: ReportsInteractorOutput { +extension ReportsPresenter: TasksInteractorOutput { func tasksDidLoad (_ tasks: [Task]) { @@ -110,7 +108,7 @@ extension ReportsPresenter: ReportsInteractorOutput { let reportInteractor = CreateMonthReport() let reports = reportInteractor.reports(fromTasks: currentTasks, targetHoursInDay: targetHoursInDay, roundHours: true) currentReports = reports.byTasks -// ui.showReports(currentReports, numberOfDays: reports.byDays.count) + ui.showReports(currentReports, numberOfDays: reports.byDays.count, type: .reports) // let settings = SettingsInteractor().getAppSettings() diff --git a/Delivery/macOS/Screens/Reports/ReportsViewController.swift b/Delivery/macOS/Screens/Reports/ReportsViewController.swift index f71a5dd..e253a10 100644 --- a/Delivery/macOS/Screens/Reports/ReportsViewController.swift +++ b/Delivery/macOS/Screens/Reports/ReportsViewController.swift @@ -16,9 +16,6 @@ class ReportsViewController: NSViewController { weak var appWireframe: AppWireframe? var presenter: ReportsPresenterInput? - /// Property to keep a reference to the active cell rect - private var rectToDisplayPopoverAt: NSRect? - private var activePopover: NSPopover? override func viewDidLoad() { super.viewDidLoad() @@ -77,12 +74,11 @@ extension ReportsViewController: ReportsPresenterOutput { tasksScrollView = scrollView } - func removeTasksController() { + func removeReports() { if tasksScrollView != nil { tasksScrollView?.removeFromSuperview() tasksScrollView = nil } } - } diff --git a/Delivery/macOS/Screens/Tasks/TasksInteractor.swift b/Delivery/macOS/Screens/Tasks/TasksInteractor.swift index b72a28f..c6e13c5 100644 --- a/Delivery/macOS/Screens/Tasks/TasksInteractor.swift +++ b/Delivery/macOS/Screens/Tasks/TasksInteractor.swift @@ -13,7 +13,7 @@ import RCLog protocol TasksInteractorInput: class { func reloadTasks (inDay day: Day) - func reloadTasks (inMonth day: Day) + func reloadTasks (inMonth date: Date) } protocol TasksInteractorOutput: class { @@ -47,10 +47,10 @@ extension TasksInteractor: TasksInteractorInput { reloadTasks(dateStart: dateStart, dateEnd: dateEnd) } - func reloadTasks (inMonth day: Day) { + func reloadTasks (inMonth date: Date) { - let dateStart = day.dateStart.startOfMonth() - let dateEnd = dateStart.endOfMonth() + let dateStart = date.startOfMonth() + let dateEnd = date.endOfMonth() currentDateStart = dateStart reloadTasks(dateStart: dateStart, dateEnd: dateEnd) } diff --git a/External/AppleScriptCommands/NewTaskCommand.swift b/External/AppleScriptCommands/NewTaskCommand.swift index e0fd6cf..b688c57 100644 --- a/External/AppleScriptCommands/NewTaskCommand.swift +++ b/External/AppleScriptCommands/NewTaskCommand.swift @@ -30,7 +30,7 @@ class NewTaskCommand: NSScriptCommand { guard let data = validJson.data(using: String.Encoding.utf8) else { return nil } - guard let jdict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String], let dict = jdict else { + guard let jdict = ((try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String]) as [String : String]??), let dict = jdict else { return nil } RCLog(dict) diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 70b70b4..edcc3da 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -245,8 +245,6 @@ 2863492D23B5EF9C009F81D7 /* ReportsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492B23B5EF9B009F81D7 /* ReportsViewController.swift */; }; 2863492F23B5EFD1009F81D7 /* ReportsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */; }; 2863493023B5EFD1009F81D7 /* ReportsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */; }; - 2863493223B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */; }; - 2863493323B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */; }; 2863493523B790B1009F81D7 /* ProjectsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */; }; 2863493623B790B1009F81D7 /* ProjectsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */; }; 2863493823B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */; }; @@ -672,7 +670,6 @@ 2863492823B5EF87009F81D7 /* Reports.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Reports.storyboard; sourceTree = ""; }; 2863492B23B5EF9B009F81D7 /* ReportsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsViewController.swift; sourceTree = ""; }; 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsPresenter.swift; sourceTree = ""; }; - 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsInteractor.swift; sourceTree = ""; }; 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsListViewController.swift; sourceTree = ""; }; 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsPresenter.swift; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; @@ -1102,7 +1099,6 @@ 2863492823B5EF87009F81D7 /* Reports.storyboard */, 2863492B23B5EF9B009F81D7 /* ReportsViewController.swift */, 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */, - 2863493123B5EFE6009F81D7 /* ReportsInteractor.swift */, 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */, 2845B141206AE2A4006EFB3B /* cells */, 2898D3A62181905600CF5AD4 /* MonthReports */, @@ -1934,7 +1930,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 1010; + LastUpgradeCheck = 1130; ORGANIZATIONNAME = "Imagin soft"; TargetAttributes = { 280D70B51ECFE06A005D2689 = { @@ -1954,7 +1950,7 @@ 280D711C1ED5EE7F005D2689 = { CreatedOnToolsVersion = 8.3.2; DevelopmentTeam = 5NHDC5EV44; - LastSwiftMigration = 0920; + LastSwiftMigration = 1130; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.Sandbox = { @@ -1976,7 +1972,7 @@ 4065D3031DD3A1AA00B73201 = { CreatedOnToolsVersion = 8.1; DevelopmentTeam = 5NHDC5EV44; - LastSwiftMigration = 0920; + LastSwiftMigration = 1130; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.Push = { @@ -1993,7 +1989,7 @@ 4065D4011DD4532100B73201 = { CreatedOnToolsVersion = 8.1; DevelopmentTeam = 5NHDC5EV44; - LastSwiftMigration = 0920; + LastSwiftMigration = 1130; ProvisioningStyle = Manual; TestTargetID = 280D711C1ED5EE7F005D2689; }; @@ -2367,7 +2363,6 @@ 280D71A61ED6043D005D2689 /* SQLiteSchema.swift in Sources */, 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */, 28279AD121BBF08C00376304 /* InMemoryCoreDataRepository.swift in Sources */, - 2863493223B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */, 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, 569C4C5E202319630049FBF1 /* JitCell.swift in Sources */, 28A2839A20382BBB00DDCB63 /* GitCommit.swift in Sources */, @@ -2417,7 +2412,6 @@ 287195372027CAD9001C237E /* TimeInteractor.swift in Sources */, 287195192022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, 28577EAD1E8ADC53002B07FD /* SSettings.swift in Sources */, - 2863493323B5EFE6009F81D7 /* ReportsInteractor.swift in Sources */, 28577EB51E8AF08F002B07FD /* SQLiteDB.swift in Sources */, 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, 286BC00521909F85004D4CDD /* CloseDay.swift in Sources */, @@ -2749,6 +2743,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 5NHDC5EV44; + ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "$(SRCROOT)/Delivery/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -2760,7 +2755,7 @@ PROVISIONING_PROFILE_SPECIFIER = "jirassic dev"; SWIFT_OBJC_BRIDGING_HEADER = "Delivery/macOS/Bridging-Header.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -2773,6 +2768,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 5NHDC5EV44; + ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "$(SRCROOT)/Delivery/macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -2784,7 +2780,7 @@ PROVISIONING_PROFILE_SPECIFIER = "jirassic distribution"; SWIFT_OBJC_BRIDGING_HEADER = "Delivery/macOS/Bridging-Header.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -2799,6 +2795,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 5NHDC5EV44; + ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "$(SRCROOT)/Delivery/macOS-launcher/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -2845,6 +2842,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -2903,6 +2901,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -2960,6 +2959,7 @@ CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = 5NHDC5EV44; + ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -2980,7 +2980,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Delivery/macOS/Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -3009,7 +3009,7 @@ PROVISIONING_PROFILE_SPECIFIER = "jirassic appstore"; SWIFT_OBJC_BRIDGING_HEADER = "Delivery/macOS/Bridging-Header.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -3031,7 +3031,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Jirassic no cloud.app/Contents/MacOS/Jirassic no cloud"; }; name = Debug; @@ -3054,7 +3054,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Jirassic no cloud.app/Contents/MacOS/Jirassic no cloud"; }; name = Release; diff --git a/Jirassic.xcodeproj/xcshareddata/xcschemes/Jirassic AppStore.xcscheme b/Jirassic.xcodeproj/xcshareddata/xcschemes/Jirassic AppStore.xcscheme index 1129f76..3d10aa6 100644 --- a/Jirassic.xcodeproj/xcshareddata/xcschemes/Jirassic AppStore.xcscheme +++ b/Jirassic.xcodeproj/xcshareddata/xcschemes/Jirassic AppStore.xcscheme @@ -1,6 +1,6 @@ + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + + + + @@ -40,17 +49,6 @@ - - - - - - - - - - - - + + - - + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> + + + + @@ -40,17 +49,6 @@ - - - - - - - - - - - - + + - - - - - - + + - - Date: Mon, 6 Jan 2020 17:44:38 +0200 Subject: [PATCH 07/48] Fix start day --- .../macOS/Screens/Calendar/CalendarViewController.swift | 2 +- Delivery/macOS/Screens/Tasks/TasksPresenter.swift | 4 +++- Delivery/macOS/Screens/Tasks/TasksViewController.swift | 7 +------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Delivery/macOS/Screens/Calendar/CalendarViewController.swift b/Delivery/macOS/Screens/Calendar/CalendarViewController.swift index 921508e..285e10d 100644 --- a/Delivery/macOS/Screens/Calendar/CalendarViewController.swift +++ b/Delivery/macOS/Screens/Calendar/CalendarViewController.swift @@ -20,7 +20,7 @@ class CalendarViewController: NSViewController { var didChangeDay: ((_ day: Day) -> Void)? var didChangeMonth: ((_ date: Date) -> Void)? - private var day: Day? = Day(dateStart: Date(), dateEnd: nil) + private var day: Day? = Day(dateStart: Date().startOfDay(), dateEnd: nil) var selectedDay: Day? { get { return day diff --git a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift index 25b19e6..4f931f4 100644 --- a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift @@ -17,6 +17,7 @@ protocol TasksPresenterInput: class { func insertTaskWithData (_ taskData: TaskCreationData) func insertTask (after row: Int) func removeTask (at row: Int) + func didClickStartDay() func didClickSaveWorklogs() } @@ -67,7 +68,7 @@ extension TasksPresenter: TasksPresenterInput { } } - func messageButtonDidPress() { + func didClickStartDay() { if currentTasks.count == 0 { startDay() @@ -159,6 +160,7 @@ extension TasksPresenter: TasksInteractorOutput { } ui.showLoadingIndicator(false) currentTasks = tasks + ui.removeTasks() ui.showTasks(currentTasks) updateNoTasksState() } diff --git a/Delivery/macOS/Screens/Tasks/TasksViewController.swift b/Delivery/macOS/Screens/Tasks/TasksViewController.swift index dee746f..d9750d0 100644 --- a/Delivery/macOS/Screens/Tasks/TasksViewController.swift +++ b/Delivery/macOS/Screens/Tasks/TasksViewController.swift @@ -21,11 +21,6 @@ class TasksViewController: NSViewController { weak var appWireframe: AppWireframe? var presenter: TasksPresenterInput? - override func viewDidLoad() { - super.viewDidLoad() - presenter!.reloadLastSelectedDay() - } - deinit { RCLog(self) } @@ -48,7 +43,7 @@ extension TasksViewController: TasksPresenterOutput { let controller = appWireframe!.presentPlaceholder(message, in: self.view) controller.didPressButton = { -// self.presenter?.messageButtonDidPress() + self.presenter?.didClickStartDay() } } From 8a2ea21b5effc5a13c521dc30992443890074629 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Tue, 7 Jan 2020 20:07:51 +0200 Subject: [PATCH 08/48] Rename cell --- .../Screens/Reports/ReportsDataSource.swift | 1 + .../CopyReportCell/CopyReportCell.swift} | 10 +++---- .../CopyReportCell/CopyReportCell.xib} | 10 +++---- Jirassic.xcodeproj/project.pbxproj | 30 +++++++++---------- 4 files changed, 26 insertions(+), 25 deletions(-) rename Delivery/macOS/Screens/Reports/{MonthReports/MonthReportsHeaderView.swift => cells/CopyReportCell/CopyReportCell.swift} (75%) rename Delivery/macOS/Screens/Reports/{MonthReports/MonthReportsHeaderView.xib => cells/CopyReportCell/CopyReportCell.xib} (95%) diff --git a/Delivery/macOS/Screens/Reports/ReportsDataSource.swift b/Delivery/macOS/Screens/Reports/ReportsDataSource.swift index 0392fe4..32cb4be 100644 --- a/Delivery/macOS/Screens/Reports/ReportsDataSource.swift +++ b/Delivery/macOS/Screens/Reports/ReportsDataSource.swift @@ -16,6 +16,7 @@ class ReportsDataSource: NSObject, TasksAndReportsDataSource { tableView.usesAutomaticRowHeights = true } ReportCell.register(in: tableView) + CopyReportCell.register(in: tableView) } } var didClickAddRow: ((_ row: Int) -> Void)? diff --git a/Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.swift b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.swift similarity index 75% rename from Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.swift rename to Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.swift index 5270f3c..31c81e0 100644 --- a/Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.swift +++ b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.swift @@ -8,7 +8,7 @@ import Cocoa -class MonthReportsHeaderView: ReportsHeaderView { +class CopyReportCell: NSTableRowView { @IBOutlet private var butCopyAll: NSButton! @IBOutlet private var butCopyAsHtml: NSButton! @@ -27,18 +27,18 @@ class MonthReportsHeaderView: ReportsHeaderView { override func awakeFromNib() { super.awakeFromNib() - butCopyAsHtml.state = pref.bool(.copyWorklogsAsHtml) ? .on : .off +// butCopyAsHtml.state = pref.bool(.copyWorklogsAsHtml) ? .on : .off butCopyAsHtml.toolTip = "This can be set in 'Settings/Tracking/Working between'" } } -extension MonthReportsHeaderView { +extension CopyReportCell { @IBAction func handleCopyAllButton (_ sender: NSButton) { - didClickCopyAll?(pref.bool(.copyWorklogsAsHtml)) +// didClickCopyAll?(pref.bool(.copyWorklogsAsHtml)) } @IBAction func handleHtmlButton (_ sender: NSButton) { - pref.set(sender.state == .on, forKey: .copyWorklogsAsHtml) +// pref.set(sender.state == .on, forKey: .copyWorklogsAsHtml) } } diff --git a/Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.xib b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.xib similarity index 95% rename from Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.xib rename to Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.xib index 3703176..ed3d18c 100644 --- a/Delivery/macOS/Screens/Reports/MonthReports/MonthReportsHeaderView.xib +++ b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.xib @@ -1,15 +1,15 @@ - + - + - + @@ -37,7 +37,7 @@ - + @@ -69,7 +69,7 @@ - + diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index edcc3da..f3b7fa7 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -306,8 +306,8 @@ 2892E80F208D9DD0004E5298 /* OutputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2892E80D208D9DD0004E5298 /* OutputsScrollView.xib */; }; 2892E811208E6275004E5298 /* LocalPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892E810208E6275004E5298 /* LocalPreferences.swift */; }; 2892E812208E6275004E5298 /* LocalPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892E810208E6275004E5298 /* LocalPreferences.swift */; }; - 2898D3A82181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */; }; - 2898D3A92181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */; }; + 2898D3A82181907700CF5AD4 /* CopyReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3A72181907700CF5AD4 /* CopyReportCell.swift */; }; + 2898D3A92181907700CF5AD4 /* CopyReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3A72181907700CF5AD4 /* CopyReportCell.swift */; }; 2898D3AB2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */; }; 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */; }; 2898D3AE2184ED3000CF5AD4 /* Components.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2898D3AD2184ED3000CF5AD4 /* Components.storyboard */; }; @@ -364,8 +364,8 @@ 28AFE7311E9A594500BAAD8C /* UserDefaults+token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */; }; 28B116B821AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */; }; 28B116B921AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */; }; - 28B116BB21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */; }; - 28B116BC21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */; }; + 28B116BB21AE6179004ACE01 /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */; }; + 28B116BC21AE6179004ACE01 /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */; }; 28C6A38421D359E60036DB29 /* RemoveDuplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */; }; 28C6A38521D359E60036DB29 /* RemoveDuplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */; }; 28C9C6221EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; @@ -695,7 +695,7 @@ 2892E80A208D9B42004E5298 /* InputsScrollView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InputsScrollView.xib; sourceTree = ""; }; 2892E80D208D9DD0004E5298 /* OutputsScrollView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OutputsScrollView.xib; sourceTree = ""; }; 2892E810208E6275004E5298 /* LocalPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalPreferences.swift; sourceTree = ""; }; - 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthReportsHeaderView.swift; sourceTree = ""; }; + 2898D3A72181907700CF5AD4 /* CopyReportCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyReportCell.swift; sourceTree = ""; }; 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeBoxViewController.swift; sourceTree = ""; }; 2898D3AD2184ED3000CF5AD4 /* Components.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Components.storyboard; sourceTree = ""; }; 2898D3B02185959300CF5AD4 /* EditableTimeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTimeBox.swift; sourceTree = ""; }; @@ -725,7 +725,7 @@ 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Settings.swift"; sourceTree = ""; }; 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+token.swift"; sourceTree = ""; }; 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReportsHeaderView.xib; sourceTree = ""; }; - 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MonthReportsHeaderView.xib; sourceTree = ""; }; + 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CopyReportCell.xib; sourceTree = ""; }; 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveDuplicate.swift; sourceTree = ""; }; 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+uploadToken.swift"; sourceTree = ""; }; 28CBB595204554A2006F9D3A /* ParseGitBranch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseGitBranch.swift; sourceTree = ""; }; @@ -983,6 +983,7 @@ children = ( 287B943B21A690C100FFC4A5 /* HeaderView */, 2845B143206AE3A8006EFB3B /* ReportCell */, + 2898D3A62181905600CF5AD4 /* CopyReportCell */, ); path = cells; sourceTree = ""; @@ -1101,7 +1102,6 @@ 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */, 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */, 2845B141206AE2A4006EFB3B /* cells */, - 2898D3A62181905600CF5AD4 /* MonthReports */, ); path = Reports; sourceTree = ""; @@ -1183,13 +1183,13 @@ path = Jira; sourceTree = ""; }; - 2898D3A62181905600CF5AD4 /* MonthReports */ = { + 2898D3A62181905600CF5AD4 /* CopyReportCell */ = { isa = PBXGroup; children = ( - 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */, - 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */, + 2898D3A72181907700CF5AD4 /* CopyReportCell.swift */, + 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */, ); - path = MonthReports; + path = CopyReportCell; sourceTree = ""; }; 28A28392203786F600DDCB63 /* GitLogs */ = { @@ -2060,7 +2060,7 @@ 280D71C01ED608D6005D2689 /* Placeholder.storyboard in Resources */, 280C1D441ED74EF900C126A1 /* ShellSupport.scpt in Resources */, 28FB265820224E9B00AEA38D /* HookupCell.xib in Resources */, - 28B116BB21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, + 28B116BB21AE6179004ACE01 /* CopyReportCell.xib in Resources */, 287B358A20FBAFD60022F43E /* WizardCalendarView.xib in Resources */, 280D71C11ED608D6005D2689 /* Welcome.storyboard in Resources */, 2863491E23B393E1009F81D7 /* CloseDayCell.xib in Resources */, @@ -2130,7 +2130,7 @@ 56D90D0220876F4900F24442 /* WizardJiraView.xib in Resources */, 4065D3D31DD3B44200B73201 /* jirassic.sdef in Resources */, 40E092411DE385E4001EF5DA /* Settings.storyboard in Resources */, - 28B116BC21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, + 28B116BC21AE6179004ACE01 /* CopyReportCell.xib in Resources */, 56D90CFF20876F3C00F24442 /* WizardGitView.xib in Resources */, 564E55FA2028857300CE4C76 /* Worklogs.storyboard in Resources */, 2863491F23B393E1009F81D7 /* CloseDayCell.xib in Resources */, @@ -2330,7 +2330,7 @@ 28C6A38421D359E60036DB29 /* RemoveDuplicate.swift in Sources */, 280D71821ED6043D005D2689 /* CellProtocol.swift in Sources */, 280D71831ED6043D005D2689 /* TasksView.swift in Sources */, - 2898D3A82181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */, + 2898D3A82181907700CF5AD4 /* CopyReportCell.swift in Sources */, 2845B16A206AE468006EFB3B /* TaskCell.swift in Sources */, 2845B1352066C6E6006EFB3B /* AppleScriptProtocol.swift in Sources */, 569C4C582023193B0049FBF1 /* ShellCell.swift in Sources */, @@ -2423,7 +2423,7 @@ 28AA500B1EDCD51300AAF03D /* CoreDataRepository+User.swift in Sources */, 4065D3D81DD3B44200B73201 /* InternalNotifications.swift in Sources */, 4065D3FA1DD3B44200B73201 /* RepositoryInteractor.swift in Sources */, - 2898D3A92181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */, + 2898D3A92181907700CF5AD4 /* CopyReportCell.swift in Sources */, 28DCA4562018B6E700DFAE29 /* JProjectIssue.swift in Sources */, 28A928871E910EA70022AB55 /* SqliteRepository+Settings.swift in Sources */, 28FE1888207A520B00DF796E /* NewTaskCommand.swift in Sources */, From d71385929b2b6d5fdf97d508862f9d0a624d65c7 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Wed, 8 Jan 2020 09:59:39 +0200 Subject: [PATCH 09/48] Start day on past days with the time set in settings --- App/Entities/Task.swift | 4 +++ App/Extensions/DateExtension.swift | 5 ++++ ...loseDay.swift => CloseDayInteractor.swift} | 13 +++++----- .../macOS/Screens/Tasks/TasksPresenter.swift | 26 ++++++++++++++----- .../cells/TaskCell/TaskCellPresenter.swift | 6 ++--- Jirassic.xcodeproj/project.pbxproj | 12 ++++----- 6 files changed, 44 insertions(+), 22 deletions(-) rename App/Tasks/{CloseDay.swift => CloseDayInteractor.swift} (78%) diff --git a/App/Entities/Task.swift b/App/Entities/Task.swift index caed9bf..f1c9af9 100644 --- a/App/Entities/Task.swift +++ b/App/Entities/Task.swift @@ -94,6 +94,10 @@ extension Task { self.taskType = type self.objectId = String.generateId() } + + var isSaved: Bool { + return objectId != nil + } } /// Object used to pass task data to and from the cell, for displaying and editing diff --git a/App/Extensions/DateExtension.swift b/App/Extensions/DateExtension.swift index a27be12..73282bf 100644 --- a/App/Extensions/DateExtension.swift +++ b/App/Extensions/DateExtension.swift @@ -259,6 +259,11 @@ extension Date { return Date().dateByUpdating(hour: comps.hour!, minute: comps.minute!) } + func dateByKeepingTime(from date: Date) -> Date { + let comps = gregorian.dateComponents(ymdhmsUnitFlags, from: date) + return self.dateByUpdating(hour: comps.hour!, minute: comps.minute!) + } + func dateByAddingMonths(_ numberOfMonths: Int) -> Date { return Calendar.current.date(byAdding: .month, value: numberOfMonths, to: self) ?? self } diff --git a/App/Tasks/CloseDay.swift b/App/Tasks/CloseDayInteractor.swift similarity index 78% rename from App/Tasks/CloseDay.swift rename to App/Tasks/CloseDayInteractor.swift index 7ee7e23..ef04960 100644 --- a/App/Tasks/CloseDay.swift +++ b/App/Tasks/CloseDayInteractor.swift @@ -13,26 +13,27 @@ class CloseDayInteractor { func close (with tasks: [Task]) { + RCLog("Close day with \(tasks.count) tasks") guard tasks.count > 1 else { return } let interactor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) - // Find if the day ended already + /// Find if the day ended already let endDayTask: Task? = tasks.filter({$0.taskType == .endDay}).first - // If not, end it now + /// If not, end it now if endDayTask == nil { let endDayDate = tasks.last?.endDate ?? Date() let endDayTask = Task(endDate: endDayDate, type: .endDay) - interactor.saveTask(endDayTask, allowSyncing: true) { savedTask in } + interactor.saveTask(endDayTask, allowSyncing: true) { _ in } } - // Save to db only the tasks that are not already saved, like git commits and calendar events + /// Save to db only the tasks that are not already saved, like git commits and calendar events for task in tasks { - if task.objectId == nil { + if !task.isSaved { var task = task RCLog("Unsaved task found \(task)") task.objectId = String.generateId() - interactor.saveTask(task, allowSyncing: true) { savedTask in } + interactor.saveTask(task, allowSyncing: true) { _ in } } } } diff --git a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift index 4f931f4..b47dff7 100644 --- a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift @@ -59,10 +59,17 @@ extension TasksPresenter: TasksPresenterInput { func updateNoTasksState() { if currentTasks.count == 0 { - ui!.showMessage(( - title: "Good morning!", - message: "Ready to start working today?", + if lastSelectedDay.dateStart.isToday() { + ui!.showMessage(( + title: "Good morning!", + message: "Ready to start working today?", + buttonTitle: "Start day")) + } else { + ui!.showMessage(( + title: "Day was not started!", + message: "Do you want to start it now?", buttonTitle: "Start day")) + } } else { appWireframe!.removePlaceholder() } @@ -79,7 +86,14 @@ extension TasksPresenter: TasksPresenterInput { func startDay() { - let task = Task(endDate: Date(), type: .startDay) + /// The day will start + /// 1. current timestamp if day is today + /// 2. start timestamp from settings if day is not today + let settings: Settings = SettingsInteractor().getAppSettings() + let startDate = lastSelectedDay.dateStart.isToday() + ? Date() + : lastSelectedDay.dateStart.dateByKeepingTime(from: settings.settingsTracking.startOfDayTime) + let task = Task(endDate: startDate, type: .startDay) let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) saveInteractor.saveTask(task, allowSyncing: true, completion: { [weak self] savedTask in self?.reloadLastSelectedDay() @@ -115,9 +129,7 @@ extension TasksPresenter: TasksPresenterInput { task.taskType = taskData.taskType let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) - saveInteractor.saveTask(task, allowSyncing: false, completion: { savedTask in - - }) + saveInteractor.saveTask(task, allowSyncing: false, completion: { _ in }) } func insertTask (after row: Int) { diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift index aa332eb..00016a2 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift @@ -61,9 +61,9 @@ class TaskCellPresenter: NSObject { taskType: theTask.taskType ) cell.isDark = AppDelegate.sharedApp().theme.isDark - cell.isEditable = theTask.objectId != nil - cell.isRemovable = theTask.objectId != nil + cell.isEditable = theTask.isSaved + cell.isRemovable = theTask.isSaved cell.isIgnored = theTask.taskType == .lunch || theTask.taskType == .waste - cell.timeToolTip = theTask.objectId != nil ? "Click to edit" : "Item can be edited after the day is closed" + cell.timeToolTip = theTask.isSaved ? "Click to edit" : "Item can be edited after the day is closed" } } diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index f3b7fa7..6f8356d 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -251,8 +251,8 @@ 2863493923B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */; }; 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; - 286BC00421909F85004D4CDD /* CloseDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDay.swift */; }; - 286BC00521909F85004D4CDD /* CloseDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDay.swift */; }; + 286BC00421909F85004D4CDD /* CloseDayInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */; }; + 286BC00521909F85004D4CDD /* CloseDayInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */; }; 287195152022E1E2001C237E /* HookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195142022E1E2001C237E /* HookupPresenter.swift */; }; 287195162022E1E2001C237E /* HookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195142022E1E2001C237E /* HookupPresenter.swift */; }; 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195172022E8BC001C237E /* OutputTableViewDataSource.swift */; }; @@ -673,7 +673,7 @@ 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsListViewController.swift; sourceTree = ""; }; 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsPresenter.swift; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; - 286BC00321909F85004D4CDD /* CloseDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDay.swift; sourceTree = ""; }; + 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDayInteractor.swift; sourceTree = ""; }; 287195142022E1E2001C237E /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; 287195172022E8BC001C237E /* OutputTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputTableViewDataSource.swift; sourceTree = ""; }; 2871951A2022ECF7001C237E /* OutputsScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputsScrollView.swift; sourceTree = ""; }; @@ -1452,7 +1452,7 @@ children = ( 4065D32E1DD3B44200B73201 /* ReadDaysInteractor.swift */, 280D71171ED4CFCB005D2689 /* ReadDaysInteractorTests.swift */, - 286BC00321909F85004D4CDD /* CloseDay.swift */, + 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */, 4065D32F1DD3B44200B73201 /* ReadTasksInteractor.swift */, 4065D3301DD3B44200B73201 /* TaskFinder.swift */, 4065D3311DD3B44200B73201 /* TaskFinderTests.swift */, @@ -2231,7 +2231,7 @@ 28FE18A5207B369800DF796E /* CocoaHookupCell.swift in Sources */, 280D71391ED6043D005D2689 /* ViewControllerStoryboard.swift in Sources */, 2871952B20241B25001C237E /* TrackingView.swift in Sources */, - 286BC00421909F85004D4CDD /* CloseDay.swift in Sources */, + 286BC00421909F85004D4CDD /* CloseDayInteractor.swift in Sources */, 284192DD2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */, 2812F61E2145031B008EE81E /* IAPHelper.swift in Sources */, @@ -2414,7 +2414,7 @@ 28577EAD1E8ADC53002B07FD /* SSettings.swift in Sources */, 28577EB51E8AF08F002B07FD /* SQLiteDB.swift in Sources */, 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, - 286BC00521909F85004D4CDD /* CloseDay.swift in Sources */, + 286BC00521909F85004D4CDD /* CloseDayInteractor.swift in Sources */, 28A928951E9110980022AB55 /* CloudKitRepository+User.swift in Sources */, 4065D3EF1DD3B44200B73201 /* TasksScrollView.swift in Sources */, 4065D3E01DD3B44200B73201 /* SettingsPresenter.swift in Sources */, From 09c815e3961810a0d132fc49c5d8f7598d35b876 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Wed, 8 Jan 2020 10:08:51 +0200 Subject: [PATCH 10/48] Add cli command to start date --- Delivery/macOS-cmd/main.swift | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/Delivery/macOS-cmd/main.swift b/Delivery/macOS-cmd/main.swift index 3eaeb11..f25011c 100644 --- a/Delivery/macOS-cmd/main.swift +++ b/Delivery/macOS-cmd/main.swift @@ -9,7 +9,7 @@ import Foundation var shouldKeepRunning = true let theRL = RunLoop.current -let appVersion = "18.12.12" +let appVersion = "20.01.08" //while shouldKeepRunning && theRL.run(mode: .defaultRunLoopMode, before: .distantFuture) {} enum ArgType { @@ -20,6 +20,7 @@ enum ArgType { enum Command: String { case list = "list" case reports = "reports" + case start = "start" case insert = "insert" case scrum = "scrum" case lunch = "lunch" @@ -32,11 +33,12 @@ enum Command: String { func printHelp() { print("") - print("jirassic \(appVersion) - (c)2018 Imagin soft") + print("jirassic \(appVersion) - (c)2020 Imagin soft") print("") print("Usage:") print(" list [yyyy.mm.dd] If date is missing list tasks from today") print(" reports [yyyy.mm.dd|yyyy.mm] [hours per day] If date is missing, list reports from today") + print(" start") print(" insert -nr -notes -duration ") print(" scrum|lunch|meeting|waste|learning|coderev Duration in minutes") print("") @@ -128,7 +130,7 @@ func reports (forMonth date: Date, targetHoursInDay: Double?) { let tasks = reader.tasksInMonth(date) let monthReportsInteractor = CreateMonthReport() let duration: Double? = targetHoursInDay != nil ? targetHoursInDay!.hoursToSec : nil - let result = monthReportsInteractor.reports(fromTasks: tasks, targetHoursInDay: duration) + let result = monthReportsInteractor.reports(fromTasks: tasks, targetHoursInDay: duration, roundHours: true) if result.byTasks.count > 0 { let joined = monthReportsInteractor.joinReports(result.byTasks) print(joined.notes) @@ -204,20 +206,14 @@ func insert (taskType: Command, arguments: [String]) { var task: Task? switch taskType { + case .start: task = Task(endDate: Date(), type: .startDay) case .scrum: task = Task(endDate: Date(), type: .scrum) - break case .lunch: task = Task(endDate: Date(), type: .lunch) - break case .meeting: task = Task(endDate: Date(), type: .meeting) - break case .waste: task = Task(endDate: Date(), type: .waste) - break case .learning: task = Task(endDate: Date(), type: .learning) - break case .coderev: task = Task(endDate: Date(), type: .coderev) - break - default: - return + default: return } if let duration = arguments.first { @@ -267,12 +263,12 @@ if let command = Command(rawValue: commandStr) { } reports (forDay: date, targetHoursInDay: duration) break + case .start: + insert (taskType: command, arguments: []) case .insert: insertIssue (arguments: arguments) - break case .scrum, .lunch, .meeting, .waste, .learning, .coderev: insert (taskType: command, arguments: arguments) - break case .version: print(appVersion) } From 0b5902b7f6271829db9d31719fa461a9a0fd3b7d Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Wed, 8 Jan 2020 11:22:48 +0200 Subject: [PATCH 11/48] Refactor and add end day command --- Delivery/macOS-cmd/main.swift | 58 +++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/Delivery/macOS-cmd/main.swift b/Delivery/macOS-cmd/main.swift index f25011c..c9e9a1e 100644 --- a/Delivery/macOS-cmd/main.swift +++ b/Delivery/macOS-cmd/main.swift @@ -21,6 +21,7 @@ enum Command: String { case list = "list" case reports = "reports" case start = "start" + case end = "end" case insert = "insert" case scrum = "scrum" case lunch = "lunch" @@ -38,7 +39,7 @@ func printHelp() { print("Usage:") print(" list [yyyy.mm.dd] If date is missing list tasks from today") print(" reports [yyyy.mm.dd|yyyy.mm] [hours per day] If date is missing, list reports from today") - print(" start") + print(" start|end Current date is used") print(" insert -nr -notes -duration ") print(" scrum|lunch|meeting|waste|learning|coderev Duration in minutes") print("") @@ -76,7 +77,7 @@ guard arguments.count > 0 else { exit(0) } -func dayStarted() -> Bool { +func isDayStarted() -> Bool { let currentTasks = reader.tasksInDay(Date()) guard currentTasks.count > 0 else { @@ -102,7 +103,7 @@ func list (dayOnDate date: Date) { print("") } -func reports (forDay date: Date, targetHoursInDay: Double?) { +func listReports (forDay date: Date, targetHoursInDay: Double?) { print("") let tasks = reader.tasksInDay(date) @@ -121,7 +122,7 @@ func reports (forDay date: Date, targetHoursInDay: Double?) { print("") } -func reports (forMonth date: Date, targetHoursInDay: Double?) { +func listReports (forMonth date: Date, targetHoursInDay: Double?) { print("") print("Reports for the month of \(date.startOfMonth().MMMMdd()) - \(date.endOfMonth().MMMMdd()) \(date.YYYY())") @@ -144,7 +145,7 @@ func reports (forMonth date: Date, targetHoursInDay: Double?) { func insertIssue (arguments: [String]) { - guard dayStarted() else { + guard isDayStarted() else { return } @@ -200,13 +201,12 @@ func insertIssue (arguments: [String]) { func insert (taskType: Command, arguments: [String]) { - guard dayStarted() else { + guard isDayStarted() else { return } var task: Task? switch taskType { - case .start: task = Task(endDate: Date(), type: .startDay) case .scrum: task = Task(endDate: Date(), type: .scrum) case .lunch: task = Task(endDate: Date(), type: .lunch) case .meeting: task = Task(endDate: Date(), type: .meeting) @@ -232,6 +232,24 @@ func insert (taskType: Command, arguments: [String]) { print(taskType.rawValue.capitalized + " saved") } +func startDay() { + guard !isDayStarted() else { + let tasks = reader.tasksInDay(Date()) + let startDate = tasks.filter({$0.taskType == .startDay}).first?.endDate + print("Day was already started at \(startDate?.HHmm() ?? "...")") + return + } + let task = Task(endDate: Date(), type: .startDay) + let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: nil) + saveInteractor.saveTask(task, allowSyncing: false, completion: { _ in }) + + print("Day was started") +} + +func closeDay() { + print("Day cannot be closed because git commits and calendars will be lost, use the app to close the day!") +} + let commandStr = arguments.remove(at: 0) if let command = Command(rawValue: commandStr) { switch command { @@ -242,7 +260,7 @@ if let command = Command(rawValue: commandStr) { date = Date(YYYYMMddString: arg) } list (dayOnDate: date) - break + case .reports: var date = Date() var duration: Double? = nil @@ -250,21 +268,23 @@ if let command = Command(rawValue: commandStr) { duration = Double(arguments[1]) } if arguments.count > 0 { - let arg = arguments.remove(at: 0) - if arg.components(separatedBy: ".").count == 2 { - // We have only year and month. Add a day and call the month reports - date = Date(YYYYMMddString: "\(arg).01") - reports (forMonth: date, targetHoursInDay: duration) + let dateString = arguments.remove(at: 0) + if dateString.components(separatedBy: ".").count == 2 { + /// We have only year.month. Add a day and list the month reports + date = Date(YYYYMMddString: "\(dateString).01") + listReports (forMonth: date, targetHoursInDay: duration) break - } else if arg.components(separatedBy: ".").count == 3 { - // We have year, month and day - date = Date(YYYYMMddString: arg) + } else if dateString.components(separatedBy: ".").count == 3 { + /// We have year.month.day + date = Date(YYYYMMddString: dateString) } } - reports (forDay: date, targetHoursInDay: duration) - break + listReports (forDay: date, targetHoursInDay: duration) + case .start: - insert (taskType: command, arguments: []) + startDay() + case .end: + closeDay() case .insert: insertIssue (arguments: arguments) case .scrum, .lunch, .meeting, .waste, .learning, .coderev: From 4eb483e8192034e5e9a9c1f50a6233f98b848e28 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Wed, 8 Jan 2020 15:26:57 +0200 Subject: [PATCH 12/48] Add CopyReportCell --- .../Screens/Reports/ReportsDataSource.swift | 20 ++++- .../cells/CopyReportCell/CopyReportCell.swift | 47 +++++++++++- .../cells/HeaderView/ReportsHeaderView.swift | 75 ------------------- .../cells/HeaderView/ReportsHeaderView.xib | 69 ----------------- Jirassic.xcodeproj/project.pbxproj | 20 ----- 5 files changed, 60 insertions(+), 171 deletions(-) delete mode 100644 Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.swift delete mode 100644 Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.xib diff --git a/Delivery/macOS/Screens/Reports/ReportsDataSource.swift b/Delivery/macOS/Screens/Reports/ReportsDataSource.swift index 32cb4be..662f521 100644 --- a/Delivery/macOS/Screens/Reports/ReportsDataSource.swift +++ b/Delivery/macOS/Screens/Reports/ReportsDataSource.swift @@ -17,6 +17,7 @@ class ReportsDataSource: NSObject, TasksAndReportsDataSource { } ReportCell.register(in: tableView) CopyReportCell.register(in: tableView) + ClosedDayCell.register(in: tableView) } } var didClickAddRow: ((_ row: Int) -> Void)? @@ -45,11 +46,15 @@ class ReportsDataSource: NSObject, TasksAndReportsDataSource { extension ReportsDataSource: NSTableViewDataSource { func numberOfRows (in aTableView: NSTableView) -> Int { - return reports.count + return reports.count > 0 ? reports.count + 1 : 0 } func tableView (_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { - + print("\(row) < \(reports.count)") + guard row < reports.count else { + print("return 100") + return 100 + } if #available(OSX 10.13, *) { // This version of osx supports cell autoresizing return CGFloat(50) @@ -69,7 +74,16 @@ extension ReportsDataSource: NSTableViewDataSource { extension ReportsDataSource: NSTableViewDelegate { func tableView (_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { - + + guard row < reports.count else { + return ClosedDayCell.instantiate(in: tableView) + let cell = CopyReportCell.instantiate(in: tableView) + cell.didClickCopyAll = { isHtml in + //self.didClickCloseDay?(self.tasks) + } + return cell + } + let theData = reports[row] let cell: CellProtocol = ReportCell.instantiate(in: self.tableView) ReportCellPresenter(cell: cell).present(theReport: theData) diff --git a/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.swift b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.swift index 31c81e0..9b7ee84 100644 --- a/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.swift +++ b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.swift @@ -7,14 +7,37 @@ // import Cocoa +import RCPreferences class CopyReportCell: NSTableRowView { @IBOutlet private var butCopyAll: NSButton! @IBOutlet private var butCopyAsHtml: NSButton! @IBOutlet private var totalDaysTextField: NSTextField! + @IBOutlet private var backgroundView: NSVisualEffectView! + @IBOutlet private var butPercents: NSButton! + @IBOutlet private var butRound: NSButton! + @IBOutlet private var totalTimeTextField: NSTextField! + internal let pref = RCPreferences() var didClickCopyAll: ((Bool) -> Void)? - + var didChangeSettings: (() -> Void)? + // In hours + var workedTime: String { + get { + return "" + } + set { + totalTimeTextField.stringValue = newValue + } + } + var workdayTime: Double { + get { + return 0.0 + } + set { + butRound.title = "Round to \(newValue) hours" + } + } var numberOfDays: Int { get { return 0 @@ -27,18 +50,34 @@ class CopyReportCell: NSTableRowView { override func awakeFromNib() { super.awakeFromNib() -// butCopyAsHtml.state = pref.bool(.copyWorklogsAsHtml) ? .on : .off + butCopyAsHtml.state = pref.bool(.copyWorklogsAsHtml) ? .on : .off butCopyAsHtml.toolTip = "This can be set in 'Settings/Tracking/Working between'" + + butPercents.title = "Show time in percents" + butPercents.state = pref.bool(.usePercents) ? .on : .off + + butRound.state = pref.bool(.enableRoundingDay) ? .on : .off + butRound.toolTip = "This can be set in 'Settings/Tracking/Working between'" } } extension CopyReportCell { @IBAction func handleCopyAllButton (_ sender: NSButton) { -// didClickCopyAll?(pref.bool(.copyWorklogsAsHtml)) + didClickCopyAll?(pref.bool(.copyWorklogsAsHtml)) } @IBAction func handleHtmlButton (_ sender: NSButton) { -// pref.set(sender.state == .on, forKey: .copyWorklogsAsHtml) + pref.set(sender.state == .on, forKey: .copyWorklogsAsHtml) + } + + @IBAction func handleRoundButton (_ sender: NSButton) { + pref.set(sender.state == .on, forKey: .enableRoundingDay) + //didChangeSettings?() + } + + @IBAction func handlePercentsButton (_ sender: NSButton) { + pref.set(sender.state == .on, forKey: .usePercents) + //didChangeSettings?() } } diff --git a/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.swift b/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.swift deleted file mode 100644 index dd0a162..0000000 --- a/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// ReportsHeaderView.swift -// Jirassic -// -// Created by Cristian Baluta on 19/02/2017. -// Copyright © 2017 Imagin soft. All rights reserved. -// - -import Cocoa -import RCPreferences - -class ReportsHeaderView: NSTableHeaderView { - - @IBOutlet private var backgroundView: NSVisualEffectView! - @IBOutlet private var butPercents: NSButton! - @IBOutlet private var butRound: NSButton! - @IBOutlet private var totalTimeTextField: NSTextField! - internal let pref = RCPreferences() - - var didChangeSettings: (() -> Void)? - // In hours - var workedTime: String { - get { - return "" - } - set { - totalTimeTextField.stringValue = newValue - } - } - var workdayTime: Double { - get { - return 0.0 - } - set { - butRound.title = "Round to \(newValue) hours" - } - } - - override func awakeFromNib() { - super.awakeFromNib() - - butPercents.title = "Show time in percents" - butPercents.state = pref.bool(.usePercents) ? .on : .off - - butRound.state = pref.bool(.enableRoundingDay) ? .on : .off - butRound.toolTip = "This can be set in 'Settings/Tracking/Working between'" - - if #available(OSX 10.14, *) { - // In OS14 there is already a default blurry background - backgroundView.isHidden = true - } - } - - // Overriding this in OS14 removes default blurry background and the custom one adds ugly edges to buttons -// override func draw (_ dirtyRect: NSRect) { -// } - - override func headerRect(ofColumn column: Int) -> NSRect { - // This will prevent for a label to appear in the middle of the header - return NSRect.zero - } -} - -extension ReportsHeaderView { - - @IBAction func handleRoundButton (_ sender: NSButton) { - pref.set(sender.state == .on, forKey: .enableRoundingDay) - didChangeSettings?() - } - - @IBAction func handlePercentsButton (_ sender: NSButton) { - pref.set(sender.state == .on, forKey: .usePercents) - didChangeSettings?() - } -} diff --git a/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.xib b/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.xib deleted file mode 100644 index 6c6f3db..0000000 --- a/Delivery/macOS/Screens/Reports/cells/HeaderView/ReportsHeaderView.xib +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 6f8356d..64375aa 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -178,8 +178,6 @@ 2845B14E206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B146206AE3A8006EFB3B /* ReportCellPresenter.swift */; }; 2845B14F206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */; }; 2845B150206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */; }; - 2845B151206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */; }; - 2845B152206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */; }; 2845B160206AE468006EFB3B /* TaskCell_.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell_.swift */; }; 2845B161206AE468006EFB3B /* TaskCell_.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell_.swift */; }; 2845B164206AE468006EFB3B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */; }; @@ -362,8 +360,6 @@ 28AA500D1EDCD51300AAF03D /* CTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38E1DD3B44200B73201 /* CTask.swift */; }; 28AA500E1EDCD51300AAF03D /* CUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38F1DD3B44200B73201 /* CUser.swift */; }; 28AFE7311E9A594500BAAD8C /* UserDefaults+token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */; }; - 28B116B821AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */; }; - 28B116B921AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */; }; 28B116BB21AE6179004ACE01 /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */; }; 28B116BC21AE6179004ACE01 /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */; }; 28C6A38421D359E60036DB29 /* RemoveDuplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */; }; @@ -637,7 +633,6 @@ 2845B145206AE3A8006EFB3B /* ReportCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReportCell.xib; sourceTree = ""; }; 2845B146206AE3A8006EFB3B /* ReportCellPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportCellPresenter.swift; sourceTree = ""; }; 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportsDataSource.swift; sourceTree = ""; }; - 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportsHeaderView.swift; sourceTree = ""; }; 2845B155206AE467006EFB3B /* TaskCell_.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell_.swift; sourceTree = ""; }; 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCellPresenter.swift; sourceTree = ""; }; 2845B15C206AE467006EFB3B /* TaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell.swift; sourceTree = ""; }; @@ -724,7 +719,6 @@ 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+User.swift"; sourceTree = ""; }; 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Settings.swift"; sourceTree = ""; }; 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+token.swift"; sourceTree = ""; }; - 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReportsHeaderView.xib; sourceTree = ""; }; 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CopyReportCell.xib; sourceTree = ""; }; 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveDuplicate.swift; sourceTree = ""; }; 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+uploadToken.swift"; sourceTree = ""; }; @@ -981,7 +975,6 @@ 2845B141206AE2A4006EFB3B /* cells */ = { isa = PBXGroup; children = ( - 287B943B21A690C100FFC4A5 /* HeaderView */, 2845B143206AE3A8006EFB3B /* ReportCell */, 2898D3A62181905600CF5AD4 /* CopyReportCell */, ); @@ -1160,15 +1153,6 @@ path = Onboarding; sourceTree = ""; }; - 287B943B21A690C100FFC4A5 /* HeaderView */ = { - isa = PBXGroup; - children = ( - 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */, - 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */, - ); - path = HeaderView; - sourceTree = ""; - }; 2892B2961F0949D70085BAC2 /* Jira */ = { isa = PBXGroup; children = ( @@ -2073,7 +2057,6 @@ 2863492523B3A966009F81D7 /* ClosedDayCell.xib in Resources */, 56D90D0120876F4900F24442 /* WizardJiraView.xib in Resources */, 280C1D431ED74EF900C126A1 /* BrowserSupport.scpt in Resources */, - 28B116B821AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */, 2871952820241A6C001C237E /* TrackingView.xib in Resources */, 28FB265420224E5B00AEA38D /* JiraTempoCell.xib in Resources */, 2845B14B206AE3A8006EFB3B /* ReportCell.xib in Resources */, @@ -2113,7 +2096,6 @@ 5685C7681DE8721100CA545E /* Login.storyboard in Resources */, 2863492A23B5EF87009F81D7 /* Reports.storyboard in Resources */, 569C4C71202319DE0049FBF1 /* ShellCell.xib in Resources */, - 28B116B921AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */, 2892E80C208D9B42004E5298 /* InputsScrollView.xib in Resources */, 2863490F23B29979009F81D7 /* Main.storyboard in Resources */, 287B358B20FBAFD60022F43E /* WizardCalendarView.xib in Resources */, @@ -2317,7 +2299,6 @@ 28A283942037874600DDCB63 /* ModuleGitLogs.swift in Sources */, 280D717A1ED6043D005D2689 /* TasksViewController.swift in Sources */, 288BB6842087157F00CF720A /* WizardAppleScriptView.swift in Sources */, - 2845B151206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */, 28A283AB203A93AB00DDCB63 /* GitBranchParser.swift in Sources */, 280D717B1ED6043D005D2689 /* TasksPresenter.swift in Sources */, 2818848121A4A2F800B33B9C /* Jirassic.xcdatamodeld in Sources */, @@ -2558,7 +2539,6 @@ 288BB67F2085252900CF720A /* WizardViewController.swift in Sources */, 2892B2A81F094C800085BAC2 /* JWorkAttribute.swift in Sources */, 5685C76C1DE8724400CA545E /* AccountViewController.swift in Sources */, - 2845B152206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */, 2892B2991F094A170085BAC2 /* JiraRepository.swift in Sources */, 28A2839B20382BBB00DDCB63 /* GitCommit.swift in Sources */, 4065D3A91DD3B44200B73201 /* Conversions.swift in Sources */, From d3c6b482f1f093629f95333e36cc6561ef8556c3 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Thu, 9 Jan 2020 17:18:27 +0200 Subject: [PATCH 13/48] Remove background in cell --- .../Reports/cells/CopyReportCell/CopyReportCell.xib | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.xib b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.xib index ed3d18c..ecfa3d2 100644 --- a/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.xib +++ b/Delivery/macOS/Screens/Reports/cells/CopyReportCell/CopyReportCell.xib @@ -13,9 +13,6 @@ - - - - - - - + + + - - - - - - - - - - + - - - - - - - - + + + + + + + + + - - + - + - + diff --git a/Delivery/macOS/Screens/Tasks/DataSource.swift b/Delivery/macOS/Screens/Tasks/DataSource.swift index 25346f4..e25af83 100644 --- a/Delivery/macOS/Screens/Tasks/DataSource.swift +++ b/Delivery/macOS/Screens/Tasks/DataSource.swift @@ -9,14 +9,20 @@ import Cocoa protocol TasksAndReportsDataSource { + var tableView: NSTableView! {get set} + func addTask (_ task: Task, at row: Int) func removeTask (at row: Int) + // Used by tasks var didClickAddRow: ((_ row: Int) -> Void)? {get set} var didClickRemoveRow: ((_ row: Int) -> Void)? {get set} var didClickCloseDay: ((_ tasks: [Task]) -> Void)? {get set} var didClickSaveWorklogs: (() -> Void)? {get set} var didClickSetupJira: (() -> Void)? {get set} + // Used by reports + var didClickCopyMonthlyReport: ((_ asHtml: Bool) -> Void)? {get set} + var didChangeSettings: (() -> Void)? {get set} } typealias DataSource = NSTableViewDataSource & NSTableViewDelegate & TasksAndReportsDataSource diff --git a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift index 6452ed9..54d4d89 100644 --- a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift +++ b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift @@ -20,6 +20,8 @@ class TasksDataSource: NSObject, TasksAndReportsDataSource { var didClickCloseDay: ((_ tasks: [Task]) -> Void)? var didClickSaveWorklogs: (() -> Void)? var didClickSetupJira: (() -> Void)? + var didClickCopyMonthlyReport: ((_ asHtml: Bool) -> Void)? + var didChangeSettings: (() -> Void)? internal var tableView: NSTableView! { didSet { @@ -49,6 +51,7 @@ class TasksDataSource: NSObject, TasksAndReportsDataSource { extension TasksDataSource: NSTableViewDataSource { func numberOfRows (in aTableView: NSTableView) -> Int { + // Add an extra row at the end for 'Add task' and 'Close day' buttons return tasks.count > 0 ? tasks.count + 1 : 0 } diff --git a/Delivery/macOS/Screens/Tasks/TasksScrollView.swift b/Delivery/macOS/Screens/Tasks/TasksScrollView.swift index ac683f3..784ab75 100644 --- a/Delivery/macOS/Screens/Tasks/TasksScrollView.swift +++ b/Delivery/macOS/Screens/Tasks/TasksScrollView.swift @@ -13,17 +13,7 @@ class TasksScrollView: NSScrollView { private var tableView: NSTableView! private var dataSource: DataSource! - private var listType: ListType! - private let pref = RCPreferences() - - var didClickAddRow: ((_ row: Int, _ rect: CGRect?) -> Void)? - var didClickRemoveRow: ((_ row: Int) -> Void)? - var didClickCloseDay: ((_ tasks: [Task], _ shouldSaveToJira: Bool) -> Void)? - var didClickSaveWorklogs: (() -> Void)? - var didClickSetupJira: (() -> Void)? -// var didClickCopyMonthlyReport: ((_ asHtml: Bool) -> Void)? -// var didChangeSettings: (() -> Void)? - + required init?(coder: NSCoder) { super.init(coder: coder) } @@ -33,10 +23,9 @@ class TasksScrollView: NSScrollView { setupTableView() } - convenience init (dataSource: DataSource, listType: ListType) { + convenience init (dataSource: DataSource) { self.init(frame: NSRect.zero) self.setupTableView() - self.listType = listType reloadDataSource(dataSource) } @@ -49,22 +38,6 @@ class TasksScrollView: NSScrollView { self.dataSource.tableView = tableView tableView.dataSource = self.dataSource tableView.delegate = self.dataSource - -// self.dataSource.didClickAddRow = { [weak self] row in -// self?.didClickAddRow!(row, nil) -// } -// self.dataSource.didClickRemoveRow = { [weak self] row in -// self?.didClickRemoveRow!(row) -// } -// self.dataSource.didClickCloseDay = { [weak self] tasks in -// self?.didClickCloseDay!(tasks, false) -// } -// self.dataSource.didClickSaveWorklogs = { [weak self] in -// self?.didClickSaveWorklogs!() -// } -// self.dataSource.didClickSetupJira = { [weak self] in -// self?.didClickSetupJira!() -// } } private func setupTableView() { @@ -80,7 +53,7 @@ class TasksScrollView: NSScrollView { tableView.headerView = nil let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: "taskColumn")) - column.width = 400 + column.width = self.frame.size.width tableView.addTableColumn(column) self.documentView = tableView! @@ -103,8 +76,4 @@ class TasksScrollView: NSScrollView { func frameOfCell (atRow row: Int) -> NSRect { return tableView.frameOfCell(atColumn: 0, row: row) } - - func view() -> NSTableView { - return self.tableView - } } diff --git a/Delivery/macOS/Screens/Tasks/TasksViewController.swift b/Delivery/macOS/Screens/Tasks/TasksViewController.swift index d9750d0..f351549 100644 --- a/Delivery/macOS/Screens/Tasks/TasksViewController.swift +++ b/Delivery/macOS/Screens/Tasks/TasksViewController.swift @@ -74,7 +74,7 @@ extension TasksViewController: TasksPresenterOutput { self?.appWireframe!.flipToSettingsController() } - let scrollView = TasksScrollView(dataSource: dataSource, listType: .tasks) + let scrollView = TasksScrollView(dataSource: dataSource) self.view.addSubview(scrollView) scrollView.constrainToSuperview() // scrollView.didClickAddRow = { [weak self] (row, rect) in From 3e95844cfbc5a314e0e12f5bde73e98f581f27f4 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Fri, 10 Jan 2020 17:40:29 +0200 Subject: [PATCH 15/48] Refresh footer when a row is removed This will change the footer if last row was endDay and deleted --- .../macOS/Screens/Tasks/TasksPresenter.swift | 8 ++--- .../macOS/Screens/Tasks/TasksScrollView.swift | 5 +++ .../Screens/Tasks/TasksViewController.swift | 33 ++++--------------- .../Screens/Worklogs/WorklogsPresenter.swift | 17 +++++----- .../Worklogs/WorklogsViewController.swift | 3 +- 5 files changed, 26 insertions(+), 40 deletions(-) diff --git a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift index b47dff7..21a48f9 100644 --- a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift @@ -27,9 +27,9 @@ protocol TasksPresenterOutput: class { func showMessage (_ message: MessageViewModel) func showTasks (_ tasks: [Task]) func presentNewTaskController (date: Date) - func presentEndDayController (date: Date, tasks: [Task]) + func showWorklogs (date: Date, tasks: [Task]) func removeTasks() - func removeEndDayController() + func removeWorklogs() } class TasksPresenter { @@ -51,7 +51,7 @@ extension TasksPresenter: TasksPresenterInput { func reloadTasksOnDay (_ day: Day) { lastSelectedDay = day ui!.removeTasks() - ui!.removeEndDayController() + ui!.removeWorklogs() ui!.showLoadingIndicator(true) interactor!.reloadTasks(inDay: day) } @@ -116,7 +116,7 @@ extension TasksPresenter: TasksPresenterInput { func didClickSaveWorklogs() { // Reload data will be called after save with success ui!.removeTasks() - ui!.presentEndDayController(date: lastSelectedDay.dateStart, tasks: currentTasks) + ui!.showWorklogs(date: lastSelectedDay.dateStart, tasks: currentTasks) } func insertTaskWithData (_ taskData: TaskCreationData) { diff --git a/Delivery/macOS/Screens/Tasks/TasksScrollView.swift b/Delivery/macOS/Screens/Tasks/TasksScrollView.swift index 784ab75..83c7c65 100644 --- a/Delivery/macOS/Screens/Tasks/TasksScrollView.swift +++ b/Delivery/macOS/Screens/Tasks/TasksScrollView.swift @@ -32,6 +32,11 @@ class TasksScrollView: NSScrollView { func reloadData() { tableView!.reloadData() } + + func reloadFooter() { + tableView!.reloadData(forRowIndexes: IndexSet(integer: tableView.numberOfRows - 1), + columnIndexes: IndexSet(integer: 0)) + } func reloadDataSource (_ dataSource: DataSource) { self.dataSource = dataSource diff --git a/Delivery/macOS/Screens/Tasks/TasksViewController.swift b/Delivery/macOS/Screens/Tasks/TasksViewController.swift index f351549..7d2faa4 100644 --- a/Delivery/macOS/Screens/Tasks/TasksViewController.swift +++ b/Delivery/macOS/Screens/Tasks/TasksViewController.swift @@ -15,7 +15,7 @@ class TasksViewController: NSViewController { private var tasksScrollView: TasksScrollView? private var worklogsViewController: WorklogsViewController? /// Property to keep a reference to the active cell rect - private var rectToDisplayPopoverAt: NSRect? + private var activeCellRect: NSRect? private var activePopover: NSPopover? weak var appWireframe: AppWireframe? @@ -53,7 +53,7 @@ extension TasksViewController: TasksPresenterOutput { dataSource.didClickAddRow = { [weak self] row in RCLogO("Add item after row \(row)") if row >= 0 { - self?.rectToDisplayPopoverAt = self?.tasksScrollView?.frameOfCell(atRow: row) + self?.activeCellRect = self?.tasksScrollView?.frameOfCell(atRow: row) self?.presenter!.insertTask(after: row) } } @@ -62,6 +62,7 @@ extension TasksViewController: TasksPresenterOutput { if row >= 0 { self?.presenter!.removeTask(at: row) self?.tasksScrollView!.removeTask(at: row) + self?.tasksScrollView!.reloadFooter() } } dataSource.didClickCloseDay = { [weak self] tasks in @@ -77,26 +78,6 @@ extension TasksViewController: TasksPresenterOutput { let scrollView = TasksScrollView(dataSource: dataSource) self.view.addSubview(scrollView) scrollView.constrainToSuperview() -// scrollView.didClickAddRow = { [weak self] (row, rect) in -// RCLogO("Add item after row \(row)") -// if row >= 0 { -// self?.rectToDisplayPopoverAt = rect ?? self?.tasksScrollView?.frameOfCell(atRow: row) -// self?.presenter!.insertTask(after: row) -// } -// } -// scrollView.didClickRemoveRow = { [weak self] row in -// RCLogO("Remove item at row \(row)") -// if row >= 0 { -// self?.presenter!.removeTask(at: row) -// self?.tasksScrollView!.removeTask(at: row) -// } -// } -// scrollView.didClickCloseDay = { [weak self] (tasks, shouldSaveToJira) in -// self?.presenter!.closeDay(shouldSaveToJira: shouldSaveToJira) -// } -// scrollView.didClickSaveWorklogs = { [weak self] in -// -// } scrollView.reloadData() tasksScrollView = scrollView @@ -131,7 +112,7 @@ extension TasksViewController: TasksPresenterOutput { } } popover.contentViewController = controller - popover.show(relativeTo: rectToDisplayPopoverAt!, + popover.show(relativeTo: activeCellRect!, of: self.view, preferredEdge: NSRectEdge.maxY) // Add data after popover is presented @@ -140,7 +121,7 @@ extension TasksViewController: TasksPresenterOutput { activePopover = popover } - func presentEndDayController (date: Date, tasks: [Task]) { + func showWorklogs (date: Date, tasks: [Task]) { let controller = appWireframe!.createWorklogsViewController() controller.date = date @@ -151,20 +132,18 @@ extension TasksViewController: TasksPresenterOutput { guard let self = self else { return } - self.removeEndDayController() self.presenter!.reloadLastSelectedDay() } controller.onCancel = { [weak self] in guard let self = self else { return } - self.removeEndDayController() self.presenter!.reloadLastSelectedDay() } worklogsViewController = controller } - func removeEndDayController() { + func removeWorklogs() { if worklogsViewController != nil { worklogsViewController?.removeFromSuperview() diff --git a/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift b/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift index 40f75b9..cbefcf2 100644 --- a/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift +++ b/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift @@ -11,7 +11,7 @@ import RCPreferences protocol WorklogsPresenterInput: class { func setup (date: Date, tasks: [Task]) - func save (worklog: String) + func save (worklog: String, duration: String) func enableRounding (_ enabled: Bool) } @@ -53,7 +53,8 @@ extension WorklogsPresenter: WorklogsPresenterInput { let settings = SettingsInteractor().getAppSettings() workdayLength = TimeInteractor(settings: settings).workingDayLength() workedLength = StatisticsInteractor().duration(of: reports) - let duration = (pref.bool(.enableRoundingDay) ? workdayLength : workedLength).secToPercent + let isRoundingEnabled = pref.bool(.enableRoundingDay) + let duration = (isRoundingEnabled ? workdayLength : workedLength).secToPercent userInterface!.showDuration(duration) userInterface!.showWorklog(message) @@ -66,15 +67,15 @@ extension WorklogsPresenter: WorklogsPresenterInput { title: "Round worklogs duration to \(String(describing: workdayLength)) hours") } - func save (worklog: String) { + func save (worklog: String, duration: String) { userInterface!.showJiraMessage("", isError: false) - // Save to jira tempo - let isRoundingEnabled = pref.bool(.enableRoundingDay) - + guard let d = Double(duration) else { + return + } userInterface!.showProgressIndicator(true) - let duration = isRoundingEnabled ? workdayLength : workedLength - moduleJira.postWorklog(worklog: worklog, duration: duration, date: date!, success: { [weak self] in + /// Save to jira tempo + moduleJira.postWorklog(worklog: worklog, duration: d, date: date!, success: { [weak self] in DispatchQueue.main.async { if let userInterface = self?.userInterface { diff --git a/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift b/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift index 6f86b86..295836d 100644 --- a/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift +++ b/Delivery/macOS/Screens/Worklogs/WorklogsViewController.swift @@ -40,7 +40,8 @@ class WorklogsViewController: NSViewController { } @IBAction func handleSaveButton (_ sender: NSButton) { - presenter!.save(worklog: worklogTextView.string) + let duration = durationTextField.stringValue + presenter!.save(worklog: worklogTextView.string, duration: duration) } @IBAction func handleRoundButton (_ sender: NSButton) { From 1846bda76a463ab667d55b26a87345616b1ddc7a Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Sun, 12 Jan 2020 22:46:34 +0200 Subject: [PATCH 16/48] Add projects db --- App/Projects/ProjectInteractor.swift | 66 +++++++ App/Projects/ReadProjectsInteractor.swift | 18 ++ .../ProjectDetailsViewController.swift | 27 ++- .../Screens/Projects/Projects.storyboard | 17 +- .../Screens/Projects/ProjectsInteractor.swift | 10 +- .../CloudKitRepository+Projects.swift | 179 ++++++++++++++++++ External/CloudKit/CloudKitRepository.swift | 2 +- External/CoreData/CProject.swift | 18 ++ .../CoreDataRepository+Projects.swift | 28 +++ External/Repository.swift | 11 +- External/sqlite/SProject.swift | 28 +++ .../sqlite/SqliteRepository+Projects.swift | 62 ++++++ Jirassic.xcodeproj/project.pbxproj | 108 ++++++++--- 13 files changed, 534 insertions(+), 40 deletions(-) create mode 100644 App/Projects/ProjectInteractor.swift create mode 100644 App/Projects/ReadProjectsInteractor.swift create mode 100644 External/CloudKit/CloudKitRepository+Projects.swift create mode 100644 External/CoreData/CProject.swift create mode 100644 External/CoreData/CoreDataRepository+Projects.swift create mode 100644 External/sqlite/SProject.swift create mode 100644 External/sqlite/SqliteRepository+Projects.swift diff --git a/App/Projects/ProjectInteractor.swift b/App/Projects/ProjectInteractor.swift new file mode 100644 index 0000000..a830ba0 --- /dev/null +++ b/App/Projects/ProjectInteractor.swift @@ -0,0 +1,66 @@ +// +// ProjectInteractor.swift +// Jirassic +// +// Created by Cristian Baluta on 12/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +class ProjectInteractor: RepositoryInteractor { + +// func queryTask (withId objectId: String) -> Task? { +// return repository.queryTask(withId: objectId) +// } + + func saveProject (_ project: Project, allowSyncing: Bool, completion: @escaping (_ savedProject: Project?) -> Void) { + + guard project.objectId != nil else { + fatalError("Cannot save a task without objectId") + } + var project = project + project.lastModifiedDate = nil + + self.repository.saveProject(project, completion: { [weak self] savedProject in + guard let localProject = savedProject else { + completion(nil) + return + } + if allowSyncing { + // We don't care if the task doesn't get saved to server + self?.syncProject(localProject, completion: { project in }) + } + completion(localProject) + }) + } + + func deleteProject (_ project: Project) { + + guard project.objectId != nil else { + fatalError("Cannot delete a task without objectId") + } + self.repository.deleteProject(project, permanently: false, completion: { success in + #if !CMD + if let remoteRepository = self.remoteRepository { + let sync = RCSync(localRepository: self.repository, remoteRepository: remoteRepository) +// sync.deleteProject(project, completion: { success in }) + } + #endif + }) + } + + private func syncProject (_ project: Project, completion: @escaping (_ uploadedTask: Task) -> Void) { + + #if !CMD + if let remoteRepository = self.remoteRepository { + let sync = RCSync(localRepository: self.repository, remoteRepository: remoteRepository) +// sync.uploadProject(project, completion: { (success) in +// DispatchQueue.main.async { +// completion(project) +// } +// }) + } + #endif + } +} diff --git a/App/Projects/ReadProjectsInteractor.swift b/App/Projects/ReadProjectsInteractor.swift new file mode 100644 index 0000000..d49b186 --- /dev/null +++ b/App/Projects/ReadProjectsInteractor.swift @@ -0,0 +1,18 @@ +// +// ReadReportsInteractor.swift +// Jirassic +// +// Created by Cristian Baluta on 12/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import RCLog + +class ReadProjectsInteractor: RepositoryInteractor { + + // Return a list of all projects + func allProjects() -> [Project] { + return self.repository.projects() + } +} diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 9c294cf..6c6406e 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -10,6 +10,7 @@ import Cocoa class ProjectDetailsViewController: NSViewController { + @IBOutlet private var butCredentials: NSButton! @IBOutlet private var baseUrlTextField: NSTextField! @IBOutlet private var userTextField: NSTextField! @IBOutlet private var passwordTextField: NSTextField! @@ -19,7 +20,9 @@ class ProjectDetailsViewController: NSViewController { @IBOutlet private var progressIndicator: NSProgressIndicator! @IBOutlet private var emailsTextField: NSTextField! @IBOutlet private var pathsTextField: NSTextField! + @IBOutlet private var taskNumberPrefixTextField: NSTextField! @IBOutlet private var butPick: NSButton! + @IBOutlet private var butDelete: NSButton! var project: Project? { didSet { @@ -51,8 +54,20 @@ class ProjectDetailsViewController: NSViewController { func reloadData() { - baseUrlTextField.stringValue = project?.jiraBaseUrl ?? "" - userTextField.stringValue = project?.jiraUser ?? "" + guard let project = project else { + return + } + butCredentials.state = project.jiraBaseUrl == nil ? .on : .off + baseUrlTextField.stringValue = project.jiraBaseUrl ?? "" + userTextField.stringValue = project.jiraUser ?? "" + errorTextField.stringValue = "" + projectNamePopup.removeAllItems() + projectNamePopup.addItem(withTitle: project.jiraProject ?? "") + projectIssueNamePopup.removeAllItems() + projectIssueNamePopup.addItem(withTitle: project.jiraIssue ?? "") + pathsTextField.stringValue = project.gitBaseUrls.toString() + emailsTextField.stringValue = project.gitUsers.toString() + taskNumberPrefixTextField.stringValue = project.taskNumberPrefix ?? "" } @@ -64,6 +79,14 @@ class ProjectDetailsViewController: NSViewController { } + @IBAction func handleDeleteButton (_ sender: NSButton) { + + } + + @IBAction func handleCredentialsButton (_ sender: NSButton) { + + } + @objc func emailTextFieldClicked() { guard gitUsersPopover == nil else { return diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard index 84cc856..c378330 100644 --- a/Delivery/macOS/Screens/Projects/Projects.storyboard +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -29,7 +29,7 @@ - + @@ -201,7 +201,7 @@ - + @@ -210,7 +210,7 @@ - + @@ -278,12 +278,15 @@ + + + - + @@ -352,6 +355,9 @@ + + + @@ -404,6 +410,8 @@ + + @@ -412,6 +420,7 @@ + diff --git a/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift index abf3b0c..fbf66f4 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift @@ -39,8 +39,10 @@ extension ProjectsInteractor: ProjectsInteractorInput { } func reloadProjects() { -// presenter?.projectsDidLoad([]) -// return; +// let interactor = ReadProjectsInteractor(repository: localRepository, remoteRepository: remoteRepository) +// let projects = interactor.allProjects() +// presenter?.projectsDidLoad(projects) + presenter?.projectsDidLoad([ Project(objectId: nil, lastModifiedDate: nil, @@ -57,8 +59,8 @@ extension ProjectsInteractor: ProjectsInteractorInput { title: "Terma", jiraBaseUrl: "https://jira.fortech.ro", jiraUser: "cristianbal", - jiraProject: "BOSCH", - jiraIssue: "BOSCH-100", + jiraProject: "TERMA", + jiraIssue: "TERMA-001", gitBaseUrls: [], gitUsers: [], taskNumberPrefix: "TERMA") diff --git a/External/CloudKit/CloudKitRepository+Projects.swift b/External/CloudKit/CloudKitRepository+Projects.swift new file mode 100644 index 0000000..cee253c --- /dev/null +++ b/External/CloudKit/CloudKitRepository+Projects.swift @@ -0,0 +1,179 @@ +// +// CloudKitRepository+Projects.swift +// Jirassic +// +// Created by Cristian Baluta on 12/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CloudKit +import RCLog + +extension CloudKitRepository: RepositoryProjects { + + func projects() -> [Project] { + fatalError("This method is not applicable to CloudKitRepository") + } + + func queryProjects(_ completion: @escaping (_ projects: [Project]) -> Void) { + let predicate = NSPredicate(value: true) + fetchRecords(ofType: "Project", predicate: predicate) { records in + completion( self.projectsFromRecords(records ?? []) ) + } + } + + func queryUpdates (_ completion: @escaping ([Project], [String], NSError?) -> Void) { + + let changeToken = UserDefaults.standard.serverChangeToken + + fetchChangedRecords(token: changeToken, + previousRecords: [], + previousDeletedRecordsIds: [], + completion: { changedRecords, deletedRecordsIds in + + completion(self.projectsFromRecords(changedRecords), self.stringIdsFromCKRecordIds(deletedRecordsIds), nil) + }) + } + + func saveProject (_ project: Project, completion: @escaping ((_ task: Project?) -> Void)) { + RCLogO("1. Save to cloudkit \(project)") + + guard let customZone = self.customZone, let privateDB = self.privateDB else { + RCLog("Can't save, not logged in to iCloud") + return + } + + // Query for the task from server if exists + fetchCKRecordOfProject(project) { record in + var record: CKRecord? = record + // No record found on server, creating one now + if record == nil { + let recordId = CKRecord.ID(recordName: project.objectId!, zoneID: customZone.zoneID) + record = CKRecord(recordType: "Project", recordID: recordId) + } + record = self.updatedRecord(record!, withProject: project) + + privateDB.save(record!, completionHandler: { savedRecord, error in + + RCLog("2. Record after saving to CloudKit") + RCLogO(savedRecord) + RCLogErrorO(error) + + if let record = savedRecord { + let uploadedProject = self.projectFromRecord(record) + completion(uploadedProject) + } + if let ckerror = error as? CKError { + switch ckerror { + case CKError.quotaExceeded: + // The user has run out of iCloud storage space. + // Prompt the user to go to iCloud Settings to manage storage. + #warning("Present quotaExceeded message to the user") + break + default: + break + } + completion(nil) + } + }) + } + } + + func deleteProject (_ project: Project, permanently: Bool, completion: @escaping ((_ success: Bool) -> Void)) { + + guard let privateDB = self.privateDB else { + RCLog("Not logged in") + return + } + + fetchCKRecordOfProject(project) { record in + if let cproject = record { + + privateDB.delete(withRecordID: cproject.recordID, completionHandler: { recordID, error in + RCLogO(recordID) + RCLogErrorO(error) + completion(error != nil) + }) + } else { + completion(false) + } + } + } +} + +extension CloudKitRepository { + + func fetchCKRecordOfProject (_ project: Project, completion: @escaping ((_ cproject: CKRecord?) -> Void)) { + + guard let customZone = self.customZone, let privateDB = self.privateDB else { + RCLog("Not logged in") + return + } + + let predicate = NSPredicate(format: "objectId == %@", project.objectId! as CVarArg) + let query = CKQuery(recordType: "Project", predicate: predicate) + privateDB.perform(query, inZoneWith: customZone.zoneID) { (results: [CKRecord]?, error) in + + RCLogErrorO(error) + + if let result = results?.first { + completion(result) + } else { + completion(nil) + } + } + } + + private func projectsFromRecords (_ records: [CKRecord]) -> [Project] { + + var projects = [Project]() + for record in records { + projects.append( projectFromRecord(record) ) + } + + return projects + } + + private func projectFromRecord (_ record: CKRecord) -> Project { + + return Project( + objectId: record["objectId"] as? String, + lastModifiedDate: record["lastModifiedDate"] as? Date, + title: record["title"] as! String, + jiraBaseUrl: record["jiraBaseUrl"] as? String, + jiraUser: record["jiraUser"] as? String, + jiraProject: record["jiraProject"] as? String, + jiraIssue: record["jiraIssue"] as? String, + gitBaseUrls: (record["gitBaseUrls"] as? String ?? "").toArray(), + gitUsers: (record["gitUsers"] as? String ?? "").toArray(), + taskNumberPrefix: record["taskNumberPrefix"] as? String + ) + } + + private func updatedRecord (_ record: CKRecord, withProject project: Project) -> CKRecord { + + record["objectId"] = project.objectId as CKRecordValue? + record["lastModifiedDate"] = project.lastModifiedDate as CKRecordValue? + record["title"] = project.title as CKRecordValue + record["jiraBaseUrl"] = project.jiraBaseUrl as CKRecordValue? + record["jiraUser"] = project.jiraUser as CKRecordValue? + record["jiraProject"] = project.jiraProject as CKRecordValue? + record["jiraIssue"] = project.jiraIssue as CKRecordValue? + record["gitBaseUrls"] = project.gitBaseUrls.toString() as CKRecordValue? + record["gitUsers"] = project.gitUsers.toString() as CKRecordValue? + record["taskNumberPrefix"] = project.taskNumberPrefix as CKRecordValue? + + return record + } + + private func stringIdsFromCKRecordIds (_ ckrecords: [CKRecord.ID]) -> [String] { + + var ids = [String]() + for ckrecord in ckrecords { + ids.append( ckrecord.recordName ) + } + + return ids + } +} diff --git a/External/CloudKit/CloudKitRepository.swift b/External/CloudKit/CloudKitRepository.swift index 8ec9624..b5350e0 100644 --- a/External/CloudKit/CloudKitRepository.swift +++ b/External/CloudKit/CloudKitRepository.swift @@ -117,7 +117,7 @@ extension CloudKitRepository { privateDB.add(op) } - func fetchRecords (ofType type: String, predicate: NSPredicate, completion: @escaping ((_ ctask: [CKRecord]?) -> Void)) { + func fetchRecords (ofType type: String, predicate: NSPredicate, completion: @escaping ((_ record: [CKRecord]?) -> Void)) { guard let customZone = self.customZone, let privateDB = self.privateDB else { RCLog("Not logged in") diff --git a/External/CoreData/CProject.swift b/External/CoreData/CProject.swift new file mode 100644 index 0000000..4bd54c9 --- /dev/null +++ b/External/CoreData/CProject.swift @@ -0,0 +1,18 @@ +// +// CProject.swift +// Jirassic +// +// Created by Cristian Baluta on 12/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CoreData + + +class CProject: NSManagedObject { + +// @NSManaged var userId: String? +// @NSManaged var email: String? + +} diff --git a/External/CoreData/CoreDataRepository+Projects.swift b/External/CoreData/CoreDataRepository+Projects.swift new file mode 100644 index 0000000..6475cbb --- /dev/null +++ b/External/CoreData/CoreDataRepository+Projects.swift @@ -0,0 +1,28 @@ +// +// CoreDataRepository+Project.swift +// Jirassic +// +// Created by Cristian Baluta on 12/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +extension CoreDataRepository: RepositoryProjects { + + func projects() -> [Project] { + return [] + } + + func queryProjects(_ completion: @escaping ((_ task: [Project]) -> Void)) { + + } + + func saveProject (_ project: Project, completion: @escaping ((_ task: Project?) -> Void)) { + + } + + func deleteProject (_ project: Project, permanently: Bool, completion: @escaping ((_ success: Bool) -> Void)) { + + } +} diff --git a/External/Repository.swift b/External/Repository.swift index bcc4013..c4527bd 100644 --- a/External/Repository.swift +++ b/External/Repository.swift @@ -40,4 +40,13 @@ protocol RepositorySettings { } -typealias Repository = RepositoryUser & RepositoryTasks & RepositorySettings +protocol RepositoryProjects { + + func projects() -> [Project] + func queryProjects(_ completion: @escaping ((_ task: [Project]) -> Void)) + func saveProject (_ project: Project, completion: @escaping ((_ task: Project?) -> Void)) + func deleteProject (_ project: Project, permanently: Bool, completion: @escaping ((_ success: Bool) -> Void)) + +} + +typealias Repository = RepositoryUser & RepositoryTasks & RepositorySettings & RepositoryProjects diff --git a/External/sqlite/SProject.swift b/External/sqlite/SProject.swift new file mode 100644 index 0000000..5015fc6 --- /dev/null +++ b/External/sqlite/SProject.swift @@ -0,0 +1,28 @@ +// +// SProject.swift +// Jirassic +// +// Created by Cristian Baluta on 12/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +class SProject: SQLTable { + + var objectId: String? = nil + var lastModifiedDate: Date? = nil + var title: String? = nil + var jiraBaseUrl: String? = nil + var jiraUser: String? = nil + var jiraProject: String? = nil + var jiraIssue: String? = nil + + var gitBaseUrls: String? = nil + var gitUsers: String? = nil + var taskNumberPrefix: String? = nil + + override func primaryKey() -> String { + return "objectId" + } +} diff --git a/External/sqlite/SqliteRepository+Projects.swift b/External/sqlite/SqliteRepository+Projects.swift new file mode 100644 index 0000000..63263dd --- /dev/null +++ b/External/sqlite/SqliteRepository+Projects.swift @@ -0,0 +1,62 @@ +// +// SqliteRepository+Projects.swift +// Jirassic +// +// Created by Cristian Baluta on 12/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +extension SqliteRepository: RepositoryProjects { + + func projects() -> [Project] { + return [] + } + + func queryProjects(_ completion: @escaping ((_ task: [Project]) -> Void)) { + + } + + func saveProject (_ project: Project, completion: @escaping ((_ task: Project?) -> Void)) { + + } + + func deleteProject (_ project: Project, permanently: Bool, completion: @escaping ((_ success: Bool) -> Void)) { + + } + + private func projectFromSProject (_ sproject: SProject) -> Project { + + return Project( + objectId: sproject.objectId, + lastModifiedDate: sproject.lastModifiedDate, + title: sproject.title ?? "", + jiraBaseUrl: sproject.jiraBaseUrl, + jiraUser: sproject.jiraUser, + jiraProject: sproject.jiraProject, + jiraIssue: sproject.jiraIssue, + + gitBaseUrls: (sproject.gitBaseUrls ?? "").toArray(), + gitUsers: (sproject.gitBaseUrls ?? "").toArray(), + taskNumberPrefix: sproject.taskNumberPrefix + ) + } + + private func sprojectFromProject (_ project: Project) -> SProject { + + let sproject = SProject() + sproject.objectId = project.objectId + sproject.lastModifiedDate = project.lastModifiedDate + sproject.title = project.title + sproject.jiraBaseUrl = project.jiraBaseUrl + sproject.jiraUser = project.jiraUser + sproject.jiraProject = project.jiraProject + sproject.jiraIssue = project.jiraIssue + sproject.gitBaseUrls = project.gitBaseUrls.toString() + sproject.gitUsers = project.gitUsers.toString() + sproject.taskNumberPrefix = project.taskNumberPrefix + + return sproject + } +} diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 64375aa..59fd8f1 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -37,7 +37,6 @@ 280D70DA1ECFE83F005D2689 /* CreateReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D32B1DD3B44200B73201 /* CreateReport.swift */; }; 280D70DB1ECFE847005D2689 /* ReadDaysInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D32E1DD3B44200B73201 /* ReadDaysInteractor.swift */; }; 280D70DC1ECFE84C005D2689 /* ReadTasksInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D32F1DD3B44200B73201 /* ReadTasksInteractor.swift */; }; - 280D70DE1ECFE870005D2689 /* UserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33D1DD3B44200B73201 /* UserInteractor.swift */; }; 280D70DF1ECFE888005D2689 /* CoreDataRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38C1DD3B44200B73201 /* CoreDataRepository.swift */; }; 280D70E01ECFE88F005D2689 /* CoreDataRepository+Tasks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928891E910F4D0022AB55 /* CoreDataRepository+Tasks.swift */; }; 280D70E11ECFE88F005D2689 /* CoreDataRepository+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A9288C1E910F970022AB55 /* CoreDataRepository+Settings.swift */; }; @@ -54,7 +53,6 @@ 280D70F11ECFE8D0005D2689 /* RepositoryInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3951DD3B44200B73201 /* RepositoryInteractor.swift */; }; 280D70F41ECFE96F005D2689 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D31A1DD3B44200B73201 /* Settings.swift */; }; 280D70F51ECFE98C005D2689 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; - 280D70F61ECFE9B3005D2689 /* RegisterUserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33C1DD3B44200B73201 /* RegisterUserInteractor.swift */; }; 280D70F71ED0E6EC005D2689 /* StringIdGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3221DD3B44200B73201 /* StringIdGenerator.swift */; }; 280D70FA1ED0E7B0005D2689 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 280D70F91ED0E7B0005D2689 /* CloudKit.framework */; }; 280D71021ED35A25005D2689 /* UserDefaults+token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */; }; @@ -81,8 +79,6 @@ 280D71471ED6043D005D2689 /* TaskTypeEstimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3341DD3B44200B73201 /* TaskTypeEstimator.swift */; }; 280D71491ED6043D005D2689 /* TaskTypeSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3361DD3B44200B73201 /* TaskTypeSelection.swift */; }; 280D714A1ED6043D005D2689 /* PredictiveTimeTyping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3391DD3B44200B73201 /* PredictiveTimeTyping.swift */; }; - 280D714C1ED6043D005D2689 /* RegisterUserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33C1DD3B44200B73201 /* RegisterUserInteractor.swift */; }; - 280D714D1ED6043D005D2689 /* UserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33D1DD3B44200B73201 /* UserInteractor.swift */; }; 280D71591ED6043D005D2689 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3541DD3B44200B73201 /* AppDelegate.swift */; }; 280D715A1ED6043D005D2689 /* AppWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3551DD3B44200B73201 /* AppWireframe.swift */; }; 280D715B1ED6043D005D2689 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 405B15651DEF75660009871C /* AppViewController.swift */; }; @@ -247,6 +243,32 @@ 2863493623B790B1009F81D7 /* ProjectsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */; }; 2863493823B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */; }; 2863493923B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */; }; + 2863493B23CB6B07009F81D7 /* ReadProjectsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493A23CB6B07009F81D7 /* ReadProjectsInteractor.swift */; }; + 2863493C23CB6B08009F81D7 /* ReadProjectsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493A23CB6B07009F81D7 /* ReadProjectsInteractor.swift */; }; + 2863494123CB6E9F009F81D7 /* UserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493E23CB6E9E009F81D7 /* UserInteractor.swift */; }; + 2863494223CB6E9F009F81D7 /* UserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493E23CB6E9E009F81D7 /* UserInteractor.swift */; }; + 2863494323CB6E9F009F81D7 /* UserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863493E23CB6E9E009F81D7 /* UserInteractor.swift */; }; + 2863494723CB6E9F009F81D7 /* RegisterUserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494023CB6E9F009F81D7 /* RegisterUserInteractor.swift */; }; + 2863494823CB6E9F009F81D7 /* RegisterUserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494023CB6E9F009F81D7 /* RegisterUserInteractor.swift */; }; + 2863494923CB6E9F009F81D7 /* RegisterUserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494023CB6E9F009F81D7 /* RegisterUserInteractor.swift */; }; + 2863494B23CB6F9E009F81D7 /* SqliteRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494A23CB6F9E009F81D7 /* SqliteRepository+Projects.swift */; }; + 2863494C23CB6F9E009F81D7 /* SqliteRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494A23CB6F9E009F81D7 /* SqliteRepository+Projects.swift */; }; + 2863494D23CB6F9E009F81D7 /* SqliteRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494A23CB6F9E009F81D7 /* SqliteRepository+Projects.swift */; }; + 2863494F23CB73E4009F81D7 /* SProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494E23CB73E4009F81D7 /* SProject.swift */; }; + 2863495023CB73E4009F81D7 /* SProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494E23CB73E4009F81D7 /* SProject.swift */; }; + 2863495123CB73E4009F81D7 /* SProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863494E23CB73E4009F81D7 /* SProject.swift */; }; + 2863495323CB7DFA009F81D7 /* CoreDataRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495223CB7DFA009F81D7 /* CoreDataRepository+Projects.swift */; }; + 2863495423CB7DFA009F81D7 /* CoreDataRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495223CB7DFA009F81D7 /* CoreDataRepository+Projects.swift */; }; + 2863495523CB7DFA009F81D7 /* CoreDataRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495223CB7DFA009F81D7 /* CoreDataRepository+Projects.swift */; }; + 2863495723CB7E4D009F81D7 /* CProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495623CB7E4D009F81D7 /* CProject.swift */; }; + 2863495823CB7E4D009F81D7 /* CProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495623CB7E4D009F81D7 /* CProject.swift */; }; + 2863495923CB7E4D009F81D7 /* CProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495623CB7E4D009F81D7 /* CProject.swift */; }; + 2863495B23CB8003009F81D7 /* CloudKitRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */; }; + 2863495C23CB8003009F81D7 /* CloudKitRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */; }; + 2863495D23CB8003009F81D7 /* CloudKitRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */; }; + 2863495F23CB9134009F81D7 /* ProjectInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */; }; + 2863496023CB9134009F81D7 /* ProjectInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */; }; + 2863496123CB9134009F81D7 /* ProjectInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */; }; 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 286BC00421909F85004D4CDD /* CloseDayInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */; }; @@ -429,8 +451,6 @@ 4065D3B41DD3B44200B73201 /* TaskTypeEstimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3341DD3B44200B73201 /* TaskTypeEstimator.swift */; }; 4065D3B61DD3B44200B73201 /* TaskTypeSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3361DD3B44200B73201 /* TaskTypeSelection.swift */; }; 4065D3B81DD3B44200B73201 /* PredictiveTimeTyping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3391DD3B44200B73201 /* PredictiveTimeTyping.swift */; }; - 4065D3BA1DD3B44200B73201 /* RegisterUserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33C1DD3B44200B73201 /* RegisterUserInteractor.swift */; }; - 4065D3BB1DD3B44200B73201 /* UserInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33D1DD3B44200B73201 /* UserInteractor.swift */; }; 4065D3C81DD3B44200B73201 /* FlipAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3521DD3B44200B73201 /* FlipAnimation.swift */; }; 4065D3C91DD3B44200B73201 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3541DD3B44200B73201 /* AppDelegate.swift */; }; 4065D3CA1DD3B44200B73201 /* AppWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3551DD3B44200B73201 /* AppWireframe.swift */; }; @@ -455,7 +475,6 @@ 4065D4171DD4536200B73201 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3871DD3B44200B73201 /* main.swift */; }; 4065D4181DD454C500B73201 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3941DD3B44200B73201 /* Repository.swift */; }; 4065D4191DD454CB00B73201 /* RepositoryInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3951DD3B44200B73201 /* RepositoryInteractor.swift */; }; - 4065D4201DD4561D00B73201 /* UserInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33E1DD3B44200B73201 /* UserInteractorTests.swift */; }; 4065D4211DD4562100B73201 /* PredictiveTimeTypingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D33A1DD3B44200B73201 /* PredictiveTimeTypingTests.swift */; }; 4065D4231DD4562900B73201 /* DateExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3211DD3B44200B73201 /* DateExtensionTests.swift */; }; 4065D4241DD4562900B73201 /* CreateReportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D32C1DD3B44200B73201 /* CreateReportTests.swift */; }; @@ -667,6 +686,16 @@ 2863492E23B5EFD1009F81D7 /* ReportsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsPresenter.swift; sourceTree = ""; }; 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsListViewController.swift; sourceTree = ""; }; 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsPresenter.swift; sourceTree = ""; }; + 2863493A23CB6B07009F81D7 /* ReadProjectsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadProjectsInteractor.swift; sourceTree = ""; }; + 2863493E23CB6E9E009F81D7 /* UserInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInteractor.swift; sourceTree = ""; }; + 2863493F23CB6E9F009F81D7 /* UserInteractorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInteractorTests.swift; sourceTree = ""; }; + 2863494023CB6E9F009F81D7 /* RegisterUserInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegisterUserInteractor.swift; sourceTree = ""; }; + 2863494A23CB6F9E009F81D7 /* SqliteRepository+Projects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SqliteRepository+Projects.swift"; sourceTree = ""; }; + 2863494E23CB73E4009F81D7 /* SProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SProject.swift; sourceTree = ""; }; + 2863495223CB7DFA009F81D7 /* CoreDataRepository+Projects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataRepository+Projects.swift"; sourceTree = ""; }; + 2863495623CB7E4D009F81D7 /* CProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CProject.swift; sourceTree = ""; }; + 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Projects.swift"; sourceTree = ""; }; + 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectInteractor.swift; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDayInteractor.swift; sourceTree = ""; }; 287195142022E1E2001C237E /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; @@ -781,9 +810,6 @@ 4065D3361DD3B44200B73201 /* TaskTypeSelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskTypeSelection.swift; sourceTree = ""; }; 4065D3391DD3B44200B73201 /* PredictiveTimeTyping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredictiveTimeTyping.swift; sourceTree = ""; }; 4065D33A1DD3B44200B73201 /* PredictiveTimeTypingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredictiveTimeTypingTests.swift; sourceTree = ""; }; - 4065D33C1DD3B44200B73201 /* RegisterUserInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegisterUserInteractor.swift; sourceTree = ""; }; - 4065D33D1DD3B44200B73201 /* UserInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInteractor.swift; sourceTree = ""; }; - 4065D33E1DD3B44200B73201 /* UserInteractorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInteractorTests.swift; sourceTree = ""; }; 4065D3421DD3B44200B73201 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 4065D3441DD3B44200B73201 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 4065D3461DD3B44200B73201 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -1041,9 +1067,11 @@ 28A928801E910DA40022AB55 /* SqliteRepository+Tasks.swift */, 28A928831E910E5E0022AB55 /* SqliteRepository+User.swift */, 28A928861E910EA70022AB55 /* SqliteRepository+Settings.swift */, + 2863494A23CB6F9E009F81D7 /* SqliteRepository+Projects.swift */, 28577EA81E8ADC53002B07FD /* SSettings.swift */, 28577EA91E8ADC53002B07FD /* STask.swift */, 28577EAA1E8ADC53002B07FD /* SUser.swift */, + 2863494E23CB73E4009F81D7 /* SProject.swift */, 28577EB31E8AF08F002B07FD /* SQLiteDB.swift */, 28577EB41E8AF08F002B07FD /* SQLTable.swift */, 28A928771E8F78580022AB55 /* SQLiteSchema.swift */, @@ -1055,6 +1083,8 @@ 286348FE23B237D9009F81D7 /* Projects */ = { isa = PBXGroup; children = ( + 2863493A23CB6B07009F81D7 /* ReadProjectsInteractor.swift */, + 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */, ); path = Projects; sourceTree = ""; @@ -1099,6 +1129,16 @@ path = Reports; sourceTree = ""; }; + 2863493D23CB6E87009F81D7 /* User */ = { + isa = PBXGroup; + children = ( + 2863494023CB6E9F009F81D7 /* RegisterUserInteractor.swift */, + 2863493E23CB6E9E009F81D7 /* UserInteractor.swift */, + 2863493F23CB6E9F009F81D7 /* UserInteractorTests.swift */, + ); + path = User; + sourceTree = ""; + }; 28667B591FCADF4E007B98E3 /* Modules */ = { isa = PBXGroup; children = ( @@ -1364,6 +1404,7 @@ 4065D3161DD3B44200B73201 /* App */ = { isa = PBXGroup; children = ( + 2863493D23CB6E87009F81D7 /* User */, 4065D3171DD3B44200B73201 /* Entities */, 4065D31F1DD3B44200B73201 /* Extensions */, 4065D3271DD3B44200B73201 /* Notifications */, @@ -1372,7 +1413,6 @@ 4065D32D1DD3B44200B73201 /* Tasks */, 280300D41EDB5E6D000A763E /* Statistics */, 4065D3371DD3B44200B73201 /* Time */, - 4065D33B1DD3B44200B73201 /* User */, 28CBB59420455467006F9D3A /* Parsing */, ); path = App; @@ -1462,16 +1502,6 @@ path = Time; sourceTree = ""; }; - 4065D33B1DD3B44200B73201 /* User */ = { - isa = PBXGroup; - children = ( - 4065D33C1DD3B44200B73201 /* RegisterUserInteractor.swift */, - 4065D33D1DD3B44200B73201 /* UserInteractor.swift */, - 4065D33E1DD3B44200B73201 /* UserInteractorTests.swift */, - ); - path = User; - sourceTree = ""; - }; 4065D3401DD3B44200B73201 /* Delivery */ = { isa = PBXGroup; children = ( @@ -1648,6 +1678,7 @@ 28A928921E91104B0022AB55 /* CloudKitRepository+Tasks.swift */, 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */, 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */, + 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */, 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */, ); path = CloudKit; @@ -1660,9 +1691,11 @@ 28A928891E910F4D0022AB55 /* CoreDataRepository+Tasks.swift */, 28A9288C1E910F970022AB55 /* CoreDataRepository+Settings.swift */, 28A9288F1E910FF60022AB55 /* CoreDataRepository+User.swift */, + 2863495223CB7DFA009F81D7 /* CoreDataRepository+Projects.swift */, 4065D38D1DD3B44200B73201 /* CSettings.swift */, 4065D38E1DD3B44200B73201 /* CTask.swift */, 4065D38F1DD3B44200B73201 /* CUser.swift */, + 2863495623CB7E4D009F81D7 /* CProject.swift */, 2818847F21A4A2F800B33B9C /* Jirassic.xcdatamodeld */, ); path = CoreData; @@ -2136,15 +2169,18 @@ buildActionMask = 2147483647; files = ( 280D70E51ECFE88F005D2689 /* CUser.swift in Sources */, + 2863494323CB6E9F009F81D7 /* UserInteractor.swift in Sources */, 280D70F51ECFE98C005D2689 /* UserDefaults+uploadToken.swift in Sources */, 280D70E91ECFE8B9005D2689 /* CloudKitRepository+Settings.swift in Sources */, + 2863495923CB7E4D009F81D7 /* CProject.swift in Sources */, 280D70D21ECFE7ED005D2689 /* Day.swift in Sources */, 280D70DA1ECFE83F005D2689 /* CreateReport.swift in Sources */, 280D70D71ECFE817005D2689 /* DateExtension.swift in Sources */, + 2863495D23CB8003009F81D7 /* CloudKitRepository+Projects.swift in Sources */, 280D70F01ECFE8CC005D2689 /* Repository.swift in Sources */, + 2863495523CB7DFA009F81D7 /* CoreDataRepository+Projects.swift in Sources */, 286348FC23B237B1009F81D7 /* Project.swift in Sources */, - 280D70F61ECFE9B3005D2689 /* RegisterUserInteractor.swift in Sources */, - 280D70DE1ECFE870005D2689 /* UserInteractor.swift in Sources */, + 2863494923CB6E9F009F81D7 /* RegisterUserInteractor.swift in Sources */, 280D70E11ECFE88F005D2689 /* CoreDataRepository+Settings.swift in Sources */, 280D70C81ECFE5AC005D2689 /* AppDelegate.swift in Sources */, 280D70E21ECFE88F005D2689 /* CoreDataRepository+User.swift in Sources */, @@ -2171,11 +2207,14 @@ 280D70DB1ECFE847005D2689 /* ReadDaysInteractor.swift in Sources */, 28CBB598204554A2006F9D3A /* ParseGitBranch.swift in Sources */, 280D70E41ECFE88F005D2689 /* CTask.swift in Sources */, + 2863495123CB73E4009F81D7 /* SProject.swift in Sources */, 280D70D41ECFE7FF005D2689 /* Task.swift in Sources */, + 2863494D23CB6F9E009F81D7 /* SqliteRepository+Projects.swift in Sources */, 280D70D51ECFE803005D2689 /* User.swift in Sources */, 280D70D11ECFE5EE005D2689 /* TasksViewController.swift in Sources */, 280D70F71ED0E6EC005D2689 /* StringIdGenerator.swift in Sources */, 280D70D61ECFE807005D2689 /* Week.swift in Sources */, + 2863496123CB9134009F81D7 /* ProjectInteractor.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2191,6 +2230,7 @@ 2898D3AB2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, 280D712F1ED6043D005D2689 /* Task.swift in Sources */, 2863491B23B393CF009F81D7 /* CloseDayCell.swift in Sources */, + 2863493B23CB6B07009F81D7 /* ReadProjectsInteractor.swift in Sources */, 287195312025C360001C237E /* CSettings.swift in Sources */, 56ADBF6A21C3F625008350A6 /* GitUser.swift in Sources */, 287195302025C35C001C237E /* CoreDataRepository+User.swift in Sources */, @@ -2199,6 +2239,7 @@ 566B9FA7217DE67800EAF324 /* TasksInteractor.swift in Sources */, 288BB67E2085252900CF720A /* WizardViewController.swift in Sources */, 28FB265E2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */, + 2863494B23CB6F9E009F81D7 /* SqliteRepository+Projects.swift in Sources */, 2863492F23B5EFD1009F81D7 /* ReportsPresenter.swift in Sources */, 280D71331ED6043D005D2689 /* DateExtension.swift in Sources */, 56ADBF6D21C3F94D008350A6 /* GitUserParser.swift in Sources */, @@ -2220,11 +2261,14 @@ 280D713A1ED6043D005D2689 /* Conversions.swift in Sources */, 280D713B1ED6043D005D2689 /* ComputerWakeUpInteractor.swift in Sources */, 280D713D1ED6043D005D2689 /* CreateReport.swift in Sources */, + 2863494723CB6E9F009F81D7 /* RegisterUserInteractor.swift in Sources */, + 2863495F23CB9134009F81D7 /* ProjectInteractor.swift in Sources */, 280D713F1ED6043D005D2689 /* ReadDaysInteractor.swift in Sources */, 280D71411ED6043D005D2689 /* ReadTasksInteractor.swift in Sources */, 280D71431ED6043D005D2689 /* TaskFinder.swift in Sources */, 286348F523B136B3009F81D7 /* ProjectsInteractor.swift in Sources */, 280D71451ED6043D005D2689 /* TaskInteractor.swift in Sources */, + 2863494123CB6E9F009F81D7 /* UserInteractor.swift in Sources */, 285465A2216C84E10052CB6A /* CreateMonthReport.swift in Sources */, 56D90CF820876F1100F24442 /* WizardJiraView.swift in Sources */, 2801388F205F86460051B532 /* TimeBox.swift in Sources */, @@ -2233,8 +2277,6 @@ 2845B160206AE468006EFB3B /* TaskCell_.swift in Sources */, 28FE18AB207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */, 280D714A1ED6043D005D2689 /* PredictiveTimeTyping.swift in Sources */, - 280D714C1ED6043D005D2689 /* RegisterUserInteractor.swift in Sources */, - 280D714D1ED6043D005D2689 /* UserInteractor.swift in Sources */, 28FB265120224E3A00AEA38D /* JiraTempoCell.swift in Sources */, 280D71591ED6043D005D2689 /* AppDelegate.swift in Sources */, 280D715A1ED6043D005D2689 /* AppWireframe.swift in Sources */, @@ -2285,7 +2327,9 @@ 280D71701ED6043D005D2689 /* ExtensionsInteractor.swift in Sources */, 2845B13F2068351F006EFB3B /* TableViewCell.swift in Sources */, 288BB6872087169F00CF720A /* ViewXib.swift in Sources */, + 2863494F23CB73E4009F81D7 /* SProject.swift in Sources */, 28A283972037975600DDCB63 /* Saveable.swift in Sources */, + 2863495723CB7E4D009F81D7 /* CProject.swift in Sources */, 2863493523B790B1009F81D7 /* ProjectsListViewController.swift in Sources */, 280D71711ED6043D005D2689 /* ExtensionsInstallerInteractor.swift in Sources */, 280D71721ED6043D005D2689 /* AppleScript.swift in Sources */, @@ -2322,6 +2366,7 @@ 280D718E1ED6043D005D2689 /* UserNotifications.swift in Sources */, 280D718F1ED6043D005D2689 /* SleepNotifications.swift in Sources */, 2863490923B24C34009F81D7 /* CalendarInteractor.swift in Sources */, + 2863495323CB7DFA009F81D7 /* CoreDataRepository+Projects.swift in Sources */, 2892B2981F094A170085BAC2 /* JiraRepository.swift in Sources */, 280D71901ED6043D005D2689 /* BrowserNotification.swift in Sources */, 280D719D1ED6043D005D2689 /* SqliteRepository.swift in Sources */, @@ -2338,6 +2383,7 @@ 569C4C6A202319A20049FBF1 /* BrowserCell.swift in Sources */, 280D71A31ED6043D005D2689 /* SUser.swift in Sources */, 280D71A41ED6043D005D2689 /* SQLiteDB.swift in Sources */, + 2863495B23CB8003009F81D7 /* CloudKitRepository+Projects.swift in Sources */, 2871952F2025C359001C237E /* CoreDataRepository+Settings.swift in Sources */, 280D71A51ED6043D005D2689 /* SQLTable.swift in Sources */, 2845B139206703C5006EFB3B /* Keychain.swift in Sources */, @@ -2381,6 +2427,7 @@ 28A928811E910DA40022AB55 /* SqliteRepository+Tasks.swift in Sources */, 28279AD321BBF09900376304 /* Jirassic.xcdatamodeld in Sources */, 56ADBF6E21C3F94D008350A6 /* GitUserParser.swift in Sources */, + 2863495C23CB8003009F81D7 /* CloudKitRepository+Projects.swift in Sources */, 2898D3B22185959300CF5AD4 /* EditableTimeBox.swift in Sources */, 2828B04F2381D04E0005B188 /* NoAnimation.swift in Sources */, 4065D39F1DD3B44200B73201 /* Task.swift in Sources */, @@ -2395,6 +2442,7 @@ 28577EAD1E8ADC53002B07FD /* SSettings.swift in Sources */, 28577EB51E8AF08F002B07FD /* SQLiteDB.swift in Sources */, 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, + 2863494823CB6E9F009F81D7 /* RegisterUserInteractor.swift in Sources */, 286BC00521909F85004D4CDD /* CloseDayInteractor.swift in Sources */, 28A928951E9110980022AB55 /* CloudKitRepository+User.swift in Sources */, 4065D3EF1DD3B44200B73201 /* TasksScrollView.swift in Sources */, @@ -2421,9 +2469,11 @@ 569C4C592023193B0049FBF1 /* ShellCell.swift in Sources */, 2845B14E206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */, 2823C9351E4F69970055D036 /* Versioning.swift in Sources */, + 2863495823CB7E4D009F81D7 /* CProject.swift in Sources */, + 2863494C23CB6F9E009F81D7 /* SqliteRepository+Projects.swift in Sources */, + 2863495423CB7DFA009F81D7 /* CoreDataRepository+Projects.swift in Sources */, 4065D3A11DD3B44200B73201 /* User.swift in Sources */, 4065D3A71DD3B44200B73201 /* ViewController.swift in Sources */, - 4065D3BB1DD3B44200B73201 /* UserInteractor.swift in Sources */, 4065D3E31DD3B44200B73201 /* CellProtocol.swift in Sources */, 5623914B21CBC6A10031B882 /* ProjectsViewController.swift in Sources */, 2812F61F2145031B008EE81E /* IAPHelper.swift in Sources */, @@ -2452,7 +2502,6 @@ 2845B1402068351F006EFB3B /* TableViewCell.swift in Sources */, 28C9C6221EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */, 284192DE2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, - 4065D3BA1DD3B44200B73201 /* RegisterUserInteractor.swift in Sources */, 28FE18A6207B369800DF796E /* CocoaHookupCell.swift in Sources */, 4065D3B41DD3B44200B73201 /* TaskTypeEstimator.swift in Sources */, 5623914E21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */, @@ -2479,9 +2528,11 @@ 284192D82018841700E64A9A /* JProject.swift in Sources */, 56D90CF920876F1100F24442 /* WizardJiraView.swift in Sources */, 28279AD221BBF08E00376304 /* InMemoryCoreDataRepository.swift in Sources */, + 2863495023CB73E4009F81D7 /* SProject.swift in Sources */, 28EDE9391E59EC1500B360A4 /* TasksView.swift in Sources */, 28A283AC203A93AB00DDCB63 /* GitBranchParser.swift in Sources */, 40FCE4301DFD7C8D00D4FD45 /* Animatable.swift in Sources */, + 2863494223CB6E9F009F81D7 /* UserInteractor.swift in Sources */, 2863491523B299DB009F81D7 /* MainPresenter.swift in Sources */, 4065D3F21DD3B44200B73201 /* CloudKitRepository.swift in Sources */, 4065D3AE1DD3B44200B73201 /* ReadDaysInteractor.swift in Sources */, @@ -2491,6 +2542,7 @@ 4065D3DA1DD3B44200B73201 /* SleepNotifications.swift in Sources */, 280F507D1EC868B0007416AB /* StringArray.swift in Sources */, 2863492D23B5EF9C009F81D7 /* ReportsViewController.swift in Sources */, + 2863496023CB9134009F81D7 /* ProjectInteractor.swift in Sources */, 569C4C68202319930049FBF1 /* GitPresenter.swift in Sources */, 28FB265C20224EA700AEA38D /* HookupCell.swift in Sources */, 2845B13A206703C5006EFB3B /* Keychain.swift in Sources */, @@ -2504,6 +2556,7 @@ 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, 56ADBF6B21C3F625008350A6 /* GitUser.swift in Sources */, 286348F623B136B3009F81D7 /* ProjectsInteractor.swift in Sources */, + 2863493C23CB6B08009F81D7 /* ReadProjectsInteractor.swift in Sources */, 28AA500A1EDCD51300AAF03D /* CoreDataRepository+Settings.swift in Sources */, 280D70B11ECC09D9005D2689 /* AppTheme.swift in Sources */, 5685C7671DE8721100CA545E /* CloudKitLoginViewController.swift in Sources */, @@ -2572,7 +2625,6 @@ 4065D4251DD4562900B73201 /* TaskFinderTests.swift in Sources */, 4065D4261DD4562900B73201 /* TaskInteractorTests.swift in Sources */, 28A283B3203CB1B900DDCB63 /* MergeTasksInteractorTests.swift in Sources */, - 4065D4201DD4561D00B73201 /* UserInteractorTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From d7314e6ca3ab7e2286039cbc7b075cc3f7474cda Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Mon, 13 Jan 2020 16:25:19 +0200 Subject: [PATCH 17/48] Migrate db to v2 --- External/sqlite/SQLiteSchema.swift | 44 --------------- External/sqlite/SqliteRepository.swift | 2 +- External/sqlite/UpdateSchemaInteractor.swift | 56 ++++++++++++++++++++ Jirassic.xcodeproj/project.pbxproj | 16 +++--- 4 files changed, 65 insertions(+), 53 deletions(-) delete mode 100644 External/sqlite/SQLiteSchema.swift create mode 100644 External/sqlite/UpdateSchemaInteractor.swift diff --git a/External/sqlite/SQLiteSchema.swift b/External/sqlite/SQLiteSchema.swift deleted file mode 100644 index ab6b548..0000000 --- a/External/sqlite/SQLiteSchema.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// SQLiteMigrator.swift -// Jirassic -// -// Created by Cristian Baluta on 01/04/2017. -// Copyright © 2017 Imagin soft. All rights reserved. -// - -import Foundation - -enum SQLiteSchemaVersion: Int { - case v1 = 1 -} - -class SQLiteSchema { - - fileprivate let expectedVersion: SQLiteSchemaVersion = .v1 - - init (db: SQLiteDB) { - - if db.version != expectedVersion.rawValue { - migrate(db: db, toVersion: expectedVersion) - #warning("This should not be here") -// UserDefaults.standard.serverChangeToken = nil - } - } -} - -extension SQLiteSchema { - - func migrate (db: SQLiteDB, toVersion version: SQLiteSchemaVersion) { - - switch version { - case .v1: - let _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS stasks (lastModifiedDate DATETIME, markedForDeletion BOOL DEFAULT 0, startDate DATETIME, endDate DATETIME, notes TEXT, taskNumber TEXT, taskTitle TEXT, taskType INTEGER NOT NULL, objectId varchar(30) PRIMARY KEY);") - - let _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS ssettingss (autotrack BOOL, autotrackingMode INTEGER, trackLunch BOOL, trackScrum BOOL, trackMeetings BOOL, trackCodeReviews BOOL, trackWastedTime BOOL, trackStartOfDay BOOL, enableBackup BOOL, startOfDayTime DATETIME, endOfDayTime DATETIME, lunchTime DATETIME, scrumTime DATETIME, minSleepDuration INTEGER, minCodeRevDuration INTEGER, codeRevLink TEXT, minWasteDuration INTEGER, wasteLinks TEXT, i INTEGER NOT NULL PRIMARY KEY);") - - let _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS susers (userId TEXT, email TEXT, lastSyncDate DATETIME, isLoggedIn BOOL, i INTEGER NOT NULL PRIMARY KEY);") - break - } - db.version = version.rawValue - } -} diff --git a/External/sqlite/SqliteRepository.swift b/External/sqlite/SqliteRepository.swift index 755a53e..68ad92c 100644 --- a/External/sqlite/SqliteRepository.swift +++ b/External/sqlite/SqliteRepository.swift @@ -38,7 +38,7 @@ class SqliteRepository { #endif db = SQLiteDB(url: dbUrl) - _ = SQLiteSchema(db: db) + UpdateSchemaInteractor().execute(with: db) } internal func queryWithPredicate (_ predicate: String?, sortingKeyPath: String?) -> [T] { diff --git a/External/sqlite/UpdateSchemaInteractor.swift b/External/sqlite/UpdateSchemaInteractor.swift new file mode 100644 index 0000000..3e93fa4 --- /dev/null +++ b/External/sqlite/UpdateSchemaInteractor.swift @@ -0,0 +1,56 @@ +// +// SQLiteMigrator.swift +// Jirassic +// +// Created by Cristian Baluta on 01/04/2017. +// Copyright © 2017 Imagin soft. All rights reserved. +// + +import Foundation + +enum SQLiteSchemaVersion: Int { + case v1 = 1 + case v2 = 2 + + static func allVersions() -> [SQLiteSchemaVersion] { + return [.v1, .v2] + } +} + +class UpdateSchemaInteractor { + + func execute (with db: SQLiteDB) { + guard let expectedVersion = SQLiteSchemaVersion.allVersions().last, db.version != expectedVersion.rawValue else { + return + } + for version in SQLiteSchemaVersion.allVersions() { + guard version.rawValue > db.version else { + /// Skip versions already executed + continue + } + migrate(db: db, toVersion: version) + } + } + + private func migrate (db: SQLiteDB, toVersion version: SQLiteSchemaVersion) { + + switch version { + case .v1: + /// Tasks table + _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS stasks (lastModifiedDate DATETIME, markedForDeletion BOOL DEFAULT 0, startDate DATETIME, endDate DATETIME, notes TEXT, taskNumber TEXT, taskTitle TEXT, taskType INTEGER NOT NULL, objectId varchar(30) PRIMARY KEY);") + /// Settings table + _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS ssettingss (autotrack BOOL, autotrackingMode INTEGER, trackLunch BOOL, trackScrum BOOL, trackMeetings BOOL, trackCodeReviews BOOL, trackWastedTime BOOL, trackStartOfDay BOOL, enableBackup BOOL, startOfDayTime DATETIME, endOfDayTime DATETIME, lunchTime DATETIME, scrumTime DATETIME, minSleepDuration INTEGER, minCodeRevDuration INTEGER, codeRevLink TEXT, minWasteDuration INTEGER, wasteLinks TEXT, i INTEGER NOT NULL PRIMARY KEY);") + /// Users table + _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS susers (userId TEXT, email TEXT, lastSyncDate DATETIME, isLoggedIn BOOL, i INTEGER NOT NULL PRIMARY KEY);") + + case .v2: + /// Edit Tasks table + _ = db.execute(sql: "ALTER TABLE stasks ADD COLUMN projectId varchar(30);") + /// Projects table + _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS sprojects (objectId varchar(30) PRIMARY KEY, lastModifiedDate DATETIME, title TEXT, jiraBaseUrl TEXT, jiraUser TEXT, jiraProject TEXT, jiraIssue TEXT, gitBaseUrls TEXT, gitUsers TEXT, taskNumberPrefix TEXT);") + /// Metadata table + _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS smetadata (i INTEGER NOT NULL PRIMARY KEY, tasksLastSyncDate DATETIME, projectsLastSyncDate DATETIME, tasksLastSyncToken TEXT, projectsLastSyncToken TEXT);") + } + db.version = version.rawValue + } +} diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 59fd8f1..2b6fe81 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -125,7 +125,7 @@ 280D71A31ED6043D005D2689 /* SUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EAA1E8ADC53002B07FD /* SUser.swift */; }; 280D71A41ED6043D005D2689 /* SQLiteDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EB31E8AF08F002B07FD /* SQLiteDB.swift */; }; 280D71A51ED6043D005D2689 /* SQLTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EB41E8AF08F002B07FD /* SQLTable.swift */; }; - 280D71A61ED6043D005D2689 /* SQLiteSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* SQLiteSchema.swift */; }; + 280D71A61ED6043D005D2689 /* UpdateSchemaInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */; }; 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; 280D71B61ED6043D005D2689 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3941DD3B44200B73201 /* Repository.swift */; }; 280D71B71ED6043D005D2689 /* RepositoryInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3951DD3B44200B73201 /* RepositoryInteractor.swift */; }; @@ -360,8 +360,8 @@ 28A283B3203CB1B900DDCB63 /* MergeTasksInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283B2203CB1B900DDCB63 /* MergeTasksInteractorTests.swift */; }; 28A5F27E1E5789FC002BE564 /* TasksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */; }; 28A5F2821E586426002BE564 /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F2811E586426002BE564 /* DataSource.swift */; }; - 28A928781E8F78580022AB55 /* SQLiteSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* SQLiteSchema.swift */; }; - 28A928791E8F78580022AB55 /* SQLiteSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* SQLiteSchema.swift */; }; + 28A928781E8F78580022AB55 /* UpdateSchemaInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */; }; + 28A928791E8F78580022AB55 /* UpdateSchemaInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */; }; 28A9287A1E8FA8E90022AB55 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 28577EB91E8AF379002B07FD /* libsqlite3.tbd */; }; 28A9287B1E8FC9B90022AB55 /* CreateReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D32B1DD3B44200B73201 /* CreateReport.swift */; }; 28A9287D1E9030BA0022AB55 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28A9287C1E9030BA0022AB55 /* CloudKit.framework */; }; @@ -736,7 +736,7 @@ 28A283B2203CB1B900DDCB63 /* MergeTasksInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MergeTasksInteractorTests.swift; sourceTree = ""; }; 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksDataSource.swift; sourceTree = ""; }; 28A5F2811E586426002BE564 /* DataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = ""; }; - 28A928771E8F78580022AB55 /* SQLiteSchema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteSchema.swift; sourceTree = ""; }; + 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateSchemaInteractor.swift; sourceTree = ""; }; 28A9287C1E9030BA0022AB55 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; 28A928801E910DA40022AB55 /* SqliteRepository+Tasks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SqliteRepository+Tasks.swift"; sourceTree = ""; }; 28A928831E910E5E0022AB55 /* SqliteRepository+User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SqliteRepository+User.swift"; sourceTree = ""; }; @@ -1074,7 +1074,7 @@ 2863494E23CB73E4009F81D7 /* SProject.swift */, 28577EB31E8AF08F002B07FD /* SQLiteDB.swift */, 28577EB41E8AF08F002B07FD /* SQLTable.swift */, - 28A928771E8F78580022AB55 /* SQLiteSchema.swift */, + 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */, 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */, ); path = sqlite; @@ -2387,7 +2387,7 @@ 2871952F2025C359001C237E /* CoreDataRepository+Settings.swift in Sources */, 280D71A51ED6043D005D2689 /* SQLTable.swift in Sources */, 2845B139206703C5006EFB3B /* Keychain.swift in Sources */, - 280D71A61ED6043D005D2689 /* SQLiteSchema.swift in Sources */, + 280D71A61ED6043D005D2689 /* UpdateSchemaInteractor.swift in Sources */, 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */, 28279AD121BBF08C00376304 /* InMemoryCoreDataRepository.swift in Sources */, 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, @@ -2575,7 +2575,7 @@ 2845B16B206AE468006EFB3B /* TaskCell.swift in Sources */, 4065D39D1DD3B44200B73201 /* Report.swift in Sources */, 40FCE4251DF7646F00D4FD45 /* AppleScript.swift in Sources */, - 28A928781E8F78580022AB55 /* SQLiteSchema.swift in Sources */, + 28A928781E8F78580022AB55 /* UpdateSchemaInteractor.swift in Sources */, 2863493623B790B1009F81D7 /* ProjectsListViewController.swift in Sources */, 28FE18AC207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */, 4065D3AF1DD3B44200B73201 /* ReadTasksInteractor.swift in Sources */, @@ -2661,7 +2661,7 @@ 28577EB21E8ADC53002B07FD /* SUser.swift in Sources */, 4065D42F1DD4579400B73201 /* TaskInteractor.swift in Sources */, 28577EB61E8AF08F002B07FD /* SQLiteDB.swift in Sources */, - 28A928791E8F78580022AB55 /* SQLiteSchema.swift in Sources */, + 28A928791E8F78580022AB55 /* UpdateSchemaInteractor.swift in Sources */, 4065D4181DD454C500B73201 /* Repository.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; From 357482f9e7b908f4a3713cb1343567eef471e1f4 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 13 Jan 2020 21:04:41 +0200 Subject: [PATCH 18/48] Add metadata repository --- .../CloudKitRepository+Metadata.swift | 22 +++++++ External/Repository.swift | 28 ++++++++- External/sqlite/SMetadata.swift | 22 +++++++ .../sqlite/SqliteRepository+Metadata.swift | 59 +++++++++++++++++++ .../sqlite/SqliteRepository+Settings.swift | 4 +- Jirassic.xcodeproj/project.pbxproj | 18 ++++++ 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 External/CloudKit/CloudKitRepository+Metadata.swift create mode 100644 External/sqlite/SMetadata.swift create mode 100644 External/sqlite/SqliteRepository+Metadata.swift diff --git a/External/CloudKit/CloudKitRepository+Metadata.swift b/External/CloudKit/CloudKitRepository+Metadata.swift new file mode 100644 index 0000000..76c7f75 --- /dev/null +++ b/External/CloudKit/CloudKitRepository+Metadata.swift @@ -0,0 +1,22 @@ +// +// CloudKitRepository+Metadata.swift +// Jirassic +// +// Created by Cristian Baluta on 13/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +extension CloudKitRepository: RepositoryMetadata { + + func tasksLastSyncDate() -> Date? { fatalError("This method is not applicable to CloudKitRepository") } + func projectsLastSyncDate() -> Date? { fatalError("This method is not applicable to CloudKitRepository") } + func tasksLastSyncToken() -> String? { fatalError("This method is not applicable to CloudKitRepository") } + func projectsLastSyncToken() -> String? { fatalError("This method is not applicable to CloudKitRepository") } + func set(tasksLastSyncDate: Date?) { fatalError("This method is not applicable to CloudKitRepository") } + func set(projectsLastSyncDate: Date?) { fatalError("This method is not applicable to CloudKitRepository") } + func set(tasksLastSyncToken: String?) { fatalError("This method is not applicable to CloudKitRepository") } + func set(projectsLastSyncToken: String?) { fatalError("This method is not applicable to CloudKitRepository") } + +} diff --git a/External/Repository.swift b/External/Repository.swift index c4527bd..d32d355 100644 --- a/External/Repository.swift +++ b/External/Repository.swift @@ -49,4 +49,30 @@ protocol RepositoryProjects { } -typealias Repository = RepositoryUser & RepositoryTasks & RepositorySettings & RepositoryProjects +protocol RepositoryMetadata { + + func tasksLastSyncDate() -> Date? + func projectsLastSyncDate() -> Date? + func tasksLastSyncToken() -> String? + func projectsLastSyncToken() -> String? + func set(tasksLastSyncDate: Date?) + func set(projectsLastSyncDate: Date?) + func set(tasksLastSyncToken: String?) + func set(projectsLastSyncToken: String?) + +} + +extension RepositoryMetadata { + + func tasksLastSyncDate() -> Date? { return nil } + func projectsLastSyncDate() -> Date? { return nil } + func tasksLastSyncToken() -> String? { return nil } + func projectsLastSyncToken() -> String? { return nil } + func set(tasksLastSyncDate: Date?) {} + func set(projectsLastSyncDate: Date?) {} + func set(tasksLastSyncToken: String?) {} + func set(projectsLastSyncToken: String?) {} + +} + +typealias Repository = RepositoryUser & RepositoryTasks & RepositorySettings & RepositoryProjects & RepositoryMetadata diff --git a/External/sqlite/SMetadata.swift b/External/sqlite/SMetadata.swift new file mode 100644 index 0000000..2f74dfb --- /dev/null +++ b/External/sqlite/SMetadata.swift @@ -0,0 +1,22 @@ +// +// SMetadata.swift +// Jirassic +// +// Created by Cristian Baluta on 13/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +class SMetadata: SQLTable { + + var i: Int = 0 + var tasksLastSyncDate: Date? = nil + var projectsLastSyncDate: Date? = nil + var tasksLastSyncToken: String? = nil + var projectsLastSyncToken: String? = nil + + override func primaryKey() -> String { + return "i" + } +} diff --git a/External/sqlite/SqliteRepository+Metadata.swift b/External/sqlite/SqliteRepository+Metadata.swift new file mode 100644 index 0000000..bdcbef4 --- /dev/null +++ b/External/sqlite/SqliteRepository+Metadata.swift @@ -0,0 +1,59 @@ +// +// SqliteRepository+Metadata.swift +// Jirassic +// +// Created by Cristian Baluta on 13/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +extension SqliteRepository: RepositoryMetadata { + + func tasksLastSyncDate() -> Date? { return metadata().tasksLastSyncDate } + func projectsLastSyncDate() -> Date? { return metadata().projectsLastSyncDate } + func tasksLastSyncToken() -> String? { return metadata().tasksLastSyncToken } + func projectsLastSyncToken() -> String? { return metadata().projectsLastSyncToken } + func set(tasksLastSyncDate: Date?) { + let meta = metadata() + meta.tasksLastSyncDate = tasksLastSyncDate + saveMetadata(meta) + } + func set(projectsLastSyncDate: Date?) { + let meta = metadata() + meta.projectsLastSyncDate = projectsLastSyncDate + saveMetadata(meta) + } + func set(tasksLastSyncToken: String?) { + let meta = metadata() + meta.tasksLastSyncToken = tasksLastSyncToken + saveMetadata(meta) + } + func set(projectsLastSyncToken: String?) { + let meta = metadata() + meta.projectsLastSyncToken = projectsLastSyncToken + saveMetadata(meta) + } + + + private func metadata() -> SMetadata { + + let results: [SMetadata] = queryWithPredicate(nil, sortingKeyPath: nil) + guard let smetadata = results.first else { + let smetadata = SMetadata() + smetadata.tasksLastSyncDate = nil + smetadata.projectsLastSyncDate = nil + smetadata.tasksLastSyncToken = nil + smetadata.projectsLastSyncToken = nil + return smetadata + } + + return smetadata + } + + + func saveMetadata (_ metadata: SMetadata) { + _ = metadata.save() + } + +} diff --git a/External/sqlite/SqliteRepository+Settings.swift b/External/sqlite/SqliteRepository+Settings.swift index f6df166..35c73fb 100644 --- a/External/sqlite/SqliteRepository+Settings.swift +++ b/External/sqlite/SqliteRepository+Settings.swift @@ -46,7 +46,7 @@ extension SqliteRepository: RepositorySettings { _ = ssettings.save() } - fileprivate func settingsFromSSettings (_ ssettings: SSettings) -> Settings { + private func settingsFromSSettings (_ ssettings: SSettings) -> Settings { return Settings(enableBackup: ssettings.enableBackup, settingsTracking: SettingsTracking( @@ -73,7 +73,7 @@ extension SqliteRepository: RepositorySettings { ) } - fileprivate func ssettingsFromSettings (_ settings: Settings) -> SSettings { + private func ssettingsFromSettings (_ settings: Settings) -> SSettings { let results: [SSettings] = queryWithPredicate(nil, sortingKeyPath: nil) var ssettings: SSettings? = results.first diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 2b6fe81..39298f2 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -269,6 +269,12 @@ 2863495F23CB9134009F81D7 /* ProjectInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */; }; 2863496023CB9134009F81D7 /* ProjectInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */; }; 2863496123CB9134009F81D7 /* ProjectInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */; }; + 2863496323CCF28B009F81D7 /* SqliteRepository+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496223CCF28B009F81D7 /* SqliteRepository+Metadata.swift */; }; + 2863496423CCF28B009F81D7 /* SqliteRepository+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496223CCF28B009F81D7 /* SqliteRepository+Metadata.swift */; }; + 2863496623CCF311009F81D7 /* SMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496523CCF311009F81D7 /* SMetadata.swift */; }; + 2863496723CCF311009F81D7 /* SMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496523CCF311009F81D7 /* SMetadata.swift */; }; + 2863496923CCF53C009F81D7 /* CloudKitRepository+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */; }; + 2863496A23CCF53C009F81D7 /* CloudKitRepository+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */; }; 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 286BC00421909F85004D4CDD /* CloseDayInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */; }; @@ -696,6 +702,9 @@ 2863495623CB7E4D009F81D7 /* CProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CProject.swift; sourceTree = ""; }; 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Projects.swift"; sourceTree = ""; }; 2863495E23CB9134009F81D7 /* ProjectInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectInteractor.swift; sourceTree = ""; }; + 2863496223CCF28B009F81D7 /* SqliteRepository+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SqliteRepository+Metadata.swift"; sourceTree = ""; }; + 2863496523CCF311009F81D7 /* SMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMetadata.swift; sourceTree = ""; }; + 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Metadata.swift"; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDayInteractor.swift; sourceTree = ""; }; 287195142022E1E2001C237E /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; @@ -1068,10 +1077,12 @@ 28A928831E910E5E0022AB55 /* SqliteRepository+User.swift */, 28A928861E910EA70022AB55 /* SqliteRepository+Settings.swift */, 2863494A23CB6F9E009F81D7 /* SqliteRepository+Projects.swift */, + 2863496223CCF28B009F81D7 /* SqliteRepository+Metadata.swift */, 28577EA81E8ADC53002B07FD /* SSettings.swift */, 28577EA91E8ADC53002B07FD /* STask.swift */, 28577EAA1E8ADC53002B07FD /* SUser.swift */, 2863494E23CB73E4009F81D7 /* SProject.swift */, + 2863496523CCF311009F81D7 /* SMetadata.swift */, 28577EB31E8AF08F002B07FD /* SQLiteDB.swift */, 28577EB41E8AF08F002B07FD /* SQLTable.swift */, 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */, @@ -1679,6 +1690,7 @@ 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */, 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */, 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */, + 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */, 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */, ); path = CloudKit; @@ -2282,6 +2294,7 @@ 280D715A1ED6043D005D2689 /* AppWireframe.swift in Sources */, 280D715B1ED6043D005D2689 /* AppViewController.swift in Sources */, 28279AD521C6245700376304 /* GitUsersViewController.swift in Sources */, + 2863496323CCF28B009F81D7 /* SqliteRepository+Metadata.swift in Sources */, 287195322025C364001C237E /* CTask.swift in Sources */, 5683DC3E20ECDED30000A138 /* ModuleCalendar.swift in Sources */, 2892E811208E6275004E5298 /* LocalPreferences.swift in Sources */, @@ -2314,6 +2327,7 @@ 28A283B0203CB1A100DDCB63 /* MergeTasksInteractor.swift in Sources */, 2812F6202145031B008EE81E /* Store.swift in Sources */, 280D716B1ED6043D005D2689 /* AccountViewController.swift in Sources */, + 2863496923CCF53C009F81D7 /* CloudKitRepository+Metadata.swift in Sources */, 56D90CFB20876F2B00F24442 /* WizardGitView.swift in Sources */, 280D716C1ED6043D005D2689 /* CloudKitLoginViewController.swift in Sources */, 2871952D2025C353001C237E /* CoreDataRepository.swift in Sources */, @@ -2389,6 +2403,7 @@ 2845B139206703C5006EFB3B /* Keychain.swift in Sources */, 280D71A61ED6043D005D2689 /* UpdateSchemaInteractor.swift in Sources */, 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */, + 2863496623CCF311009F81D7 /* SMetadata.swift in Sources */, 28279AD121BBF08C00376304 /* InMemoryCoreDataRepository.swift in Sources */, 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, 569C4C5E202319630049FBF1 /* JitCell.swift in Sources */, @@ -2523,6 +2538,7 @@ 28A928931E91104B0022AB55 /* CloudKitRepository+Tasks.swift in Sources */, 566B9FA8217DE67800EAF324 /* TasksInteractor.swift in Sources */, 4065D3B01DD3B44200B73201 /* TaskFinder.swift in Sources */, + 2863496423CCF28B009F81D7 /* SqliteRepository+Metadata.swift in Sources */, 28A283952037874600DDCB63 /* ModuleGitLogs.swift in Sources */, 2863491823B354A0009F81D7 /* CalendarDayCellView.swift in Sources */, 284192D82018841700E64A9A /* JProject.swift in Sources */, @@ -2555,7 +2571,9 @@ 4051D5F91E0EA48A002042BB /* AppLauncher.swift in Sources */, 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, 56ADBF6B21C3F625008350A6 /* GitUser.swift in Sources */, + 2863496A23CCF53C009F81D7 /* CloudKitRepository+Metadata.swift in Sources */, 286348F623B136B3009F81D7 /* ProjectsInteractor.swift in Sources */, + 2863496723CCF311009F81D7 /* SMetadata.swift in Sources */, 2863493C23CB6B08009F81D7 /* ReadProjectsInteractor.swift in Sources */, 28AA500A1EDCD51300AAF03D /* CoreDataRepository+Settings.swift in Sources */, 280D70B11ECC09D9005D2689 /* AppTheme.swift in Sources */, From bda53e7bb4459eb8719a5e0c15ed54935bf75624 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Sun, 19 Jan 2020 12:49:06 +0200 Subject: [PATCH 19/48] Add metadata interactors --- App/Metadata/ReadMetadataInteractor.swift | 45 +++++++++++++++++++ App/Metadata/WriteMetadataInteractor.swift | 44 ++++++++++++++++++ .../CloudKitRepository+Projects.swift | 2 +- .../CloudKit/CloudKitRepository+Tasks.swift | 2 +- External/CloudKit/CloudKitRepository.swift | 4 +- .../sqlite/SqliteRepository+Metadata.swift | 1 + External/sqlite/UpdateSchemaInteractor.swift | 3 +- Jirassic.xcodeproj/project.pbxproj | 20 +++++++++ 8 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 App/Metadata/ReadMetadataInteractor.swift create mode 100644 App/Metadata/WriteMetadataInteractor.swift diff --git a/App/Metadata/ReadMetadataInteractor.swift b/App/Metadata/ReadMetadataInteractor.swift new file mode 100644 index 0000000..3ec6ae6 --- /dev/null +++ b/App/Metadata/ReadMetadataInteractor.swift @@ -0,0 +1,45 @@ +// +// ReadMetadata.swift +// Jirassic +// +// Created by Cristian Baluta on 17/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CloudKit + +class ReadMetadataInteractor: RepositoryInteractor { + + init() { + super.init(repository: localRepository, remoteRepository: nil) + } + + func tasksLastSyncDate() -> Date? { + return repository.tasksLastSyncDate() + } + func projectsLastSyncDate() -> Date? { + return repository.projectsLastSyncDate() + } + + func tasksLastSyncToken() -> CKServerChangeToken? { + let stringToken = repository.tasksLastSyncToken() + return token(from: stringToken) + } + func projectsLastSyncToken() -> CKServerChangeToken? { + let stringToken = repository.projectsLastSyncToken() + return token(from: stringToken) + } + + private func token (from stringToken: String?) -> CKServerChangeToken? { + + guard let data = stringToken?.data(using: .utf8) else { + return nil + } + guard let token = NSKeyedUnarchiver.unarchiveObject(with: data) as? CKServerChangeToken else { + return nil + } + + return token + } +} diff --git a/App/Metadata/WriteMetadataInteractor.swift b/App/Metadata/WriteMetadataInteractor.swift new file mode 100644 index 0000000..0abdd79 --- /dev/null +++ b/App/Metadata/WriteMetadataInteractor.swift @@ -0,0 +1,44 @@ +// +// WriteMetadataInteractor.swift +// Jirassic +// +// Created by Cristian Baluta on 17/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CloudKit + +class WriteMetadataInteractor: RepositoryInteractor { + + init() { + super.init(repository: localRepository, remoteRepository: nil) + } + + func set(tasksLastSyncDate: Date?) { + repository.set(tasksLastSyncDate: tasksLastSyncDate) + } + func set(projectsLastSyncDate: Date?) { + repository.set(tasksLastSyncDate: projectsLastSyncDate) + } + + func set(tasksLastSyncToken: CKServerChangeToken?) { + repository.set(tasksLastSyncToken: string(from: tasksLastSyncToken)) + } + func set(projectsLastSyncToken: CKServerChangeToken?) { + repository.set(tasksLastSyncToken: string(from: projectsLastSyncToken)) + } + + private func string (from token: CKServerChangeToken?) -> String? { + + guard let token = token else { + return nil + } + let data = NSKeyedArchiver.archivedData(withRootObject: token) + guard let string = String(data: data, encoding: .utf8) else { + return nil + } + return string + } +} + diff --git a/External/CloudKit/CloudKitRepository+Projects.swift b/External/CloudKit/CloudKitRepository+Projects.swift index cee253c..874e01c 100644 --- a/External/CloudKit/CloudKitRepository+Projects.swift +++ b/External/CloudKit/CloudKitRepository+Projects.swift @@ -25,7 +25,7 @@ extension CloudKitRepository: RepositoryProjects { func queryUpdates (_ completion: @escaping ([Project], [String], NSError?) -> Void) { - let changeToken = UserDefaults.standard.serverChangeToken + let changeToken = ReadMetadataInteractor().projectsLastSyncToken() fetchChangedRecords(token: changeToken, previousRecords: [], diff --git a/External/CloudKit/CloudKitRepository+Tasks.swift b/External/CloudKit/CloudKitRepository+Tasks.swift index eab11bb..1d9684e 100644 --- a/External/CloudKit/CloudKitRepository+Tasks.swift +++ b/External/CloudKit/CloudKitRepository+Tasks.swift @@ -37,7 +37,7 @@ extension CloudKitRepository: RepositoryTasks { func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) { - let changeToken = UserDefaults.standard.serverChangeToken + let changeToken = ReadMetadataInteractor().tasksLastSyncToken() fetchChangedRecords(token: changeToken, previousRecords: [], diff --git a/External/CloudKit/CloudKitRepository.swift b/External/CloudKit/CloudKitRepository.swift index b5350e0..eea0bb0 100644 --- a/External/CloudKit/CloudKitRepository.swift +++ b/External/CloudKit/CloudKitRepository.swift @@ -78,7 +78,7 @@ extension CloudKitRepository { switch ckerror { case CKError.changeTokenExpired: // Reset the token and try to do the request again - UserDefaults.standard.serverChangeToken = nil + WriteMetadataInteractor().set(tasksLastSyncToken: nil) self.fetchChangedRecords(token: nil, previousRecords: changedRecords, previousDeletedRecordsIds: deletedRecordsIds, @@ -91,7 +91,7 @@ extension CloudKitRepository { completion(changedRecords, deletedRecordsIds) return } - UserDefaults.standard.serverChangeToken = serverChangeToken + WriteMetadataInteractor().set(tasksLastSyncToken: serverChangeToken) if moreComing { self.fetchChangedRecords(token: serverChangeToken, diff --git a/External/sqlite/SqliteRepository+Metadata.swift b/External/sqlite/SqliteRepository+Metadata.swift index bdcbef4..f263f30 100644 --- a/External/sqlite/SqliteRepository+Metadata.swift +++ b/External/sqlite/SqliteRepository+Metadata.swift @@ -14,6 +14,7 @@ extension SqliteRepository: RepositoryMetadata { func projectsLastSyncDate() -> Date? { return metadata().projectsLastSyncDate } func tasksLastSyncToken() -> String? { return metadata().tasksLastSyncToken } func projectsLastSyncToken() -> String? { return metadata().projectsLastSyncToken } + func set(tasksLastSyncDate: Date?) { let meta = metadata() meta.tasksLastSyncDate = tasksLastSyncDate diff --git a/External/sqlite/UpdateSchemaInteractor.swift b/External/sqlite/UpdateSchemaInteractor.swift index 3e93fa4..a20d368 100644 --- a/External/sqlite/UpdateSchemaInteractor.swift +++ b/External/sqlite/UpdateSchemaInteractor.swift @@ -34,6 +34,7 @@ class UpdateSchemaInteractor { private func migrate (db: SQLiteDB, toVersion version: SQLiteSchemaVersion) { + /// Note: TABLES MUST BE AT PLURAL switch version { case .v1: /// Tasks table @@ -49,7 +50,7 @@ class UpdateSchemaInteractor { /// Projects table _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS sprojects (objectId varchar(30) PRIMARY KEY, lastModifiedDate DATETIME, title TEXT, jiraBaseUrl TEXT, jiraUser TEXT, jiraProject TEXT, jiraIssue TEXT, gitBaseUrls TEXT, gitUsers TEXT, taskNumberPrefix TEXT);") /// Metadata table - _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS smetadata (i INTEGER NOT NULL PRIMARY KEY, tasksLastSyncDate DATETIME, projectsLastSyncDate DATETIME, tasksLastSyncToken TEXT, projectsLastSyncToken TEXT);") + _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS smetadatas (i INTEGER NOT NULL PRIMARY KEY, tasksLastSyncDate DATETIME, projectsLastSyncDate DATETIME, tasksLastSyncToken TEXT, projectsLastSyncToken TEXT);") } db.version = version.rawValue } diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 39298f2..2020f71 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -275,6 +275,10 @@ 2863496723CCF311009F81D7 /* SMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496523CCF311009F81D7 /* SMetadata.swift */; }; 2863496923CCF53C009F81D7 /* CloudKitRepository+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */; }; 2863496A23CCF53C009F81D7 /* CloudKitRepository+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */; }; + 2863496D23D21286009F81D7 /* ReadMetadataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496C23D21286009F81D7 /* ReadMetadataInteractor.swift */; }; + 2863496E23D21287009F81D7 /* ReadMetadataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496C23D21286009F81D7 /* ReadMetadataInteractor.swift */; }; + 2863497023D22591009F81D7 /* WriteMetadataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496F23D22591009F81D7 /* WriteMetadataInteractor.swift */; }; + 2863497123D22591009F81D7 /* WriteMetadataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496F23D22591009F81D7 /* WriteMetadataInteractor.swift */; }; 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 286BC00421909F85004D4CDD /* CloseDayInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */; }; @@ -705,6 +709,8 @@ 2863496223CCF28B009F81D7 /* SqliteRepository+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SqliteRepository+Metadata.swift"; sourceTree = ""; }; 2863496523CCF311009F81D7 /* SMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMetadata.swift; sourceTree = ""; }; 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Metadata.swift"; sourceTree = ""; }; + 2863496C23D21286009F81D7 /* ReadMetadataInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMetadataInteractor.swift; sourceTree = ""; }; + 2863496F23D22591009F81D7 /* WriteMetadataInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteMetadataInteractor.swift; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDayInteractor.swift; sourceTree = ""; }; 287195142022E1E2001C237E /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; @@ -1150,6 +1156,15 @@ path = User; sourceTree = ""; }; + 2863496B23D21259009F81D7 /* Metadata */ = { + isa = PBXGroup; + children = ( + 2863496C23D21286009F81D7 /* ReadMetadataInteractor.swift */, + 2863496F23D22591009F81D7 /* WriteMetadataInteractor.swift */, + ); + path = Metadata; + sourceTree = ""; + }; 28667B591FCADF4E007B98E3 /* Modules */ = { isa = PBXGroup; children = ( @@ -1415,6 +1430,7 @@ 4065D3161DD3B44200B73201 /* App */ = { isa = PBXGroup; children = ( + 2863496B23D21259009F81D7 /* Metadata */, 2863493D23CB6E87009F81D7 /* User */, 4065D3171DD3B44200B73201 /* Entities */, 4065D31F1DD3B44200B73201 /* Extensions */, @@ -2262,6 +2278,7 @@ 280D71371ED6043D005D2689 /* ViewAutolayout.swift in Sources */, 285465A7217858790052CB6A /* StoreView.swift in Sources */, 280D71381ED6043D005D2689 /* ViewController.swift in Sources */, + 2863496D23D21286009F81D7 /* ReadMetadataInteractor.swift in Sources */, 2871951E2022FD14001C237E /* InputsScrollView.swift in Sources */, 28FE18A5207B369800DF796E /* CocoaHookupCell.swift in Sources */, 280D71391ED6043D005D2689 /* ViewControllerStoryboard.swift in Sources */, @@ -2270,6 +2287,7 @@ 284192DD2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */, 2812F61E2145031B008EE81E /* IAPHelper.swift in Sources */, + 2863497023D22591009F81D7 /* WriteMetadataInteractor.swift in Sources */, 280D713A1ED6043D005D2689 /* Conversions.swift in Sources */, 280D713B1ED6043D005D2689 /* ComputerWakeUpInteractor.swift in Sources */, 280D713D1ED6043D005D2689 /* CreateReport.swift in Sources */, @@ -2569,6 +2587,7 @@ 2871952C20241B25001C237E /* TrackingView.swift in Sources */, 40FCE4261DF7646F00D4FD45 /* ExtensionsInteractor.swift in Sources */, 4051D5F91E0EA48A002042BB /* AppLauncher.swift in Sources */, + 2863497123D22591009F81D7 /* WriteMetadataInteractor.swift in Sources */, 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, 56ADBF6B21C3F625008350A6 /* GitUser.swift in Sources */, 2863496A23CCF53C009F81D7 /* CloudKitRepository+Metadata.swift in Sources */, @@ -2579,6 +2598,7 @@ 280D70B11ECC09D9005D2689 /* AppTheme.swift in Sources */, 5685C7671DE8721100CA545E /* CloudKitLoginViewController.swift in Sources */, 28AA500E1EDCD51300AAF03D /* CUser.swift in Sources */, + 2863496E23D21287009F81D7 /* ReadMetadataInteractor.swift in Sources */, 2845B165206AE468006EFB3B /* TaskCellPresenter.swift in Sources */, 2871951C2022ECF7001C237E /* OutputsScrollView.swift in Sources */, 2845B1362066C6E6006EFB3B /* AppleScriptProtocol.swift in Sources */, From 7968a27d1ab508845b3626a4a8ef5ff7c9769802 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 20 Jan 2020 01:41:13 +0200 Subject: [PATCH 20/48] Create separate zones for projects and tasks --- App/Entities/Task.swift | 1 + App/Metadata/ReadMetadataInteractor.swift | 7 +- App/Metadata/WriteMetadataInteractor.swift | 4 +- App/Tasks/ReadDaysInteractor.swift | 2 + App/Tasks/TaskInteractor.swift | 5 +- Delivery/macOS/App/AppDelegate.swift | 1 + Delivery/macOS/App/Migrator.swift | 28 ++++ .../CloudKitRepository+Projects.swift | 18 +-- .../CloudKit/CloudKitRepository+Tasks.swift | 26 ++-- .../CloudKit/CloudKitRepository+User.swift | 3 +- External/CloudKit/CloudKitRepository.swift | 110 +-------------- External/CloudKit/CloudKitZone.swift | 131 ++++++++++++++++++ External/CloudKit/UserDefaults+token.swift | 35 ----- .../CoreData/CoreDataRepository+Tasks.swift | 10 +- External/RCSync.swift | 24 ++-- External/Repository.swift | 2 +- External/sqlite/STask.swift | 3 +- External/sqlite/SqliteRepository+Tasks.swift | 17 ++- .../sqlite/UserDefaults+uploadToken.swift | 21 --- Jirassic.xcodeproj/project.pbxproj | 30 ++-- 20 files changed, 244 insertions(+), 234 deletions(-) create mode 100644 Delivery/macOS/App/Migrator.swift create mode 100644 External/CloudKit/CloudKitZone.swift delete mode 100644 External/CloudKit/UserDefaults+token.swift delete mode 100644 External/sqlite/UserDefaults+uploadToken.swift diff --git a/App/Entities/Task.swift b/App/Entities/Task.swift index f1c9af9..d6fc6ac 100644 --- a/App/Entities/Task.swift +++ b/App/Entities/Task.swift @@ -72,6 +72,7 @@ struct Task { /// Created locally and used for matching with the remote object /// If objectId is missing means the task is not saved to db nor to server (eg. unsaved git and calendar items) var objectId: String? + var projectId: String? } extension Task { diff --git a/App/Metadata/ReadMetadataInteractor.swift b/App/Metadata/ReadMetadataInteractor.swift index 3ec6ae6..a94b4ea 100644 --- a/App/Metadata/ReadMetadataInteractor.swift +++ b/App/Metadata/ReadMetadataInteractor.swift @@ -33,10 +33,9 @@ class ReadMetadataInteractor: RepositoryInteractor { private func token (from stringToken: String?) -> CKServerChangeToken? { - guard let data = stringToken?.data(using: .utf8) else { - return nil - } - guard let token = NSKeyedUnarchiver.unarchiveObject(with: data) as? CKServerChangeToken else { + guard let string = stringToken, + let data = Data(base64Encoded: string), + let token = NSKeyedUnarchiver.unarchiveObject(with: data) as? CKServerChangeToken else { return nil } diff --git a/App/Metadata/WriteMetadataInteractor.swift b/App/Metadata/WriteMetadataInteractor.swift index 0abdd79..b8c0ca7 100644 --- a/App/Metadata/WriteMetadataInteractor.swift +++ b/App/Metadata/WriteMetadataInteractor.swift @@ -35,9 +35,7 @@ class WriteMetadataInteractor: RepositoryInteractor { return nil } let data = NSKeyedArchiver.archivedData(withRootObject: token) - guard let string = String(data: data, encoding: .utf8) else { - return nil - } + let string = data.base64EncodedString() return string } } diff --git a/App/Tasks/ReadDaysInteractor.swift b/App/Tasks/ReadDaysInteractor.swift index 4eb8660..90ead67 100644 --- a/App/Tasks/ReadDaysInteractor.swift +++ b/App/Tasks/ReadDaysInteractor.swift @@ -44,6 +44,8 @@ class ReadDaysInteractor: RepositoryInteractor { let sync = RCSync(localRepository: self.repository, remoteRepository: remoteRepository) sync.start { [weak self] hasIncomingChanges in + WriteMetadataInteractor().set(tasksLastSyncDate: Date()) + guard let self = self, hasIncomingChanges else { return } diff --git a/App/Tasks/TaskInteractor.swift b/App/Tasks/TaskInteractor.swift index e792a0e..cace9b9 100644 --- a/App/Tasks/TaskInteractor.swift +++ b/App/Tasks/TaskInteractor.swift @@ -57,8 +57,11 @@ class TaskInteractor: RepositoryInteractor { #if !CMD if let remoteRepository = self.remoteRepository { let sync = RCSync(localRepository: self.repository, remoteRepository: remoteRepository) - sync.uploadTask(task, completion: { (success) in + sync.uploadTask(task, completion: { success, lastSyncDate in DispatchQueue.main.async { + if let date = lastSyncDate { + WriteMetadataInteractor().set(tasksLastSyncDate: date) + } completion(task) } }) diff --git a/Delivery/macOS/App/AppDelegate.swift b/Delivery/macOS/App/AppDelegate.swift index dfe606f..c6fc039 100644 --- a/Delivery/macOS/App/AppDelegate.swift +++ b/Delivery/macOS/App/AppDelegate.swift @@ -49,6 +49,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.window?.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.floatingWindow))) localRepository = SqliteRepository() + Migrator.migrate() #if APPSTORE if SettingsInteractor().getAppSettings().enableBackup { remoteRepository = CloudKitRepository() diff --git a/Delivery/macOS/App/Migrator.swift b/Delivery/macOS/App/Migrator.swift new file mode 100644 index 0000000..0693dd9 --- /dev/null +++ b/Delivery/macOS/App/Migrator.swift @@ -0,0 +1,28 @@ +// +// Migrator.swift +// Jirassic +// +// Created by Cristian Baluta on 19/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CloudKit + +class Migrator { + + static func migrate() { + + if let lastSyncDateWithRemote = UserDefaults.standard.object(forKey: "localChangeDate") as? Date { + WriteMetadataInteractor().set(tasksLastSyncDate: lastSyncDateWithRemote) + UserDefaults.standard.removeObject(forKey: "localChangeDate") + } + + if let data = UserDefaults.standard.value(forKey: "ChangeToken") as? Data, + let token = NSKeyedUnarchiver.unarchiveObject(with: data) as? CKServerChangeToken { + + WriteMetadataInteractor().set(tasksLastSyncToken: token) + UserDefaults.standard.removeObject(forKey: "ChangeToken") + } + } +} diff --git a/External/CloudKit/CloudKitRepository+Projects.swift b/External/CloudKit/CloudKitRepository+Projects.swift index 874e01c..f4471be 100644 --- a/External/CloudKit/CloudKitRepository+Projects.swift +++ b/External/CloudKit/CloudKitRepository+Projects.swift @@ -18,7 +18,7 @@ extension CloudKitRepository: RepositoryProjects { func queryProjects(_ completion: @escaping (_ projects: [Project]) -> Void) { let predicate = NSPredicate(value: true) - fetchRecords(ofType: "Project", predicate: predicate) { records in + projectsZone?.fetchRecords(ofType: "Project", predicate: predicate) { records in completion( self.projectsFromRecords(records ?? []) ) } } @@ -27,10 +27,10 @@ extension CloudKitRepository: RepositoryProjects { let changeToken = ReadMetadataInteractor().projectsLastSyncToken() - fetchChangedRecords(token: changeToken, - previousRecords: [], - previousDeletedRecordsIds: [], - completion: { changedRecords, deletedRecordsIds in + projectsZone?.fetchChangedRecords(token: changeToken, + previousRecords: [], + previousDeletedRecordsIds: [], + completion: { changedRecords, deletedRecordsIds in completion(self.projectsFromRecords(changedRecords), self.stringIdsFromCKRecordIds(deletedRecordsIds), nil) }) @@ -39,7 +39,7 @@ extension CloudKitRepository: RepositoryProjects { func saveProject (_ project: Project, completion: @escaping ((_ task: Project?) -> Void)) { RCLogO("1. Save to cloudkit \(project)") - guard let customZone = self.customZone, let privateDB = self.privateDB else { + guard let zoneId = self.tasksZone?.zoneId, let privateDB = self.privateDB else { RCLog("Can't save, not logged in to iCloud") return } @@ -49,7 +49,7 @@ extension CloudKitRepository: RepositoryProjects { var record: CKRecord? = record // No record found on server, creating one now if record == nil { - let recordId = CKRecord.ID(recordName: project.objectId!, zoneID: customZone.zoneID) + let recordId = CKRecord.ID(recordName: project.objectId!, zoneID: zoneId) record = CKRecord(recordType: "Project", recordID: recordId) } record = self.updatedRecord(record!, withProject: project) @@ -106,14 +106,14 @@ extension CloudKitRepository { func fetchCKRecordOfProject (_ project: Project, completion: @escaping ((_ cproject: CKRecord?) -> Void)) { - guard let customZone = self.customZone, let privateDB = self.privateDB else { + guard let zoneId = self.tasksZone?.zoneId, let privateDB = self.privateDB else { RCLog("Not logged in") return } let predicate = NSPredicate(format: "objectId == %@", project.objectId! as CVarArg) let query = CKQuery(recordType: "Project", predicate: predicate) - privateDB.perform(query, inZoneWith: customZone.zoneID) { (results: [CKRecord]?, error) in + privateDB.perform(query, inZoneWith: zoneId) { (results: [CKRecord]?, error) in RCLogErrorO(error) diff --git a/External/CloudKit/CloudKitRepository+Tasks.swift b/External/CloudKit/CloudKitRepository+Tasks.swift index 1d9684e..7deb5af 100644 --- a/External/CloudKit/CloudKitRepository+Tasks.swift +++ b/External/CloudKit/CloudKitRepository+Tasks.swift @@ -22,12 +22,12 @@ extension CloudKitRepository: RepositoryTasks { func queryTasks (startDate: Date, endDate: Date, predicate: NSPredicate? = nil, completion: @escaping ([Task], NSError?) -> Void) { let predicate = NSPredicate(format: "endDate >= %@ AND endDate <= %@", startDate as CVarArg, endDate as CVarArg) - fetchRecords(ofType: "Task", predicate: predicate) { (records) in + tasksZone?.fetchRecords(ofType: "Task", predicate: predicate) { (records) in completion(self.tasksFromRecords(records ?? []), nil) } } - func queryUnsyncedTasks() -> [Task] { + func queryUnsyncedTasks(since lastSyncDate: Date?) -> [Task] { fatalError("This method is not applicable to CloudKitRepository") } @@ -39,11 +39,11 @@ extension CloudKitRepository: RepositoryTasks { let changeToken = ReadMetadataInteractor().tasksLastSyncToken() - fetchChangedRecords(token: changeToken, - previousRecords: [], - previousDeletedRecordsIds: [], - completion: { (changedRecords, deletedRecordsIds) in - + tasksZone?.fetchChangedRecords(token: changeToken, + previousRecords: [], + previousDeletedRecordsIds: [], + completion: { (changedRecords, deletedRecordsIds) in + completion(self.tasksFromRecords(changedRecords), self.stringIdsFromCKRecordIds(deletedRecordsIds), nil) }) } @@ -76,7 +76,7 @@ extension CloudKitRepository: RepositoryTasks { func saveTask (_ task: Task, completion: @escaping ((_ task: Task?) -> Void)) { RCLogO("1. Save to cloudkit \(task)") - guard let customZone = self.customZone, let privateDB = self.privateDB else { + guard let zoneId = self.tasksZone?.zoneId, let privateDB = self.privateDB else { RCLog("Can't save, not logged in to iCloud") return } @@ -86,7 +86,7 @@ extension CloudKitRepository: RepositoryTasks { var record: CKRecord? = record // No record found on server, creating one now if record == nil { - let recordId = CKRecord.ID(recordName: task.objectId!, zoneID: customZone.zoneID) + let recordId = CKRecord.ID(recordName: task.objectId!, zoneID: zoneId) record = CKRecord(recordType: "Task", recordID: recordId) } record = self.updatedRecord(record!, withTask: task) @@ -122,14 +122,14 @@ extension CloudKitRepository { func fetchCKRecordOfTask (_ task: Task, completion: @escaping ((_ ctask: CKRecord?) -> Void)) { - guard let customZone = self.customZone, let privateDB = self.privateDB else { + guard let zoneId = self.tasksZone?.zoneId, let privateDB = self.privateDB else { RCLog("Not logged in") return } let predicate = NSPredicate(format: "objectId == %@", task.objectId! as CVarArg) let query = CKQuery(recordType: "Task", predicate: predicate) - privateDB.perform(query, inZoneWith: customZone.zoneID) { (results: [CKRecord]?, error) in + privateDB.perform(query, inZoneWith: zoneId) { (results: [CKRecord]?, error) in RCLogErrorO(error) @@ -160,7 +160,8 @@ extension CloudKitRepository { taskNumber: record["taskNumber"] as? String, taskTitle: record["taskTitle"] as? String, taskType: TaskType(rawValue: (record["taskType"] as! NSNumber).intValue)!, - objectId: record["objectId"] as? String + objectId: record["objectId"] as? String, + projectId: record["projectId"] as? String ) } @@ -173,6 +174,7 @@ extension CloudKitRepository { record["taskTitle"] = task.taskTitle as CKRecordValue? record["taskType"] = task.taskType.rawValue as CKRecordValue record["objectId"] = task.objectId as CKRecordValue? + record["projectId"] = task.projectId as CKRecordValue? return record } diff --git a/External/CloudKit/CloudKitRepository+User.swift b/External/CloudKit/CloudKitRepository+User.swift index cc14d9f..1d87e15 100644 --- a/External/CloudKit/CloudKitRepository+User.swift +++ b/External/CloudKit/CloudKitRepository+User.swift @@ -35,6 +35,7 @@ extension CloudKitRepository: RepositoryUser { func logout() { user = nil privateDB = nil - customZone = nil + tasksZone = nil + projectsZone = nil } } diff --git a/External/CloudKit/CloudKitRepository.swift b/External/CloudKit/CloudKitRepository.swift index eea0bb0..80b9dfd 100644 --- a/External/CloudKit/CloudKitRepository.swift +++ b/External/CloudKit/CloudKitRepository.swift @@ -15,7 +15,8 @@ class CloudKitRepository { internal var user: User? internal let container = CKContainer(identifier: "iCloud.com.jirassic.macos") internal var privateDB: CKDatabase? - internal var customZone: CKRecordZone? + internal var tasksZone: CloudKitZone? + internal var projectsZone: CloudKitZone? init() { getUser { [weak self] (user) in @@ -29,111 +30,10 @@ class CloudKitRepository { func initDB() { privateDB = container.privateCloudDatabase - customZone = CKRecordZone(zoneName: "TasksZone") - - privateDB!.save(customZone!) { (recordZone, err) in - RCLogO(recordZone) - RCLogErrorO(err) - } - } -} - -extension CloudKitRepository { - - func fetchChangedRecords (token: CKServerChangeToken?, - previousRecords: [CKRecord], - previousDeletedRecordsIds: [CKRecord.ID], - completion: @escaping ((_ changedRecords: [CKRecord], _ deletedRecordsIds: [CKRecord.ID]) -> Void)) { - - guard let customZone = self.customZone, let privateDB = self.privateDB else { - RCLog("Not logged in") + guard let db = privateDB else { return } - - var changedRecords = previousRecords - var deletedRecordsIds = previousDeletedRecordsIds - - let options = CKFetchRecordZoneChangesOperation.ZoneOptions() - options.previousServerChangeToken = token - - // let op = CKFetchRecordZoneChangesOperation(recordZoneIDs: [customZone.zoneID], previousServerChangeToken: token) - let op = CKFetchRecordZoneChangesOperation(recordZoneIDs: [customZone.zoneID], optionsByRecordZoneID: [customZone.zoneID: options]) - op.fetchAllChanges = true -// let op = CKFetchDatabaseChangesOperation(previousServerChangeToken: token) - - op.recordChangedBlock = { record in - RCLog("Changed record: \(record)") - changedRecords.append(record) - } - op.recordZoneChangeTokensUpdatedBlock = { zoneId, serverChangeToken, data in - - } - op.recordZoneFetchCompletionBlock = { zoneId, serverChangeToken, data, moreComing, error in - RCLogO(serverChangeToken) - RCLogO(data) - RCLogErrorO(error) - - guard error == nil else { - if let ckerror = error as? CKError { - switch ckerror { - case CKError.changeTokenExpired: - // Reset the token and try to do the request again - WriteMetadataInteractor().set(tasksLastSyncToken: nil) - self.fetchChangedRecords(token: nil, - previousRecords: changedRecords, - previousDeletedRecordsIds: deletedRecordsIds, - completion: completion) - return - default: - break - } - } - completion(changedRecords, deletedRecordsIds) - return - } - WriteMetadataInteractor().set(tasksLastSyncToken: serverChangeToken) - - if moreComing { - self.fetchChangedRecords(token: serverChangeToken, - previousRecords: changedRecords, - previousDeletedRecordsIds: deletedRecordsIds, - completion: completion) - } else { - completion(changedRecords, deletedRecordsIds) - } - } - op.fetchRecordZoneChangesCompletionBlock = { error in - - } - op.recordWithIDWasDeletedBlock = { recordID, recordType in - RCLog("Deleted recordID: \(recordID)") - deletedRecordsIds.append(recordID) - } -// op.fetchRecordChangesCompletionBlock = { serverChangeToken, data, error in -// -// -// } - - privateDB.add(op) - } - - func fetchRecords (ofType type: String, predicate: NSPredicate, completion: @escaping ((_ record: [CKRecord]?) -> Void)) { - - guard let customZone = self.customZone, let privateDB = self.privateDB else { - RCLog("Not logged in") - return - } - - let query = CKQuery(recordType: type, predicate: predicate) - privateDB.perform(query, inZoneWith: customZone.zoneID) { (results: [CKRecord]?, error) in - - RCLogErrorO(error) - - if let results = results { - completion(results) - } else { - completion(nil) - } - } + tasksZone = CloudKitZone(db: db, zoneName: "TasksZone") + projectsZone = CloudKitZone(db: db, zoneName: "ProjectsZone") } } diff --git a/External/CloudKit/CloudKitZone.swift b/External/CloudKit/CloudKitZone.swift new file mode 100644 index 0000000..32cbf1b --- /dev/null +++ b/External/CloudKit/CloudKitZone.swift @@ -0,0 +1,131 @@ +// +// CloudKitZone.swift +// Jirassic +// +// Created by Cristian Baluta on 20/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CloudKit +import RCLog + +class CloudKitZone { + + private var db: CKDatabase + private var customZone: CKRecordZone? + + var zoneId: CKRecordZone.ID? { + return customZone?.zoneID + } + + init (db: CKDatabase, zoneName: String) { + + self.db = db + self.customZone = CKRecordZone(zoneName: zoneName) + + db.save(customZone!) { (recordZone, err) in + RCLogO(recordZone) + RCLogErrorO(err) + } + } + + func fetchChangedRecords (token: CKServerChangeToken?, + previousRecords: [CKRecord], + previousDeletedRecordsIds: [CKRecord.ID], + completion: @escaping ((_ changedRecords: [CKRecord], _ deletedRecordsIds: [CKRecord.ID]) -> Void)) { + + guard let zoneId = self.zoneId else { + RCLog("Not logged in") + return + } + + var changedRecords = previousRecords + var deletedRecordsIds = previousDeletedRecordsIds + + let options = CKFetchRecordZoneChangesOperation.ZoneOptions() + options.previousServerChangeToken = token + + // let op = CKFetchRecordZoneChangesOperation(recordZoneIDs: [customZone.zoneID], previousServerChangeToken: token) + let op = CKFetchRecordZoneChangesOperation(recordZoneIDs: [zoneId], + optionsByRecordZoneID: [zoneId: options]) + op.fetchAllChanges = true +// let op = CKFetchDatabaseChangesOperation(previousServerChangeToken: token) + + op.recordChangedBlock = { record in + RCLog("Changed record: \(record)") + changedRecords.append(record) + } + op.recordZoneChangeTokensUpdatedBlock = { zoneId, serverChangeToken, data in + /// Do not save the toke here because the results are not yet saved to local db +// WriteMetadataInteractor().set(tasksLastSyncToken: serverChangeToken) + } + op.recordZoneFetchCompletionBlock = { zoneId, serverChangeToken, data, moreComing, error in + RCLogO(serverChangeToken) + RCLogO(data) + RCLogErrorO(error) + + guard error == nil else { + if let ckerror = error as? CKError { + switch ckerror { + case CKError.changeTokenExpired: + // Reset the token and try to do the request again + WriteMetadataInteractor().set(tasksLastSyncToken: nil) + self.fetchChangedRecords(token: nil, + previousRecords: changedRecords, + previousDeletedRecordsIds: deletedRecordsIds, + completion: completion) + return + default: + break + } + } + completion(changedRecords, deletedRecordsIds) + return + } + WriteMetadataInteractor().set(tasksLastSyncToken: serverChangeToken) + + if moreComing { + self.fetchChangedRecords(token: serverChangeToken, + previousRecords: changedRecords, + previousDeletedRecordsIds: deletedRecordsIds, + completion: completion) + } else { + completion(changedRecords, deletedRecordsIds) + } + } + op.fetchRecordZoneChangesCompletionBlock = { error in + + } + op.recordWithIDWasDeletedBlock = { recordID, recordType in + RCLog("Deleted recordID: \(recordID)") + deletedRecordsIds.append(recordID) + } +// op.fetchRecordChangesCompletionBlock = { serverChangeToken, data, error in +// +// +// } + + db.add(op) + } + + func fetchRecords (ofType type: String, predicate: NSPredicate, completion: @escaping ((_ record: [CKRecord]?) -> Void)) { + + guard let zoneId = self.zoneId else { + RCLog("Not logged in") + return + } + + let query = CKQuery(recordType: type, predicate: predicate) + db.perform(query, inZoneWith: zoneId) { (results: [CKRecord]?, error) in + + RCLogErrorO(error) + + if let results = results { + completion(results) + } else { + completion(nil) + } + } + } +} diff --git a/External/CloudKit/UserDefaults+token.swift b/External/CloudKit/UserDefaults+token.swift deleted file mode 100644 index 44c608d..0000000 --- a/External/CloudKit/UserDefaults+token.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// UserDefaults+token.swift -// Jirassic -// -// Created by Cristian Baluta on 09/04/2017. -// Copyright © 2017 Imagin soft. All rights reserved. -// - -import Foundation -import CloudKit - -public extension UserDefaults { - - var serverChangeToken: CKServerChangeToken? { - get { - guard let data = self.value(forKey: "ChangeToken") as? Data else { - return nil - } - guard let token = NSKeyedUnarchiver.unarchiveObject(with: data) as? CKServerChangeToken else { - return nil - } - - return token - } - set { - if let token = newValue { - let data = NSKeyedArchiver.archivedData(withRootObject: token) - self.set(data, forKey: "ChangeToken") - self.synchronize() - } else { - self.removeObject(forKey: "ChangeToken") - } - } - } -} diff --git a/External/CoreData/CoreDataRepository+Tasks.swift b/External/CoreData/CoreDataRepository+Tasks.swift index 43a2398..6c3c838 100644 --- a/External/CoreData/CoreDataRepository+Tasks.swift +++ b/External/CoreData/CoreDataRepository+Tasks.swift @@ -28,13 +28,13 @@ extension CoreDataRepository: RepositoryTasks { completion(tasks, nil) } - func queryUnsyncedTasks() -> [Task] { + func queryUnsyncedTasks(since lastSyncDate: Date?) -> [Task] { var subpredicates = [ NSPredicate(format: "markedForDeletion == NO || markedForDeletion == nil") ] - if let lastSyncDateWithRemote = UserDefaults.standard.lastSyncDateWithRemote { - subpredicates.append(NSPredicate(format: "lastModifiedDate == nil || lastModifiedDate > %@", lastSyncDateWithRemote as CVarArg)) + if let date = lastSyncDate { + subpredicates.append(NSPredicate(format: "lastModifiedDate == nil || lastModifiedDate > %@", date as CVarArg)) } else { subpredicates.append(NSPredicate(format: "lastModifiedDate == nil")) } @@ -56,7 +56,9 @@ extension CoreDataRepository: RepositoryTasks { func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) { - completion(queryUnsyncedTasks(), [], nil) + let lastSyncDate = ReadMetadataInteractor().tasksLastSyncDate() + let unsyncedTasks = queryUnsyncedTasks(since: lastSyncDate) + completion(unsyncedTasks, [], nil) } func deleteTask (_ task: Task, permanently: Bool, completion: @escaping ((_ success: Bool) -> Void)) { diff --git a/External/RCSync.swift b/External/RCSync.swift index 6196829..b019f58 100644 --- a/External/RCSync.swift +++ b/External/RCSync.swift @@ -28,7 +28,8 @@ class RCSync { RCLog("1. Sync already in progress, not starting it again") return } - objectsToUpload = localRepository.queryUnsyncedTasks() + let lastSyncDate = ReadMetadataInteractor().tasksLastSyncDate() + objectsToUpload = localRepository.queryUnsyncedTasks(since: lastSyncDate) RCLog("1. Nr of unsynced tasks: \(objectsToUpload.count)") localRepository.queryDeletedTasks { deletedTasks in RCLog("1. Nr of deleted tasks: \(deletedTasks.count)") @@ -45,40 +46,39 @@ class RCSync { var task = objectsToUpload.first if task != nil { objectsToUpload.remove(at: 0) - uploadTask(task!, completion: { (success) in + uploadTask(task!, completion: { success, lastSyncDate in self.syncNext(completion) }) } else { task = objectsToDelete.first if task != nil { objectsToDelete.remove(at: 0) - deleteTask(task!, completion: { (success) in + deleteTask(task!, completion: { success in self.syncNext(completion) }) } else { - UserDefaults.standard.lastSyncDateWithRemote = Date() completion(true) } } } - func uploadTask (_ task: Task, completion: @escaping ((_ success: Bool) -> Void)) { + func uploadTask (_ task: Task, completion: @escaping ((_ success: Bool, _ lastSyncDate: Date?) -> Void)) { RCLog("1.1 >>> Save \(task)") _ = remoteRepository.saveTask(task) { uploadedTask in guard let uploadedTask = uploadedTask else { - completion(false) + completion(false, nil) return } RCLog("1.1 Save <<< uploadedTask \(String(describing: uploadedTask.objectId))") // After task was saved to server update it to local datastore _ = self.localRepository.saveTask(uploadedTask, completion: { savedTask in - // If task was saved locally successful update the last sync date - // Otherwise last sync date will be an older date than the tasks last modified date - if let savedTask = savedTask { - UserDefaults.standard.lastSyncDateWithRemote = savedTask.lastModifiedDate - } - completion(savedTask != nil) +// // If task was saved locally successful update the last sync date +// // Otherwise last sync date will be an older date than the tasks last modified date +// if let savedTask = savedTask { +// UserDefaults.standard.lastSyncDateWithRemote = savedTask.lastModifiedDate +// } + completion(savedTask != nil, savedTask?.lastModifiedDate) }) } } diff --git a/External/Repository.swift b/External/Repository.swift index d32d355..f44a0a9 100644 --- a/External/Repository.swift +++ b/External/Repository.swift @@ -22,7 +22,7 @@ protocol RepositoryTasks { func queryTask (withId objectId: String) -> Task? func queryTasks (startDate: Date, endDate: Date, predicate: NSPredicate?) -> [Task] func queryTasks (startDate: Date, endDate: Date, predicate: NSPredicate?, completion: @escaping ([Task], NSError?) -> Void) - func queryUnsyncedTasks() -> [Task] + func queryUnsyncedTasks (since lastSyncDate: Date?) -> [Task] func queryDeletedTasks (_ completion: @escaping ([Task]) -> Void) func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) // Marks the Task as deleted. If permanently is true it will be removed from db diff --git a/External/sqlite/STask.swift b/External/sqlite/STask.swift index e5ec72a..1cacd0d 100644 --- a/External/sqlite/STask.swift +++ b/External/sqlite/STask.swift @@ -19,12 +19,13 @@ class STask: SQLTable { var taskTitle: String? var taskType: Int = 0 var objectId: String? + var projectId: String? override func primaryKey() -> String { return "objectId" } override var description: String { - return "" + return "" } } diff --git a/External/sqlite/SqliteRepository+Tasks.swift b/External/sqlite/SqliteRepository+Tasks.swift index 5bc3d02..f2b3cea 100644 --- a/External/sqlite/SqliteRepository+Tasks.swift +++ b/External/sqlite/SqliteRepository+Tasks.swift @@ -35,14 +35,14 @@ extension SqliteRepository: RepositoryTasks { } } - func queryUnsyncedTasks() -> [Task] { + func queryUnsyncedTasks (since lastSyncDate: Date?) -> [Task] { #if !CMD - RCLog("Query tasks since last sync date: \(String(describing: UserDefaults.standard.lastSyncDateWithRemote))") + RCLog("Query tasks since last sync date: \(String(describing: lastSyncDate))") #endif var sinceDatePredicate = "" - if let lastSyncDateWithRemote = UserDefaults.standard.lastSyncDateWithRemote { - sinceDatePredicate = " OR datetime(lastModifiedDate) > datetime('\(lastSyncDateWithRemote.YYYYMMddHHmmssGMT())')" + if let date = lastSyncDate { + sinceDatePredicate = " OR datetime(lastModifiedDate) > datetime('\(date.YYYYMMddHHmmssGMT())')" } let predicate = "(lastModifiedDate is NULL\(sinceDatePredicate)) AND markedForDeletion == 0" let results: [STask] = queryWithPredicate(predicate, sortingKeyPath: nil) @@ -62,8 +62,9 @@ extension SqliteRepository: RepositoryTasks { func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) { - queryDeletedTasks { (deletedTasks) in - let unsyncedTasks = self.queryUnsyncedTasks() + queryDeletedTasks { deletedTasks in + let lastSyncDate = ReadMetadataInteractor().tasksLastSyncDate() + let unsyncedTasks = self.queryUnsyncedTasks(since: lastSyncDate) let deletedTasksIds = deletedTasks.map{ $0.objectId! } completion(unsyncedTasks, deletedTasksIds, nil) } @@ -134,7 +135,8 @@ extension SqliteRepository { taskNumber: stask.taskNumber, taskTitle: stask.taskTitle, taskType: TaskType(rawValue: stask.taskType)!, - objectId: stask.objectId! + objectId: stask.objectId!, + projectId: stask.projectId ) } @@ -171,6 +173,7 @@ extension SqliteRepository { stask.startDate = task.startDate stask.endDate = task.endDate stask.lastModifiedDate = task.lastModifiedDate + stask.projectId = task.projectId return stask } diff --git a/External/sqlite/UserDefaults+uploadToken.swift b/External/sqlite/UserDefaults+uploadToken.swift deleted file mode 100644 index ba11435..0000000 --- a/External/sqlite/UserDefaults+uploadToken.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// UserDefaults+uploadToken.swift -// Jirassic -// -// Created by Cristian Baluta on 23/04/2017. -// Copyright © 2017 Imagin soft. All rights reserved. -// - -import Foundation - -public extension UserDefaults { - - var lastSyncDateWithRemote: Date? { - get { - return self.object(forKey: "localChangeDate") as? Date - } - set { - self.set(newValue, forKey: "localChangeDate") - } - } -} diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 2020f71..daeb34c 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -48,14 +48,11 @@ 280D70E81ECFE8B9005D2689 /* CloudKitRepository+Tasks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928921E91104B0022AB55 /* CloudKitRepository+Tasks.swift */; }; 280D70E91ECFE8B9005D2689 /* CloudKitRepository+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */; }; 280D70EA1ECFE8B9005D2689 /* CloudKitRepository+User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */; }; - 280D70EB1ECFE8B9005D2689 /* UserDefaults+token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */; }; 280D70F01ECFE8CC005D2689 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3941DD3B44200B73201 /* Repository.swift */; }; 280D70F11ECFE8D0005D2689 /* RepositoryInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3951DD3B44200B73201 /* RepositoryInteractor.swift */; }; 280D70F41ECFE96F005D2689 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D31A1DD3B44200B73201 /* Settings.swift */; }; - 280D70F51ECFE98C005D2689 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; 280D70F71ED0E6EC005D2689 /* StringIdGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3221DD3B44200B73201 /* StringIdGenerator.swift */; }; 280D70FA1ED0E7B0005D2689 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 280D70F91ED0E7B0005D2689 /* CloudKit.framework */; }; - 280D71021ED35A25005D2689 /* UserDefaults+token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */; }; 280D71031ED35A3E005D2689 /* StringArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280F507C1EC868B0007416AB /* StringArray.swift */; }; 280D712C1ED6043D005D2689 /* Day.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3181DD3B44200B73201 /* Day.swift */; }; 280D712D1ED6043D005D2689 /* Report.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3191DD3B44200B73201 /* Report.swift */; }; @@ -126,7 +123,6 @@ 280D71A41ED6043D005D2689 /* SQLiteDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EB31E8AF08F002B07FD /* SQLiteDB.swift */; }; 280D71A51ED6043D005D2689 /* SQLTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EB41E8AF08F002B07FD /* SQLTable.swift */; }; 280D71A61ED6043D005D2689 /* UpdateSchemaInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */; }; - 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; 280D71B61ED6043D005D2689 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3941DD3B44200B73201 /* Repository.swift */; }; 280D71B71ED6043D005D2689 /* RepositoryInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3951DD3B44200B73201 /* RepositoryInteractor.swift */; }; 280D71BC1ED60683005D2689 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 28577EB91E8AF379002B07FD /* libsqlite3.tbd */; }; @@ -279,6 +275,10 @@ 2863496E23D21287009F81D7 /* ReadMetadataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496C23D21286009F81D7 /* ReadMetadataInteractor.swift */; }; 2863497023D22591009F81D7 /* WriteMetadataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496F23D22591009F81D7 /* WriteMetadataInteractor.swift */; }; 2863497123D22591009F81D7 /* WriteMetadataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863496F23D22591009F81D7 /* WriteMetadataInteractor.swift */; }; + 2863497323D4790B009F81D7 /* Migrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863497223D4790B009F81D7 /* Migrator.swift */; }; + 2863497423D4790B009F81D7 /* Migrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863497223D4790B009F81D7 /* Migrator.swift */; }; + 2863497623D51825009F81D7 /* CloudKitZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863497523D51825009F81D7 /* CloudKitZone.swift */; }; + 2863497723D51825009F81D7 /* CloudKitZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2863497523D51825009F81D7 /* CloudKitZone.swift */; }; 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 286BC00421909F85004D4CDD /* CloseDayInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */; }; @@ -391,13 +391,10 @@ 28AA500C1EDCD51300AAF03D /* CSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38D1DD3B44200B73201 /* CSettings.swift */; }; 28AA500D1EDCD51300AAF03D /* CTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38E1DD3B44200B73201 /* CTask.swift */; }; 28AA500E1EDCD51300AAF03D /* CUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38F1DD3B44200B73201 /* CUser.swift */; }; - 28AFE7311E9A594500BAAD8C /* UserDefaults+token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */; }; 28B116BB21AE6179004ACE01 /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */; }; 28B116BC21AE6179004ACE01 /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */; }; 28C6A38421D359E60036DB29 /* RemoveDuplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */; }; 28C6A38521D359E60036DB29 /* RemoveDuplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */; }; - 28C9C6221EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; - 28C9C6231EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; 28CBB596204554A2006F9D3A /* ParseGitBranch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28CBB595204554A2006F9D3A /* ParseGitBranch.swift */; }; 28CBB597204554A2006F9D3A /* ParseGitBranch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28CBB595204554A2006F9D3A /* ParseGitBranch.swift */; }; 28CBB598204554A2006F9D3A /* ParseGitBranch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28CBB595204554A2006F9D3A /* ParseGitBranch.swift */; }; @@ -711,6 +708,8 @@ 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Metadata.swift"; sourceTree = ""; }; 2863496C23D21286009F81D7 /* ReadMetadataInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMetadataInteractor.swift; sourceTree = ""; }; 2863496F23D22591009F81D7 /* WriteMetadataInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteMetadataInteractor.swift; sourceTree = ""; }; + 2863497223D4790B009F81D7 /* Migrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Migrator.swift; sourceTree = ""; }; + 2863497523D51825009F81D7 /* CloudKitZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitZone.swift; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; 286BC00321909F85004D4CDD /* CloseDayInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDayInteractor.swift; sourceTree = ""; }; 287195142022E1E2001C237E /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; @@ -762,10 +761,8 @@ 28A928921E91104B0022AB55 /* CloudKitRepository+Tasks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Tasks.swift"; sourceTree = ""; }; 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+User.swift"; sourceTree = ""; }; 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Settings.swift"; sourceTree = ""; }; - 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+token.swift"; sourceTree = ""; }; 28B116BA21AE6179004ACE01 /* CopyReportCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CopyReportCell.xib; sourceTree = ""; }; 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveDuplicate.swift; sourceTree = ""; }; - 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+uploadToken.swift"; sourceTree = ""; }; 28CBB595204554A2006F9D3A /* ParseGitBranch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseGitBranch.swift; sourceTree = ""; }; 28CBB59920474755006F9D3A /* ParseGitBranchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseGitBranchTests.swift; sourceTree = ""; }; 28DCA4542018B6E700DFAE29 /* JProjectIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JProjectIssue.swift; sourceTree = ""; }; @@ -1092,7 +1089,6 @@ 28577EB31E8AF08F002B07FD /* SQLiteDB.swift */, 28577EB41E8AF08F002B07FD /* SQLTable.swift */, 28A928771E8F78580022AB55 /* UpdateSchemaInteractor.swift */, - 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */, ); path = sqlite; sourceTree = ""; @@ -1595,6 +1591,7 @@ 2823C9341E4F69970055D036 /* Versioning.swift */, 280D70B01ECC09D9005D2689 /* AppTheme.swift */, 2892E810208E6275004E5298 /* LocalPreferences.swift */, + 2863497223D4790B009F81D7 /* Migrator.swift */, ); path = App; sourceTree = ""; @@ -1707,7 +1704,7 @@ 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */, 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */, 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */, - 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */, + 2863497523D51825009F81D7 /* CloudKitZone.swift */, ); path = CloudKit; sourceTree = ""; @@ -2198,7 +2195,6 @@ files = ( 280D70E51ECFE88F005D2689 /* CUser.swift in Sources */, 2863494323CB6E9F009F81D7 /* UserInteractor.swift in Sources */, - 280D70F51ECFE98C005D2689 /* UserDefaults+uploadToken.swift in Sources */, 280D70E91ECFE8B9005D2689 /* CloudKitRepository+Settings.swift in Sources */, 2863495923CB7E4D009F81D7 /* CProject.swift in Sources */, 280D70D21ECFE7ED005D2689 /* Day.swift in Sources */, @@ -2225,7 +2221,6 @@ 280D70E01ECFE88F005D2689 /* CoreDataRepository+Tasks.swift in Sources */, 280D70E31ECFE88F005D2689 /* CSettings.swift in Sources */, 280D70DC1ECFE84C005D2689 /* ReadTasksInteractor.swift in Sources */, - 280D70EB1ECFE8B9005D2689 /* UserDefaults+token.swift in Sources */, 280D70F11ECFE8D0005D2689 /* RepositoryInteractor.swift in Sources */, 280D70D01ECFE5EA005D2689 /* TaskCell.swift in Sources */, 280D70D81ECFE82E005D2689 /* ViewAutolayout.swift in Sources */, @@ -2260,6 +2255,7 @@ 2863491B23B393CF009F81D7 /* CloseDayCell.swift in Sources */, 2863493B23CB6B07009F81D7 /* ReadProjectsInteractor.swift in Sources */, 287195312025C360001C237E /* CSettings.swift in Sources */, + 2863497623D51825009F81D7 /* CloudKitZone.swift in Sources */, 56ADBF6A21C3F625008350A6 /* GitUser.swift in Sources */, 287195302025C35C001C237E /* CoreDataRepository+User.swift in Sources */, 280D71311ED6043D005D2689 /* User.swift in Sources */, @@ -2352,6 +2348,7 @@ 280D716E1ED6043D005D2689 /* LoginPresenter.swift in Sources */, 28FB265B20224EA700AEA38D /* HookupCell.swift in Sources */, 287195332025C367001C237E /* CUser.swift in Sources */, + 2863497323D4790B009F81D7 /* Migrator.swift in Sources */, 280D716F1ED6043D005D2689 /* LoginViewController.swift in Sources */, 2898D3B12185959300CF5AD4 /* EditableTimeBox.swift in Sources */, 2845B149206AE3A8006EFB3B /* ReportCell.swift in Sources */, @@ -2420,7 +2417,6 @@ 280D71A51ED6043D005D2689 /* SQLTable.swift in Sources */, 2845B139206703C5006EFB3B /* Keychain.swift in Sources */, 280D71A61ED6043D005D2689 /* UpdateSchemaInteractor.swift in Sources */, - 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */, 2863496623CCF311009F81D7 /* SMetadata.swift in Sources */, 28279AD121BBF08C00376304 /* InMemoryCoreDataRepository.swift in Sources */, 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, @@ -2476,6 +2472,7 @@ 28577EB51E8AF08F002B07FD /* SQLiteDB.swift in Sources */, 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, 2863494823CB6E9F009F81D7 /* RegisterUserInteractor.swift in Sources */, + 2863497723D51825009F81D7 /* CloudKitZone.swift in Sources */, 286BC00521909F85004D4CDD /* CloseDayInteractor.swift in Sources */, 28A928951E9110980022AB55 /* CloudKitRepository+User.swift in Sources */, 4065D3EF1DD3B44200B73201 /* TasksScrollView.swift in Sources */, @@ -2495,7 +2492,6 @@ 4065D3B21DD3B44200B73201 /* TaskInteractor.swift in Sources */, 2863490723B24C22009F81D7 /* CalendarPresenter.swift in Sources */, 4065D3A21DD3B44200B73201 /* Week.swift in Sources */, - 28AFE7311E9A594500BAAD8C /* UserDefaults+token.swift in Sources */, 4065D3F01DD3B44200B73201 /* TasksViewController.swift in Sources */, 5685C76A1DE8721100CA545E /* LoginViewController.swift in Sources */, 2863491C23B393CF009F81D7 /* CloseDayCell.swift in Sources */, @@ -2533,7 +2529,6 @@ 2892B29C1F094A760085BAC2 /* JiraRepository+Reports.swift in Sources */, 2863490423B24800009F81D7 /* CalendarViewController.swift in Sources */, 2845B1402068351F006EFB3B /* TableViewCell.swift in Sources */, - 28C9C6221EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */, 284192DE2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, 28FE18A6207B369800DF796E /* CocoaHookupCell.swift in Sources */, 4065D3B41DD3B44200B73201 /* TaskTypeEstimator.swift in Sources */, @@ -2586,6 +2581,7 @@ 5685C7691DE8721100CA545E /* LoginPresenter.swift in Sources */, 2871952C20241B25001C237E /* TrackingView.swift in Sources */, 40FCE4261DF7646F00D4FD45 /* ExtensionsInteractor.swift in Sources */, + 2863497423D4790B009F81D7 /* Migrator.swift in Sources */, 4051D5F91E0EA48A002042BB /* AppLauncher.swift in Sources */, 2863497123D22591009F81D7 /* WriteMetadataInteractor.swift in Sources */, 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, @@ -2670,7 +2666,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 28C9C6231EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */, 28577EB81E8AF08F002B07FD /* SQLTable.swift in Sources */, 4065D42D1DD4576C00B73201 /* Day.swift in Sources */, 28577EAC1E8ADC53002B07FD /* SqliteRepository.swift in Sources */, @@ -2689,7 +2684,6 @@ 4065D4301DD4579F00B73201 /* ReadTasksInteractor.swift in Sources */, 4065D4171DD4536200B73201 /* main.swift in Sources */, 4065D42B1DD4576500B73201 /* Settings.swift in Sources */, - 280D71021ED35A25005D2689 /* UserDefaults+token.swift in Sources */, 4065D4281DD4574D00B73201 /* User.swift in Sources */, 4065D4311DD457B000B73201 /* DateExtension.swift in Sources */, 286348FD23B237B2009F81D7 /* Project.swift in Sources */, From aa11724774c938702bc8416174e0216266c351b4 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 20 Jan 2020 09:24:50 +0200 Subject: [PATCH 21/48] Read projects from db --- App/Metadata/ReadMetadataInteractor.swift | 2 +- App/Metadata/WriteMetadataInteractor.swift | 9 ++-- Delivery/macOS/App/AppDelegate.swift | 8 +-- .../Screens/Projects/ProjectsInteractor.swift | 50 +++++++++---------- .../CloudKit/CloudKitRepository+Tasks.swift | 19 ++----- External/RCSync.swift | 8 +-- .../sqlite/SqliteRepository+Projects.swift | 12 +++-- 7 files changed, 48 insertions(+), 60 deletions(-) diff --git a/App/Metadata/ReadMetadataInteractor.swift b/App/Metadata/ReadMetadataInteractor.swift index a94b4ea..147f3eb 100644 --- a/App/Metadata/ReadMetadataInteractor.swift +++ b/App/Metadata/ReadMetadataInteractor.swift @@ -1,5 +1,5 @@ // -// ReadMetadata.swift +// ReadMetadataInteractor.swift // Jirassic // // Created by Cristian Baluta on 17/01/2020. diff --git a/App/Metadata/WriteMetadataInteractor.swift b/App/Metadata/WriteMetadataInteractor.swift index b8c0ca7..aed1d6a 100644 --- a/App/Metadata/WriteMetadataInteractor.swift +++ b/App/Metadata/WriteMetadataInteractor.swift @@ -15,17 +15,17 @@ class WriteMetadataInteractor: RepositoryInteractor { super.init(repository: localRepository, remoteRepository: nil) } - func set(tasksLastSyncDate: Date?) { + func set (tasksLastSyncDate: Date?) { repository.set(tasksLastSyncDate: tasksLastSyncDate) } - func set(projectsLastSyncDate: Date?) { + func set (projectsLastSyncDate: Date?) { repository.set(tasksLastSyncDate: projectsLastSyncDate) } - func set(tasksLastSyncToken: CKServerChangeToken?) { + func set (tasksLastSyncToken: CKServerChangeToken?) { repository.set(tasksLastSyncToken: string(from: tasksLastSyncToken)) } - func set(projectsLastSyncToken: CKServerChangeToken?) { + func set (projectsLastSyncToken: CKServerChangeToken?) { repository.set(tasksLastSyncToken: string(from: projectsLastSyncToken)) } @@ -39,4 +39,3 @@ class WriteMetadataInteractor: RepositoryInteractor { return string } } - diff --git a/Delivery/macOS/App/AppDelegate.swift b/Delivery/macOS/App/AppDelegate.swift index c6fc039..a9d4f46 100644 --- a/Delivery/macOS/App/AppDelegate.swift +++ b/Delivery/macOS/App/AppDelegate.swift @@ -53,11 +53,11 @@ class AppDelegate: NSObject, NSApplicationDelegate { #if APPSTORE if SettingsInteractor().getAppSettings().enableBackup { remoteRepository = CloudKitRepository() - remoteRepository?.getUser({ (user) in + remoteRepository?.getUser { user in if user == nil { remoteRepository = nil } - }) + } } // _ = Store.shared #else @@ -157,7 +157,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { taskNumber: nil, taskTitle: nil, taskType: .coderev, - objectId: String.generateId() + objectId: String.generateId(), + projectId: nil ) let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) saveInteractor.saveTask(task, allowSyncing: true, completion: { savedTask in @@ -260,4 +261,3 @@ extension AppDelegate: NSUserNotificationCenterDelegate { return true } } - diff --git a/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift index fbf66f4..85b39a1 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift @@ -39,31 +39,31 @@ extension ProjectsInteractor: ProjectsInteractorInput { } func reloadProjects() { -// let interactor = ReadProjectsInteractor(repository: localRepository, remoteRepository: remoteRepository) -// let projects = interactor.allProjects() -// presenter?.projectsDidLoad(projects) + let interactor = ReadProjectsInteractor(repository: localRepository, remoteRepository: remoteRepository) + let projects = interactor.allProjects() + presenter?.projectsDidLoad(projects) - presenter?.projectsDidLoad([ - Project(objectId: nil, - lastModifiedDate: nil, - title: "BoschEbike", - jiraBaseUrl: "https://jira.fortech.ro", - jiraUser: "cristianbal", - jiraProject: "BOSCH", - jiraIssue: "BOSCH-100", - gitBaseUrls: [], - gitUsers: ["cristi.baluta@gmail.com", "cristianbal@fortech.ro"], - taskNumberPrefix: "APP"), - Project(objectId: nil, - lastModifiedDate: nil, - title: "Terma", - jiraBaseUrl: "https://jira.fortech.ro", - jiraUser: "cristianbal", - jiraProject: "TERMA", - jiraIssue: "TERMA-001", - gitBaseUrls: [], - gitUsers: [], - taskNumberPrefix: "TERMA") - ]) +// presenter?.projectsDidLoad([ +// Project(objectId: nil, +// lastModifiedDate: nil, +// title: "BoschEbike", +// jiraBaseUrl: "https://jira.fortech.ro", +// jiraUser: "cristianbal", +// jiraProject: "BOSCH", +// jiraIssue: "BOSCH-100", +// gitBaseUrls: [], +// gitUsers: ["cristi.baluta@gmail.com", "cristianbal@fortech.ro"], +// taskNumberPrefix: "APP"), +// Project(objectId: nil, +// lastModifiedDate: nil, +// title: "Terma", +// jiraBaseUrl: "https://jira.fortech.ro", +// jiraUser: "cristianbal", +// jiraProject: "TERMA", +// jiraIssue: "TERMA-001", +// gitBaseUrls: [], +// gitUsers: [], +// taskNumberPrefix: "TERMA") +// ]) } } diff --git a/External/CloudKit/CloudKitRepository+Tasks.swift b/External/CloudKit/CloudKitRepository+Tasks.swift index 7deb5af..94088b3 100644 --- a/External/CloudKit/CloudKitRepository+Tasks.swift +++ b/External/CloudKit/CloudKitRepository+Tasks.swift @@ -93,8 +93,7 @@ extension CloudKitRepository: RepositoryTasks { privateDB.save(record!, completionHandler: { savedRecord, error in - RCLog("2. Record after saving to CloudKit") - RCLogO(savedRecord) + RCLog("2. Saved to CloudKit \(String(describing: savedRecord))") RCLogErrorO(error) if let record = savedRecord { @@ -142,13 +141,7 @@ extension CloudKitRepository { } private func tasksFromRecords (_ records: [CKRecord]) -> [Task] { - - var tasks = [Task]() - for record in records { - tasks.append( taskFromRecord(record) ) - } - - return tasks + return records.map({ taskFromRecord($0) }) } private func taskFromRecord (_ record: CKRecord) -> Task { @@ -180,12 +173,6 @@ extension CloudKitRepository { } private func stringIdsFromCKRecordIds (_ ckrecords: [CKRecord.ID]) -> [String] { - - var ids = [String]() - for ckrecord in ckrecords { - ids.append( ckrecord.recordName ) - } - - return ids + return ckrecords.map({ $0.recordName }) } } diff --git a/External/RCSync.swift b/External/RCSync.swift index b019f58..9a443ec 100644 --- a/External/RCSync.swift +++ b/External/RCSync.swift @@ -86,9 +86,9 @@ class RCSync { func deleteTask (_ task: Task, completion: @escaping ((_ success: Bool) -> Void)) { RCLog("1.1 Delete \(task)") - _ = remoteRepository.deleteTask(task, permanently: true) { (uploadedTask) in + _ = remoteRepository.deleteTask(task, permanently: true) { uploadedTask in // After task was marked as deleted to server, delete it permanently from local db - _ = self.localRepository.deleteTask(task, permanently: true, completion: { (task) in + _ = self.localRepository.deleteTask(task, permanently: true, completion: { task in completion(true) }) } @@ -100,13 +100,13 @@ class RCSync { remoteRepository.queryUpdates { changedTasks, deletedTasksIds, error in RCLog("2. Number of changes: \(changedTasks.count)") for task in changedTasks { - self.localRepository.saveTask(task, completion: { (task) in + self.localRepository.saveTask(task, completion: { task in RCLog("2. Saved to local db \(String(describing: task?.objectId))") }) } RCLog("2. Number of deletes: \(deletedTasksIds.count)") for remoteId in deletedTasksIds { - self.localRepository.deleteTask(objectId: remoteId, completion: { (success) in + self.localRepository.deleteTask(objectId: remoteId, completion: { success in RCLog("2. Deleted from local db: \(remoteId) \(success)") }) } diff --git a/External/sqlite/SqliteRepository+Projects.swift b/External/sqlite/SqliteRepository+Projects.swift index 63263dd..e94239f 100644 --- a/External/sqlite/SqliteRepository+Projects.swift +++ b/External/sqlite/SqliteRepository+Projects.swift @@ -11,14 +11,16 @@ import Foundation extension SqliteRepository: RepositoryProjects { func projects() -> [Project] { - return [] + + let projects: [SProject] = queryWithPredicate(nil, sortingKeyPath: nil) + return projects.map({ projectFromSProject($0) }) } - func queryProjects(_ completion: @escaping ((_ task: [Project]) -> Void)) { - + func queryProjects(_ completion: @escaping ((_ projects: [Project]) -> Void)) { + completion(projects()) } - - func saveProject (_ project: Project, completion: @escaping ((_ task: Project?) -> Void)) { + + func saveProject (_ project: Project, completion: @escaping ((_ project: Project?) -> Void)) { } From ab464d88fb3bfa45714ca368e5fe34310092de74 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 20 Jan 2020 10:17:13 +0200 Subject: [PATCH 22/48] Add save projects method --- .../macOS/Screens/Projects/ProjectDetailsPresenter.swift | 5 +++++ .../Screens/Projects/ProjectDetailsViewController.swift | 2 +- .../Screens/Projects/ProjectsListViewController.swift | 8 -------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index 7395783..0e4470f 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -13,6 +13,7 @@ protocol ProjectDetailsPresenterInput: class { func didPickUrl (_ url: URL) func save (emails: String, paths: String) + func saveProject (_ project: Project) } protocol ProjectDetailsPresenterOutput: class { @@ -59,6 +60,10 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { savePaths(paths) } + func saveProject (_ project: Project) { + + } + private func saveEmails (_ emails: String) { } diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 6c6406e..494145d 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -76,7 +76,7 @@ class ProjectDetailsViewController: NSViewController { } @IBAction func handleSaveButton (_ sender: NSButton) { - +// presenter!.saveProject(project) } @IBAction func handleDeleteButton (_ sender: NSButton) { diff --git a/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift index 2fe1c7f..d292c26 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift @@ -13,7 +13,6 @@ class ProjectsListViewController: NSViewController { @IBOutlet private weak var scrollView: NSScrollView! @IBOutlet private weak var tableView: NSTableView! @IBOutlet private weak var butAdd: NSButton! - @IBOutlet private weak var butRemove: NSButton! private var selectedProject: Project? @@ -35,13 +34,6 @@ class ProjectsListViewController: NSViewController { @IBAction func handleAddButton (_ sender: NSButton) { didSelectAddProject?() } - - @IBAction func handleRemoveButton (_ sender: NSButton) { - guard let project = selectedProject else { - return - } - didSelectRemoveProject?(project) - } } extension ProjectsListViewController: NSTableViewDataSource { From cbf4003962927fd770e450cbdc0238532de57194 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Mon, 20 Jan 2020 15:51:03 +0200 Subject: [PATCH 23/48] Save project to db --- App/Projects/ProjectInteractor.swift | 2 +- .../Projects/ProjectDetailsPresenter.swift | 13 +++- .../ProjectDetailsViewController.swift | 13 +++- .../Screens/Projects/Projects.storyboard | 15 ++--- .../Projects/ProjectsListViewController.swift | 13 ++-- .../Screens/Projects/ProjectsPresenter.swift | 2 +- .../Projects/ProjectsViewController.swift | 10 +++ External/sqlite/SProject.swift | 41 ++++++++++++ External/sqlite/STask.swift | 36 ++++++++++ .../sqlite/SqliteRepository+Projects.swift | 67 +++++++++---------- External/sqlite/SqliteRepository+Tasks.swift | 46 ++----------- External/sqlite/UpdateSchemaInteractor.swift | 2 +- 12 files changed, 163 insertions(+), 97 deletions(-) diff --git a/App/Projects/ProjectInteractor.swift b/App/Projects/ProjectInteractor.swift index a830ba0..246ceef 100644 --- a/App/Projects/ProjectInteractor.swift +++ b/App/Projects/ProjectInteractor.swift @@ -17,7 +17,7 @@ class ProjectInteractor: RepositoryInteractor { func saveProject (_ project: Project, allowSyncing: Bool, completion: @escaping (_ savedProject: Project?) -> Void) { guard project.objectId != nil else { - fatalError("Cannot save a task without objectId") + fatalError("Cannot save a project without objectId") } var project = project project.lastModifiedDate = nil diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index 0e4470f..dce8e02 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -14,6 +14,7 @@ protocol ProjectDetailsPresenterInput: class { func didPickUrl (_ url: URL) func save (emails: String, paths: String) func saveProject (_ project: Project) + func deleteProject (_ project: Project) } protocol ProjectDetailsPresenterOutput: class { @@ -61,7 +62,17 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { } func saveProject (_ project: Project) { - + if project.objectId == nil { + + } + let interactor = ProjectInteractor(repository: localRepository, remoteRepository: remoteRepository) + interactor.saveProject(project, allowSyncing: true) { savedProject in + + } + } + + func deleteProject (_ project: Project) { + } private func saveEmails (_ emails: String) { diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 494145d..0a78e0f 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -76,11 +76,20 @@ class ProjectDetailsViewController: NSViewController { } @IBAction func handleSaveButton (_ sender: NSButton) { -// presenter!.saveProject(project) + + project!.jiraBaseUrl = baseUrlTextField.stringValue + project!.jiraUser = userTextField.stringValue + project!.jiraProject = projectNamePopup.selectedItem?.title + project!.jiraIssue = projectIssueNamePopup.selectedItem?.title + project!.gitBaseUrls = pathsTextField.stringValue.toArray() + project!.gitUsers = emailsTextField.stringValue.toArray() + project!.taskNumberPrefix = taskNumberPrefixTextField.stringValue + + presenter!.saveProject(project!) } @IBAction func handleDeleteButton (_ sender: NSButton) { - + presenter!.deleteProject(project!) } @IBAction func handleCredentialsButton (_ sender: NSButton) { diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard index c378330..15003ab 100644 --- a/Delivery/macOS/Screens/Projects/Projects.storyboard +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -14,22 +14,21 @@ - + - + - - + - + @@ -228,7 +227,7 @@ - + @@ -246,7 +245,7 @@ - + @@ -295,7 +294,7 @@ - + diff --git a/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift index d292c26..fadfdd3 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift @@ -14,7 +14,7 @@ class ProjectsListViewController: NSViewController { @IBOutlet private weak var tableView: NSTableView! @IBOutlet private weak var butAdd: NSButton! - private var selectedProject: Project? +// private var selectedProject: Project? var projects = [Project]() { didSet { @@ -22,6 +22,7 @@ class ProjectsListViewController: NSViewController { } } var didSelectProject: ((Project) -> Void)? + var didUpdateProject: ((Project) -> Void)? var didSelectAddProject: (() -> Void)? var didSelectRemoveProject: ((Project) -> Void)? @@ -48,9 +49,6 @@ extension ProjectsListViewController: NSTableViewDelegate { func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { let project = projects[row] - if (tableColumn?.identifier)?.rawValue == "isSelected" { - return nil - } if (tableColumn?.identifier)?.rawValue == "name" { return project.title } @@ -59,13 +57,14 @@ extension ProjectsListViewController: NSTableViewDelegate { func tableView(_ tableView: NSTableView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, row: Int) { - if (tableColumn?.identifier)?.rawValue == "isSelected" { - + if (tableColumn?.identifier)?.rawValue == "name" { + projects[row].title = object as? String ?? "" + didUpdateProject?(projects[row]) } } func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { - selectedProject = projects[row] +// selectedProject = projects[row] didSelectProject?(projects[row]) return true } diff --git a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift index 70c381e..ddd1c24 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift @@ -38,7 +38,7 @@ extension ProjectsPresenter: ProjectsPresenterInput { func addProject() { ui!.hideMessage() // interactor!.addProject() - let newProject = Project(objectId: nil, + let newProject = Project(objectId: String.generateId(), lastModifiedDate: nil, title: "Project \(projects.count + 1)", jiraBaseUrl: "https://", diff --git a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift index 2dabd21..2c8588d 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift @@ -27,10 +27,12 @@ class ProjectsViewController: NSSplitViewController { return } let c1 = ProjectsListViewController.instantiateFromStoryboard("Projects") + let c2 = ProjectDetailsViewController.instantiateFromStoryboard("Projects") let c2Presenter = ProjectDetailsPresenter() c2.presenter = c2Presenter c2Presenter.ui = c2 + let s1 = NSSplitViewItem(viewController: c1) s1.minimumThickness = 120 let s2 = NSSplitViewItem(viewController: c2) @@ -69,6 +71,14 @@ extension ProjectsViewController: ProjectsPresenterOutput { } controller.project = project } + controller.didUpdateProject = { project in + + guard let controller = self.splitViewItems.last?.viewController as? ProjectDetailsViewController else { + return + } + /// In the list of projects only the title can change + controller.project?.title = project.title + } controller.didSelectAddProject = { self.presenter?.addProject() } diff --git a/External/sqlite/SProject.swift b/External/sqlite/SProject.swift index 5015fc6..f1fa277 100644 --- a/External/sqlite/SProject.swift +++ b/External/sqlite/SProject.swift @@ -12,6 +12,7 @@ class SProject: SQLTable { var objectId: String? = nil var lastModifiedDate: Date? = nil + var markedForDeletion = false var title: String? = nil var jiraBaseUrl: String? = nil var jiraUser: String? = nil @@ -26,3 +27,43 @@ class SProject: SQLTable { return "objectId" } } + +extension SProject { + + func toProject() -> Project { + + return Project( + objectId: self.objectId, + lastModifiedDate: self.lastModifiedDate, + title: self.title ?? "", + jiraBaseUrl: self.jiraBaseUrl, + jiraUser: self.jiraUser, + jiraProject: self.jiraProject, + jiraIssue: self.jiraIssue, + + gitBaseUrls: (self.gitBaseUrls ?? "").toArray(), + gitUsers: (self.gitBaseUrls ?? "").toArray(), + taskNumberPrefix: self.taskNumberPrefix + ) + } +} + +extension Project { + + func toSProject() -> SProject { + + let sproject = SProject() + sproject.objectId = self.objectId + sproject.lastModifiedDate = self.lastModifiedDate + sproject.title = self.title + sproject.jiraBaseUrl = self.jiraBaseUrl + sproject.jiraUser = self.jiraUser + sproject.jiraProject = self.jiraProject + sproject.jiraIssue = self.jiraIssue + sproject.gitBaseUrls = self.gitBaseUrls.toString() + sproject.gitUsers = self.gitUsers.toString() + sproject.taskNumberPrefix = self.taskNumberPrefix + + return sproject + } +} diff --git a/External/sqlite/STask.swift b/External/sqlite/STask.swift index 1cacd0d..151f8b9 100644 --- a/External/sqlite/STask.swift +++ b/External/sqlite/STask.swift @@ -29,3 +29,39 @@ class STask: SQLTable { return "" } } + +extension STask { + + func toTask() -> Task { + + return Task(lastModifiedDate: self.lastModifiedDate, + startDate: self.startDate, + endDate: self.endDate!, + notes: self.notes, + taskNumber: self.taskNumber, + taskTitle: self.taskTitle, + taskType: TaskType(rawValue: self.taskType)!, + objectId: self.objectId!, + projectId: self.projectId + ) + } + + func update (with task: Task) { + // Update only updatable properties. objectId can't be updated + self.taskNumber = task.taskNumber + self.taskType = task.taskType.rawValue + self.taskTitle = task.taskTitle + self.notes = task.notes + self.startDate = task.startDate + self.endDate = task.endDate + self.lastModifiedDate = task.lastModifiedDate + self.projectId = task.projectId + } +} + +//extension Task { +// +// func toSTask() -> STask { +// +// } +//} diff --git a/External/sqlite/SqliteRepository+Projects.swift b/External/sqlite/SqliteRepository+Projects.swift index e94239f..9cb3963 100644 --- a/External/sqlite/SqliteRepository+Projects.swift +++ b/External/sqlite/SqliteRepository+Projects.swift @@ -7,13 +7,14 @@ // import Foundation +import RCLog extension SqliteRepository: RepositoryProjects { func projects() -> [Project] { let projects: [SProject] = queryWithPredicate(nil, sortingKeyPath: nil) - return projects.map({ projectFromSProject($0) }) + return projects.map({ $0.toProject() }) } func queryProjects(_ completion: @escaping ((_ projects: [Project]) -> Void)) { @@ -21,44 +22,38 @@ extension SqliteRepository: RepositoryProjects { } func saveProject (_ project: Project, completion: @escaping ((_ project: Project?) -> Void)) { - + + let sproject = project.toSProject() + let saved = sproject.save() + #if !CMD + RCLog("Project saved to sqlite \(saved) \(project)") + #endif + if saved == 1 { + completion( sproject.toProject() ) + } else { + completion(nil) + } } func deleteProject (_ project: Project, permanently: Bool, completion: @escaping ((_ success: Bool) -> Void)) { - - } - - private func projectFromSProject (_ sproject: SProject) -> Project { - - return Project( - objectId: sproject.objectId, - lastModifiedDate: sproject.lastModifiedDate, - title: sproject.title ?? "", - jiraBaseUrl: sproject.jiraBaseUrl, - jiraUser: sproject.jiraUser, - jiraProject: sproject.jiraProject, - jiraIssue: sproject.jiraIssue, - - gitBaseUrls: (sproject.gitBaseUrls ?? "").toArray(), - gitUsers: (sproject.gitBaseUrls ?? "").toArray(), - taskNumberPrefix: sproject.taskNumberPrefix - ) + + let sproject = project.toSProject() + if permanently { + completion( sproject.delete() ) + } else { + sproject.markedForDeletion = true + completion( sproject.save() == 1 ) + } } - - private func sprojectFromProject (_ project: Project) -> SProject { - - let sproject = SProject() - sproject.objectId = project.objectId - sproject.lastModifiedDate = project.lastModifiedDate - sproject.title = project.title - sproject.jiraBaseUrl = project.jiraBaseUrl - sproject.jiraUser = project.jiraUser - sproject.jiraProject = project.jiraProject - sproject.jiraIssue = project.jiraIssue - sproject.gitBaseUrls = project.gitBaseUrls.toString() - sproject.gitUsers = project.gitUsers.toString() - sproject.taskNumberPrefix = project.taskNumberPrefix - - return sproject + + func deleteProject (objectId: String, completion: @escaping ((_ success: Bool) -> Void)) { + + let projectPredicate = "objectId == '\(objectId)'" + let projects: [SProject] = queryWithPredicate(projectPredicate, sortingKeyPath: nil) + if let sproject = projects.first { + completion( sproject.delete() ) + } else { + completion( false ) + } } } diff --git a/External/sqlite/SqliteRepository+Tasks.swift b/External/sqlite/SqliteRepository+Tasks.swift index f2b3cea..950ec9f 100644 --- a/External/sqlite/SqliteRepository+Tasks.swift +++ b/External/sqlite/SqliteRepository+Tasks.swift @@ -16,7 +16,7 @@ extension SqliteRepository: RepositoryTasks { let taskPredicate = "objectId == '\(objectId)'" let tasks: [STask] = queryWithPredicate(taskPredicate, sortingKeyPath: nil) if let stask = tasks.first { - return taskFromSTask(stask) + return stask.toTask() } return nil } @@ -100,7 +100,7 @@ extension SqliteRepository: RepositoryTasks { RCLog("Saved to sqlite \(saved) \(task)") #endif if saved == 1 { - completion( taskFromSTask(stask)) + completion( stask.toTask() ) } else { completion(nil) } @@ -126,28 +126,8 @@ extension SqliteRepository { return tasks } - private func taskFromSTask (_ stask: STask) -> Task { - - return Task(lastModifiedDate: stask.lastModifiedDate, - startDate: stask.startDate, - endDate: stask.endDate!, - notes: stask.notes, - taskNumber: stask.taskNumber, - taskTitle: stask.taskTitle, - taskType: TaskType(rawValue: stask.taskType)!, - objectId: stask.objectId!, - projectId: stask.projectId - ) - } - - private func tasksFromSTasks (_ rtasks: [STask]) -> [Task] { - - var tasks = [Task]() - for rtask in rtasks { - tasks.append( taskFromSTask(rtask) ) - } - - return tasks + private func tasksFromSTasks (_ stasks: [STask]) -> [Task] { + return stasks.map({ $0.toTask() }) } private func staskFromTask (_ task: Task) -> STask { @@ -159,22 +139,8 @@ extension SqliteRepository { stask = STask() stask!.objectId = task.objectId } + stask!.update(with: task) - return updatedSTask(stask!, withTask: task) - } - - // Update only updatable properties. objectId can't be updated - private func updatedSTask (_ stask: STask, withTask task: Task) -> STask { - - stask.taskNumber = task.taskNumber - stask.taskType = task.taskType.rawValue - stask.taskTitle = task.taskTitle - stask.notes = task.notes - stask.startDate = task.startDate - stask.endDate = task.endDate - stask.lastModifiedDate = task.lastModifiedDate - stask.projectId = task.projectId - - return stask + return stask! } } diff --git a/External/sqlite/UpdateSchemaInteractor.swift b/External/sqlite/UpdateSchemaInteractor.swift index a20d368..1e0902d 100644 --- a/External/sqlite/UpdateSchemaInteractor.swift +++ b/External/sqlite/UpdateSchemaInteractor.swift @@ -48,7 +48,7 @@ class UpdateSchemaInteractor { /// Edit Tasks table _ = db.execute(sql: "ALTER TABLE stasks ADD COLUMN projectId varchar(30);") /// Projects table - _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS sprojects (objectId varchar(30) PRIMARY KEY, lastModifiedDate DATETIME, title TEXT, jiraBaseUrl TEXT, jiraUser TEXT, jiraProject TEXT, jiraIssue TEXT, gitBaseUrls TEXT, gitUsers TEXT, taskNumberPrefix TEXT);") + _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS sprojects (objectId varchar(30) PRIMARY KEY, lastModifiedDate DATETIME, markedForDeletion BOOL DEFAULT 0, title TEXT, jiraBaseUrl TEXT, jiraUser TEXT, jiraProject TEXT, jiraIssue TEXT, gitBaseUrls TEXT, gitUsers TEXT, taskNumberPrefix TEXT);") /// Metadata table _ = db.execute(sql: "CREATE TABLE IF NOT EXISTS smetadatas (i INTEGER NOT NULL PRIMARY KEY, tasksLastSyncDate DATETIME, projectsLastSyncDate DATETIME, tasksLastSyncToken TEXT, projectsLastSyncToken TEXT);") } From ee8502ff31022eac05bd1e02d6ac338dff9e334d Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Tue, 21 Jan 2020 10:12:57 +0200 Subject: [PATCH 24/48] Save project to db --- .../ProjectDetailsViewController.swift | 1 - .../Screens/Projects/ProjectsInteractor.swift | 35 ---------- .../Screens/Projects/ProjectsPresenter.swift | 14 ++-- .../Projects/ProjectsViewController.swift | 64 +++++++------------ 4 files changed, 29 insertions(+), 85 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 0a78e0f..d6d1eb2 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -70,7 +70,6 @@ class ProjectDetailsViewController: NSViewController { taskNumberPrefixTextField.stringValue = project.taskNumberPrefix ?? "" } - @IBAction func handlePickerButton (_ sender: NSButton) { pickPath() } diff --git a/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift index 85b39a1..5b025ee 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsInteractor.swift @@ -11,8 +11,6 @@ import Foundation protocol ProjectsInteractorInput: class { func reloadProjects() - func addProject() - func removeProject (_ project: Project) } protocol ProjectsInteractorOutput: class { @@ -28,42 +26,9 @@ class ProjectsInteractor { extension ProjectsInteractor: ProjectsInteractorInput { - func addProject() { -// presenter?.projectsDidLoad([ -// -// ]) - } - - func removeProject (_ project: Project) { - - } - func reloadProjects() { let interactor = ReadProjectsInteractor(repository: localRepository, remoteRepository: remoteRepository) let projects = interactor.allProjects() presenter?.projectsDidLoad(projects) - -// presenter?.projectsDidLoad([ -// Project(objectId: nil, -// lastModifiedDate: nil, -// title: "BoschEbike", -// jiraBaseUrl: "https://jira.fortech.ro", -// jiraUser: "cristianbal", -// jiraProject: "BOSCH", -// jiraIssue: "BOSCH-100", -// gitBaseUrls: [], -// gitUsers: ["cristi.baluta@gmail.com", "cristianbal@fortech.ro"], -// taskNumberPrefix: "APP"), -// Project(objectId: nil, -// lastModifiedDate: nil, -// title: "Terma", -// jiraBaseUrl: "https://jira.fortech.ro", -// jiraUser: "cristianbal", -// jiraProject: "TERMA", -// jiraIssue: "TERMA-001", -// gitBaseUrls: [], -// gitUsers: [], -// taskNumberPrefix: "TERMA") -// ]) } } diff --git a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift index ddd1c24..1c6b347 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift @@ -15,7 +15,8 @@ protocol ProjectsPresenterInput: class { } protocol ProjectsPresenterOutput: class { - func projectsDidLoad(_ projects: [Project]) + func showProjects(_ projects: [Project]) + func hideProjects() func showMessage (_ message: MessageViewModel) func hideMessage() } @@ -32,16 +33,16 @@ class ProjectsPresenter { extension ProjectsPresenter: ProjectsPresenterInput { func reloadProjects() { + ui!.hideProjects() interactor!.reloadProjects() } func addProject() { ui!.hideMessage() -// interactor!.addProject() let newProject = Project(objectId: String.generateId(), lastModifiedDate: nil, title: "Project \(projects.count + 1)", - jiraBaseUrl: "https://", + jiraBaseUrl: nil, jiraUser: nil, jiraProject: nil, jiraIssue: nil, @@ -53,9 +54,8 @@ extension ProjectsPresenter: ProjectsPresenterInput { } func removeProject (_ project: Project) { -// interactor!.removeProject(project) - let newProjects = projects.filter( { $0.title != project.title } ) - projectsDidLoad(newProjects) + projects = projects.filter( { $0.title != project.title } ) + projectsDidLoad(projects) } } @@ -69,7 +69,7 @@ extension ProjectsPresenter: ProjectsInteractorOutput { message: "Add your first project!", buttonTitle: "Add")) } else { - ui!.projectsDidLoad(projects) + ui!.showProjects(projects) } } } diff --git a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift index 2c8588d..7e1bc3c 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift @@ -20,32 +20,12 @@ class ProjectsViewController: NSSplitViewController { presenter!.reloadProjects() } - - private func setupControllers() { - - guard self.splitViewItems.count == 0 else { - return - } - let c1 = ProjectsListViewController.instantiateFromStoryboard("Projects") - - let c2 = ProjectDetailsViewController.instantiateFromStoryboard("Projects") - let c2Presenter = ProjectDetailsPresenter() - c2.presenter = c2Presenter - c2Presenter.ui = c2 - - let s1 = NSSplitViewItem(viewController: c1) - s1.minimumThickness = 120 - let s2 = NSSplitViewItem(viewController: c2) - self.splitViewItems = [s1, s2] - } } extension ProjectsViewController: ProjectsPresenterOutput { func showMessage (_ message: MessageViewModel) { - self.splitViewItems = [] - let controller = appWireframe!.presentPlaceholder(message, in: self.view) controller.didPressButton = { self.presenter?.addProject() @@ -56,34 +36,34 @@ extension ProjectsViewController: ProjectsPresenterOutput { appWireframe!.removePlaceholder() } - func projectsDidLoad(_ projects: [Project]) { + func hideProjects() { + self.splitViewItems = [] + } + + func showProjects(_ projects: [Project]) { - setupControllers() + let projectsList = ProjectsListViewController.instantiateFromStoryboard("Projects") + + let projectDetails = ProjectDetailsViewController.instantiateFromStoryboard("Projects") + let c2Presenter = ProjectDetailsPresenter() + projectDetails.presenter = c2Presenter + c2Presenter.ui = projectDetails + + let s1 = NSSplitViewItem(viewController: projectsList) + s1.minimumThickness = 120 + let s2 = NSSplitViewItem(viewController: projectDetails) + self.splitViewItems = [s1, s2] - guard let controller = self.splitViewItems.first?.viewController as? ProjectsListViewController else { - return - } - controller.projects = projects - controller.didSelectProject = { project in - - guard let controller = self.splitViewItems.last?.viewController as? ProjectDetailsViewController else { - return - } - controller.project = project + projectsList.projects = projects + projectsList.didSelectProject = { project in + projectDetails.project = project } - controller.didUpdateProject = { project in - - guard let controller = self.splitViewItems.last?.viewController as? ProjectDetailsViewController else { - return - } + projectsList.didUpdateProject = { project in /// In the list of projects only the title can change - controller.project?.title = project.title + projectDetails.project?.title = project.title } - controller.didSelectAddProject = { + projectsList.didSelectAddProject = { self.presenter?.addProject() } - controller.didSelectRemoveProject = { project in - self.presenter?.removeProject(project) - } } } From 197e05bc4750cc6e8513b8de0bbb4d84ade2b2fe Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Tue, 21 Jan 2020 15:06:38 +0200 Subject: [PATCH 25/48] Refactor --- External/CloudKit/CKRecord+Project.swift | 43 ++++++++++++++ External/CloudKit/CKRecord+Task.swift | 39 +++++++++++++ .../CloudKitRepository+Projects.swift | 56 ++----------------- .../CloudKit/CloudKitRepository+Tasks.swift | 42 ++------------ External/CloudKit/CloudKitRepository.swift | 4 ++ .../CoreData/CoreDataRepository+Tasks.swift | 2 +- External/RCSync.swift | 21 +++---- External/Repository.swift | 2 +- External/sqlite/SqliteRepository+Tasks.swift | 2 +- Jirassic.xcodeproj/project.pbxproj | 16 ++++++ 10 files changed, 125 insertions(+), 102 deletions(-) create mode 100644 External/CloudKit/CKRecord+Project.swift create mode 100644 External/CloudKit/CKRecord+Task.swift diff --git a/External/CloudKit/CKRecord+Project.swift b/External/CloudKit/CKRecord+Project.swift new file mode 100644 index 0000000..7b2a1ac --- /dev/null +++ b/External/CloudKit/CKRecord+Project.swift @@ -0,0 +1,43 @@ +// +// CKRecord+Project.swift +// Jirassic +// +// Created by Cristian Baluta on 21/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CloudKit + +extension CKRecord { + + func toProject() -> Project { + + return Project( + objectId: self["objectId"] as? String, + lastModifiedDate: self["lastModifiedDate"] as? Date, + title: self["title"] as! String, + jiraBaseUrl: self["jiraBaseUrl"] as? String, + jiraUser: self["jiraUser"] as? String, + jiraProject: self["jiraProject"] as? String, + jiraIssue: self["jiraIssue"] as? String, + gitBaseUrls: (self["gitBaseUrls"] as? String ?? "").toArray(), + gitUsers: (self["gitUsers"] as? String ?? "").toArray(), + taskNumberPrefix: self["taskNumberPrefix"] as? String + ) + } + + func update (with project: Project) { + + self["objectId"] = project.objectId as CKRecordValue? + self["lastModifiedDate"] = project.lastModifiedDate as CKRecordValue? + self["title"] = project.title as CKRecordValue + self["jiraBaseUrl"] = project.jiraBaseUrl as CKRecordValue? + self["jiraUser"] = project.jiraUser as CKRecordValue? + self["jiraProject"] = project.jiraProject as CKRecordValue? + self["jiraIssue"] = project.jiraIssue as CKRecordValue? + self["gitBaseUrls"] = project.gitBaseUrls.toString() as CKRecordValue? + self["gitUsers"] = project.gitUsers.toString() as CKRecordValue? + self["taskNumberPrefix"] = project.taskNumberPrefix as CKRecordValue? + } +} diff --git a/External/CloudKit/CKRecord+Task.swift b/External/CloudKit/CKRecord+Task.swift new file mode 100644 index 0000000..e599b6d --- /dev/null +++ b/External/CloudKit/CKRecord+Task.swift @@ -0,0 +1,39 @@ +// +// CKRecord+Task.swift +// Jirassic +// +// Created by Cristian Baluta on 21/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation +import CloudKit + +extension CKRecord { + + func toTask() -> Task { + + return Task(lastModifiedDate: self.modificationDate, + startDate: self["startDate"] as? Date, + endDate: self["endDate"] as! Date, + notes: self["notes"] as? String, + taskNumber: self["taskNumber"] as? String, + taskTitle: self["taskTitle"] as? String, + taskType: TaskType(rawValue: (self["taskType"] as! NSNumber).intValue)!, + objectId: self["objectId"] as? String, + projectId: self["projectId"] as? String + ) + } + + func update (with task: Task) { + + self["startDate"] = task.startDate as CKRecordValue? + self["endDate"] = task.endDate as CKRecordValue + self["notes"] = task.notes as CKRecordValue? + self["taskNumber"] = task.taskNumber as CKRecordValue? + self["taskTitle"] = task.taskTitle as CKRecordValue? + self["taskType"] = task.taskType.rawValue as CKRecordValue + self["objectId"] = task.objectId as CKRecordValue? + self["projectId"] = task.projectId as CKRecordValue? + } +} diff --git a/External/CloudKit/CloudKitRepository+Projects.swift b/External/CloudKit/CloudKitRepository+Projects.swift index f4471be..d9efaf7 100644 --- a/External/CloudKit/CloudKitRepository+Projects.swift +++ b/External/CloudKit/CloudKitRepository+Projects.swift @@ -52,7 +52,7 @@ extension CloudKitRepository: RepositoryProjects { let recordId = CKRecord.ID(recordName: project.objectId!, zoneID: zoneId) record = CKRecord(recordType: "Project", recordID: recordId) } - record = self.updatedRecord(record!, withProject: project) + record!.update(with: project) privateDB.save(record!, completionHandler: { savedRecord, error in @@ -61,8 +61,8 @@ extension CloudKitRepository: RepositoryProjects { RCLogErrorO(error) if let record = savedRecord { - let uploadedProject = self.projectFromRecord(record) - completion(uploadedProject) + let project = record.toProject() + completion(project) } if let ckerror = error as? CKError { switch ckerror { @@ -126,54 +126,6 @@ extension CloudKitRepository { } private func projectsFromRecords (_ records: [CKRecord]) -> [Project] { - - var projects = [Project]() - for record in records { - projects.append( projectFromRecord(record) ) - } - - return projects - } - - private func projectFromRecord (_ record: CKRecord) -> Project { - - return Project( - objectId: record["objectId"] as? String, - lastModifiedDate: record["lastModifiedDate"] as? Date, - title: record["title"] as! String, - jiraBaseUrl: record["jiraBaseUrl"] as? String, - jiraUser: record["jiraUser"] as? String, - jiraProject: record["jiraProject"] as? String, - jiraIssue: record["jiraIssue"] as? String, - gitBaseUrls: (record["gitBaseUrls"] as? String ?? "").toArray(), - gitUsers: (record["gitUsers"] as? String ?? "").toArray(), - taskNumberPrefix: record["taskNumberPrefix"] as? String - ) - } - - private func updatedRecord (_ record: CKRecord, withProject project: Project) -> CKRecord { - - record["objectId"] = project.objectId as CKRecordValue? - record["lastModifiedDate"] = project.lastModifiedDate as CKRecordValue? - record["title"] = project.title as CKRecordValue - record["jiraBaseUrl"] = project.jiraBaseUrl as CKRecordValue? - record["jiraUser"] = project.jiraUser as CKRecordValue? - record["jiraProject"] = project.jiraProject as CKRecordValue? - record["jiraIssue"] = project.jiraIssue as CKRecordValue? - record["gitBaseUrls"] = project.gitBaseUrls.toString() as CKRecordValue? - record["gitUsers"] = project.gitUsers.toString() as CKRecordValue? - record["taskNumberPrefix"] = project.taskNumberPrefix as CKRecordValue? - - return record - } - - private func stringIdsFromCKRecordIds (_ ckrecords: [CKRecord.ID]) -> [String] { - - var ids = [String]() - for ckrecord in ckrecords { - ids.append( ckrecord.recordName ) - } - - return ids + return records.map({ $0.toProject() }) } } diff --git a/External/CloudKit/CloudKitRepository+Tasks.swift b/External/CloudKit/CloudKitRepository+Tasks.swift index 94088b3..abcffba 100644 --- a/External/CloudKit/CloudKitRepository+Tasks.swift +++ b/External/CloudKit/CloudKitRepository+Tasks.swift @@ -35,7 +35,7 @@ extension CloudKitRepository: RepositoryTasks { fatalError("This method is not applicable to CloudKitRepository") } - func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) { + func queryUpdatedTasks (_ completion: @escaping ([Task], [String], NSError?) -> Void) { let changeToken = ReadMetadataInteractor().tasksLastSyncToken() @@ -89,7 +89,7 @@ extension CloudKitRepository: RepositoryTasks { let recordId = CKRecord.ID(recordName: task.objectId!, zoneID: zoneId) record = CKRecord(recordType: "Task", recordID: recordId) } - record = self.updatedRecord(record!, withTask: task) + record!.update(with: task) privateDB.save(record!, completionHandler: { savedRecord, error in @@ -97,8 +97,8 @@ extension CloudKitRepository: RepositoryTasks { RCLogErrorO(error) if let record = savedRecord { - let uploadedTask = self.taskFromRecord(record) - completion(uploadedTask) + let task = record.toTask() + completion(task) } if let ckerror = error as? CKError { switch ckerror { @@ -141,38 +141,6 @@ extension CloudKitRepository { } private func tasksFromRecords (_ records: [CKRecord]) -> [Task] { - return records.map({ taskFromRecord($0) }) - } - - private func taskFromRecord (_ record: CKRecord) -> Task { - - return Task(lastModifiedDate: record.modificationDate, - startDate: record["startDate"] as? Date, - endDate: record["endDate"] as! Date, - notes: record["notes"] as? String, - taskNumber: record["taskNumber"] as? String, - taskTitle: record["taskTitle"] as? String, - taskType: TaskType(rawValue: (record["taskType"] as! NSNumber).intValue)!, - objectId: record["objectId"] as? String, - projectId: record["projectId"] as? String - ) - } - - private func updatedRecord (_ record: CKRecord, withTask task: Task) -> CKRecord { - - record["startDate"] = task.startDate as CKRecordValue? - record["endDate"] = task.endDate as CKRecordValue - record["notes"] = task.notes as CKRecordValue? - record["taskNumber"] = task.taskNumber as CKRecordValue? - record["taskTitle"] = task.taskTitle as CKRecordValue? - record["taskType"] = task.taskType.rawValue as CKRecordValue - record["objectId"] = task.objectId as CKRecordValue? - record["projectId"] = task.projectId as CKRecordValue? - - return record - } - - private func stringIdsFromCKRecordIds (_ ckrecords: [CKRecord.ID]) -> [String] { - return ckrecords.map({ $0.recordName }) + return records.map({ $0.toTask() }) } } diff --git a/External/CloudKit/CloudKitRepository.swift b/External/CloudKit/CloudKitRepository.swift index 80b9dfd..91832b8 100644 --- a/External/CloudKit/CloudKitRepository.swift +++ b/External/CloudKit/CloudKitRepository.swift @@ -36,4 +36,8 @@ class CloudKitRepository { tasksZone = CloudKitZone(db: db, zoneName: "TasksZone") projectsZone = CloudKitZone(db: db, zoneName: "ProjectsZone") } + + internal func stringIdsFromCKRecordIds (_ ckrecords: [CKRecord.ID]) -> [String] { + return ckrecords.map({ $0.recordName }) + } } diff --git a/External/CoreData/CoreDataRepository+Tasks.swift b/External/CoreData/CoreDataRepository+Tasks.swift index 6c3c838..a03e886 100644 --- a/External/CoreData/CoreDataRepository+Tasks.swift +++ b/External/CoreData/CoreDataRepository+Tasks.swift @@ -54,7 +54,7 @@ extension CoreDataRepository: RepositoryTasks { completion(tasks) } - func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) { + func queryUpdatedTasks (_ completion: @escaping ([Task], [String], NSError?) -> Void) { let lastSyncDate = ReadMetadataInteractor().tasksLastSyncDate() let unsyncedTasks = queryUnsyncedTasks(since: lastSyncDate) diff --git a/External/RCSync.swift b/External/RCSync.swift index 9a443ec..d4c8d24 100644 --- a/External/RCSync.swift +++ b/External/RCSync.swift @@ -34,13 +34,13 @@ class RCSync { localRepository.queryDeletedTasks { deletedTasks in RCLog("1. Nr of deleted tasks: \(deletedTasks.count)") self.objectsToDelete = deletedTasks - self.syncNext { (success) in + self.syncNext { success in self.getLatestServerChanges(completion) } } } - // Send to CloudKit the changes recursivelly then call the completion block + /// Send to CloudKit the changes recursivelly then call the completion block private func syncNext (_ completion: @escaping ((_ success: Bool) -> Void)) { var task = objectsToUpload.first @@ -71,10 +71,10 @@ class RCSync { return } RCLog("1.1 Save <<< uploadedTask \(String(describing: uploadedTask.objectId))") - // After task was saved to server update it to local datastore + /// After task was saved to server update it to local datastore _ = self.localRepository.saveTask(uploadedTask, completion: { savedTask in -// // If task was saved locally successful update the last sync date -// // Otherwise last sync date will be an older date than the tasks last modified date +// /// If task was saved locally successful update the last sync date +// /// Otherwise last sync date will be an older date than the tasks last modified date // if let savedTask = savedTask { // UserDefaults.standard.lastSyncDateWithRemote = savedTask.lastModifiedDate // } @@ -87,7 +87,7 @@ class RCSync { RCLog("1.1 Delete \(task)") _ = remoteRepository.deleteTask(task, permanently: true) { uploadedTask in - // After task was marked as deleted to server, delete it permanently from local db + /// After task was marked as deleted to server, delete it permanently from local db _ = self.localRepository.deleteTask(task, permanently: true, completion: { task in completion(true) }) @@ -97,19 +97,20 @@ class RCSync { private func getLatestServerChanges (_ completion: @escaping ((_ hasIncomingChanges: Bool) -> Void)) { RCLog("2. Request latest server changes") - remoteRepository.queryUpdates { changedTasks, deletedTasksIds, error in - RCLog("2. Number of changes: \(changedTasks.count)") + remoteRepository.queryUpdatedTasks { changedTasks, deletedTasksIds, error in + RCLog("2. Number of changes on server: \(changedTasks.count)") for task in changedTasks { self.localRepository.saveTask(task, completion: { task in - RCLog("2. Saved to local db \(String(describing: task?.objectId))") + RCLog("2. Change saved to local db \(String(describing: task?.objectId))") }) } - RCLog("2. Number of deletes: \(deletedTasksIds.count)") + RCLog("2. Number of deletes on server: \(deletedTasksIds.count)") for remoteId in deletedTasksIds { self.localRepository.deleteTask(objectId: remoteId, completion: { success in RCLog("2. Deleted from local db: \(remoteId) \(success)") }) } + RCLog("3. Sync finished") completion(changedTasks.count > 0 || deletedTasksIds.count > 0) } } diff --git a/External/Repository.swift b/External/Repository.swift index f44a0a9..d5b1617 100644 --- a/External/Repository.swift +++ b/External/Repository.swift @@ -24,7 +24,7 @@ protocol RepositoryTasks { func queryTasks (startDate: Date, endDate: Date, predicate: NSPredicate?, completion: @escaping ([Task], NSError?) -> Void) func queryUnsyncedTasks (since lastSyncDate: Date?) -> [Task] func queryDeletedTasks (_ completion: @escaping ([Task]) -> Void) - func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) + func queryUpdatedTasks (_ completion: @escaping ([Task], [String], NSError?) -> Void) // Marks the Task as deleted. If permanently is true it will be removed from db func deleteTask (_ task: Task, permanently: Bool, completion: @escaping ((_ success: Bool) -> Void)) func deleteTask (objectId: String, completion: @escaping ((_ success: Bool) -> Void)) diff --git a/External/sqlite/SqliteRepository+Tasks.swift b/External/sqlite/SqliteRepository+Tasks.swift index 950ec9f..be71680 100644 --- a/External/sqlite/SqliteRepository+Tasks.swift +++ b/External/sqlite/SqliteRepository+Tasks.swift @@ -60,7 +60,7 @@ extension SqliteRepository: RepositoryTasks { completion(tasks) } - func queryUpdates (_ completion: @escaping ([Task], [String], NSError?) -> Void) { + func queryUpdatedTasks (_ completion: @escaping ([Task], [String], NSError?) -> Void) { queryDeletedTasks { deletedTasks in let lastSyncDate = ReadMetadataInteractor().tasksLastSyncDate() diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index daeb34c..25d996b 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -515,6 +515,12 @@ 5623915121CBC6CD0031B882 /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5623914F21CBC6CD0031B882 /* Project.swift */; }; 5623915321CBC9750031B882 /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5623915221CBC9750031B882 /* Projects.storyboard */; }; 5623915421CBC9750031B882 /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5623915221CBC9750031B882 /* Projects.storyboard */; }; + 563FA91B23D72A880030E50D /* CKRecord+Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563FA91A23D72A880030E50D /* CKRecord+Task.swift */; }; + 563FA91C23D72A880030E50D /* CKRecord+Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563FA91A23D72A880030E50D /* CKRecord+Task.swift */; }; + 563FA91D23D72A880030E50D /* CKRecord+Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563FA91A23D72A880030E50D /* CKRecord+Task.swift */; }; + 563FA91F23D72C5D0030E50D /* CKRecord+Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563FA91E23D72C5D0030E50D /* CKRecord+Project.swift */; }; + 563FA92023D72C5D0030E50D /* CKRecord+Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563FA91E23D72C5D0030E50D /* CKRecord+Project.swift */; }; + 563FA92123D72C5D0030E50D /* CKRecord+Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563FA91E23D72C5D0030E50D /* CKRecord+Project.swift */; }; 564E55F3202883DB00CE4C76 /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */; }; 564E55F4202883DB00CE4C76 /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */; }; 564E55F6202884DE00CE4C76 /* WorklogsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */; }; @@ -875,6 +881,8 @@ 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsPresenter.swift; sourceTree = ""; }; 5623914F21CBC6CD0031B882 /* Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.swift; sourceTree = ""; }; 5623915221CBC9750031B882 /* Projects.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Projects.storyboard; sourceTree = ""; }; + 563FA91A23D72A880030E50D /* CKRecord+Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CKRecord+Task.swift"; sourceTree = ""; }; + 563FA91E23D72C5D0030E50D /* CKRecord+Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CKRecord+Project.swift"; sourceTree = ""; }; 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsViewController.swift; sourceTree = ""; }; 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsPresenter.swift; sourceTree = ""; }; 564E55F82028857300CE4C76 /* Worklogs.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Worklogs.storyboard; sourceTree = ""; }; @@ -1700,9 +1708,11 @@ children = ( 4065D38A1DD3B44200B73201 /* CloudKitRepository.swift */, 28A928921E91104B0022AB55 /* CloudKitRepository+Tasks.swift */, + 563FA91A23D72A880030E50D /* CKRecord+Task.swift */, 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */, 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */, 2863495A23CB8003009F81D7 /* CloudKitRepository+Projects.swift */, + 563FA91E23D72C5D0030E50D /* CKRecord+Project.swift */, 2863496823CCF53C009F81D7 /* CloudKitRepository+Metadata.swift */, 2863497523D51825009F81D7 /* CloudKitZone.swift */, ); @@ -2228,8 +2238,10 @@ 280D70D91ECFE835005D2689 /* Conversions.swift in Sources */, 565E19F220A5E336003A5E2A /* RCSync.swift in Sources */, 280D70DB1ECFE847005D2689 /* ReadDaysInteractor.swift in Sources */, + 563FA92123D72C5D0030E50D /* CKRecord+Project.swift in Sources */, 28CBB598204554A2006F9D3A /* ParseGitBranch.swift in Sources */, 280D70E41ECFE88F005D2689 /* CTask.swift in Sources */, + 563FA91D23D72A880030E50D /* CKRecord+Task.swift in Sources */, 2863495123CB73E4009F81D7 /* SProject.swift in Sources */, 280D70D41ECFE7FF005D2689 /* Task.swift in Sources */, 2863494D23CB6F9E009F81D7 /* SqliteRepository+Projects.swift in Sources */, @@ -2276,6 +2288,7 @@ 280D71381ED6043D005D2689 /* ViewController.swift in Sources */, 2863496D23D21286009F81D7 /* ReadMetadataInteractor.swift in Sources */, 2871951E2022FD14001C237E /* InputsScrollView.swift in Sources */, + 563FA91F23D72C5D0030E50D /* CKRecord+Project.swift in Sources */, 28FE18A5207B369800DF796E /* CocoaHookupCell.swift in Sources */, 280D71391ED6043D005D2689 /* ViewControllerStoryboard.swift in Sources */, 2871952B20241B25001C237E /* TrackingView.swift in Sources */, @@ -2322,6 +2335,7 @@ 28CBB596204554A2006F9D3A /* ParseGitBranch.swift in Sources */, 286348F923B22775009F81D7 /* ProjectDetailsViewController.swift in Sources */, 280D71601ED6043D005D2689 /* MenuBarController.swift in Sources */, + 563FA91B23D72A880030E50D /* CKRecord+Task.swift in Sources */, 280D71611ED6043D005D2689 /* MenuBarIconView.swift in Sources */, 569C4C67202319930049FBF1 /* GitPresenter.swift in Sources */, 2871951B2022ECF7001C237E /* OutputsScrollView.swift in Sources */, @@ -2536,6 +2550,7 @@ 285465A8217858790052CB6A /* StoreView.swift in Sources */, 286348FA23B22775009F81D7 /* ProjectDetailsViewController.swift in Sources */, 4065D3D91DD3B44200B73201 /* UserNotifications.swift in Sources */, + 563FA91C23D72A880030E50D /* CKRecord+Task.swift in Sources */, 569C4C6E202319CA0049FBF1 /* BrowserPresenter.swift in Sources */, 4065D3A51DD3B44200B73201 /* StringIdGenerator.swift in Sources */, 2863493923B7EA02009F81D7 /* ProjectDetailsPresenter.swift in Sources */, @@ -2607,6 +2622,7 @@ 5623915121CBC6CD0031B882 /* Project.swift in Sources */, 28EE1F6520EE92DA00C5C1D6 /* CalendarAppPresenter.swift in Sources */, 2845B16B206AE468006EFB3B /* TaskCell.swift in Sources */, + 563FA92023D72C5D0030E50D /* CKRecord+Project.swift in Sources */, 4065D39D1DD3B44200B73201 /* Report.swift in Sources */, 40FCE4251DF7646F00D4FD45 /* AppleScript.swift in Sources */, 28A928781E8F78580022AB55 /* UpdateSchemaInteractor.swift in Sources */, From 033ffd0f73ffd9fd6c7b047fe7f5fd8396acccc2 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Tue, 21 Jan 2020 16:10:46 +0200 Subject: [PATCH 26/48] Fix project save --- .../ProjectDetailsViewController.swift | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index d6d1eb2..119355c 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -76,15 +76,21 @@ class ProjectDetailsViewController: NSViewController { @IBAction func handleSaveButton (_ sender: NSButton) { - project!.jiraBaseUrl = baseUrlTextField.stringValue - project!.jiraUser = userTextField.stringValue - project!.jiraProject = projectNamePopup.selectedItem?.title - project!.jiraIssue = projectIssueNamePopup.selectedItem?.title - project!.gitBaseUrls = pathsTextField.stringValue.toArray() - project!.gitUsers = emailsTextField.stringValue.toArray() - project!.taskNumberPrefix = taskNumberPrefixTextField.stringValue + /// Make a copy of the project. Directly editing the project will call reloadData for each change + guard var project = self.project else { + return + } + project.jiraBaseUrl = baseUrlTextField.stringValue + project.jiraUser = userTextField.stringValue + project.jiraProject = projectNamePopup.selectedItem?.title + project.jiraIssue = projectIssueNamePopup.selectedItem?.title + project.gitBaseUrls = pathsTextField.stringValue.toArray() + project.gitUsers = emailsTextField.stringValue.toArray() + project.taskNumberPrefix = taskNumberPrefixTextField.stringValue + + self.project = project - presenter!.saveProject(project!) + presenter!.saveProject(project) } @IBAction func handleDeleteButton (_ sender: NSButton) { From dcd294e4755316a244d975f959fccc3aaaf0c50f Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Tue, 21 Jan 2020 16:53:49 +0200 Subject: [PATCH 27/48] Fix git users seave --- .../Projects/ProjectDetailsPresenter.swift | 46 +++++++++++-------- .../ProjectDetailsViewController.swift | 45 +++++++++--------- External/sqlite/SProject.swift | 2 +- 3 files changed, 50 insertions(+), 43 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index dce8e02..2c1efc5 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -10,7 +10,8 @@ import Foundation import RCPreferences protocol ProjectDetailsPresenterInput: class { - + + var project: Project? {get set} func didPickUrl (_ url: URL) func save (emails: String, paths: String) func saveProject (_ project: Project) @@ -18,8 +19,9 @@ protocol ProjectDetailsPresenterInput: class { } protocol ProjectDetailsPresenterOutput: class { - + func pickPath() + func show (_ project: Project) func setPaths (_ paths: String?, enabled: Bool?) func setEmails (_ emails: String?, enabled: Bool?) } @@ -28,32 +30,40 @@ class ProjectDetailsPresenter { weak var ui: ProjectDetailsPresenterOutput? private let localPreferences = RCPreferences() - + + var project: Project? { + didSet { + reloadData() + } + } } extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { func reloadData() { - - ui?.setPaths("", - enabled: localPreferences.bool(.enableGit)) - - ui?.setEmails("", - enabled: localPreferences.bool(.enableGit)) - + + guard let project = project else { + return + } + ui!.show(project) + ui!.setPaths(project.gitBaseUrls.toString(), enabled: localPreferences.bool(.enableGit)) + ui!.setEmails(project.gitUsers.toString(), enabled: localPreferences.bool(.enableGit)) } func didPickUrl (_ url: URL) { - + + guard let project = project else { + return + } var path = url.absoluteString path = path.replacingOccurrences(of: "file://", with: "") path.removeLast() // TODO: Validate if the picked project is a git project -// let existingPaths = wself.localPreferences.string(.settingsGitPaths) -// let updatedPaths = existingPaths == "" ? path : (existingPaths + "," + path) -// savePaths(updatedPaths) -// ui.setPaths(updatedPaths, enabled: localPreferences.bool(.enableGit)) + var existingPaths = project.gitBaseUrls + existingPaths.append(path) + + ui!.setPaths(existingPaths.toString(), enabled: localPreferences.bool(.enableGit)) } func save (emails: String, paths: String) { @@ -62,9 +72,9 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { } func saveProject (_ project: Project) { - if project.objectId == nil { - - } +// if project.objectId == nil { +// +// } let interactor = ProjectInteractor(repository: localRepository, remoteRepository: remoteRepository) interactor.saveProject(project, allowSyncing: true) { savedProject in diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 119355c..a348521 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -26,7 +26,7 @@ class ProjectDetailsViewController: NSViewController { var project: Project? { didSet { - reloadData() + presenter?.project = project } } @@ -51,25 +51,7 @@ class ProjectDetailsViewController: NSViewController { emailsTextField.removeGestureRecognizer(gesture) } } - - func reloadData() { - - guard let project = project else { - return - } - butCredentials.state = project.jiraBaseUrl == nil ? .on : .off - baseUrlTextField.stringValue = project.jiraBaseUrl ?? "" - userTextField.stringValue = project.jiraUser ?? "" - errorTextField.stringValue = "" - projectNamePopup.removeAllItems() - projectNamePopup.addItem(withTitle: project.jiraProject ?? "") - projectIssueNamePopup.removeAllItems() - projectIssueNamePopup.addItem(withTitle: project.jiraIssue ?? "") - pathsTextField.stringValue = project.gitBaseUrls.toString() - emailsTextField.stringValue = project.gitUsers.toString() - taskNumberPrefixTextField.stringValue = project.taskNumberPrefix ?? "" - } - + @IBAction func handlePickerButton (_ sender: NSButton) { pickPath() } @@ -108,7 +90,7 @@ class ProjectDetailsViewController: NSViewController { let popover = NSPopover() let view = GitUsersViewController.instantiateFromStoryboard("Components") view.onDone = { - self.emailsTextField.stringValue = view.selectedUsers.joined(separator: ",") + self.project?.gitUsers = view.selectedUsers self.gitUsersPopover?.performClose(nil) self.gitUsersPopover = nil } @@ -117,13 +99,28 @@ class ProjectDetailsViewController: NSViewController { size: emailsTextField.frame.size) popover.show(relativeTo: rect, of: emailsTextField, preferredEdge: NSRectEdge.minY) gitUsersPopover = popover - // Set emails after popover is presented - view.selectedUsers = ["cristi.baluta@gmail.com"] + /// Set emails after popover is presented + view.selectedUsers = presenter?.project?.gitUsers ?? [] } } extension ProjectDetailsViewController: ProjectDetailsPresenterOutput { - + + func show (_ project: Project) { + + butCredentials.state = project.jiraBaseUrl == nil ? .on : .off + baseUrlTextField.stringValue = project.jiraBaseUrl ?? "" + userTextField.stringValue = project.jiraUser ?? "" + errorTextField.stringValue = "" + projectNamePopup.removeAllItems() + projectNamePopup.addItem(withTitle: project.jiraProject ?? "") + projectIssueNamePopup.removeAllItems() + projectIssueNamePopup.addItem(withTitle: project.jiraIssue ?? "") + pathsTextField.stringValue = project.gitBaseUrls.toString() + emailsTextField.stringValue = project.gitUsers.toString() + taskNumberPrefixTextField.stringValue = project.taskNumberPrefix ?? "" + } + func pickPath() { let panel = NSOpenPanel() diff --git a/External/sqlite/SProject.swift b/External/sqlite/SProject.swift index f1fa277..865397d 100644 --- a/External/sqlite/SProject.swift +++ b/External/sqlite/SProject.swift @@ -42,7 +42,7 @@ extension SProject { jiraIssue: self.jiraIssue, gitBaseUrls: (self.gitBaseUrls ?? "").toArray(), - gitUsers: (self.gitBaseUrls ?? "").toArray(), + gitUsers: (self.gitUsers ?? "").toArray(), taskNumberPrefix: self.taskNumberPrefix ) } From 894f658fd2d86feddbabc2eb852ffe793b54108c Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Wed, 22 Jan 2020 01:42:40 +0200 Subject: [PATCH 28/48] Projects --- App/Entities/Project.swift | 2 +- .../Projects/ProjectDetailsPresenter.swift | 50 ++++++++++++++----- .../ProjectDetailsViewController.swift | 19 ++++++- .../Screens/Projects/Projects.storyboard | 9 ++-- .../Projects/ProjectsListViewController.swift | 10 ++++ .../Screens/Projects/ProjectsPresenter.swift | 10 ++++ .../Projects/ProjectsViewController.swift | 9 ++++ 7 files changed, 89 insertions(+), 20 deletions(-) diff --git a/App/Entities/Project.swift b/App/Entities/Project.swift index f79b75e..2cd5efc 100644 --- a/App/Entities/Project.swift +++ b/App/Entities/Project.swift @@ -8,7 +8,7 @@ import Foundation -struct Project { +struct Project: Equatable { var objectId: String? var lastModifiedDate: Date? diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index 2c1efc5..5e10e03 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -13,7 +13,8 @@ protocol ProjectDetailsPresenterInput: class { var project: Project? {get set} func didPickUrl (_ url: URL) - func save (emails: String, paths: String) + func save (emails: String) + func save (paths: String) func saveProject (_ project: Project) func deleteProject (_ project: Project) } @@ -24,6 +25,8 @@ protocol ProjectDetailsPresenterOutput: class { func show (_ project: Project) func setPaths (_ paths: String?, enabled: Bool?) func setEmails (_ emails: String?, enabled: Bool?) + func enableSaveButton (_ enable: Bool) + func handleProjectDidSave (_ project: Project) } class ProjectDetailsPresenter { @@ -33,9 +36,16 @@ class ProjectDetailsPresenter { var project: Project? { didSet { + editedProject = project reloadData() } } + /// Do all changes on the copy + var editedProject: Project? + + private func enableDisableSave() { + ui!.enableSaveButton(project != editedProject) + } } extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { @@ -48,11 +58,12 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { ui!.show(project) ui!.setPaths(project.gitBaseUrls.toString(), enabled: localPreferences.bool(.enableGit)) ui!.setEmails(project.gitUsers.toString(), enabled: localPreferences.bool(.enableGit)) + enableDisableSave() } func didPickUrl (_ url: URL) { - guard let project = project else { + guard var project = editedProject else { return } var path = url.absoluteString @@ -62,13 +73,21 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { var existingPaths = project.gitBaseUrls existingPaths.append(path) + project.gitBaseUrls = existingPaths + editedProject = project ui!.setPaths(existingPaths.toString(), enabled: localPreferences.bool(.enableGit)) + enableDisableSave() } - func save (emails: String, paths: String) { - saveEmails(emails) - savePaths(paths) + func save (emails: String) { + editedProject?.gitUsers = emails.toArray() + enableDisableSave() + } + + func save (paths: String) { + editedProject?.gitBaseUrls = paths.toArray() + enableDisableSave() } func saveProject (_ project: Project) { @@ -77,7 +96,12 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { // } let interactor = ProjectInteractor(repository: localRepository, remoteRepository: remoteRepository) interactor.saveProject(project, allowSyncing: true) { savedProject in - + guard let project = savedProject else { + return + } + self.project = project + self.enableDisableSave() + self.ui!.handleProjectDidSave(project) } } @@ -85,11 +109,11 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { } - private func saveEmails (_ emails: String) { - - } - - private func savePaths (_ paths: String) { - - } +// private func saveEmails (_ emails: String) { +// +// } +// +// private func savePaths (_ paths: String) { +// +// } } diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index a348521..bbf423c 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -22,6 +22,7 @@ class ProjectDetailsViewController: NSViewController { @IBOutlet private var pathsTextField: NSTextField! @IBOutlet private var taskNumberPrefixTextField: NSTextField! @IBOutlet private var butPick: NSButton! + @IBOutlet private var butSave: NSButton! @IBOutlet private var butDelete: NSButton! var project: Project? { @@ -29,6 +30,7 @@ class ProjectDetailsViewController: NSViewController { presenter?.project = project } } + var projectDidSave: ((Project) -> Void)? var presenter: ProjectDetailsPresenterInput? @@ -38,6 +40,10 @@ class ProjectDetailsViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() + baseUrlTextField.delegate = self + userTextField.delegate = self + passwordTextField.delegate = self + taskNumberPrefixTextField.delegate = self emailsTextField.delegate = self pathsTextField.delegate = self @@ -90,7 +96,7 @@ class ProjectDetailsViewController: NSViewController { let popover = NSPopover() let view = GitUsersViewController.instantiateFromStoryboard("Components") view.onDone = { - self.project?.gitUsers = view.selectedUsers + self.presenter?.save(emails: view.selectedUsers.toString()) self.gitUsersPopover?.performClose(nil) self.gitUsersPopover = nil } @@ -156,11 +162,20 @@ extension ProjectDetailsViewController: ProjectDetailsPresenterOutput { emailsTextField.isEnabled = enabled } } + + func enableSaveButton (_ enable: Bool) { + butSave.isEnabled = enable + } + + func handleProjectDidSave (_ project: Project) { + projectDidSave?(project) + } } extension ProjectDetailsViewController: NSTextFieldDelegate { func controlTextDidEndEditing(_ obj: Notification) { -// save() + presenter!.save(emails: emailsTextField.stringValue) + presenter!.save(paths: pathsTextField.stringValue) } } diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard index 15003ab..8a0ba41 100644 --- a/Delivery/macOS/Screens/Projects/Projects.storyboard +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -28,7 +28,7 @@ - + @@ -227,7 +227,7 @@ - + @@ -245,7 +245,7 @@ - + @@ -294,7 +294,7 @@ - + @@ -412,6 +412,7 @@ + diff --git a/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift index fadfdd3..cfba82e 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsListViewController.swift @@ -35,6 +35,16 @@ class ProjectsListViewController: NSViewController { @IBAction func handleAddButton (_ sender: NSButton) { didSelectAddProject?() } + + func selectProject (_ project: Project) { + for i in 0...projects.count { + if projects[i].objectId == project.objectId { + let indexSet = IndexSet(integer: i) + tableView.selectRowIndexes(indexSet, byExtendingSelection: false) + break + } + } + } } extension ProjectsListViewController: NSTableViewDataSource { diff --git a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift index 1c6b347..1a3d05f 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsPresenter.swift @@ -12,6 +12,7 @@ protocol ProjectsPresenterInput: class { func reloadProjects() func addProject() func removeProject (_ project: Project) + func updateProject (_ project: Project) } protocol ProjectsPresenterOutput: class { @@ -57,6 +58,15 @@ extension ProjectsPresenter: ProjectsPresenterInput { projects = projects.filter( { $0.title != project.title } ) projectsDidLoad(projects) } + + func updateProject (_ project: Project) { + for i in 0...projects.count { + if projects[i].objectId == project.objectId { + projects[i] = project + break + } + } + } } extension ProjectsPresenter: ProjectsInteractorOutput { diff --git a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift index 7e1bc3c..1a3b3bd 100644 --- a/Delivery/macOS/Screens/Projects/ProjectsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectsViewController.swift @@ -65,5 +65,14 @@ extension ProjectsViewController: ProjectsPresenterOutput { projectsList.didSelectAddProject = { self.presenter?.addProject() } + + projectDetails.projectDidSave = { project in + self.presenter!.updateProject(project) + } + + if let firstProject = projects.first { + projectsList.selectProject(firstProject) + projectDetails.project = firstProject + } } } From cfd04e91f81721ffceb2f6e3c9876618b1ebfc96 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Wed, 22 Jan 2020 09:27:17 +0200 Subject: [PATCH 29/48] ... --- .../Projects/ProjectDetailsPresenter.swift | 19 +++++++------------ .../ProjectDetailsViewController.swift | 18 ++++++++++++++++++ .../Screens/Projects/Projects.storyboard | 6 ++++++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index 5e10e03..72a31ac 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -12,6 +12,7 @@ import RCPreferences protocol ProjectDetailsPresenterInput: class { var project: Project? {get set} + var editedProject: Project? {get set} func didPickUrl (_ url: URL) func save (emails: String) func save (paths: String) @@ -41,7 +42,11 @@ class ProjectDetailsPresenter { } } /// Do all changes on the copy - var editedProject: Project? + var editedProject: Project? { + didSet { + enableDisableSave() + } + } private func enableDisableSave() { ui!.enableSaveButton(project != editedProject) @@ -77,17 +82,14 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { editedProject = project ui!.setPaths(existingPaths.toString(), enabled: localPreferences.bool(.enableGit)) - enableDisableSave() } func save (emails: String) { editedProject?.gitUsers = emails.toArray() - enableDisableSave() } func save (paths: String) { editedProject?.gitBaseUrls = paths.toArray() - enableDisableSave() } func saveProject (_ project: Project) { @@ -102,18 +104,11 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { self.project = project self.enableDisableSave() self.ui!.handleProjectDidSave(project) + self.enableDisableSave() } } func deleteProject (_ project: Project) { } - -// private func saveEmails (_ emails: String) { -// -// } -// -// private func savePaths (_ paths: String) { -// -// } } diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index bbf423c..8f3a464 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -33,6 +33,7 @@ class ProjectDetailsViewController: NSViewController { var projectDidSave: ((Project) -> Void)? var presenter: ProjectDetailsPresenterInput? + var jiraPresenter: JiraTempoPresenterInput = JiraTempoPresenter() private var emailClickGestureRecognizer: NSClickGestureRecognizer? private var gitUsersPopover: NSPopover? @@ -89,6 +90,17 @@ class ProjectDetailsViewController: NSViewController { } + @IBAction func projectNamePopupSelected (_ sender: NSPopUpButton) { + if let title = sender.selectedItem?.title { + presenter!.editedProject?.jiraProject = title + jiraPresenter.loadProjectIssues(for: title) + } + } + + @IBAction func projectIssueNamePopupSelected (_ sender: NSPopUpButton) { + presenter!.editedProject?.jiraIssue = sender.selectedItem?.title + } + @objc func emailTextFieldClicked() { guard gitUsersPopover == nil else { return @@ -175,6 +187,12 @@ extension ProjectDetailsViewController: ProjectDetailsPresenterOutput { extension ProjectDetailsViewController: NSTextFieldDelegate { func controlTextDidEndEditing(_ obj: Notification) { + + presenter!.editedProject?.jiraBaseUrl = baseUrlTextField.stringValue + presenter!.editedProject?.jiraUser = userTextField.stringValue + presenter!.editedProject?.gitBaseUrls = pathsTextField.stringValue.toArray() +// project.gitUsers = emailsTextField.stringValue.toArray() +// project.taskNumberPrefix = taskNumberPrefixTextField.stringValue presenter!.save(emails: emailsTextField.stringValue) presenter!.save(paths: pathsTextField.stringValue) } diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard index 8a0ba41..67c5617 100644 --- a/Delivery/macOS/Screens/Projects/Projects.storyboard +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -236,6 +236,9 @@ + + + @@ -254,6 +257,9 @@ + + + From 7b1177623901036fb32ca71e56b0111b172fc9d9 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Thu, 23 Jan 2020 09:35:03 +0200 Subject: [PATCH 30/48] Add projects list to task creation --- App/Entities/Task.swift | 3 +- .../Reports/cells/ReportCell/ReportCell.swift | 3 +- .../ReportCell/ReportCellPresenter.swift | 3 +- .../TaskSuggestionPresenter.swift | 17 ++++- .../TaskSuggestionViewController.swift | 10 +++ .../Screens/Tasks/NewTaskViewController.swift | 39 +++++++--- Delivery/macOS/Screens/Tasks/Tasks.storyboard | 71 ++++++++++++++++--- .../cells/TaskCell/TaskCellPresenter.swift | 3 +- 8 files changed, 122 insertions(+), 27 deletions(-) diff --git a/App/Entities/Task.swift b/App/Entities/Task.swift index d6fc6ac..4c6efa9 100644 --- a/App/Entities/Task.swift +++ b/App/Entities/Task.swift @@ -107,5 +107,6 @@ typealias TaskCreationData = ( dateEnd: Date, taskNumber: String?, notes: String?, - taskType: TaskType + taskType: TaskType, + projectId: String? ) diff --git a/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift index 35da83f..975cc05 100644 --- a/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift +++ b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCell.swift @@ -31,7 +31,8 @@ class ReportCell: NSTableRowView, CellProtocol { dateEnd: Date(), taskNumber: self.taskNrTextField!.stringValue, notes: self.notesTextField!.stringValue, - taskType: .issue + taskType: .issue, + projectId: nil ) } set { diff --git a/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCellPresenter.swift b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCellPresenter.swift index d389dff..9b89773 100644 --- a/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCellPresenter.swift +++ b/Delivery/macOS/Screens/Reports/cells/ReportCell/ReportCellPresenter.swift @@ -34,7 +34,8 @@ class ReportCellPresenter: NSObject { dateEnd: Date(), taskNumber: taskNumber + " " + title, notes: notesJoined, - taskType: .issue + taskType: .issue, + projectId: nil ) cell.duration = pref.bool(.usePercents) ? "\(theReport.duration.secToPercent)" diff --git a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift index 0fcb264..acc002b 100644 --- a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift +++ b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift @@ -11,13 +11,14 @@ import Foundation protocol TaskSuggestionPresenterInput: class { func setup (startSleepDate: Date?, endSleepDate: Date?) func selectSegment (atIndex index: Int) - func save (selectedSegment: Int, notes: String, startSleepDate: Date?, endSleepDate: Date?) + func save (selectedSegment: Int, selectedProjectIndex: Int, notes: String, startSleepDate: Date?, endSleepDate: Date?) } protocol TaskSuggestionPresenterOutput: class { func selectSegment (atIndex index: Int) func setTime (_ notes: String) func setNotes (_ notes: String) + func setProjects (_ projects: [String]) func hideTaskTypes() } @@ -27,6 +28,7 @@ class TaskSuggestionPresenter { private var isStartOfDay = false private let startWorkText = "Good morning, ready to start working?" private let moduleCalendar = ModuleCalendar() + var projects: [Project] = [] private func taskType (forIndex index: Int) -> TaskType { @@ -51,6 +53,13 @@ class TaskSuggestionPresenter { default: return -1 } } + + private func project(at index: Int) -> Project? { + guard projects.count > 0 else { + return nil + } + return projects[index] + } } extension TaskSuggestionPresenter: TaskSuggestionPresenterInput { @@ -90,6 +99,9 @@ extension TaskSuggestionPresenter: TaskSuggestionPresenterInput { } } } + + projects = ReadProjectsInteractor(repository: localRepository, remoteRepository: nil).allProjects() + userInterface!.setProjects(projects.map({$0.title})) } else { isStartOfDay = true userInterface!.setNotes(startWorkText) @@ -104,7 +116,7 @@ extension TaskSuggestionPresenter: TaskSuggestionPresenterInput { userInterface!.selectSegment(atIndex: index) } - func save (selectedSegment: Int, notes: String, startSleepDate: Date?, endSleepDate: Date?) { + func save (selectedSegment: Int, selectedProjectIndex: Int, notes: String, startSleepDate: Date?, endSleepDate: Date?) { var task: Task @@ -115,6 +127,7 @@ extension TaskSuggestionPresenter: TaskSuggestionPresenterInput { task = Task(endDate: endSleepDate!, type: type) task.notes = notes task.startDate = startSleepDate + task.projectId = project(at: selectedProjectIndex)?.objectId } let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) diff --git a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift index d593926..61fe731 100644 --- a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift +++ b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift @@ -11,6 +11,7 @@ import Cocoa class TaskSuggestionViewController: NSViewController { @IBOutlet private weak var segmentedControl: NSSegmentedControl? + @IBOutlet private weak var projectSelector: NSPopUpButton! @IBOutlet private weak var titleTextField: NSTextField! @IBOutlet private weak var notesTextField: NSTextField! @@ -33,6 +34,7 @@ class TaskSuggestionViewController: NSViewController { @IBAction func handleSaveButton (_ sender: NSButton) { presenter!.save(selectedSegment: segmentedControl != nil ? segmentedControl!.selectedSegment : -1, + selectedProjectIndex: projectSelector.indexOfSelectedItem, notes: notesTextField.stringValue, startSleepDate: startSleepDate, endSleepDate: endSleepDate) @@ -54,9 +56,17 @@ extension TaskSuggestionViewController: TaskSuggestionPresenterOutput { notesTextField.stringValue = notes } + func setProjects (_ projects: [String]) { + projectSelector.removeAllItems() + projectSelector.addItems(withTitles: projects) + projectSelector.selectItem(at: 0) + } + func hideTaskTypes() { segmentedControl!.removeFromSuperview() segmentedControl = nil + projectSelector!.removeFromSuperview() + projectSelector = nil // notesTextField.removeAutoresizing() // _ = notesTextField.constraintToBottom(self.view, distance: CGFloat(0)) } diff --git a/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift b/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift index de36f46..c945f2a 100644 --- a/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift +++ b/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift @@ -11,15 +11,16 @@ import RCPreferences class NewTaskViewController: NSViewController { + @IBOutlet private weak var projectSelector: NSPopUpButton! @IBOutlet private weak var taskTypeSelector: NSPopUpButton! - @IBOutlet private weak var issueIdTextField: NSTextField! - @IBOutlet private weak var notesTextField: NSTextField! - @IBOutlet private weak var endDateTextField: NSTextField! + @IBOutlet private weak var issueIdTextField: NSTextField! + @IBOutlet private weak var notesTextField: NSTextField! + @IBOutlet private weak var endDateTextField: NSTextField! @IBOutlet private weak var startDateTextField: NSTextField! @IBOutlet private weak var startDateButton: NSButton! - var onSave: ((_ taskData: TaskCreationData) -> Void)? - var onCancel: (() -> Void)? + var onSave: ((_ taskData: TaskCreationData) -> Void)? + var onCancel: (() -> Void)? private var activeEditingTextFieldContent = "" private var issueTypes = [String]() private let predictor = PredictiveTimeTyping() @@ -80,14 +81,23 @@ class NewTaskViewController: NSViewController { self.issueIdTextField.stringValue = newValue } } + var projects: [Project] = [] override func viewDidLoad() { super.viewDidLoad() + + projects = ReadProjectsInteractor(repository: localRepository, remoteRepository: nil).allProjects() + projectSelector.removeAllItems() + projectSelector.addItems(withTitles: projects.map({$0.title})) + projectSelector.selectItem(at: 0) + taskTypeSelector.removeAllItems() taskTypeSelector.addItems(withTitles: ["Task", "Scrum", "Meeting", "Food", "Social & Media", "Learning", "Code review"]) taskTypeSelector.selectItem(at: 0) + setupStartDateButtonTitle() startDateTextField.toolTip = "Predictive Time Typing (use digits and backspace):\n • First digit if between 2 and 9 means AM hours.\n • Leaving minutes empty, defaults to 00.\n • Last digit replaces itself, no need to delete." + endDateTextField.toolTip = startDateTextField.toolTip } @@ -105,6 +115,13 @@ class NewTaskViewController: NSViewController { } } + private func selectedProject() -> Project? { + guard projects.count > 0 else { + return nil + } + return projects[projectSelector.indexOfSelectedItem] + } + private func estimateTaskType() { let typeEstimator = TaskTypeEstimator() @@ -128,12 +145,11 @@ extension NewTaskViewController: NSTextFieldDelegate { func controlTextDidBeginEditing (_ obj: Notification) { - if let textField = obj.object as? NSTextField { - guard textField == endDateTextField || textField == startDateTextField else { - return - } - activeEditingTextFieldContent = textField.stringValue + guard let textField = obj.object as? NSTextField, + textField == endDateTextField || textField == startDateTextField else { + return } + activeEditingTextFieldContent = textField.stringValue } func controlTextDidChange (_ obj: Notification) { @@ -163,7 +179,8 @@ extension NewTaskViewController { dateEnd: self.dateEnd, taskNumber: self.taskNumber != "" ? self.taskNumber : nil, notes: self.notes != "" ? self.notes : nil, - taskType: selectedTaskType() + taskType: selectedTaskType(), + projectId: selectedProject()?.objectId ) self.onSave?(taskData) } diff --git a/Delivery/macOS/Screens/Tasks/Tasks.storyboard b/Delivery/macOS/Screens/Tasks/Tasks.storyboard index 7bcbd65..987f260 100644 --- a/Delivery/macOS/Screens/Tasks/Tasks.storyboard +++ b/Delivery/macOS/Screens/Tasks/Tasks.storyboard @@ -11,11 +11,11 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -305,20 +352,23 @@ - + + + + @@ -327,6 +377,7 @@ + @@ -334,7 +385,7 @@ - + diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift index 00016a2..13ec21c 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift @@ -58,7 +58,8 @@ class TaskCellPresenter: NSObject { dateEnd: theTask.endDate, taskNumber: theTask.taskNumber, notes: notes, - taskType: theTask.taskType + taskType: theTask.taskType, + projectId: theTask.projectId ) cell.isDark = AppDelegate.sharedApp().theme.isDark cell.isEditable = theTask.isSaved From 8f8848f5f5073502584ab7eafdd5e5bed60a40ea Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Thu, 23 Jan 2020 09:56:47 +0200 Subject: [PATCH 31/48] Handle credentials checkbox --- .../Screens/Projects/ProjectDetailsPresenter.swift | 14 ++++++++++++++ .../Projects/ProjectDetailsViewController.swift | 12 +++++++++++- Jirassic.xcodeproj/project.pbxproj | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index 72a31ac..921028d 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -13,6 +13,7 @@ protocol ProjectDetailsPresenterInput: class { var project: Project? {get set} var editedProject: Project? {get set} + func enableDefaultCredentials(_ enabled: Bool) func didPickUrl (_ url: URL) func save (emails: String) func save (paths: String) @@ -24,6 +25,7 @@ protocol ProjectDetailsPresenterOutput: class { func pickPath() func show (_ project: Project) + func setCredentials(url: String, user: String, password: String, editable: Bool) func setPaths (_ paths: String?, enabled: Bool?) func setEmails (_ emails: String?, enabled: Bool?) func enableSaveButton (_ enable: Bool) @@ -66,6 +68,18 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { enableDisableSave() } + func enableDefaultCredentials(_ enabled: Bool) { + + if enabled { + ui!.setCredentials(url: localPreferences.string(.settingsJiraUrl), + user: localPreferences.string(.settingsJiraUser), + password: "", + editable: false) + } else { + ui!.setCredentials(url: "", user: "", password: "", editable: true) + } + } + func didPickUrl (_ url: URL) { guard var project = editedProject else { diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 8f3a464..435283b 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -87,7 +87,7 @@ class ProjectDetailsViewController: NSViewController { } @IBAction func handleCredentialsButton (_ sender: NSButton) { - + presenter!.enableDefaultCredentials(sender.state == .on) } @IBAction func projectNamePopupSelected (_ sender: NSPopUpButton) { @@ -156,6 +156,16 @@ extension ProjectDetailsViewController: ProjectDetailsPresenterOutput { } } + func setCredentials(url: String, user: String, password: String, editable: Bool) { + + baseUrlTextField.stringValue = url + userTextField.stringValue = user + passwordTextField.stringValue = password + baseUrlTextField.isEditable = editable + userTextField.isEditable = editable + passwordTextField.isEditable = editable + } + func setPaths (_ paths: String?, enabled: Bool?) { if let paths = paths { pathsTextField.stringValue = paths diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index 25d996b..f9336b4 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -1774,13 +1774,13 @@ 5623914821CBC66C0031B882 /* Projects */ = { isa = PBXGroup; children = ( + 5623915221CBC9750031B882 /* Projects.storyboard */, 5623914921CBC6A10031B882 /* ProjectsViewController.swift */, 5623914C21CBC6B40031B882 /* ProjectsPresenter.swift */, 286348F423B136B3009F81D7 /* ProjectsInteractor.swift */, 2863493423B790B1009F81D7 /* ProjectsListViewController.swift */, 286348F823B22775009F81D7 /* ProjectDetailsViewController.swift */, 2863493723B7EA02009F81D7 /* ProjectDetailsPresenter.swift */, - 5623915221CBC9750031B882 /* Projects.storyboard */, ); path = Projects; sourceTree = ""; From cf1326db55abc648e1546651120f56ddf73da70b Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Thu, 23 Jan 2020 17:57:56 +0200 Subject: [PATCH 32/48] Add edit task button --- App/Entities/Task.swift | 61 ++++++++++++------- .../Screens/Tasks/NewTaskViewController.swift | 53 ++++++++++------ .../macOS/Screens/Tasks/TasksDataSource.swift | 8 +++ .../macOS/Screens/Tasks/TasksPresenter.swift | 18 +++++- .../Screens/Tasks/TasksViewController.swift | 39 ++++++++++++ .../Tasks/cells/TaskCell/TaskCell.swift | 7 ++- .../Screens/Tasks/cells/TaskCell/TaskCell.xib | 23 +++++-- 7 files changed, 161 insertions(+), 48 deletions(-) diff --git a/App/Entities/Task.swift b/App/Entities/Task.swift index 4c6efa9..9671af1 100644 --- a/App/Entities/Task.swift +++ b/App/Entities/Task.swift @@ -8,9 +8,9 @@ import Foundation -// Never change the indexes because they are already stored in the database enum TaskType: Int { - + + /// Never change the indexes because they are already stored in the database case issue = 0 case startDay = 1 case scrum = 2 @@ -22,35 +22,54 @@ enum TaskType: Int { case coderev = 8 case endDay = 9 case calendar = 10 + case support = 11 var defaultNotes: String { switch self { - case .startDay: return "Working day started" - case .endDay: return "Working day ended" - case .scrum: return "Scrum meeting" - case .lunch: return "Lunch break 🥑🥑🥑" - case .meeting: return "Meeting" - case .waste: return "Social & Media 👤👤👤" - case .learning: return "Learning session" - case .coderev: return "Reviewing and fixing code" - case .calendar: return "Calendar event" - default: return "" + case .startDay: return "Working day started" + case .endDay: return "Working day ended" + case .scrum: return "Scrum meeting" + case .lunch: return "Lunch break 🥑🥑🥑" + case .meeting: return "Meeting" + case .waste: return "Social & Media 👤👤👤" + case .learning: return "Learning session" + case .coderev: return "Reviewing and fixing code" + case .calendar: return "Calendar event" + case .support: return "Support" + default: return "" } } - // Used to group reports + /// Used to group reports together var defaultTaskNumber: String? { switch self { - case .scrum: return "scrum" - case .lunch: return "lunch" - case .meeting: return "meeting" - case .waste: return "waste" - case .learning: return "learning" - case .coderev: return "coderev" - case .calendar: return "meeting" - default: return nil + case .scrum: return "scrum" + case .lunch: return "lunch" + case .meeting: return "meeting" + case .waste: return "waste" + case .learning: return "learning" + case .coderev: return "coderev" + case .calendar: return "meeting" + case .support: return "support" + default: return nil } } + + var title: String { + switch self { + case .issue: return "Task" + case .scrum: return "Scrum" + case .lunch: return "Food" + case .meeting: return "Meeting" + case .waste: return "Social & Media" + case .learning: return "Learning" + case .coderev: return "Code review" + case .support: return "Support" + default: return "" + } + } + + static var allValues: [TaskType] = [] } // Object representing a task diff --git a/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift b/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift index c945f2a..33bb05e 100644 --- a/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift +++ b/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift @@ -81,18 +81,32 @@ class NewTaskViewController: NSViewController { self.issueIdTextField.stringValue = newValue } } + var taskType: TaskType = .issue { + didSet { + for i in 0...taskTypes.count { + if taskTypes[i] == taskType { + taskTypeSelector.selectItem(at: i) + break + } + } + } + } + private let taskTypes: [TaskType] = [.issue, .scrum, .lunch, .meeting, .waste, .learning, .coderev, .support] +// var project: Project { +// +// } var projects: [Project] = [] - + override func viewDidLoad() { super.viewDidLoad() - + projects = ReadProjectsInteractor(repository: localRepository, remoteRepository: nil).allProjects() projectSelector.removeAllItems() projectSelector.addItems(withTitles: projects.map({$0.title})) projectSelector.selectItem(at: 0) - + taskTypeSelector.removeAllItems() - taskTypeSelector.addItems(withTitles: ["Task", "Scrum", "Meeting", "Food", "Social & Media", "Learning", "Code review"]) + taskTypeSelector.addItems(withTitles: taskTypes.map({$0.title})) taskTypeSelector.selectItem(at: 0) setupStartDateButtonTitle() @@ -111,6 +125,7 @@ class NewTaskViewController: NSViewController { case 4: return .waste case 5: return .learning case 6: return .coderev + case 7: return .support default: return .issue } } @@ -127,13 +142,16 @@ class NewTaskViewController: NSViewController { let typeEstimator = TaskTypeEstimator() let settings = SettingsInteractor().getAppSettings() let estimatedType: TaskType = typeEstimator.taskTypeAroundDate(initialDate, withSettings: settings) - if estimatedType == .scrum { - taskTypeSelector.selectItem(at: 1) - handleTaskTypeSelector(taskTypeSelector) - - let settingsScrumTime = gregorian.dateComponents(ymdhmsUnitFlags, from: settings.settingsTracking.scrumTime) - self.dateStart = self.initialDate.dateByUpdating(hour: settingsScrumTime.hour!, minute: settingsScrumTime.minute!) + guard estimatedType == .scrum else { + return } + taskTypeSelector.selectItem(at: 1) + handleTaskTypeSelector(taskTypeSelector) + + let settingsScrumTime = gregorian.dateComponents(ymdhmsUnitFlags, + from: settings.settingsTracking.scrumTime) + self.dateStart = self.initialDate.dateByUpdating(hour: settingsScrumTime.hour!, + minute: settingsScrumTime.minute!) } private func setupStartDateButtonTitle() { @@ -154,15 +172,14 @@ extension NewTaskViewController: NSTextFieldDelegate { func controlTextDidChange (_ obj: Notification) { - if let textField = obj.object as? NSTextField { - guard textField == endDateTextField || textField == startDateTextField else { - return - } - let comps = textField.stringValue.map { String($0) } - let newDigit = activeEditingTextFieldContent.count > comps.count ? "" : comps.last - activeEditingTextFieldContent = predictor.timeByAdding(newDigit!, to: activeEditingTextFieldContent) - textField.stringValue = activeEditingTextFieldContent + guard let textField = obj.object as? NSTextField, + textField == endDateTextField || textField == startDateTextField else { + return } + let comps = textField.stringValue.map { String($0) } + let newDigit = activeEditingTextFieldContent.count > comps.count ? "" : comps.last + activeEditingTextFieldContent = predictor.timeByAdding(newDigit!, to: activeEditingTextFieldContent) + textField.stringValue = activeEditingTextFieldContent } } diff --git a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift index 54d4d89..279e0b2 100644 --- a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift +++ b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift @@ -17,6 +17,7 @@ class TasksDataSource: NSObject, TasksAndReportsDataSource { var didClickAddRow: ((_ row: Int) -> Void)? var didClickRemoveRow: ((_ row: Int) -> Void)? + var didClickEditRow: ((_ row: Int) -> Void)? var didClickCloseDay: ((_ tasks: [Task]) -> Void)? var didClickSaveWorklogs: (() -> Void)? var didClickSetupJira: (() -> Void)? @@ -96,6 +97,13 @@ extension TasksDataSource: NSTableViewDelegate { TaskCellPresenter(cell: cell).present(previousTask: thePreviousData, currentTask: theData) cell.didClickEditCell = { [weak self] (cell: CellProtocol) in + // Ugly hack to find the row number from which the action came + tableView.enumerateAvailableRowViews( { (rowView, rowIndex) -> Void in + if rowView.subviews.first! == cell as! NSTableRowView { + self?.didClickEditRow!(rowIndex) + return + } + }) // let updatedData = cell.data // theData.taskNumber = updatedData.taskNumber // theData.notes = updatedData.notes diff --git a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift index 21a48f9..1c55a56 100644 --- a/Delivery/macOS/Screens/Tasks/TasksPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/TasksPresenter.swift @@ -15,8 +15,10 @@ protocol TasksPresenterInput: class { func updateNoTasksState() func closeDay (showWorklogs: Bool) func insertTaskWithData (_ taskData: TaskCreationData) + func updateTask (_ task: Task, with taskData: TaskCreationData) func insertTask (after row: Int) func removeTask (at row: Int) + func editTask (at row: Int) func didClickStartDay() func didClickSaveWorklogs() } @@ -27,6 +29,7 @@ protocol TasksPresenterOutput: class { func showMessage (_ message: MessageViewModel) func showTasks (_ tasks: [Task]) func presentNewTaskController (date: Date) + func presentTaskEditor(task: Task) func showWorklogs (date: Date, tasks: [Task]) func removeTasks() func removeWorklogs() @@ -120,14 +123,18 @@ extension TasksPresenter: TasksPresenterInput { } func insertTaskWithData (_ taskData: TaskCreationData) { - - var task = Task() + updateTask(Task(), with: taskData) + } + + func updateTask (_ task: Task, with taskData: TaskCreationData) { + + var task = task task.notes = taskData.notes task.taskNumber = taskData.taskNumber task.startDate = taskData.dateStart task.endDate = taskData.dateEnd task.taskType = taskData.taskType - + let saveInteractor = TaskInteractor(repository: localRepository, remoteRepository: remoteRepository) saveInteractor.saveTask(task, allowSyncing: false, completion: { _ in }) } @@ -161,6 +168,11 @@ extension TasksPresenter: TasksPresenterInput { } } + + func editTask (at row: Int) { + let task = currentTasks[row] + ui!.presentTaskEditor(task: task) + } } extension TasksPresenter: TasksInteractorOutput { diff --git a/Delivery/macOS/Screens/Tasks/TasksViewController.swift b/Delivery/macOS/Screens/Tasks/TasksViewController.swift index 7d2faa4..28d8886 100644 --- a/Delivery/macOS/Screens/Tasks/TasksViewController.swift +++ b/Delivery/macOS/Screens/Tasks/TasksViewController.swift @@ -65,6 +65,13 @@ extension TasksViewController: TasksPresenterOutput { self?.tasksScrollView!.reloadFooter() } } + dataSource.didClickEditRow = { [weak self] row in + RCLogO("Edit item after row \(row)") + if row >= 0 { + self?.activeCellRect = self?.tasksScrollView?.frameOfCell(atRow: row) + self?.presenter!.editTask(at: row) + } + } dataSource.didClickCloseDay = { [weak self] tasks in self?.presenter!.closeDay(showWorklogs: true) } @@ -120,6 +127,38 @@ extension TasksViewController: TasksPresenterOutput { controller.dateEnd = date activePopover = popover } + + func presentTaskEditor (task: Task) { + + let popover = NSPopover() + let controller = NewTaskViewController.instantiateFromStoryboard("Tasks") + controller.onSave = { [weak self] (taskData: TaskCreationData) -> Void in + guard let self = self else { + return + } + self.presenter?.updateTask(task, with: taskData) + self.presenter!.reloadLastSelectedDay() + popover.performClose(nil) + } + controller.onCancel = { [weak self] in + if let strongSelf = self { + popover.performClose(nil) + strongSelf.activePopover = nil + strongSelf.presenter!.updateNoTasksState() + } + } + popover.contentViewController = controller + popover.show(relativeTo: activeCellRect!, + of: self.view, + preferredEdge: NSRectEdge.maxY) + activePopover = popover + + controller.notes = task.notes ?? "" + controller.taskNumber = task.taskNumber ?? "" + controller.dateStart = task.startDate + controller.dateEnd = task.endDate + controller.taskType = task.taskType + } func showWorklogs (date: Date, tasks: [Task]) { diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift index d0dc99b..ceaf699 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift @@ -20,7 +20,7 @@ class TaskCell: NSTableRowView, CellProtocol { @IBOutlet private var notesTextFieldTrailingContraint: NSLayoutConstraint! @IBOutlet private var butRemove: NSButton! @IBOutlet private var butAdd: NSButton! - @IBOutlet private var butAEdit: NSButton! + @IBOutlet private var butEdit: NSButton! private var isEditing = false private var wasEdited = false @@ -108,6 +108,7 @@ class TaskCell: NSTableRowView, CellProtocol { butRemove.isHidden = true butAdd.isHidden = true + butEdit.isHidden = true butRemove.wantsLayer = true dateStartTextField.onClick = { self.createTimeboxPopover(timebox: self.dateStartTextField) @@ -176,7 +177,8 @@ extension TaskCell { self.butRemove.isHidden = false self.butAdd.isHidden = false - self.notesTextFieldTrailingContraint.constant = 80 + self.butEdit.isHidden = false + self.notesTextFieldTrailingContraint.constant = 120 self.setNeedsDisplay(self.frame) } @@ -185,6 +187,7 @@ extension TaskCell { self.butRemove.isHidden = true self.butAdd.isHidden = true + self.butEdit.isHidden = true self.notesTextFieldTrailingContraint.constant = 10 self.setNeedsDisplay(self.frame) } diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib index 3a0cbe7..5a29f8b 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib @@ -31,9 +31,6 @@ - - - @@ -82,7 +79,21 @@ - + + + + @@ -98,9 +109,11 @@ + + @@ -115,6 +128,7 @@ + @@ -127,6 +141,7 @@ + From f1b44b3e4abcf0e05ef916abb642a9749af45b9a Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Mon, 27 Jan 2020 10:02:09 +0200 Subject: [PATCH 33/48] Fix new task rect y position --- Delivery/macOS/Screens/Tasks/TasksViewController.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Delivery/macOS/Screens/Tasks/TasksViewController.swift b/Delivery/macOS/Screens/Tasks/TasksViewController.swift index 28d8886..245953e 100644 --- a/Delivery/macOS/Screens/Tasks/TasksViewController.swift +++ b/Delivery/macOS/Screens/Tasks/TasksViewController.swift @@ -119,7 +119,9 @@ extension TasksViewController: TasksPresenterOutput { } } popover.contentViewController = controller - popover.show(relativeTo: activeCellRect!, + var rect = activeCellRect! + rect.origin.y = self.view.frame.height - rect.origin.y - rect.height + popover.show(relativeTo: rect, of: self.view, preferredEdge: NSRectEdge.maxY) // Add data after popover is presented @@ -148,7 +150,9 @@ extension TasksViewController: TasksPresenterOutput { } } popover.contentViewController = controller - popover.show(relativeTo: activeCellRect!, + var rect = activeCellRect! + rect.origin.y = self.view.frame.height - rect.origin.y - rect.height/2 + popover.show(relativeTo: rect, of: self.view, preferredEdge: NSRectEdge.maxY) activePopover = popover From 3542d3e675f86df2c22c14ec75b8a2a362826e42 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Mon, 27 Jan 2020 15:28:36 +0200 Subject: [PATCH 34/48] ... --- .../macOS/Screens/Tasks/TasksDataSource.swift | 5 ++- .../Tasks/cells/TaskCell/TaskCell.swift | 3 +- .../Screens/Tasks/cells/TaskCell/TaskCell.xib | 8 ++--- .../cells/TaskCell/TaskCellPresenter.swift | 34 +++++++++---------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift index 279e0b2..e210296 100644 --- a/Delivery/macOS/Screens/Tasks/TasksDataSource.swift +++ b/Delivery/macOS/Screens/Tasks/TasksDataSource.swift @@ -92,9 +92,12 @@ extension TasksDataSource: NSTableViewDelegate { } var theData = tasks[row] + let lastData = tasks.last let thePreviousData: Task? = row == 0 ? nil : tasks[row-1] var cell: CellProtocol = TaskCell.instantiate(in: tableView) - TaskCellPresenter(cell: cell).present(previousTask: thePreviousData, currentTask: theData) + TaskCellPresenter(cell: cell).present(previousTask: thePreviousData, + currentTask: theData, + lastTask: lastData) cell.didClickEditCell = { [weak self] (cell: CellProtocol) in // Ugly hack to find the row number from which the action came diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift index ceaf699..06c208b 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.swift @@ -11,7 +11,6 @@ import Cocoa class TaskCell: NSTableRowView, CellProtocol { @IBOutlet var statusImage: NSImageView? - @IBOutlet private var statusImageWidthContraint: NSLayoutConstraint! @IBOutlet private var dateStartTextField: TimeBox! @IBOutlet private var dateStartTextFieldLeadingContraint: NSLayoutConstraint! @IBOutlet private var bullet: NSTextField! @@ -178,7 +177,7 @@ extension TaskCell { self.butRemove.isHidden = false self.butAdd.isHidden = false self.butEdit.isHidden = false - self.notesTextFieldTrailingContraint.constant = 120 + self.notesTextFieldTrailingContraint.constant = 80 self.setNeedsDisplay(self.frame) } diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib index 5a29f8b..a93cd17 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCell.xib @@ -14,11 +14,11 @@ - - + + - + @@ -117,7 +117,7 @@ - + diff --git a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift index 13ec21c..2916eb5 100644 --- a/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift +++ b/Delivery/macOS/Screens/Tasks/cells/TaskCell/TaskCellPresenter.swift @@ -17,11 +17,11 @@ class TaskCellPresenter: NSObject { self.cell = cell } - func present (previousTask: Task?, currentTask theTask: Task) { + func present (previousTask: Task?, currentTask: Task, lastTask: Task?) { cell.statusImage?.image = nil - switch theTask.taskType { + switch currentTask.taskType { case .issue: cell.statusImage?.image = NSImage(named: NSImage.statusAvailableName) @@ -42,29 +42,29 @@ class TaskCellPresenter: NSObject { cell.color = NSColor.systemGray } - var notes = theTask.notes ?? "" + var notes = currentTask.notes ?? "" if notes == "" { - notes = theTask.taskType.defaultNotes + notes = currentTask.taskType.defaultNotes } // The codereview notes is a list of tasks that were reviewed - if theTask.taskType == .coderev && theTask.notes != nil && theTask.notes != "" { - notes = "\(theTask.taskType.defaultNotes): \(notes)" + if currentTask.taskType == .coderev && currentTask.notes != nil && currentTask.notes != "" { + notes = "\(currentTask.taskType.defaultNotes): \(notes)" } - if theTask.taskType == .lunch { - notes = theTask.taskType.defaultNotes + if currentTask.taskType == .lunch { + notes = currentTask.taskType.defaultNotes } cell.data = ( - dateStart: theTask.startDate, - dateEnd: theTask.endDate, - taskNumber: theTask.taskNumber, + dateStart: currentTask.startDate, + dateEnd: currentTask.endDate, + taskNumber: currentTask.taskNumber, notes: notes, - taskType: theTask.taskType, - projectId: theTask.projectId + taskType: currentTask.taskType, + projectId: currentTask.projectId ) cell.isDark = AppDelegate.sharedApp().theme.isDark - cell.isEditable = theTask.isSaved - cell.isRemovable = theTask.isSaved - cell.isIgnored = theTask.taskType == .lunch || theTask.taskType == .waste - cell.timeToolTip = theTask.isSaved ? "Click to edit" : "Item can be edited after the day is closed" + cell.isEditable = currentTask.isSaved && false + cell.isRemovable = currentTask.isSaved + cell.isIgnored = currentTask.taskType == .lunch || currentTask.taskType == .waste + cell.timeToolTip = currentTask.isSaved ? "Click to edit" : "Item can be edited after the day is closed" } } From fe2cf57ccb169aae6a9b52f0c5009ae55dcff780 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Tue, 28 Jan 2020 15:24:34 +0200 Subject: [PATCH 35/48] Fix mouse over text width --- .../Screens/Tasks/NewTaskViewController.swift | 2 +- .../Tasks/cells/TaskCell/TaskCell.swift | 27 ++++++++++++------- .../Screens/Tasks/cells/TaskCell/TaskCell.xib | 6 +++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift b/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift index 33bb05e..03fb93f 100644 --- a/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift +++ b/Delivery/macOS/Screens/Tasks/NewTaskViewController.swift @@ -83,7 +83,7 @@ class NewTaskViewController: NSViewController { } var taskType: TaskType = .issue { didSet { - for i in 0...taskTypes.count { + for i in 0.. + + + @@ -117,7 +120,6 @@ - @@ -134,7 +136,7 @@ - + From e06b5c17340454a7f320207382d220b77210a8ee Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Tue, 28 Jan 2020 17:59:51 +0200 Subject: [PATCH 36/48] Read git paths and users from projects --- App/Projects/ReadProjectsInteractor.swift | 8 ++++++++ Delivery/macOS/App/LocalPreferences.swift | 4 ---- Delivery/macOS/App/Migrator.swift | 7 +++++++ Delivery/macOS/Modules/GitLogs/ModuleGitLogs.swift | 11 +++++------ .../Screens/Projects/ProjectDetailsPresenter.swift | 7 ++++++- .../Projects/ProjectDetailsViewController.swift | 8 ++++++-- 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/App/Projects/ReadProjectsInteractor.swift b/App/Projects/ReadProjectsInteractor.swift index d49b186..a437820 100644 --- a/App/Projects/ReadProjectsInteractor.swift +++ b/App/Projects/ReadProjectsInteractor.swift @@ -15,4 +15,12 @@ class ReadProjectsInteractor: RepositoryInteractor { func allProjects() -> [Project] { return self.repository.projects() } + + func allGitPaths() -> [String] { + return allProjects().flatMap({$0.gitBaseUrls}) + } + + func allGitUsers() -> [String] { + return allProjects().flatMap({$0.gitUsers}) + } } diff --git a/Delivery/macOS/App/LocalPreferences.swift b/Delivery/macOS/App/LocalPreferences.swift index 41ea938..eca0e91 100644 --- a/Delivery/macOS/App/LocalPreferences.swift +++ b/Delivery/macOS/App/LocalPreferences.swift @@ -25,8 +25,6 @@ enum LocalPreferences: String, RCPreferencesProtocol { case settingsJiraProjectIssueKey = "settingsJiraProjectIssueKey" case settingsHookupCmdName = "settingsHookupCmdName" case settingsHookupAppName = "settingsHookupAppName" - case settingsGitPaths = "settingsGitPaths" - case settingsGitAuthors = "settingsGitAuthors" case settingsSelectedCalendars = "settingsSelectedCalendars" case enableGit = "enableGit" case enableJit = "enableJit" @@ -52,8 +50,6 @@ enum LocalPreferences: String, RCPreferencesProtocol { case .settingsJiraProjectIssueKey:return "" case .settingsHookupCmdName: return "" case .settingsHookupAppName: return "" - case .settingsGitPaths: return "" - case .settingsGitAuthors: return "" case .settingsSelectedCalendars:return "Work,Calendar" case .enableGit: return false case .enableJit: return true diff --git a/Delivery/macOS/App/Migrator.swift b/Delivery/macOS/App/Migrator.swift index 0693dd9..074b407 100644 --- a/Delivery/macOS/App/Migrator.swift +++ b/Delivery/macOS/App/Migrator.swift @@ -24,5 +24,12 @@ class Migrator { WriteMetadataInteractor().set(tasksLastSyncToken: token) UserDefaults.standard.removeObject(forKey: "ChangeToken") } + + if let _ = UserDefaults.standard.object(forKey: "RCPreferences-settingsGitPaths") as? String { + UserDefaults.standard.removeObject(forKey: "RCPreferences-settingsGitPaths") + } + if let _ = UserDefaults.standard.object(forKey: "RCPreferences-settingsGitAuthors") as? String { + UserDefaults.standard.removeObject(forKey: "RCPreferences-settingsGitAuthors") + } } } diff --git a/Delivery/macOS/Modules/GitLogs/ModuleGitLogs.swift b/Delivery/macOS/Modules/GitLogs/ModuleGitLogs.swift index 5128054..8ab8e51 100644 --- a/Delivery/macOS/Modules/GitLogs/ModuleGitLogs.swift +++ b/Delivery/macOS/Modules/GitLogs/ModuleGitLogs.swift @@ -7,13 +7,12 @@ // import Foundation -import RCPreferences import RCLog class ModuleGitLogs { private let extensions = ExtensionsInteractor() - private let pref = RCPreferences() + private let projectsInteractor = ReadProjectsInteractor(repository: localRepository, remoteRepository: nil) // func isReachable (completion: @escaping (Bool) -> Void) { // checkIfGitInstalled(completion: completion) @@ -39,9 +38,9 @@ class ModuleGitLogs { } /// Returns a list of commiters emails - func fetchUsers(completion: @escaping (([GitUser]) -> Void)) { + func fetchUsers (completion: @escaping (([GitUser]) -> Void)) { - let paths = pref.string(.settingsGitPaths).split(separator: ",").map { String($0) } + let paths = projectsInteractor.allGitPaths() users(paths: paths, previousUsers: []) { gitUsers in completion(gitUsers) } @@ -75,7 +74,7 @@ class ModuleGitLogs { } let interval = dates.removeFirst() - let paths = pref.string(.settingsGitPaths).split(separator: ",").map { String($0) } + let paths = projectsInteractor.allGitPaths() logs(dateStart: interval.dateStart, dateEnd: interval.dateEnd, paths: paths, previousCommits: []) { commits in for commit in commits { // Sometimes git returns commits that are not in the provided interval, filter them out @@ -119,7 +118,7 @@ class ModuleGitLogs { } paths.removeFirst() - let allowedAuthors: [String] = pref.string(.settingsGitAuthors).split(separator: ",").map { String($0) } + let allowedAuthors = projectsInteractor.allGitUsers() getGitLogs(at: path, dateStart: dateStart, dateEnd: dateEnd, completion: { rawResults in diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index 921028d..d58c990 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -25,7 +25,8 @@ protocol ProjectDetailsPresenterOutput: class { func pickPath() func show (_ project: Project) - func setCredentials(url: String, user: String, password: String, editable: Bool) + func setCredentialsCheckbox (enabled: Bool) + func setCredentials (url: String, user: String, password: String, editable: Bool) func setPaths (_ paths: String?, enabled: Bool?) func setEmails (_ emails: String?, enabled: Bool?) func enableSaveButton (_ enable: Bool) @@ -66,6 +67,10 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { ui!.setPaths(project.gitBaseUrls.toString(), enabled: localPreferences.bool(.enableGit)) ui!.setEmails(project.gitUsers.toString(), enabled: localPreferences.bool(.enableGit)) enableDisableSave() + + let credentialsEnabled = (project.jiraBaseUrl ?? "") == "" && (project.jiraUser ?? "") == "" + enableDefaultCredentials(credentialsEnabled) + ui!.setCredentialsCheckbox(enabled: credentialsEnabled) } func enableDefaultCredentials(_ enabled: Bool) { diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 435283b..411f3f5 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -155,8 +155,12 @@ extension ProjectDetailsViewController: ProjectDetailsPresenterOutput { self?.presenter?.didPickUrl(url) } } - - func setCredentials(url: String, user: String, password: String, editable: Bool) { + + func setCredentialsCheckbox (enabled: Bool) { + butCredentials.state = enabled ? .on : .off + } + + func setCredentials (url: String, user: String, password: String, editable: Bool) { baseUrlTextField.stringValue = url userTextField.stringValue = user From e734287afc74c355c1e1d6d0c82daf5367951688 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Wed, 29 Jan 2020 13:00:06 +0200 Subject: [PATCH 37/48] Fix crash in the morning --- .../TaskSuggestionPresenter.swift | 48 +++++++++++-------- .../TaskSuggestionViewController.swift | 7 ++- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift index acc002b..91be37d 100644 --- a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift +++ b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift @@ -7,6 +7,7 @@ // import Foundation +import RCLog protocol TaskSuggestionPresenterInput: class { func setup (startSleepDate: Date?, endSleepDate: Date?) @@ -29,7 +30,11 @@ class TaskSuggestionPresenter { private let startWorkText = "Good morning, ready to start working?" private let moduleCalendar = ModuleCalendar() var projects: [Project] = [] - + + deinit { + RCLog("deinit") + } + private func taskType (forIndex index: Int) -> TaskType { switch index { @@ -76,32 +81,33 @@ extension TaskSuggestionPresenter: TaskSuggestionPresenterInput { if let startDate = startSleepDate { let settings: Settings = SettingsInteractor().getAppSettings() let interactor = ComputerWakeUpInteractor(repository: localRepository, remoteRepository: remoteRepository, settings: settings) - if let type = interactor.estimationForDate(startDate, currentDate: endSleepDate ?? Date()) { - if type == .startDay { - isStartOfDay = true - userInterface!.setNotes(startWorkText) - userInterface!.hideTaskTypes() - } else { - let index = selectedSegment(forTaskType: type) - selectSegment(atIndex: index) - // If selected segment is meeting, try to see if the meeting is a calendar event and populate with that data - if type == .meeting { - moduleCalendar.events(dateStart: startDate, dateEnd: startDate.endOfDay()) { (tasks) in - for task in tasks { - if task.startDate!.isAlmostSameHourAs(startDate) && - task.endDate.isAlmostSameHourAs(endSleepDate!, devianceSeconds: 30.0.minToSec) { - - self.userInterface!.setNotes(task.notes ?? "") - break - } + guard let type = interactor.estimationForDate(startDate, currentDate: endSleepDate ?? Date()) else { + return + } + if type == .startDay { + isStartOfDay = true + userInterface!.setNotes(startWorkText) + userInterface!.hideTaskTypes() + } else { + let index = selectedSegment(forTaskType: type) + selectSegment(atIndex: index) + // If selected segment is meeting, try to see if the meeting is a calendar event and populate with that data + if type == .meeting { + moduleCalendar.events(dateStart: startDate, dateEnd: startDate.endOfDay()) { (tasks) in + for task in tasks { + if task.startDate!.isAlmostSameHourAs(startDate) && + task.endDate.isAlmostSameHourAs(endSleepDate!, devianceSeconds: 30.0.minToSec) { + + self.userInterface!.setNotes(task.notes ?? "") + break } } } } + projects = ReadProjectsInteractor(repository: localRepository, remoteRepository: nil).allProjects() + userInterface!.setProjects(projects.map({$0.title})) } - projects = ReadProjectsInteractor(repository: localRepository, remoteRepository: nil).allProjects() - userInterface!.setProjects(projects.map({$0.title})) } else { isStartOfDay = true userInterface!.setNotes(startWorkText) diff --git a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift index 61fe731..711c1eb 100644 --- a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift +++ b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift @@ -7,6 +7,7 @@ // import Cocoa +import RCLog class TaskSuggestionViewController: NSViewController { @@ -18,7 +19,11 @@ class TaskSuggestionViewController: NSViewController { var presenter: TaskSuggestionPresenterInput? var startSleepDate: Date? var endSleepDate: Date? - + + deinit { + RCLog("deinit") + } + override func viewDidLoad() { super.viewDidLoad() presenter!.setup(startSleepDate: startSleepDate, endSleepDate: endSleepDate) From 40da1bf0c46deac3740a3a1984668dc722149c44 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Thu, 30 Jan 2020 13:20:23 +0200 Subject: [PATCH 38/48] Fix crash --- .../Screens/Projects/ProjectDetailsPresenter.swift | 12 ++++++------ .../Projects/ProjectDetailsViewController.swift | 4 +++- .../TaskSuggestion/TaskSuggestionPresenter.swift | 4 ++-- .../TaskSuggestionViewController.swift | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift index d58c990..df3984f 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsPresenter.swift @@ -36,7 +36,7 @@ protocol ProjectDetailsPresenterOutput: class { class ProjectDetailsPresenter { weak var ui: ProjectDetailsPresenterOutput? - private let localPreferences = RCPreferences() + private let prefs = RCPreferences() var project: Project? { didSet { @@ -64,8 +64,8 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { return } ui!.show(project) - ui!.setPaths(project.gitBaseUrls.toString(), enabled: localPreferences.bool(.enableGit)) - ui!.setEmails(project.gitUsers.toString(), enabled: localPreferences.bool(.enableGit)) + ui!.setPaths(project.gitBaseUrls.toString(), enabled: prefs.bool(.enableGit)) + ui!.setEmails(project.gitUsers.toString(), enabled: prefs.bool(.enableGit)) enableDisableSave() let credentialsEnabled = (project.jiraBaseUrl ?? "") == "" && (project.jiraUser ?? "") == "" @@ -76,8 +76,8 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { func enableDefaultCredentials(_ enabled: Bool) { if enabled { - ui!.setCredentials(url: localPreferences.string(.settingsJiraUrl), - user: localPreferences.string(.settingsJiraUser), + ui!.setCredentials(url: prefs.string(.settingsJiraUrl), + user: prefs.string(.settingsJiraUser), password: "", editable: false) } else { @@ -100,7 +100,7 @@ extension ProjectDetailsPresenter: ProjectDetailsPresenterInput { project.gitBaseUrls = existingPaths editedProject = project - ui!.setPaths(existingPaths.toString(), enabled: localPreferences.bool(.enableGit)) + ui!.setPaths(existingPaths.toString(), enabled: prefs.bool(.enableGit)) } func save (emails: String) { diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index 411f3f5..a72ec60 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -108,9 +108,11 @@ class ProjectDetailsViewController: NSViewController { let popover = NSPopover() let view = GitUsersViewController.instantiateFromStoryboard("Components") view.onDone = { - self.presenter?.save(emails: view.selectedUsers.toString()) + let emails = view.selectedUsers.toString() + self.presenter?.save(emails: emails) self.gitUsersPopover?.performClose(nil) self.gitUsersPopover = nil + self.emailsTextField.stringValue = emails } popover.contentViewController = view let rect = CGRect(origin: CGPoint(x: emailsTextField.frame.width/2, y: 0), diff --git a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift index 91be37d..b7f80a0 100644 --- a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift +++ b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionPresenter.swift @@ -59,8 +59,8 @@ class TaskSuggestionPresenter { } } - private func project(at index: Int) -> Project? { - guard projects.count > 0 else { + private func project (at index: Int) -> Project? { + guard projects.count > 0, index >= 0, index < projects.count else { return nil } return projects[index] diff --git a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift index 711c1eb..94b28aa 100644 --- a/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift +++ b/Delivery/macOS/Screens/TaskSuggestion/TaskSuggestionViewController.swift @@ -39,7 +39,7 @@ class TaskSuggestionViewController: NSViewController { @IBAction func handleSaveButton (_ sender: NSButton) { presenter!.save(selectedSegment: segmentedControl != nil ? segmentedControl!.selectedSegment : -1, - selectedProjectIndex: projectSelector.indexOfSelectedItem, + selectedProjectIndex: projectSelector != nil ? projectSelector.indexOfSelectedItem : -1, notes: notesTextField.stringValue, startSleepDate: startSleepDate, endSleepDate: endSleepDate) From 22035f120d2005382ceeb3cd2207d37d8690bcf4 Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Thu, 30 Jan 2020 18:09:40 +0200 Subject: [PATCH 39/48] Pass jira credentials from outside --- App/Entities/JiraUser.swift | 17 +++++++ .../Screens/Onboarding/WizardJiraView.swift | 6 +-- .../ProjectDetailsViewController.swift | 47 ++++++++++++++++- .../Screens/Projects/Projects.storyboard | 23 ++++++--- .../Output/JiraTempo/JiraTempoCell.swift | 50 ++++++++++++------- .../Output/JiraTempo/JiraTempoPresenter.swift | 39 ++++++--------- Jirassic.xcodeproj/project.pbxproj | 6 +++ 7 files changed, 136 insertions(+), 52 deletions(-) create mode 100644 App/Entities/JiraUser.swift diff --git a/App/Entities/JiraUser.swift b/App/Entities/JiraUser.swift new file mode 100644 index 0000000..9ce0bf6 --- /dev/null +++ b/App/Entities/JiraUser.swift @@ -0,0 +1,17 @@ +// +// JiraUser.swift +// Jirassic +// +// Created by Cristian Baluta on 30/01/2020. +// Copyright © 2020 Imagin soft. All rights reserved. +// + +import Foundation + +struct JiraUser { + var url: String + var user: String + var password: String + var project: String + var issue: String +} diff --git a/Delivery/macOS/Screens/Onboarding/WizardJiraView.swift b/Delivery/macOS/Screens/Onboarding/WizardJiraView.swift index c5d14b1..c8c0c29 100644 --- a/Delivery/macOS/Screens/Onboarding/WizardJiraView.swift +++ b/Delivery/macOS/Screens/Onboarding/WizardJiraView.swift @@ -45,9 +45,7 @@ class WizardJiraView: NSView { } func save() { - presenter.save(url: baseUrlTextField.stringValue, - user: userTextField.stringValue, - password: passwordTextField.stringValue) + } @IBAction func handleLoginButton (_ sender: NSButton) { @@ -57,7 +55,7 @@ class WizardJiraView: NSView { passwordTextField.stringValue != "" else { return } - presenter.checkCredentials() +// presenter.checkCredentials() } @IBAction func handleSkipButton (_ sender: NSButton) { diff --git a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift index a72ec60..775ca98 100644 --- a/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift +++ b/Delivery/macOS/Screens/Projects/ProjectDetailsViewController.swift @@ -24,6 +24,7 @@ class ProjectDetailsViewController: NSViewController { @IBOutlet private var butPick: NSButton! @IBOutlet private var butSave: NSButton! @IBOutlet private var butDelete: NSButton! + @IBOutlet private var butPurchase: NSButton! var project: Project? { didSet { @@ -33,7 +34,7 @@ class ProjectDetailsViewController: NSViewController { var projectDidSave: ((Project) -> Void)? var presenter: ProjectDetailsPresenterInput? - var jiraPresenter: JiraTempoPresenterInput = JiraTempoPresenter() + private let jiraPresenter: JiraTempoPresenterInput = JiraTempoPresenter() private var emailClickGestureRecognizer: NSClickGestureRecognizer? private var gitUsersPopover: NSPopover? @@ -51,6 +52,9 @@ class ProjectDetailsViewController: NSViewController { let gesture = NSClickGestureRecognizer(target: self, action: #selector(ProjectDetailsViewController.emailTextFieldClicked)) emailsTextField.addGestureRecognizer(gesture) self.emailClickGestureRecognizer = gesture + + (jiraPresenter as! JiraTempoPresenter).userInterface = self + jiraPresenter.setupUserInterface() } deinit { @@ -139,6 +143,13 @@ extension ProjectDetailsViewController: ProjectDetailsPresenterOutput { pathsTextField.stringValue = project.gitBaseUrls.toString() emailsTextField.stringValue = project.gitUsers.toString() taskNumberPrefixTextField.stringValue = project.taskNumberPrefix ?? "" + + let user = JiraUser(url: project.jiraBaseUrl ?? "", + user: project.jiraUser ?? "", + password: Keychain.getPassword(), + project: project.jiraProject ?? "", + issue: project.jiraIssue ?? "") + jiraPresenter.checkCredentials(user) } func pickPath() { @@ -213,3 +224,37 @@ extension ProjectDetailsViewController: NSTextFieldDelegate { presenter!.save(paths: pathsTextField.stringValue) } } + +extension ProjectDetailsViewController: JiraTempoPresenterOutput { + + func setPurchased (_ purchased: Bool) { + butPurchase.isHidden = purchased + baseUrlTextField.isEnabled = purchased + userTextField.isEnabled = purchased + passwordTextField.isEnabled = purchased + projectNamePopup.isEnabled = purchased + projectIssueNamePopup.isEnabled = purchased + } + + func enableProgressIndicator (_ enabled: Bool) { + enabled + ? progressIndicator.startAnimation(nil) + : progressIndicator.stopAnimation(nil) + } + + func showProjects (_ projects: [String], selectedProject: String) { + projectNamePopup.removeAllItems() + projectNamePopup.addItems(withTitles: projects) + projectNamePopup.selectItem(withTitle: selectedProject) + } + + func showProjectIssues (_ issues: [String], selectedIssue: String) { + projectIssueNamePopup.removeAllItems() + projectIssueNamePopup.addItems(withTitles: issues) + projectIssueNamePopup.selectItem(withTitle: selectedIssue) + } + + func showErrorMessage (_ message: String) { + errorTextField.stringValue = message + } +} diff --git a/Delivery/macOS/Screens/Projects/Projects.storyboard b/Delivery/macOS/Screens/Projects/Projects.storyboard index 67c5617..d2ddeb0 100644 --- a/Delivery/macOS/Screens/Projects/Projects.storyboard +++ b/Delivery/macOS/Screens/Projects/Projects.storyboard @@ -28,7 +28,7 @@ - + @@ -227,7 +227,7 @@ - + @@ -248,7 +248,7 @@ - + @@ -276,9 +276,8 @@ - + @@ -372,6 +378,7 @@ + @@ -382,6 +389,7 @@ + @@ -389,11 +397,13 @@ + + @@ -418,6 +428,7 @@ + diff --git a/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoCell.swift b/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoCell.swift index d5b04cb..03689d3 100644 --- a/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoCell.swift +++ b/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoCell.swift @@ -22,7 +22,7 @@ class JiraTempoCell: NSTableRowView { @IBOutlet private var projectIssueNamePopup: NSPopUpButton! @IBOutlet private var progressIndicator: NSProgressIndicator! - private let localPreferences = RCPreferences() + private let pref = RCPreferences() var presenter: JiraTempoPresenterInput = JiraTempoPresenter() var onPurchasePressed: (() -> Void)? @@ -33,8 +33,8 @@ class JiraTempoCell: NSTableRowView { userTextField.delegate = self passwordTextField.delegate = self - baseUrlTextField.stringValue = localPreferences.string(.settingsJiraUrl) - userTextField.stringValue = localPreferences.string(.settingsJiraUser) + baseUrlTextField.stringValue = pref.string(.settingsJiraUrl) + userTextField.stringValue = pref.string(.settingsJiraUser) passwordTextField.stringValue = Keychain.getPassword() projectNamePopup.target = self @@ -45,13 +45,29 @@ class JiraTempoCell: NSTableRowView { (presenter as! JiraTempoPresenter).userInterface = self presenter.setupUserInterface() - presenter.checkCredentials() + checkCredentials() } - + + func checkCredentials() { + let user = JiraUser(url: pref.string(.settingsJiraUrl), + user: pref.string(.settingsJiraUser), + password: Keychain.getPassword(), + project: pref.string(.settingsJiraProjectKey), + issue: pref.string(.settingsJiraProjectIssueKey)) + presenter.checkCredentials(user) + } + func save() { - presenter.save(url: baseUrlTextField.stringValue, - user: userTextField.stringValue, - password: passwordTextField.stringValue) + saveCredentialsToPref() + } + + func saveCredentialsToPref() { + pref.set(baseUrlTextField.stringValue, forKey: .settingsJiraUrl) + pref.set(userTextField.stringValue, forKey: .settingsJiraUser) + // Save password only if different than the existing one + if passwordTextField.stringValue != Keychain.getPassword() { + Keychain.setPassword(passwordTextField.stringValue) + } } @IBAction func handlePurchaseButton (_ sender: NSButton) { @@ -60,13 +76,13 @@ class JiraTempoCell: NSTableRowView { @IBAction func projectNamePopupSelected (_ sender: NSPopUpButton) { if let title = sender.selectedItem?.title { - localPreferences.set(title, forKey: .settingsJiraProjectKey) + pref.set(title, forKey: .settingsJiraProjectKey) presenter.loadProjectIssues(for: title) } } @IBAction func projectIssueNamePopupSelected (_ sender: NSPopUpButton) { - localPreferences.set(sender.selectedItem?.title ?? "", forKey: .settingsJiraProjectIssueKey) + pref.set(sender.selectedItem?.title ?? "", forKey: .settingsJiraProjectIssueKey) } } @@ -111,17 +127,17 @@ extension JiraTempoCell: NSTextFieldDelegate { guard baseUrlTextField.stringValue != "", userTextField.stringValue != "", passwordTextField.stringValue != "" else { - // Fields are empty - save() + /// Fields are empty + saveCredentialsToPref() return } - guard baseUrlTextField.stringValue != localPreferences.string(.settingsJiraUrl) || - userTextField.stringValue != localPreferences.string(.settingsJiraUser) || + guard baseUrlTextField.stringValue != pref.string(.settingsJiraUrl) || + userTextField.stringValue != pref.string(.settingsJiraUser) || passwordTextField.stringValue != Keychain.getPassword() else { - // No change to the fields + /// No change to the fields return } - save() - presenter.checkCredentials() + saveCredentialsToPref() + checkCredentials() } } diff --git a/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoPresenter.swift b/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoPresenter.swift index 1f32482..ead9473 100644 --- a/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoPresenter.swift +++ b/Delivery/macOS/Screens/Settings/Output/JiraTempo/JiraTempoPresenter.swift @@ -13,10 +13,9 @@ import RCLog protocol JiraTempoPresenterInput: class { func setupUserInterface() - func checkCredentials() + func checkCredentials (_ credentials: JiraUser) func loadProjects() func loadProjectIssues (for projectKey: String) - func save (url: String, user: String, password: String) } protocol JiraTempoPresenterOutput: class { @@ -33,12 +32,13 @@ class JiraTempoPresenter { weak var userInterface: JiraTempoPresenterOutput? private var moduleJira = ModuleJiraTempo() private let store = Store.shared - private let pref = RCPreferences() + private var credentials: JiraUser? } extension JiraTempoPresenter: JiraTempoPresenterInput { - func checkCredentials() { + func checkCredentials (_ credentials: JiraUser) { + self.credentials = credentials loadProjects() } @@ -47,11 +47,11 @@ extension JiraTempoPresenter: JiraTempoPresenterInput { userInterface!.setPurchased(store.isJiraTempoPurchased) userInterface!.showErrorMessage("") - let selectedProjectName = pref.string(.settingsJiraProjectKey) + let selectedProjectName = credentials?.project ?? "" let projects = selectedProjectName != "" ? [selectedProjectName] : [] userInterface!.showProjects(projects, selectedProject: selectedProjectName) - let selectedProjectIssueName = pref.string(.settingsJiraProjectIssueKey) + let selectedProjectIssueName = credentials?.issue ?? "" let issues = selectedProjectIssueName != "" ? [selectedProjectIssueName] : [] userInterface!.showProjectIssues(issues, selectedIssue: selectedProjectIssueName) } @@ -75,11 +75,11 @@ extension JiraTempoPresenter: JiraTempoPresenterInput { userInterface.enableProgressIndicator(false) let titles = projects.map { $0.key } - let selectedProjectKey = wself.pref.string(.settingsJiraProjectKey) + let selectedProject = wself.credentials?.project ?? "" - userInterface.showProjects(titles, selectedProject: selectedProjectKey) - if projects.count > 0 && selectedProjectKey != "" { - wself.loadProjectIssues(for: selectedProjectKey) + userInterface.showProjects(titles, selectedProject: selectedProject) + if projects.count > 0 && selectedProject != "" { + wself.loadProjectIssues(for: selectedProject) } } @@ -106,7 +106,8 @@ extension JiraTempoPresenter: JiraTempoPresenterInput { } userInterface.enableProgressIndicator(false) let titles = issues.map { $0.key } - userInterface.showProjectIssues(titles, selectedIssue: wself.pref.string(.settingsJiraProjectIssueKey)) + let selectedIssue = wself.credentials?.issue ?? "" + userInterface.showProjectIssues(titles, selectedIssue: selectedIssue) } }, failure: { [weak self] (error) in @@ -122,21 +123,11 @@ extension JiraTempoPresenter: JiraTempoPresenterInput { RCLog(error) var errorMessage = error.localizedDescription switch error._code { - case -1001: errorMessage = "Server not reachable. Is your Jira limited to internal network?" - case 1: errorMessage = "Unknown, please verify login via browser. Possible causes are wrong domain or you're using an expired password which is causing Jira to ask for captcha." - default: errorMessage = error.localizedDescription + case -1001: errorMessage = "Server not reachable. Is your Jira limited to internal network?" + case 1: errorMessage = "Unknown, please verify login via browser. Possible causes are wrong domain or you're using an expired password which is causing Jira to ask for captcha." + default: errorMessage = error.localizedDescription } userInterface!.enableProgressIndicator(false) userInterface!.showErrorMessage("Error: \(errorMessage)") } - - func save (url: String, user: String, password: String) { - - pref.set(url, forKey: .settingsJiraUrl) - pref.set(user, forKey: .settingsJiraUser) - // Save password only if different than the existing one - if password != Keychain.getPassword() { - Keychain.setPassword(password) - } - } } diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index f9336b4..2d8cf73 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -563,6 +563,8 @@ 56ADBF6B21C3F625008350A6 /* GitUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADBF6921C3F625008350A6 /* GitUser.swift */; }; 56ADBF6D21C3F94D008350A6 /* GitUserParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADBF6C21C3F94D008350A6 /* GitUserParser.swift */; }; 56ADBF6E21C3F94D008350A6 /* GitUserParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADBF6C21C3F94D008350A6 /* GitUserParser.swift */; }; + 56B50A6C23E330BA002F35FD /* JiraUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B50A6B23E330BA002F35FD /* JiraUser.swift */; }; + 56B50A6D23E330BA002F35FD /* JiraUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B50A6B23E330BA002F35FD /* JiraUser.swift */; }; 56CD22BF1E72F89700F9CDB8 /* BuildScript.sh in Resources */ = {isa = PBXBuildFile; fileRef = 56CD22BE1E72F89700F9CDB8 /* BuildScript.sh */; }; 56D069E1216CABCB000D051D /* CreateMonthReportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D069E0216CABCB000D051D /* CreateMonthReportTests.swift */; }; 56D90CF820876F1100F24442 /* WizardJiraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D90CF720876F1100F24442 /* WizardJiraView.swift */; }; @@ -906,6 +908,7 @@ 569C4C7820231A0B0049FBF1 /* BrowserCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BrowserCell.xib; sourceTree = ""; }; 56ADBF6921C3F625008350A6 /* GitUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitUser.swift; sourceTree = ""; }; 56ADBF6C21C3F94D008350A6 /* GitUserParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitUserParser.swift; sourceTree = ""; }; + 56B50A6B23E330BA002F35FD /* JiraUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JiraUser.swift; sourceTree = ""; }; 56CD22BE1E72F89700F9CDB8 /* BuildScript.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = BuildScript.sh; sourceTree = ""; }; 56D069E0216CABCB000D051D /* CreateMonthReportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateMonthReportTests.swift; sourceTree = ""; }; 56D90CF720876F1100F24442 /* WizardJiraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardJiraView.swift; sourceTree = ""; }; @@ -1460,6 +1463,7 @@ 4065D31E1DD3B44200B73201 /* Week.swift */, 28A2839920382BBB00DDCB63 /* GitCommit.swift */, 56ADBF6921C3F625008350A6 /* GitUser.swift */, + 56B50A6B23E330BA002F35FD /* JiraUser.swift */, 5623914F21CBC6CD0031B882 /* Project.swift */, ); path = Entities; @@ -2259,6 +2263,7 @@ files = ( 280D712C1ED6043D005D2689 /* Day.swift in Sources */, 2892B29B1F094A760085BAC2 /* JiraRepository+Reports.swift in Sources */, + 56B50A6C23E330BA002F35FD /* JiraUser.swift in Sources */, 280D712D1ED6043D005D2689 /* Report.swift in Sources */, 28EE1F6420EE92DA00C5C1D6 /* CalendarAppPresenter.swift in Sources */, 280D712E1ED6043D005D2689 /* Settings.swift in Sources */, @@ -2545,6 +2550,7 @@ 2845B1402068351F006EFB3B /* TableViewCell.swift in Sources */, 284192DE2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, 28FE18A6207B369800DF796E /* CocoaHookupCell.swift in Sources */, + 56B50A6D23E330BA002F35FD /* JiraUser.swift in Sources */, 4065D3B41DD3B44200B73201 /* TaskTypeEstimator.swift in Sources */, 5623914E21CBC6B40031B882 /* ProjectsPresenter.swift in Sources */, 285465A8217858790052CB6A /* StoreView.swift in Sources */, From d8c3036f2c8c83083db791591a2b21c52f2e2f4a Mon Sep 17 00:00:00 2001 From: Cristian Baluta Date: Fri, 31 Jan 2020 18:05:46 +0200 Subject: [PATCH 40/48] Convert hours to seconds when saving worklogs --- Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift b/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift index cbefcf2..34daf45 100644 --- a/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift +++ b/Delivery/macOS/Screens/Worklogs/WorklogsPresenter.swift @@ -75,7 +75,7 @@ extension WorklogsPresenter: WorklogsPresenterInput { } userInterface!.showProgressIndicator(true) /// Save to jira tempo - moduleJira.postWorklog(worklog: worklog, duration: d, date: date!, success: { [weak self] in + moduleJira.postWorklog(worklog: worklog, duration: d.hoursToSec, date: date!, success: { [weak self] in DispatchQueue.main.async { if let userInterface = self?.userInterface { From b81434001cd63181d7f44dec9da596eaffbb6904 Mon Sep 17 00:00:00 2001 From: Baluta Cristian Date: Sat, 1 Feb 2020 18:31:41 +0200 Subject: [PATCH 41/48] Update cell icons --- .../EditIcon.imageset/Contents.json | 18 ++++++++++++ .../EditIcon.imageset/icons8-edit-32.png | Bin 0 -> 359 bytes .../EditIcon.imageset/icons8-edit-33.png | Bin 0 -> 359 bytes .../GitIcon.imageset/Contents.json | 11 +++---- .../GitIcon.imageset/GitIcon.png | Bin 1068 -> 0 bytes .../GitIcon.imageset/icons8-commit-git-80.png | Bin 0 -> 15130 bytes .../GitIcon.imageset/icons8-commit-git-81.png | Bin 0 -> 15130 bytes .../Tasks/cells/TaskCell/TaskCell.swift | 27 +++++++++--------- .../Screens/Tasks/cells/TaskCell/TaskCell.xib | 10 +++---- 9 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 Delivery/macOS/Images.xcassets/EditIcon.imageset/Contents.json create mode 100644 Delivery/macOS/Images.xcassets/EditIcon.imageset/icons8-edit-32.png create mode 100644 Delivery/macOS/Images.xcassets/EditIcon.imageset/icons8-edit-33.png delete mode 100644 Delivery/macOS/Images.xcassets/GitIcon.imageset/GitIcon.png create mode 100644 Delivery/macOS/Images.xcassets/GitIcon.imageset/icons8-commit-git-80.png create mode 100644 Delivery/macOS/Images.xcassets/GitIcon.imageset/icons8-commit-git-81.png diff --git a/Delivery/macOS/Images.xcassets/EditIcon.imageset/Contents.json b/Delivery/macOS/Images.xcassets/EditIcon.imageset/Contents.json new file mode 100644 index 0000000..88611c1 --- /dev/null +++ b/Delivery/macOS/Images.xcassets/EditIcon.imageset/Contents.json @@ -0,0 +1,18 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "icons8-edit-33.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "filename" : "icons8-edit-32.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Delivery/macOS/Images.xcassets/EditIcon.imageset/icons8-edit-32.png b/Delivery/macOS/Images.xcassets/EditIcon.imageset/icons8-edit-32.png new file mode 100644 index 0000000000000000000000000000000000000000..25beea1e94ac7e720f9c1c773f1e507455f6fc27 GIT binary patch literal 359 zcmV-t0hs=YP)j${542Nk@6SMYxaPcNC)FELG-E*a>?;v+m_ zw@mnQEdGS6Xn!S1WQD^{04|Nc-A9T_v~t7YFd!$X09E=k`UalskjJgL+Y0~y002ovPDHLk FV1fjvolyV) literal 0 HcmV?d00001 diff --git a/Delivery/macOS/Images.xcassets/EditIcon.imageset/icons8-edit-33.png b/Delivery/macOS/Images.xcassets/EditIcon.imageset/icons8-edit-33.png new file mode 100644 index 0000000000000000000000000000000000000000..25beea1e94ac7e720f9c1c773f1e507455f6fc27 GIT binary patch literal 359 zcmV-t0hs=YP)j${542Nk@6SMYxaPcNC)FELG-E*a>?;v+m_ zw@mnQEdGS6Xn!S1WQD^{04|Nc-A9T_v~t7YFd!$X09E=k`UalskjJgL+Y0~y002ovPDHLk FV1fjvolyV) literal 0 HcmV?d00001 diff --git a/Delivery/macOS/Images.xcassets/GitIcon.imageset/Contents.json b/Delivery/macOS/Images.xcassets/GitIcon.imageset/Contents.json index 345adc5..537070c 100644 --- a/Delivery/macOS/Images.xcassets/GitIcon.imageset/Contents.json +++ b/Delivery/macOS/Images.xcassets/GitIcon.imageset/Contents.json @@ -1,17 +1,14 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "GitIcon.png", + "idiom" : "mac", + "filename" : "icons8-commit-git-81.png", "scale" : "1x" }, { - "idiom" : "universal", + "idiom" : "mac", + "filename" : "icons8-commit-git-80.png", "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" } ], "info" : { diff --git a/Delivery/macOS/Images.xcassets/GitIcon.imageset/GitIcon.png b/Delivery/macOS/Images.xcassets/GitIcon.imageset/GitIcon.png deleted file mode 100644 index dfcfd8aaaeb30e160df01c37c276422af1f410ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1068 zcmaJ=%TE(Q9NvmmED{SLDn^rWF#-wg?zWV6Hx&BVmNs#*DNSs+=(0P&2KM3ZKo?@L z5H(1QH!nux&4h$_!-J1#BACEG;3Ik=FO=f52m*4mOUfUI_`bU?kzVl9@H( z(ClDAnw^kXne6Way*7slRER`itCO0=*-^5>%i*(Qrbtk6K@(AOH>skK14-S4Ams1y zNesh)FzaVRkzgRy1p+h^qG*<4XdlgR49x{-u=kNTnkkQRc|N@t3*VyTI6?+TQKeGJ zUkdtlbBtnGmZj(b6$tpShtHbPkZAiftF6kwLrXFh11Y)&97b_OpF&X*XS!R0YV6Z$ z)?S-%!zf!cD8^4aB~={-;s36xy6qxi8bps@mm9BvfL6j8DF?Lo^g1}`p3yGQpvwW1qJbp!yxloW!bw}7Z zolJEzOsXdmPO*tdFwHOtHpPe2RV=SdQ!3O@6)PWL6E(4pAXEcK=Ao%fLpg2gDyS6B zDYbKn)a2X4%C&Q0Yho#!4CRdXuW?sRSVPWnU%dFTFMp_E&6`-LkW z^T?0YuWN*RGWO`ink%;AS__x$I`8GfTUUX*D|dS{_Nrm=!D;VX&r-1bv2k%b;5s%9 z`tCgSwsyq7eTpyrEEn&&`iYJ2^DXVP_h@r$X?3x%;?CcAoLT=6Upm#aY&T_gE`Yg# z*!j#{b7m(Do*&t2kF@m7Z{Cy^JYUN1htGMRxVL`gUYzXgX(bLFCx-8z?W%wGd&_yh M+0-EaEOBk&4=IIEHvj+t diff --git a/Delivery/macOS/Images.xcassets/GitIcon.imageset/icons8-commit-git-80.png b/Delivery/macOS/Images.xcassets/GitIcon.imageset/icons8-commit-git-80.png new file mode 100644 index 0000000000000000000000000000000000000000..a6298be6fe32620856c74a6c08234f21feb62a83 GIT binary patch literal 15130 zcmeI3eQ*=#T4AeSYK@?APLMTpoNp|zrG)od_lVZz}8rzD6@i?}dO|vxF-DWp! zS`o?>f%-yyNtSf*@`xD|J_ZZ=?D*Y9#o)bJm(Sz}IN0bb(9| zH;z;PvWWE?CJ;nnK3`R>RD0&ptQgWWKCy<=M?(@&6NG(MRASgVPSMnGwY=cae%tlF zR>S)o+WRdY$|JeB0AJc1<|>=ZtJvl`*5=dBa%S73G)NHQ6h;#b1qGRoI<&F8G>Fw^ zQmctclsbpDNG+(T_INceG0bT!`T`wmFcoU7Hod`OD>RyCXpEGhfTV1s!K|YUG-abH zgC_CPII8XmJjp;FHz%`UUQ&2b(5QKt8Zn|cv|6=L@|C!*kd!PW$O(3UA{k{Q(x9ivOePond~06FqjjMCs{k=ctfF#rDdh00U$^q z&FGY?nk0^_;AAlpX1S6Ea4C~BUA@BlQtu?w#kQGK?xiYsCH0XKWM;jQ% z&zfwkuExYzb>_bh`U?=`2Tw)3b?^)gtl6P6YF7_78mtB|GA1?)20J;V zEmb+LuU2O@7>VhvMhd)0gZi1U!rkfC1=qI1-Rahp^FD!BoW?}8v}VYqp1$#ep%UCE zaLxjY#bmLQ>CNd5FCXQC)ovc_U|C(47I0lLPkKj2kE|>-~#<-A(U;)Mj(?G@;7ZMRH zz_?%<$Qa{7B7y}N7fb^gV_Zl?umIzNX&_^a3yBC8U|cW_WQ=hk5y1kC3#NgLF)kz` zSb%ZCG>|dIg+v4kFfNz|GRC-&h+qN61=B#r7#9)|EWo&68ps&qLL!0%7#Bj0>iLj4>`GB3OWN!8DLD#)U)#3otI21~SID zkceOb#s$+r#uyh85iG#CU>eBynz*u4kNGWJlwb1Te$)}7^q=OD?9|zJcS@y zS_$GxKluKdAR-h&e6yG!=w}JyR`IF7bk8A(>w;zOqN?b>&z5H1rjQV25hq>JcTTa?@$GlhI2;?1clO071#g)bLx+eC% zGjryh`GyN4Ug=g^FHW>x41Q(pJowSMZQi-{ckDg2e&wpb8tA>h|N7ki(=WYSTwhVN zX!gi=j_+E!#>+hu-F`*68reLl{gy|jPL1xl?bOCkE#u|7o}=sb7lqp&W%EB?k=662 z^!cRI^{aO|Z*TE;ww-M{wq)PsU1Ow27W*&$>pQ`xdAIur#azysP?~pob4A&_qd#)J zv*&JIH|OpL~)T+g7-{<%zH6=FNZo zffou#`({j^Gi{xFO^;*LE55NkZ~me9@!A_-Z0m5{{NJnxKG`YVwJgWvKfR;%eEF88 z2Pd7Ma?8GY`aopE2)Cdyeee<_x~Rv%gp~{;g2shPvK|&hNKe1B-v*_NZtJNNwbQxC%~o9|!gJ+nNp!xcCn zI=g=#T4AeSYK@?APLMTpoNp|zrG)od_lVZz}8rzD6@i?}dO|vxF-DWp! zS`o?>f%-yyNtSf*@`xD|J_ZZ=?D*Y9#o)bJm(Sz}IN0bb(9| zH;z;PvWWE?CJ;nnK3`R>RD0&ptQgWWKCy<=M?(@&6NG(MRASgVPSMnGwY=cae%tlF zR>S)o+WRdY$|JeB0AJc1<|>=ZtJvl`*5=dBa%S73G)NHQ6h;#b1qGRoI<&F8G>Fw^ zQmctclsbpDNG+(T_INceG0bT!`T`wmFcoU7Hod`OD>RyCXpEGhfTV1s!K|YUG-abH zgC_CPII8XmJjp;FHz%`UUQ&2b(5QKt8Zn|cv|6=L@|C!*kd!PW$O(3UA{k{Q(x9ivOePond~06FqjjMCs{k=ctfF#rDdh00U$^q z&FGY?nk0^_;AAlpX1S6Ea4C~BUA@BlQtu?w#kQGK?xiYsCH0XKWM;jQ% z&zfwkuExYzb>_bh`U?=`2Tw)3b?^)gtl6P6YF7_78mtB|GA1?)20J;V zEmb+LuU2O@7>VhvMhd)0gZi1U!rkfC1=qI1-Rahp^FD!BoW?}8v}VYqp1$#ep%UCE zaLxjY#bmLQ>CNd5FCXQC)ovc_U|C(47I0lLPkKj2kE|>-~#<-A(U;)Mj(?G@;7ZMRH zz_?%<$Qa{7B7y}N7fb^gV_Zl?umIzNX&_^a3yBC8U|cW_WQ=hk5y1kC3#NgLF)kz` zSb%ZCG>|dIg+v4kFfNz|GRC-&h+qN61=B#r7#9)|EWo&68ps&qLL!0%7#Bj0>iLj4>`GB3OWN!8DLD#)U)#3otI21~SID zkceOb#s$+r#uyh85iG#CU>eBynz*u4kNGWJlwb1Te$)}7^q=OD?9|zJcS@y zS_$GxKluKdAR-h&e6yG!=w}JyR`IF7bk8A(>w;zOqN?b>&z5H1rjQV25hq>JcTTa?@$GlhI2;?1clO071#g)bLx+eC% zGjryh`GyN4Ug=g^FHW>x41Q(pJowSMZQi-{ckDg2e&wpb8tA>h|N7ki(=WYSTwhVN zX!gi=j_+E!#>+hu-F`*68reLl{gy|jPL1xl?bOCkE#u|7o}=sb7lqp&W%EB?k=662 z^!cRI^{aO|Z*TE;ww-M{wq)PsU1Ow27W*&$>pQ`xdAIur#azysP?~pob4A&_qd#)J zv*&JIH|OpL~)T+g7-{<%zH6=FNZo zffou#`({j^Gi{xFO^;*LE55NkZ~me9@!A_-Z0m5{{NJnxKG`YVwJgWvKfR;%eEF88 z2Pd7Ma?8GY`aopE2)Cdyeee<_x~Rv%gp~{;g2shPvK|&hNKe1B-v*_NZtJNNwbQxC%~o9|!gJ+nNp!xcCn zI=g diff --git a/Delivery/macOS/Screens/Settings/Settings.storyboard b/Delivery/macOS/Screens/Settings/Settings.storyboard index b4c93a0..4340ba4 100644 --- a/Delivery/macOS/Screens/Settings/Settings.storyboard +++ b/Delivery/macOS/Screens/Settings/Settings.storyboard @@ -1,8 +1,8 @@ - + - + @@ -47,7 +47,7 @@ - + @@ -78,17 +78,17 @@ - + - - + + @@ -45,14 +44,14 @@ - + - + @@ -72,19 +71,19 @@ - + - + - + - + @@ -210,7 +209,7 @@ - + @@ -218,12 +217,9 @@ - - - - + @@ -232,6 +228,9 @@ + + + @@ -240,13 +239,13 @@ - + - + @@ -257,7 +256,7 @@ - + @@ -268,7 +267,7 @@ - + @@ -288,7 +287,7 @@ - + @@ -298,25 +297,22 @@ - - - - + @@ -325,8 +321,11 @@ + + + - + @@ -410,7 +409,7 @@ - + @@ -420,13 +419,13 @@ - + - + diff --git a/Jirassic.xcodeproj/project.pbxproj b/Jirassic.xcodeproj/project.pbxproj index efda615..1fc406b 100644 --- a/Jirassic.xcodeproj/project.pbxproj +++ b/Jirassic.xcodeproj/project.pbxproj @@ -93,30 +93,11 @@ 280D71611ED6043D005D2689 /* MenuBarIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3661DD3B44200B73201 /* MenuBarIconView.swift */; }; 280D71621ED6043D005D2689 /* FlipAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3521DD3B44200B73201 /* FlipAnimation.swift */; }; 280D71631ED6043D005D2689 /* Animatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE42F1DFD7C8D00D4FD45 /* Animatable.swift */; }; - 280D71641ED6043D005D2689 /* PlaceholderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28279E9B1E8300E200EAF9FC /* PlaceholderViewController.swift */; }; - 280D71661ED6043D005D2689 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE42B1DFC3F8700D4FD45 /* WelcomeViewController.swift */; }; - 280D71681ED6043D005D2689 /* TaskSuggestionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 405B15601DEF3D080009871C /* TaskSuggestionViewController.swift */; }; - 280D71691ED6043D005D2689 /* TaskSuggestionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 405B15621DEF3F2A0009871C /* TaskSuggestionPresenter.swift */; }; - 280D716B1ED6043D005D2689 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C76B1DE8724400CA545E /* AccountViewController.swift */; }; - 280D716C1ED6043D005D2689 /* CloudKitLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C7631DE8721100CA545E /* CloudKitLoginViewController.swift */; }; - 280D716E1ED6043D005D2689 /* LoginPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C7651DE8721100CA545E /* LoginPresenter.swift */; }; - 280D716F1ED6043D005D2689 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C7661DE8721100CA545E /* LoginViewController.swift */; }; 280D71701ED6043D005D2689 /* ExtensionsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE4231DF7646F00D4FD45 /* ExtensionsInteractor.swift */; }; 280D71711ED6043D005D2689 /* ExtensionsInstallerInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280F507A1EC8541D007416AB /* ExtensionsInstallerInteractor.swift */; }; 280D71721ED6043D005D2689 /* AppleScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE4221DF7646F00D4FD45 /* AppleScript.swift */; }; 280D71731ED6043D005D2689 /* SandboxedAppleScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE4241DF7646F00D4FD45 /* SandboxedAppleScript.swift */; }; - 280D71741ED6043D005D2689 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3751DD3B44200B73201 /* SettingsViewController.swift */; }; - 280D71751ED6043D005D2689 /* SettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3741DD3B44200B73201 /* SettingsPresenter.swift */; }; - 280D71761ED6043D005D2689 /* SettingsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3731DD3B44200B73201 /* SettingsInteractor.swift */; }; 280D71781ED6043D005D2689 /* NewTaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3711DD3B44200B73201 /* NewTaskViewController.swift */; }; - 280D717A1ED6043D005D2689 /* TasksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3851DD3B44200B73201 /* TasksViewController.swift */; }; - 280D717B1ED6043D005D2689 /* TasksPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3831DD3B44200B73201 /* TasksPresenter.swift */; }; - 280D717C1ED6043D005D2689 /* CalendarScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */; }; - 280D717D1ED6043D005D2689 /* TasksScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3841DD3B44200B73201 /* TasksScrollView.swift */; }; - 280D717E1ED6043D005D2689 /* TasksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */; }; - 280D71811ED6043D005D2689 /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F2811E586426002BE564 /* DataSource.swift */; }; - 280D71821ED6043D005D2689 /* CellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3781DD3B44200B73201 /* CellProtocol.swift */; }; - 280D71831ED6043D005D2689 /* TasksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EDE9381E59EC1500B360A4 /* TasksView.swift */; }; 280D718D1ED6043D005D2689 /* InternalNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3681DD3B44200B73201 /* InternalNotifications.swift */; }; 280D718E1ED6043D005D2689 /* UserNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3691DD3B44200B73201 /* UserNotifications.swift */; }; 280D718F1ED6043D005D2689 /* SleepNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D36A1DD3B44200B73201 /* SleepNotifications.swift */; }; @@ -137,11 +118,6 @@ 280D71BC1ED60683005D2689 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 28577EB91E8AF379002B07FD /* libsqlite3.tbd */; }; 280D71BE1ED6069A005D2689 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4055B1381E0D82A900279430 /* ServiceManagement.framework */; }; 280D71BF1ED608D6005D2689 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4065D3591DD3B44200B73201 /* Main.storyboard */; }; - 280D71C01ED608D6005D2689 /* Placeholder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28E896621E830D6700722032 /* Placeholder.storyboard */; }; - 280D71C11ED608D6005D2689 /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 40FCE42D1DFC576C00D4FD45 /* Welcome.storyboard */; }; - 280D71C21ED608D6005D2689 /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5685C7641DE8721100CA545E /* Login.storyboard */; }; - 280D71C31ED608D6005D2689 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 40E092401DE385E4001EF5DA /* Settings.storyboard */; }; - 280D71C41ED608D6005D2689 /* Tasks.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 406384881DE388C5004795A4 /* Tasks.storyboard */; }; 280D71C81ED608D6005D2689 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4065D35B1DD3B44200B73201 /* Images.xcassets */; }; 280D71C91ED60908005D2689 /* jirassic.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 4065D3611DD3B44200B73201 /* jirassic.sdef */; }; 280F507D1EC868B0007416AB /* StringArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280F507C1EC868B0007416AB /* StringArray.swift */; }; @@ -157,7 +133,6 @@ 28279AD321BBF09900376304 /* Jirassic.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 2818847F21A4A2F800B33B9C /* Jirassic.xcdatamodeld */; }; 28279AD521C6245700376304 /* GitUsersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28279AD421C6245700376304 /* GitUsersViewController.swift */; }; 28279AD621C6245700376304 /* GitUsersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28279AD421C6245700376304 /* GitUsersViewController.swift */; }; - 28279E9C1E8300E200EAF9FC /* PlaceholderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28279E9B1E8300E200EAF9FC /* PlaceholderViewController.swift */; }; 284192D72018841700E64A9A /* JProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 284192D62018841700E64A9A /* JProject.swift */; }; 284192D82018841700E64A9A /* JProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 284192D62018841700E64A9A /* JProject.swift */; }; 284192DA2018855B00E64A9A /* JiraRepository+Projects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 284192D92018855B00E64A9A /* JiraRepository+Projects.swift */; }; @@ -170,38 +145,9 @@ 2845B13A206703C5006EFB3B /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B138206703C5006EFB3B /* Keychain.swift */; }; 2845B13F2068351F006EFB3B /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B13E2068351F006EFB3B /* TableViewCell.swift */; }; 2845B1402068351F006EFB3B /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B13E2068351F006EFB3B /* TableViewCell.swift */; }; - 2845B149206AE3A8006EFB3B /* ReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B144206AE3A8006EFB3B /* ReportCell.swift */; }; - 2845B14A206AE3A8006EFB3B /* ReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B144206AE3A8006EFB3B /* ReportCell.swift */; }; - 2845B14B206AE3A8006EFB3B /* ReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B145206AE3A8006EFB3B /* ReportCell.xib */; }; - 2845B14C206AE3A8006EFB3B /* ReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B145206AE3A8006EFB3B /* ReportCell.xib */; }; - 2845B14D206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B146206AE3A8006EFB3B /* ReportCellPresenter.swift */; }; - 2845B14E206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B146206AE3A8006EFB3B /* ReportCellPresenter.swift */; }; - 2845B14F206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */; }; - 2845B150206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */; }; - 2845B151206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */; }; - 2845B152206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */; }; - 2845B160206AE468006EFB3B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell.swift */; }; - 2845B161206AE468006EFB3B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B155206AE467006EFB3B /* TaskCell.swift */; }; - 2845B162206AE468006EFB3B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B156206AE467006EFB3B /* TaskCell.xib */; }; - 2845B163206AE468006EFB3B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B156206AE467006EFB3B /* TaskCell.xib */; }; - 2845B164206AE468006EFB3B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */; }; - 2845B165206AE468006EFB3B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */; }; - 2845B166206AE468006EFB3B /* TasksHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B159206AE467006EFB3B /* TasksHeaderView.swift */; }; - 2845B167206AE468006EFB3B /* TasksHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B159206AE467006EFB3B /* TasksHeaderView.swift */; }; - 2845B168206AE468006EFB3B /* TasksHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */; }; - 2845B169206AE468006EFB3B /* TasksHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */; }; - 2845B16A206AE468006EFB3B /* NonTaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* NonTaskCell.swift */; }; - 2845B16B206AE468006EFB3B /* NonTaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B15C206AE467006EFB3B /* NonTaskCell.swift */; }; - 2845B16C206AE468006EFB3B /* NonTaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15D206AE467006EFB3B /* NonTaskCell.xib */; }; - 2845B16D206AE468006EFB3B /* NonTaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2845B15D206AE467006EFB3B /* NonTaskCell.xib */; }; - 2845B16E206AE46F006EFB3B /* TaskCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2845B154206AE467006EFB3B /* TaskCellTests.swift */; }; 285465A2216C84E10052CB6A /* CreateMonthReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A1216C84E10052CB6A /* CreateMonthReport.swift */; }; 285465A3216C84E10052CB6A /* CreateMonthReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A1216C84E10052CB6A /* CreateMonthReport.swift */; }; 285465A4216C84E10052CB6A /* CreateMonthReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A1216C84E10052CB6A /* CreateMonthReport.swift */; }; - 285465A7217858790052CB6A /* StoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A6217858790052CB6A /* StoreView.swift */; }; - 285465A8217858790052CB6A /* StoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285465A6217858790052CB6A /* StoreView.swift */; }; - 285465AD217858AF0052CB6A /* StoreView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 285465AC217858AF0052CB6A /* StoreView.xib */; }; - 285465AE217858AF0052CB6A /* StoreView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 285465AC217858AF0052CB6A /* StoreView.xib */; }; 28577EAB1E8ADC53002B07FD /* SqliteRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EA71E8ADC53002B07FD /* SqliteRepository.swift */; }; 28577EAC1E8ADC53002B07FD /* SqliteRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EA71E8ADC53002B07FD /* SqliteRepository.swift */; }; 28577EAD1E8ADC53002B07FD /* SSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28577EA81E8ADC53002B07FD /* SSettings.swift */; }; @@ -219,20 +165,6 @@ 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */; }; 286BC00421909F85004D4CDD /* CloseDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDay.swift */; }; 286BC00521909F85004D4CDD /* CloseDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 286BC00321909F85004D4CDD /* CloseDay.swift */; }; - 287195152022E1E2001C237E /* HookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195142022E1E2001C237E /* HookupPresenter.swift */; }; - 287195162022E1E2001C237E /* HookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195142022E1E2001C237E /* HookupPresenter.swift */; }; - 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195172022E8BC001C237E /* OutputTableViewDataSource.swift */; }; - 287195192022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195172022E8BC001C237E /* OutputTableViewDataSource.swift */; }; - 2871951B2022ECF7001C237E /* OutputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2871951A2022ECF7001C237E /* OutputsScrollView.swift */; }; - 2871951C2022ECF7001C237E /* OutputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2871951A2022ECF7001C237E /* OutputsScrollView.swift */; }; - 2871951E2022FD14001C237E /* InputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2871951D2022FD14001C237E /* InputsScrollView.swift */; }; - 2871951F2022FD14001C237E /* InputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2871951D2022FD14001C237E /* InputsScrollView.swift */; }; - 287195212022FD87001C237E /* InputsTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195202022FD87001C237E /* InputsTableViewDataSource.swift */; }; - 287195222022FD87001C237E /* InputsTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195202022FD87001C237E /* InputsTableViewDataSource.swift */; }; - 2871952820241A6C001C237E /* TrackingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2871952720241A6C001C237E /* TrackingView.xib */; }; - 2871952920241A6C001C237E /* TrackingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2871952720241A6C001C237E /* TrackingView.xib */; }; - 2871952B20241B25001C237E /* TrackingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2871952A20241B25001C237E /* TrackingView.swift */; }; - 2871952C20241B25001C237E /* TrackingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2871952A20241B25001C237E /* TrackingView.swift */; }; 2871952D2025C353001C237E /* CoreDataRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38C1DD3B44200B73201 /* CoreDataRepository.swift */; }; 2871952E2025C356001C237E /* CoreDataRepository+Tasks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928891E910F4D0022AB55 /* CoreDataRepository+Tasks.swift */; }; 2871952F2025C359001C237E /* CoreDataRepository+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A9288C1E910F970022AB55 /* CoreDataRepository+Settings.swift */; }; @@ -243,16 +175,6 @@ 287195362027CAD9001C237E /* TimeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195352027CAD9001C237E /* TimeInteractor.swift */; }; 287195372027CAD9001C237E /* TimeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287195352027CAD9001C237E /* TimeInteractor.swift */; }; 287558451EFE5969009A2503 /* ReadDaysInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 280D71171ED4CFCB005D2689 /* ReadDaysInteractorTests.swift */; }; - 287B358720FBAFC40022F43E /* WizardCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287B358620FBAFC40022F43E /* WizardCalendarView.swift */; }; - 287B358820FBAFC40022F43E /* WizardCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287B358620FBAFC40022F43E /* WizardCalendarView.swift */; }; - 287B358A20FBAFD60022F43E /* WizardCalendarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 287B358920FBAFD60022F43E /* WizardCalendarView.xib */; }; - 287B358B20FBAFD60022F43E /* WizardCalendarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 287B358920FBAFD60022F43E /* WizardCalendarView.xib */; }; - 288BB67E2085252900CF720A /* WizardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 288BB67D2085252900CF720A /* WizardViewController.swift */; }; - 288BB67F2085252900CF720A /* WizardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 288BB67D2085252900CF720A /* WizardViewController.swift */; }; - 288BB6812087152B00CF720A /* WizardAppleScriptView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 288BB6802087152B00CF720A /* WizardAppleScriptView.xib */; }; - 288BB6822087152B00CF720A /* WizardAppleScriptView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 288BB6802087152B00CF720A /* WizardAppleScriptView.xib */; }; - 288BB6842087157F00CF720A /* WizardAppleScriptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 288BB6832087157F00CF720A /* WizardAppleScriptView.swift */; }; - 288BB6852087157F00CF720A /* WizardAppleScriptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 288BB6832087157F00CF720A /* WizardAppleScriptView.swift */; }; 288BB6872087169F00CF720A /* ViewXib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 288BB6862087169F00CF720A /* ViewXib.swift */; }; 288BB6882087169F00CF720A /* ViewXib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 288BB6862087169F00CF720A /* ViewXib.swift */; }; 2892B2981F094A170085BAC2 /* JiraRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892B2971F094A170085BAC2 /* JiraRepository.swift */; }; @@ -263,14 +185,8 @@ 2892B29F1F094B0D0085BAC2 /* JReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892B29D1F094B0D0085BAC2 /* JReport.swift */; }; 2892B2A71F094C800085BAC2 /* JWorkAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892B2A61F094C800085BAC2 /* JWorkAttribute.swift */; }; 2892B2A81F094C800085BAC2 /* JWorkAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892B2A61F094C800085BAC2 /* JWorkAttribute.swift */; }; - 2892E80B208D9B42004E5298 /* InputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2892E80A208D9B42004E5298 /* InputsScrollView.xib */; }; - 2892E80C208D9B42004E5298 /* InputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2892E80A208D9B42004E5298 /* InputsScrollView.xib */; }; - 2892E80E208D9DD0004E5298 /* OutputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2892E80D208D9DD0004E5298 /* OutputsScrollView.xib */; }; - 2892E80F208D9DD0004E5298 /* OutputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2892E80D208D9DD0004E5298 /* OutputsScrollView.xib */; }; 2892E811208E6275004E5298 /* LocalPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892E810208E6275004E5298 /* LocalPreferences.swift */; }; 2892E812208E6275004E5298 /* LocalPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2892E810208E6275004E5298 /* LocalPreferences.swift */; }; - 2898D3A82181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */; }; - 2898D3A92181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */; }; 2898D3AB2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */; }; 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */; }; 2898D3AE2184ED3000CF5AD4 /* Components.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2898D3AD2184ED3000CF5AD4 /* Components.storyboard */; }; @@ -279,25 +195,17 @@ 2898D3B22185959300CF5AD4 /* EditableTimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2898D3B02185959300CF5AD4 /* EditableTimeBox.swift */; }; 28A283942037874600DDCB63 /* ModuleGitLogs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283932037874600DDCB63 /* ModuleGitLogs.swift */; }; 28A283952037874600DDCB63 /* ModuleGitLogs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283932037874600DDCB63 /* ModuleGitLogs.swift */; }; - 28A283972037975600DDCB63 /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283962037975600DDCB63 /* Saveable.swift */; }; - 28A283982037975600DDCB63 /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283962037975600DDCB63 /* Saveable.swift */; }; 28A2839A20382BBB00DDCB63 /* GitCommit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A2839920382BBB00DDCB63 /* GitCommit.swift */; }; 28A2839B20382BBB00DDCB63 /* GitCommit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A2839920382BBB00DDCB63 /* GitCommit.swift */; }; 28A2839F20385BE000DDCB63 /* GitCommitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A2839E20385BE000DDCB63 /* GitCommitsParser.swift */; }; 28A283A020385BE000DDCB63 /* GitCommitsParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A2839E20385BE000DDCB63 /* GitCommitsParser.swift */; }; 28A283A220385F8C00DDCB63 /* GitCommitsParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283A120385F8C00DDCB63 /* GitCommitsParserTests.swift */; }; - 28A283A52038AFB100DDCB63 /* JirassicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283A42038AFB100DDCB63 /* JirassicCell.swift */; }; - 28A283A62038AFB100DDCB63 /* JirassicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283A42038AFB100DDCB63 /* JirassicCell.swift */; }; - 28A283A82038AFC500DDCB63 /* JirassicCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28A283A72038AFC500DDCB63 /* JirassicCell.xib */; }; - 28A283A92038AFC500DDCB63 /* JirassicCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28A283A72038AFC500DDCB63 /* JirassicCell.xib */; }; 28A283AB203A93AB00DDCB63 /* GitBranchParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283AA203A93AB00DDCB63 /* GitBranchParser.swift */; }; 28A283AC203A93AB00DDCB63 /* GitBranchParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283AA203A93AB00DDCB63 /* GitBranchParser.swift */; }; 28A283AE203C069F00DDCB63 /* GitBranchParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283AD203C069F00DDCB63 /* GitBranchParserTests.swift */; }; 28A283B0203CB1A100DDCB63 /* MergeTasksInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283AF203CB1A100DDCB63 /* MergeTasksInteractor.swift */; }; 28A283B1203CB1A100DDCB63 /* MergeTasksInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283AF203CB1A100DDCB63 /* MergeTasksInteractor.swift */; }; 28A283B3203CB1B900DDCB63 /* MergeTasksInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A283B2203CB1B900DDCB63 /* MergeTasksInteractorTests.swift */; }; - 28A5F27E1E5789FC002BE564 /* TasksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */; }; - 28A5F2821E586426002BE564 /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A5F2811E586426002BE564 /* DataSource.swift */; }; 28A928781E8F78580022AB55 /* SQLiteSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* SQLiteSchema.swift */; }; 28A928791E8F78580022AB55 /* SQLiteSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A928771E8F78580022AB55 /* SQLiteSchema.swift */; }; 28A9287A1E8FA8E90022AB55 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 28577EB91E8AF379002B07FD /* libsqlite3.tbd */; }; @@ -320,10 +228,6 @@ 28AA500D1EDCD51300AAF03D /* CTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38E1DD3B44200B73201 /* CTask.swift */; }; 28AA500E1EDCD51300AAF03D /* CUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38F1DD3B44200B73201 /* CUser.swift */; }; 28AFE7311E9A594500BAAD8C /* UserDefaults+token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */; }; - 28B116B821AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */; }; - 28B116B921AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */; }; - 28B116BB21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */; }; - 28B116BC21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */; }; 28C6A38421D359E60036DB29 /* RemoveDuplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */; }; 28C6A38521D359E60036DB29 /* RemoveDuplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */; }; 28C9C6221EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */; }; @@ -334,42 +238,15 @@ 28CBB59A20474755006F9D3A /* ParseGitBranchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28CBB59920474755006F9D3A /* ParseGitBranchTests.swift */; }; 28DCA4552018B6E700DFAE29 /* JProjectIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28DCA4542018B6E700DFAE29 /* JProjectIssue.swift */; }; 28DCA4562018B6E700DFAE29 /* JProjectIssue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28DCA4542018B6E700DFAE29 /* JProjectIssue.swift */; }; - 28E896631E830D6700722032 /* Placeholder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28E896621E830D6700722032 /* Placeholder.storyboard */; }; 28E896711E83732200722032 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 28E8966F1E83732200722032 /* AppDelegate.m */; }; 28E896721E83732200722032 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 28E896701E83732200722032 /* main.m */; }; 28E896751E83770D00722032 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28E896731E83770D00722032 /* MainMenu.xib */; }; - 28EDE9391E59EC1500B360A4 /* TasksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EDE9381E59EC1500B360A4 /* TasksView.swift */; }; - 28EE1F5E20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F5D20EE8D1000C5C1D6 /* CalendarCell.swift */; }; - 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F5D20EE8D1000C5C1D6 /* CalendarCell.swift */; }; - 28EE1F6120EE8D6100C5C1D6 /* CalendarCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */; }; - 28EE1F6220EE8D6100C5C1D6 /* CalendarCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */; }; - 28EE1F6420EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */; }; - 28EE1F6520EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */; }; - 28FB265120224E3A00AEA38D /* JiraTempoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265020224E3A00AEA38D /* JiraTempoCell.swift */; }; - 28FB265220224E3A00AEA38D /* JiraTempoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265020224E3A00AEA38D /* JiraTempoCell.swift */; }; - 28FB265420224E5B00AEA38D /* JiraTempoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28FB265320224E5B00AEA38D /* JiraTempoCell.xib */; }; - 28FB265520224E5B00AEA38D /* JiraTempoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28FB265320224E5B00AEA38D /* JiraTempoCell.xib */; }; - 28FB265820224E9B00AEA38D /* HookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28FB265720224E9B00AEA38D /* HookupCell.xib */; }; - 28FB265920224E9B00AEA38D /* HookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28FB265720224E9B00AEA38D /* HookupCell.xib */; }; - 28FB265B20224EA700AEA38D /* HookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265A20224EA700AEA38D /* HookupCell.swift */; }; - 28FB265C20224EA700AEA38D /* HookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265A20224EA700AEA38D /* HookupCell.swift */; }; - 28FB265E2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265D2022DC2B00AEA38D /* JiraTempoPresenter.swift */; }; - 28FB265F2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FB265D2022DC2B00AEA38D /* JiraTempoPresenter.swift */; }; 28FE1887207A520B00DF796E /* NewTaskCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FE1886207A520B00DF796E /* NewTaskCommand.swift */; }; 28FE1888207A520B00DF796E /* NewTaskCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FE1886207A520B00DF796E /* NewTaskCommand.swift */; }; - 28FE18A5207B369800DF796E /* CocoaHookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FE18A4207B369800DF796E /* CocoaHookupCell.swift */; }; - 28FE18A6207B369800DF796E /* CocoaHookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FE18A4207B369800DF796E /* CocoaHookupCell.swift */; }; - 28FE18A8207B36CB00DF796E /* CocoaHookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28FE18A7207B36CB00DF796E /* CocoaHookupCell.xib */; }; - 28FE18A9207B36CB00DF796E /* CocoaHookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28FE18A7207B36CB00DF796E /* CocoaHookupCell.xib */; }; - 28FE18AB207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FE18AA207B375A00DF796E /* CocoaHookupPresenter.swift */; }; - 28FE18AC207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28FE18AA207B375A00DF796E /* CocoaHookupPresenter.swift */; }; 4051D5F91E0EA48A002042BB /* AppLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4051D5F81E0EA48A002042BB /* AppLauncher.swift */; }; 4055B1361E0D821500279430 /* JirassicLauncher.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4055B1231E0D802300279430 /* JirassicLauncher.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 4055B1391E0D82A900279430 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4055B1381E0D82A900279430 /* ServiceManagement.framework */; }; - 405B15611DEF3D080009871C /* TaskSuggestionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 405B15601DEF3D080009871C /* TaskSuggestionViewController.swift */; }; - 405B15631DEF3F2A0009871C /* TaskSuggestionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 405B15621DEF3F2A0009871C /* TaskSuggestionPresenter.swift */; }; 405B15661DEF75660009871C /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 405B15651DEF75660009871C /* AppViewController.swift */; }; - 406384891DE388C5004795A4 /* Tasks.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 406384881DE388C5004795A4 /* Tasks.storyboard */; }; 4065D39C1DD3B44200B73201 /* Day.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3181DD3B44200B73201 /* Day.swift */; }; 4065D39D1DD3B44200B73201 /* Report.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3191DD3B44200B73201 /* Report.swift */; }; 4065D39E1DD3B44200B73201 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D31A1DD3B44200B73201 /* Settings.swift */; }; @@ -405,14 +282,6 @@ 4065D3D91DD3B44200B73201 /* UserNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3691DD3B44200B73201 /* UserNotifications.swift */; }; 4065D3DA1DD3B44200B73201 /* SleepNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D36A1DD3B44200B73201 /* SleepNotifications.swift */; }; 4065D3DE1DD3B44200B73201 /* NewTaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3711DD3B44200B73201 /* NewTaskViewController.swift */; }; - 4065D3DF1DD3B44200B73201 /* SettingsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3731DD3B44200B73201 /* SettingsInteractor.swift */; }; - 4065D3E01DD3B44200B73201 /* SettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3741DD3B44200B73201 /* SettingsPresenter.swift */; }; - 4065D3E11DD3B44200B73201 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3751DD3B44200B73201 /* SettingsViewController.swift */; }; - 4065D3E21DD3B44200B73201 /* CalendarScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */; }; - 4065D3E31DD3B44200B73201 /* CellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3781DD3B44200B73201 /* CellProtocol.swift */; }; - 4065D3EE1DD3B44200B73201 /* TasksPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3831DD3B44200B73201 /* TasksPresenter.swift */; }; - 4065D3EF1DD3B44200B73201 /* TasksScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3841DD3B44200B73201 /* TasksScrollView.swift */; }; - 4065D3F01DD3B44200B73201 /* TasksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3851DD3B44200B73201 /* TasksViewController.swift */; }; 4065D3F21DD3B44200B73201 /* CloudKitRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D38A1DD3B44200B73201 /* CloudKitRepository.swift */; }; 4065D3F91DD3B44200B73201 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3941DD3B44200B73201 /* Repository.swift */; }; 4065D3FA1DD3B44200B73201 /* RepositoryInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3951DD3B44200B73201 /* RepositoryInteractor.swift */; }; @@ -438,66 +307,21 @@ 4065D4311DD457B000B73201 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3201DD3B44200B73201 /* DateExtension.swift */; }; 4065D4321DD457B500B73201 /* Conversions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4065D3261DD3B44200B73201 /* Conversions.swift */; }; 4073184F1DE9B1B40046F409 /* ComputerWakeUpInteractorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4073184E1DE9B1B40046F409 /* ComputerWakeUpInteractorTests.swift */; }; - 40E092411DE385E4001EF5DA /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 40E092401DE385E4001EF5DA /* Settings.storyboard */; }; 40FCE4251DF7646F00D4FD45 /* AppleScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE4221DF7646F00D4FD45 /* AppleScript.swift */; }; 40FCE4261DF7646F00D4FD45 /* ExtensionsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE4231DF7646F00D4FD45 /* ExtensionsInteractor.swift */; }; 40FCE4271DF7646F00D4FD45 /* SandboxedAppleScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE4241DF7646F00D4FD45 /* SandboxedAppleScript.swift */; }; - 40FCE4291DFC1CB400D4FD45 /* TaskSuggestionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE4281DFC1CB400D4FD45 /* TaskSuggestionTests.swift */; }; - 40FCE42C1DFC3F8700D4FD45 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE42B1DFC3F8700D4FD45 /* WelcomeViewController.swift */; }; - 40FCE42E1DFC576C00D4FD45 /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 40FCE42D1DFC576C00D4FD45 /* Welcome.storyboard */; }; 40FCE4301DFD7C8D00D4FD45 /* Animatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40FCE42F1DFD7C8D00D4FD45 /* Animatable.swift */; }; - 564E55F3202883DB00CE4C76 /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */; }; - 564E55F4202883DB00CE4C76 /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */; }; - 564E55F6202884DE00CE4C76 /* WorklogsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */; }; - 564E55F7202884DE00CE4C76 /* WorklogsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */; }; - 564E55F92028857300CE4C76 /* Worklogs.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 564E55F82028857300CE4C76 /* Worklogs.storyboard */; }; - 564E55FA2028857300CE4C76 /* Worklogs.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 564E55F82028857300CE4C76 /* Worklogs.storyboard */; }; 565E19F020A5E336003A5E2A /* RCSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 565E19EF20A5E336003A5E2A /* RCSync.swift */; }; 565E19F120A5E336003A5E2A /* RCSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 565E19EF20A5E336003A5E2A /* RCSync.swift */; }; 565E19F220A5E336003A5E2A /* RCSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 565E19EF20A5E336003A5E2A /* RCSync.swift */; }; - 566B9FA7217DE67800EAF324 /* TasksInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566B9FA6217DE67700EAF324 /* TasksInteractor.swift */; }; - 566B9FA8217DE67800EAF324 /* TasksInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566B9FA6217DE67700EAF324 /* TasksInteractor.swift */; }; 5683DC3E20ECDED30000A138 /* ModuleCalendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5683DC3D20ECDED30000A138 /* ModuleCalendar.swift */; }; 5683DC3F20ECDED30000A138 /* ModuleCalendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5683DC3D20ECDED30000A138 /* ModuleCalendar.swift */; }; - 5685C7671DE8721100CA545E /* CloudKitLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C7631DE8721100CA545E /* CloudKitLoginViewController.swift */; }; - 5685C7681DE8721100CA545E /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5685C7641DE8721100CA545E /* Login.storyboard */; }; - 5685C7691DE8721100CA545E /* LoginPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C7651DE8721100CA545E /* LoginPresenter.swift */; }; - 5685C76A1DE8721100CA545E /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C7661DE8721100CA545E /* LoginViewController.swift */; }; - 5685C76C1DE8724400CA545E /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5685C76B1DE8724400CA545E /* AccountViewController.swift */; }; - 569C4C582023193B0049FBF1 /* ShellCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C572023193B0049FBF1 /* ShellCell.swift */; }; - 569C4C592023193B0049FBF1 /* ShellCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C572023193B0049FBF1 /* ShellCell.swift */; }; - 569C4C5E202319630049FBF1 /* JitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C5D202319630049FBF1 /* JitCell.swift */; }; - 569C4C5F202319630049FBF1 /* JitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C5D202319630049FBF1 /* JitCell.swift */; }; - 569C4C64202319870049FBF1 /* GitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C63202319870049FBF1 /* GitCell.swift */; }; - 569C4C65202319870049FBF1 /* GitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C63202319870049FBF1 /* GitCell.swift */; }; - 569C4C67202319930049FBF1 /* GitPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C66202319930049FBF1 /* GitPresenter.swift */; }; - 569C4C68202319930049FBF1 /* GitPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C66202319930049FBF1 /* GitPresenter.swift */; }; - 569C4C6A202319A20049FBF1 /* BrowserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C69202319A20049FBF1 /* BrowserCell.swift */; }; - 569C4C6B202319A20049FBF1 /* BrowserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C69202319A20049FBF1 /* BrowserCell.swift */; }; - 569C4C6D202319CA0049FBF1 /* BrowserPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C6C202319CA0049FBF1 /* BrowserPresenter.swift */; }; - 569C4C6E202319CA0049FBF1 /* BrowserPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569C4C6C202319CA0049FBF1 /* BrowserPresenter.swift */; }; - 569C4C70202319DE0049FBF1 /* ShellCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C6F202319DE0049FBF1 /* ShellCell.xib */; }; - 569C4C71202319DE0049FBF1 /* ShellCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C6F202319DE0049FBF1 /* ShellCell.xib */; }; - 569C4C73202319EE0049FBF1 /* JitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C72202319EE0049FBF1 /* JitCell.xib */; }; - 569C4C74202319EE0049FBF1 /* JitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C72202319EE0049FBF1 /* JitCell.xib */; }; - 569C4C76202319FC0049FBF1 /* GitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C75202319FC0049FBF1 /* GitCell.xib */; }; - 569C4C77202319FC0049FBF1 /* GitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C75202319FC0049FBF1 /* GitCell.xib */; }; - 569C4C7920231A0B0049FBF1 /* BrowserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C7820231A0B0049FBF1 /* BrowserCell.xib */; }; - 569C4C7A20231A0B0049FBF1 /* BrowserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 569C4C7820231A0B0049FBF1 /* BrowserCell.xib */; }; 56ADBF6A21C3F625008350A6 /* GitUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADBF6921C3F625008350A6 /* GitUser.swift */; }; 56ADBF6B21C3F625008350A6 /* GitUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADBF6921C3F625008350A6 /* GitUser.swift */; }; 56ADBF6D21C3F94D008350A6 /* GitUserParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADBF6C21C3F94D008350A6 /* GitUserParser.swift */; }; 56ADBF6E21C3F94D008350A6 /* GitUserParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ADBF6C21C3F94D008350A6 /* GitUserParser.swift */; }; 56CD22BF1E72F89700F9CDB8 /* BuildScript.sh in Resources */ = {isa = PBXBuildFile; fileRef = 56CD22BE1E72F89700F9CDB8 /* BuildScript.sh */; }; 56D069E1216CABCB000D051D /* CreateMonthReportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D069E0216CABCB000D051D /* CreateMonthReportTests.swift */; }; - 56D90CF820876F1100F24442 /* WizardJiraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D90CF720876F1100F24442 /* WizardJiraView.swift */; }; - 56D90CF920876F1100F24442 /* WizardJiraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D90CF720876F1100F24442 /* WizardJiraView.swift */; }; - 56D90CFB20876F2B00F24442 /* WizardGitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D90CFA20876F2B00F24442 /* WizardGitView.swift */; }; - 56D90CFC20876F2B00F24442 /* WizardGitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D90CFA20876F2B00F24442 /* WizardGitView.swift */; }; - 56D90CFE20876F3C00F24442 /* WizardGitView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56D90CFD20876F3C00F24442 /* WizardGitView.xib */; }; - 56D90CFF20876F3C00F24442 /* WizardGitView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56D90CFD20876F3C00F24442 /* WizardGitView.xib */; }; - 56D90D0120876F4900F24442 /* WizardJiraView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56D90D0020876F4900F24442 /* WizardJiraView.xib */; }; - 56D90D0220876F4900F24442 /* WizardJiraView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56D90D0020876F4900F24442 /* WizardJiraView.xib */; }; 6D3702602BA83E9A002260D0 /* SwiftKeychainWrapper in Frameworks */ = {isa = PBXBuildFile; productRef = 6D37025F2BA83E9A002260D0 /* SwiftKeychainWrapper */; }; 6D3702622BA83EB9002260D0 /* RCLog in Frameworks */ = {isa = PBXBuildFile; productRef = 6D3702612BA83EB9002260D0 /* RCLog */; }; 6D3702642BA83EBF002260D0 /* RCPreferences in Frameworks */ = {isa = PBXBuildFile; productRef = 6D3702632BA83EBF002260D0 /* RCPreferences */; }; @@ -505,6 +329,226 @@ 6D5BC4C82C1B5BE6002DA29B /* CreateDayReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC4C62C1B5B70002DA29B /* CreateDayReport.swift */; }; 6D5BC4C92C1B5BE6002DA29B /* CreateDayReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC4C62C1B5B70002DA29B /* CreateDayReport.swift */; }; 6D5BC4CB2C1B5BF7002DA29B /* CreateDayReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC4C62C1B5B70002DA29B /* CreateDayReport.swift */; }; + 6D5BC5C92C1C1970002DA29B /* WizardGitView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5502C1C1970002DA29B /* WizardGitView.xib */; }; + 6D5BC5CA2C1C1970002DA29B /* JitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC57D2C1C1970002DA29B /* JitCell.xib */; }; + 6D5BC5CB2C1C1970002DA29B /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC53A2C1C1970002DA29B /* Login.storyboard */; }; + 6D5BC5CC2C1C1970002DA29B /* Placeholder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5552C1C1970002DA29B /* Placeholder.storyboard */; }; + 6D5BC5CD2C1C1970002DA29B /* JiraTempoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC58F2C1C1970002DA29B /* JiraTempoCell.xib */; }; + 6D5BC5CE2C1C1970002DA29B /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5492C1C1970002DA29B /* Welcome.storyboard */; }; + 6D5BC5CF2C1C1970002DA29B /* Reports.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5682C1C1970002DA29B /* Reports.storyboard */; }; + 6D5BC5D02C1C1970002DA29B /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC55A2C1C1970002DA29B /* Projects.storyboard */; }; + 6D5BC5D12C1C1970002DA29B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A92C1C1970002DA29B /* TaskCell.xib */; }; + 6D5BC5D22C1C1970002DA29B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5452C1C1970002DA29B /* Main.storyboard */; }; + 6D5BC5D32C1C1970002DA29B /* TrackingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC59A2C1C1970002DA29B /* TrackingView.xib */; }; + 6D5BC5D42C1C1970002DA29B /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5612C1C1970002DA29B /* CopyReportCell.xib */; }; + 6D5BC5D52C1C1970002DA29B /* ReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5642C1C1970002DA29B /* ReportCell.xib */; }; + 6D5BC5D62C1C1970002DA29B /* CocoaHookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5872C1C1970002DA29B /* CocoaHookupCell.xib */; }; + 6D5BC5D72C1C1970002DA29B /* Tasks.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B72C1C1970002DA29B /* Tasks.storyboard */; }; + 6D5BC5D82C1C1970002DA29B /* CalendarCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5732C1C1970002DA29B /* CalendarCell.xib */; }; + 6D5BC5D92C1C1970002DA29B /* InputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5832C1C1970002DA29B /* InputsScrollView.xib */; }; + 6D5BC5DA2C1C1970002DA29B /* ClosedDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A62C1C1970002DA29B /* ClosedDayCell.xib */; }; + 6D5BC5DB2C1C1970002DA29B /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC59D2C1C1970002DA29B /* Settings.storyboard */; }; + 6D5BC5DC2C1C1970002DA29B /* BrowserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC56E2C1C1970002DA29B /* BrowserCell.xib */; }; + 6D5BC5DD2C1C1970002DA29B /* ShellCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5802C1C1970002DA29B /* ShellCell.xib */; }; + 6D5BC5DE2C1C1970002DA29B /* Worklogs.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C52C1C1970002DA29B /* Worklogs.storyboard */; }; + 6D5BC5DF2C1C1970002DA29B /* WizardJiraView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5522C1C1970002DA29B /* WizardJiraView.xib */; }; + 6D5BC5E02C1C1970002DA29B /* CloseDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A32C1C1970002DA29B /* CloseDayCell.xib */; }; + 6D5BC5E12C1C1970002DA29B /* StoreView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5972C1C1970002DA29B /* StoreView.xib */; }; + 6D5BC5E22C1C1970002DA29B /* HookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC58B2C1C1970002DA29B /* HookupCell.xib */; }; + 6D5BC5E32C1C1970002DA29B /* WizardCalendarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC54E2C1C1970002DA29B /* WizardCalendarView.xib */; }; + 6D5BC5E42C1C1970002DA29B /* OutputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5932C1C1970002DA29B /* OutputsScrollView.xib */; }; + 6D5BC5E52C1C1970002DA29B /* Calendar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC53E2C1C1970002DA29B /* Calendar.storyboard */; }; + 6D5BC5E62C1C1970002DA29B /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B12C1C1970002DA29B /* ReportsHeaderView.xib */; }; + 6D5BC5E72C1C1970002DA29B /* WizardAppleScriptView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC54C2C1C1970002DA29B /* WizardAppleScriptView.xib */; }; + 6D5BC5E82C1C1970002DA29B /* GitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5762C1C1970002DA29B /* GitCell.xib */; }; + 6D5BC5E92C1C1970002DA29B /* JirassicCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC57A2C1C1970002DA29B /* JirassicCell.xib */; }; + 6D5BC5EA2C1C1970002DA29B /* JitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC57C2C1C1970002DA29B /* JitCell.swift */; }; + 6D5BC5EB2C1C1970002DA29B /* TasksPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BA2C1C1970002DA29B /* TasksPresenter.swift */; }; + 6D5BC5EC2C1C1970002DA29B /* ClosedDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A52C1C1970002DA29B /* ClosedDayCell.swift */; }; + 6D5BC5ED2C1C1970002DA29B /* PlaceholderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5562C1C1970002DA29B /* PlaceholderViewController.swift */; }; + 6D5BC5EE2C1C1970002DA29B /* ReportsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56A2C1C1970002DA29B /* ReportsPresenter.swift */; }; + 6D5BC5EF2C1C1970002DA29B /* WizardCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54D2C1C1970002DA29B /* WizardCalendarView.swift */; }; + 6D5BC5F02C1C1970002DA29B /* CocoaHookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5882C1C1970002DA29B /* CocoaHookupPresenter.swift */; }; + 6D5BC5F12C1C1970002DA29B /* OutputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5922C1C1970002DA29B /* OutputsScrollView.swift */; }; + 6D5BC5F22C1C1970002DA29B /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5382C1C1970002DA29B /* AccountViewController.swift */; }; + 6D5BC5F32C1C1970002DA29B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A82C1C1970002DA29B /* TaskCell.swift */; }; + 6D5BC5F42C1C1970002DA29B /* SettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC59F2C1C1970002DA29B /* SettingsPresenter.swift */; }; + 6D5BC5F52C1C1970002DA29B /* ReportCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5652C1C1970002DA29B /* ReportCellPresenter.swift */; }; + 6D5BC5F62C1C1970002DA29B /* TasksInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B92C1C1970002DA29B /* TasksInteractor.swift */; }; + 6D5BC5F72C1C1970002DA29B /* ProjectsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55B2C1C1970002DA29B /* ProjectsInteractor.swift */; }; + 6D5BC5F82C1C1970002DA29B /* TaskSuggestionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C32C1C1970002DA29B /* TaskSuggestionViewController.swift */; }; + 6D5BC5F92C1C1970002DA29B /* ReportsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56B2C1C1970002DA29B /* ReportsViewController.swift */; }; + 6D5BC5FA2C1C1970002DA29B /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B42C1C1970002DA29B /* DataSource.swift */; }; + 6D5BC5FB2C1C1970002DA29B /* TimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BE2C1C1970002DA29B /* TimeBox.swift */; }; + 6D5BC5FC2C1C1970002DA29B /* HookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC58A2C1C1970002DA29B /* HookupCell.swift */; }; + 6D5BC5FD2C1C1970002DA29B /* BrowserPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56F2C1C1970002DA29B /* BrowserPresenter.swift */; }; + 6D5BC5FE2C1C1970002DA29B /* JiraTempoPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5902C1C1970002DA29B /* JiraTempoPresenter.swift */; }; + 6D5BC5FF2C1C1970002DA29B /* EditableTimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B52C1C1970002DA29B /* EditableTimeBox.swift */; }; + 6D5BC6002C1C1970002DA29B /* CalendarDayCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC53F2C1C1970002DA29B /* CalendarDayCellView.swift */; }; + 6D5BC6012C1C1970002DA29B /* CopyReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5602C1C1970002DA29B /* CopyReportCell.swift */; }; + 6D5BC6022C1C1970002DA29B /* TrackingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5992C1C1970002DA29B /* TrackingView.swift */; }; + 6D5BC6032C1C1970002DA29B /* SettingsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC59E2C1C1970002DA29B /* SettingsInteractor.swift */; }; + 6D5BC6042C1C1970002DA29B /* BrowserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56D2C1C1970002DA29B /* BrowserCell.swift */; }; + 6D5BC6052C1C1970002DA29B /* InputsTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5842C1C1970002DA29B /* InputsTableViewDataSource.swift */; }; + 6D5BC6062C1C1970002DA29B /* StoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5962C1C1970002DA29B /* StoreView.swift */; }; + 6D5BC6072C1C1970002DA29B /* WizardGitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54F2C1C1970002DA29B /* WizardGitView.swift */; }; + 6D5BC6082C1C1970002DA29B /* WizardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5532C1C1970002DA29B /* WizardViewController.swift */; }; + 6D5BC6092C1C1970002DA29B /* MainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5462C1C1970002DA29B /* MainPresenter.swift */; }; + 6D5BC60A2C1C1970002DA29B /* InputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5822C1C1970002DA29B /* InputsScrollView.swift */; }; + 6D5BC60B2C1C1970002DA29B /* CalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5422C1C1970002DA29B /* CalendarViewController.swift */; }; + 6D5BC60C2C1C1970002DA29B /* CellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AD2C1C1970002DA29B /* CellProtocol.swift */; }; + 6D5BC60D2C1C1970002DA29B /* ReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5632C1C1970002DA29B /* ReportCell.swift */; }; + 6D5BC60E2C1C1970002DA29B /* HookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC58C2C1C1970002DA29B /* HookupPresenter.swift */; }; + 6D5BC60F2C1C1970002DA29B /* TaskCell_.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AA2C1C1970002DA29B /* TaskCell_.swift */; }; + 6D5BC6102C1C1970002DA29B /* ReportsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5692C1C1970002DA29B /* ReportsDataSource.swift */; }; + 6D5BC6112C1C1970002DA29B /* CalendarInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5402C1C1970002DA29B /* CalendarInteractor.swift */; }; + 6D5BC6122C1C1970002DA29B /* ProjectsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55D2C1C1970002DA29B /* ProjectsPresenter.swift */; }; + 6D5BC6132C1C1970002DA29B /* CloudKitLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5392C1C1970002DA29B /* CloudKitLoginViewController.swift */; }; + 6D5BC6142C1C1970002DA29B /* ProjectsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55E2C1C1970002DA29B /* ProjectsViewController.swift */; }; + 6D5BC6152C1C1970002DA29B /* NewTaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B62C1C1970002DA29B /* NewTaskViewController.swift */; }; + 6D5BC6162C1C1970002DA29B /* TaskSuggestionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C12C1C1970002DA29B /* TaskSuggestionPresenter.swift */; }; + 6D5BC6172C1C1970002DA29B /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5412C1C1970002DA29B /* CalendarPresenter.swift */; }; + 6D5BC6182C1C1970002DA29B /* GitPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5772C1C1970002DA29B /* GitPresenter.swift */; }; + 6D5BC6192C1C1970002DA29B /* OutputTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5942C1C1970002DA29B /* OutputTableViewDataSource.swift */; }; + 6D5BC61A2C1C1970002DA29B /* TaskSuggestionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C22C1C1970002DA29B /* TaskSuggestionTests.swift */; }; + 6D5BC61B2C1C1970002DA29B /* ProjectDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5592C1C1970002DA29B /* ProjectDetailsViewController.swift */; }; + 6D5BC61C2C1C1970002DA29B /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A02C1C1970002DA29B /* SettingsViewController.swift */; }; + 6D5BC61D2C1C1970002DA29B /* JiraTempoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC58E2C1C1970002DA29B /* JiraTempoCell.swift */; }; + 6D5BC61E2C1C1970002DA29B /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5582C1C1970002DA29B /* ProjectDetailsPresenter.swift */; }; + 6D5BC61F2C1C1970002DA29B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AB2C1C1970002DA29B /* TaskCellPresenter.swift */; }; + 6D5BC6202C1C1970002DA29B /* CloseDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A22C1C1970002DA29B /* CloseDayCell.swift */; }; + 6D5BC6212C1C1970002DA29B /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC59C2C1C1970002DA29B /* Saveable.swift */; }; + 6D5BC6222C1C1970002DA29B /* ShellCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC57F2C1C1970002DA29B /* ShellCell.swift */; }; + 6D5BC6232C1C1970002DA29B /* CalendarAppPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5712C1C1970002DA29B /* CalendarAppPresenter.swift */; }; + 6D5BC6242C1C1970002DA29B /* JirassicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5792C1C1970002DA29B /* JirassicCell.swift */; }; + 6D5BC6252C1C1970002DA29B /* TasksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B82C1C1970002DA29B /* TasksDataSource.swift */; }; + 6D5BC6262C1C1970002DA29B /* WizardJiraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5512C1C1970002DA29B /* WizardJiraView.swift */; }; + 6D5BC6272C1C1970002DA29B /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC53C2C1C1970002DA29B /* LoginViewController.swift */; }; + 6D5BC6282C1C1970002DA29B /* MonthReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AF2C1C1970002DA29B /* MonthReportsHeaderView.swift */; }; + 6D5BC6292C1C1970002DA29B /* CalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5722C1C1970002DA29B /* CalendarCell.swift */; }; + 6D5BC62A2C1C1970002DA29B /* LoginPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC53B2C1C1970002DA29B /* LoginPresenter.swift */; }; + 6D5BC62B2C1C1970002DA29B /* TimeBoxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BF2C1C1970002DA29B /* TimeBoxViewController.swift */; }; + 6D5BC62C2C1C1970002DA29B /* TasksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BC2C1C1970002DA29B /* TasksView.swift */; }; + 6D5BC62D2C1C1970002DA29B /* GitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5752C1C1970002DA29B /* GitCell.swift */; }; + 6D5BC62E2C1C1970002DA29B /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5472C1C1970002DA29B /* MainViewController.swift */; }; + 6D5BC62F2C1C1970002DA29B /* TasksScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BB2C1C1970002DA29B /* TasksScrollView.swift */; }; + 6D5BC6302C1C1970002DA29B /* CocoaHookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5862C1C1970002DA29B /* CocoaHookupCell.swift */; }; + 6D5BC6312C1C1970002DA29B /* WizardAppleScriptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54B2C1C1970002DA29B /* WizardAppleScriptView.swift */; }; + 6D5BC6322C1C1970002DA29B /* ProjectsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55C2C1C1970002DA29B /* ProjectsListViewController.swift */; }; + 6D5BC6332C1C1970002DA29B /* TasksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BD2C1C1970002DA29B /* TasksViewController.swift */; }; + 6D5BC6342C1C1970002DA29B /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54A2C1C1970002DA29B /* WelcomeViewController.swift */; }; + 6D5BC6352C1C1970002DA29B /* WorklogsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C62C1C1970002DA29B /* WorklogsPresenter.swift */; }; + 6D5BC6362C1C1970002DA29B /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C72C1C1970002DA29B /* WorklogsViewController.swift */; }; + 6D5BC6372C1C1970002DA29B /* WizardGitView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5502C1C1970002DA29B /* WizardGitView.xib */; }; + 6D5BC6382C1C1970002DA29B /* JitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC57D2C1C1970002DA29B /* JitCell.xib */; }; + 6D5BC6392C1C1970002DA29B /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC53A2C1C1970002DA29B /* Login.storyboard */; }; + 6D5BC63A2C1C1970002DA29B /* Placeholder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5552C1C1970002DA29B /* Placeholder.storyboard */; }; + 6D5BC63B2C1C1970002DA29B /* JiraTempoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC58F2C1C1970002DA29B /* JiraTempoCell.xib */; }; + 6D5BC63C2C1C1970002DA29B /* Welcome.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5492C1C1970002DA29B /* Welcome.storyboard */; }; + 6D5BC63D2C1C1970002DA29B /* Reports.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5682C1C1970002DA29B /* Reports.storyboard */; }; + 6D5BC63E2C1C1970002DA29B /* Projects.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC55A2C1C1970002DA29B /* Projects.storyboard */; }; + 6D5BC63F2C1C1970002DA29B /* TaskCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A92C1C1970002DA29B /* TaskCell.xib */; }; + 6D5BC6402C1C1970002DA29B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5452C1C1970002DA29B /* Main.storyboard */; }; + 6D5BC6412C1C1970002DA29B /* TrackingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC59A2C1C1970002DA29B /* TrackingView.xib */; }; + 6D5BC6422C1C1970002DA29B /* CopyReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5612C1C1970002DA29B /* CopyReportCell.xib */; }; + 6D5BC6432C1C1970002DA29B /* ReportCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5642C1C1970002DA29B /* ReportCell.xib */; }; + 6D5BC6442C1C1970002DA29B /* CocoaHookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5872C1C1970002DA29B /* CocoaHookupCell.xib */; }; + 6D5BC6452C1C1970002DA29B /* Tasks.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B72C1C1970002DA29B /* Tasks.storyboard */; }; + 6D5BC6462C1C1970002DA29B /* CalendarCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5732C1C1970002DA29B /* CalendarCell.xib */; }; + 6D5BC6472C1C1970002DA29B /* InputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5832C1C1970002DA29B /* InputsScrollView.xib */; }; + 6D5BC6482C1C1970002DA29B /* ClosedDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A62C1C1970002DA29B /* ClosedDayCell.xib */; }; + 6D5BC6492C1C1970002DA29B /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC59D2C1C1970002DA29B /* Settings.storyboard */; }; + 6D5BC64A2C1C1970002DA29B /* BrowserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC56E2C1C1970002DA29B /* BrowserCell.xib */; }; + 6D5BC64B2C1C1970002DA29B /* ShellCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5802C1C1970002DA29B /* ShellCell.xib */; }; + 6D5BC64C2C1C1970002DA29B /* Worklogs.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C52C1C1970002DA29B /* Worklogs.storyboard */; }; + 6D5BC64D2C1C1970002DA29B /* WizardJiraView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5522C1C1970002DA29B /* WizardJiraView.xib */; }; + 6D5BC64E2C1C1970002DA29B /* CloseDayCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A32C1C1970002DA29B /* CloseDayCell.xib */; }; + 6D5BC64F2C1C1970002DA29B /* StoreView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5972C1C1970002DA29B /* StoreView.xib */; }; + 6D5BC6502C1C1970002DA29B /* HookupCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC58B2C1C1970002DA29B /* HookupCell.xib */; }; + 6D5BC6512C1C1970002DA29B /* WizardCalendarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC54E2C1C1970002DA29B /* WizardCalendarView.xib */; }; + 6D5BC6522C1C1970002DA29B /* OutputsScrollView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5932C1C1970002DA29B /* OutputsScrollView.xib */; }; + 6D5BC6532C1C1970002DA29B /* Calendar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC53E2C1C1970002DA29B /* Calendar.storyboard */; }; + 6D5BC6542C1C1970002DA29B /* ReportsHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B12C1C1970002DA29B /* ReportsHeaderView.xib */; }; + 6D5BC6552C1C1970002DA29B /* WizardAppleScriptView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC54C2C1C1970002DA29B /* WizardAppleScriptView.xib */; }; + 6D5BC6562C1C1970002DA29B /* GitCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC5762C1C1970002DA29B /* GitCell.xib */; }; + 6D5BC6572C1C1970002DA29B /* JirassicCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D5BC57A2C1C1970002DA29B /* JirassicCell.xib */; }; + 6D5BC6582C1C1970002DA29B /* JitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC57C2C1C1970002DA29B /* JitCell.swift */; }; + 6D5BC6592C1C1970002DA29B /* TasksPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BA2C1C1970002DA29B /* TasksPresenter.swift */; }; + 6D5BC65A2C1C1970002DA29B /* ClosedDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A52C1C1970002DA29B /* ClosedDayCell.swift */; }; + 6D5BC65B2C1C1970002DA29B /* PlaceholderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5562C1C1970002DA29B /* PlaceholderViewController.swift */; }; + 6D5BC65C2C1C1970002DA29B /* ReportsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56A2C1C1970002DA29B /* ReportsPresenter.swift */; }; + 6D5BC65D2C1C1970002DA29B /* WizardCalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54D2C1C1970002DA29B /* WizardCalendarView.swift */; }; + 6D5BC65E2C1C1970002DA29B /* CocoaHookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5882C1C1970002DA29B /* CocoaHookupPresenter.swift */; }; + 6D5BC65F2C1C1970002DA29B /* OutputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5922C1C1970002DA29B /* OutputsScrollView.swift */; }; + 6D5BC6602C1C1970002DA29B /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5382C1C1970002DA29B /* AccountViewController.swift */; }; + 6D5BC6612C1C1970002DA29B /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A82C1C1970002DA29B /* TaskCell.swift */; }; + 6D5BC6622C1C1970002DA29B /* SettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC59F2C1C1970002DA29B /* SettingsPresenter.swift */; }; + 6D5BC6632C1C1970002DA29B /* ReportCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5652C1C1970002DA29B /* ReportCellPresenter.swift */; }; + 6D5BC6642C1C1970002DA29B /* TasksInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B92C1C1970002DA29B /* TasksInteractor.swift */; }; + 6D5BC6652C1C1970002DA29B /* ProjectsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55B2C1C1970002DA29B /* ProjectsInteractor.swift */; }; + 6D5BC6662C1C1970002DA29B /* TaskSuggestionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C32C1C1970002DA29B /* TaskSuggestionViewController.swift */; }; + 6D5BC6672C1C1970002DA29B /* ReportsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56B2C1C1970002DA29B /* ReportsViewController.swift */; }; + 6D5BC6682C1C1970002DA29B /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B42C1C1970002DA29B /* DataSource.swift */; }; + 6D5BC6692C1C1970002DA29B /* TimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BE2C1C1970002DA29B /* TimeBox.swift */; }; + 6D5BC66A2C1C1970002DA29B /* HookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC58A2C1C1970002DA29B /* HookupCell.swift */; }; + 6D5BC66B2C1C1970002DA29B /* BrowserPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56F2C1C1970002DA29B /* BrowserPresenter.swift */; }; + 6D5BC66C2C1C1970002DA29B /* JiraTempoPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5902C1C1970002DA29B /* JiraTempoPresenter.swift */; }; + 6D5BC66D2C1C1970002DA29B /* EditableTimeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B52C1C1970002DA29B /* EditableTimeBox.swift */; }; + 6D5BC66E2C1C1970002DA29B /* CalendarDayCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC53F2C1C1970002DA29B /* CalendarDayCellView.swift */; }; + 6D5BC66F2C1C1970002DA29B /* CopyReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5602C1C1970002DA29B /* CopyReportCell.swift */; }; + 6D5BC6702C1C1970002DA29B /* TrackingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5992C1C1970002DA29B /* TrackingView.swift */; }; + 6D5BC6712C1C1970002DA29B /* SettingsInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC59E2C1C1970002DA29B /* SettingsInteractor.swift */; }; + 6D5BC6722C1C1970002DA29B /* BrowserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC56D2C1C1970002DA29B /* BrowserCell.swift */; }; + 6D5BC6732C1C1970002DA29B /* InputsTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5842C1C1970002DA29B /* InputsTableViewDataSource.swift */; }; + 6D5BC6742C1C1970002DA29B /* StoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5962C1C1970002DA29B /* StoreView.swift */; }; + 6D5BC6752C1C1970002DA29B /* WizardGitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54F2C1C1970002DA29B /* WizardGitView.swift */; }; + 6D5BC6762C1C1970002DA29B /* WizardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5532C1C1970002DA29B /* WizardViewController.swift */; }; + 6D5BC6772C1C1970002DA29B /* MainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5462C1C1970002DA29B /* MainPresenter.swift */; }; + 6D5BC6782C1C1970002DA29B /* InputsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5822C1C1970002DA29B /* InputsScrollView.swift */; }; + 6D5BC6792C1C1970002DA29B /* CalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5422C1C1970002DA29B /* CalendarViewController.swift */; }; + 6D5BC67A2C1C1970002DA29B /* CellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AD2C1C1970002DA29B /* CellProtocol.swift */; }; + 6D5BC67B2C1C1970002DA29B /* ReportCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5632C1C1970002DA29B /* ReportCell.swift */; }; + 6D5BC67C2C1C1970002DA29B /* HookupPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC58C2C1C1970002DA29B /* HookupPresenter.swift */; }; + 6D5BC67D2C1C1970002DA29B /* TaskCell_.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AA2C1C1970002DA29B /* TaskCell_.swift */; }; + 6D5BC67E2C1C1970002DA29B /* ReportsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5692C1C1970002DA29B /* ReportsDataSource.swift */; }; + 6D5BC67F2C1C1970002DA29B /* CalendarInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5402C1C1970002DA29B /* CalendarInteractor.swift */; }; + 6D5BC6802C1C1970002DA29B /* ProjectsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55D2C1C1970002DA29B /* ProjectsPresenter.swift */; }; + 6D5BC6812C1C1970002DA29B /* CloudKitLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5392C1C1970002DA29B /* CloudKitLoginViewController.swift */; }; + 6D5BC6822C1C1970002DA29B /* ProjectsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55E2C1C1970002DA29B /* ProjectsViewController.swift */; }; + 6D5BC6832C1C1970002DA29B /* NewTaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B62C1C1970002DA29B /* NewTaskViewController.swift */; }; + 6D5BC6842C1C1970002DA29B /* TaskSuggestionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C12C1C1970002DA29B /* TaskSuggestionPresenter.swift */; }; + 6D5BC6852C1C1970002DA29B /* CalendarPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5412C1C1970002DA29B /* CalendarPresenter.swift */; }; + 6D5BC6862C1C1970002DA29B /* GitPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5772C1C1970002DA29B /* GitPresenter.swift */; }; + 6D5BC6872C1C1970002DA29B /* OutputTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5942C1C1970002DA29B /* OutputTableViewDataSource.swift */; }; + 6D5BC6882C1C1970002DA29B /* TaskSuggestionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C22C1C1970002DA29B /* TaskSuggestionTests.swift */; }; + 6D5BC6892C1C1970002DA29B /* ProjectDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5592C1C1970002DA29B /* ProjectDetailsViewController.swift */; }; + 6D5BC68A2C1C1970002DA29B /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A02C1C1970002DA29B /* SettingsViewController.swift */; }; + 6D5BC68B2C1C1970002DA29B /* JiraTempoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC58E2C1C1970002DA29B /* JiraTempoCell.swift */; }; + 6D5BC68C2C1C1970002DA29B /* ProjectDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5582C1C1970002DA29B /* ProjectDetailsPresenter.swift */; }; + 6D5BC68D2C1C1970002DA29B /* TaskCellPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AB2C1C1970002DA29B /* TaskCellPresenter.swift */; }; + 6D5BC68E2C1C1970002DA29B /* CloseDayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5A22C1C1970002DA29B /* CloseDayCell.swift */; }; + 6D5BC68F2C1C1970002DA29B /* Saveable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC59C2C1C1970002DA29B /* Saveable.swift */; }; + 6D5BC6902C1C1970002DA29B /* ShellCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC57F2C1C1970002DA29B /* ShellCell.swift */; }; + 6D5BC6912C1C1970002DA29B /* CalendarAppPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5712C1C1970002DA29B /* CalendarAppPresenter.swift */; }; + 6D5BC6922C1C1970002DA29B /* JirassicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5792C1C1970002DA29B /* JirassicCell.swift */; }; + 6D5BC6932C1C1970002DA29B /* TasksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5B82C1C1970002DA29B /* TasksDataSource.swift */; }; + 6D5BC6942C1C1970002DA29B /* WizardJiraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5512C1C1970002DA29B /* WizardJiraView.swift */; }; + 6D5BC6952C1C1970002DA29B /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC53C2C1C1970002DA29B /* LoginViewController.swift */; }; + 6D5BC6962C1C1970002DA29B /* MonthReportsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5AF2C1C1970002DA29B /* MonthReportsHeaderView.swift */; }; + 6D5BC6972C1C1970002DA29B /* CalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5722C1C1970002DA29B /* CalendarCell.swift */; }; + 6D5BC6982C1C1970002DA29B /* LoginPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC53B2C1C1970002DA29B /* LoginPresenter.swift */; }; + 6D5BC6992C1C1970002DA29B /* TimeBoxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BF2C1C1970002DA29B /* TimeBoxViewController.swift */; }; + 6D5BC69A2C1C1970002DA29B /* TasksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BC2C1C1970002DA29B /* TasksView.swift */; }; + 6D5BC69B2C1C1970002DA29B /* GitCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5752C1C1970002DA29B /* GitCell.swift */; }; + 6D5BC69C2C1C1970002DA29B /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5472C1C1970002DA29B /* MainViewController.swift */; }; + 6D5BC69D2C1C1970002DA29B /* TasksScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BB2C1C1970002DA29B /* TasksScrollView.swift */; }; + 6D5BC69E2C1C1970002DA29B /* CocoaHookupCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5862C1C1970002DA29B /* CocoaHookupCell.swift */; }; + 6D5BC69F2C1C1970002DA29B /* WizardAppleScriptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54B2C1C1970002DA29B /* WizardAppleScriptView.swift */; }; + 6D5BC6A02C1C1970002DA29B /* ProjectsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC55C2C1C1970002DA29B /* ProjectsListViewController.swift */; }; + 6D5BC6A12C1C1970002DA29B /* TasksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5BD2C1C1970002DA29B /* TasksViewController.swift */; }; + 6D5BC6A22C1C1970002DA29B /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC54A2C1C1970002DA29B /* WelcomeViewController.swift */; }; + 6D5BC6A32C1C1970002DA29B /* WorklogsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C62C1C1970002DA29B /* WorklogsPresenter.swift */; }; + 6D5BC6A42C1C1970002DA29B /* WorklogsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D5BC5C72C1C1970002DA29B /* WorklogsViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -585,29 +629,13 @@ 2818848021A4A2F800B33B9C /* Jirassic.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Jirassic.xcdatamodel; sourceTree = ""; }; 2823C9341E4F69970055D036 /* Versioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Versioning.swift; sourceTree = ""; }; 28279AD421C6245700376304 /* GitUsersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitUsersViewController.swift; sourceTree = ""; }; - 28279E9B1E8300E200EAF9FC /* PlaceholderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaceholderViewController.swift; sourceTree = ""; }; 284192D62018841700E64A9A /* JProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JProject.swift; sourceTree = ""; }; 284192D92018855B00E64A9A /* JiraRepository+Projects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JiraRepository+Projects.swift"; sourceTree = ""; }; 284192DC2018A8B200E64A9A /* ModuleJiraTempo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleJiraTempo.swift; sourceTree = ""; }; 2845B1342066C6E6006EFB3B /* AppleScriptProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptProtocol.swift; sourceTree = ""; }; 2845B138206703C5006EFB3B /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; }; 2845B13E2068351F006EFB3B /* TableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; - 2845B144206AE3A8006EFB3B /* ReportCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportCell.swift; sourceTree = ""; }; - 2845B145206AE3A8006EFB3B /* ReportCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReportCell.xib; sourceTree = ""; }; - 2845B146206AE3A8006EFB3B /* ReportCellPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportCellPresenter.swift; sourceTree = ""; }; - 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportsDataSource.swift; sourceTree = ""; }; - 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportsHeaderView.swift; sourceTree = ""; }; - 2845B154206AE467006EFB3B /* TaskCellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCellTests.swift; sourceTree = ""; }; - 2845B155206AE467006EFB3B /* TaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell.swift; sourceTree = ""; }; - 2845B156206AE467006EFB3B /* TaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TaskCell.xib; sourceTree = ""; }; - 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCellPresenter.swift; sourceTree = ""; }; - 2845B159206AE467006EFB3B /* TasksHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksHeaderView.swift; sourceTree = ""; }; - 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TasksHeaderView.xib; sourceTree = ""; }; - 2845B15C206AE467006EFB3B /* NonTaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonTaskCell.swift; sourceTree = ""; }; - 2845B15D206AE467006EFB3B /* NonTaskCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NonTaskCell.xib; sourceTree = ""; }; 285465A1216C84E10052CB6A /* CreateMonthReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateMonthReport.swift; sourceTree = ""; }; - 285465A6217858790052CB6A /* StoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreView.swift; sourceTree = ""; }; - 285465AC217858AF0052CB6A /* StoreView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StoreView.xib; sourceTree = ""; }; 28577EA71E8ADC53002B07FD /* SqliteRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SqliteRepository.swift; sourceTree = ""; }; 28577EA81E8ADC53002B07FD /* SSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SSettings.swift; sourceTree = ""; }; 28577EA91E8ADC53002B07FD /* STask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = STask.swift; sourceTree = ""; }; @@ -618,44 +646,24 @@ 28577EBB1E8AFB1B002B07FD /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; 28667B5C1FCB7017007B98E3 /* ModuleHookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleHookup.swift; sourceTree = ""; }; 286BC00321909F85004D4CDD /* CloseDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDay.swift; sourceTree = ""; }; - 287195142022E1E2001C237E /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; - 287195172022E8BC001C237E /* OutputTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputTableViewDataSource.swift; sourceTree = ""; }; - 2871951A2022ECF7001C237E /* OutputsScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputsScrollView.swift; sourceTree = ""; }; - 2871951D2022FD14001C237E /* InputsScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputsScrollView.swift; sourceTree = ""; }; - 287195202022FD87001C237E /* InputsTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputsTableViewDataSource.swift; sourceTree = ""; }; - 2871952720241A6C001C237E /* TrackingView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrackingView.xib; sourceTree = ""; }; - 2871952A20241B25001C237E /* TrackingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingView.swift; sourceTree = ""; }; 287195352027CAD9001C237E /* TimeInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInteractor.swift; sourceTree = ""; }; - 287B358620FBAFC40022F43E /* WizardCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardCalendarView.swift; sourceTree = ""; }; - 287B358920FBAFD60022F43E /* WizardCalendarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardCalendarView.xib; sourceTree = ""; }; - 288BB67D2085252900CF720A /* WizardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardViewController.swift; sourceTree = ""; }; - 288BB6802087152B00CF720A /* WizardAppleScriptView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardAppleScriptView.xib; sourceTree = ""; }; - 288BB6832087157F00CF720A /* WizardAppleScriptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardAppleScriptView.swift; sourceTree = ""; }; 288BB6862087169F00CF720A /* ViewXib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewXib.swift; sourceTree = ""; }; 2892B2971F094A170085BAC2 /* JiraRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JiraRepository.swift; sourceTree = ""; }; 2892B29A1F094A760085BAC2 /* JiraRepository+Reports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JiraRepository+Reports.swift"; sourceTree = ""; }; 2892B29D1F094B0D0085BAC2 /* JReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JReport.swift; sourceTree = ""; }; 2892B2A61F094C800085BAC2 /* JWorkAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWorkAttribute.swift; sourceTree = ""; }; - 2892E80A208D9B42004E5298 /* InputsScrollView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InputsScrollView.xib; sourceTree = ""; }; - 2892E80D208D9DD0004E5298 /* OutputsScrollView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OutputsScrollView.xib; sourceTree = ""; }; 2892E810208E6275004E5298 /* LocalPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalPreferences.swift; sourceTree = ""; }; - 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthReportsHeaderView.swift; sourceTree = ""; }; 2898D3AA2184D1DB00CF5AD4 /* TimeBoxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeBoxViewController.swift; sourceTree = ""; }; 2898D3AD2184ED3000CF5AD4 /* Components.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Components.storyboard; sourceTree = ""; }; 2898D3B02185959300CF5AD4 /* EditableTimeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTimeBox.swift; sourceTree = ""; }; 28A283932037874600DDCB63 /* ModuleGitLogs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleGitLogs.swift; sourceTree = ""; }; - 28A283962037975600DDCB63 /* Saveable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Saveable.swift; sourceTree = ""; }; 28A2839920382BBB00DDCB63 /* GitCommit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitCommit.swift; sourceTree = ""; }; 28A2839E20385BE000DDCB63 /* GitCommitsParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitCommitsParser.swift; sourceTree = ""; }; 28A283A120385F8C00DDCB63 /* GitCommitsParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitCommitsParserTests.swift; sourceTree = ""; }; - 28A283A42038AFB100DDCB63 /* JirassicCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JirassicCell.swift; sourceTree = ""; }; - 28A283A72038AFC500DDCB63 /* JirassicCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JirassicCell.xib; sourceTree = ""; }; 28A283AA203A93AB00DDCB63 /* GitBranchParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitBranchParser.swift; sourceTree = ""; }; 28A283AD203C069F00DDCB63 /* GitBranchParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitBranchParserTests.swift; sourceTree = ""; }; 28A283AF203CB1A100DDCB63 /* MergeTasksInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MergeTasksInteractor.swift; sourceTree = ""; }; 28A283B2203CB1B900DDCB63 /* MergeTasksInteractorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MergeTasksInteractorTests.swift; sourceTree = ""; }; - 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksDataSource.swift; sourceTree = ""; }; - 28A5F2811E586426002BE564 /* DataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = ""; }; 28A928771E8F78580022AB55 /* SQLiteSchema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteSchema.swift; sourceTree = ""; }; 28A9287C1E9030BA0022AB55 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; 28A928801E910DA40022AB55 /* SqliteRepository+Tasks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SqliteRepository+Tasks.swift"; sourceTree = ""; }; @@ -668,41 +676,23 @@ 28A928941E9110980022AB55 /* CloudKitRepository+User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+User.swift"; sourceTree = ""; }; 28A928961E9110BF0022AB55 /* CloudKitRepository+Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CloudKitRepository+Settings.swift"; sourceTree = ""; }; 28AFE7301E9A594500BAAD8C /* UserDefaults+token.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+token.swift"; sourceTree = ""; }; - 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReportsHeaderView.xib; sourceTree = ""; }; - 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MonthReportsHeaderView.xib; sourceTree = ""; }; 28C6A38321D359E60036DB29 /* RemoveDuplicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveDuplicate.swift; sourceTree = ""; }; 28C9C6211EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+uploadToken.swift"; sourceTree = ""; }; 28CBB595204554A2006F9D3A /* ParseGitBranch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseGitBranch.swift; sourceTree = ""; }; 28CBB59920474755006F9D3A /* ParseGitBranchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseGitBranchTests.swift; sourceTree = ""; }; 28DCA4542018B6E700DFAE29 /* JProjectIssue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JProjectIssue.swift; sourceTree = ""; }; - 28E896621E830D6700722032 /* Placeholder.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Placeholder.storyboard; sourceTree = ""; }; 28E8966E1E83732200722032 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = "macOS-launcher/AppDelegate.h"; sourceTree = ""; }; 28E8966F1E83732200722032 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = "macOS-launcher/AppDelegate.m"; sourceTree = ""; }; 28E896701E83732200722032 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = "macOS-launcher/main.m"; sourceTree = ""; }; 28E896741E83770D00722032 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "macOS-launcher/Base.lproj/MainMenu.xib"; sourceTree = ""; }; - 28EDE9381E59EC1500B360A4 /* TasksView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksView.swift; sourceTree = ""; }; - 28EE1F5D20EE8D1000C5C1D6 /* CalendarCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarCell.swift; sourceTree = ""; }; - 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CalendarCell.xib; sourceTree = ""; }; - 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarPresenter.swift; sourceTree = ""; }; - 28FB265020224E3A00AEA38D /* JiraTempoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JiraTempoCell.swift; sourceTree = ""; }; - 28FB265320224E5B00AEA38D /* JiraTempoCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JiraTempoCell.xib; sourceTree = ""; }; - 28FB265720224E9B00AEA38D /* HookupCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HookupCell.xib; sourceTree = ""; }; - 28FB265A20224EA700AEA38D /* HookupCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupCell.swift; sourceTree = ""; }; - 28FB265D2022DC2B00AEA38D /* JiraTempoPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JiraTempoPresenter.swift; sourceTree = ""; }; 28FE1886207A520B00DF796E /* NewTaskCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTaskCommand.swift; sourceTree = ""; }; - 28FE18A4207B369800DF796E /* CocoaHookupCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaHookupCell.swift; sourceTree = ""; }; - 28FE18A7207B36CB00DF796E /* CocoaHookupCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CocoaHookupCell.xib; sourceTree = ""; }; - 28FE18AA207B375A00DF796E /* CocoaHookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaHookupPresenter.swift; sourceTree = ""; }; 4051D5EF1E0EA0EA002042BB /* JirassicLauncher.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = JirassicLauncher.entitlements; path = "macOS-launcher/JirassicLauncher.entitlements"; sourceTree = ""; }; 4051D5F01E0EA0EA002042BB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = "macOS-launcher/Info.plist"; sourceTree = ""; }; 4051D5F71E0EA320002042BB /* Jirassic.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Jirassic.entitlements; sourceTree = ""; }; 4051D5F81E0EA48A002042BB /* AppLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLauncher.swift; sourceTree = ""; }; 4055B1231E0D802300279430 /* JirassicLauncher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JirassicLauncher.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4055B1381E0D82A900279430 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; }; - 405B15601DEF3D080009871C /* TaskSuggestionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TaskSuggestionViewController.swift; path = TaskSuggestion/TaskSuggestionViewController.swift; sourceTree = ""; }; - 405B15621DEF3F2A0009871C /* TaskSuggestionPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TaskSuggestionPresenter.swift; path = TaskSuggestion/TaskSuggestionPresenter.swift; sourceTree = ""; }; 405B15651DEF75660009871C /* AppViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = ""; }; - 406384881DE388C5004795A4 /* Tasks.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Tasks.storyboard; sourceTree = ""; }; 4065D3041DD3A1AA00B73201 /* Jirassic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jirassic.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4065D3181DD3B44200B73201 /* Day.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Day.swift; sourceTree = ""; }; 4065D3191DD3B44200B73201 /* Report.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Report.swift; sourceTree = ""; }; @@ -756,14 +746,6 @@ 4065D3691DD3B44200B73201 /* UserNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotifications.swift; sourceTree = ""; }; 4065D36A1DD3B44200B73201 /* SleepNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SleepNotifications.swift; sourceTree = ""; }; 4065D3711DD3B44200B73201 /* NewTaskViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewTaskViewController.swift; sourceTree = ""; }; - 4065D3731DD3B44200B73201 /* SettingsInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsInteractor.swift; sourceTree = ""; }; - 4065D3741DD3B44200B73201 /* SettingsPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsPresenter.swift; sourceTree = ""; }; - 4065D3751DD3B44200B73201 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; - 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarScrollView.swift; sourceTree = ""; }; - 4065D3781DD3B44200B73201 /* CellProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellProtocol.swift; sourceTree = ""; }; - 4065D3831DD3B44200B73201 /* TasksPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksPresenter.swift; sourceTree = ""; }; - 4065D3841DD3B44200B73201 /* TasksScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksScrollView.swift; sourceTree = ""; }; - 4065D3851DD3B44200B73201 /* TasksViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TasksViewController.swift; sourceTree = ""; }; 4065D3871DD3B44200B73201 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 4065D38A1DD3B44200B73201 /* CloudKitRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitRepository.swift; sourceTree = ""; }; 4065D38C1DD3B44200B73201 /* CoreDataRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataRepository.swift; sourceTree = ""; }; @@ -777,45 +759,128 @@ 4065D4061DD4532100B73201 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4065D4101DD4534C00B73201 /* jirassic */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jirassic; sourceTree = BUILT_PRODUCTS_DIR; }; 4073184E1DE9B1B40046F409 /* ComputerWakeUpInteractorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComputerWakeUpInteractorTests.swift; sourceTree = ""; }; - 40E092401DE385E4001EF5DA /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 40FCE4221DF7646F00D4FD45 /* AppleScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleScript.swift; sourceTree = ""; }; 40FCE4231DF7646F00D4FD45 /* ExtensionsInteractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionsInteractor.swift; sourceTree = ""; }; 40FCE4241DF7646F00D4FD45 /* SandboxedAppleScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SandboxedAppleScript.swift; sourceTree = ""; }; - 40FCE4281DFC1CB400D4FD45 /* TaskSuggestionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TaskSuggestionTests.swift; path = TaskSuggestion/TaskSuggestionTests.swift; sourceTree = ""; }; - 40FCE42B1DFC3F8700D4FD45 /* WelcomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = ""; }; - 40FCE42D1DFC576C00D4FD45 /* Welcome.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Welcome.storyboard; sourceTree = ""; }; 40FCE42F1DFD7C8D00D4FD45 /* Animatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animatable.swift; sourceTree = ""; }; - 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsViewController.swift; sourceTree = ""; }; - 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsPresenter.swift; sourceTree = ""; }; - 564E55F82028857300CE4C76 /* Worklogs.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Worklogs.storyboard; sourceTree = ""; }; 565E19EF20A5E336003A5E2A /* RCSync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RCSync.swift; sourceTree = ""; }; - 566B9FA6217DE67700EAF324 /* TasksInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksInteractor.swift; sourceTree = ""; }; 5683DC3D20ECDED30000A138 /* ModuleCalendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleCalendar.swift; sourceTree = ""; }; - 5685C7631DE8721100CA545E /* CloudKitLoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitLoginViewController.swift; sourceTree = ""; }; - 5685C7641DE8721100CA545E /* Login.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Login.storyboard; sourceTree = ""; }; - 5685C7651DE8721100CA545E /* LoginPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginPresenter.swift; sourceTree = ""; }; - 5685C7661DE8721100CA545E /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; - 5685C76B1DE8724400CA545E /* AccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = ""; }; - 569C4C572023193B0049FBF1 /* ShellCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellCell.swift; sourceTree = ""; }; - 569C4C5D202319630049FBF1 /* JitCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitCell.swift; sourceTree = ""; }; - 569C4C63202319870049FBF1 /* GitCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitCell.swift; sourceTree = ""; }; - 569C4C66202319930049FBF1 /* GitPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitPresenter.swift; sourceTree = ""; }; - 569C4C69202319A20049FBF1 /* BrowserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserCell.swift; sourceTree = ""; }; - 569C4C6C202319CA0049FBF1 /* BrowserPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserPresenter.swift; sourceTree = ""; }; - 569C4C6F202319DE0049FBF1 /* ShellCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShellCell.xib; sourceTree = ""; }; - 569C4C72202319EE0049FBF1 /* JitCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JitCell.xib; sourceTree = ""; }; - 569C4C75202319FC0049FBF1 /* GitCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GitCell.xib; sourceTree = ""; }; - 569C4C7820231A0B0049FBF1 /* BrowserCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BrowserCell.xib; sourceTree = ""; }; 56ADBF6921C3F625008350A6 /* GitUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitUser.swift; sourceTree = ""; }; 56ADBF6C21C3F94D008350A6 /* GitUserParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitUserParser.swift; sourceTree = ""; }; 56CD22BE1E72F89700F9CDB8 /* BuildScript.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = BuildScript.sh; sourceTree = ""; }; 56D069E0216CABCB000D051D /* CreateMonthReportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateMonthReportTests.swift; sourceTree = ""; }; - 56D90CF720876F1100F24442 /* WizardJiraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardJiraView.swift; sourceTree = ""; }; - 56D90CFA20876F2B00F24442 /* WizardGitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardGitView.swift; sourceTree = ""; }; - 56D90CFD20876F3C00F24442 /* WizardGitView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardGitView.xib; sourceTree = ""; }; - 56D90D0020876F4900F24442 /* WizardJiraView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardJiraView.xib; sourceTree = ""; }; 6D3702672BA84233002260D0 /* Jirassic macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Jirassic macOS.entitlements"; sourceTree = ""; }; 6D5BC4C62C1B5B70002DA29B /* CreateDayReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateDayReport.swift; sourceTree = ""; }; + 6D5BC5382C1C1970002DA29B /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = ""; }; + 6D5BC5392C1C1970002DA29B /* CloudKitLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitLoginViewController.swift; sourceTree = ""; }; + 6D5BC53A2C1C1970002DA29B /* Login.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Login.storyboard; sourceTree = ""; }; + 6D5BC53B2C1C1970002DA29B /* LoginPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginPresenter.swift; sourceTree = ""; }; + 6D5BC53C2C1C1970002DA29B /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; + 6D5BC53E2C1C1970002DA29B /* Calendar.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Calendar.storyboard; sourceTree = ""; }; + 6D5BC53F2C1C1970002DA29B /* CalendarDayCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDayCellView.swift; sourceTree = ""; }; + 6D5BC5402C1C1970002DA29B /* CalendarInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarInteractor.swift; sourceTree = ""; }; + 6D5BC5412C1C1970002DA29B /* CalendarPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarPresenter.swift; sourceTree = ""; }; + 6D5BC5422C1C1970002DA29B /* CalendarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewController.swift; sourceTree = ""; }; + 6D5BC5442C1C1970002DA29B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 6D5BC5462C1C1970002DA29B /* MainPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainPresenter.swift; sourceTree = ""; }; + 6D5BC5472C1C1970002DA29B /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; + 6D5BC5492C1C1970002DA29B /* Welcome.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Welcome.storyboard; sourceTree = ""; }; + 6D5BC54A2C1C1970002DA29B /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = ""; }; + 6D5BC54B2C1C1970002DA29B /* WizardAppleScriptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardAppleScriptView.swift; sourceTree = ""; }; + 6D5BC54C2C1C1970002DA29B /* WizardAppleScriptView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardAppleScriptView.xib; sourceTree = ""; }; + 6D5BC54D2C1C1970002DA29B /* WizardCalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardCalendarView.swift; sourceTree = ""; }; + 6D5BC54E2C1C1970002DA29B /* WizardCalendarView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardCalendarView.xib; sourceTree = ""; }; + 6D5BC54F2C1C1970002DA29B /* WizardGitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardGitView.swift; sourceTree = ""; }; + 6D5BC5502C1C1970002DA29B /* WizardGitView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardGitView.xib; sourceTree = ""; }; + 6D5BC5512C1C1970002DA29B /* WizardJiraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardJiraView.swift; sourceTree = ""; }; + 6D5BC5522C1C1970002DA29B /* WizardJiraView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WizardJiraView.xib; sourceTree = ""; }; + 6D5BC5532C1C1970002DA29B /* WizardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardViewController.swift; sourceTree = ""; }; + 6D5BC5552C1C1970002DA29B /* Placeholder.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Placeholder.storyboard; sourceTree = ""; }; + 6D5BC5562C1C1970002DA29B /* PlaceholderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderViewController.swift; sourceTree = ""; }; + 6D5BC5582C1C1970002DA29B /* ProjectDetailsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsPresenter.swift; sourceTree = ""; }; + 6D5BC5592C1C1970002DA29B /* ProjectDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsViewController.swift; sourceTree = ""; }; + 6D5BC55A2C1C1970002DA29B /* Projects.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Projects.storyboard; sourceTree = ""; }; + 6D5BC55B2C1C1970002DA29B /* ProjectsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsInteractor.swift; sourceTree = ""; }; + 6D5BC55C2C1C1970002DA29B /* ProjectsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsListViewController.swift; sourceTree = ""; }; + 6D5BC55D2C1C1970002DA29B /* ProjectsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsPresenter.swift; sourceTree = ""; }; + 6D5BC55E2C1C1970002DA29B /* ProjectsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectsViewController.swift; sourceTree = ""; }; + 6D5BC5602C1C1970002DA29B /* CopyReportCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyReportCell.swift; sourceTree = ""; }; + 6D5BC5612C1C1970002DA29B /* CopyReportCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CopyReportCell.xib; sourceTree = ""; }; + 6D5BC5632C1C1970002DA29B /* ReportCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportCell.swift; sourceTree = ""; }; + 6D5BC5642C1C1970002DA29B /* ReportCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReportCell.xib; sourceTree = ""; }; + 6D5BC5652C1C1970002DA29B /* ReportCellPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportCellPresenter.swift; sourceTree = ""; }; + 6D5BC5682C1C1970002DA29B /* Reports.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Reports.storyboard; sourceTree = ""; }; + 6D5BC5692C1C1970002DA29B /* ReportsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsDataSource.swift; sourceTree = ""; }; + 6D5BC56A2C1C1970002DA29B /* ReportsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsPresenter.swift; sourceTree = ""; }; + 6D5BC56B2C1C1970002DA29B /* ReportsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsViewController.swift; sourceTree = ""; }; + 6D5BC56D2C1C1970002DA29B /* BrowserCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserCell.swift; sourceTree = ""; }; + 6D5BC56E2C1C1970002DA29B /* BrowserCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BrowserCell.xib; sourceTree = ""; }; + 6D5BC56F2C1C1970002DA29B /* BrowserPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserPresenter.swift; sourceTree = ""; }; + 6D5BC5712C1C1970002DA29B /* CalendarAppPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarAppPresenter.swift; sourceTree = ""; }; + 6D5BC5722C1C1970002DA29B /* CalendarCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarCell.swift; sourceTree = ""; }; + 6D5BC5732C1C1970002DA29B /* CalendarCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CalendarCell.xib; sourceTree = ""; }; + 6D5BC5752C1C1970002DA29B /* GitCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitCell.swift; sourceTree = ""; }; + 6D5BC5762C1C1970002DA29B /* GitCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GitCell.xib; sourceTree = ""; }; + 6D5BC5772C1C1970002DA29B /* GitPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitPresenter.swift; sourceTree = ""; }; + 6D5BC5792C1C1970002DA29B /* JirassicCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JirassicCell.swift; sourceTree = ""; }; + 6D5BC57A2C1C1970002DA29B /* JirassicCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JirassicCell.xib; sourceTree = ""; }; + 6D5BC57C2C1C1970002DA29B /* JitCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitCell.swift; sourceTree = ""; }; + 6D5BC57D2C1C1970002DA29B /* JitCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JitCell.xib; sourceTree = ""; }; + 6D5BC57F2C1C1970002DA29B /* ShellCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellCell.swift; sourceTree = ""; }; + 6D5BC5802C1C1970002DA29B /* ShellCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShellCell.xib; sourceTree = ""; }; + 6D5BC5822C1C1970002DA29B /* InputsScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputsScrollView.swift; sourceTree = ""; }; + 6D5BC5832C1C1970002DA29B /* InputsScrollView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InputsScrollView.xib; sourceTree = ""; }; + 6D5BC5842C1C1970002DA29B /* InputsTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputsTableViewDataSource.swift; sourceTree = ""; }; + 6D5BC5862C1C1970002DA29B /* CocoaHookupCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaHookupCell.swift; sourceTree = ""; }; + 6D5BC5872C1C1970002DA29B /* CocoaHookupCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CocoaHookupCell.xib; sourceTree = ""; }; + 6D5BC5882C1C1970002DA29B /* CocoaHookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaHookupPresenter.swift; sourceTree = ""; }; + 6D5BC58A2C1C1970002DA29B /* HookupCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupCell.swift; sourceTree = ""; }; + 6D5BC58B2C1C1970002DA29B /* HookupCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HookupCell.xib; sourceTree = ""; }; + 6D5BC58C2C1C1970002DA29B /* HookupPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookupPresenter.swift; sourceTree = ""; }; + 6D5BC58E2C1C1970002DA29B /* JiraTempoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JiraTempoCell.swift; sourceTree = ""; }; + 6D5BC58F2C1C1970002DA29B /* JiraTempoCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JiraTempoCell.xib; sourceTree = ""; }; + 6D5BC5902C1C1970002DA29B /* JiraTempoPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JiraTempoPresenter.swift; sourceTree = ""; }; + 6D5BC5922C1C1970002DA29B /* OutputsScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputsScrollView.swift; sourceTree = ""; }; + 6D5BC5932C1C1970002DA29B /* OutputsScrollView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OutputsScrollView.xib; sourceTree = ""; }; + 6D5BC5942C1C1970002DA29B /* OutputTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputTableViewDataSource.swift; sourceTree = ""; }; + 6D5BC5962C1C1970002DA29B /* StoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreView.swift; sourceTree = ""; }; + 6D5BC5972C1C1970002DA29B /* StoreView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StoreView.xib; sourceTree = ""; }; + 6D5BC5992C1C1970002DA29B /* TrackingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingView.swift; sourceTree = ""; }; + 6D5BC59A2C1C1970002DA29B /* TrackingView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrackingView.xib; sourceTree = ""; }; + 6D5BC59C2C1C1970002DA29B /* Saveable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Saveable.swift; sourceTree = ""; }; + 6D5BC59D2C1C1970002DA29B /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; + 6D5BC59E2C1C1970002DA29B /* SettingsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsInteractor.swift; sourceTree = ""; }; + 6D5BC59F2C1C1970002DA29B /* SettingsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPresenter.swift; sourceTree = ""; }; + 6D5BC5A02C1C1970002DA29B /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; + 6D5BC5A22C1C1970002DA29B /* CloseDayCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseDayCell.swift; sourceTree = ""; }; + 6D5BC5A32C1C1970002DA29B /* CloseDayCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CloseDayCell.xib; sourceTree = ""; }; + 6D5BC5A52C1C1970002DA29B /* ClosedDayCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedDayCell.swift; sourceTree = ""; }; + 6D5BC5A62C1C1970002DA29B /* ClosedDayCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ClosedDayCell.xib; sourceTree = ""; }; + 6D5BC5A82C1C1970002DA29B /* TaskCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskCell.swift; sourceTree = ""; }; + 6D5BC5A92C1C1970002DA29B /* TaskCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TaskCell.xib; sourceTree = ""; }; + 6D5BC5AA2C1C1970002DA29B /* TaskCell_.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskCell_.swift; sourceTree = ""; }; + 6D5BC5AB2C1C1970002DA29B /* TaskCellPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskCellPresenter.swift; sourceTree = ""; }; + 6D5BC5AD2C1C1970002DA29B /* CellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellProtocol.swift; sourceTree = ""; }; + 6D5BC5AF2C1C1970002DA29B /* MonthReportsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthReportsHeaderView.swift; sourceTree = ""; }; + 6D5BC5B12C1C1970002DA29B /* ReportsHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReportsHeaderView.xib; sourceTree = ""; }; + 6D5BC5B42C1C1970002DA29B /* DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = ""; }; + 6D5BC5B52C1C1970002DA29B /* EditableTimeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableTimeBox.swift; sourceTree = ""; }; + 6D5BC5B62C1C1970002DA29B /* NewTaskViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTaskViewController.swift; sourceTree = ""; }; + 6D5BC5B72C1C1970002DA29B /* Tasks.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Tasks.storyboard; sourceTree = ""; }; + 6D5BC5B82C1C1970002DA29B /* TasksDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksDataSource.swift; sourceTree = ""; }; + 6D5BC5B92C1C1970002DA29B /* TasksInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksInteractor.swift; sourceTree = ""; }; + 6D5BC5BA2C1C1970002DA29B /* TasksPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksPresenter.swift; sourceTree = ""; }; + 6D5BC5BB2C1C1970002DA29B /* TasksScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksScrollView.swift; sourceTree = ""; }; + 6D5BC5BC2C1C1970002DA29B /* TasksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksView.swift; sourceTree = ""; }; + 6D5BC5BD2C1C1970002DA29B /* TasksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksViewController.swift; sourceTree = ""; }; + 6D5BC5BE2C1C1970002DA29B /* TimeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeBox.swift; sourceTree = ""; }; + 6D5BC5BF2C1C1970002DA29B /* TimeBoxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeBoxViewController.swift; sourceTree = ""; }; + 6D5BC5C12C1C1970002DA29B /* TaskSuggestionPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskSuggestionPresenter.swift; sourceTree = ""; }; + 6D5BC5C22C1C1970002DA29B /* TaskSuggestionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskSuggestionTests.swift; sourceTree = ""; }; + 6D5BC5C32C1C1970002DA29B /* TaskSuggestionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskSuggestionViewController.swift; sourceTree = ""; }; + 6D5BC5C52C1C1970002DA29B /* Worklogs.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Worklogs.storyboard; sourceTree = ""; }; + 6D5BC5C62C1C1970002DA29B /* WorklogsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsPresenter.swift; sourceTree = ""; }; + 6D5BC5C72C1C1970002DA29B /* WorklogsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorklogsViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -905,15 +970,6 @@ path = IAP; sourceTree = ""; }; - 28279E9A1E8300E200EAF9FC /* Placeholder */ = { - isa = PBXGroup; - children = ( - 28279E9B1E8300E200EAF9FC /* PlaceholderViewController.swift */, - 28E896621E830D6700722032 /* Placeholder.storyboard */, - ); - path = Placeholder; - sourceTree = ""; - }; 2845B137206703A8006EFB3B /* Keychain */ = { isa = PBXGroup; children = ( @@ -922,84 +978,6 @@ path = Keychain; sourceTree = ""; }; - 2845B141206AE2A4006EFB3B /* Reports */ = { - isa = PBXGroup; - children = ( - 2845B147206AE3A8006EFB3B /* ReportsDataSource.swift */, - 287B943B21A690C100FFC4A5 /* HeaderView */, - 2845B143206AE3A8006EFB3B /* ReportCell */, - ); - path = Reports; - sourceTree = ""; - }; - 2845B142206AE342006EFB3B /* Calendar */ = { - isa = PBXGroup; - children = ( - 4065D3771DD3B44200B73201 /* CalendarScrollView.swift */, - ); - path = Calendar; - sourceTree = ""; - }; - 2845B143206AE3A8006EFB3B /* ReportCell */ = { - isa = PBXGroup; - children = ( - 2845B144206AE3A8006EFB3B /* ReportCell.swift */, - 2845B145206AE3A8006EFB3B /* ReportCell.xib */, - 2845B146206AE3A8006EFB3B /* ReportCellPresenter.swift */, - ); - path = ReportCell; - sourceTree = ""; - }; - 2845B153206AE467006EFB3B /* TaskCell */ = { - isa = PBXGroup; - children = ( - 2845B155206AE467006EFB3B /* TaskCell.swift */, - 2845B156206AE467006EFB3B /* TaskCell.xib */, - 2845B154206AE467006EFB3B /* TaskCellTests.swift */, - 2845B157206AE467006EFB3B /* TaskCellPresenter.swift */, - ); - path = TaskCell; - sourceTree = ""; - }; - 2845B158206AE467006EFB3B /* HeaderView */ = { - isa = PBXGroup; - children = ( - 2845B159206AE467006EFB3B /* TasksHeaderView.swift */, - 2845B15A206AE467006EFB3B /* TasksHeaderView.xib */, - ); - path = HeaderView; - sourceTree = ""; - }; - 2845B15B206AE467006EFB3B /* NonTaskCell */ = { - isa = PBXGroup; - children = ( - 2845B15C206AE467006EFB3B /* NonTaskCell.swift */, - 2845B15D206AE467006EFB3B /* NonTaskCell.xib */, - ); - path = NonTaskCell; - sourceTree = ""; - }; - 2845B16F206C2888006EFB3B /* AllTasks */ = { - isa = PBXGroup; - children = ( - 28A5F27D1E5789FC002BE564 /* TasksDataSource.swift */, - 4065D3781DD3B44200B73201 /* CellProtocol.swift */, - 2845B158206AE467006EFB3B /* HeaderView */, - 2845B15B206AE467006EFB3B /* NonTaskCell */, - 2845B153206AE467006EFB3B /* TaskCell */, - ); - path = AllTasks; - sourceTree = ""; - }; - 285465A5217858120052CB6A /* Store */ = { - isa = PBXGroup; - children = ( - 285465A6217858790052CB6A /* StoreView.swift */, - 285465AC217858AF0052CB6A /* StoreView.xib */, - ); - path = Store; - sourceTree = ""; - }; 28577EA01E8ADBBD002B07FD /* sqlite */ = { isa = PBXGroup; children = ( @@ -1045,42 +1023,6 @@ path = Hookup; sourceTree = ""; }; - 287195232022FEA4001C237E /* Tracking */ = { - isa = PBXGroup; - children = ( - 2871952A20241B25001C237E /* TrackingView.swift */, - 2871952720241A6C001C237E /* TrackingView.xib */, - ); - path = Tracking; - sourceTree = ""; - }; - 287B358520FBAD160022F43E /* Onboarding */ = { - isa = PBXGroup; - children = ( - 40FCE42D1DFC576C00D4FD45 /* Welcome.storyboard */, - 40FCE42B1DFC3F8700D4FD45 /* WelcomeViewController.swift */, - 288BB67D2085252900CF720A /* WizardViewController.swift */, - 288BB6832087157F00CF720A /* WizardAppleScriptView.swift */, - 288BB6802087152B00CF720A /* WizardAppleScriptView.xib */, - 56D90CFA20876F2B00F24442 /* WizardGitView.swift */, - 56D90CFD20876F3C00F24442 /* WizardGitView.xib */, - 287B358620FBAFC40022F43E /* WizardCalendarView.swift */, - 287B358920FBAFD60022F43E /* WizardCalendarView.xib */, - 56D90CF720876F1100F24442 /* WizardJiraView.swift */, - 56D90D0020876F4900F24442 /* WizardJiraView.xib */, - ); - path = Onboarding; - sourceTree = ""; - }; - 287B943B21A690C100FFC4A5 /* HeaderView */ = { - isa = PBXGroup; - children = ( - 2845B148206AE3A8006EFB3B /* ReportsHeaderView.swift */, - 28B116B721AE5C45004ACE01 /* ReportsHeaderView.xib */, - ); - path = HeaderView; - sourceTree = ""; - }; 2892B2961F0949D70085BAC2 /* Jira */ = { isa = PBXGroup; children = ( @@ -1095,15 +1037,6 @@ path = Jira; sourceTree = ""; }; - 2898D3A62181905600CF5AD4 /* MonthReports */ = { - isa = PBXGroup; - children = ( - 2898D3A72181907700CF5AD4 /* MonthReportsHeaderView.swift */, - 28B116BA21AE6179004ACE01 /* MonthReportsHeaderView.xib */, - ); - path = MonthReports; - sourceTree = ""; - }; 28A28392203786F600DDCB63 /* GitLogs */ = { isa = PBXGroup; children = ( @@ -1117,15 +1050,6 @@ path = GitLogs; sourceTree = ""; }; - 28A283A32038AF9600DDCB63 /* JirassicCmd */ = { - isa = PBXGroup; - children = ( - 28A283A42038AFB100DDCB63 /* JirassicCell.swift */, - 28A283A72038AFC500DDCB63 /* JirassicCell.xib */, - ); - path = JirassicCmd; - sourceTree = ""; - }; 28CBB59420455467006F9D3A /* Parsing */ = { isa = PBXGroup; children = ( @@ -1135,65 +1059,6 @@ path = Parsing; sourceTree = ""; }; - 28EE1F5C20EE8CE600C5C1D6 /* Calendar */ = { - isa = PBXGroup; - children = ( - 28EE1F5D20EE8D1000C5C1D6 /* CalendarCell.swift */, - 28EE1F6320EE92DA00C5C1D6 /* CalendarPresenter.swift */, - 28EE1F6020EE8D6100C5C1D6 /* CalendarCell.xib */, - ); - path = Calendar; - sourceTree = ""; - }; - 28FB264D20224DE200AEA38D /* Input */ = { - isa = PBXGroup; - children = ( - 2871951D2022FD14001C237E /* InputsScrollView.swift */, - 2892E80A208D9B42004E5298 /* InputsScrollView.xib */, - 287195202022FD87001C237E /* InputsTableViewDataSource.swift */, - 28EE1F5C20EE8CE600C5C1D6 /* Calendar */, - 28A283A32038AF9600DDCB63 /* JirassicCmd */, - 569C4C53202318F50049FBF1 /* Shell */, - 569C4C56202319120049FBF1 /* Jit */, - 569C4C552023190A0049FBF1 /* Git */, - 569C4C54202319010049FBF1 /* Browser */, - ); - path = Input; - sourceTree = ""; - }; - 28FB264E20224DEB00AEA38D /* Output */ = { - isa = PBXGroup; - children = ( - 2871951A2022ECF7001C237E /* OutputsScrollView.swift */, - 2892E80D208D9DD0004E5298 /* OutputsScrollView.xib */, - 287195172022E8BC001C237E /* OutputTableViewDataSource.swift */, - 28FB264F20224E0A00AEA38D /* JiraTempo */, - 28FB265620224E8600AEA38D /* Hookup */, - 28FE18A3207B366A00DF796E /* CocoaHookup */, - ); - path = Output; - sourceTree = ""; - }; - 28FB264F20224E0A00AEA38D /* JiraTempo */ = { - isa = PBXGroup; - children = ( - 28FB265020224E3A00AEA38D /* JiraTempoCell.swift */, - 28FB265D2022DC2B00AEA38D /* JiraTempoPresenter.swift */, - 28FB265320224E5B00AEA38D /* JiraTempoCell.xib */, - ); - path = JiraTempo; - sourceTree = ""; - }; - 28FB265620224E8600AEA38D /* Hookup */ = { - isa = PBXGroup; - children = ( - 28FB265A20224EA700AEA38D /* HookupCell.swift */, - 287195142022E1E2001C237E /* HookupPresenter.swift */, - 28FB265720224E9B00AEA38D /* HookupCell.xib */, - ); - path = Hookup; - sourceTree = ""; - }; 28FE1885207A516300DF796E /* AppleScriptCommands */ = { isa = PBXGroup; children = ( @@ -1202,16 +1067,6 @@ path = AppleScriptCommands; sourceTree = ""; }; - 28FE18A3207B366A00DF796E /* CocoaHookup */ = { - isa = PBXGroup; - children = ( - 28FE18A4207B369800DF796E /* CocoaHookupCell.swift */, - 28FE18AA207B375A00DF796E /* CocoaHookupPresenter.swift */, - 28FE18A7207B36CB00DF796E /* CocoaHookupCell.xib */, - ); - path = CocoaHookup; - sourceTree = ""; - }; 4055B1371E0D82A800279430 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1237,16 +1092,6 @@ name = "macOS-launcher"; sourceTree = ""; }; - 405B155F1DEF3CE20009871C /* TaskSuggestion */ = { - isa = PBXGroup; - children = ( - 405B15601DEF3D080009871C /* TaskSuggestionViewController.swift */, - 405B15621DEF3F2A0009871C /* TaskSuggestionPresenter.swift */, - 40FCE4281DFC1CB400D4FD45 /* TaskSuggestionTests.swift */, - ); - name = TaskSuggestion; - sourceTree = ""; - }; 405B15641DEF66EE0009871C /* Resources */ = { isa = PBXGroup; children = ( @@ -1492,51 +1337,21 @@ 4065D36B1DD3B44200B73201 /* Screens */ = { isa = PBXGroup; children = ( - 28279E9A1E8300E200EAF9FC /* Placeholder */, - 287B358520FBAD160022F43E /* Onboarding */, - 405B155F1DEF3CE20009871C /* TaskSuggestion */, - 5685C7621DE8721100CA545E /* Account */, - 4065D3721DD3B44200B73201 /* Settings */, - 564E55F12028839F00CE4C76 /* Worklogs */, - 2845B142206AE342006EFB3B /* Calendar */, - 4065D3761DD3B44200B73201 /* Tasks */, + 6D5BC53D2C1C1970002DA29B /* Account */, + 6D5BC5432C1C1970002DA29B /* Calendar */, + 6D5BC5482C1C1970002DA29B /* Main */, + 6D5BC5542C1C1970002DA29B /* Onboarding */, + 6D5BC5572C1C1970002DA29B /* Placeholder */, + 6D5BC55F2C1C1970002DA29B /* Projects */, + 6D5BC56C2C1C1970002DA29B /* Reports */, + 6D5BC5A12C1C1970002DA29B /* Settings */, + 6D5BC5C02C1C1970002DA29B /* Tasks */, + 6D5BC5C42C1C1970002DA29B /* TaskSuggestion */, + 6D5BC5C82C1C1970002DA29B /* Worklogs */, ); path = Screens; sourceTree = ""; }; - 4065D3721DD3B44200B73201 /* Settings */ = { - isa = PBXGroup; - children = ( - 4065D3751DD3B44200B73201 /* SettingsViewController.swift */, - 4065D3741DD3B44200B73201 /* SettingsPresenter.swift */, - 4065D3731DD3B44200B73201 /* SettingsInteractor.swift */, - 28A283962037975600DDCB63 /* Saveable.swift */, - 40E092401DE385E4001EF5DA /* Settings.storyboard */, - 285465A5217858120052CB6A /* Store */, - 287195232022FEA4001C237E /* Tracking */, - 28FB264E20224DEB00AEA38D /* Output */, - 28FB264D20224DE200AEA38D /* Input */, - ); - path = Settings; - sourceTree = ""; - }; - 4065D3761DD3B44200B73201 /* Tasks */ = { - isa = PBXGroup; - children = ( - 406384881DE388C5004795A4 /* Tasks.storyboard */, - 4065D3851DD3B44200B73201 /* TasksViewController.swift */, - 4065D3831DD3B44200B73201 /* TasksPresenter.swift */, - 566B9FA6217DE67700EAF324 /* TasksInteractor.swift */, - 4065D3841DD3B44200B73201 /* TasksScrollView.swift */, - 28EDE9381E59EC1500B360A4 /* TasksView.swift */, - 28A5F2811E586426002BE564 /* DataSource.swift */, - 2845B16F206C2888006EFB3B /* AllTasks */, - 2845B141206AE2A4006EFB3B /* Reports */, - 2898D3A62181905600CF5AD4 /* MonthReports */, - ); - path = Tasks; - sourceTree = ""; - }; 4065D3861DD3B44200B73201 /* macOS-cmd */ = { isa = PBXGroup; children = ( @@ -1624,74 +1439,385 @@ path = External; sourceTree = ""; }; - 564E55F12028839F00CE4C76 /* Worklogs */ = { + 5683DC3C20ECDEA30000A138 /* CalendarEvents */ = { isa = PBXGroup; children = ( - 564E55F2202883DB00CE4C76 /* WorklogsViewController.swift */, - 564E55F5202884DE00CE4C76 /* WorklogsPresenter.swift */, - 564E55F82028857300CE4C76 /* Worklogs.storyboard */, + 5683DC3D20ECDED30000A138 /* ModuleCalendar.swift */, ); - path = Worklogs; + path = CalendarEvents; sourceTree = ""; }; - 5683DC3C20ECDEA30000A138 /* CalendarEvents */ = { + 6D5BC53D2C1C1970002DA29B /* Account */ = { isa = PBXGroup; children = ( - 5683DC3D20ECDED30000A138 /* ModuleCalendar.swift */, + 6D5BC5382C1C1970002DA29B /* AccountViewController.swift */, + 6D5BC5392C1C1970002DA29B /* CloudKitLoginViewController.swift */, + 6D5BC53A2C1C1970002DA29B /* Login.storyboard */, + 6D5BC53B2C1C1970002DA29B /* LoginPresenter.swift */, + 6D5BC53C2C1C1970002DA29B /* LoginViewController.swift */, ); - path = CalendarEvents; + path = Account; sourceTree = ""; }; - 5685C7621DE8721100CA545E /* Account */ = { + 6D5BC5432C1C1970002DA29B /* Calendar */ = { isa = PBXGroup; children = ( - 5685C76B1DE8724400CA545E /* AccountViewController.swift */, - 5685C7631DE8721100CA545E /* CloudKitLoginViewController.swift */, - 5685C7641DE8721100CA545E /* Login.storyboard */, - 5685C7651DE8721100CA545E /* LoginPresenter.swift */, - 5685C7661DE8721100CA545E /* LoginViewController.swift */, + 6D5BC53E2C1C1970002DA29B /* Calendar.storyboard */, + 6D5BC53F2C1C1970002DA29B /* CalendarDayCellView.swift */, + 6D5BC5402C1C1970002DA29B /* CalendarInteractor.swift */, + 6D5BC5412C1C1970002DA29B /* CalendarPresenter.swift */, + 6D5BC5422C1C1970002DA29B /* CalendarViewController.swift */, ); - path = Account; + path = Calendar; sourceTree = ""; }; - 569C4C53202318F50049FBF1 /* Shell */ = { + 6D5BC5482C1C1970002DA29B /* Main */ = { isa = PBXGroup; children = ( - 569C4C572023193B0049FBF1 /* ShellCell.swift */, - 569C4C6F202319DE0049FBF1 /* ShellCell.xib */, + 6D5BC5452C1C1970002DA29B /* Main.storyboard */, + 6D5BC5462C1C1970002DA29B /* MainPresenter.swift */, + 6D5BC5472C1C1970002DA29B /* MainViewController.swift */, ); - path = Shell; + path = Main; sourceTree = ""; }; - 569C4C54202319010049FBF1 /* Browser */ = { + 6D5BC5542C1C1970002DA29B /* Onboarding */ = { isa = PBXGroup; children = ( - 569C4C69202319A20049FBF1 /* BrowserCell.swift */, - 569C4C6C202319CA0049FBF1 /* BrowserPresenter.swift */, - 569C4C7820231A0B0049FBF1 /* BrowserCell.xib */, + 6D5BC5492C1C1970002DA29B /* Welcome.storyboard */, + 6D5BC54A2C1C1970002DA29B /* WelcomeViewController.swift */, + 6D5BC54B2C1C1970002DA29B /* WizardAppleScriptView.swift */, + 6D5BC54C2C1C1970002DA29B /* WizardAppleScriptView.xib */, + 6D5BC54D2C1C1970002DA29B /* WizardCalendarView.swift */, + 6D5BC54E2C1C1970002DA29B /* WizardCalendarView.xib */, + 6D5BC54F2C1C1970002DA29B /* WizardGitView.swift */, + 6D5BC5502C1C1970002DA29B /* WizardGitView.xib */, + 6D5BC5512C1C1970002DA29B /* WizardJiraView.swift */, + 6D5BC5522C1C1970002DA29B /* WizardJiraView.xib */, + 6D5BC5532C1C1970002DA29B /* WizardViewController.swift */, + ); + path = Onboarding; + sourceTree = ""; + }; + 6D5BC5572C1C1970002DA29B /* Placeholder */ = { + isa = PBXGroup; + children = ( + 6D5BC5552C1C1970002DA29B /* Placeholder.storyboard */, + 6D5BC5562C1C1970002DA29B /* PlaceholderViewController.swift */, + ); + path = Placeholder; + sourceTree = ""; + }; + 6D5BC55F2C1C1970002DA29B /* Projects */ = { + isa = PBXGroup; + children = ( + 6D5BC5582C1C1970002DA29B /* ProjectDetailsPresenter.swift */, + 6D5BC5592C1C1970002DA29B /* ProjectDetailsViewController.swift */, + 6D5BC55A2C1C1970002DA29B /* Projects.storyboard */, + 6D5BC55B2C1C1970002DA29B /* ProjectsInteractor.swift */, + 6D5BC55C2C1C1970002DA29B /* ProjectsListViewController.swift */, + 6D5BC55D2C1C1970002DA29B /* ProjectsPresenter.swift */, + 6D5BC55E2C1C1970002DA29B /* ProjectsViewController.swift */, + ); + path = Projects; + sourceTree = ""; + }; + 6D5BC5622C1C1970002DA29B /* CopyReportCell */ = { + isa = PBXGroup; + children = ( + 6D5BC5602C1C1970002DA29B /* CopyReportCell.swift */, + 6D5BC5612C1C1970002DA29B /* CopyReportCell.xib */, + ); + path = CopyReportCell; + sourceTree = ""; + }; + 6D5BC5662C1C1970002DA29B /* ReportCell */ = { + isa = PBXGroup; + children = ( + 6D5BC5632C1C1970002DA29B /* ReportCell.swift */, + 6D5BC5642C1C1970002DA29B /* ReportCell.xib */, + 6D5BC5652C1C1970002DA29B /* ReportCellPresenter.swift */, + ); + path = ReportCell; + sourceTree = ""; + }; + 6D5BC5672C1C1970002DA29B /* cells */ = { + isa = PBXGroup; + children = ( + 6D5BC5622C1C1970002DA29B /* CopyReportCell */, + 6D5BC5662C1C1970002DA29B /* ReportCell */, + ); + path = cells; + sourceTree = ""; + }; + 6D5BC56C2C1C1970002DA29B /* Reports */ = { + isa = PBXGroup; + children = ( + 6D5BC5672C1C1970002DA29B /* cells */, + 6D5BC5682C1C1970002DA29B /* Reports.storyboard */, + 6D5BC5692C1C1970002DA29B /* ReportsDataSource.swift */, + 6D5BC56A2C1C1970002DA29B /* ReportsPresenter.swift */, + 6D5BC56B2C1C1970002DA29B /* ReportsViewController.swift */, + ); + path = Reports; + sourceTree = ""; + }; + 6D5BC5702C1C1970002DA29B /* Browser */ = { + isa = PBXGroup; + children = ( + 6D5BC56D2C1C1970002DA29B /* BrowserCell.swift */, + 6D5BC56E2C1C1970002DA29B /* BrowserCell.xib */, + 6D5BC56F2C1C1970002DA29B /* BrowserPresenter.swift */, ); path = Browser; sourceTree = ""; }; - 569C4C552023190A0049FBF1 /* Git */ = { + 6D5BC5742C1C1970002DA29B /* Calendar */ = { isa = PBXGroup; children = ( - 569C4C63202319870049FBF1 /* GitCell.swift */, - 569C4C66202319930049FBF1 /* GitPresenter.swift */, - 569C4C75202319FC0049FBF1 /* GitCell.xib */, + 6D5BC5712C1C1970002DA29B /* CalendarAppPresenter.swift */, + 6D5BC5722C1C1970002DA29B /* CalendarCell.swift */, + 6D5BC5732C1C1970002DA29B /* CalendarCell.xib */, + ); + path = Calendar; + sourceTree = ""; + }; + 6D5BC5782C1C1970002DA29B /* Git */ = { + isa = PBXGroup; + children = ( + 6D5BC5752C1C1970002DA29B /* GitCell.swift */, + 6D5BC5762C1C1970002DA29B /* GitCell.xib */, + 6D5BC5772C1C1970002DA29B /* GitPresenter.swift */, ); path = Git; sourceTree = ""; }; - 569C4C56202319120049FBF1 /* Jit */ = { + 6D5BC57B2C1C1970002DA29B /* JirassicCmd */ = { + isa = PBXGroup; + children = ( + 6D5BC5792C1C1970002DA29B /* JirassicCell.swift */, + 6D5BC57A2C1C1970002DA29B /* JirassicCell.xib */, + ); + path = JirassicCmd; + sourceTree = ""; + }; + 6D5BC57E2C1C1970002DA29B /* Jit */ = { isa = PBXGroup; children = ( - 569C4C5D202319630049FBF1 /* JitCell.swift */, - 569C4C72202319EE0049FBF1 /* JitCell.xib */, + 6D5BC57C2C1C1970002DA29B /* JitCell.swift */, + 6D5BC57D2C1C1970002DA29B /* JitCell.xib */, ); path = Jit; sourceTree = ""; }; + 6D5BC5812C1C1970002DA29B /* Shell */ = { + isa = PBXGroup; + children = ( + 6D5BC57F2C1C1970002DA29B /* ShellCell.swift */, + 6D5BC5802C1C1970002DA29B /* ShellCell.xib */, + ); + path = Shell; + sourceTree = ""; + }; + 6D5BC5852C1C1970002DA29B /* Input */ = { + isa = PBXGroup; + children = ( + 6D5BC5702C1C1970002DA29B /* Browser */, + 6D5BC5742C1C1970002DA29B /* Calendar */, + 6D5BC5782C1C1970002DA29B /* Git */, + 6D5BC57B2C1C1970002DA29B /* JirassicCmd */, + 6D5BC57E2C1C1970002DA29B /* Jit */, + 6D5BC5812C1C1970002DA29B /* Shell */, + 6D5BC5822C1C1970002DA29B /* InputsScrollView.swift */, + 6D5BC5832C1C1970002DA29B /* InputsScrollView.xib */, + 6D5BC5842C1C1970002DA29B /* InputsTableViewDataSource.swift */, + ); + path = Input; + sourceTree = ""; + }; + 6D5BC5892C1C1970002DA29B /* CocoaHookup */ = { + isa = PBXGroup; + children = ( + 6D5BC5862C1C1970002DA29B /* CocoaHookupCell.swift */, + 6D5BC5872C1C1970002DA29B /* CocoaHookupCell.xib */, + 6D5BC5882C1C1970002DA29B /* CocoaHookupPresenter.swift */, + ); + path = CocoaHookup; + sourceTree = ""; + }; + 6D5BC58D2C1C1970002DA29B /* Hookup */ = { + isa = PBXGroup; + children = ( + 6D5BC58A2C1C1970002DA29B /* HookupCell.swift */, + 6D5BC58B2C1C1970002DA29B /* HookupCell.xib */, + 6D5BC58C2C1C1970002DA29B /* HookupPresenter.swift */, + ); + path = Hookup; + sourceTree = ""; + }; + 6D5BC5912C1C1970002DA29B /* JiraTempo */ = { + isa = PBXGroup; + children = ( + 6D5BC58E2C1C1970002DA29B /* JiraTempoCell.swift */, + 6D5BC58F2C1C1970002DA29B /* JiraTempoCell.xib */, + 6D5BC5902C1C1970002DA29B /* JiraTempoPresenter.swift */, + ); + path = JiraTempo; + sourceTree = ""; + }; + 6D5BC5952C1C1970002DA29B /* Output */ = { + isa = PBXGroup; + children = ( + 6D5BC5892C1C1970002DA29B /* CocoaHookup */, + 6D5BC58D2C1C1970002DA29B /* Hookup */, + 6D5BC5912C1C1970002DA29B /* JiraTempo */, + 6D5BC5922C1C1970002DA29B /* OutputsScrollView.swift */, + 6D5BC5932C1C1970002DA29B /* OutputsScrollView.xib */, + 6D5BC5942C1C1970002DA29B /* OutputTableViewDataSource.swift */, + ); + path = Output; + sourceTree = ""; + }; + 6D5BC5982C1C1970002DA29B /* Store */ = { + isa = PBXGroup; + children = ( + 6D5BC5962C1C1970002DA29B /* StoreView.swift */, + 6D5BC5972C1C1970002DA29B /* StoreView.xib */, + ); + path = Store; + sourceTree = ""; + }; + 6D5BC59B2C1C1970002DA29B /* Tracking */ = { + isa = PBXGroup; + children = ( + 6D5BC5992C1C1970002DA29B /* TrackingView.swift */, + 6D5BC59A2C1C1970002DA29B /* TrackingView.xib */, + ); + path = Tracking; + sourceTree = ""; + }; + 6D5BC5A12C1C1970002DA29B /* Settings */ = { + isa = PBXGroup; + children = ( + 6D5BC5852C1C1970002DA29B /* Input */, + 6D5BC5952C1C1970002DA29B /* Output */, + 6D5BC5982C1C1970002DA29B /* Store */, + 6D5BC59B2C1C1970002DA29B /* Tracking */, + 6D5BC59C2C1C1970002DA29B /* Saveable.swift */, + 6D5BC59D2C1C1970002DA29B /* Settings.storyboard */, + 6D5BC59E2C1C1970002DA29B /* SettingsInteractor.swift */, + 6D5BC59F2C1C1970002DA29B /* SettingsPresenter.swift */, + 6D5BC5A02C1C1970002DA29B /* SettingsViewController.swift */, + ); + path = Settings; + sourceTree = ""; + }; + 6D5BC5A42C1C1970002DA29B /* CloseDayCell */ = { + isa = PBXGroup; + children = ( + 6D5BC5A22C1C1970002DA29B /* CloseDayCell.swift */, + 6D5BC5A32C1C1970002DA29B /* CloseDayCell.xib */, + ); + path = CloseDayCell; + sourceTree = ""; + }; + 6D5BC5A72C1C1970002DA29B /* ClosedDayCell */ = { + isa = PBXGroup; + children = ( + 6D5BC5A52C1C1970002DA29B /* ClosedDayCell.swift */, + 6D5BC5A62C1C1970002DA29B /* ClosedDayCell.xib */, + ); + path = ClosedDayCell; + sourceTree = ""; + }; + 6D5BC5AC2C1C1970002DA29B /* TaskCell */ = { + isa = PBXGroup; + children = ( + 6D5BC5A82C1C1970002DA29B /* TaskCell.swift */, + 6D5BC5A92C1C1970002DA29B /* TaskCell.xib */, + 6D5BC5AA2C1C1970002DA29B /* TaskCell_.swift */, + 6D5BC5AB2C1C1970002DA29B /* TaskCellPresenter.swift */, + ); + path = TaskCell; + sourceTree = ""; + }; + 6D5BC5AE2C1C1970002DA29B /* cells */ = { + isa = PBXGroup; + children = ( + 6D5BC5A42C1C1970002DA29B /* CloseDayCell */, + 6D5BC5A72C1C1970002DA29B /* ClosedDayCell */, + 6D5BC5AC2C1C1970002DA29B /* TaskCell */, + 6D5BC5AD2C1C1970002DA29B /* CellProtocol.swift */, + ); + path = cells; + sourceTree = ""; + }; + 6D5BC5B02C1C1970002DA29B /* MonthReports */ = { + isa = PBXGroup; + children = ( + 6D5BC5AF2C1C1970002DA29B /* MonthReportsHeaderView.swift */, + ); + path = MonthReports; + sourceTree = ""; + }; + 6D5BC5B22C1C1970002DA29B /* HeaderView */ = { + isa = PBXGroup; + children = ( + 6D5BC5B12C1C1970002DA29B /* ReportsHeaderView.xib */, + ); + path = HeaderView; + sourceTree = ""; + }; + 6D5BC5B32C1C1970002DA29B /* Reports */ = { + isa = PBXGroup; + children = ( + 6D5BC5B22C1C1970002DA29B /* HeaderView */, + ); + path = Reports; + sourceTree = ""; + }; + 6D5BC5C02C1C1970002DA29B /* Tasks */ = { + isa = PBXGroup; + children = ( + 6D5BC5AE2C1C1970002DA29B /* cells */, + 6D5BC5B02C1C1970002DA29B /* MonthReports */, + 6D5BC5B32C1C1970002DA29B /* Reports */, + 6D5BC5B42C1C1970002DA29B /* DataSource.swift */, + 6D5BC5B52C1C1970002DA29B /* EditableTimeBox.swift */, + 6D5BC5B62C1C1970002DA29B /* NewTaskViewController.swift */, + 6D5BC5B72C1C1970002DA29B /* Tasks.storyboard */, + 6D5BC5B82C1C1970002DA29B /* TasksDataSource.swift */, + 6D5BC5B92C1C1970002DA29B /* TasksInteractor.swift */, + 6D5BC5BA2C1C1970002DA29B /* TasksPresenter.swift */, + 6D5BC5BB2C1C1970002DA29B /* TasksScrollView.swift */, + 6D5BC5BC2C1C1970002DA29B /* TasksView.swift */, + 6D5BC5BD2C1C1970002DA29B /* TasksViewController.swift */, + 6D5BC5BE2C1C1970002DA29B /* TimeBox.swift */, + 6D5BC5BF2C1C1970002DA29B /* TimeBoxViewController.swift */, + ); + path = Tasks; + sourceTree = ""; + }; + 6D5BC5C42C1C1970002DA29B /* TaskSuggestion */ = { + isa = PBXGroup; + children = ( + 6D5BC5C12C1C1970002DA29B /* TaskSuggestionPresenter.swift */, + 6D5BC5C22C1C1970002DA29B /* TaskSuggestionTests.swift */, + 6D5BC5C32C1C1970002DA29B /* TaskSuggestionViewController.swift */, + ); + path = TaskSuggestion; + sourceTree = ""; + }; + 6D5BC5C82C1C1970002DA29B /* Worklogs */ = { + isa = PBXGroup; + children = ( + 6D5BC5C52C1C1970002DA29B /* Worklogs.storyboard */, + 6D5BC5C62C1C1970002DA29B /* WorklogsPresenter.swift */, + 6D5BC5C72C1C1970002DA29B /* WorklogsViewController.swift */, + ); + path = Worklogs; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1930,41 +2056,45 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2892E80E208D9DD0004E5298 /* OutputsScrollView.xib in Resources */, - 569C4C7920231A0B0049FBF1 /* BrowserCell.xib in Resources */, - 569C4C70202319DE0049FBF1 /* ShellCell.xib in Resources */, - 28A283A82038AFC500DDCB63 /* JirassicCell.xib in Resources */, - 288BB6812087152B00CF720A /* WizardAppleScriptView.xib in Resources */, 280D71C91ED60908005D2689 /* jirassic.sdef in Resources */, - 2892E80B208D9B42004E5298 /* InputsScrollView.xib in Resources */, - 28EE1F6120EE8D6100C5C1D6 /* CalendarCell.xib in Resources */, 280D71BF1ED608D6005D2689 /* Main.storyboard in Resources */, 2898D3AE2184ED3000CF5AD4 /* Components.storyboard in Resources */, - 280D71C01ED608D6005D2689 /* Placeholder.storyboard in Resources */, 280C1D441ED74EF900C126A1 /* ShellSupport.scpt in Resources */, - 28FB265820224E9B00AEA38D /* HookupCell.xib in Resources */, - 28B116BB21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, - 287B358A20FBAFD60022F43E /* WizardCalendarView.xib in Resources */, - 280D71C11ED608D6005D2689 /* Welcome.storyboard in Resources */, - 280D71C21ED608D6005D2689 /* Login.storyboard in Resources */, - 56D90CFE20876F3C00F24442 /* WizardGitView.xib in Resources */, - 280D71C31ED608D6005D2689 /* Settings.storyboard in Resources */, - 280D71C41ED608D6005D2689 /* Tasks.storyboard in Resources */, - 564E55F92028857300CE4C76 /* Worklogs.storyboard in Resources */, - 2845B162206AE468006EFB3B /* TaskCell.xib in Resources */, - 56D90D0120876F4900F24442 /* WizardJiraView.xib in Resources */, 280C1D431ED74EF900C126A1 /* BrowserSupport.scpt in Resources */, - 28B116B821AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */, - 2871952820241A6C001C237E /* TrackingView.xib in Resources */, - 28FB265420224E5B00AEA38D /* JiraTempoCell.xib in Resources */, - 2845B14B206AE3A8006EFB3B /* ReportCell.xib in Resources */, - 28FE18A8207B36CB00DF796E /* CocoaHookupCell.xib in Resources */, - 569C4C76202319FC0049FBF1 /* GitCell.xib in Resources */, - 2845B168206AE468006EFB3B /* TasksHeaderView.xib in Resources */, 280D71C81ED608D6005D2689 /* Images.xcassets in Resources */, - 285465AD217858AF0052CB6A /* StoreView.xib in Resources */, - 2845B16C206AE468006EFB3B /* NonTaskCell.xib in Resources */, - 569C4C73202319EE0049FBF1 /* JitCell.xib in Resources */, + 6D5BC5C92C1C1970002DA29B /* WizardGitView.xib in Resources */, + 6D5BC5CA2C1C1970002DA29B /* JitCell.xib in Resources */, + 6D5BC5CB2C1C1970002DA29B /* Login.storyboard in Resources */, + 6D5BC5CC2C1C1970002DA29B /* Placeholder.storyboard in Resources */, + 6D5BC5CD2C1C1970002DA29B /* JiraTempoCell.xib in Resources */, + 6D5BC5CE2C1C1970002DA29B /* Welcome.storyboard in Resources */, + 6D5BC5CF2C1C1970002DA29B /* Reports.storyboard in Resources */, + 6D5BC5D02C1C1970002DA29B /* Projects.storyboard in Resources */, + 6D5BC5D12C1C1970002DA29B /* TaskCell.xib in Resources */, + 6D5BC5D22C1C1970002DA29B /* Main.storyboard in Resources */, + 6D5BC5D32C1C1970002DA29B /* TrackingView.xib in Resources */, + 6D5BC5D42C1C1970002DA29B /* CopyReportCell.xib in Resources */, + 6D5BC5D52C1C1970002DA29B /* ReportCell.xib in Resources */, + 6D5BC5D62C1C1970002DA29B /* CocoaHookupCell.xib in Resources */, + 6D5BC5D72C1C1970002DA29B /* Tasks.storyboard in Resources */, + 6D5BC5D82C1C1970002DA29B /* CalendarCell.xib in Resources */, + 6D5BC5D92C1C1970002DA29B /* InputsScrollView.xib in Resources */, + 6D5BC5DA2C1C1970002DA29B /* ClosedDayCell.xib in Resources */, + 6D5BC5DB2C1C1970002DA29B /* Settings.storyboard in Resources */, + 6D5BC5DC2C1C1970002DA29B /* BrowserCell.xib in Resources */, + 6D5BC5DD2C1C1970002DA29B /* ShellCell.xib in Resources */, + 6D5BC5DE2C1C1970002DA29B /* Worklogs.storyboard in Resources */, + 6D5BC5DF2C1C1970002DA29B /* WizardJiraView.xib in Resources */, + 6D5BC5E02C1C1970002DA29B /* CloseDayCell.xib in Resources */, + 6D5BC5E12C1C1970002DA29B /* StoreView.xib in Resources */, + 6D5BC5E22C1C1970002DA29B /* HookupCell.xib in Resources */, + 6D5BC5E32C1C1970002DA29B /* WizardCalendarView.xib in Resources */, + 6D5BC5E42C1C1970002DA29B /* OutputsScrollView.xib in Resources */, + 6D5BC5E52C1C1970002DA29B /* Calendar.storyboard in Resources */, + 6D5BC5E62C1C1970002DA29B /* ReportsHeaderView.xib in Resources */, + 6D5BC5E72C1C1970002DA29B /* WizardAppleScriptView.xib in Resources */, + 6D5BC5E82C1C1970002DA29B /* GitCell.xib in Resources */, + 6D5BC5E92C1C1970002DA29B /* JirassicCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1981,39 +2111,43 @@ buildActionMask = 2147483647; files = ( 2898D3AF2184ED3000CF5AD4 /* Components.storyboard in Resources */, - 569C4C7A20231A0B0049FBF1 /* BrowserCell.xib in Resources */, - 288BB6822087152B00CF720A /* WizardAppleScriptView.xib in Resources */, - 569C4C74202319EE0049FBF1 /* JitCell.xib in Resources */, - 2845B16D206AE468006EFB3B /* NonTaskCell.xib in Resources */, - 2871952920241A6C001C237E /* TrackingView.xib in Resources */, - 2892E80F208D9DD0004E5298 /* OutputsScrollView.xib in Resources */, - 285465AE217858AF0052CB6A /* StoreView.xib in Resources */, - 28FB265920224E9B00AEA38D /* HookupCell.xib in Resources */, - 40FCE42E1DFC576C00D4FD45 /* Welcome.storyboard in Resources */, - 5685C7681DE8721100CA545E /* Login.storyboard in Resources */, - 569C4C71202319DE0049FBF1 /* ShellCell.xib in Resources */, - 28B116B921AE5C45004ACE01 /* ReportsHeaderView.xib in Resources */, - 2892E80C208D9B42004E5298 /* InputsScrollView.xib in Resources */, - 287B358B20FBAFD60022F43E /* WizardCalendarView.xib in Resources */, + 6D5BC6372C1C1970002DA29B /* WizardGitView.xib in Resources */, + 6D5BC6382C1C1970002DA29B /* JitCell.xib in Resources */, + 6D5BC6392C1C1970002DA29B /* Login.storyboard in Resources */, + 6D5BC63A2C1C1970002DA29B /* Placeholder.storyboard in Resources */, + 6D5BC63B2C1C1970002DA29B /* JiraTempoCell.xib in Resources */, + 6D5BC63C2C1C1970002DA29B /* Welcome.storyboard in Resources */, + 6D5BC63D2C1C1970002DA29B /* Reports.storyboard in Resources */, + 6D5BC63E2C1C1970002DA29B /* Projects.storyboard in Resources */, + 6D5BC63F2C1C1970002DA29B /* TaskCell.xib in Resources */, + 6D5BC6402C1C1970002DA29B /* Main.storyboard in Resources */, + 6D5BC6412C1C1970002DA29B /* TrackingView.xib in Resources */, + 6D5BC6422C1C1970002DA29B /* CopyReportCell.xib in Resources */, + 6D5BC6432C1C1970002DA29B /* ReportCell.xib in Resources */, + 6D5BC6442C1C1970002DA29B /* CocoaHookupCell.xib in Resources */, + 6D5BC6452C1C1970002DA29B /* Tasks.storyboard in Resources */, + 6D5BC6462C1C1970002DA29B /* CalendarCell.xib in Resources */, + 6D5BC6472C1C1970002DA29B /* InputsScrollView.xib in Resources */, + 6D5BC6482C1C1970002DA29B /* ClosedDayCell.xib in Resources */, + 6D5BC6492C1C1970002DA29B /* Settings.storyboard in Resources */, + 6D5BC64A2C1C1970002DA29B /* BrowserCell.xib in Resources */, + 6D5BC64B2C1C1970002DA29B /* ShellCell.xib in Resources */, + 6D5BC64C2C1C1970002DA29B /* Worklogs.storyboard in Resources */, + 6D5BC64D2C1C1970002DA29B /* WizardJiraView.xib in Resources */, + 6D5BC64E2C1C1970002DA29B /* CloseDayCell.xib in Resources */, + 6D5BC64F2C1C1970002DA29B /* StoreView.xib in Resources */, + 6D5BC6502C1C1970002DA29B /* HookupCell.xib in Resources */, + 6D5BC6512C1C1970002DA29B /* WizardCalendarView.xib in Resources */, + 6D5BC6522C1C1970002DA29B /* OutputsScrollView.xib in Resources */, + 6D5BC6532C1C1970002DA29B /* Calendar.storyboard in Resources */, + 6D5BC6542C1C1970002DA29B /* ReportsHeaderView.xib in Resources */, + 6D5BC6552C1C1970002DA29B /* WizardAppleScriptView.xib in Resources */, + 6D5BC6562C1C1970002DA29B /* GitCell.xib in Resources */, + 6D5BC6572C1C1970002DA29B /* JirassicCell.xib in Resources */, 4065D3CF1DD3B44200B73201 /* Images.xcassets in Resources */, 56CD22BF1E72F89700F9CDB8 /* BuildScript.sh in Resources */, - 28FB265520224E5B00AEA38D /* JiraTempoCell.xib in Resources */, - 2845B14C206AE3A8006EFB3B /* ReportCell.xib in Resources */, - 406384891DE388C5004795A4 /* Tasks.storyboard in Resources */, - 28FE18A9207B36CB00DF796E /* CocoaHookupCell.xib in Resources */, - 569C4C77202319FC0049FBF1 /* GitCell.xib in Resources */, - 28E896631E830D6700722032 /* Placeholder.storyboard in Resources */, - 56D90D0220876F4900F24442 /* WizardJiraView.xib in Resources */, 4065D3CE1DD3B44200B73201 /* Main.storyboard in Resources */, - 2845B163206AE468006EFB3B /* TaskCell.xib in Resources */, 4065D3D31DD3B44200B73201 /* jirassic.sdef in Resources */, - 40E092411DE385E4001EF5DA /* Settings.storyboard in Resources */, - 28B116BC21AE6179004ACE01 /* MonthReportsHeaderView.xib in Resources */, - 56D90CFF20876F3C00F24442 /* WizardGitView.xib in Resources */, - 564E55FA2028857300CE4C76 /* Worklogs.storyboard in Resources */, - 2845B169206AE468006EFB3B /* TasksHeaderView.xib in Resources */, - 28A283A92038AFC500DDCB63 /* JirassicCell.xib in Resources */, - 28EE1F6220EE8D6100C5C1D6 /* CalendarCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2081,7 +2215,6 @@ 280D712C1ED6043D005D2689 /* Day.swift in Sources */, 2892B29B1F094A760085BAC2 /* JiraRepository+Reports.swift in Sources */, 280D712D1ED6043D005D2689 /* Report.swift in Sources */, - 28EE1F6420EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */, 280D712E1ED6043D005D2689 /* Settings.swift in Sources */, 2898D3AB2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, 280D712F1ED6043D005D2689 /* Task.swift in Sources */, @@ -2090,24 +2223,94 @@ 287195302025C35C001C237E /* CoreDataRepository+User.swift in Sources */, 280D71311ED6043D005D2689 /* User.swift in Sources */, 280D71321ED6043D005D2689 /* Week.swift in Sources */, - 566B9FA7217DE67800EAF324 /* TasksInteractor.swift in Sources */, - 288BB67E2085252900CF720A /* WizardViewController.swift in Sources */, - 28FB265E2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */, 280D71331ED6043D005D2689 /* DateExtension.swift in Sources */, 56ADBF6D21C3F94D008350A6 /* GitUserParser.swift in Sources */, 280D71351ED6043D005D2689 /* StringIdGenerator.swift in Sources */, 280D71361ED6043D005D2689 /* StringArray.swift in Sources */, 287195362027CAD9001C237E /* TimeInteractor.swift in Sources */, 280D71371ED6043D005D2689 /* ViewAutolayout.swift in Sources */, - 285465A7217858790052CB6A /* StoreView.swift in Sources */, 280D71381ED6043D005D2689 /* ViewController.swift in Sources */, - 2871951E2022FD14001C237E /* InputsScrollView.swift in Sources */, - 28FE18A5207B369800DF796E /* CocoaHookupCell.swift in Sources */, 280D71391ED6043D005D2689 /* ViewControllerStoryboard.swift in Sources */, - 2871952B20241B25001C237E /* TrackingView.swift in Sources */, 286BC00421909F85004D4CDD /* CloseDay.swift in Sources */, 284192DD2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, 28667B5D1FCB7017007B98E3 /* ModuleHookup.swift in Sources */, + 6D5BC5EA2C1C1970002DA29B /* JitCell.swift in Sources */, + 6D5BC5EB2C1C1970002DA29B /* TasksPresenter.swift in Sources */, + 6D5BC5EC2C1C1970002DA29B /* ClosedDayCell.swift in Sources */, + 6D5BC5ED2C1C1970002DA29B /* PlaceholderViewController.swift in Sources */, + 6D5BC5EE2C1C1970002DA29B /* ReportsPresenter.swift in Sources */, + 6D5BC5EF2C1C1970002DA29B /* WizardCalendarView.swift in Sources */, + 6D5BC5F02C1C1970002DA29B /* CocoaHookupPresenter.swift in Sources */, + 6D5BC5F12C1C1970002DA29B /* OutputsScrollView.swift in Sources */, + 6D5BC5F22C1C1970002DA29B /* AccountViewController.swift in Sources */, + 6D5BC5F32C1C1970002DA29B /* TaskCell.swift in Sources */, + 6D5BC5F42C1C1970002DA29B /* SettingsPresenter.swift in Sources */, + 6D5BC5F52C1C1970002DA29B /* ReportCellPresenter.swift in Sources */, + 6D5BC5F62C1C1970002DA29B /* TasksInteractor.swift in Sources */, + 6D5BC5F72C1C1970002DA29B /* ProjectsInteractor.swift in Sources */, + 6D5BC5F82C1C1970002DA29B /* TaskSuggestionViewController.swift in Sources */, + 6D5BC5F92C1C1970002DA29B /* ReportsViewController.swift in Sources */, + 6D5BC5FA2C1C1970002DA29B /* DataSource.swift in Sources */, + 6D5BC5FB2C1C1970002DA29B /* TimeBox.swift in Sources */, + 6D5BC5FC2C1C1970002DA29B /* HookupCell.swift in Sources */, + 6D5BC5FD2C1C1970002DA29B /* BrowserPresenter.swift in Sources */, + 6D5BC5FE2C1C1970002DA29B /* JiraTempoPresenter.swift in Sources */, + 6D5BC5FF2C1C1970002DA29B /* EditableTimeBox.swift in Sources */, + 6D5BC6002C1C1970002DA29B /* CalendarDayCellView.swift in Sources */, + 6D5BC6012C1C1970002DA29B /* CopyReportCell.swift in Sources */, + 6D5BC6022C1C1970002DA29B /* TrackingView.swift in Sources */, + 6D5BC6032C1C1970002DA29B /* SettingsInteractor.swift in Sources */, + 6D5BC6042C1C1970002DA29B /* BrowserCell.swift in Sources */, + 6D5BC6052C1C1970002DA29B /* InputsTableViewDataSource.swift in Sources */, + 6D5BC6062C1C1970002DA29B /* StoreView.swift in Sources */, + 6D5BC6072C1C1970002DA29B /* WizardGitView.swift in Sources */, + 6D5BC6082C1C1970002DA29B /* WizardViewController.swift in Sources */, + 6D5BC6092C1C1970002DA29B /* MainPresenter.swift in Sources */, + 6D5BC60A2C1C1970002DA29B /* InputsScrollView.swift in Sources */, + 6D5BC60B2C1C1970002DA29B /* CalendarViewController.swift in Sources */, + 6D5BC60C2C1C1970002DA29B /* CellProtocol.swift in Sources */, + 6D5BC60D2C1C1970002DA29B /* ReportCell.swift in Sources */, + 6D5BC60E2C1C1970002DA29B /* HookupPresenter.swift in Sources */, + 6D5BC60F2C1C1970002DA29B /* TaskCell_.swift in Sources */, + 6D5BC6102C1C1970002DA29B /* ReportsDataSource.swift in Sources */, + 6D5BC6112C1C1970002DA29B /* CalendarInteractor.swift in Sources */, + 6D5BC6122C1C1970002DA29B /* ProjectsPresenter.swift in Sources */, + 6D5BC6132C1C1970002DA29B /* CloudKitLoginViewController.swift in Sources */, + 6D5BC6142C1C1970002DA29B /* ProjectsViewController.swift in Sources */, + 6D5BC6152C1C1970002DA29B /* NewTaskViewController.swift in Sources */, + 6D5BC6162C1C1970002DA29B /* TaskSuggestionPresenter.swift in Sources */, + 6D5BC6172C1C1970002DA29B /* CalendarPresenter.swift in Sources */, + 6D5BC6182C1C1970002DA29B /* GitPresenter.swift in Sources */, + 6D5BC6192C1C1970002DA29B /* OutputTableViewDataSource.swift in Sources */, + 6D5BC61A2C1C1970002DA29B /* TaskSuggestionTests.swift in Sources */, + 6D5BC61B2C1C1970002DA29B /* ProjectDetailsViewController.swift in Sources */, + 6D5BC61C2C1C1970002DA29B /* SettingsViewController.swift in Sources */, + 6D5BC61D2C1C1970002DA29B /* JiraTempoCell.swift in Sources */, + 6D5BC61E2C1C1970002DA29B /* ProjectDetailsPresenter.swift in Sources */, + 6D5BC61F2C1C1970002DA29B /* TaskCellPresenter.swift in Sources */, + 6D5BC6202C1C1970002DA29B /* CloseDayCell.swift in Sources */, + 6D5BC6212C1C1970002DA29B /* Saveable.swift in Sources */, + 6D5BC6222C1C1970002DA29B /* ShellCell.swift in Sources */, + 6D5BC6232C1C1970002DA29B /* CalendarAppPresenter.swift in Sources */, + 6D5BC6242C1C1970002DA29B /* JirassicCell.swift in Sources */, + 6D5BC6252C1C1970002DA29B /* TasksDataSource.swift in Sources */, + 6D5BC6262C1C1970002DA29B /* WizardJiraView.swift in Sources */, + 6D5BC6272C1C1970002DA29B /* LoginViewController.swift in Sources */, + 6D5BC6282C1C1970002DA29B /* MonthReportsHeaderView.swift in Sources */, + 6D5BC6292C1C1970002DA29B /* CalendarCell.swift in Sources */, + 6D5BC62A2C1C1970002DA29B /* LoginPresenter.swift in Sources */, + 6D5BC62B2C1C1970002DA29B /* TimeBoxViewController.swift in Sources */, + 6D5BC62C2C1C1970002DA29B /* TasksView.swift in Sources */, + 6D5BC62D2C1C1970002DA29B /* GitCell.swift in Sources */, + 6D5BC62E2C1C1970002DA29B /* MainViewController.swift in Sources */, + 6D5BC62F2C1C1970002DA29B /* TasksScrollView.swift in Sources */, + 6D5BC6302C1C1970002DA29B /* CocoaHookupCell.swift in Sources */, + 6D5BC6312C1C1970002DA29B /* WizardAppleScriptView.swift in Sources */, + 6D5BC6322C1C1970002DA29B /* ProjectsListViewController.swift in Sources */, + 6D5BC6332C1C1970002DA29B /* TasksViewController.swift in Sources */, + 6D5BC6342C1C1970002DA29B /* WelcomeViewController.swift in Sources */, + 6D5BC6352C1C1970002DA29B /* WorklogsPresenter.swift in Sources */, + 6D5BC6362C1C1970002DA29B /* WorklogsViewController.swift in Sources */, 2812F61E2145031B008EE81E /* IAPHelper.swift in Sources */, 280D713A1ED6043D005D2689 /* Conversions.swift in Sources */, 280D713B1ED6043D005D2689 /* ComputerWakeUpInteractor.swift in Sources */, @@ -2117,16 +2320,12 @@ 280D71431ED6043D005D2689 /* TaskFinder.swift in Sources */, 280D71451ED6043D005D2689 /* TaskInteractor.swift in Sources */, 285465A2216C84E10052CB6A /* CreateMonthReport.swift in Sources */, - 56D90CF820876F1100F24442 /* WizardJiraView.swift in Sources */, 2801388F205F86460051B532 /* TimeBox.swift in Sources */, 280D71471ED6043D005D2689 /* TaskTypeEstimator.swift in Sources */, 280D71491ED6043D005D2689 /* TaskTypeSelection.swift in Sources */, - 2845B160206AE468006EFB3B /* TaskCell.swift in Sources */, - 28FE18AB207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */, 280D714A1ED6043D005D2689 /* PredictiveTimeTyping.swift in Sources */, 280D714C1ED6043D005D2689 /* RegisterUserInteractor.swift in Sources */, 280D714D1ED6043D005D2689 /* UserInteractor.swift in Sources */, - 28FB265120224E3A00AEA38D /* JiraTempoCell.swift in Sources */, 280D71591ED6043D005D2689 /* AppDelegate.swift in Sources */, 280D715A1ED6043D005D2689 /* AppWireframe.swift in Sources */, 280D715B1ED6043D005D2689 /* AppViewController.swift in Sources */, @@ -2136,71 +2335,33 @@ 2892E811208E6275004E5298 /* LocalPreferences.swift in Sources */, 280D715D1ED6043D005D2689 /* AppLauncher.swift in Sources */, 280D715E1ED6043D005D2689 /* Versioning.swift in Sources */, - 2845B14D206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */, - 2845B164206AE468006EFB3B /* TaskCellPresenter.swift in Sources */, 280D715F1ED6043D005D2689 /* AppTheme.swift in Sources */, 28CBB596204554A2006F9D3A /* ParseGitBranch.swift in Sources */, 280D71601ED6043D005D2689 /* MenuBarController.swift in Sources */, 280D71611ED6043D005D2689 /* MenuBarIconView.swift in Sources */, - 569C4C67202319930049FBF1 /* GitPresenter.swift in Sources */, - 2871951B2022ECF7001C237E /* OutputsScrollView.swift in Sources */, 280D71621ED6043D005D2689 /* FlipAnimation.swift in Sources */, 280D71631ED6043D005D2689 /* Animatable.swift in Sources */, - 564E55F3202883DB00CE4C76 /* WorklogsViewController.swift in Sources */, - 280D71641ED6043D005D2689 /* PlaceholderViewController.swift in Sources */, 28FE1887207A520B00DF796E /* NewTaskCommand.swift in Sources */, - 280D71661ED6043D005D2689 /* WelcomeViewController.swift in Sources */, - 280D71681ED6043D005D2689 /* TaskSuggestionViewController.swift in Sources */, - 280D71691ED6043D005D2689 /* TaskSuggestionPresenter.swift in Sources */, 2871952E2025C356001C237E /* CoreDataRepository+Tasks.swift in Sources */, 28A283B0203CB1A100DDCB63 /* MergeTasksInteractor.swift in Sources */, 2812F6202145031B008EE81E /* Store.swift in Sources */, - 280D716B1ED6043D005D2689 /* AccountViewController.swift in Sources */, - 56D90CFB20876F2B00F24442 /* WizardGitView.swift in Sources */, - 280D716C1ED6043D005D2689 /* CloudKitLoginViewController.swift in Sources */, 2871952D2025C353001C237E /* CoreDataRepository.swift in Sources */, - 280D716E1ED6043D005D2689 /* LoginPresenter.swift in Sources */, - 28FB265B20224EA700AEA38D /* HookupCell.swift in Sources */, 287195332025C367001C237E /* CUser.swift in Sources */, - 280D716F1ED6043D005D2689 /* LoginViewController.swift in Sources */, 2898D3B12185959300CF5AD4 /* EditableTimeBox.swift in Sources */, - 2845B149206AE3A8006EFB3B /* ReportCell.swift in Sources */, 2892B29E1F094B0D0085BAC2 /* JReport.swift in Sources */, 280D71701ED6043D005D2689 /* ExtensionsInteractor.swift in Sources */, 2845B13F2068351F006EFB3B /* TableViewCell.swift in Sources */, 288BB6872087169F00CF720A /* ViewXib.swift in Sources */, - 28A283972037975600DDCB63 /* Saveable.swift in Sources */, 280D71711ED6043D005D2689 /* ExtensionsInstallerInteractor.swift in Sources */, 280D71721ED6043D005D2689 /* AppleScript.swift in Sources */, - 28A283A52038AFB100DDCB63 /* JirassicCell.swift in Sources */, 280D71731ED6043D005D2689 /* SandboxedAppleScript.swift in Sources */, - 280D71741ED6043D005D2689 /* SettingsViewController.swift in Sources */, - 280D71751ED6043D005D2689 /* SettingsPresenter.swift in Sources */, - 280D71761ED6043D005D2689 /* SettingsInteractor.swift in Sources */, 280D71781ED6043D005D2689 /* NewTaskViewController.swift in Sources */, 28A283942037874600DDCB63 /* ModuleGitLogs.swift in Sources */, - 280D717A1ED6043D005D2689 /* TasksViewController.swift in Sources */, - 288BB6842087157F00CF720A /* WizardAppleScriptView.swift in Sources */, - 2845B151206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */, 28A283AB203A93AB00DDCB63 /* GitBranchParser.swift in Sources */, - 280D717B1ED6043D005D2689 /* TasksPresenter.swift in Sources */, 2818848121A4A2F800B33B9C /* Jirassic.xcdatamodeld in Sources */, - 2845B14F206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */, - 280D717C1ED6043D005D2689 /* CalendarScrollView.swift in Sources */, - 280D717D1ED6043D005D2689 /* TasksScrollView.swift in Sources */, - 280D717E1ED6043D005D2689 /* TasksDataSource.swift in Sources */, 28A2839F20385BE000DDCB63 /* GitCommitsParser.swift in Sources */, - 287195152022E1E2001C237E /* HookupPresenter.swift in Sources */, - 280D71811ED6043D005D2689 /* DataSource.swift in Sources */, 28C6A38421D359E60036DB29 /* RemoveDuplicate.swift in Sources */, - 280D71821ED6043D005D2689 /* CellProtocol.swift in Sources */, - 280D71831ED6043D005D2689 /* TasksView.swift in Sources */, - 2898D3A82181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */, - 2845B16A206AE468006EFB3B /* NonTaskCell.swift in Sources */, 2845B1352066C6E6006EFB3B /* AppleScriptProtocol.swift in Sources */, - 569C4C582023193B0049FBF1 /* ShellCell.swift in Sources */, - 569C4C64202319870049FBF1 /* GitCell.swift in Sources */, - 569C4C6D202319CA0049FBF1 /* BrowserPresenter.swift in Sources */, 280D718D1ED6043D005D2689 /* InternalNotifications.swift in Sources */, 280D718E1ED6043D005D2689 /* UserNotifications.swift in Sources */, 280D718F1ED6043D005D2689 /* SleepNotifications.swift in Sources */, @@ -2210,14 +2371,11 @@ 284192D72018841700E64A9A /* JProject.swift in Sources */, 280D719E1ED6043D005D2689 /* SqliteRepository+Tasks.swift in Sources */, 2892B2A71F094C800085BAC2 /* JWorkAttribute.swift in Sources */, - 2845B166206AE468006EFB3B /* TasksHeaderView.swift in Sources */, 28DCA4552018B6E700DFAE29 /* JProjectIssue.swift in Sources */, 280D719F1ED6043D005D2689 /* SqliteRepository+User.swift in Sources */, - 28EE1F5E20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, 280D71A01ED6043D005D2689 /* SqliteRepository+Settings.swift in Sources */, 280D71A11ED6043D005D2689 /* SSettings.swift in Sources */, 280D71A21ED6043D005D2689 /* STask.swift in Sources */, - 569C4C6A202319A20049FBF1 /* BrowserCell.swift in Sources */, 280D71A31ED6043D005D2689 /* SUser.swift in Sources */, 280D71A41ED6043D005D2689 /* SQLiteDB.swift in Sources */, 2871952F2025C359001C237E /* CoreDataRepository+Settings.swift in Sources */, @@ -2226,15 +2384,10 @@ 280D71A61ED6043D005D2689 /* SQLiteSchema.swift in Sources */, 280D71A71ED6043D005D2689 /* UserDefaults+uploadToken.swift in Sources */, 28279AD121BBF08C00376304 /* InMemoryCoreDataRepository.swift in Sources */, - 287195182022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, - 569C4C5E202319630049FBF1 /* JitCell.swift in Sources */, 28A2839A20382BBB00DDCB63 /* GitCommit.swift in Sources */, 284192DA2018855B00E64A9A /* JiraRepository+Projects.swift in Sources */, 565E19F020A5E336003A5E2A /* RCSync.swift in Sources */, 280D71B61ED6043D005D2689 /* Repository.swift in Sources */, - 564E55F6202884DE00CE4C76 /* WorklogsPresenter.swift in Sources */, - 287195212022FD87001C237E /* InputsTableViewDataSource.swift in Sources */, - 287B358720FBAFC40022F43E /* WizardCalendarView.swift in Sources */, 280D71B71ED6043D005D2689 /* RepositoryInteractor.swift in Sources */, 6D5BC4C82C1B5BE6002DA29B /* CreateDayReport.swift in Sources */, 280300D61EDB5ECA000A763E /* StatisticsInteractor.swift in Sources */, @@ -2256,7 +2409,6 @@ files = ( 2803A0CA1EC184FF005F9389 /* BrowserNotification.swift in Sources */, 28279AD621C6245700376304 /* GitUsersViewController.swift in Sources */, - 4065D3EE1DD3B44200B73201 /* TasksPresenter.swift in Sources */, 4065D3AB1DD3B44200B73201 /* ComputerWakeUpInteractor.swift in Sources */, 2892B29F1F094B0D0085BAC2 /* JReport.swift in Sources */, 28A928811E910DA40022AB55 /* SqliteRepository+Tasks.swift in Sources */, @@ -2265,26 +2417,19 @@ 2898D3B22185959300CF5AD4 /* EditableTimeBox.swift in Sources */, 4065D39F1DD3B44200B73201 /* Task.swift in Sources */, 4065D3CA1DD3B44200B73201 /* AppWireframe.swift in Sources */, - 569C4C5F202319630049FBF1 /* JitCell.swift in Sources */, 4065D3C91DD3B44200B73201 /* AppDelegate.swift in Sources */, 405B15661DEF75660009871C /* AppViewController.swift in Sources */, 6D5BC4C92C1B5BE6002DA29B /* CreateDayReport.swift in Sources */, - 569C4C65202319870049FBF1 /* GitCell.swift in Sources */, 287195372027CAD9001C237E /* TimeInteractor.swift in Sources */, - 287195192022E8BC001C237E /* OutputTableViewDataSource.swift in Sources */, 28577EAD1E8ADC53002B07FD /* SSettings.swift in Sources */, 28577EB51E8AF08F002B07FD /* SQLiteDB.swift in Sources */, 2898D3AC2184D1DB00CF5AD4 /* TimeBoxViewController.swift in Sources */, 286BC00521909F85004D4CDD /* CloseDay.swift in Sources */, 28A928951E9110980022AB55 /* CloudKitRepository+User.swift in Sources */, - 4065D3EF1DD3B44200B73201 /* TasksScrollView.swift in Sources */, - 4065D3E01DD3B44200B73201 /* SettingsPresenter.swift in Sources */, - 569C4C6B202319A20049FBF1 /* BrowserCell.swift in Sources */, 4065D3A31DD3B44200B73201 /* DateExtension.swift in Sources */, 28AA500B1EDCD51300AAF03D /* CoreDataRepository+User.swift in Sources */, 4065D3D81DD3B44200B73201 /* InternalNotifications.swift in Sources */, 4065D3FA1DD3B44200B73201 /* RepositoryInteractor.swift in Sources */, - 2898D3A92181907700CF5AD4 /* MonthReportsHeaderView.swift in Sources */, 28DCA4562018B6E700DFAE29 /* JProjectIssue.swift in Sources */, 28A928871E910EA70022AB55 /* SqliteRepository+Settings.swift in Sources */, 28FE1888207A520B00DF796E /* NewTaskCommand.swift in Sources */, @@ -2294,31 +2439,21 @@ 4065D3B21DD3B44200B73201 /* TaskInteractor.swift in Sources */, 4065D3A21DD3B44200B73201 /* Week.swift in Sources */, 28AFE7311E9A594500BAAD8C /* UserDefaults+token.swift in Sources */, - 4065D3F01DD3B44200B73201 /* TasksViewController.swift in Sources */, - 5685C76A1DE8721100CA545E /* LoginViewController.swift in Sources */, - 569C4C592023193B0049FBF1 /* ShellCell.swift in Sources */, - 2845B14E206AE3A8006EFB3B /* ReportCellPresenter.swift in Sources */, 2823C9351E4F69970055D036 /* Versioning.swift in Sources */, 4065D3A11DD3B44200B73201 /* User.swift in Sources */, 4065D3A71DD3B44200B73201 /* ViewController.swift in Sources */, 4065D3BB1DD3B44200B73201 /* UserInteractor.swift in Sources */, - 4065D3E31DD3B44200B73201 /* CellProtocol.swift in Sources */, 2812F61F2145031B008EE81E /* IAPHelper.swift in Sources */, 4065D3A61DD3B44200B73201 /* ViewAutolayout.swift in Sources */, 28CBB597204554A2006F9D3A /* ParseGitBranch.swift in Sources */, 28667B5E1FCB7017007B98E3 /* ModuleHookup.swift in Sources */, 40FCE4271DF7646F00D4FD45 /* SandboxedAppleScript.swift in Sources */, 4065D3B81DD3B44200B73201 /* PredictiveTimeTyping.swift in Sources */, - 2845B14A206AE3A8006EFB3B /* ReportCell.swift in Sources */, 288BB6882087169F00CF720A /* ViewXib.swift in Sources */, 5683DC3F20ECDED30000A138 /* ModuleCalendar.swift in Sources */, 28C6A38521D359E60036DB29 /* RemoveDuplicate.swift in Sources */, 284192DB2018855B00E64A9A /* JiraRepository+Projects.swift in Sources */, 28A928971E9110BF0022AB55 /* CloudKitRepository+Settings.swift in Sources */, - 564E55F4202883DB00CE4C76 /* WorklogsViewController.swift in Sources */, - 288BB6852087157F00CF720A /* WizardAppleScriptView.swift in Sources */, - 2871951F2022FD14001C237E /* InputsScrollView.swift in Sources */, - 4065D3E11DD3B44200B73201 /* SettingsViewController.swift in Sources */, 4065D3C81DD3B44200B73201 /* FlipAnimation.swift in Sources */, 4065D39E1DD3B44200B73201 /* Settings.swift in Sources */, 28A283B1203CB1A100DDCB63 /* MergeTasksInteractor.swift in Sources */, @@ -2327,29 +2462,18 @@ 28C9C6221EAD37D0007EB3E6 /* UserDefaults+uploadToken.swift in Sources */, 284192DE2018A8B200E64A9A /* ModuleJiraTempo.swift in Sources */, 4065D3BA1DD3B44200B73201 /* RegisterUserInteractor.swift in Sources */, - 28FE18A6207B369800DF796E /* CocoaHookupCell.swift in Sources */, 4065D3B41DD3B44200B73201 /* TaskTypeEstimator.swift in Sources */, - 285465A8217858790052CB6A /* StoreView.swift in Sources */, 4065D3D91DD3B44200B73201 /* UserNotifications.swift in Sources */, - 569C4C6E202319CA0049FBF1 /* BrowserPresenter.swift in Sources */, 4065D3A51DD3B44200B73201 /* StringIdGenerator.swift in Sources */, - 2845B150206AE3A8006EFB3B /* ReportsDataSource.swift in Sources */, 28577EAB1E8ADC53002B07FD /* SqliteRepository.swift in Sources */, - 28A283982037975600DDCB63 /* Saveable.swift in Sources */, 28AA50081EDCD51300AAF03D /* CoreDataRepository.swift in Sources */, 280300D71EDB5ECA000A763E /* StatisticsInteractor.swift in Sources */, - 28A283A62038AFB100DDCB63 /* JirassicCell.swift in Sources */, - 4065D3DF1DD3B44200B73201 /* SettingsInteractor.swift in Sources */, 28577EAF1E8ADC53002B07FD /* STask.swift in Sources */, - 28A5F2821E586426002BE564 /* DataSource.swift in Sources */, 28A928931E91104B0022AB55 /* CloudKitRepository+Tasks.swift in Sources */, - 566B9FA8217DE67800EAF324 /* TasksInteractor.swift in Sources */, 4065D3B01DD3B44200B73201 /* TaskFinder.swift in Sources */, 28A283952037874600DDCB63 /* ModuleGitLogs.swift in Sources */, 284192D82018841700E64A9A /* JProject.swift in Sources */, - 56D90CF920876F1100F24442 /* WizardJiraView.swift in Sources */, 28279AD221BBF08E00376304 /* InMemoryCoreDataRepository.swift in Sources */, - 28EDE9391E59EC1500B360A4 /* TasksView.swift in Sources */, 28A283AC203A93AB00DDCB63 /* GitBranchParser.swift in Sources */, 40FCE4301DFD7C8D00D4FD45 /* Animatable.swift in Sources */, 4065D3F21DD3B44200B73201 /* CloudKitRepository.swift in Sources */, @@ -2359,64 +2483,112 @@ 28A283A020385BE000DDCB63 /* GitCommitsParser.swift in Sources */, 4065D3DA1DD3B44200B73201 /* SleepNotifications.swift in Sources */, 280F507D1EC868B0007416AB /* StringArray.swift in Sources */, - 569C4C68202319930049FBF1 /* GitPresenter.swift in Sources */, - 28FB265C20224EA700AEA38D /* HookupCell.swift in Sources */, 2845B13A206703C5006EFB3B /* Keychain.swift in Sources */, 28AA500D1EDCD51300AAF03D /* CTask.swift in Sources */, 4065D3A81DD3B44200B73201 /* ViewControllerStoryboard.swift in Sources */, 28AA500C1EDCD51300AAF03D /* CSettings.swift in Sources */, - 5685C7691DE8721100CA545E /* LoginPresenter.swift in Sources */, - 2871952C20241B25001C237E /* TrackingView.swift in Sources */, 40FCE4261DF7646F00D4FD45 /* ExtensionsInteractor.swift in Sources */, 4051D5F91E0EA48A002042BB /* AppLauncher.swift in Sources */, - 28EE1F5F20EE8D1000C5C1D6 /* CalendarCell.swift in Sources */, 56ADBF6B21C3F625008350A6 /* GitUser.swift in Sources */, - 2845B167206AE468006EFB3B /* TasksHeaderView.swift in Sources */, 28AA500A1EDCD51300AAF03D /* CoreDataRepository+Settings.swift in Sources */, 280D70B11ECC09D9005D2689 /* AppTheme.swift in Sources */, - 5685C7671DE8721100CA545E /* CloudKitLoginViewController.swift in Sources */, 28AA500E1EDCD51300AAF03D /* CUser.swift in Sources */, - 2845B165206AE468006EFB3B /* TaskCellPresenter.swift in Sources */, - 2871951C2022ECF7001C237E /* OutputsScrollView.swift in Sources */, 2845B1362066C6E6006EFB3B /* AppleScriptProtocol.swift in Sources */, 28577EB11E8ADC53002B07FD /* SUser.swift in Sources */, 4065D3DE1DD3B44200B73201 /* NewTaskViewController.swift in Sources */, 4065D39C1DD3B44200B73201 /* Day.swift in Sources */, - 56D90CFC20876F2B00F24442 /* WizardGitView.swift in Sources */, - 40FCE42C1DFC3F8700D4FD45 /* WelcomeViewController.swift in Sources */, - 28EE1F6520EE92DA00C5C1D6 /* CalendarPresenter.swift in Sources */, - 2845B16B206AE468006EFB3B /* NonTaskCell.swift in Sources */, 4065D39D1DD3B44200B73201 /* Report.swift in Sources */, 40FCE4251DF7646F00D4FD45 /* AppleScript.swift in Sources */, 28A928781E8F78580022AB55 /* SQLiteSchema.swift in Sources */, - 28FE18AC207B375A00DF796E /* CocoaHookupPresenter.swift in Sources */, 4065D3AF1DD3B44200B73201 /* ReadTasksInteractor.swift in Sources */, 565E19F120A5E336003A5E2A /* RCSync.swift in Sources */, 28AA50091EDCD51300AAF03D /* CoreDataRepository+Tasks.swift in Sources */, - 287195162022E1E2001C237E /* HookupPresenter.swift in Sources */, 2892E812208E6275004E5298 /* LocalPreferences.swift in Sources */, - 4065D3E21DD3B44200B73201 /* CalendarScrollView.swift in Sources */, 4065D3AC1DD3B44200B73201 /* CreateReport.swift in Sources */, - 2845B161206AE468006EFB3B /* TaskCell.swift in Sources */, - 28FB265F2022DC2B00AEA38D /* JiraTempoPresenter.swift in Sources */, - 287195222022FD87001C237E /* InputsTableViewDataSource.swift in Sources */, 28577EB71E8AF08F002B07FD /* SQLTable.swift in Sources */, 28A928841E910E5E0022AB55 /* SqliteRepository+User.swift in Sources */, - 288BB67F2085252900CF720A /* WizardViewController.swift in Sources */, 2892B2A81F094C800085BAC2 /* JWorkAttribute.swift in Sources */, - 5685C76C1DE8724400CA545E /* AccountViewController.swift in Sources */, - 2845B152206AE3A8006EFB3B /* ReportsHeaderView.swift in Sources */, 2892B2991F094A170085BAC2 /* JiraRepository.swift in Sources */, 28A2839B20382BBB00DDCB63 /* GitCommit.swift in Sources */, 4065D3A91DD3B44200B73201 /* Conversions.swift in Sources */, - 405B15611DEF3D080009871C /* TaskSuggestionViewController.swift in Sources */, - 564E55F7202884DE00CE4C76 /* WorklogsPresenter.swift in Sources */, - 28279E9C1E8300E200EAF9FC /* PlaceholderViewController.swift in Sources */, - 28FB265220224E3A00AEA38D /* JiraTempoCell.swift in Sources */, - 287B358820FBAFC40022F43E /* WizardCalendarView.swift in Sources */, - 405B15631DEF3F2A0009871C /* TaskSuggestionPresenter.swift in Sources */, + 6D5BC6582C1C1970002DA29B /* JitCell.swift in Sources */, + 6D5BC6592C1C1970002DA29B /* TasksPresenter.swift in Sources */, + 6D5BC65A2C1C1970002DA29B /* ClosedDayCell.swift in Sources */, + 6D5BC65B2C1C1970002DA29B /* PlaceholderViewController.swift in Sources */, + 6D5BC65C2C1C1970002DA29B /* ReportsPresenter.swift in Sources */, + 6D5BC65D2C1C1970002DA29B /* WizardCalendarView.swift in Sources */, + 6D5BC65E2C1C1970002DA29B /* CocoaHookupPresenter.swift in Sources */, + 6D5BC65F2C1C1970002DA29B /* OutputsScrollView.swift in Sources */, + 6D5BC6602C1C1970002DA29B /* AccountViewController.swift in Sources */, + 6D5BC6612C1C1970002DA29B /* TaskCell.swift in Sources */, + 6D5BC6622C1C1970002DA29B /* SettingsPresenter.swift in Sources */, + 6D5BC6632C1C1970002DA29B /* ReportCellPresenter.swift in Sources */, + 6D5BC6642C1C1970002DA29B /* TasksInteractor.swift in Sources */, + 6D5BC6652C1C1970002DA29B /* ProjectsInteractor.swift in Sources */, + 6D5BC6662C1C1970002DA29B /* TaskSuggestionViewController.swift in Sources */, + 6D5BC6672C1C1970002DA29B /* ReportsViewController.swift in Sources */, + 6D5BC6682C1C1970002DA29B /* DataSource.swift in Sources */, + 6D5BC6692C1C1970002DA29B /* TimeBox.swift in Sources */, + 6D5BC66A2C1C1970002DA29B /* HookupCell.swift in Sources */, + 6D5BC66B2C1C1970002DA29B /* BrowserPresenter.swift in Sources */, + 6D5BC66C2C1C1970002DA29B /* JiraTempoPresenter.swift in Sources */, + 6D5BC66D2C1C1970002DA29B /* EditableTimeBox.swift in Sources */, + 6D5BC66E2C1C1970002DA29B /* CalendarDayCellView.swift in Sources */, + 6D5BC66F2C1C1970002DA29B /* CopyReportCell.swift in Sources */, + 6D5BC6702C1C1970002DA29B /* TrackingView.swift in Sources */, + 6D5BC6712C1C1970002DA29B /* SettingsInteractor.swift in Sources */, + 6D5BC6722C1C1970002DA29B /* BrowserCell.swift in Sources */, + 6D5BC6732C1C1970002DA29B /* InputsTableViewDataSource.swift in Sources */, + 6D5BC6742C1C1970002DA29B /* StoreView.swift in Sources */, + 6D5BC6752C1C1970002DA29B /* WizardGitView.swift in Sources */, + 6D5BC6762C1C1970002DA29B /* WizardViewController.swift in Sources */, + 6D5BC6772C1C1970002DA29B /* MainPresenter.swift in Sources */, + 6D5BC6782C1C1970002DA29B /* InputsScrollView.swift in Sources */, + 6D5BC6792C1C1970002DA29B /* CalendarViewController.swift in Sources */, + 6D5BC67A2C1C1970002DA29B /* CellProtocol.swift in Sources */, + 6D5BC67B2C1C1970002DA29B /* ReportCell.swift in Sources */, + 6D5BC67C2C1C1970002DA29B /* HookupPresenter.swift in Sources */, + 6D5BC67D2C1C1970002DA29B /* TaskCell_.swift in Sources */, + 6D5BC67E2C1C1970002DA29B /* ReportsDataSource.swift in Sources */, + 6D5BC67F2C1C1970002DA29B /* CalendarInteractor.swift in Sources */, + 6D5BC6802C1C1970002DA29B /* ProjectsPresenter.swift in Sources */, + 6D5BC6812C1C1970002DA29B /* CloudKitLoginViewController.swift in Sources */, + 6D5BC6822C1C1970002DA29B /* ProjectsViewController.swift in Sources */, + 6D5BC6832C1C1970002DA29B /* NewTaskViewController.swift in Sources */, + 6D5BC6842C1C1970002DA29B /* TaskSuggestionPresenter.swift in Sources */, + 6D5BC6852C1C1970002DA29B /* CalendarPresenter.swift in Sources */, + 6D5BC6862C1C1970002DA29B /* GitPresenter.swift in Sources */, + 6D5BC6872C1C1970002DA29B /* OutputTableViewDataSource.swift in Sources */, + 6D5BC6882C1C1970002DA29B /* TaskSuggestionTests.swift in Sources */, + 6D5BC6892C1C1970002DA29B /* ProjectDetailsViewController.swift in Sources */, + 6D5BC68A2C1C1970002DA29B /* SettingsViewController.swift in Sources */, + 6D5BC68B2C1C1970002DA29B /* JiraTempoCell.swift in Sources */, + 6D5BC68C2C1C1970002DA29B /* ProjectDetailsPresenter.swift in Sources */, + 6D5BC68D2C1C1970002DA29B /* TaskCellPresenter.swift in Sources */, + 6D5BC68E2C1C1970002DA29B /* CloseDayCell.swift in Sources */, + 6D5BC68F2C1C1970002DA29B /* Saveable.swift in Sources */, + 6D5BC6902C1C1970002DA29B /* ShellCell.swift in Sources */, + 6D5BC6912C1C1970002DA29B /* CalendarAppPresenter.swift in Sources */, + 6D5BC6922C1C1970002DA29B /* JirassicCell.swift in Sources */, + 6D5BC6932C1C1970002DA29B /* TasksDataSource.swift in Sources */, + 6D5BC6942C1C1970002DA29B /* WizardJiraView.swift in Sources */, + 6D5BC6952C1C1970002DA29B /* LoginViewController.swift in Sources */, + 6D5BC6962C1C1970002DA29B /* MonthReportsHeaderView.swift in Sources */, + 6D5BC6972C1C1970002DA29B /* CalendarCell.swift in Sources */, + 6D5BC6982C1C1970002DA29B /* LoginPresenter.swift in Sources */, + 6D5BC6992C1C1970002DA29B /* TimeBoxViewController.swift in Sources */, + 6D5BC69A2C1C1970002DA29B /* TasksView.swift in Sources */, + 6D5BC69B2C1C1970002DA29B /* GitCell.swift in Sources */, + 6D5BC69C2C1C1970002DA29B /* MainViewController.swift in Sources */, + 6D5BC69D2C1C1970002DA29B /* TasksScrollView.swift in Sources */, + 6D5BC69E2C1C1970002DA29B /* CocoaHookupCell.swift in Sources */, + 6D5BC69F2C1C1970002DA29B /* WizardAppleScriptView.swift in Sources */, + 6D5BC6A02C1C1970002DA29B /* ProjectsListViewController.swift in Sources */, + 6D5BC6A12C1C1970002DA29B /* TasksViewController.swift in Sources */, + 6D5BC6A22C1C1970002DA29B /* WelcomeViewController.swift in Sources */, + 6D5BC6A32C1C1970002DA29B /* WorklogsPresenter.swift in Sources */, + 6D5BC6A42C1C1970002DA29B /* WorklogsViewController.swift in Sources */, 4065D3D61DD3B44200B73201 /* MenuBarController.swift in Sources */, - 28A5F27E1E5789FC002BE564 /* TasksDataSource.swift in Sources */, 4065D3B61DD3B44200B73201 /* TaskTypeSelection.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2429,12 +2601,10 @@ 56D069E1216CABCB000D051D /* CreateMonthReportTests.swift in Sources */, 287558451EFE5969009A2503 /* ReadDaysInteractorTests.swift in Sources */, 4065D4241DD4562900B73201 /* CreateReportTests.swift in Sources */, - 2845B16E206AE46F006EFB3B /* TaskCellTests.swift in Sources */, 4073184F1DE9B1B40046F409 /* ComputerWakeUpInteractorTests.swift in Sources */, 28A283AE203C069F00DDCB63 /* GitBranchParserTests.swift in Sources */, 4065D4271DD4562900B73201 /* TaskTypeEstimatorTests.swift in Sources */, 28CBB59A20474755006F9D3A /* ParseGitBranchTests.swift in Sources */, - 40FCE4291DFC1CB400D4FD45 /* TaskSuggestionTests.swift in Sources */, 4065D4211DD4562100B73201 /* PredictiveTimeTypingTests.swift in Sources */, 28A283A220385F8C00DDCB63 /* GitCommitsParserTests.swift in Sources */, 4065D4251DD4562900B73201 /* TaskFinderTests.swift in Sources */, @@ -2531,6 +2701,14 @@ path = ..; sourceTree = ""; }; + 6D5BC5452C1C1970002DA29B /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 6D5BC5442C1C1970002DA29B /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */