From de9eb9dc2b932066e8ef1668d71170230edd1d38 Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Wed, 16 Feb 2022 00:22:05 -0600 Subject: - Localized strings. - Implemented account details, about and sign out. - Map shows first on startup now. - Save server URL on preferences. - More improvements and fixes! --- iosApp/iosApp.xcodeproj/project.pbxproj | 37 +++++++++++-- iosApp/iosApp/Details/DetailsView.swift | 2 + iosApp/iosApp/Devices/DeviceRow.swift | 11 ++++ iosApp/iosApp/Localizable.strings | 55 ------------------- iosApp/iosApp/Map/MapView.swift | 5 +- iosApp/iosApp/Map/MapViewController.swift | 4 +- iosApp/iosApp/Map/MapViewController.xib | 10 ++++ iosApp/iosApp/Session/AboutView.swift | 34 ++++++++++++ iosApp/iosApp/Session/AccountView.swift | 73 +++++++++++++++++++++++++ iosApp/iosApp/Session/AccountViewModel.swift | 29 ++++++++++ iosApp/iosApp/Session/RootView.swift | 48 ++++++++-------- iosApp/iosApp/Session/RootViewModel.swift | 23 ++++++-- iosApp/iosApp/Session/UserInformationView.swift | 50 +++++++++++++++++ iosApp/iosApp/Units/UnitsView.swift | 10 +++- iosApp/iosApp/Units/UnitsViewModel.swift | 13 +++-- iosApp/iosApp/en.lproj/Localizable.strings | 68 +++++++++++++++++++++++ iosApp/iosApp/es-419.lproj/Localizable.strings | 68 +++++++++++++++++++++++ 17 files changed, 443 insertions(+), 97 deletions(-) delete mode 100644 iosApp/iosApp/Localizable.strings create mode 100644 iosApp/iosApp/Session/AboutView.swift create mode 100644 iosApp/iosApp/Session/AccountView.swift create mode 100644 iosApp/iosApp/Session/AccountViewModel.swift create mode 100644 iosApp/iosApp/Session/UserInformationView.swift create mode 100644 iosApp/iosApp/en.lproj/Localizable.strings create mode 100644 iosApp/iosApp/es-419.lproj/Localizable.strings diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index b3e7b29..9bd7b9a 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -20,7 +20,11 @@ E33A237127A7553500DD647F /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E33A237027A7553500DD647F /* MapView.swift */; }; E33A237327A7581A00DD647F /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E33A237227A7581A00DD647F /* Utils.swift */; }; E34A2F4827A7878200AD8AEB /* HyperlinkText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E34A2F4727A7878200AD8AEB /* HyperlinkText.swift */; }; - E34A2F4D27A7DB2200AD8AEB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E34A2F4C27A7DB2200AD8AEB /* Localizable.strings */; }; + E360251527BCA3AD00958B21 /* UserInformationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E360251427BCA3AD00958B21 /* UserInformationView.swift */; }; + E360251727BCA83300958B21 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E360251627BCA83300958B21 /* AccountView.swift */; }; + E360251927BCA84000958B21 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E360251827BCA84000958B21 /* AboutView.swift */; }; + E360251B27BCA8A600958B21 /* AccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E360251A27BCA8A600958B21 /* AccountViewModel.swift */; }; + E360251C27BCC97000958B21 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E360251E27BCC97000958B21 /* Localizable.strings */; }; E36A5A8627B4BFC40070DED5 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = E36A5A8527B4BFC40070DED5 /* FirebaseMessaging */; }; E36DF77B27AB740C003C561C /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36DF77927AB740C003C561C /* MapViewController.swift */; }; E36DF77C27AB740C003C561C /* MapViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E36DF77A27AB740C003C561C /* MapViewController.xib */; }; @@ -72,7 +76,12 @@ E33A237027A7553500DD647F /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; E33A237227A7581A00DD647F /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; E34A2F4727A7878200AD8AEB /* HyperlinkText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HyperlinkText.swift; sourceTree = ""; }; - E34A2F4C27A7DB2200AD8AEB /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + E360251427BCA3AD00958B21 /* UserInformationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInformationView.swift; sourceTree = ""; }; + E360251627BCA83300958B21 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = ""; }; + E360251827BCA84000958B21 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; + E360251A27BCA8A600958B21 /* AccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewModel.swift; sourceTree = ""; }; + E360251D27BCC97000958B21 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + E360251F27BCC97300958B21 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = ""; }; E36A5A8927B4C8BB0070DED5 /* iosApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iosApp.entitlements; sourceTree = ""; }; E36DF77927AB740C003C561C /* MapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; E36DF77A27AB740C003C561C /* MapViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MapViewController.xib; sourceTree = ""; }; @@ -138,7 +147,7 @@ children = ( E392BE1827B791F0002698F3 /* GoogleService-Info.plist */, E36A5A8927B4C8BB0070DED5 /* iosApp.entitlements */, - E34A2F4C27A7DB2200AD8AEB /* Localizable.strings */, + E360251E27BCC97000958B21 /* Localizable.strings */, E33A235E27A4FD1C00DD647F /* Map */, E35A078427AB615F00F24D71 /* Details */, E39ABC4427A4EBB000965D05 /* Devices */, @@ -233,6 +242,10 @@ children = ( 7555FF82242A565900829871 /* RootView.swift */, E38F241B27A26DD70069FC45 /* RootViewModel.swift */, + E360251427BCA3AD00958B21 /* UserInformationView.swift */, + E360251627BCA83300958B21 /* AccountView.swift */, + E360251827BCA84000958B21 /* AboutView.swift */, + E360251A27BCA8A600958B21 /* AccountViewModel.swift */, ); path = Session; sourceTree = ""; @@ -323,7 +336,7 @@ buildActionMask = 2147483647; files = ( 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, - E34A2F4D27A7DB2200AD8AEB /* Localizable.strings in Resources */, + E360251C27BCC97000958B21 /* Localizable.strings in Resources */, E392BE1927B791F0002698F3 /* GoogleService-Info.plist in Resources */, 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, E36DF77C27AB740C003C561C /* MapViewController.xib in Resources */, @@ -363,6 +376,9 @@ E38F241727A242C70069FC45 /* Resolver.swift in Sources */, E33A236027A4FD2C00DD647F /* UnitMapView.swift in Sources */, E396282427AFBD3D005D070E /* UnitInformationView.swift in Sources */, + E360251727BCA83300958B21 /* AccountView.swift in Sources */, + E360251527BCA3AD00958B21 /* UserInformationView.swift in Sources */, + E360251927BCA84000958B21 /* AboutView.swift in Sources */, E312E74F27B366060018C5DE /* DevicesViewModel.swift in Sources */, E33A236727A64E4500DD647F /* MarkerTransformations.swift in Sources */, E38F241527A242870069FC45 /* Inject.swift in Sources */, @@ -376,6 +392,7 @@ E3E77EE6279E6CE400150070 /* FlowCollector.swift in Sources */, E33A236527A530F300DD647F /* SmallLabelStyle.swift in Sources */, E39ABC4327A4E88C00965D05 /* UnitsViewModel.swift in Sources */, + E360251B27BCA8A600958B21 /* AccountViewModel.swift in Sources */, E34A2F4827A7878200AD8AEB /* HyperlinkText.swift in Sources */, 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, E39ABC4627A4EBD500965D05 /* DevicesView.swift in Sources */, @@ -387,6 +404,18 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXVariantGroup section */ + E360251E27BCC97000958B21 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + E360251D27BCC97000958B21 /* en */, + E360251F27BCC97300958B21 /* es-419 */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 7555FFA3242A565B00829871 /* Debug */ = { isa = XCBuildConfiguration; diff --git a/iosApp/iosApp/Details/DetailsView.swift b/iosApp/iosApp/Details/DetailsView.swift index abbed0a..1b86d2b 100644 --- a/iosApp/iosApp/Details/DetailsView.swift +++ b/iosApp/iosApp/Details/DetailsView.swift @@ -40,6 +40,8 @@ struct DetailsView: View { UnitReportsView(unit: unit) case .commands: UnitCommandsView(unit: unit) + default: + EmptyView() } } else { LoadingView() diff --git a/iosApp/iosApp/Devices/DeviceRow.swift b/iosApp/iosApp/Devices/DeviceRow.swift index 2c2cf3b..42ef0dc 100644 --- a/iosApp/iosApp/Devices/DeviceRow.swift +++ b/iosApp/iosApp/Devices/DeviceRow.swift @@ -25,6 +25,7 @@ struct DeviceRow: View { case details case reports case commands + case close } var callback: (Action) -> () var isCell: Bool = true @@ -98,6 +99,16 @@ struct DeviceRow: View { /* MARK: - Device name */ Text(unit.device.name) Spacer() + + /* MARK: - Close button */ + if !isCell { + Button { + callback(.close) + } label: { + Image(systemName: "xmark") + .foregroundColor(.secondary) + } + } } .padding(.bottom, 5.0) diff --git a/iosApp/iosApp/Localizable.strings b/iosApp/iosApp/Localizable.strings deleted file mode 100644 index 76e7bd4..0000000 --- a/iosApp/iosApp/Localizable.strings +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 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 . - */ -"app-name" = "TrackerMap"; -"app-server-url" = "https://etbsa.net/api"; - -"loading" = "Loading"; -"done" = "Done"; - -"username" = "Username"; -"password" = "Password"; -"server-url" = "Server URL"; -"login" = "Login"; - -"devices" = "Devices"; -"map" = "Map"; -"search" = "Search"; - -"details" = "Details"; -"reports" = "Reports"; -"commands" = "Commands"; -"select-action" = "Select an action"; - -"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"; -"open-location-browser" = "Open location in browser"; -"maps-url-template" = "https://www.google.com/maps/place/{y},{x}?z=19"; - -"send-command" = "Send command"; -"send-command-confirm" = "Are you sure you want to send the command?"; diff --git a/iosApp/iosApp/Map/MapView.swift b/iosApp/iosApp/Map/MapView.swift index f9757f4..e52c034 100644 --- a/iosApp/iosApp/Map/MapView.swift +++ b/iosApp/iosApp/Map/MapView.swift @@ -28,8 +28,6 @@ struct MapView: UIViewControllerRepresentable { @Binding var selected: Marker? var markerCallback: MarkerCallback? - var shouldCenter = true - class Coordinator { var shouldCenter: Bool = true var oldMarkers: [Marker] = [] @@ -52,11 +50,12 @@ struct MapView: UIViewControllerRepresentable { // MARK: - Set markers if context.coordinator.oldMarkers != markers { + print("center = \(context.coordinator.shouldCenter)") uiViewController.display(markers: markers, isReport: false, center: context.coordinator.shouldCenter) + context.coordinator.shouldCenter = false } - context.coordinator.shouldCenter = false context.coordinator.oldMarkers = markers // MARK: - Center selected marker diff --git a/iosApp/iosApp/Map/MapViewController.swift b/iosApp/iosApp/Map/MapViewController.swift index bc793e2..131a511 100644 --- a/iosApp/iosApp/Map/MapViewController.swift +++ b/iosApp/iosApp/Map/MapViewController.swift @@ -77,7 +77,7 @@ class MapViewController: UIViewController { func display(markers: [Marker], isReport: Bool, center: Bool = false) { - DispatchQueue.main.async { + mapView.runOnInit { self.mapView.display(markers: markers, isReport: isReport, center: center) @@ -85,7 +85,7 @@ class MapViewController: UIViewController { } func focusOn(marker: Marker) { - DispatchQueue.main.async { + mapView.runOnInit { self.mapView.focusOn(marker: marker) } } diff --git a/iosApp/iosApp/Map/MapViewController.xib b/iosApp/iosApp/Map/MapViewController.xib index 9d1694f..305a15b 100644 --- a/iosApp/iosApp/Map/MapViewController.xib +++ b/iosApp/iosApp/Map/MapViewController.xib @@ -35,6 +35,11 @@ + + + + + @@ -54,6 +59,11 @@ + + + + + diff --git a/iosApp/iosApp/Session/AboutView.swift b/iosApp/iosApp/Session/AboutView.swift new file mode 100644 index 0000000..c0f9099 --- /dev/null +++ b/iosApp/iosApp/Session/AboutView.swift @@ -0,0 +1,34 @@ +// +// AboutView.swift +// iosApp +// +// Created by Iván on 15/02/22. +// Copyright © 2022 orgName. All rights reserved. +// + +import SwiftUI + +struct AboutView: View { + var body: some View { + List { + Text("about-text").padding() + HStack { + Text("version") + Spacer() + Text("1.0").foregroundColor(.secondary) + } + + Button { + // TODO + } label: { + Label("source-code", systemImage: "chevron.left.forwardslash.chevron.right") + } + + Button { + // TODO + } label: { + Label("website", systemImage: "globe") + } + } + } +} diff --git a/iosApp/iosApp/Session/AccountView.swift b/iosApp/iosApp/Session/AccountView.swift new file mode 100644 index 0000000..4ca453a --- /dev/null +++ b/iosApp/iosApp/Session/AccountView.swift @@ -0,0 +1,73 @@ +// +// AccountView.swift +// iosApp +// +// Created by Iván on 15/02/22. +// Copyright © 2022 orgName. All rights reserved. +// + +import SwiftUI + +struct AccountView: View { + @StateObject var accountViewModel = AccountViewModel() + @EnvironmentObject var rootViewModel: RootViewModel + + var body: some View { + List { + Section { + // MARK: - Name + if let name = accountViewModel.user?.name { + HStack { + Text("username") + Spacer() + Text(name).foregroundColor(.secondaryLabel) + } + } + // MARK: - E-mail + if let email = accountViewModel.user?.email { + HStack { + Text("email") + Spacer() + Text(email).foregroundColor(.secondaryLabel) + } + } + // MARK: - Unique ID + if let uid = accountViewModel.user?.id { + HStack { + Text("unique-id") + Spacer() + Text("\(uid)").foregroundColor(.secondaryLabel) + } + } + // MARK: - Administrator + if let admin = accountViewModel.user?.administrator { + HStack { + Text("admin") + Spacer() + Text("\(admin)").foregroundColor(.secondaryLabel) + } + } + // MARK: - Server URL + if let server = UserDefaults.standard.string(forKey: "server-url") { + HStack { + Text("server-url") + Spacer() + Text(server).foregroundColor(.secondaryLabel) + } + } + } + + Section { + // MARK: - Sign out + Button { + rootViewModel.signOut() + } label: { + Label("signout", systemImage: "rectangle.portrait.and.arrow.right") + .foregroundColor(.systemRed) + } + } + }.onAppear { + accountViewModel.fetchUserInfo() + } + } +} diff --git a/iosApp/iosApp/Session/AccountViewModel.swift b/iosApp/iosApp/Session/AccountViewModel.swift new file mode 100644 index 0000000..7953265 --- /dev/null +++ b/iosApp/iosApp/Session/AccountViewModel.swift @@ -0,0 +1,29 @@ +// +// AccountViewModel.swift +// iosApp +// +// Created by Iván on 15/02/22. +// Copyright © 2022 orgName. All rights reserved. +// + +import Foundation +import shared + +class AccountViewModel: ObservableObject { + @Inject var sessionController: SessionController + + @Published var user: User? = nil + + init() { + let userCollector = Collector(callback: setUser) + sessionController.userFlow.collect(collector: userCollector) {_, _ in } + } + + func setUser(user: User?) { + self.user = user + } + + func fetchUserInfo() { + self.sessionController.getSession() + } +} diff --git a/iosApp/iosApp/Session/RootView.swift b/iosApp/iosApp/Session/RootView.swift index bc8a7d1..297a2aa 100644 --- a/iosApp/iosApp/Session/RootView.swift +++ b/iosApp/iosApp/Session/RootView.swift @@ -18,24 +18,35 @@ import SwiftUI import shared +class SessionState: ObservableObject { + @Published var loggedIn = false +} + struct RootView: View { @StateObject private var rootViewModel = RootViewModel() @State private var username = "" @State private var password = "" - @State private var server = "" + @State private var server: String + + init() { + server = UserDefaults.standard.string(forKey: "server-url") + ?? NSLocalizedString("app-server-url", comment: "") + } var body: some View { - switch rootViewModel.loginState { - case is SessionController.LoginStateLoading: - LoadingView() - case is SessionController.LoginStateSuccess: - UnitsView() - default: - LoginContentView(username: $username, - password: $password, - server: $server, - onLogin: rootViewModel.login) - } + Group { + switch rootViewModel.loginState { + case is SessionController.LoginStateLoading: + LoadingView() + case is SessionController.LoginStateSuccess: + UnitsView() + default: + LoginContentView(username: $username, + password: $password, + server: $server, + onLogin: rootViewModel.login) + } + }.environmentObject(rootViewModel) } } @@ -44,13 +55,7 @@ struct LoginContentView: View { @Binding var password: String @Binding var server: String - let onLogin: (SessionBody) -> Void - - func getFcmToken() -> String? { - let token = UserDefaults.standard.string(forKey: "fcmtoken") - print("FCM token is \(String(describing: token))") - return token - } + let onLogin: (String, String, String) -> Void var body: some View { VStack { @@ -63,10 +68,7 @@ struct LoginContentView: View { server: $server) Button(action: { - self.onLogin(SessionBody(url: server, - email: username, - password: password, - fcmToken: getFcmToken())) + self.onLogin(username, password, server) }) { Text("login") .font(.system(size: 18)) diff --git a/iosApp/iosApp/Session/RootViewModel.swift b/iosApp/iosApp/Session/RootViewModel.swift index d065777..682deee 100644 --- a/iosApp/iosApp/Session/RootViewModel.swift +++ b/iosApp/iosApp/Session/RootViewModel.swift @@ -38,10 +38,23 @@ class RootViewModel: ObservableObject { sessionController.restoreSession() } - func login(session: SessionBody) { - print("Username: \(session.email)") - print("Password: \(session.password)") - print("Server URL: \(session.url)") - sessionController.login(body: session) + private func getFcmToken() -> String? { + let token = UserDefaults.standard.string(forKey: "fcmtoken") + print("FCM token is \(String(describing: token))") + return token + } + + func login(username: String, password: String, url: String) { + print("Username: \(username)") + print("Password: \(password)") + print("Server URL: \(url)") + sessionController.login(body: SessionBody(url: url, + email: username, + password: password, + fcmToken: getFcmToken())) + } + + func signOut() { + sessionController.logout(token: getFcmToken()) } } diff --git a/iosApp/iosApp/Session/UserInformationView.swift b/iosApp/iosApp/Session/UserInformationView.swift new file mode 100644 index 0000000..98d4bfd --- /dev/null +++ b/iosApp/iosApp/Session/UserInformationView.swift @@ -0,0 +1,50 @@ +// +// UserInformationView.swift +// iosApp +// +// Created by Iván on 15/02/22. +// Copyright © 2022 orgName. All rights reserved. +// + +import SwiftUI + +struct UserInformationView: View { + @Environment(\.presentationMode) var presentationMode + @State var action = Action.account + + enum Action { + case account + case about + } + + var body: some View { + NavigationView { + VStack { + switch action { + case .account: + AccountView() + case .about: + AboutView() + } + } + .navigationBarTitleView( + Picker(selection: $action) { + Text("account").tag(Action.account) + Text("about").tag(Action.about) + } label: { + EmptyView() + }.pickerStyle(SegmentedPickerStyle()) + ) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + presentationMode.wrappedValue.dismiss() + } label: { + Text("done").bold() + } + } + } + } + } +} diff --git a/iosApp/iosApp/Units/UnitsView.swift b/iosApp/iosApp/Units/UnitsView.swift index d813c6d..a91511e 100644 --- a/iosApp/iosApp/Units/UnitsView.swift +++ b/iosApp/iosApp/Units/UnitsView.swift @@ -67,7 +67,9 @@ struct UnitsView: View { .visible(unitsViewModel.unitsDisplayMode == .map) } ToolbarItem(placement: .navigationBarTrailing) { - Button(action: {}) { + Button { + unitsViewModel.showUserInfo = true + } label: { Image(systemName: "person") } } @@ -81,6 +83,12 @@ struct UnitsView: View { action: unitsViewModel.detailsAction, for: unitsViewModel.detailsUnit) } + .sheet(isPresented: $unitsViewModel.showUserInfo) { + print("Dismissed") + } content: { + UserInformationView() + } + } private func getNavigationTitle(_ unitDisplayMode: UnitsViewModel.UnitsDisplayMode) -> LocalizedStringKey { diff --git a/iosApp/iosApp/Units/UnitsViewModel.swift b/iosApp/iosApp/Units/UnitsViewModel.swift index 7ffcfd1..af6fe32 100644 --- a/iosApp/iosApp/Units/UnitsViewModel.swift +++ b/iosApp/iosApp/Units/UnitsViewModel.swift @@ -52,7 +52,8 @@ class UnitsViewModel: ObservableObject { } @Published var isEditing = false @Published var showDetails = false - @Published var unitsDisplayMode: UnitsDisplayMode = .list + @Published var showUserInfo = false + @Published var unitsDisplayMode: UnitsDisplayMode = .map @Published var units: [UnitInformation] = [] { didSet { markers = units.compactMap(Marker.companion.fromUnit) @@ -127,8 +128,12 @@ class UnitsViewModel: ObservableObject { } func show(action: DeviceRow.Action, for unit: UnitInformation) { - detailsAction = action - detailsUnit = unit - showDetails = true + if action != .close { + detailsAction = action + detailsUnit = unit + showDetails = true + } else { + selectedUnit = nil + } } } diff --git a/iosApp/iosApp/en.lproj/Localizable.strings b/iosApp/iosApp/en.lproj/Localizable.strings new file mode 100644 index 0000000..392bc1c --- /dev/null +++ b/iosApp/iosApp/en.lproj/Localizable.strings @@ -0,0 +1,68 @@ +/** + * 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 . + */ +"app-name" = "TrackerMap"; +"app-server-url" = "https://gps.trackermap.mx/api"; +"app-website" = "https://trackermap.mx"; +"app-source-code" = "https://git.sr.ht/~avalos/trackermap-mobile"; + +"loading" = "Loading"; +"done" = "Done"; + +"username" = "Username"; +"password" = "Password"; +"server-url" = "Server URL"; +"login" = "Login"; + +"devices" = "Devices"; +"map" = "Map"; +"search" = "Search"; + +"details" = "Details"; +"reports" = "Reports"; +"commands" = "Commands"; +"select-action" = "Select an action"; + +"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"; +"open-location-browser" = "Open location in browser"; +"maps-url-template" = "https://www.google.com/maps/place/{y},{x}?z=19"; + +"send-command" = "Send command"; +"send-command-confirm" = "Are you sure you want to send the command?"; + +"account" = "Account"; +"about" = "About"; +"email" = "E-mail"; +"admin" = "Administrator"; +"signout" = "Sign out"; + +"about-text" = "TrackerMap is a free (as in freedom) software app for tracking and managing GPS devices in Traccar servers.\n\nTrackerMap source code is licensed under the GNU General Public License Version 3."; +"version" = "Version"; +"website" = "Website"; +"source-code" = "Source code"; diff --git a/iosApp/iosApp/es-419.lproj/Localizable.strings b/iosApp/iosApp/es-419.lproj/Localizable.strings new file mode 100644 index 0000000..401582e --- /dev/null +++ b/iosApp/iosApp/es-419.lproj/Localizable.strings @@ -0,0 +1,68 @@ +/** + * 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 . + */ +"app-name" = "TrackerMap"; +"app-server-url" = "https://gps.trackermap.mx/api"; +"app-website" = "https://trackermap.mx"; +"app-source-code" = "https://git.sr.ht/~avalos/trackermap-mobile"; + +"loading" = "Cargando"; +"done" = "Hecho"; + +"username" = "Usuario"; +"password" = "Contraseña"; +"server-url" = "URL del servidor"; +"login" = "Iniciar sesión"; + +"devices" = "Dispositivos"; +"map" = "Mapa"; +"search" = "Buscar"; + +"details" = "Detalles"; +"reports" = "Reportes"; +"commands" = "Comandos"; +"select-action" = "Seleccionar una acción"; + +"map-layer" = "Capa del mapa"; +"openstreetmap" = "OpenStreetMap"; +"satellite" = "Satélite"; + +"contact" = "Contacto"; +"unique-id" = "ID único"; +"datetime" = "Fecha y hora"; +"latitude" = "Latitud"; +"longitude" = "Longitud"; +"speed" = "Velocidad"; +"address" = "Dirección"; +"hourmeter" = "Horómetro"; +"protocol" = "Protocolo"; +"open-location-browser" = "Abrir ubicación en navegador"; +"maps-url-template" = "https://www.google.com/maps/place/{y},{x}?z=19"; + +"send-command" = "Enviar comando"; +"send-command-confirm" = "¿Está seguro que desea enviar el comando?"; + +"account" = "Cuenta"; +"about" = "Acerca de"; +"email" = "Correo electrónico"; +"admin" = "Administrador"; +"signout" = "Cerrar sesión"; + +"about-text" = "TrackerMap es una aplicación de software libre para rastrear y gestionar dispositivos GPS en servidores de Traccar.\n\nEl código fuente de TrackerMap está disponible bajo la licencia GNU General Public License versión 3."; +"version" = "Versión"; +"website" = "Sitio web"; +"source-code" = "Código fuente"; -- cgit v1.2.3