├── 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 | 
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 |  
6 |
7 |   
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 | 
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 | 
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 |
--------------------------------------------------------------------------------