├── CHANGELOG.md ├── _config.yml ├── Cartfile ├── Freelancer ├── Assets.xcassets │ ├── Contents.json │ ├── Buttons │ │ ├── Contents.json │ │ ├── Today.imageset │ │ │ ├── today-button.png │ │ │ └── Contents.json │ │ ├── Weekly.imageset │ │ │ ├── weekly-button.png │ │ │ └── Contents.json │ │ ├── Earnings.imageset │ │ │ ├── earnings-button.png │ │ │ └── Contents.json │ │ └── Preferences.imageset │ │ │ ├── preferences-button.png │ │ │ └── Contents.json │ ├── Notification Window │ │ ├── Contents.json │ │ └── icons8-time_card.imageset │ │ │ ├── icons8-time_card.png │ │ │ └── Contents.json │ ├── 32x32.imageset │ │ ├── 32x32.png │ │ ├── 32x32@2x.png │ │ └── Contents.json │ ├── 128x128.imageset │ │ ├── 128x128.png │ │ ├── 128x128@2x.png │ │ └── Contents.json │ ├── 256x256.imageset │ │ ├── 256x256.png │ │ ├── 256x256@2x.png │ │ └── Contents.json │ ├── 512x512.imageset │ │ ├── 512x512.png │ │ ├── 512x512@2x.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── 16x16.png │ │ ├── 32x32.png │ │ ├── 128x128.png │ │ ├── 16x16@2x.png │ │ ├── 256x256.png │ │ ├── 32x32@2x.png │ │ ├── 512x512.png │ │ ├── 128x128@2x.png │ │ ├── 256x256@2x.png │ │ ├── 512x512@2x.png │ │ └── Contents.json │ ├── AllScreens.imageset │ │ ├── AllScreens.png │ │ └── Contents.json │ ├── icons8-play.imageset │ │ ├── icons8-play.png │ │ ├── icons8-play-1.png │ │ ├── icons8-play-2.png │ │ └── Contents.json │ ├── icons8-save.imageset │ │ ├── icons8-save.png │ │ ├── icons8-save-1.png │ │ ├── icons8-save-2.png │ │ └── Contents.json │ ├── icons8-task.imageset │ │ ├── icons8-task.png │ │ ├── icons8-task-1.png │ │ ├── icons8-task-2.png │ │ └── Contents.json │ ├── icons8-pause.imageset │ │ ├── icons8-pause.png │ │ ├── icons8-pause-1.png │ │ ├── icons8-pause-2.png │ │ └── Contents.json │ ├── icons8-tasks.imageset │ │ ├── icons8-tasks.png │ │ ├── icons8-tasks-1.png │ │ ├── icons8-tasks-2.png │ │ └── Contents.json │ ├── icons8-cancel.imageset │ │ ├── icons8-cancel.png │ │ ├── icons8-cancel-1.png │ │ ├── icons8-cancel-2.png │ │ └── Contents.json │ ├── icons8-serbia.imageset │ │ ├── icons8-serbia.png │ │ ├── icons8-serbia-1.png │ │ ├── icons8-serbia-2.png │ │ └── Contents.json │ ├── icons8-timer.imageset │ │ ├── icons8-timer-2.png │ │ └── Contents.json │ ├── clock_filled-50.imageset │ │ ├── clock_filled-50.png │ │ └── Contents.json │ ├── icons8-day_view.imageset │ │ ├── icons8-day_view.png │ │ ├── icons8-day_view-1.png │ │ ├── icons8-day_view-2.png │ │ └── Contents.json │ ├── icons8-overtime.imageset │ │ ├── icons8-overtime.png │ │ ├── icons8-overtime-1.png │ │ ├── icons8-overtime-2.png │ │ └── Contents.json │ ├── icons8-private.imageset │ │ ├── icons8-private-1.png │ │ ├── icons8-private-2.png │ │ ├── icons8-private.png │ │ └── Contents.json │ ├── icons8-project.imageset │ │ ├── icons8-project.png │ │ ├── icons8-group_of_projects-1.png │ │ ├── icons8-group_of_projects.png │ │ └── Contents.json │ ├── icons8-settings.imageset │ │ ├── icons8-settings.png │ │ ├── icons8-settings-1.png │ │ ├── icons8-settings-2.png │ │ └── Contents.json │ ├── icons8-shutdown.imageset │ │ ├── icons8-shutdown.png │ │ ├── icons8-shutdown-1.png │ │ ├── icons8-shutdown-2.png │ │ └── Contents.json │ ├── timer_filled-50.imageset │ │ ├── timer_filled-50.png │ │ └── Contents.json │ ├── future_filled-50.imageset │ │ ├── future_filled-50.png │ │ └── Contents.json │ ├── icons8-stopwatch.imageset │ │ ├── icons8-stopwatch.png │ │ ├── icons8-stopwatch-1.png │ │ ├── icons8-stopwatch-2.png │ │ └── Contents.json │ ├── icons8-time_card.imageset │ │ ├── icons8-time_card.png │ │ └── Contents.json │ ├── icons8-week_view.imageset │ │ ├── icons8-week_view.png │ │ ├── icons8-week_view-1.png │ │ ├── icons8-week_view-2.png │ │ └── Contents.json │ ├── icons8-calculator.imageset │ │ ├── icons8-calculator.png │ │ ├── icons8-calculator-1.png │ │ ├── icons8-calculator-2.png │ │ └── Contents.json │ ├── icons8-month_view.imageset │ │ ├── icons8-month_view.png │ │ ├── icons8-month_view-1.png │ │ ├── icons8-month_view-2.png │ │ └── Contents.json │ ├── icons8-play_round.imageset │ │ ├── icons8-play_round.png │ │ ├── icons8-play_round-1.png │ │ ├── icons8-play_round-2.png │ │ └── Contents.json │ ├── icons8-sleep_mode.imageset │ │ ├── icons8-sleep_mode.png │ │ ├── icons8-sleep_mode-1.png │ │ ├── icons8-sleep_mode-2.png │ │ └── Contents.json │ ├── icons8-time_card-1.imageset │ │ ├── icons8-time_card.png │ │ ├── icons8-time_card-1.png │ │ ├── icons8-time_card-2.png │ │ └── Contents.json │ ├── icons8-filled_like.imageset │ │ ├── icons8-filled_like.png │ │ └── Contents.json │ ├── icons8-free_shipping.imageset │ │ ├── icons8-free_shipping-1.png │ │ ├── icons8-free_shipping-2.png │ │ └── Contents.json │ ├── icons8-verified_account.imageset │ │ ├── icons8-verified_account.png │ │ ├── icons8-verified_account-1.png │ │ ├── icons8-verified_account-2.png │ │ └── Contents.json │ ├── empty_hourglass_filled-50.imageset │ │ ├── empty_hourglass_filled-50.png │ │ └── Contents.json │ ├── sand-clock-ready-to-start.imageset │ │ ├── sand-clock-ready-to-start.png │ │ └── Contents.json │ └── icons8-split_payment_copy_2.imageset │ │ ├── icons8-split_payment_copy_2.png │ │ ├── icons8-split_payment_copy_2-1.png │ │ ├── icons8-split_payment_copy_2-2.png │ │ └── Contents.json ├── Freelancer.entitlements ├── Freelancer.xcdatamodeld │ ├── .xccurrentversion │ └── Freelancer.xcdatamodel │ │ └── contents ├── Extensions │ ├── FD.swift │ ├── Extensions.swift │ └── temp.swift ├── Controller │ ├── Timer │ │ ├── AcknowledgmentsViewController.swift │ │ └── TimerController.swift │ ├── AboutViewController.swift │ ├── EventMonitor.swift │ ├── OnBoarding │ │ └── OnBoardingViewController.swift │ ├── HyperlinkTextField.swift │ ├── Earnings │ │ └── EarningsViewController.swift │ ├── ViewController.swift │ ├── Notification │ │ └── NotificationViewController.swift │ ├── Preferences │ │ └── GeneralPreferenceViewController.swift │ └── Time Sheets │ │ ├── TodayTimeSheetViewController.swift │ │ └── WeekTimeSheetViewController.swift ├── Model │ ├── CustomAnimator.swift │ ├── FLTimerProtocol.swift │ └── Preferences.swift ├── Info.plist ├── Storyboard │ ├── Notification.storyboard │ ├── Timer.storyboard │ └── OnBoarding.storyboard └── AppDelegate.swift ├── Freelancer.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── nikola.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "stephencelis/SQLite.swift" 2 | github "malcommac/SwiftDate" 3 | 4 | -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Notification Window/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/32x32.imageset/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/32x32.imageset/32x32.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/128x128.imageset/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/128x128.imageset/128x128.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/256x256.imageset/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/256x256.imageset/256x256.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/32x32.imageset/32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/32x32.imageset/32x32@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/512x512.imageset/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/512x512.imageset/512x512.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/16x16.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/32x32.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/128x128.imageset/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/128x128.imageset/128x128@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/256x256.imageset/256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/256x256.imageset/256x256@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/512x512.imageset/512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/512x512.imageset/512x512@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/128x128.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/16x16@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/256x256.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/32x32@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/512x512.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AllScreens.imageset/AllScreens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AllScreens.imageset/AllScreens.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/128x128@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/256x256@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/AppIcon.appiconset/512x512@2x.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play.imageset/icons8-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-play.imageset/icons8-play.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-save.imageset/icons8-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-save.imageset/icons8-save.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-task.imageset/icons8-task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-task.imageset/icons8-task.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Today.imageset/today-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/Buttons/Today.imageset/today-button.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-pause.imageset/icons8-pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-pause.imageset/icons8-pause.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play.imageset/icons8-play-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-play.imageset/icons8-play-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play.imageset/icons8-play-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-play.imageset/icons8-play-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-save.imageset/icons8-save-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-save.imageset/icons8-save-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-save.imageset/icons8-save-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-save.imageset/icons8-save-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-task.imageset/icons8-task-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-task.imageset/icons8-task-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-task.imageset/icons8-task-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-task.imageset/icons8-task-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-tasks.imageset/icons8-tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-tasks.imageset/icons8-tasks.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Weekly.imageset/weekly-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/Buttons/Weekly.imageset/weekly-button.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-cancel.imageset/icons8-cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-cancel.imageset/icons8-cancel.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-pause.imageset/icons8-pause-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-pause.imageset/icons8-pause-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-pause.imageset/icons8-pause-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-pause.imageset/icons8-pause-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-serbia.imageset/icons8-serbia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-serbia.imageset/icons8-serbia.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-tasks.imageset/icons8-tasks-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-tasks.imageset/icons8-tasks-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-tasks.imageset/icons8-tasks-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-tasks.imageset/icons8-tasks-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-timer.imageset/icons8-timer-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-timer.imageset/icons8-timer-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/clock_filled-50.imageset/clock_filled-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/clock_filled-50.imageset/clock_filled-50.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-cancel.imageset/icons8-cancel-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-cancel.imageset/icons8-cancel-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-cancel.imageset/icons8-cancel-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-cancel.imageset/icons8-cancel-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-day_view.imageset/icons8-day_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-day_view.imageset/icons8-day_view.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-overtime.imageset/icons8-overtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-overtime.imageset/icons8-overtime.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-private.imageset/icons8-private-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-private.imageset/icons8-private-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-private.imageset/icons8-private-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-private.imageset/icons8-private-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-private.imageset/icons8-private.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-private.imageset/icons8-private.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-project.imageset/icons8-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-project.imageset/icons8-project.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-serbia.imageset/icons8-serbia-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-serbia.imageset/icons8-serbia-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-serbia.imageset/icons8-serbia-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-serbia.imageset/icons8-serbia-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-settings.imageset/icons8-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-settings.imageset/icons8-settings.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-shutdown.imageset/icons8-shutdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-shutdown.imageset/icons8-shutdown.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/timer_filled-50.imageset/timer_filled-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/timer_filled-50.imageset/timer_filled-50.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Earnings.imageset/earnings-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/Buttons/Earnings.imageset/earnings-button.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/future_filled-50.imageset/future_filled-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/future_filled-50.imageset/future_filled-50.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-day_view.imageset/icons8-day_view-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-day_view.imageset/icons8-day_view-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-day_view.imageset/icons8-day_view-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-day_view.imageset/icons8-day_view-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-overtime.imageset/icons8-overtime-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-overtime.imageset/icons8-overtime-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-overtime.imageset/icons8-overtime-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-overtime.imageset/icons8-overtime-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-settings.imageset/icons8-settings-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-settings.imageset/icons8-settings-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-settings.imageset/icons8-settings-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-settings.imageset/icons8-settings-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-shutdown.imageset/icons8-shutdown-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-shutdown.imageset/icons8-shutdown-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-shutdown.imageset/icons8-shutdown-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-shutdown.imageset/icons8-shutdown-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-stopwatch.imageset/icons8-stopwatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-stopwatch.imageset/icons8-stopwatch.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-time_card.imageset/icons8-time_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-time_card.imageset/icons8-time_card.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-week_view.imageset/icons8-week_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-week_view.imageset/icons8-week_view.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-calculator.imageset/icons8-calculator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-calculator.imageset/icons8-calculator.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-month_view.imageset/icons8-month_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-month_view.imageset/icons8-month_view.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play_round.imageset/icons8-play_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-play_round.imageset/icons8-play_round.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-sleep_mode.imageset/icons8-sleep_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-sleep_mode.imageset/icons8-sleep_mode.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-stopwatch.imageset/icons8-stopwatch-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-stopwatch.imageset/icons8-stopwatch-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-stopwatch.imageset/icons8-stopwatch-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-stopwatch.imageset/icons8-stopwatch-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-time_card-1.imageset/icons8-time_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-time_card-1.imageset/icons8-time_card.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-week_view.imageset/icons8-week_view-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-week_view.imageset/icons8-week_view-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-week_view.imageset/icons8-week_view-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-week_view.imageset/icons8-week_view-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Preferences.imageset/preferences-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/Buttons/Preferences.imageset/preferences-button.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-calculator.imageset/icons8-calculator-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-calculator.imageset/icons8-calculator-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-calculator.imageset/icons8-calculator-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-calculator.imageset/icons8-calculator-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-filled_like.imageset/icons8-filled_like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-filled_like.imageset/icons8-filled_like.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-month_view.imageset/icons8-month_view-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-month_view.imageset/icons8-month_view-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-month_view.imageset/icons8-month_view-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-month_view.imageset/icons8-month_view-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play_round.imageset/icons8-play_round-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-play_round.imageset/icons8-play_round-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play_round.imageset/icons8-play_round-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-play_round.imageset/icons8-play_round-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-sleep_mode.imageset/icons8-sleep_mode-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-sleep_mode.imageset/icons8-sleep_mode-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-sleep_mode.imageset/icons8-sleep_mode-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-sleep_mode.imageset/icons8-sleep_mode-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-time_card-1.imageset/icons8-time_card-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-time_card-1.imageset/icons8-time_card-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-time_card-1.imageset/icons8-time_card-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-time_card-1.imageset/icons8-time_card-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-project.imageset/icons8-group_of_projects-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-project.imageset/icons8-group_of_projects-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-project.imageset/icons8-group_of_projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-project.imageset/icons8-group_of_projects.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-free_shipping.imageset/icons8-free_shipping-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-free_shipping.imageset/icons8-free_shipping-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-free_shipping.imageset/icons8-free_shipping-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-free_shipping.imageset/icons8-free_shipping-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-verified_account.imageset/icons8-verified_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-verified_account.imageset/icons8-verified_account.png -------------------------------------------------------------------------------- /Freelancer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/empty_hourglass_filled-50.imageset/empty_hourglass_filled-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/empty_hourglass_filled-50.imageset/empty_hourglass_filled-50.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-verified_account.imageset/icons8-verified_account-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-verified_account.imageset/icons8-verified_account-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-verified_account.imageset/icons8-verified_account-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-verified_account.imageset/icons8-verified_account-2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/sand-clock-ready-to-start.imageset/sand-clock-ready-to-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/sand-clock-ready-to-start.imageset/sand-clock-ready-to-start.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Notification Window/icons8-time_card.imageset/icons8-time_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/Notification Window/icons8-time_card.imageset/icons8-time_card.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-split_payment_copy_2.imageset/icons8-split_payment_copy_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-split_payment_copy_2.imageset/icons8-split_payment_copy_2.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-split_payment_copy_2.imageset/icons8-split_payment_copy_2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-split_payment_copy_2.imageset/icons8-split_payment_copy_2-1.png -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-split_payment_copy_2.imageset/icons8-split_payment_copy_2-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dragstor/freelancers-dashboard/HEAD/Freelancer/Assets.xcassets/icons8-split_payment_copy_2.imageset/icons8-split_payment_copy_2-2.png -------------------------------------------------------------------------------- /Freelancer/Freelancer.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Freelancer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Freelancer/Freelancer.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | Freelancer.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Freelancer/Extensions/FD.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FD.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 3/6/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | 12 | extension Notification.Name { 13 | static let hoursUpdated = Notification.Name( 14 | rawValue: "hoursUpdated") 15 | } 16 | -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AllScreens.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "AllScreens.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Today.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "today-button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Weekly.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "weekly-button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/clock_filled-50.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "clock_filled-50.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-timer.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-timer-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/timer_filled-50.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "timer_filled-50.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Earnings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "earnings-button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/future_filled-50.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "future_filled-50.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-time_card.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-time_card.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Buttons/Preferences.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "preferences-button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-filled_like.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-filled_like.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/32x32.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "32x32.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "32x32@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/empty_hourglass_filled-50.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "empty_hourglass_filled-50.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/sand-clock-ready-to-start.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sand-clock-ready-to-start.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/Notification Window/icons8-time_card.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "icons8-time_card.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/128x128.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "128x128.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "128x128@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/256x256.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "256x256.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "256x256@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/512x512.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "512x512.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "512x512@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-free_shipping.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-free_shipping-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-free_shipping-2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-play-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-play.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-play-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-save.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-save.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-save-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-save-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-task.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-task.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-task-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-task-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-cancel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-cancel.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-cancel-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-cancel-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-pause.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-pause-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-pause-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-serbia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-serbia-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-serbia-2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-serbia.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-tasks.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-tasks.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-tasks-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-tasks-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-private.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-private-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-private.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-private-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-day_view.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-day_view-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-day_view.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-day_view-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-overtime.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-overtime.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-overtime-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-overtime-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-settings.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-settings-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-settings-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-shutdown.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-shutdown-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-shutdown.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-shutdown-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Controller/Timer/AcknowledgmentsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AcknowledgmentsViewController: NSViewController { 12 | 13 | @IBOutlet weak var linkIcon8: NSTextField! 14 | 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | // Do view setup here. 19 | 20 | } 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-stopwatch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-stopwatch.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-stopwatch-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-stopwatch-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-time_card-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-time_card.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-time_card-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-time_card-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-week_view.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-week_view.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-week_view-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-week_view-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-calculator.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-calculator.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-calculator-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-calculator-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-month_view.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-month_view.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-month_view-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-month_view-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-play_round.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-play_round.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-play_round-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-play_round-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-sleep_mode.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-sleep_mode.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-sleep_mode-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-sleep_mode-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-project.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-project.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-group_of_projects.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-group_of_projects-1.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-verified_account.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-verified_account-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-verified_account.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-verified_account-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/icons8-split_payment_copy_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-split_payment_copy_2-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-split_payment_copy_2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-split_payment_copy_2-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Freelancer.xcodeproj/xcuserdata/nikola.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Freelancer.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 933204332207C1DF0098632C 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Freelancer/Controller/AboutViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/5/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AboutViewController: NSViewController { 12 | 13 | @IBOutlet weak var lblVersion: NSTextField! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | // Do view setup here. 18 | let version = Bundle.main.versionNumber 19 | let build = Bundle.main.buildNumber 20 | 21 | lblVersion.stringValue = "version \(version) (Build \(build))" 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE REQUEST]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: dragstor 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Click on '...' 16 | 2. Change value to '....' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Desktop (please complete the following information):** 26 | - OS: [e.g. High Sierra 10.13] 27 | - Version [e.g. v1.0 build 210] 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /Freelancer/Controller/EventMonitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EventMonitor.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | // Used to hangle dismissing the timer popover (status bar) 10 | 11 | import Cocoa 12 | 13 | public class EventMonitor { 14 | private var monitor: Any? 15 | private let mask: NSEvent.EventTypeMask 16 | private let handler: (NSEvent?) -> Void 17 | 18 | public init(mask: NSEvent.EventTypeMask, handler: @escaping (NSEvent?) -> Void) { 19 | self.mask = mask 20 | self.handler = handler 21 | } 22 | 23 | deinit { 24 | stop() 25 | } 26 | 27 | public func start() { 28 | monitor = NSEvent.addGlobalMonitorForEvents(matching: mask, handler: handler) 29 | } 30 | 31 | public func stop() { 32 | if monitor != nil { 33 | NSEvent.removeMonitor(monitor!) 34 | monitor = nil 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Freelancer/Controller/OnBoarding/OnBoardingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnBoardingViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 3/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class OnBoardingViewController: NSViewController { 12 | 13 | @IBOutlet weak var onboardingView: NSView! 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | // Do view setup here. 17 | 18 | if self.view.isDarkMode == true { 19 | self.view.window?.appearance = NSAppearance(named: .vibrantDark) 20 | } else { 21 | self.view.window?.appearance = NSAppearance(named: .vibrantLight) 22 | } 23 | } 24 | 25 | override func viewDidAppear() { 26 | self.view.window?.styleMask.remove([.closable,.miniaturizable,.resizable,.titled]) 27 | // self.view.window?.styleMask.insert(.unifiedTitleAndToolbar) 28 | 29 | // let customToolbar = NSToolbar() 30 | 31 | self.view.window?.titlebarAppearsTransparent = true 32 | self.view.window?.titleVisibility = .hidden 33 | 34 | // self.view.window?.toolbar = customToolbar 35 | 36 | } 37 | 38 | 39 | @IBAction func close(_ sender: Any?) { 40 | self.view.window?.close() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Freelancer/Model/CustomAnimator.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | class ReplacePresentationAnimator: NSObject, NSViewControllerPresentationAnimator { 4 | func animatePresentation(of viewController: NSViewController, from fromViewController: NSViewController) { 5 | if let window = fromViewController.view.window { 6 | NSAnimationContext.runAnimationGroup({ (context) -> Void in 7 | fromViewController.view.animator().alphaValue = 0 8 | }, completionHandler: { () -> Void in 9 | viewController.view.alphaValue = 0 10 | window.contentViewController = viewController 11 | viewController.view.animator().alphaValue = 1.0 12 | }) 13 | } 14 | } 15 | 16 | func animateDismissal(of viewController: NSViewController, from fromViewController: NSViewController) { 17 | if let window = viewController.view.window { 18 | NSAnimationContext.runAnimationGroup({ (context) -> Void in 19 | viewController.view.animator().alphaValue = 0 20 | }, completionHandler: { () -> Void in 21 | fromViewController.view.alphaValue = 0 22 | window.contentViewController = fromViewController 23 | fromViewController.view.animator().alphaValue = 1.0 24 | }) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Freelancer/Controller/HyperlinkTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HyperlinkTextField.swift 3 | // Freelancer 4 | // 5 | // Modified by Nikola on 2/4/19. 6 | // Copyright © 2017 StackOverflow user "jeremyforan" (https://stackoverflow.com/users/749730/jeremyforan). All rights reserved. 7 | // Original source: 8 | // https://stackoverflow.com/a/46897824/414187 9 | // 10 | // Hyperlink text field (clickable & handled by default browser) 11 | 12 | import Cocoa 13 | 14 | @IBDesignable 15 | class HyperlinkTextField: NSTextField { 16 | 17 | override func draw(_ dirtyRect: NSRect) { 18 | super.draw(dirtyRect) 19 | 20 | // Drawing code here. 21 | } 22 | @IBInspectable var href: String = "" 23 | 24 | override func resetCursorRects() { 25 | discardCursorRects() 26 | addCursorRect(self.bounds, cursor: NSCursor.pointingHand) 27 | } 28 | 29 | override func awakeFromNib() { 30 | super.awakeFromNib() 31 | 32 | let attributes: [NSAttributedString.Key: Any] = [ 33 | NSAttributedString.Key.foregroundColor: NSColor.linkColor, 34 | NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue as AnyObject 35 | ] 36 | attributedStringValue = NSAttributedString(string: self.stringValue, attributes: attributes) 37 | } 38 | 39 | override func mouseDown(with theEvent: NSEvent) { 40 | if let localHref = URL(string: href) { 41 | NSWorkspace.shared.open(localHref) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Freelancer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.1 21 | CFBundleVersion 22 | 66 23 | LSApplicationCategoryType 24 | public.app-category.productivity 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2019 Nikola Stojković. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | NSServices 34 | 35 | 36 | NSMenuItem 37 | 38 | NSMessage 39 | 40 | 41 | 42 | NSUserNotificationAlertStyle 43 | alert 44 | 45 | 46 | -------------------------------------------------------------------------------- /Freelancer/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Freelancer/Controller/Earnings/EarningsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EarningsViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/17/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SQLite 11 | import SwiftDate 12 | 13 | class EarningsViewController: NSViewController { 14 | @IBOutlet weak var lblToday: NSTextField! 15 | @IBOutlet weak var lblWeek: NSTextField! 16 | @IBOutlet weak var lblAllTheTime: NSTextField! 17 | 18 | let ts_id = Expression("id") 19 | let ts_date = Expression("ts_date") 20 | let ts_from = Expression("ts_from") 21 | let ts_to = Expression("ts_to") 22 | let ts_total_time = Expression("ts_total_time") 23 | 24 | let fmt = DateFormatter() 25 | let format = "yyyy-MM-dd HH:mm:ss" 26 | let formatSec = "HH:mm:ss" 27 | let formatDay = "EEEE" 28 | let formatWeekShort = "yyyy-MM-dd" 29 | let formatWeekLong = "yyyy-MM-dd HH:mm:ss" 30 | 31 | var totalHours:TimeInterval = 0 32 | var newTime:TimeInterval = 0 33 | 34 | var todayTime:TimeInterval = 0 35 | var weekTime:TimeInterval = 0 36 | 37 | var totalHoursDay: TimeInterval = 0 38 | var totalHoursWeek: TimeInterval = 0 39 | 40 | var days:[[String:String]] = [[:]] 41 | 42 | override func viewDidLoad() { 43 | super.viewDidLoad() 44 | // Do view setup here. 45 | SwiftDate.defaultRegion = Region.current 46 | 47 | 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Freelancer/Freelancer.xcdatamodeld/Freelancer.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Freelancer/Model/FLTimerProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FLTimerProtocol.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/5/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol FLTimerProtocol { 12 | func timeElapsedOnTimer(_ timer: FLTimer, timeElapsed: TimeInterval) 13 | func timerHasFinished(_ timer: FLTimer) 14 | } 15 | 16 | class FLTimer { 17 | 18 | var timer: Timer? = nil 19 | var startTime: Date? 20 | var elapsedTime: TimeInterval = 0 21 | 22 | var isStopped: Bool { 23 | return timer == nil && elapsedTime == -1 24 | } 25 | 26 | var isPaused: Bool { 27 | return timer == nil && elapsedTime > 0 28 | } 29 | 30 | var delegate: FLTimerProtocol? 31 | 32 | @objc dynamic func timerAction() { 33 | guard let startTime = startTime else { 34 | return 35 | } 36 | 37 | elapsedTime = -1 * startTime.timeIntervalSinceNow 38 | let secondsElapsed = elapsedTime.rounded() 39 | 40 | if secondsElapsed == -1 { 41 | stopTimer() 42 | delegate?.timerHasFinished(self) 43 | } else { 44 | delegate?.timeElapsedOnTimer(self, timeElapsed: secondsElapsed) 45 | } 46 | NotificationCenter.default.post(name: .hoursUpdated, object: nil) 47 | } 48 | 49 | func startTimer() { 50 | startTime = Date() 51 | elapsedTime = 0 52 | timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true) 53 | 54 | timerAction() 55 | } 56 | 57 | func stopTimer() { 58 | timer?.invalidate() 59 | timer = nil 60 | startTime = nil 61 | elapsedTime = -1 62 | 63 | timerAction() 64 | } 65 | 66 | } 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![header](https://i.imgur.com/EWlF8V8.png) 2 | 3 | This app is a work in progress and it's made for freelancers who need to keep a simple track of time and earnings. I'm still learning Swift, so bare with me :blush: 4 | 5 | ![](https://img.shields.io/github/issues/dragstor/freelancers-dashboard.svg?style=flat) ![](https://img.shields.io/github/license/dragstor/freelancers-dashboard.svg?style=flat) 6 | 7 | ![](https://img.shields.io/github/languages/top/dragstor/freelancers-dashboard.svg?style=flat) ![](https://img.shields.io/github/commit-activity/m/dragstor/freelancers-dashboard.svg?style=flat) ![](https://img.shields.io/github/languages/code-size/dragstor/freelancers-dashboard.svg?style=flat) 8 | 9 | ## Features 10 | 11 | - Dark and light appearance OOB 12 | - Simple work timer 13 | - Simple earnings overview 14 | - Configurable 15 | - Maximum weekly working hours 16 | - Hourly rate (used to calculate earnings) 17 | - Autostart 18 | 19 | # The App 20 | 21 | App consists of the popover timer (sits in the status bar) and a main window with few features currently available. 22 | 23 | ## Main window 24 | 25 | ![main window](https://i.imgur.com/LvRVhjH.png) 26 | 27 | The app itslef consists of a main window with (currently) just a few available features, such as: 28 | 29 | - Time sheets 30 | - for today 31 | - for current week 32 | - Earnings 33 | - Preferences 34 | 35 | ## Popover Timer 36 | 37 | ![Popover Timer](https://i.imgur.com/6hIBtbt.png) 38 | 39 | Perhaps the most useful feature - work timer. It's fully manual, so user has to start and stop the timer before and after they finish with work they want to log time for. 40 | 41 | # 3rd party libraries & attributions 42 | - [SQLite.swift](https://github.com/stephencelis/SQLite.swift) by @stephencelis 43 | - [SwiftDate](https://github.com/malcommac/SwiftDate) by @malcommac 44 | - Icons & glyphs from [Icons8 App](https://icons8.com/) 45 | 46 | # TODO 47 | 48 | - [x] Enter worked hours into the timesheet upon stopping the timer 49 | - [ ] Edit worked hours (from the timesheets) 50 | - [ ] Implement calendar (extend timesheets) 51 | - [x] Automatically calculate earnings (based on timesheets) 52 | - and many more, check the [board](https://github.com/dragstor/freelancers-dashboard/projects/1) 53 | # License 54 | 55 | GPL 3.0 56 | -------------------------------------------------------------------------------- /Freelancer/Controller/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | @IBOutlet weak var progress: NSLevelIndicator! 14 | @IBOutlet weak var lblTimerRunningStatus: NSTextField! 15 | @IBOutlet weak var btnToday: NSButton! 16 | @IBOutlet weak var btnWeekly: NSButton! 17 | @IBOutlet weak var btnEarnings: NSButton! 18 | @IBOutlet weak var btnPreferences: NSButton! 19 | var prefs = Preferences() 20 | 21 | 22 | 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | getWorkedTime() 28 | onBoarding() 29 | 30 | } 31 | 32 | override var representedObject: Any? { 33 | didSet { 34 | // Update the view, if already loaded. 35 | 36 | getWorkedTime() 37 | } 38 | } 39 | 40 | override func viewDidAppear() { 41 | let mwh = prefs.maxWeeklyHours 42 | let rph = prefs.ratePerHour 43 | 44 | if mwh != 0 && rph != 0.0 { 45 | btnToday.isEnabled = true 46 | btnWeekly.isEnabled = true 47 | btnEarnings.isEnabled = true 48 | } 49 | NotificationCenter.default.addObserver(self, selector: #selector(onDidReceiveData(notification:)), name: .hoursUpdated, object: nil) 50 | 51 | } 52 | 53 | 54 | @objc func onDidReceiveData(notification:Notification) { 55 | getWorkedTime() 56 | } 57 | 58 | func getWorkedTime() { 59 | let maxHr:TimeInterval = TimeInterval(Int(prefs.maxWeeklyHours) * 3600) 60 | let weekHr:TimeInterval = prefs.weekHours // 3600 61 | 62 | let pMax = Double(maxHr / 360) 63 | 64 | progress.maxValue = pMax 65 | progress.warningValue = pMax * 7 * 0.1 66 | progress.criticalValue = pMax * 9 * 0.1 67 | 68 | progress.doubleValue = weekHr / 360 69 | // print("Vrednost notifikacija: \(weekHr)") 70 | } 71 | 72 | func onBoarding() { 73 | let firstRun: Bool = prefs.firstRun 74 | // goToScreen(id: "onboarding", fromBoard: "OnBoarding") 75 | if firstRun == true { 76 | let viewController:NSViewController = NSStoryboard(name: "OnBoarding", bundle: nil).instantiateController(withIdentifier: "onboarding") as! NSViewController 77 | presentAsModalWindow(viewController) 78 | 79 | let preferencesController:NSViewController = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "preferences") as! NSViewController 80 | presentAsModalWindow(preferencesController) 81 | 82 | btnToday.isEnabled = false 83 | btnWeekly.isEnabled = false 84 | btnEarnings.isEnabled = false 85 | } else { 86 | btnToday.isEnabled = true 87 | btnWeekly.isEnabled = true 88 | btnEarnings.isEnabled = true 89 | } 90 | } 91 | 92 | } 93 | 94 | -------------------------------------------------------------------------------- /Freelancer/Controller/Notification/NotificationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import Foundation 11 | 12 | 13 | class NotificationViewController: NSViewController { 14 | 15 | 16 | 17 | @IBOutlet weak var notificationView: NSView! 18 | @IBOutlet weak var image: NSImageView! 19 | @IBOutlet weak var message: NSTextField! 20 | 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | let when = DispatchTime.now()// + 3 26 | DispatchQueue.main.asyncAfter(deadline: when, execute: { 27 | self.view.window?.fadeOut(sender: self, duration: 5, timingFunction: nil, targetAlpha: 0.0, resetAlphaAfterAnimation: false, closeSelector: .performClose, completionHandler: self.view.window?.close) 28 | }) 29 | 30 | } 31 | 32 | override func viewWillAppear() { 33 | super.viewWillAppear() 34 | 35 | 36 | // Customize the notification alert 37 | let visualEffect = NSVisualEffectView() 38 | 39 | // Adjust the icon color to the currently set appearance (dark/light) 40 | if self.view.isDarkMode == true { 41 | image.contentFilters.append(CIFilter.init(name: "CIColorInvert")!) 42 | self.view.window?.appearance = NSAppearance(named: .vibrantDark) 43 | } else { 44 | image.contentFilters.removeAll() 45 | self.view.window?.appearance = NSAppearance(named: .vibrantLight) 46 | } 47 | 48 | 49 | visualEffect.translatesAutoresizingMaskIntoConstraints = false 50 | // visualEffect.material = .appearanceBased 51 | visualEffect.state = .active 52 | visualEffect.wantsLayer = true 53 | visualEffect.layer?.cornerRadius = 16.0 54 | 55 | 56 | self.view.window?.titleVisibility = .hidden 57 | self.view.window?.styleMask.remove(.titled) 58 | self.view.window?.styleMask.remove(.resizable) 59 | 60 | self.view.window?.backgroundColor = .clear 61 | self.view.window?.isMovableByWindowBackground = false 62 | 63 | 64 | self.view.window?.contentView?.addSubview(visualEffect) 65 | self.view.window?.contentView?.addSubview(notificationView) 66 | 67 | guard let constraints = self.view.window?.contentView else { 68 | return 69 | } 70 | 71 | visualEffect.leadingAnchor.constraint(equalTo: constraints.leadingAnchor).isActive = true 72 | visualEffect.trailingAnchor.constraint(equalTo: constraints.trailingAnchor).isActive = true 73 | visualEffect.topAnchor.constraint(equalTo: constraints.topAnchor).isActive = true 74 | visualEffect.bottomAnchor.constraint(equalTo: constraints.bottomAnchor).isActive = true 75 | 76 | } 77 | 78 | override var representedObject: Any? { 79 | didSet { 80 | // Update the view, if already loaded. 81 | } 82 | } 83 | 84 | } 85 | 86 | -------------------------------------------------------------------------------- /Freelancer/Controller/Preferences/GeneralPreferenceViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneralPreferenceViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | //import LaunchAtLogin 11 | 12 | class GeneralPreferencesViewController: NSViewController, NSTextFieldDelegate { 13 | @IBOutlet weak var autoStart: NSButton! 14 | 15 | @IBOutlet weak var txtHours: NSTextField! 16 | @IBOutlet weak var txtRPH: NSTextField! 17 | @IBOutlet weak var currency: NSPopUpButton! 18 | 19 | @IBOutlet weak var btnCancel: NSButton! 20 | @IBOutlet weak var btnSave: NSButton! 21 | 22 | var prefs = Preferences() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | showExistingPrefs() 28 | 29 | 30 | } 31 | 32 | override var representedObject: Any? { 33 | didSet { 34 | // Update the view, if already loaded. 35 | } 36 | } 37 | 38 | func showExistingPrefs() { 39 | let autoStartApp = Bool(prefs.autoStart) 40 | let hrs = Int16(prefs.maxWeeklyHours) 41 | let rph = Double(prefs.ratePerHour) 42 | 43 | if autoStartApp == true { 44 | // Autostart functionality and check the checkbutton 45 | autoStart.state = .on 46 | } 47 | 48 | txtHours.stringValue = String(hrs) 49 | txtRPH.stringValue = String(rph) 50 | currency.selectItem(at: prefs.currency) 51 | 52 | } 53 | 54 | func saveExistingPrefs() { 55 | let rph = txtRPH.stringValue 56 | let mwh = txtHours.stringValue 57 | let cur = currency.indexOfSelectedItem 58 | 59 | if autoStart.state == .on { 60 | prefs.autoStart = true 61 | // LaunchAtLogin.isEnabled = true 62 | } else { 63 | prefs.autoStart = false 64 | // LaunchAtLogin.isEnabled = false 65 | } 66 | prefs.maxWeeklyHours = Int16(mwh)! 67 | prefs.ratePerHour = Double(rph)! 68 | prefs.currency = cur 69 | 70 | if rph != "0.0" && mwh != "0" { 71 | prefs.firstRun = false 72 | } 73 | 74 | } 75 | 76 | 77 | 78 | @IBAction func btnSave(_ sender: NSButton!) { 79 | let rph = txtRPH.stringValue 80 | let mwh = txtHours.stringValue 81 | 82 | if rph == "0.0" || rph == "" || mwh == "0" || mwh == "" { 83 | NSAlert.showAlert(title: "ERROR", message: "You must enter valid data!", style: .warning) 84 | } else { 85 | saveExistingPrefs() 86 | super.view.window?.close() 87 | } 88 | } 89 | 90 | @IBAction func btnCancel(_ sender: NSButton!) { 91 | let rph = txtRPH.stringValue 92 | let mwh = txtHours.stringValue 93 | 94 | if rph == "0.0" || rph == "" || mwh == "0" || mwh == "" { 95 | NSAlert.showAlert(title: "ERROR", message: "You must enter valid data!", style: .warning) 96 | } else { 97 | super.view.window?.close() 98 | } 99 | } 100 | 101 | 102 | } 103 | extension ViewController: NSControlTextEditingDelegate { 104 | func controlTextDidChange(_ notification: Notification) { 105 | if let textField = notification.object as? NSTextField { 106 | print(textField.stringValue) 107 | //do what you need here 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Freelancer/Model/Preferences.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Preferences.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | import Cocoa 9 | 10 | /// Preferences struct 11 | /// Used to set and get params important to the appß 12 | struct Preferences { 13 | /// Defines if the app will auto start with the system boot (user login) 14 | var autoStart: Bool { 15 | get { 16 | let savedAutoStart = UserDefaults.standard.bool(forKey: "autoStart") 17 | if savedAutoStart == true { 18 | return true 19 | } 20 | 21 | return false 22 | } 23 | set { 24 | UserDefaults.standard.set(newValue, forKey: "autoStart") 25 | } 26 | } 27 | 28 | /// Used to determine if the onboarding screen will appear 29 | var firstRun: Bool { 30 | get { 31 | let isFirstRun = UserDefaults.standard.bool(forKey: "firstRun") 32 | if isFirstRun == true { 33 | return true 34 | } 35 | 36 | return false 37 | } 38 | set { 39 | UserDefaults.standard.set(newValue, forKey: "firstRun") 40 | } 41 | } 42 | 43 | /// Freelancer's maximum weekly billable hours 44 | var maxWeeklyHours: Int16 { 45 | get { 46 | let maxWHours = UserDefaults.standard.integer(forKey: "maxWeeklyHours") 47 | if maxWHours > 0 { 48 | return Int16(maxWHours) 49 | } 50 | 51 | return 0 52 | } 53 | 54 | set { 55 | UserDefaults.standard.set(newValue, forKey: "maxWeeklyHours") 56 | } 57 | } 58 | 59 | 60 | /// Freelancer's per hour rate 61 | var ratePerHour: Double { 62 | get { 63 | let ratePH = UserDefaults.standard.double(forKey: "ratePerHour") 64 | 65 | if ratePH != 0.00 { 66 | return Double(ratePH) 67 | } 68 | 69 | return 0.00 70 | } 71 | 72 | set { 73 | UserDefaults.standard.set(newValue, forKey: "ratePerHour") 74 | } 75 | } 76 | 77 | /// Worked hours this week 78 | var weekHours: TimeInterval { 79 | get { 80 | let weekHours = UserDefaults.standard.double(forKey: "weekHours") 81 | 82 | if weekHours != 0.0 { 83 | return TimeInterval(weekHours) 84 | } 85 | 86 | return 0.0 87 | } 88 | 89 | set { 90 | UserDefaults.standard.set(newValue, forKey: "weekHours") 91 | } 92 | } 93 | 94 | /// Earnings for current week 95 | var weekEarnings: Double { 96 | get { 97 | let weekEarnings = UserDefaults.standard.double(forKey: "weekEarnings") 98 | 99 | if weekEarnings != 0.0 { 100 | return Double(weekEarnings) 101 | } 102 | 103 | return 0.0 104 | } 105 | 106 | set { 107 | UserDefaults.standard.set(newValue, forKey: "weekEarnings") 108 | } 109 | } 110 | 111 | var currency: Int { 112 | get { 113 | let currency = UserDefaults.standard.integer(forKey: "currency") 114 | 115 | return currency 116 | } 117 | 118 | set { 119 | UserDefaults.standard.set(newValue, forKey: "currency") 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Freelancer/Storyboard/Notification.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Freelancer/Controller/Time Sheets/TodayTimeSheetViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TodayTimeSheetViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | import SQLite 12 | import SwiftDate 13 | 14 | class TodayTimeSheetViewController: NSViewController { 15 | 16 | 17 | 18 | @IBOutlet weak var lblTotalHours: NSTextField! 19 | @IBOutlet weak var lblTotalEarnings: NSTextField! 20 | @IBOutlet weak var tableView: NSTableView! 21 | @IBOutlet weak var cellFrom: NSTableCellView! 22 | @IBOutlet weak var cellTo: NSTableCellView! 23 | @IBOutlet weak var cellTotal: NSTableCellView! 24 | @IBOutlet weak var cellEarned: NSTableCellView! 25 | @IBOutlet weak var currentDay: NSTextField! 26 | 27 | 28 | let ts_date = Expression("ts_date") 29 | let ts_from = Expression("ts_from") 30 | let ts_to = Expression("ts_to") 31 | let ts_total_time = Expression("ts_total_time") 32 | let ts_approved = Expression("ts_approved") 33 | 34 | 35 | 36 | let fmt = DateFormatter() 37 | let format = "yyyy-MM-dd HH:mm:ss" 38 | let formatSec = "HH:mm:ss" 39 | 40 | var totalHours: TimeInterval = 0 41 | var newTime: TimeInterval = 0 42 | var timeClock:TimeInterval = 0 43 | var data:[[String:String]] = [[:]] 44 | 45 | override func viewDidLoad() { 46 | 47 | super.viewDidLoad() 48 | SwiftDate.defaultRegion = Region.current 49 | 50 | do { 51 | let db = try Connection("\( NSApp.supportFolderGet())/db.sqlite3") 52 | let tableTimesheets = Table("timesheets_interval") 53 | 54 | let now = DateInRegion().convertTo(region: Region.current) 55 | let today_start = now.dateAtStartOf(.day).timeIntervalSince1970 56 | let today_end = now.dateAtEndOf(.day).timeIntervalSince1970 57 | 58 | let query = tableTimesheets 59 | .select(ts_date, ts_from, ts_to, ts_total_time) 60 | .filter( 61 | today_start...today_end ~= ts_date 62 | ) 63 | 64 | let today_timesheet = try db.prepare(query) 65 | 66 | data.removeAll() 67 | 68 | var timeTmp:TimeInterval = 0 69 | for entry in today_timesheet { 70 | do { 71 | fmt.dateFormat = formatSec 72 | let date_from = Date(timeIntervalSince1970: entry[ts_from]).toFormat(formatSec) 73 | let date_to = Date(timeIntervalSince1970: entry[ts_to]).toFormat(formatSec) 74 | let date_total = entry[ts_total_time].toString() 75 | 76 | data.append( 77 | [ 78 | "CellFrom": date_from, 79 | "CellTo": date_to, 80 | "CellTotal": date_total, 81 | "CellEarned": entry[ts_total_time].getEarnings() 82 | ] 83 | ) 84 | timeTmp = addTime(timeToAdd: entry[ts_total_time]) 85 | } 86 | } 87 | 88 | tableView.reloadData() 89 | 90 | currentDay.stringValue = now.toFormat("EEEE, MMM dd, YYYY") //.getDay() 91 | lblTotalEarnings.stringValue = timeTmp.getEarnings() 92 | } catch { 93 | NSAlert.showAlert(title: "Error loading timesheets", message: "Unable to load timesheet!", style: .critical) 94 | } 95 | } 96 | 97 | 98 | override var representedObject: Any? { 99 | didSet { 100 | // Update the view, if already loaded. 101 | } 102 | } 103 | 104 | func addTime(timeToAdd: TimeInterval) -> TimeInterval { 105 | 106 | let time = DateInRegion(seconds: timeToAdd) 107 | let h = time.hour * 3600 108 | let m = time.minute * 60 109 | let s = time.second 110 | 111 | let sum = h + m + s 112 | 113 | let previous = totalHours 114 | newTime = previous + TimeInterval(sum) 115 | totalHours = newTime 116 | 117 | lblTotalHours.stringValue = newTime.toString() 118 | 119 | return newTime 120 | } 121 | } 122 | 123 | extension TodayTimeSheetViewController: NSTableViewDataSource, NSTableViewDelegate { 124 | func numberOfRows(in tableView: NSTableView) -> Int { 125 | return (data.count) 126 | } 127 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 128 | let field = data[row] 129 | 130 | let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView 131 | cell?.textField?.stringValue = field[tableColumn!.identifier.rawValue]! 132 | 133 | return cell 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /Freelancer/Extensions/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SwiftDate 11 | 12 | extension TimerController { 13 | // MARK: Storyboard instantiation 14 | static func freshController() -> TimerController { 15 | //1. 16 | let storyboard = NSStoryboard(name: NSStoryboard.Name("Timer"), bundle: nil) 17 | //2. 18 | let identifier = NSStoryboard.SceneIdentifier("TimerScene") 19 | //3. 20 | guard let viewcontroller = storyboard.instantiateController(withIdentifier: identifier) as? TimerController else { 21 | fatalError("Why cant i find Timer popover? - Check Timer.storyboard") 22 | } 23 | return viewcontroller 24 | } 25 | } 26 | 27 | 28 | extension Bundle { 29 | 30 | var appName: String { 31 | return infoDictionary?["CFBundleName"] as! String 32 | } 33 | 34 | var bundleId: String { 35 | return bundleIdentifier! 36 | } 37 | 38 | var versionNumber: String { 39 | return infoDictionary?["CFBundleShortVersionString"] as! String 40 | } 41 | 42 | var buildNumber: String { 43 | return infoDictionary?["CFBundleVersion"] as! String 44 | } 45 | 46 | } 47 | 48 | extension Date { 49 | 50 | func getDay(withFormat format: String = "EEEE, d MMM yyyy")-> String { 51 | let dateFormatter = DateFormatter() 52 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 53 | dateFormatter.dateFormat = format 54 | let day = dateFormatter.string(from: self) 55 | return day 56 | } 57 | 58 | func getTime(withFormat format: String = "HH:mm:ss")-> String { 59 | let dateFormatter = DateFormatter() 60 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 61 | dateFormatter.dateFormat = format 62 | let time = dateFormatter.string(from: self) 63 | return time 64 | } 65 | 66 | static func - (lhs: Date, rhs: Date) -> TimeInterval { 67 | return lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate 68 | } 69 | } 70 | 71 | 72 | extension String { 73 | 74 | func toTime(withFormat format: String = "yyyy-mm-dd HH:mm:ss")-> Date?{ 75 | let dateFormatter = DateFormatter() 76 | dateFormatter.dateFormat = format 77 | let date = dateFormatter.date(from: self) 78 | 79 | return date 80 | 81 | } 82 | 83 | func getDay(withFormat format: String = "yyyy-mm-dd")-> Date?{ 84 | let dateFormatter = DateFormatter() 85 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 86 | dateFormatter.dateFormat = "EEEE, d MMM yyyy" 87 | let day = dateFormatter.date(from: self) 88 | 89 | return day 90 | } 91 | } 92 | 93 | extension TimeInterval { 94 | func getEarnings()-> String { 95 | let dateFormatter = DateFormatter() 96 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 97 | dateFormatter.dateFormat = "H:m:s" 98 | 99 | var earnings = 0.0 100 | var prefs = Preferences() 101 | let rph = prefs.ratePerHour 102 | 103 | let time = DateInRegion(seconds: self) 104 | let h = Double(time.hour) 105 | let m = Double(time.minute) * (1 / 60) 106 | let s = Double(time.second) * (1 / 3600 ) 107 | 108 | earnings = Double(h + m + s) * rph 109 | 110 | return String(format:"%.2f", earnings) 111 | } 112 | } 113 | 114 | extension NSApplication { 115 | func supportFolderGet()-> String { 116 | let path = NSSearchPathForDirectoriesInDomains( 117 | .applicationSupportDirectory, .userDomainMask, true 118 | ).first! + "/" + Bundle.main.bundleIdentifier! 119 | 120 | return path 121 | } 122 | } 123 | 124 | extension NSAlert { 125 | static func showAlert(title: String?, message: String?, style: NSAlert.Style = .informational) { 126 | let alert = NSAlert() 127 | if let title = title { 128 | alert.messageText = title 129 | } 130 | if let message = message { 131 | alert.informativeText = message 132 | } 133 | alert.alertStyle = style 134 | alert.runModal() 135 | } 136 | } 137 | 138 | extension NSView { 139 | var isDarkMode: Bool { 140 | if #available(OSX 10.14, *) { 141 | if effectiveAppearance.name == .darkAqua { 142 | return true 143 | } 144 | } 145 | return false 146 | } 147 | } 148 | 149 | 150 | extension NSViewController { 151 | func goToScreen(id : String, fromBoard: String) { 152 | let viewController:NSViewController = NSStoryboard(name: fromBoard, bundle: Bundle.main).instantiateController(withIdentifier: id) as! NSViewController 153 | // self.present(viewController, animator: ReplacePresentationAnimator()) 154 | // present(viewController, animator: ReplacePresentationAnimator()) 155 | // viewController.view.window?.styleMask.remove(.fullScreen) 156 | // self.view.present(viewController, asPopoverRelativeTo: self, of: sender, preferredEdge: .maxY, behavior: .transient) 157 | self.presentAsSheet(viewController) 158 | 159 | } 160 | } 161 | 162 | extension Notification.Name { 163 | static let didReceiveData = Notification.Name("didReceiveData") 164 | static let didCompleteTask = Notification.Name("didCompleteTask") 165 | static let completedLengthyDownload = Notification.Name("completedLengthyDownload") 166 | } 167 | -------------------------------------------------------------------------------- /Freelancer/Controller/Time Sheets/WeekTimeSheetViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeekTimeSheetViewController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | import SwiftDate 12 | import SQLite 13 | 14 | class WeekTimeSheetViewController: NSViewController { 15 | 16 | @IBOutlet weak var currentWeek: NSTextField! 17 | @IBOutlet weak var tableView: NSTableView! 18 | @IBOutlet weak var lblCurrentWeek: NSTextField! 19 | @IBOutlet weak var CellDay: NSTableCellView! 20 | @IBOutlet weak var CellHours: NSTableCellView! 21 | @IBOutlet weak var CellEarned: NSTableCellView! 22 | 23 | @IBOutlet weak var lblTotalHours: NSTextField! 24 | @IBOutlet weak var lblTotalEarnings: NSTextField! 25 | 26 | var currentWeekStart = Date().dateAt(.startOfWeek).timeIntervalSince1970 27 | var currentWeekEnd = Date().dateAt(.endOfWeek).timeIntervalSince1970 28 | 29 | let ts_id = Expression("id") 30 | let ts_date = Expression("ts_date") 31 | let ts_total_time = Expression("ts_total_time") 32 | 33 | let fmt = DateFormatter() 34 | let format = "yyyy-MM-dd HH:mm:ss" 35 | let formatSec = "HH:mm:ss" 36 | let formatDay = "EEEE" 37 | let formatWeekShort = "yyyy-MM-dd" 38 | let formatWeekLong = "yyyy-MM-dd HH:mm:ss" 39 | 40 | var totalHoursDay: TimeInterval = 0 41 | var totalHoursWeek: TimeInterval = 0 42 | 43 | var days:[[String:String]] = [[:]] 44 | var timePerDay = [String:(TimeInterval)]() 45 | 46 | var prefs = Preferences() 47 | 48 | override func viewDidLoad() { 49 | super.viewDidLoad() 50 | 51 | SwiftDate.defaultRegion = Region.current 52 | 53 | lblCurrentWeek.stringValue = "\(DateInRegion(seconds: currentWeekStart).toFormat(formatWeekShort)) to \(DateInRegion(seconds: currentWeekEnd).toFormat(formatWeekShort))" 54 | 55 | do{ 56 | let db = try Connection("\( NSApp.supportFolderGet())/db.sqlite3") 57 | let tableTimesheets = Table("timesheets_interval") 58 | 59 | let dayStart = currentWeekStart 60 | let dayEnd = currentWeekEnd 61 | 62 | let query = tableTimesheets 63 | .select(ts_id, ts_date, ts_total_time) 64 | .filter(dayStart...dayEnd ~= ts_date) 65 | .order(ts_id) 66 | 67 | let weekTimesheet = try db.prepare(query) 68 | 69 | let dates = DateInRegion.enumerateDates(from: DateInRegion(seconds: currentWeekStart), to: DateInRegion(seconds: currentWeekEnd), increment: 1.days) 70 | 71 | days.removeAll() 72 | 73 | fmt.dateFormat = formatWeekLong 74 | 75 | var sum:TimeInterval = 0 76 | 77 | for entry in weekTimesheet { 78 | let dateDay = entry[ts_date] 79 | let dateTotalTime = entry[ts_total_time] 80 | for weekDay in dates { 81 | if DateInRegion(seconds: dateDay).isInside(date: weekDay, granularity: .day) { 82 | totalHoursDay += dateTotalTime 83 | sum += totalHoursDay 84 | let tmp = TimeInterval(timePerDay[weekDay.toFormat(formatDay)] ?? 0) + totalHoursDay 85 | 86 | timePerDay[weekDay.toFormat(formatDay)] = tmp 87 | 88 | totalHoursWeek = sum 89 | totalHoursDay = 0 90 | } 91 | } 92 | } 93 | 94 | for tsDay in dates { 95 | let dayIterator = tsDay.toFormat(formatDay) 96 | 97 | for ts in timePerDay { 98 | if ts.key == dayIterator { 99 | let hours = ts.value 100 | let earnings = hours.getEarnings() 101 | 102 | days.append([ 103 | "CellDay": ts.key, 104 | "CellHours": hours.toString(), 105 | "CellEarned": earnings 106 | ]) 107 | } 108 | } 109 | } 110 | 111 | lblTotalHours.stringValue = totalHoursWeek.toString() 112 | lblTotalEarnings.stringValue = totalHoursWeek.getEarnings() 113 | 114 | prefs.weekHours = totalHoursWeek 115 | 116 | tableView.reloadData() 117 | } catch { 118 | NSAlert.showAlert(title: "ERROR", message: "Error with DB!", style: .critical) 119 | } 120 | } 121 | 122 | override var representedObject: Any? { 123 | didSet { 124 | // Update the view, if already loaded. 125 | } 126 | } 127 | } 128 | 129 | extension WeekTimeSheetViewController: NSTableViewDataSource, NSTableViewDelegate { 130 | func numberOfRows(in tableView: NSTableView) -> Int { 131 | return (days.count) 132 | } 133 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 134 | let field = days[row] 135 | 136 | let cell = tableView.makeView(withIdentifier: (tableColumn!.identifier), owner: self) as? NSTableCellView 137 | cell?.textField?.stringValue = field[tableColumn!.identifier.rawValue]! 138 | 139 | return cell 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Freelancer/Extensions/temp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSWindow+Fade.swift 3 | // BH Bezel Notification 4 | // 5 | // Created by Ben Leggiero on 2017-11-09. 6 | // Translated to Swift 4 from the original (ObjC): https://gist.github.com/indragiek/1397050 7 | // Copyright © 2017 Ben Leggiero. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | import Cocoa 12 | 13 | 14 | private let defaultWindowAnimationDuration: TimeInterval = 0.25 15 | 16 | 17 | 18 | public extension NSWindow { 19 | 20 | /// Called when an animation completes 21 | typealias AnimationCompletionHandler = () -> Void 22 | 23 | 24 | 25 | /// Represents a function called to make a window be presented 26 | enum PresentationFunction { 27 | /// Calls `NSWindow.makeKey()` 28 | case makeKey 29 | 30 | /// Calls `NSWindow.makeKeyAndOrderFront(_:)` 31 | case makeKeyAndOrderFront 32 | 33 | /// Calls `NSWindow.orderFront(_:)` 34 | case orderFront 35 | 36 | /// Calls `NSWindow.orderFrontRegardless()` 37 | case orderFrontRegardless 38 | 39 | 40 | /// Runs the function represented by this case on the given window, passing the given selector if needed 41 | public func run(on window: NSWindow, sender: Any?) { 42 | switch self { 43 | case .makeKey: window.makeKey() 44 | case .makeKeyAndOrderFront: window.makeKeyAndOrderFront(sender) 45 | case .orderFront: window.orderFront(sender) 46 | case .orderFrontRegardless: window.orderFrontRegardless() 47 | } 48 | } 49 | } 50 | 51 | 52 | 53 | /// Represents a function called to make a window be closed 54 | enum CloseFunction { 55 | /// Calls `NSWindow.orderOut(_:)` 56 | case orderOut 57 | 58 | /// Calls `NSWindow.close()` 59 | case close 60 | 61 | /// Calls `NSWindow.performClose()` 62 | case performClose 63 | 64 | 65 | /// Runs the function represented by this case on the given window, passing the given selector if needed 66 | public func run(on window: NSWindow, sender: Any?) { 67 | switch self { 68 | case .orderOut: window.orderOut(sender) 69 | case .close: window.close() 70 | case .performClose: window.performClose(sender) 71 | } 72 | } 73 | } 74 | 75 | 76 | 77 | /// Fades this window in using the default values. Useful for NIB-style actions 78 | @IBAction 79 | func fadeIn(_ sender: Any?) { 80 | self.fadeIn(sender: sender, duration: defaultWindowAnimationDuration) 81 | } 82 | 83 | 84 | /// Fades this window in using the given configuration 85 | /// 86 | /// - Parameters: 87 | /// - sender: The message's sender, if any 88 | /// - duration: The minimum amount of time it should to fade the window in 89 | /// - timingFunction: The timing function of the animation 90 | /// - startingAlpha: The alpha value at the start of the animation 91 | /// - targetAlpha: The alpha value at the end of the animation 92 | /// - presentationFunction: The function to use when initially presenting the window 93 | /// - completionHandler: Called when the animation completes 94 | func fadeIn(sender: Any?, 95 | duration: TimeInterval, 96 | timingFunction: CAMediaTimingFunction? = .default, 97 | startingAlpha: CGFloat = 0, 98 | targetAlpha: CGFloat = 1, 99 | presentationFunction: PresentationFunction = .makeKeyAndOrderFront, 100 | completionHandler: AnimationCompletionHandler? = nil) { 101 | 102 | alphaValue = startingAlpha 103 | 104 | presentationFunction.run(on: self, sender: sender) 105 | 106 | NSAnimationContext.runAnimationGroup({ context in 107 | context.duration = duration 108 | context.timingFunction = timingFunction 109 | animator().alphaValue = targetAlpha 110 | }, completionHandler: completionHandler) 111 | } 112 | 113 | 114 | /// Fades this window out using the default values. Useful for NIB-style actions 115 | @IBAction func fadeOut(_ sender: Any?) { 116 | self.fadeOut(sender: sender, duration: defaultWindowAnimationDuration) 117 | } 118 | 119 | 120 | /// Fades this window out using the given configuration 121 | /// 122 | /// - Note: Unlike `fadeIn`, this does not take a starting alpha value. This is because the window's current 123 | /// alpha is used. If you really want it to be different, simply change that immediately before calling 124 | /// this function. 125 | /// 126 | /// - Parameters: 127 | /// - sender: The message's sender, if any 128 | /// - duration: The minimum amount of time it should to fade the window out 129 | /// - timingFunction: The timing function of the animation 130 | /// - targetAlpha: The alpha value at the end of the animation 131 | /// - presentationFunction: The function to use when initially presenting the window 132 | /// - completionHandler: Called when the animation completes 133 | func fadeOut(sender: Any?, 134 | duration: TimeInterval, 135 | timingFunction: CAMediaTimingFunction? = .default, 136 | targetAlpha: CGFloat = 0, 137 | resetAlphaAfterAnimation: Bool = true, 138 | closeSelector: CloseFunction = .orderOut, 139 | completionHandler: AnimationCompletionHandler? = nil) { 140 | 141 | let startingAlpha = self.alphaValue 142 | 143 | NSAnimationContext.runAnimationGroup({ context in 144 | 145 | context.duration = duration 146 | context.timingFunction = timingFunction 147 | animator().alphaValue = targetAlpha 148 | 149 | }, completionHandler: { [weak weakSelf = self] in 150 | guard let weakSelf = weakSelf else { return } 151 | 152 | closeSelector.run(on: weakSelf, sender: sender) 153 | 154 | if resetAlphaAfterAnimation { 155 | weakSelf.alphaValue = startingAlpha 156 | } 157 | 158 | completionHandler?() 159 | }) 160 | } 161 | } 162 | 163 | 164 | 165 | public extension CAMediaTimingFunction { 166 | static let easeIn = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) 167 | static let easeOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) 168 | static let easenInEaseOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) 169 | static let linear = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) 170 | static let `default` = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default) 171 | } 172 | -------------------------------------------------------------------------------- /Freelancer/Storyboard/Timer.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Freelancer/Controller/Timer/TimerController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerController.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SQLite 11 | import SwiftDate 12 | 13 | class TimerController: NSViewController, NSApplicationDelegate { 14 | 15 | @IBOutlet weak var vev: NSVisualEffectView! 16 | @IBOutlet var timerWindow: NSView! 17 | @IBOutlet weak var btnStart: NSButton! 18 | @IBOutlet weak var btnEnd: NSButton! 19 | @IBOutlet weak var txtTime: NSTextField! 20 | @IBOutlet weak var progress: NSLevelIndicator! 21 | @IBOutlet weak var lblProgressValue: NSTextField! 22 | 23 | var flTimer = FLTimer() 24 | 25 | var prefs = Preferences() 26 | 27 | var t_from: TimeInterval = 0 28 | var t_to: TimeInterval = 0 29 | var t_total: TimeInterval = 0 30 | 31 | let ts_date = Expression("ts_date") 32 | let ts_from = Expression("ts_from") 33 | let ts_to = Expression("ts_to") 34 | let ts_total_time = Expression("ts_total_time") 35 | let ts_approved = Expression("ts_approved") 36 | 37 | let db = try? Connection("\(NSApp.supportFolderGet())/db.sqlite3") 38 | 39 | let tableTimesheets = Table("timesheets_interval") 40 | 41 | let fmt = DateFormatter() 42 | let format = "yyyy-MM-dd HH:mm:ss" 43 | let formatSec = "HH:mm:ss" 44 | let bgd = Region.current 45 | 46 | var em: EventMonitor? 47 | 48 | override func viewDidLoad() { 49 | super.viewDidLoad() 50 | 51 | flTimer.delegate = self 52 | _ = getRemainingTime(time: 0) 53 | vev?.blendingMode = .behindWindow 54 | 55 | self.view.window?.hasShadow = true 56 | 57 | // if #available(OSX 10.14, *) { 58 | // .material = . 59 | // } else { 60 | // // Fallback on earlier versions 61 | // vev.material = .appearanceBased 62 | // } 63 | } 64 | override func viewDidAppear() { 65 | self.view.window?.isOpaque = false 66 | self.view.window?.backgroundColor = .clear 67 | } 68 | override var representedObject: Any? { 69 | didSet { 70 | // Update the view, if already loaded. 71 | if self.view.isHidden == true { 72 | _ = getRemainingTime(time: 0) 73 | } 74 | } 75 | } 76 | 77 | @IBAction func timerStart(_ sender: Any?) { 78 | // let n = NSUserNotification() 79 | // let nc = NSUserNotificationCenter.default 80 | 81 | 82 | flTimer.startTimer() 83 | 84 | fmt.dateFormat = format 85 | t_from = DateInRegion().timeIntervalSince1970 86 | 87 | btnStart.isEnabled = false 88 | btnEnd.isEnabled = true 89 | 90 | self.view.window?.close() 91 | 92 | // n.title = "Time Logger" 93 | // n.informativeText = "Timer has started logging the time." 94 | // 95 | // n.hasActionButton = true 96 | // 97 | // n.actionButtonTitle = "Stop the timer?" 98 | // n.otherButtonTitle = "👍" 99 | // 100 | // nc.scheduleNotification(n) 101 | 102 | 103 | let viewController:NSViewController = NSStoryboard(name: "Notification", bundle: nil).instantiateController(withIdentifier: "notificationSB") as! NSViewController 104 | 105 | self.presentAsModalWindow(viewController) 106 | } 107 | 108 | @IBAction func timerStop(_ sender: Any?) { 109 | stopTimer() 110 | } 111 | 112 | func stopTimer(stopNow: Bool = false) { 113 | if stopNow == true { 114 | storeTime() 115 | } else { 116 | let ask = askToStop() 117 | if ask == true { 118 | storeTime() 119 | } 120 | } 121 | 122 | } 123 | 124 | func askToStop()-> Bool { 125 | let stop = dialogOKCancel(question: "Stop the timer?", text: "Stopping the timer will add currently worked time to today's time sheet.", btnTrue: "Yes", btnFalse: "Continue working") 126 | 127 | return stop 128 | } 129 | 130 | func storeTime() { 131 | fmt.dateFormat = format 132 | 133 | t_to = DateInRegion().timeIntervalSince1970 134 | 135 | flTimer.stopTimer() 136 | 137 | t_total = txtTime.stringValue.toDate()!.timeIntervalSince1970 138 | 139 | print("From \(t_from) - To \(t_to)") 140 | 141 | fmt.dateFormat = formatSec 142 | if t_from == 0 { 143 | return 144 | } else { 145 | let total = Date(seconds: t_to).timeIntervalSince(Date(seconds: t_from)) 146 | t_total = total 147 | 148 | do { 149 | fmt.dateFormat = format 150 | try db?.run( 151 | tableTimesheets.insert( 152 | ts_date <- t_from, 153 | ts_from <- t_from, 154 | ts_to <- t_to, 155 | ts_total_time <- t_total, 156 | ts_approved <- false 157 | ) 158 | ) 159 | } catch let Result.error(message, code, statement) where code == SQLITE_ANY { 160 | NSAlert.showAlert(title: "\(message)", message: "Error with statement: \(String(describing: statement))") 161 | } catch let error { 162 | NSAlert.showAlert(title: "ERROR", message: "\(error)") 163 | } 164 | 165 | txtTime.stringValue = "00:00:00" 166 | 167 | btnStart.isEnabled = true 168 | btnEnd.isEnabled = false 169 | } 170 | } 171 | 172 | func dialogOKCancel(question: String, text: String, btnTrue: String, btnFalse: String) -> Bool { 173 | let alert = NSAlert() 174 | alert.messageText = question 175 | alert.informativeText = text 176 | alert.alertStyle = .warning 177 | alert.addButton(withTitle: btnTrue) 178 | alert.addButton(withTitle: btnFalse) 179 | return alert.runModal() == .alertFirstButtonReturn 180 | } 181 | 182 | 183 | 184 | } 185 | 186 | extension TimerController { 187 | func updateDisplay(for timeElapsed: TimeInterval) { 188 | txtTime.stringValue = textToDisplay(for: timeElapsed) 189 | let rt = getRemainingTime(time: timeElapsed) 190 | if rt ~= 0 { 191 | storeTime() 192 | NSAlert.showAlert(title: "Time's up!", message: "You've reached maximum billable hours for this week.", style: .warning) 193 | } 194 | 195 | } 196 | 197 | private func textToDisplay(for timeElapsed: TimeInterval) -> String { 198 | if timeElapsed == -1 { 199 | return "00:00:00" 200 | } 201 | 202 | let (h, m, s) = secondsToTime(seconds: Int(timeElapsed)) 203 | let timeElapsedDisplay = "\(String(format: "%02d", h)):\(String(format: "%02d", m)):\(String(format: "%02d", s))" 204 | return timeElapsedDisplay 205 | } 206 | 207 | // 208 | private func secondsToTime( seconds: Int) -> (Int, Int, Int) { 209 | return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60) 210 | } 211 | 212 | func getRemainingTime(time: TimeInterval) -> Int { 213 | let maxHr:TimeInterval = TimeInterval(Int(prefs.maxWeeklyHours) * 3600) 214 | let weekHr:TimeInterval = prefs.weekHours // 3600 215 | var currentHr:TimeInterval = 0 216 | currentHr = maxHr - weekHr - time 217 | 218 | if currentHr <= 0 { 219 | NSAlert.showAlert(title: "Warning", message: "You don't have any billable hours available for this week!\n\nTime logging has been stopped and your hours have been logged.\n\nIf you need to continue working whatsoever, please increase maximum billable hours in the Preferences.", style: .warning) 220 | btnStart.isEnabled = false 221 | btnEnd.isEnabled = false 222 | stopTimer(stopNow: true) 223 | 224 | return 0 225 | } else { 226 | let tStr = currentHr.toString() 227 | let pMax = Double(maxHr / 360) 228 | 229 | progress.maxValue = pMax 230 | progress.warningValue = pMax * 7 * 0.1 231 | progress.criticalValue = pMax * 9 * 0.1 232 | 233 | progress.doubleValue = (weekHr + time) / 360 234 | 235 | lblProgressValue.stringValue = "Remaining billable time: \(tStr)" 236 | 237 | return Int(currentHr) 238 | } 239 | } 240 | } 241 | 242 | extension TimerController: FLTimerProtocol { 243 | func timeElapsedOnTimer(_ timer: FLTimer, timeElapsed: TimeInterval) { 244 | updateDisplay(for: timeElapsed) 245 | } 246 | 247 | func timerHasFinished(_ timer: FLTimer) { 248 | updateDisplay(for: 0) 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Freelancer/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Freelancer 4 | // 5 | // Created by Nikola on 2/4/19. 6 | // Copyright © 2019 Stojković. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SQLite 11 | 12 | @NSApplicationMain 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | 15 | 16 | let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) 17 | let popover = NSPopover() 18 | var eventMonitor: EventMonitor? 19 | 20 | func applicationDidFinishLaunching(_ aNotification: Notification) { 21 | // Insert code here to initialize your application 22 | NSUserNotificationCenter.default.delegate = self 23 | 24 | if let button = statusItem.button { 25 | button.image = NSImage(named: NSImage.Name("icons8-stopwatch")) 26 | button.action = #selector(togglePopover(_:)) 27 | 28 | } 29 | popover.contentViewController = TimerController.freshController() 30 | 31 | eventMonitor = EventMonitor(mask: [.leftMouseDown, .rightMouseDown]) { [weak self] event in 32 | if let strongSelf = self, strongSelf.popover.isShown { 33 | strongSelf.closePopover(sender: event) 34 | } 35 | } 36 | 37 | 38 | 39 | supportFolderCreate() 40 | dbCreateIfMissing() 41 | } 42 | 43 | // func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { 44 | // return true 45 | // } 46 | 47 | @objc func togglePopover(_ sender: Any?) { 48 | if popover.isShown { 49 | closePopover(sender: sender) 50 | } else { 51 | showPopover(sender: sender) 52 | } 53 | } 54 | 55 | func showPopover(sender: Any?) { 56 | NSApplication.shared.activate(ignoringOtherApps: true) 57 | if let button = statusItem.button { 58 | popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY) 59 | popover.contentViewController?.viewDidLoad() 60 | } 61 | eventMonitor?.start() 62 | } 63 | 64 | func closePopover(sender: Any?) { 65 | popover.performClose(sender) 66 | eventMonitor?.stop() 67 | } 68 | 69 | func applicationWillTerminate(_ aNotification: Notification) { 70 | // Insert code here to tear down your application 71 | } 72 | 73 | // MARK: - Core Data stack 74 | 75 | lazy var persistentContainer: NSPersistentContainer = { 76 | /* 77 | The persistent container for the application. This implementation 78 | creates and returns a container, having loaded the store for the 79 | application to it. This property is optional since there are legitimate 80 | error conditions that could cause the creation of the store to fail. 81 | */ 82 | let container = NSPersistentContainer(name: "Freelancer") 83 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in 84 | if let error = error { 85 | // Replace this implementation with code to handle the error appropriately. 86 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 87 | 88 | /* 89 | Typical reasons for an error here include: 90 | * The parent directory does not exist, cannot be created, or disallows writing. 91 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 92 | * The device is out of space. 93 | * The store could not be migrated to the current model version. 94 | Check the error message to determine what the actual problem was. 95 | */ 96 | fatalError("Unresolved error \(error)") 97 | } 98 | }) 99 | return container 100 | }() 101 | 102 | // MARK: - Core Data Saving and Undo support 103 | 104 | @IBAction func saveAction(_ sender: AnyObject?) { 105 | // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user. 106 | let context = persistentContainer.viewContext 107 | 108 | if !context.commitEditing() { 109 | NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing before saving") 110 | } 111 | if context.hasChanges { 112 | do { 113 | try context.save() 114 | } catch { 115 | // Customize this code block to include application-specific recovery steps. 116 | let nserror = error as NSError 117 | NSApplication.shared.presentError(nserror) 118 | } 119 | } 120 | } 121 | 122 | func windowWillReturnUndoManager(window: NSWindow) -> UndoManager? { 123 | // Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application. 124 | return persistentContainer.viewContext.undoManager 125 | } 126 | 127 | func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { 128 | // Save changes in the application's managed object context before the application terminates. 129 | let context = persistentContainer.viewContext 130 | 131 | if !context.commitEditing() { 132 | NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing to terminate") 133 | return .terminateCancel 134 | } 135 | 136 | if !context.hasChanges { 137 | return .terminateNow 138 | } 139 | 140 | do { 141 | try context.save() 142 | } catch { 143 | let nserror = error as NSError 144 | 145 | // Customize this code block to include application-specific recovery steps. 146 | let result = sender.presentError(nserror) 147 | if (result) { 148 | return .terminateCancel 149 | } 150 | 151 | let question = NSLocalizedString("Could not save changes while quitting. Quit anyway?", comment: "Quit without saves error question message") 152 | let info = NSLocalizedString("Quitting now will lose any changes you have made since the last successful save", comment: "Quit without saves error question info"); 153 | let quitButton = NSLocalizedString("Quit anyway", comment: "Quit anyway button title") 154 | let cancelButton = NSLocalizedString("Cancel", comment: "Cancel button title") 155 | let alert = NSAlert() 156 | alert.messageText = question 157 | alert.informativeText = info 158 | alert.addButton(withTitle: quitButton) 159 | alert.addButton(withTitle: cancelButton) 160 | 161 | let answer = alert.runModal() 162 | if answer == .alertSecondButtonReturn { 163 | return .terminateCancel 164 | } 165 | } 166 | // If we got here, it is time to quit. 167 | return .terminateNow 168 | } 169 | 170 | func dbCreateIfMissing() { 171 | let db = try? Connection("\(NSApp.supportFolderGet())/db.sqlite3") 172 | let tableTimesheets = Table("timesheets_interval") 173 | let id = Expression("id") 174 | let ts_date = Expression("ts_date") 175 | let ts_from = Expression("ts_from") 176 | let ts_to = Expression("ts_to") 177 | let ts_total_time = Expression("ts_total_time") 178 | let ts_approved = Expression("ts_approved") 179 | 180 | try! db!.run(tableTimesheets.create(ifNotExists: true) { t in 181 | t.column(id, primaryKey: true) 182 | t.column(ts_date) 183 | t.column(ts_from) 184 | t.column(ts_to) 185 | t.column(ts_total_time) 186 | t.column(ts_approved) 187 | }) 188 | } 189 | 190 | func supportFolderCreate() { 191 | let path = NSSearchPathForDirectoriesInDomains( 192 | .applicationSupportDirectory, .userDomainMask, true 193 | ).first! + "/" + Bundle.main.bundleIdentifier! 194 | 195 | do { 196 | try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true) 197 | } catch { 198 | NSAlert.showAlert(title: "ERROR", message: "Unable to create Application Support Directory due to:\n\(error)", style: .critical ) 199 | } 200 | } 201 | func userNotificationCenter(center: NSUserNotificationCenter, didActivateNotification notification: NSUserNotification) { 202 | switch (notification.activationType) { 203 | case .actionButtonClicked: 204 | NSAlert.showAlert(title: "test", message: "aaaa") 205 | default: 206 | break; 207 | } 208 | } 209 | 210 | } 211 | 212 | extension AppDelegate:NSUserNotificationCenterDelegate{ 213 | func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { 214 | return true 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Freelancer/Storyboard/OnBoarding.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Since this is your first run, you'll need to open the Preferences in order to set the maximum weekly billable hours, your rate per hour and your preferred currency, and then you can start using the app 💁🏻‍♂️ 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Freelancer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 933204382207C1DF0098632C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933204372207C1DF0098632C /* AppDelegate.swift */; }; 11 | 9332043A2207C1DF0098632C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933204392207C1DF0098632C /* ViewController.swift */; }; 12 | 9332043D2207C1DF0098632C /* Freelancer.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 9332043B2207C1DF0098632C /* Freelancer.xcdatamodeld */; }; 13 | 9332043F2207C1E10098632C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9332043E2207C1E10098632C /* Assets.xcassets */; }; 14 | 933204422207C1E10098632C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 933204402207C1E10098632C /* Main.storyboard */; }; 15 | 9332044D2207C22D0098632C /* build_number.sh in Resources */ = {isa = PBXBuildFile; fileRef = 9332044C2207C22D0098632C /* build_number.sh */; }; 16 | 9332044F2207C5F10098632C /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9332044E2207C5F10098632C /* Extensions.swift */; }; 17 | 933204512207C61F0098632C /* Timer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 933204502207C61F0098632C /* Timer.storyboard */; }; 18 | 933204532207C6370098632C /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933204522207C6370098632C /* EventMonitor.swift */; }; 19 | 933204552207C6B40098632C /* TimerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933204542207C6B40098632C /* TimerController.swift */; }; 20 | 9332045D2207FA100098632C /* TodayTimeSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9332045C2207FA100098632C /* TodayTimeSheetViewController.swift */; }; 21 | 93449F7E220A2F3E008BCC01 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93449F7D220A2F3E008BCC01 /* AboutViewController.swift */; }; 22 | 93449F80220A8D78008BCC01 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93449F7F220A8D78008BCC01 /* SQLite.framework */; }; 23 | 93449F81220A8D78008BCC01 /* SQLite.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 93449F7F220A8D78008BCC01 /* SQLite.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 24 | 934AEB3C2207FECA005CCEBD /* WeekTimeSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934AEB3B2207FECA005CCEBD /* WeekTimeSheetViewController.swift */; }; 25 | 934AEB43220804BF005CCEBD /* GeneralPreferenceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934AEB42220804BF005CCEBD /* GeneralPreferenceViewController.swift */; }; 26 | 934AEB5522081057005CCEBD /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934AEB5422081057005CCEBD /* Preferences.swift */; }; 27 | 934AEB572208D126005CCEBD /* AcknowledgmentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934AEB562208D126005CCEBD /* AcknowledgmentsViewController.swift */; }; 28 | 934AEB592208D5CC005CCEBD /* HyperlinkTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934AEB582208D5CC005CCEBD /* HyperlinkTextField.swift */; }; 29 | 934AEB5C220905E6005CCEBD /* FLTimerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934AEB5B220905E6005CCEBD /* FLTimerProtocol.swift */; }; 30 | 935D690522257B67003B3249 /* Notification.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 935D690422257B67003B3249 /* Notification.storyboard */; }; 31 | 935D690822257CC1003B3249 /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 935D690722257CC1003B3249 /* NotificationViewController.swift */; }; 32 | 935D690A22259E82003B3249 /* temp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 935D690922259E82003B3249 /* temp.swift */; }; 33 | 935D690C222CFA5D003B3249 /* OnBoarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 935D690B222CFA5D003B3249 /* OnBoarding.storyboard */; }; 34 | 935D690F222CFBF8003B3249 /* OnBoardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 935D690E222CFBF8003B3249 /* OnBoardingViewController.swift */; }; 35 | 935D6911222D99A8003B3249 /* CustomAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 935D6910222D99A8003B3249 /* CustomAnimator.swift */; }; 36 | 935D6914222F5096003B3249 /* FD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 935D6913222F5096003B3249 /* FD.swift */; }; 37 | 93908AC122198AE900CC4E11 /* EarningsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93908AC022198AE900CC4E11 /* EarningsViewController.swift */; }; 38 | 93E71E1A220E1E2E00361944 /* SwiftDate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93E71E19220E1E2E00361944 /* SwiftDate.framework */; }; 39 | 93E71E1B220E1E2E00361944 /* SwiftDate.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 93E71E19220E1E2E00361944 /* SwiftDate.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXBuildRule section */ 43 | 9332044A2207C1F90098632C /* PBXBuildRule */ = { 44 | isa = PBXBuildRule; 45 | compilerSpec = com.apple.compilers.proxy.script; 46 | fileType = pattern.proxy; 47 | isEditable = 1; 48 | outputFiles = ( 49 | ); 50 | script = "# Type a script or drag a script file from your workspace to insert its path.\n"; 51 | }; 52 | /* End PBXBuildRule section */ 53 | 54 | /* Begin PBXCopyFilesBuildPhase section */ 55 | 93449F7C220A29FD008BCC01 /* Embed Frameworks */ = { 56 | isa = PBXCopyFilesBuildPhase; 57 | buildActionMask = 2147483647; 58 | dstPath = ""; 59 | dstSubfolderSpec = 10; 60 | files = ( 61 | 93449F81220A8D78008BCC01 /* SQLite.framework in Embed Frameworks */, 62 | 93E71E1B220E1E2E00361944 /* SwiftDate.framework in Embed Frameworks */, 63 | ); 64 | name = "Embed Frameworks"; 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | 93908AB82218508100CC4E11 /* CopyFiles */ = { 68 | isa = PBXCopyFilesBuildPhase; 69 | buildActionMask = 2147483647; 70 | dstPath = ""; 71 | dstSubfolderSpec = 10; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXCopyFilesBuildPhase section */ 77 | 78 | /* Begin PBXFileReference section */ 79 | 933204342207C1DF0098632C /* Freelancer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Freelancer.app; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | 933204372207C1DF0098632C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 81 | 933204392207C1DF0098632C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 82 | 9332043C2207C1DF0098632C /* Freelancer.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Freelancer.xcdatamodel; sourceTree = ""; }; 83 | 9332043E2207C1E10098632C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 84 | 933204412207C1E10098632C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 85 | 933204432207C1E10098632C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 86 | 933204442207C1E10098632C /* Freelancer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Freelancer.entitlements; sourceTree = ""; }; 87 | 9332044C2207C22D0098632C /* build_number.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_number.sh; sourceTree = ""; }; 88 | 9332044E2207C5F10098632C /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 89 | 933204502207C61F0098632C /* Timer.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Timer.storyboard; sourceTree = ""; }; 90 | 933204522207C6370098632C /* EventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMonitor.swift; sourceTree = ""; }; 91 | 933204542207C6B40098632C /* TimerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerController.swift; sourceTree = ""; }; 92 | 9332045C2207FA100098632C /* TodayTimeSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayTimeSheetViewController.swift; sourceTree = ""; }; 93 | 93449F7D220A2F3E008BCC01 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 94 | 93449F7F220A8D78008BCC01 /* SQLite.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SQLite.framework; path = Carthage/Build/Mac/SQLite.framework; sourceTree = ""; }; 95 | 934AEB3B2207FECA005CCEBD /* WeekTimeSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekTimeSheetViewController.swift; sourceTree = ""; }; 96 | 934AEB42220804BF005CCEBD /* GeneralPreferenceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPreferenceViewController.swift; sourceTree = ""; }; 97 | 934AEB5422081057005CCEBD /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; 98 | 934AEB562208D126005CCEBD /* AcknowledgmentsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcknowledgmentsViewController.swift; sourceTree = ""; }; 99 | 934AEB582208D5CC005CCEBD /* HyperlinkTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HyperlinkTextField.swift; sourceTree = ""; }; 100 | 934AEB5B220905E6005CCEBD /* FLTimerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FLTimerProtocol.swift; sourceTree = ""; }; 101 | 935D690422257B67003B3249 /* Notification.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Notification.storyboard; sourceTree = ""; }; 102 | 935D690722257CC1003B3249 /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = ""; }; 103 | 935D690922259E82003B3249 /* temp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = temp.swift; sourceTree = ""; }; 104 | 935D690B222CFA5D003B3249 /* OnBoarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = OnBoarding.storyboard; sourceTree = ""; }; 105 | 935D690E222CFBF8003B3249 /* OnBoardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingViewController.swift; sourceTree = ""; }; 106 | 935D6910222D99A8003B3249 /* CustomAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAnimator.swift; sourceTree = ""; }; 107 | 935D6913222F5096003B3249 /* FD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FD.swift; sourceTree = ""; }; 108 | 93908AC022198AE900CC4E11 /* EarningsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EarningsViewController.swift; sourceTree = ""; }; 109 | 93E71E19220E1E2E00361944 /* SwiftDate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftDate.framework; path = Carthage/Build/Mac/SwiftDate.framework; sourceTree = ""; }; 110 | /* End PBXFileReference section */ 111 | 112 | /* Begin PBXFrameworksBuildPhase section */ 113 | 933204312207C1DF0098632C /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | 93449F80220A8D78008BCC01 /* SQLite.framework in Frameworks */, 118 | 93E71E1A220E1E2E00361944 /* SwiftDate.framework in Frameworks */, 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXFrameworksBuildPhase section */ 123 | 124 | /* Begin PBXGroup section */ 125 | 9332042B2207C1DF0098632C = { 126 | isa = PBXGroup; 127 | children = ( 128 | 9332044C2207C22D0098632C /* build_number.sh */, 129 | 933204362207C1DF0098632C /* Freelancer */, 130 | 933204352207C1DF0098632C /* Products */, 131 | 93F38B90220A28F300F099A2 /* Frameworks */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | 933204352207C1DF0098632C /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 933204342207C1DF0098632C /* Freelancer.app */, 139 | ); 140 | name = Products; 141 | sourceTree = ""; 142 | }; 143 | 933204362207C1DF0098632C /* Freelancer */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 935D6912222F5082003B3249 /* Extensions */, 147 | 934AEB5322081043005CCEBD /* Model */, 148 | 933204572207E6090098632C /* Storyboard */, 149 | 933204562207E5E40098632C /* Controller */, 150 | 933204372207C1DF0098632C /* AppDelegate.swift */, 151 | 9332043E2207C1E10098632C /* Assets.xcassets */, 152 | 933204432207C1E10098632C /* Info.plist */, 153 | 933204442207C1E10098632C /* Freelancer.entitlements */, 154 | 9332043B2207C1DF0098632C /* Freelancer.xcdatamodeld */, 155 | ); 156 | path = Freelancer; 157 | sourceTree = ""; 158 | }; 159 | 933204562207E5E40098632C /* Controller */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 935D690D222CFBC2003B3249 /* OnBoarding */, 163 | 935D690622257CA9003B3249 /* Notification */, 164 | 93908ABF22198AA800CC4E11 /* Earnings */, 165 | 934AEB5A220905B2005CCEBD /* Timer */, 166 | 934AEB41220804AE005CCEBD /* Preferences */, 167 | 9332045B2207F9FC0098632C /* Time Sheets */, 168 | 933204392207C1DF0098632C /* ViewController.swift */, 169 | 933204522207C6370098632C /* EventMonitor.swift */, 170 | 934AEB582208D5CC005CCEBD /* HyperlinkTextField.swift */, 171 | 93449F7D220A2F3E008BCC01 /* AboutViewController.swift */, 172 | ); 173 | path = Controller; 174 | sourceTree = ""; 175 | }; 176 | 933204572207E6090098632C /* Storyboard */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 933204402207C1E10098632C /* Main.storyboard */, 180 | 933204502207C61F0098632C /* Timer.storyboard */, 181 | 935D690422257B67003B3249 /* Notification.storyboard */, 182 | 935D690B222CFA5D003B3249 /* OnBoarding.storyboard */, 183 | ); 184 | path = Storyboard; 185 | sourceTree = ""; 186 | }; 187 | 9332045B2207F9FC0098632C /* Time Sheets */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 9332045C2207FA100098632C /* TodayTimeSheetViewController.swift */, 191 | 934AEB3B2207FECA005CCEBD /* WeekTimeSheetViewController.swift */, 192 | ); 193 | path = "Time Sheets"; 194 | sourceTree = ""; 195 | }; 196 | 934AEB41220804AE005CCEBD /* Preferences */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | 934AEB42220804BF005CCEBD /* GeneralPreferenceViewController.swift */, 200 | ); 201 | path = Preferences; 202 | sourceTree = ""; 203 | }; 204 | 934AEB5322081043005CCEBD /* Model */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 934AEB5422081057005CCEBD /* Preferences.swift */, 208 | 934AEB5B220905E6005CCEBD /* FLTimerProtocol.swift */, 209 | 935D6910222D99A8003B3249 /* CustomAnimator.swift */, 210 | ); 211 | path = Model; 212 | sourceTree = ""; 213 | }; 214 | 934AEB5A220905B2005CCEBD /* Timer */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 933204542207C6B40098632C /* TimerController.swift */, 218 | 934AEB562208D126005CCEBD /* AcknowledgmentsViewController.swift */, 219 | ); 220 | path = Timer; 221 | sourceTree = ""; 222 | }; 223 | 935D690622257CA9003B3249 /* Notification */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | 935D690722257CC1003B3249 /* NotificationViewController.swift */, 227 | ); 228 | path = Notification; 229 | sourceTree = ""; 230 | }; 231 | 935D690D222CFBC2003B3249 /* OnBoarding */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 935D690E222CFBF8003B3249 /* OnBoardingViewController.swift */, 235 | ); 236 | path = OnBoarding; 237 | sourceTree = ""; 238 | }; 239 | 935D6912222F5082003B3249 /* Extensions */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | 9332044E2207C5F10098632C /* Extensions.swift */, 243 | 935D6913222F5096003B3249 /* FD.swift */, 244 | 935D690922259E82003B3249 /* temp.swift */, 245 | ); 246 | path = Extensions; 247 | sourceTree = ""; 248 | }; 249 | 93908ABF22198AA800CC4E11 /* Earnings */ = { 250 | isa = PBXGroup; 251 | children = ( 252 | 93908AC022198AE900CC4E11 /* EarningsViewController.swift */, 253 | ); 254 | path = Earnings; 255 | sourceTree = ""; 256 | }; 257 | 93F38B90220A28F300F099A2 /* Frameworks */ = { 258 | isa = PBXGroup; 259 | children = ( 260 | 93E71E19220E1E2E00361944 /* SwiftDate.framework */, 261 | 93449F7F220A8D78008BCC01 /* SQLite.framework */, 262 | ); 263 | name = Frameworks; 264 | sourceTree = ""; 265 | }; 266 | /* End PBXGroup section */ 267 | 268 | /* Begin PBXNativeTarget section */ 269 | 933204332207C1DF0098632C /* Freelancer */ = { 270 | isa = PBXNativeTarget; 271 | buildConfigurationList = 933204472207C1E10098632C /* Build configuration list for PBXNativeTarget "Freelancer" */; 272 | buildPhases = ( 273 | 933204302207C1DF0098632C /* Sources */, 274 | 933204312207C1DF0098632C /* Frameworks */, 275 | 933204322207C1DF0098632C /* Resources */, 276 | 9332044B2207C2090098632C /* ShellScript */, 277 | 93449F7C220A29FD008BCC01 /* Embed Frameworks */, 278 | 93908AB82218508100CC4E11 /* CopyFiles */, 279 | ); 280 | buildRules = ( 281 | 9332044A2207C1F90098632C /* PBXBuildRule */, 282 | ); 283 | dependencies = ( 284 | ); 285 | name = Freelancer; 286 | productName = Freelancer; 287 | productReference = 933204342207C1DF0098632C /* Freelancer.app */; 288 | productType = "com.apple.product-type.application"; 289 | }; 290 | /* End PBXNativeTarget section */ 291 | 292 | /* Begin PBXProject section */ 293 | 9332042C2207C1DF0098632C /* Project object */ = { 294 | isa = PBXProject; 295 | attributes = { 296 | LastSwiftUpdateCheck = 1020; 297 | LastUpgradeCheck = 1020; 298 | ORGANIZATIONNAME = "Stojković"; 299 | TargetAttributes = { 300 | 933204332207C1DF0098632C = { 301 | CreatedOnToolsVersion = 10.2; 302 | LastSwiftMigration = 1020; 303 | SystemCapabilities = { 304 | com.apple.ApplicationGroups.Mac = { 305 | enabled = 0; 306 | }; 307 | com.apple.HardenedRuntime = { 308 | enabled = 0; 309 | }; 310 | com.apple.Sandbox = { 311 | enabled = 0; 312 | }; 313 | }; 314 | }; 315 | }; 316 | }; 317 | buildConfigurationList = 9332042F2207C1DF0098632C /* Build configuration list for PBXProject "Freelancer" */; 318 | compatibilityVersion = "Xcode 9.3"; 319 | developmentRegion = en; 320 | hasScannedForEncodings = 0; 321 | knownRegions = ( 322 | en, 323 | Base, 324 | ); 325 | mainGroup = 9332042B2207C1DF0098632C; 326 | productRefGroup = 933204352207C1DF0098632C /* Products */; 327 | projectDirPath = ""; 328 | projectRoot = ""; 329 | targets = ( 330 | 933204332207C1DF0098632C /* Freelancer */, 331 | ); 332 | }; 333 | /* End PBXProject section */ 334 | 335 | /* Begin PBXResourcesBuildPhase section */ 336 | 933204322207C1DF0098632C /* Resources */ = { 337 | isa = PBXResourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 933204512207C61F0098632C /* Timer.storyboard in Resources */, 341 | 9332043F2207C1E10098632C /* Assets.xcassets in Resources */, 342 | 933204422207C1E10098632C /* Main.storyboard in Resources */, 343 | 9332044D2207C22D0098632C /* build_number.sh in Resources */, 344 | 935D690522257B67003B3249 /* Notification.storyboard in Resources */, 345 | 935D690C222CFA5D003B3249 /* OnBoarding.storyboard in Resources */, 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | /* End PBXResourcesBuildPhase section */ 350 | 351 | /* Begin PBXShellScriptBuildPhase section */ 352 | 9332044B2207C2090098632C /* ShellScript */ = { 353 | isa = PBXShellScriptBuildPhase; 354 | buildActionMask = 2147483647; 355 | files = ( 356 | ); 357 | inputFileListPaths = ( 358 | ); 359 | inputPaths = ( 360 | ); 361 | outputFileListPaths = ( 362 | ); 363 | outputPaths = ( 364 | ); 365 | runOnlyForDeploymentPostprocessing = 0; 366 | shellPath = /bin/zsh; 367 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nzsh \"${SRCROOT}/build_number.sh\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n"; 368 | }; 369 | /* End PBXShellScriptBuildPhase section */ 370 | 371 | /* Begin PBXSourcesBuildPhase section */ 372 | 933204302207C1DF0098632C /* Sources */ = { 373 | isa = PBXSourcesBuildPhase; 374 | buildActionMask = 2147483647; 375 | files = ( 376 | 934AEB43220804BF005CCEBD /* GeneralPreferenceViewController.swift in Sources */, 377 | 935D6911222D99A8003B3249 /* CustomAnimator.swift in Sources */, 378 | 9332043D2207C1DF0098632C /* Freelancer.xcdatamodeld in Sources */, 379 | 935D690822257CC1003B3249 /* NotificationViewController.swift in Sources */, 380 | 933204532207C6370098632C /* EventMonitor.swift in Sources */, 381 | 9332043A2207C1DF0098632C /* ViewController.swift in Sources */, 382 | 933204382207C1DF0098632C /* AppDelegate.swift in Sources */, 383 | 934AEB572208D126005CCEBD /* AcknowledgmentsViewController.swift in Sources */, 384 | 93908AC122198AE900CC4E11 /* EarningsViewController.swift in Sources */, 385 | 934AEB5C220905E6005CCEBD /* FLTimerProtocol.swift in Sources */, 386 | 9332044F2207C5F10098632C /* Extensions.swift in Sources */, 387 | 934AEB5522081057005CCEBD /* Preferences.swift in Sources */, 388 | 934AEB592208D5CC005CCEBD /* HyperlinkTextField.swift in Sources */, 389 | 935D6914222F5096003B3249 /* FD.swift in Sources */, 390 | 93449F7E220A2F3E008BCC01 /* AboutViewController.swift in Sources */, 391 | 935D690A22259E82003B3249 /* temp.swift in Sources */, 392 | 934AEB3C2207FECA005CCEBD /* WeekTimeSheetViewController.swift in Sources */, 393 | 935D690F222CFBF8003B3249 /* OnBoardingViewController.swift in Sources */, 394 | 933204552207C6B40098632C /* TimerController.swift in Sources */, 395 | 9332045D2207FA100098632C /* TodayTimeSheetViewController.swift in Sources */, 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | }; 399 | /* End PBXSourcesBuildPhase section */ 400 | 401 | /* Begin PBXVariantGroup section */ 402 | 933204402207C1E10098632C /* Main.storyboard */ = { 403 | isa = PBXVariantGroup; 404 | children = ( 405 | 933204412207C1E10098632C /* Base */, 406 | ); 407 | name = Main.storyboard; 408 | sourceTree = ""; 409 | }; 410 | /* End PBXVariantGroup section */ 411 | 412 | /* Begin XCBuildConfiguration section */ 413 | 933204452207C1E10098632C /* Debug */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | ALWAYS_SEARCH_USER_PATHS = NO; 417 | CLANG_ANALYZER_NONNULL = YES; 418 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 419 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 420 | CLANG_CXX_LIBRARY = "libc++"; 421 | CLANG_ENABLE_MODULES = YES; 422 | CLANG_ENABLE_OBJC_ARC = YES; 423 | CLANG_ENABLE_OBJC_WEAK = YES; 424 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 425 | CLANG_WARN_BOOL_CONVERSION = YES; 426 | CLANG_WARN_COMMA = YES; 427 | CLANG_WARN_CONSTANT_CONVERSION = YES; 428 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 429 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 430 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 431 | CLANG_WARN_EMPTY_BODY = YES; 432 | CLANG_WARN_ENUM_CONVERSION = YES; 433 | CLANG_WARN_INFINITE_RECURSION = YES; 434 | CLANG_WARN_INT_CONVERSION = YES; 435 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 437 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 439 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 440 | CLANG_WARN_STRICT_PROTOTYPES = YES; 441 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 442 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 443 | CLANG_WARN_UNREACHABLE_CODE = YES; 444 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 445 | CODE_SIGN_IDENTITY = "Mac Developer"; 446 | COPY_PHASE_STRIP = NO; 447 | DEBUG_INFORMATION_FORMAT = dwarf; 448 | ENABLE_STRICT_OBJC_MSGSEND = YES; 449 | ENABLE_TESTABILITY = YES; 450 | GCC_C_LANGUAGE_STANDARD = gnu11; 451 | GCC_DYNAMIC_NO_PIC = NO; 452 | GCC_NO_COMMON_BLOCKS = YES; 453 | GCC_OPTIMIZATION_LEVEL = 0; 454 | GCC_PREPROCESSOR_DEFINITIONS = ( 455 | "DEBUG=1", 456 | "$(inherited)", 457 | ); 458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 460 | GCC_WARN_UNDECLARED_SELECTOR = YES; 461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 462 | GCC_WARN_UNUSED_FUNCTION = YES; 463 | GCC_WARN_UNUSED_VARIABLE = YES; 464 | MACOSX_DEPLOYMENT_TARGET = 10.13; 465 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 466 | MTL_FAST_MATH = YES; 467 | ONLY_ACTIVE_ARCH = YES; 468 | SDKROOT = macosx; 469 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 470 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 471 | }; 472 | name = Debug; 473 | }; 474 | 933204462207C1E10098632C /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ALWAYS_SEARCH_USER_PATHS = NO; 478 | CLANG_ANALYZER_NONNULL = YES; 479 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 480 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 481 | CLANG_CXX_LIBRARY = "libc++"; 482 | CLANG_ENABLE_MODULES = YES; 483 | CLANG_ENABLE_OBJC_ARC = YES; 484 | CLANG_ENABLE_OBJC_WEAK = YES; 485 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 486 | CLANG_WARN_BOOL_CONVERSION = YES; 487 | CLANG_WARN_COMMA = YES; 488 | CLANG_WARN_CONSTANT_CONVERSION = YES; 489 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 490 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 491 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 492 | CLANG_WARN_EMPTY_BODY = YES; 493 | CLANG_WARN_ENUM_CONVERSION = YES; 494 | CLANG_WARN_INFINITE_RECURSION = YES; 495 | CLANG_WARN_INT_CONVERSION = YES; 496 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 497 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 498 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 499 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 500 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 501 | CLANG_WARN_STRICT_PROTOTYPES = YES; 502 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 503 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 504 | CLANG_WARN_UNREACHABLE_CODE = YES; 505 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 506 | CODE_SIGN_IDENTITY = "Mac Developer"; 507 | COPY_PHASE_STRIP = NO; 508 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 509 | ENABLE_NS_ASSERTIONS = NO; 510 | ENABLE_STRICT_OBJC_MSGSEND = YES; 511 | GCC_C_LANGUAGE_STANDARD = gnu11; 512 | GCC_NO_COMMON_BLOCKS = YES; 513 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 514 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 515 | GCC_WARN_UNDECLARED_SELECTOR = YES; 516 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 517 | GCC_WARN_UNUSED_FUNCTION = YES; 518 | GCC_WARN_UNUSED_VARIABLE = YES; 519 | MACOSX_DEPLOYMENT_TARGET = 10.13; 520 | MTL_ENABLE_DEBUG_INFO = NO; 521 | MTL_FAST_MATH = YES; 522 | SDKROOT = macosx; 523 | SWIFT_COMPILATION_MODE = wholemodule; 524 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 525 | }; 526 | name = Release; 527 | }; 528 | 933204482207C1E10098632C /* Debug */ = { 529 | isa = XCBuildConfiguration; 530 | buildSettings = { 531 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 532 | CODE_SIGN_ENTITLEMENTS = Freelancer/Freelancer.entitlements; 533 | CODE_SIGN_IDENTITY = "Mac Developer"; 534 | CODE_SIGN_STYLE = Automatic; 535 | COMBINE_HIDPI_IMAGES = YES; 536 | DEVELOPMENT_TEAM = JW47XGYL6N; 537 | FRAMEWORK_SEARCH_PATHS = ( 538 | "$(inherited)", 539 | "$(PROJECT_DIR)", 540 | "$(PROJECT_DIR)/Carthage/Build/Mac", 541 | "$(PROJECT_DIR)/Freelancer", 542 | ); 543 | INFOPLIST_FILE = Freelancer/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = ( 545 | "$(inherited)", 546 | "@executable_path/../Frameworks", 547 | "@loader_path/../Frameworks", 548 | ); 549 | MACOSX_DEPLOYMENT_TARGET = 10.13; 550 | PRODUCT_BUNDLE_IDENTIFIER = pro.lingur.Freelancer; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | PROVISIONING_PROFILE_SPECIFIER = ""; 553 | "SWIFT_OBJC_BRIDGING_HEADER[arch=*]" = ""; 554 | SWIFT_VERSION = 5.0; 555 | }; 556 | name = Debug; 557 | }; 558 | 933204492207C1E10098632C /* Release */ = { 559 | isa = XCBuildConfiguration; 560 | buildSettings = { 561 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 562 | CODE_SIGN_ENTITLEMENTS = Freelancer/Freelancer.entitlements; 563 | CODE_SIGN_IDENTITY = "Mac Developer"; 564 | CODE_SIGN_STYLE = Automatic; 565 | COMBINE_HIDPI_IMAGES = YES; 566 | DEVELOPMENT_TEAM = JW47XGYL6N; 567 | FRAMEWORK_SEARCH_PATHS = ( 568 | "$(inherited)", 569 | "$(PROJECT_DIR)", 570 | "$(PROJECT_DIR)/Carthage/Build/Mac", 571 | "$(PROJECT_DIR)/Freelancer", 572 | ); 573 | INFOPLIST_FILE = Freelancer/Info.plist; 574 | LD_RUNPATH_SEARCH_PATHS = ( 575 | "$(inherited)", 576 | "@executable_path/../Frameworks", 577 | "@loader_path/../Frameworks", 578 | ); 579 | MACOSX_DEPLOYMENT_TARGET = 10.13; 580 | PRODUCT_BUNDLE_IDENTIFIER = pro.lingur.Freelancer; 581 | PRODUCT_NAME = "$(TARGET_NAME)"; 582 | PROVISIONING_PROFILE_SPECIFIER = ""; 583 | SWIFT_VERSION = 5.0; 584 | }; 585 | name = Release; 586 | }; 587 | /* End XCBuildConfiguration section */ 588 | 589 | /* Begin XCConfigurationList section */ 590 | 9332042F2207C1DF0098632C /* Build configuration list for PBXProject "Freelancer" */ = { 591 | isa = XCConfigurationList; 592 | buildConfigurations = ( 593 | 933204452207C1E10098632C /* Debug */, 594 | 933204462207C1E10098632C /* Release */, 595 | ); 596 | defaultConfigurationIsVisible = 0; 597 | defaultConfigurationName = Release; 598 | }; 599 | 933204472207C1E10098632C /* Build configuration list for PBXNativeTarget "Freelancer" */ = { 600 | isa = XCConfigurationList; 601 | buildConfigurations = ( 602 | 933204482207C1E10098632C /* Debug */, 603 | 933204492207C1E10098632C /* Release */, 604 | ); 605 | defaultConfigurationIsVisible = 0; 606 | defaultConfigurationName = Release; 607 | }; 608 | /* End XCConfigurationList section */ 609 | 610 | /* Begin XCVersionGroup section */ 611 | 9332043B2207C1DF0098632C /* Freelancer.xcdatamodeld */ = { 612 | isa = XCVersionGroup; 613 | children = ( 614 | 9332043C2207C1DF0098632C /* Freelancer.xcdatamodel */, 615 | ); 616 | currentVersion = 9332043C2207C1DF0098632C /* Freelancer.xcdatamodel */; 617 | path = Freelancer.xcdatamodeld; 618 | sourceTree = ""; 619 | versionGroupType = wrapper.xcdatamodel; 620 | }; 621 | /* End XCVersionGroup section */ 622 | }; 623 | rootObject = 9332042C2207C1DF0098632C /* Project object */; 624 | } 625 | --------------------------------------------------------------------------------