From a2387a76bb3b343862baa57e3fe28ed893f6cbda Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Sun, 6 Feb 2022 03:48:54 -0600 Subject: Implemented details view with details tab --- iosApp/iosApp.xcodeproj/project.pbxproj | 20 ++++ .../iosApp/Details/Commands/UnitCommandsView.swift | 28 ++++++ iosApp/iosApp/Details/DetailsView.swift | 82 ++++++++++++++++ iosApp/iosApp/Details/DetailsViewModel.swift | 31 ++++++ .../Details/Information/UnitInformationView.swift | 104 +++++++++++++++++++++ .../iosApp/Details/Reports/UnitReportsView.swift | 27 ++++++ iosApp/iosApp/Devices/DevicesView.swift | 45 +++++---- iosApp/iosApp/Localizable.strings | 13 +++ 8 files changed, 332 insertions(+), 18 deletions(-) create mode 100644 iosApp/iosApp/Details/Commands/UnitCommandsView.swift create mode 100644 iosApp/iosApp/Details/DetailsView.swift create mode 100644 iosApp/iosApp/Details/DetailsViewModel.swift create mode 100644 iosApp/iosApp/Details/Information/UnitInformationView.swift create mode 100644 iosApp/iosApp/Details/Reports/UnitReportsView.swift diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 821ffb6..7faf845 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -28,6 +28,11 @@ E38F242027A27B550069FC45 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38F241F27A27B550069FC45 /* LoadingView.swift */; }; E396281D27AF5723005D070E /* WhirlyGlobe.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E396281B27AF56BA005D070E /* WhirlyGlobe.xcframework */; }; E396281E27AF5723005D070E /* WhirlyGlobe.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E396281B27AF56BA005D070E /* WhirlyGlobe.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + E396282027AF59F2005D070E /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E396281F27AF59F2005D070E /* DetailsView.swift */; }; + E396282227AF66A3005D070E /* DetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E396282127AF66A3005D070E /* DetailsViewModel.swift */; }; + E396282427AFBD3D005D070E /* UnitInformationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E396282327AFBD3D005D070E /* UnitInformationView.swift */; }; + E396282627AFBD4E005D070E /* UnitReportsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E396282527AFBD4E005D070E /* UnitReportsView.swift */; }; + E396282827AFBD72005D070E /* UnitCommandsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E396282727AFBD72005D070E /* UnitCommandsView.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 */; }; @@ -70,6 +75,11 @@ E38F241D27A270D50069FC45 /* UnitsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsView.swift; sourceTree = ""; }; E38F241F27A27B550069FC45 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; E396281B27AF56BA005D070E /* WhirlyGlobe.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = WhirlyGlobe.xcframework; sourceTree = ""; }; + E396281F27AF59F2005D070E /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = ""; }; + E396282127AF66A3005D070E /* DetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsViewModel.swift; sourceTree = ""; }; + E396282327AFBD3D005D070E /* UnitInformationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitInformationView.swift; sourceTree = ""; }; + E396282527AFBD4E005D070E /* UnitReportsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitReportsView.swift; sourceTree = ""; }; + E396282727AFBD72005D070E /* UnitCommandsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitCommandsView.swift; sourceTree = ""; }; E39ABC4227A4E88C00965D05 /* UnitsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsViewModel.swift; sourceTree = ""; }; E39ABC4527A4EBD500965D05 /* DevicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevicesView.swift; sourceTree = ""; }; E39ABC4727A4EDEC00965D05 /* DeviceRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRow.swift; sourceTree = ""; }; @@ -157,6 +167,8 @@ E35A078727AB619700F24D71 /* Reports */, E35A078627AB619000F24D71 /* Information */, E35A078527AB618700F24D71 /* Commands */, + E396281F27AF59F2005D070E /* DetailsView.swift */, + E396282127AF66A3005D070E /* DetailsViewModel.swift */, ); path = Details; sourceTree = ""; @@ -164,6 +176,7 @@ E35A078527AB618700F24D71 /* Commands */ = { isa = PBXGroup; children = ( + E396282727AFBD72005D070E /* UnitCommandsView.swift */, ); path = Commands; sourceTree = ""; @@ -171,6 +184,7 @@ E35A078627AB619000F24D71 /* Information */ = { isa = PBXGroup; children = ( + E396282327AFBD3D005D070E /* UnitInformationView.swift */, ); path = Information; sourceTree = ""; @@ -178,6 +192,7 @@ E35A078727AB619700F24D71 /* Reports */ = { isa = PBXGroup; children = ( + E396282527AFBD4E005D070E /* UnitReportsView.swift */, ); path = Reports; sourceTree = ""; @@ -326,12 +341,17 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E396282627AFBD4E005D070E /* UnitReportsView.swift in Sources */, E33A237127A7553500DD647F /* MapView.swift in Sources */, + E396282027AF59F2005D070E /* DetailsView.swift in Sources */, E38F241727A242C70069FC45 /* Resolver.swift in Sources */, E33A236027A4FD2C00DD647F /* UnitMapView.swift in Sources */, + E396282427AFBD3D005D070E /* UnitInformationView.swift in Sources */, E33A236727A64E4500DD647F /* MarkerTransformations.swift in Sources */, E38F241527A242870069FC45 /* Inject.swift in Sources */, E39ABC4827A4EDEC00965D05 /* DeviceRow.swift in Sources */, + E396282227AF66A3005D070E /* DetailsViewModel.swift in Sources */, + E396282827AFBD72005D070E /* UnitCommandsView.swift in Sources */, E38F241C27A26DD70069FC45 /* RootViewModel.swift in Sources */, E36DF77B27AB740C003C561C /* MapViewController.swift in Sources */, E33A237327A7581A00DD647F /* Utils.swift in Sources */, diff --git a/iosApp/iosApp/Details/Commands/UnitCommandsView.swift b/iosApp/iosApp/Details/Commands/UnitCommandsView.swift new file mode 100644 index 0000000..fade29b --- /dev/null +++ b/iosApp/iosApp/Details/Commands/UnitCommandsView.swift @@ -0,0 +1,28 @@ +/** + * TrackerMap + * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import SwiftUI +import shared + +struct UnitCommandsView: View { + var unit: UnitInformation + + var body: some View { + + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} diff --git a/iosApp/iosApp/Details/DetailsView.swift b/iosApp/iosApp/Details/DetailsView.swift new file mode 100644 index 0000000..8e2a6b5 --- /dev/null +++ b/iosApp/iosApp/Details/DetailsView.swift @@ -0,0 +1,82 @@ +/** + * TrackerMap + * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import SwiftUI +import shared + +struct DetailsView: View { + @ObservedObject var detailsViewModel = DetailsViewModel() + + @Binding var isPresented: Bool + @State var action: DeviceRow.Action + var id: Int32 + + init(isPresented: Binding, action: DeviceRow.Action, forId id: Int32) { + self._isPresented = isPresented + self.action = action + self.id = id + detailsViewModel.fetchUnit(id: id) + } + + var body: some View { + NavigationView { + VStack { + if let unit = detailsViewModel.unit { + switch action { + case .details: + UnitInformationView(unit: unit) + case .reports: + UnitReportsView(unit: unit) + case .commands: + UnitCommandsView(unit: unit) + } + } else { + LoadingView() + } + } + .navigationBarTitleView( + Picker(selection: $action) { + Text("details").tag(DeviceRow.Action.details) + Text("reports").tag(DeviceRow.Action.reports) + Text("commands").tag(DeviceRow.Action.commands) + } label: { + EmptyView() + }.pickerStyle(SegmentedPickerStyle()) + ) + .navigationTitle(detailsViewModel.unit?.device.name ?? NSLocalizedString("loading", comment: "")) + .navigationBarTitleDisplayMode(.large) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + if let category = detailsViewModel.unit?.device.category { + let type = Marker.companion + .categoryToMarkerType(category: category) + Image(MarkerTransformations.markerTypeToImageName(markerType: type)) + } else { + EmptyView() + } + } + ToolbarItem(placement: .navigationBarTrailing) { + Button { + isPresented = false + } label: { + Text("done").bold() + } + } + } + } + } +} diff --git a/iosApp/iosApp/Details/DetailsViewModel.swift b/iosApp/iosApp/Details/DetailsViewModel.swift new file mode 100644 index 0000000..ce9165c --- /dev/null +++ b/iosApp/iosApp/Details/DetailsViewModel.swift @@ -0,0 +1,31 @@ +/** + * TrackerMap + * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import Foundation +import shared + +class DetailsViewModel: ObservableObject { + @Inject var unitsController: UnitsController + + @Published var unit: UnitInformation? + + func fetchUnit(id: Int32) { + unitsController.getUnit(deviceId: id) { unit, _ in + self.unit = unit + } + } +} diff --git a/iosApp/iosApp/Details/Information/UnitInformationView.swift b/iosApp/iosApp/Details/Information/UnitInformationView.swift new file mode 100644 index 0000000..3a52b84 --- /dev/null +++ b/iosApp/iosApp/Details/Information/UnitInformationView.swift @@ -0,0 +1,104 @@ +/** + * TrackerMap + * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import SwiftUI +import shared + +struct UnitInformationView: View { + var unit: UnitInformation + + var body: some View { + List { + // MARK: - Contact + if let contact = unit.device.contact { + HStack { + Text("contact") + Spacer() + Text(contact).foregroundColor(.secondaryLabel) + } + } + // MARK: - Unique ID + if let uniqueId = unit.device.uniqueId { + HStack { + Text("unique-id") + Spacer() + Text(uniqueId).foregroundColor(.secondaryLabel) + } + } + // MARK: - Date time + if let datetime = unit.position?.fixTime { + HStack { + Text("datetime") + Spacer() + Text(Formatter.companion.formatDate(str: datetime)) + .foregroundColor(.secondaryLabel) + } + } + // MARK: - Latitude + if let latitude = unit.position?.latitude { + HStack { + Text("latitude") + Spacer() + Text("\(latitude)").foregroundColor(.secondaryLabel) + } + } + // MARK: - Longitude + if let longitude = unit.position?.longitude { + HStack { + Text("longitude") + Spacer() + Text("\(longitude)").foregroundColor(.secondaryLabel) + } + } + // MARK: - Speed + if let speed = unit.position?.speed { + HStack { + Text("speed") + Spacer() + Text(Formatter.companion.formatSpeed( + speed: Double(truncating: speed), unit: .kmh)) + .foregroundColor(.secondaryLabel) + } + } + // MARK: - Address + if let address = unit.position?.address { + HStack { + Text("address") + Spacer() + Text(address).foregroundColor(.secondaryLabel) + } + } + // MARK: - Hours + if let hours = unit.getHourmeter() { + HStack { + Text("hourmeter") + Spacer() + Text(Formatter.companion.formatHours(millis: Int64(truncating: hours))) + .foregroundColor(.secondaryLabel) + } + } + // MARK: - Protocol + if let protocol_ = unit.position?.protocol { + HStack { + Text("protocol") + Spacer() + Text(protocol_).foregroundColor(.secondaryLabel) + } + } + } + } +} diff --git a/iosApp/iosApp/Details/Reports/UnitReportsView.swift b/iosApp/iosApp/Details/Reports/UnitReportsView.swift new file mode 100644 index 0000000..3e01e00 --- /dev/null +++ b/iosApp/iosApp/Details/Reports/UnitReportsView.swift @@ -0,0 +1,27 @@ +/** + * TrackerMap + * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import SwiftUI +import shared + +struct UnitReportsView: View { + var unit: UnitInformation + + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} diff --git a/iosApp/iosApp/Devices/DevicesView.swift b/iosApp/iosApp/Devices/DevicesView.swift index e971ac6..d3a361e 100644 --- a/iosApp/iosApp/Devices/DevicesView.swift +++ b/iosApp/iosApp/Devices/DevicesView.swift @@ -19,26 +19,35 @@ import SwiftUI struct DevicesView: View { @StateObject var unitsViewModel: UnitsViewModel + @State var shouldShowView = false + @State var action: DeviceRow.Action = .details + @State var id: Int32? var body: some View { - List(unitsViewModel.units, id: \.device.id) { unit in - DeviceRow(unit: unit, callback: { action in - switch action { - case .details: - print ("Selected details of \(unit.device.name)") - case .reports: - print ("Selected reports of \(unit.device.name)") - case .commands: - print ("Selected commands of \(unit.device.name)") - } - }) - .onTapGesture { - unitsViewModel.searchQuery = "" - unitsViewModel.isEditing = false - unitsViewModel.unitsDisplayMode = .map - unitsViewModel.selectedUnit = unit - UIApplication.shared.endEditing() - } + VStack { + List(unitsViewModel.units, id: \.device.id) { unit in + DeviceRow(unit: unit, callback: { action in + self.action = action + id = unit.device.id + shouldShowView = true + }) + .onTapGesture { + unitsViewModel.searchQuery = "" + unitsViewModel.isEditing = false + unitsViewModel.unitsDisplayMode = .map + unitsViewModel.selectedUnit = unit + UIApplication.shared.endEditing() + } + } + } + .sheet(isPresented: $shouldShowView) { + print("Dismissed") + } content: { + if let id = id { + DetailsView(isPresented: $shouldShowView, + action: action, + forId: id) + } } } } diff --git a/iosApp/iosApp/Localizable.strings b/iosApp/iosApp/Localizable.strings index 049518f..9013574 100644 --- a/iosApp/iosApp/Localizable.strings +++ b/iosApp/iosApp/Localizable.strings @@ -18,6 +18,9 @@ "app-name" = "TrackerMap"; "app-server-url" = "https://etbsa.net/api"; +"loading" = "Loading"; +"done" = "Done"; + "username" = "Username"; "password" = "Password"; "server-url" = "Server URL"; @@ -35,3 +38,13 @@ "map-layer" = "Map layer"; "openstreetmap" = "OpenStreetMap"; "satellite" = "Satellite"; + +"contact" = "Contact"; +"unique-id" = "Unique ID"; +"datetime" = "Datetime"; +"latitude" = "Latitude"; +"longitude" = "Longitude"; +"speed" = "Speed"; +"address" = "Address"; +"hourmeter" = "Hourmeter"; +"protocol" = "Protocol"; -- cgit v1.2.3