diff --git a/TraccarClient.xcodeproj/project.pbxproj b/TraccarClient.xcodeproj/project.pbxproj index e54a3fcba7045cef1bfe2e85048a5467e0e3a36c..04660e65fe0749020893f52354ffaddc1cb2bb74 100644 --- a/TraccarClient.xcodeproj/project.pbxproj +++ b/TraccarClient.xcodeproj/project.pbxproj @@ -92,6 +92,7 @@ 532683C12AEA50FA00A364C0 /* LoginViewControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532683C02AEA50FA00A364C0 /* LoginViewControllerProtocol.swift */; }; 532683C32AEA537F00A364C0 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532683C22AEA537F00A364C0 /* UITableView.swift */; }; 532683C82AEA631800A364C0 /* IQKeyboardManagerSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 532683C72AEA631800A364C0 /* IQKeyboardManagerSwift */; }; + 532FDC1B2B00BED500EDFC99 /* Network+Shifts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532FDC1A2B00BED500EDFC99 /* Network+Shifts.swift */; }; 53554ADF2AED1B480018BAEE /* KNButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53554AD92AED1B480018BAEE /* KNButtonView.swift */; }; 53554AE02AED1B480018BAEE /* KNButtonViewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53554ADA2AED1B480018BAEE /* KNButtonViewHelper.swift */; }; 53554AE12AED1B480018BAEE /* ButtonTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 53554ADC2AED1B480018BAEE /* ButtonTableViewCell.xib */; }; @@ -99,6 +100,7 @@ 53554AE32AED1B480018BAEE /* KNButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 53554ADE2AED1B480018BAEE /* KNButtonView.xib */; }; 53554AE52AED1BF10018BAEE /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53554AE42AED1BF10018BAEE /* UIButton.swift */; }; 5370B4702AEFB8A900AE08CC /* splash.json in Resources */ = {isa = PBXBuildFile; fileRef = 5370B46F2AEFB8A900AE08CC /* splash.json */; }; + 5392C4232B037942004EF18A /* ShiftsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5392C4222B037942004EF18A /* ShiftsModel.swift */; }; 53D62E3F2AEFA31200C80BAC /* InitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D62E3D2AEFA31200C80BAC /* InitialViewController.swift */; }; 53D62E402AEFA31200C80BAC /* InitialViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 53D62E3E2AEFA31200C80BAC /* InitialViewController.xib */; }; 53D62E432AEFA4DC00C80BAC /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 53D62E422AEFA4DC00C80BAC /* Lottie */; }; @@ -262,6 +264,7 @@ 532683BE2AEA50EF00A364C0 /* LoginDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDataProvider.swift; sourceTree = "<group>"; }; 532683C02AEA50FA00A364C0 /* LoginViewControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewControllerProtocol.swift; sourceTree = "<group>"; }; 532683C22AEA537F00A364C0 /* UITableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = "<group>"; }; + 532FDC1A2B00BED500EDFC99 /* Network+Shifts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Network+Shifts.swift"; sourceTree = "<group>"; }; 53554AD92AED1B480018BAEE /* KNButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KNButtonView.swift; sourceTree = "<group>"; }; 53554ADA2AED1B480018BAEE /* KNButtonViewHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KNButtonViewHelper.swift; sourceTree = "<group>"; }; 53554ADC2AED1B480018BAEE /* ButtonTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ButtonTableViewCell.xib; sourceTree = "<group>"; }; @@ -269,6 +272,7 @@ 53554ADE2AED1B480018BAEE /* KNButtonView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KNButtonView.xib; sourceTree = "<group>"; }; 53554AE42AED1BF10018BAEE /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = "<group>"; }; 5370B46F2AEFB8A900AE08CC /* splash.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = splash.json; sourceTree = "<group>"; }; + 5392C4222B037942004EF18A /* ShiftsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftsModel.swift; sourceTree = "<group>"; }; 53D62E3D2AEFA31200C80BAC /* InitialViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialViewController.swift; sourceTree = "<group>"; }; 53D62E3E2AEFA31200C80BAC /* InitialViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InitialViewController.xib; sourceTree = "<group>"; }; 53EBC5FC2AF199FF00601AA7 /* SettingsTextTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTextTableViewCell.swift; sourceTree = "<group>"; }; @@ -435,6 +439,7 @@ 53157EC92AEE4DFE003C9B6A /* DeviceModel.swift */, 53FFDB9F2AFA1E6C0071F396 /* PermissionsModel.swift */, 53FFDBA12AFA200E0071F396 /* TrackingParametersModel.swift */, + 5392C4222B037942004EF18A /* ShiftsModel.swift */, ); name = Models; sourceTree = "<group>"; @@ -573,6 +578,7 @@ 532683B42AE98FB600A364C0 /* UIString.swift */, 53157EC22AEE3FF9003C9B6A /* API+Auth.swift */, 53FFDB9A2AF8FD1D0071F396 /* API+Permissions.swift */, + 532FDC1A2B00BED500EDFC99 /* Network+Shifts.swift */, ); name = Network; sourceTree = "<group>"; @@ -1037,6 +1043,7 @@ 53157ED82AEE5751003C9B6A /* KNAlertViewController.swift in Sources */, 5326837A2AE923A500A364C0 /* KNFieldDropDownAddressObjectProtocol.swift in Sources */, 53EBC5FE2AF199FF00601AA7 /* SettingsTextTableViewCell.swift in Sources */, + 5392C4232B037942004EF18A /* ShiftsModel.swift in Sources */, CBAA0F7F1F68E807008BBBBE /* MainViewController.swift in Sources */, 532683B52AE98FB600A364C0 /* UIString.swift in Sources */, 5326839C2AE9482300A364C0 /* Network+Error.swift in Sources */, @@ -1055,6 +1062,7 @@ 5326837C2AE923A500A364C0 /* KNField.swift in Sources */, 53157ECA2AEE4DFE003C9B6A /* DeviceModel.swift in Sources */, 532683AB2AE94BA500A364C0 /* UIView.swift in Sources */, + 532FDC1B2B00BED500EDFC99 /* Network+Shifts.swift in Sources */, 5326839A2AE9482300A364C0 /* Network+Types.swift in Sources */, CB4197991F67724F008F301C /* NetworkManager.swift in Sources */, 532683B32AE98D0000A364C0 /* UILabel.swift in Sources */, diff --git a/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate index ea9252c735be2e5812623d170c6ff2795900089d..e8439dcef841c8139ed9e9161e8a8ef2b1456eff 100644 Binary files a/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate and b/TraccarClient.xcodeproj/project.xcworkspace/xcuserdata/g.makhoul.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TraccarClient/API+Auth.swift b/TraccarClient/API+Auth.swift index f86f7cbf8199b46763b175598afbf7f06211140e..b22993c5f481d265c0657bcb021abb62f0f8064c 100644 --- a/TraccarClient/API+Auth.swift +++ b/TraccarClient/API+Auth.swift @@ -33,5 +33,10 @@ extension NetworkRequest { completion(result) } } + func check(_ completion: @escaping ResultClosure) { + request(endpoint: endpoint(.check), method: .get) { result in + completion(result) + } + } } diff --git a/TraccarClient/InitialViewController/Controller/InitialViewController.swift b/TraccarClient/InitialViewController/Controller/InitialViewController.swift index f38220b86558df18f234f7570fb11fbbeef260af..8384c65e69b0d5c363f1d0885eb4dd493a02fc4c 100644 --- a/TraccarClient/InitialViewController/Controller/InitialViewController.swift +++ b/TraccarClient/InitialViewController/Controller/InitialViewController.swift @@ -16,6 +16,7 @@ final class InitialViewController: KNViewController { // MARK: - Properties private let animationFileName: String = "splash" var window: UIWindow? + private var shift: ShiftModel? // MARK: - LifeCycle override func viewDidLoad() { @@ -52,20 +53,64 @@ extension InitialViewController { window?.makeKeyAndVisible() } else { - api.permissions { result in + + api.check { result in if result.success { - api.parameters { result in + api.my_shift { [weak self] result in + guard let strongSelf = self else { return } if result.success { - let connect = ConnectViewController() - let navigation = KNNavigationController(rootViewController: connect) - navigation.modalPresentationStyle = .overFullScreen + if let shift = result.data as? ShiftModel { + strongSelf.shift = shift + api.permissions { result in + if result.success { + api.parameters { result in + if result.success { + let connect = ConnectViewController() + connect.shift = strongSelf.shift + let navigation = KNNavigationController(rootViewController: connect) + navigation.modalPresentationStyle = .overFullScreen + strongSelf.window?.rootViewController = navigation + strongSelf.window?.makeKeyAndVisible() + } else { + ok(result) + } + } + } else { + ok(result) + } + } + } + } else { + ok(result) + } + } + } else { + if result.statusCode == 401 { + alert(confirm: "Ok", message: result.displayError) { + // clear userDefaults + UserModel.clearUserFromUserDefaults() + DeviceModel.clearUserFromUserDefaults() + SignInModel.clearUserFromUserDefaults() + + // navigate to signin + let login = LoginViewController() + let navigation = KNNavigationController(rootViewController: login) + navigation.modalPresentationStyle = .overFullScreen + + let coordinator = AuthenticationCoordinator(navigationController: navigation) + coordinator.step = .signin + coordinator.start() + self.window?.rootViewController = navigation self.window?.makeKeyAndVisible() } + } else { + ok(result) } } } + } } } diff --git a/TraccarClient/LoginViewController/Model/LoginModelController.swift b/TraccarClient/LoginViewController/Model/LoginModelController.swift index bc87bc4f2e4cdaa80d686d7093048ca997a12cde..77114b42be9c3871f4aef8d7f6fe78e92fb82c36 100644 --- a/TraccarClient/LoginViewController/Model/LoginModelController.swift +++ b/TraccarClient/LoginViewController/Model/LoginModelController.swift @@ -23,6 +23,7 @@ final class LoginModelController { setupData() } } + private var shift: ShiftModel? // MARK: - Init init(delegate: LoginViewControllerDelegate?, viewController: KNViewController) { @@ -78,18 +79,27 @@ final class LoginModelController { api.signin(params: params, force: forcing) { [weak self] result in guard let strongSelf = self else {return} if result.success { - api.permissions { result in + api.my_shift { result in if result.success { - api.parameters { result in - spinner.stop() - if result.success { - let connect = ConnectViewController() - strongSelf.viewController.navigationController?.pushViewController(connect, animated: true) + if let shift = result.data as? ShiftModel { + strongSelf.shift = shift + api.permissions { result in + if result.success { + api.parameters { result in + spinner.stop() + if result.success { + let connect = ConnectViewController() + connect.shift = strongSelf.shift + strongSelf.viewController.navigationController?.pushViewController(connect, animated: true) + } + } + } } } + } else { + ok(result) } } - } else { spinner.stop() if result.statusCode == 401 { @@ -124,7 +134,7 @@ final class LoginModelController { } } } - }catch { + } catch { print("Error while decoding response: \(String(data: result.dataResponse?.data ?? Data(), encoding: .utf8) ?? "")") } } else if result.statusCode == 404 { @@ -143,7 +153,6 @@ final class LoginModelController { } } } - } // MARK: - Request diff --git a/TraccarClient/MainViewController/ConnectViewController.swift b/TraccarClient/MainViewController/ConnectViewController.swift index 7b0f47b9f51ac7e734b247f9a0b4f3b38c79589f..1ad57f43c75d61c072dd92415e516635dcfdcf23 100644 --- a/TraccarClient/MainViewController/ConnectViewController.swift +++ b/TraccarClient/MainViewController/ConnectViewController.swift @@ -27,6 +27,7 @@ final class ConnectViewController: KNViewController { private var isInBackground: Bool = false private var currentBackgroundDate = Date() let status: Bool = false + var shift: ShiftModel? // MARK: - LifeCycle override func viewDidLoad() { diff --git a/TraccarClient/MainViewController/ConnectViewController.xib b/TraccarClient/MainViewController/ConnectViewController.xib index d7f5552704ef78c63d1a1420c7aa457c7cbc3a6a..19377aa64b8af3d6fdbf3f5075a14436b4d45115 100644 --- a/TraccarClient/MainViewController/ConnectViewController.xib +++ b/TraccarClient/MainViewController/ConnectViewController.xib @@ -26,27 +26,27 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="nmoLogo" translatesAutoresizingMaskIntoConstraints="NO" id="nK6-XO-h2t"> - <rect key="frame" x="114" y="95" width="165" height="165"/> + <rect key="frame" x="121.66666666666669" y="95" width="150" height="150"/> <constraints> - <constraint firstAttribute="height" constant="165" id="nip-ho-zT0"/> - <constraint firstAttribute="width" constant="165" id="pbW-5n-K0u"/> + <constraint firstAttribute="height" constant="150" id="nip-ho-zT0"/> + <constraint firstAttribute="width" constant="150" id="pbW-5n-K0u"/> </constraints> </imageView> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="disconnectedLogo" translatesAutoresizingMaskIntoConstraints="NO" id="zub-LG-NJa"> - <rect key="frame" x="73" y="270" width="247" height="253"/> + <rect key="frame" x="73" y="255" width="247" height="253"/> <constraints> <constraint firstAttribute="height" constant="253" id="B4E-Zl-biZ"/> <constraint firstAttribute="width" constant="247" id="k8d-KL-54m"/> </constraints> </imageView> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GMM-3p-jRe"> - <rect key="frame" x="73" y="543" width="247" height="20.333333333333371"/> + <rect key="frame" x="73" y="528" width="247" height="20.333333333333371"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CkC-Ms-GkO"> - <rect key="frame" x="114" y="583.33333333333337" width="165" height="165"/> + <rect key="frame" x="114" y="568.33333333333337" width="165" height="165"/> <constraints> <constraint firstAttribute="width" constant="165" id="9th-Vo-PUR"/> <constraint firstAttribute="height" constant="165" id="BmZ-pN-ikQ"/> @@ -58,7 +58,7 @@ </connections> </button> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3f0-IX-LtZ"> - <rect key="frame" x="170.33333333333334" y="768.33333333333337" width="52.333333333333343" height="24"/> + <rect key="frame" x="170.33333333333334" y="753.33333333333337" width="52.333333333333343" height="24"/> <constraints> <constraint firstAttribute="height" constant="24" id="htr-pN-8Wx"/> </constraints> @@ -67,7 +67,7 @@ <nil key="highlightedColor"/> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tx9-o4-hgS"> - <rect key="frame" x="179.33333333333334" y="802.33333333333337" width="34.333333333333343" height="24"/> + <rect key="frame" x="179.33333333333334" y="787.33333333333337" width="34.333333333333343" height="24"/> <constraints> <constraint firstAttribute="height" constant="24" id="YrJ-sw-AgZ"/> </constraints> diff --git a/TraccarClient/Network+Endpoint.swift b/TraccarClient/Network+Endpoint.swift index 05661add6d1d8bb3950903eb6c5b7413579d8e07..3c56df5a1f1102b282bb96a7ee434654f3bb8139 100644 --- a/TraccarClient/Network+Endpoint.swift +++ b/TraccarClient/Network+Endpoint.swift @@ -33,6 +33,8 @@ enum Endpoint: String, CaseIterable { case logout = "logout" case permissions = "nextcloud/fleet/permissions" case parameters = "nextcloud/fleet/tracking-parameters" + case check = "user/check-token" + case shift = "shift/my-shift" // /////////////// diff --git a/TraccarClient/Network+Shifts.swift b/TraccarClient/Network+Shifts.swift new file mode 100644 index 0000000000000000000000000000000000000000..c651ca849a9718e6cde9e7974208857945101bf3 --- /dev/null +++ b/TraccarClient/Network+Shifts.swift @@ -0,0 +1,22 @@ +// +// Network+Shifts.swift +// TraccarClient +// +// Created by George Makhoul on 12/11/2023. +// Copyright © 2023 Traccar. All rights reserved. +// + +import Alamofire +import Foundation + +extension NetworkRequest { + // MARK: - Signin + func my_shift(_ completion: @escaping ResultClosure) { + request(endpoint: endpoint(.shift), method: .get) { result in + let shift = ShiftModel(result.object) + result.data = shift + print(result) + completion(result) + } + } +} diff --git a/TraccarClient/ShiftsModel.swift b/TraccarClient/ShiftsModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..290204a9c5dc78040f974fe8b0068e17c5c36d84 --- /dev/null +++ b/TraccarClient/ShiftsModel.swift @@ -0,0 +1,86 @@ +// +// ShiftsModel.swift +// TraccarClient +// +// Created by George Makhoul on 14/11/2023. +// Copyright © 2023 Traccar. All rights reserved. +// + +import Foundation + +final class ShiftModel: KNObject { + // MARK: - Properties + var id: Int = 0 + var name: String = "" + var minHours: Int? + var maxHours: Int? + var cycle: Int? + var isHourly: Bool = false + var workDays: [String] = [] + var shiftTypeId: Int = 0 + var isDefault: Bool = false + var timings: [TimingModel] = [] + var assignees: [String] = [] + + // MARK: - Init + override init() { + super.init() + } + + init(_ dict: JSON) { + super.init() + + id = dict["id"] as? Int ?? 0 + name = dict["name"] as? String ?? "" + minHours = dict["min_hours"] as? Int + maxHours = dict["max_hours"] as? Int + cycle = dict["cycle"] as? Int + isHourly = dict["is_hourly"] as? Bool ?? false + workDays = dict["work_days"] as? [String] ?? [] + shiftTypeId = dict["shift_type_id"] as? Int ?? 0 + isDefault = dict["is_default"] as? Bool ?? false + assignees = dict["assignees"] as? [String] ?? [] + + if let timingsArray = dict["timings"] as? [JSON] { + timings = timingsArray.map { TimingModel($0) } + } + } +} + +final class TimingModel: KNObject { + // MARK: - Properties + var id: Int = 0 + var daysOfWeek: [String] = [] + var startsAt: String = "" + var endsAt: String = "" + var arrivalGracePeriod: Int = 0 + var leavingGracePeriod: Int = 0 + var breaks: String? + var shiftId: Int = 0 + var createdAt: String = "" + var updatedAt: String = "" + + // MARK: - Init + override init() { + super.init() + } + + init(_ dict: JSON) { + super.init() + + id = dict["id"] as? Int ?? 0 + startsAt = dict["starts_at"] as? String ?? "" + endsAt = dict["ends_at"] as? String ?? "" + arrivalGracePeriod = dict["arrival_grace_period"] as? Int ?? 0 + leavingGracePeriod = dict["leaving_grace_period"] as? Int ?? 0 + breaks = dict["breaks"] as? String + shiftId = dict["shift_id"] as? Int ?? 0 + createdAt = dict["created_at"] as? String ?? "" + updatedAt = dict["updated_at"] as? String ?? "" + + if let daysOfWeekArray = dict["days_of_week"] as? [String] { + daysOfWeek = daysOfWeekArray + } + } +} +