aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2022-01-29 04:13:25 -0600
committerIván Ávalos <avalos@disroot.org>2022-01-29 04:13:25 -0600
commite1d8854d7d5e789ed84e8948f17fb0dac0d44da6 (patch)
treeab84d7edaead85e03950102e091da51952635159
parent64c4a58e644229b9d94233b3dd18b7805ffbb8d7 (diff)
downloadetbsa-trackermap-mobile-e1d8854d7d5e789ed84e8948f17fb0dac0d44da6.tar.gz
etbsa-trackermap-mobile-e1d8854d7d5e789ed84e8948f17fb0dac0d44da6.tar.bz2
etbsa-trackermap-mobile-e1d8854d7d5e789ed84e8948f17fb0dac0d44da6.zip
Initial implementation of device list, and added getStatus() and getEngineStop() methods to UnitInformation
-rw-r--r--iosApp/Localizable.strings4
-rw-r--r--iosApp/iosApp.xcodeproj/project.pbxproj85
-rw-r--r--iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved8
-rw-r--r--iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/avalos.xcuserdatad/UserInterfaceState.xcuserstatebin43210 -> 47814 bytes
-rw-r--r--iosApp/iosApp/Devices/DeviceRow.swift106
-rw-r--r--iosApp/iosApp/Devices/DevicesView.swift32
-rw-r--r--iosApp/iosApp/Map/MapView.swift37
-rw-r--r--iosApp/iosApp/Shared/SmallLabelStyle.swift23
-rw-r--r--iosApp/iosApp/Units/UnitsView.swift30
-rw-r--r--iosApp/iosApp/Units/UnitsViewModel.swift65
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/UnitInformation.kt33
-rw-r--r--shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/Serialization.kt12
12 files changed, 409 insertions, 26 deletions
diff --git a/iosApp/Localizable.strings b/iosApp/Localizable.strings
index c863844..aee6a31 100644
--- a/iosApp/Localizable.strings
+++ b/iosApp/Localizable.strings
@@ -12,3 +12,7 @@
"password" = "Password";
"server-url" = "Server URL";
"login" = "Login";
+
+"devices" = "Devices";
+"map" = "Map";
+"search" = "Search";
diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj
index d2f7787..6eeaa69 100644
--- a/iosApp/iosApp.xcodeproj/project.pbxproj
+++ b/iosApp/iosApp.xcodeproj/project.pbxproj
@@ -11,28 +11,21 @@
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
7555FF83242A565900829871 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* RootView.swift */; };
+ E33A236027A4FD2C00DD647F /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E33A235F27A4FD2C00DD647F /* MapView.swift */; };
+ E33A236327A5014600DD647F /* SwiftUIX in Frameworks */ = {isa = PBXBuildFile; productRef = E33A236227A5014600DD647F /* SwiftUIX */; };
+ E33A236527A530F300DD647F /* SmallLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E33A236427A530F300DD647F /* SmallLabelStyle.swift */; };
E38F241527A242870069FC45 /* Inject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38F241427A242870069FC45 /* Inject.swift */; };
E38F241727A242C70069FC45 /* Resolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38F241627A242C70069FC45 /* Resolver.swift */; };
E38F241C27A26DD70069FC45 /* RootViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38F241B27A26DD70069FC45 /* RootViewModel.swift */; };
E38F241E27A270D50069FC45 /* UnitsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38F241D27A270D50069FC45 /* UnitsView.swift */; };
E38F242027A27B550069FC45 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38F241F27A27B550069FC45 /* LoadingView.swift */; };
+ E39ABC4327A4E88C00965D05 /* UnitsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39ABC4227A4E88C00965D05 /* UnitsViewModel.swift */; };
+ E39ABC4627A4EBD500965D05 /* DevicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39ABC4527A4EBD500965D05 /* DevicesView.swift */; };
+ E39ABC4827A4EDEC00965D05 /* DeviceRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39ABC4727A4EDEC00965D05 /* DeviceRow.swift */; };
E3E77EDF279D2B5A00150070 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E3E77EDE279D2B5A00150070 /* Localizable.strings */; };
E3E77EE6279E6CE400150070 /* FlowCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E77EE5279E6CE400150070 /* FlowCollector.swift */; };
/* End PBXBuildFile section */
-/* Begin PBXCopyFilesBuildPhase section */
- 7555FFB4242A642300829871 /* Embed Frameworks */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = "";
- dstSubfolderSpec = 10;
- files = (
- );
- name = "Embed Frameworks";
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXCopyFilesBuildPhase section */
-
/* Begin PBXFileReference section */
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
@@ -40,11 +33,17 @@
7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
7555FF82242A565900829871 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ E33A235F27A4FD2C00DD647F /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; };
+ E33A236427A530F300DD647F /* SmallLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallLabelStyle.swift; sourceTree = "<group>"; };
+ E37361C827A39A4200306B21 /* WhirlyGlobeMaplyComponent.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = WhirlyGlobeMaplyComponent.framework; sourceTree = "<group>"; };
E38F241427A242870069FC45 /* Inject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inject.swift; sourceTree = "<group>"; };
E38F241627A242C70069FC45 /* Resolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Resolver.swift; sourceTree = "<group>"; };
E38F241B27A26DD70069FC45 /* RootViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewModel.swift; sourceTree = "<group>"; };
E38F241D27A270D50069FC45 /* UnitsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsView.swift; sourceTree = "<group>"; };
E38F241F27A27B550069FC45 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
+ E39ABC4227A4E88C00965D05 /* UnitsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsViewModel.swift; sourceTree = "<group>"; };
+ E39ABC4527A4EBD500965D05 /* DevicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevicesView.swift; sourceTree = "<group>"; };
+ E39ABC4727A4EDEC00965D05 /* DeviceRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRow.swift; sourceTree = "<group>"; };
E3E77EDE279D2B5A00150070 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
E3E77EE5279E6CE400150070 /* FlowCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowCollector.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -54,6 +53,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ E33A236327A5014600DD647F /* SwiftUIX in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -89,6 +89,8 @@
7555FF7D242A565900829871 /* iosApp */ = {
isa = PBXGroup;
children = (
+ E33A235E27A4FD1C00DD647F /* Map */,
+ E39ABC4427A4EBB000965D05 /* Devices */,
E38F241A27A2659C0069FC45 /* Units */,
E3E77EE1279D43C000150070 /* Shared */,
058557BA273AAA24004C7B11 /* Assets.xcassets */,
@@ -103,18 +105,37 @@
7555FFB0242A642200829871 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ E37361C827A39A4200306B21 /* WhirlyGlobeMaplyComponent.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
+ E33A235E27A4FD1C00DD647F /* Map */ = {
+ isa = PBXGroup;
+ children = (
+ E33A235F27A4FD2C00DD647F /* MapView.swift */,
+ );
+ path = Map;
+ sourceTree = "<group>";
+ };
E38F241A27A2659C0069FC45 /* Units */ = {
isa = PBXGroup;
children = (
E38F241D27A270D50069FC45 /* UnitsView.swift */,
+ E39ABC4227A4E88C00965D05 /* UnitsViewModel.swift */,
);
path = Units;
sourceTree = "<group>";
};
+ E39ABC4427A4EBB000965D05 /* Devices */ = {
+ isa = PBXGroup;
+ children = (
+ E39ABC4527A4EBD500965D05 /* DevicesView.swift */,
+ E39ABC4727A4EDEC00965D05 /* DeviceRow.swift */,
+ );
+ path = Devices;
+ sourceTree = "<group>";
+ };
E3E77EE0279D43B400150070 /* Session */ = {
isa = PBXGroup;
children = (
@@ -131,6 +152,7 @@
E38F241427A242870069FC45 /* Inject.swift */,
E38F241627A242C70069FC45 /* Resolver.swift */,
E38F241F27A27B550069FC45 /* LoadingView.swift */,
+ E33A236427A530F300DD647F /* SmallLabelStyle.swift */,
);
path = Shared;
sourceTree = "<group>";
@@ -146,13 +168,15 @@
7555FF77242A565900829871 /* Sources */,
7555FF78242A565900829871 /* Frameworks */,
7555FF79242A565900829871 /* Resources */,
- 7555FFB4242A642300829871 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = iosApp;
+ packageProductDependencies = (
+ E33A236227A5014600DD647F /* SwiftUIX */,
+ );
productName = iosApp;
productReference = 7555FF7B242A565900829871 /* iosApp.app */;
productType = "com.apple.product-type.application";
@@ -183,7 +207,7 @@
);
mainGroup = 7555FF72242A565900829871;
packageReferences = (
- E3E77EE4279E06C300150070 /* XCRemoteSwiftPackageReference "Swinject" */,
+ E33A236127A5014600DD647F /* XCRemoteSwiftPackageReference "SwiftUIX" */,
);
productRefGroup = 7555FF7C242A565900829871 /* Products */;
projectDirPath = "";
@@ -233,10 +257,15 @@
buildActionMask = 2147483647;
files = (
E38F241727A242C70069FC45 /* Resolver.swift in Sources */,
+ E33A236027A4FD2C00DD647F /* MapView.swift in Sources */,
E38F241527A242870069FC45 /* Inject.swift in Sources */,
+ E39ABC4827A4EDEC00965D05 /* DeviceRow.swift in Sources */,
E38F241C27A26DD70069FC45 /* RootViewModel.swift in Sources */,
E3E77EE6279E6CE400150070 /* FlowCollector.swift in Sources */,
+ E33A236527A530F300DD647F /* SmallLabelStyle.swift in Sources */,
+ E39ABC4327A4E88C00965D05 /* UnitsViewModel.swift in Sources */,
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
+ E39ABC4627A4EBD500965D05 /* DevicesView.swift in Sources */,
E38F242027A27B550069FC45 /* LoadingView.swift in Sources */,
E38F241E27A270D50069FC45 /* UnitsView.swift in Sources */,
7555FF83242A565900829871 /* RootView.swift in Sources */,
@@ -370,7 +399,11 @@
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = 358YRZ9P3L;
ENABLE_PREVIEWS = YES;
- FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
+ EXCLUDED_ARCHS = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+ "$(PROJECT_DIR)",
+ );
INFOPLIST_FILE = iosApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -396,7 +429,11 @@
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = 358YRZ9P3L;
ENABLE_PREVIEWS = YES;
- FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)";
+ EXCLUDED_ARCHS = "";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+ "$(PROJECT_DIR)",
+ );
INFOPLIST_FILE = iosApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -438,15 +475,23 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
- E3E77EE4279E06C300150070 /* XCRemoteSwiftPackageReference "Swinject" */ = {
+ E33A236127A5014600DD647F /* XCRemoteSwiftPackageReference "SwiftUIX" */ = {
isa = XCRemoteSwiftPackageReference;
- repositoryURL = "https://github.com/Swinject/Swinject.git";
+ repositoryURL = "https://github.com/SwiftUIX/SwiftUIX";
requirement = {
kind = upToNextMajorVersion;
- minimumVersion = 2.0.0;
+ minimumVersion = 0.1.1;
};
};
/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ E33A236227A5014600DD647F /* SwiftUIX */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = E33A236127A5014600DD647F /* XCRemoteSwiftPackageReference "SwiftUIX" */;
+ productName = SwiftUIX;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = 7555FF73242A565900829871 /* Project object */;
}
diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index fc75d3e..c8ce63d 100644
--- a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -2,12 +2,12 @@
"object": {
"pins": [
{
- "package": "Swinject",
- "repositoryURL": "https://github.com/Swinject/Swinject.git",
+ "package": "SwiftUIX",
+ "repositoryURL": "https://github.com/SwiftUIX/SwiftUIX",
"state": {
"branch": null,
- "revision": "f10b6e9ebff440f985c43008f7c2d097639fcb81",
- "version": "2.8.1"
+ "revision": "eb6a23fc4cc79545e40a8cf5c82119684750194f",
+ "version": "0.1.1"
}
}
]
diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/avalos.xcuserdatad/UserInterfaceState.xcuserstate b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/avalos.xcuserdatad/UserInterfaceState.xcuserstate
index 257d7c0..821a1e3 100644
--- a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/avalos.xcuserdatad/UserInterfaceState.xcuserstate
+++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/avalos.xcuserdatad/UserInterfaceState.xcuserstate
Binary files differ
diff --git a/iosApp/iosApp/Devices/DeviceRow.swift b/iosApp/iosApp/Devices/DeviceRow.swift
new file mode 100644
index 0000000..5fc4e11
--- /dev/null
+++ b/iosApp/iosApp/Devices/DeviceRow.swift
@@ -0,0 +1,106 @@
+//
+// DeviceRow.swift
+// iosApp
+//
+// Created by Iván on 28/01/22.
+// Copyright © 2022 orgName. All rights reserved.
+//
+
+import SwiftUI
+import shared
+
+struct DeviceRow: View {
+ var unit: UnitInformation
+
+ var body: some View {
+ VStack {
+ HStack {
+ /* Status icon */
+ switch (unit.getStatus()) {
+ case .online:
+ Image(systemName: "circle.fill")
+ .foregroundColor(.systemGreen)
+ .imageScale(.small)
+ case .offline:
+ Image(systemName: "circle.fill")
+ .foregroundColor(.systemRed)
+ .imageScale(.small)
+ default:
+ EmptyView()
+ }
+
+ /* Engine stop */
+ switch (unit.getEngineStop()) {
+ case .on:
+ Image(systemName: "lock.fill")
+ .foregroundColor(.systemRed)
+ .imageScale(.small)
+ case .off:
+ Image(systemName: "lock.open.fill")
+ .foregroundColor(.systemGreen)
+ .imageScale(.small)
+ default:
+ EmptyView()
+ }
+
+ /* Device name */
+ Text(unit.device.name)
+ Spacer()
+ }
+ .padding(.bottom, 5.0)
+
+ /* Driver */
+ if let contact = unit.device.contact {
+ HStack {
+ Label(contact, systemImage: "person")
+ .labelStyle(SmallLabelStyle())
+ Spacer()
+ }
+ }
+
+ /* Speed */
+ if let speed = unit.position?.speed {
+ HStack {
+ Label(Formatter.companion.formatSpeed(
+ speed: Double(truncating: speed),
+ unit: .kmh),
+ systemImage: "speedometer")
+ .labelStyle(SmallLabelStyle())
+ Spacer()
+ }
+ }
+
+ /* Address */
+ if let address = unit.position?.address {
+ HStack {
+ Label(address, systemImage: "mappin.and.ellipse")
+ .labelStyle(SmallLabelStyle())
+ Spacer()
+ }
+ }
+
+ /* Hourmeter */
+ if let hourmeter = unit.position?.attributes["hours"] {
+ if let hourmeter = Serialization.companion.longOrNull(json: hourmeter) {
+ HStack {
+ Label(Formatter.companion.formatHours(
+ millis: Int64(truncating: hourmeter)),
+ systemImage: "timer")
+ .labelStyle(SmallLabelStyle())
+ Spacer()
+ }
+ }
+ }
+
+ /* Date time */
+ if let datetime = unit.position?.fixTime {
+ HStack {
+ Label(Formatter.companion.formatDate(str: datetime),
+ systemImage: "calendar")
+ .labelStyle(SmallLabelStyle())
+ Spacer()
+ }
+ }
+ }
+ }
+}
diff --git a/iosApp/iosApp/Devices/DevicesView.swift b/iosApp/iosApp/Devices/DevicesView.swift
new file mode 100644
index 0000000..7271a70
--- /dev/null
+++ b/iosApp/iosApp/Devices/DevicesView.swift
@@ -0,0 +1,32 @@
+//
+// DevicesView.swift
+// iosApp
+//
+// Created by Iván on 28/01/22.
+// Copyright © 2022 orgName. All rights reserved.
+//
+
+import SwiftUI
+
+struct DevicesView: View {
+ @StateObject var unitsViewModel: UnitsViewModel
+
+ var body: some View {
+ List(unitsViewModel.units, id: \.device.id) { unit in
+ DeviceRow(unit: unit)
+ }
+ .navigationTitle("devices")
+ .toolbar {
+ ToolbarItem(placement: .navigationBarLeading) {
+ Button(action: unitsViewModel.toggleDisplayMode) {
+ Image(systemName: "map")
+ }
+ }
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button(action: {}) {
+ Image(systemName: "person")
+ }
+ }
+ }
+ }
+}
diff --git a/iosApp/iosApp/Map/MapView.swift b/iosApp/iosApp/Map/MapView.swift
new file mode 100644
index 0000000..3530946
--- /dev/null
+++ b/iosApp/iosApp/Map/MapView.swift
@@ -0,0 +1,37 @@
+//
+// MapView.swift
+// iosApp
+//
+// Created by Iván on 28/01/22.
+// Copyright © 2022 orgName. All rights reserved.
+//
+
+import SwiftUI
+
+struct MapView: View {
+ @StateObject var unitsViewModel: UnitsViewModel
+
+ var body: some View {
+ ZStack {
+ Text("Unimplemented!")
+ }
+ .navigationTitle("map")
+ .toolbar {
+ ToolbarItem(placement: .navigationBarLeading) {
+ Button(action: unitsViewModel.toggleDisplayMode) {
+ Image(systemName: "list.bullet")
+ }
+ }
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button(action: {}) {
+ Image(systemName: "square.stack.3d.up")
+ }
+ }
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button(action: {}) {
+ Image(systemName: "person")
+ }
+ }
+ }
+ }
+}
diff --git a/iosApp/iosApp/Shared/SmallLabelStyle.swift b/iosApp/iosApp/Shared/SmallLabelStyle.swift
new file mode 100644
index 0000000..27914ff
--- /dev/null
+++ b/iosApp/iosApp/Shared/SmallLabelStyle.swift
@@ -0,0 +1,23 @@
+//
+// SmallIconLabelStyle.swift
+// iosApp
+//
+// Created by Iván on 29/01/22.
+// Copyright © 2022 orgName. All rights reserved.
+//
+
+import SwiftUI
+
+struct SmallLabelStyle: LabelStyle {
+ func makeBody(configuration: Configuration) -> some View {
+ HStack {
+ configuration.icon
+ .foregroundColor(.secondary)
+ .imageScale(.small)
+
+ configuration.title
+ .font(.subheadline)
+ .foregroundColor(.secondary)
+ }
+ }
+}
diff --git a/iosApp/iosApp/Units/UnitsView.swift b/iosApp/iosApp/Units/UnitsView.swift
index 1088a5f..fd673a6 100644
--- a/iosApp/iosApp/Units/UnitsView.swift
+++ b/iosApp/iosApp/Units/UnitsView.swift
@@ -7,9 +7,37 @@
//
import SwiftUI
+import SwiftUIX
+import shared
struct UnitsView: View {
+ @StateObject var unitsViewModel = UnitsViewModel()
+
var body: some View {
- Text("Welcome to UnitsView!")
+ NavigationView {
+ ZStack {
+ switch unitsViewModel.unitsDisplayMode {
+ case .list:
+ DevicesView(unitsViewModel: unitsViewModel)
+ case .map:
+ MapView(unitsViewModel: unitsViewModel)
+ }
+ }
+ .navigationBarTitleDisplayMode(.inline)
+ .navigationSearchBar {
+ searchBar()
+ }
+ }
+ }
+
+ private func searchBar () -> SearchBar {
+ SearchBar(NSLocalizedString("search", comment: ""),
+ text: $unitsViewModel.searchQuery,
+ isEditing: $unitsViewModel.isEditing,
+ onCommit: {})
+ .showsCancelButton(unitsViewModel.isEditing)
+ .onCancel {
+ unitsViewModel.searchQuery = ""
+ }
}
}
diff --git a/iosApp/iosApp/Units/UnitsViewModel.swift b/iosApp/iosApp/Units/UnitsViewModel.swift
new file mode 100644
index 0000000..648e429
--- /dev/null
+++ b/iosApp/iosApp/Units/UnitsViewModel.swift
@@ -0,0 +1,65 @@
+//
+// UnitsViewModel.swift
+// iosApp
+//
+// Created by Iván on 28/01/22.
+// Copyright © 2022 orgName. All rights reserved.
+//
+
+import Foundation
+import shared
+
+class UnitsViewModel: ObservableObject {
+ @Inject var unitsController: UnitsController
+ @Inject var geofenceController: GeofencesController
+
+ enum UnitsDisplayMode {
+ case map
+ case list
+ }
+
+ @Published var searchQuery = "" {
+ didSet {
+ unitsDisplayMode = .list
+ search(query: searchQuery)
+ }
+ }
+ @Published var isEditing = false
+ @Published var unitsDisplayMode: UnitsDisplayMode = .list
+ @Published var units: [UnitInformation] = []
+ @Published var selectedUnit: UnitInformation? = nil
+ @Published var geofences: [Int: Geofence] = [:]
+
+ init() {
+ setupObservers()
+ }
+
+ private func setupObservers() {
+ let unitsCollector = Collector<[UnitInformation]>(callback: setUnits)
+ unitsController.displayedUnitsFlow.collect(collector: unitsCollector) { unit, error in }
+
+ let geofencesCollector = Collector<[Int: Geofence]>(callback: setGeofences)
+ geofenceController.geofencesFlow.collect(collector: geofencesCollector) { unit, error in }
+ }
+
+ private func setUnits(units: [UnitInformation]) {
+ self.units = units
+ }
+
+ private func setGeofences(geofences: [Int: Geofence]) {
+ self.geofences = geofences
+ }
+
+ func toggleDisplayMode() {
+ switch unitsDisplayMode {
+ case .map:
+ unitsDisplayMode = .list
+ case .list:
+ unitsDisplayMode = .map
+ }
+ }
+
+ func search(query: String) {
+ unitsController.search(query: query)
+ }
+}
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/UnitInformation.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/UnitInformation.kt
index edebff0..47134a6 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/UnitInformation.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/UnitInformation.kt
@@ -3,4 +3,35 @@ package mx.trackermap.TrackerMap.client.models
data class UnitInformation(
val device: Device,
val position: Position?
-) \ No newline at end of file
+) {
+ enum class Status {
+ ONLINE, OFFLINE, UNKNOWN
+ }
+
+ enum class EngineStop {
+ ON, OFF, UNKNOWN
+ }
+
+ /**
+ * Status is calculated from speed, because status based on
+ * the actual connection to the server is useless for our clients.
+ */
+ fun getStatus() = position?.speed?.let {
+ if (it >= 2) {
+ Status.ONLINE
+ } else {
+ Status.OFFLINE
+ }
+ } ?: Status.UNKNOWN
+
+ /* Many GPS devices reserve pin 1 for engine stop */
+ fun getEngineStop() = if (position?.attributes?.containsKey("out1") == true) {
+ position.attributes["out1"]?.toString()?.let {
+ when (it) {
+ "true" -> EngineStop.ON
+ "false" -> EngineStop.OFF
+ else -> EngineStop.UNKNOWN
+ }
+ } ?: EngineStop.UNKNOWN
+ } else EngineStop.UNKNOWN
+} \ No newline at end of file
diff --git a/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/Serialization.kt b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/Serialization.kt
new file mode 100644
index 0000000..be26416
--- /dev/null
+++ b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/Serialization.kt
@@ -0,0 +1,12 @@
+package mx.trackermap.TrackerMap.utils
+
+import kotlinx.serialization.json.JsonPrimitive
+import kotlinx.serialization.json.longOrNull
+
+class Serialization {
+ companion object {
+ fun longOrNull(json: JsonPrimitive) = json.longOrNull
+
+ fun toString(json: JsonPrimitive) = json.toString()
+ }
+} \ No newline at end of file