aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2022-02-25 22:02:51 -0600
committerIván Ávalos <avalos@disroot.org>2022-02-25 22:02:51 -0600
commit136e4ebe289e286b62c8e37bcd512de6df0de0d3 (patch)
tree894ce7d172a39b0a7c99e19e9b4d25064549529a
parentef7a88961841752cb1a38a39f5e0cc298b463f56 (diff)
parent70141fe10227ef4eca2ef7ae4b2b9d7c8fac5675 (diff)
downloadetbsa-trackermap-mobile-136e4ebe289e286b62c8e37bcd512de6df0de0d3.tar.gz
etbsa-trackermap-mobile-136e4ebe289e286b62c8e37bcd512de6df0de0d3.tar.bz2
etbsa-trackermap-mobile-136e4ebe289e286b62c8e37bcd512de6df0de0d3.zip
Merge branch 'main' into ios_reports
-rw-r--r--iosApp/iosApp.xcodeproj/project.pbxproj37
-rw-r--r--iosApp/iosApp/AppDelegate.swift8
-rw-r--r--iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift12
-rw-r--r--iosApp/iosApp/Details/DetailsView.swift5
-rw-r--r--iosApp/iosApp/Devices/DeviceRow.swift22
-rw-r--r--iosApp/iosApp/Info.plist8
-rw-r--r--iosApp/iosApp/Map/MapView.swift5
-rw-r--r--iosApp/iosApp/Map/MapViewController.swift4
-rw-r--r--iosApp/iosApp/Map/MapViewController.xib10
-rw-r--r--iosApp/iosApp/Session/AboutView.swift48
-rw-r--r--iosApp/iosApp/Session/AccountView.swift73
-rw-r--r--iosApp/iosApp/Session/AccountViewModel.swift29
-rw-r--r--iosApp/iosApp/Session/RootView.swift48
-rw-r--r--iosApp/iosApp/Session/RootViewModel.swift23
-rw-r--r--iosApp/iosApp/Session/UserInformationView.swift50
-rw-r--r--iosApp/iosApp/Units/UnitsView.swift10
-rw-r--r--iosApp/iosApp/Units/UnitsViewModel.swift25
-rw-r--r--iosApp/iosApp/en.lproj/Localizable.strings (renamed from iosApp/iosApp/Localizable.strings)15
-rw-r--r--iosApp/iosApp/es-419.lproj/Localizable.strings68
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt6
20 files changed, 438 insertions, 68 deletions
diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj
index aa509da..abdf916 100644
--- a/iosApp/iosApp.xcodeproj/project.pbxproj
+++ b/iosApp/iosApp.xcodeproj/project.pbxproj
@@ -21,7 +21,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 */; };
@@ -74,7 +78,12 @@
E33A237027A7553500DD647F /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; };
E33A237227A7581A00DD647F /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
E34A2F4727A7878200AD8AEB /* HyperlinkText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HyperlinkText.swift; sourceTree = "<group>"; };
- E34A2F4C27A7DB2200AD8AEB /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
+ E360251427BCA3AD00958B21 /* UserInformationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInformationView.swift; sourceTree = "<group>"; };
+ E360251627BCA83300958B21 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = "<group>"; };
+ E360251827BCA84000958B21 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
+ E360251A27BCA8A600958B21 /* AccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewModel.swift; sourceTree = "<group>"; };
+ E360251D27BCC97000958B21 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
+ E360251F27BCC97300958B21 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; };
E36A5A8927B4C8BB0070DED5 /* iosApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iosApp.entitlements; sourceTree = "<group>"; };
E36DF77927AB740C003C561C /* MapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = "<group>"; };
E36DF77A27AB740C003C561C /* MapViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MapViewController.xib; sourceTree = "<group>"; };
@@ -140,7 +149,7 @@
children = (
E392BE1827B791F0002698F3 /* GoogleService-Info.plist */,
E36A5A8927B4C8BB0070DED5 /* iosApp.entitlements */,
- E34A2F4C27A7DB2200AD8AEB /* Localizable.strings */,
+ E360251E27BCC97000958B21 /* Localizable.strings */,
E33A235E27A4FD1C00DD647F /* Map */,
E35A078427AB615F00F24D71 /* Details */,
E39ABC4427A4EBB000965D05 /* Devices */,
@@ -236,6 +245,10 @@
children = (
7555FF82242A565900829871 /* RootView.swift */,
E38F241B27A26DD70069FC45 /* RootViewModel.swift */,
+ E360251427BCA3AD00958B21 /* UserInformationView.swift */,
+ E360251627BCA83300958B21 /* AccountView.swift */,
+ E360251827BCA84000958B21 /* AboutView.swift */,
+ E360251A27BCA8A600958B21 /* AccountViewModel.swift */,
);
path = Session;
sourceTree = "<group>";
@@ -326,7 +339,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 */,
@@ -366,6 +379,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 */,
@@ -379,6 +395,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 */,
@@ -391,6 +408,18 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXVariantGroup section */
+ E360251E27BCC97000958B21 /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ E360251D27BCC97000958B21 /* en */,
+ E360251F27BCC97300958B21 /* es-419 */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
/* Begin XCBuildConfiguration section */
7555FFA3242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
diff --git a/iosApp/iosApp/AppDelegate.swift b/iosApp/iosApp/AppDelegate.swift
index 4233910..72e3e92 100644
--- a/iosApp/iosApp/AppDelegate.swift
+++ b/iosApp/iosApp/AppDelegate.swift
@@ -39,6 +39,14 @@ class AppDelegate: NSObject, UIApplicationDelegate {
print(userInfo)
return UIBackgroundFetchResult.newData
}
+
+ func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
+ #if DEBUG
+ Messaging.messaging().setAPNSToken(deviceToken, type: .sandbox)
+ #else
+ Messaging.messaging().setAPNSToken(deviceToken, type: .prod)
+ #endif
+ }
}
extension AppDelegate: UNUserNotificationCenterDelegate {
diff --git a/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift b/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift
index d7298c4..8e1da00 100644
--- a/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift
+++ b/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift
@@ -21,6 +21,7 @@ import shared
class UnitCommandsViewModel: ObservableObject {
@Inject var commandsController: CommandsController
+ var deviceId: Int32? = nil
@Published var commands = [Command]()
@Published var selected: Command?
@Published var selectedId: Int = 0 {
@@ -29,6 +30,9 @@ class UnitCommandsViewModel: ObservableObject {
Int(truncating: $0.id!) == selectedId
}) {
self.selected = selected
+ if let id = deviceId {
+ self.selected?.deviceId = KotlinInt(int: id)
+ }
notSelected = false
} else {
self.selected = nil
@@ -40,6 +44,7 @@ class UnitCommandsViewModel: ObservableObject {
@Published var showConfirmation: Bool = false
func fetchCommands(id: Int32) {
+ deviceId = id
commandsController.fetchCommands(deviceId: id) { commands, error in
print("We've got the commands! \(commands ?? [])")
self.commands = commands ?? []
@@ -48,7 +53,12 @@ class UnitCommandsViewModel: ObservableObject {
func sendCommand() {
if let command = selected {
- commandsController.sendCommand(command: command) { _, error in }
+ commandsController.sendCommand(command: command) { _, error in
+ if let error = error {
+ print("There is a fucking error")
+ print(error.localizedDescription)
+ }
+ }
}
}
}
diff --git a/iosApp/iosApp/Details/DetailsView.swift b/iosApp/iosApp/Details/DetailsView.swift
index 2c7418b..c869369 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()
@@ -48,7 +50,7 @@ struct DetailsView: View {
.navigationBarTitleView(
Picker(selection: $action) {
Text("details").tag(DeviceRow.Action.details)
- Text("reports").tag(DeviceRow.Action.reports)
+ //Text("reports").tag(DeviceRow.Action.reports)
Text("commands").tag(DeviceRow.Action.commands)
} label: {
EmptyView()
@@ -62,6 +64,7 @@ struct DetailsView: View {
let type = Marker.companion
.categoryToMarkerType(category: category)
Image(MarkerTransformations.markerTypeToImageName(markerType: type))
+ .sizeToFit()
} else {
EmptyView()
}
diff --git a/iosApp/iosApp/Devices/DeviceRow.swift b/iosApp/iosApp/Devices/DeviceRow.swift
index 4553a4b..0439fff 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
@@ -37,6 +38,7 @@ struct DeviceRow: View {
/* MARK: - Device icon */
let category = Marker.companion.categoryToMarkerType(category: unit.device.category)
Image(MarkerTransformations.markerTypeToImageName(markerType: category))
+ .sizeToFitSquare(sideLength: 40.0)
.padding(5.0)
getSharedContent()
@@ -98,6 +100,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)
@@ -160,9 +172,9 @@ struct DeviceRow: View {
Label("details", systemImage: "info.circle")
}
- let reports = Button { callback(.reports) } label: {
- Label("reports", systemImage: "clock")
- }
+ //let reports = Button { callback(.reports) } label: {
+ // Label("reports", systemImage: "clock")
+ //}
let commands = Button { callback(.commands) } label: {
Label("commands", systemImage: "paperplane")
@@ -170,12 +182,12 @@ struct DeviceRow: View {
if isCell {
commands
- reports
+ // reports
details
} else {
Group {
details
- reports
+ // reports
commands
}
.frame(maxWidth: .infinity)
diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist
index bf9db74..99c8b7b 100644
--- a/iosApp/iosApp/Info.plist
+++ b/iosApp/iosApp/Info.plist
@@ -4,10 +4,6 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
- <key>UIBackgroundModes</key>
- <array>
- <string>remote-notification</string>
- </array>
<key>CFBundleDisplayName</key>
<string>TrackerMap</string>
<key>CFBundleExecutable</key>
@@ -31,6 +27,10 @@
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
+ <key>UIBackgroundModes</key>
+ <array>
+ <string>remote-notification</string>
+ </array>
<key>UILaunchScreen</key>
<dict/>
<key>UIRequiredDeviceCapabilities</key>
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 @@
<imageReference key="image" image="plus" catalog="system" symbolScale="large"/>
<preferredSymbolConfiguration key="preferredSymbolConfiguration"/>
</state>
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+ <integer key="value" value="10"/>
+ </userDefinedRuntimeAttribute>
+ </userDefinedRuntimeAttributes>
<connections>
<action selector="onZoomInPressed:" destination="-1" eventType="touchUpInside" id="x4P-sk-jdi"/>
</connections>
@@ -54,6 +59,11 @@
<imageReference key="image" image="minus" catalog="system" symbolScale="large"/>
<preferredSymbolConfiguration key="preferredSymbolConfiguration"/>
</state>
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+ <integer key="value" value="10"/>
+ </userDefinedRuntimeAttribute>
+ </userDefinedRuntimeAttributes>
<connections>
<action selector="onZoomOutPressed:" destination="-1" eventType="touchUpInside" id="rt2-fy-VCy"/>
</connections>
diff --git a/iosApp/iosApp/Session/AboutView.swift b/iosApp/iosApp/Session/AboutView.swift
new file mode 100644
index 0000000..7e5325b
--- /dev/null
+++ b/iosApp/iosApp/Session/AboutView.swift
@@ -0,0 +1,48 @@
+//
+// AboutView.swift
+// iosApp
+//
+// Created by Iván on 15/02/22.
+// Copyright © 2022 orgName. All rights reserved.
+//
+
+import SwiftUI
+
+struct AboutView: View {
+
+ private func getVersion() -> String? {
+ return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
+ }
+
+ var body: some View {
+ List {
+ Text("about-text").padding()
+ if let version = getVersion() {
+ HStack {
+ Text("version")
+ Spacer()
+ Text(version).foregroundColor(.secondary)
+ }
+ }
+
+ Button {
+ if let url = URL(string: NSLocalizedString("app-source-code", comment: "")) {
+ if UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url, options: [:])
+ }
+ }
+ } label: {
+ Label("source-code", systemImage: "chevron.left.forwardslash.chevron.right")
+ }
+
+ Button {
+ if let url = URL(string: NSLocalizedString("app-website", comment: "")) {
+ if UIApplication.shared.canOpenURL(url) {
+ UIApplication.shared.open(url, options: [:])
+ }
+ } } 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<User?>(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..47adeb7 100644
--- a/iosApp/iosApp/Units/UnitsViewModel.swift
+++ b/iosApp/iosApp/Units/UnitsViewModel.swift
@@ -30,17 +30,6 @@ class UnitsViewModel: ObservableObject {
case list
}
- class Camera {
- let point: MaplyCoordinate?
- let height: Float?
-
- init(_ point: MaplyCoordinate? = nil,
- height: Float? = nil) {
- self.point = point
- self.height = height
- }
- }
-
var detailsUnit: UnitInformation? = nil
var detailsAction = DeviceRow.Action.details
@@ -52,7 +41,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)
@@ -71,7 +61,6 @@ class UnitsViewModel: ObservableObject {
@Published var selectedMarker: Marker? = nil
@Published var mapLayerType: MapLayer = .companion.defaultLayer
@Published var geofences: [Int: Geofence] = [:]
- @Published var camera: Camera = Camera()
init() {
unitsController.fetchUnits(scope: mainScope)
@@ -127,8 +116,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/Localizable.strings b/iosApp/iosApp/en.lproj/Localizable.strings
index 76e7bd4..392bc1c 100644
--- a/iosApp/iosApp/Localizable.strings
+++ b/iosApp/iosApp/en.lproj/Localizable.strings
@@ -16,7 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
"app-name" = "TrackerMap";
-"app-server-url" = "https://etbsa.net/api";
+"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";
@@ -53,3 +55,14 @@
"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 <avalos@disroot.org>, Henoch Ojeda <imhenoch@protonmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+"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";
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt
index d3deca1..0faaa72 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/SessionController.kt
@@ -113,7 +113,7 @@ class SessionController(
try {
userFlow.value?.let { user ->
val attributes = user.attributes.toMutableMap()
- attributes["notificationTokens"].toString().let { tokens ->
+ attributes["notificationTokens"]?.content?.let { tokens ->
if (tokens == "null" || !tokens.contains(token)) {
if (tokens == "null") {
attributes["notificationTokens"] = JsonPrimitive(token)
@@ -123,6 +123,8 @@ class SessionController(
}
+ } ?: run {
+ attributes["notificationTokens"] = JsonPrimitive(token)
}
usersApi.usersIdPut(user.copy(
attributes = attributes
@@ -138,7 +140,7 @@ class SessionController(
try {
userFlow.value?.let { user ->
val attributes = user.attributes.toMutableMap()
- attributes["notificationTokens"].toString().let { tokens ->
+ attributes["notificationTokens"]?.content?.let { tokens ->
if (tokens.contains("$token,")) {
attributes["notificationTokens"] =
JsonPrimitive(token.replace("$token,", ""))