Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions LoopFollow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
6589CC6D2E9E7D1600BB18FE /* CalendarSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC582E9E7D1600BB18FE /* CalendarSettingsView.swift */; };
6589CC6E2E9E7D1600BB18FE /* SettingsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC5F2E9E7D1600BB18FE /* SettingsMenuView.swift */; };
6589CC6F2E9E7D1600BB18FE /* AdvancedSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC572E9E7D1600BB18FE /* AdvancedSettingsViewModel.swift */; };
6589CC712E9E7D1600BB18FE /* ShareLogNoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC702E9E7D1600BB18FE /* ShareLogNoticeView.swift */; };
6589CC712E9E814F00BB18FE /* AlarmSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC702E9E814F00BB18FE /* AlarmSelectionView.swift */; };
6589CC752E9EAFB700BB18FE /* SettingsMigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC742E9EAFB700BB18FE /* SettingsMigrationManager.swift */; };
65E153C32E4BB69100693A4F /* URLTokenValidationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E153C22E4BB69100693A4F /* URLTokenValidationView.swift */; };
Expand Down Expand Up @@ -506,6 +507,7 @@
6589CC5E2E9E7D1600BB18FE /* GraphSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphSettingsView.swift; sourceTree = "<group>"; };
6589CC5F2E9E7D1600BB18FE /* SettingsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMenuView.swift; sourceTree = "<group>"; };
6589CC602E9E7D1600BB18FE /* TabCustomizationModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabCustomizationModal.swift; sourceTree = "<group>"; };
6589CC702E9E7D1600BB18FE /* ShareLogNoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareLogNoticeView.swift; sourceTree = "<group>"; };
6589CC702E9E814F00BB18FE /* AlarmSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmSelectionView.swift; sourceTree = "<group>"; };
6589CC742E9EAFB700BB18FE /* SettingsMigrationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMigrationManager.swift; sourceTree = "<group>"; };
65E153C22E4BB69100693A4F /* URLTokenValidationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLTokenValidationView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -968,6 +970,7 @@
6589CC5E2E9E7D1600BB18FE /* GraphSettingsView.swift */,
657F98172F043D8100F732BD /* HomeContentView.swift */,
6589CC5F2E9E7D1600BB18FE /* SettingsMenuView.swift */,
6589CC702E9E7D1600BB18FE /* ShareLogNoticeView.swift */,
6589CC602E9E7D1600BB18FE /* TabCustomizationModal.swift */,
);
path = Settings;
Expand Down Expand Up @@ -2249,6 +2252,7 @@
6589CC6E2E9E7D1600BB18FE /* SettingsMenuView.swift in Sources */,
657F98182F043D8100F732BD /* HomeContentView.swift in Sources */,
6589CC6F2E9E7D1600BB18FE /* AdvancedSettingsViewModel.swift in Sources */,
6589CC712E9E7D1600BB18FE /* ShareLogNoticeView.swift in Sources */,
DD493ADF2ACF22BB009A6922 /* SAge.swift in Sources */,
DDC6CA3F2DD7C6340060EE25 /* TemporaryAlarmEditor.swift in Sources */,
DDF699992C5AA3060058A8D9 /* TempTargetPresetManager.swift in Sources */,
Expand Down
37 changes: 37 additions & 0 deletions LoopFollow/Settings/ShareLogNoticeView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// LoopFollow
// ShareLogNoticeView.swift

import SwiftUI

struct ShareLogNoticeView: View {
@State private var noticeText: String = ""
let onCancel: () -> Void
let onShare: (String) -> Void

var body: some View {
NavigationView {
Form {
Section {
Text("Thanks for sharing these logs to help us find the problem. Please describe it in as much detail as possible — what time did it happen, what did you do, and what did you expect to happen that didn't?")
.font(.callout)
.foregroundColor(.secondary)
}

Section(header: Text("Description")) {
TextEditor(text: $noticeText)
.frame(minHeight: 180)
}
}
.preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
.navigationBarTitle("Share Logs", displayMode: .inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", action: onCancel)
}
ToolbarItem(placement: .confirmationAction) {
Button("Share") { onShare(noticeText) }
}
}
}
}
}
7 changes: 7 additions & 0 deletions LoopFollow/Storage/Storage+Migrate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ extension Storage {
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: legacyNotificationIDs)
}

func migrateStep8() {
// Default for debugLogLevel changed from false to true so users ship useful
// logs when they report a problem. Force-enable for existing users.
LogManager.shared.log(category: .general, message: "Running migrateStep8 — enabling debug log level")
debugLogLevel.value = true
}

func migrateStep6() {
// APNs credential separation
LogManager.shared.log(category: .general, message: "Running migrateStep6 — APNs credential separation")
Expand Down
2 changes: 1 addition & 1 deletion LoopFollow/Storage/Storage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Storage {

var selectedBLEDevice = StorageValue<BLEDevice?>(key: "selectedBLEDevice", defaultValue: nil)

var debugLogLevel = StorageValue<Bool>(key: "debugLogLevel", defaultValue: false)
var debugLogLevel = StorageValue<Bool>(key: "debugLogLevel", defaultValue: true)

var contactTrend = StorageValue<ContactIncludeOption>(key: "contactTrend", defaultValue: .off)
var contactDelta = StorageValue<ContactIncludeOption>(key: "contactDelta", defaultValue: .off)
Expand Down
5 changes: 5 additions & 0 deletions LoopFollow/ViewControllers/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,11 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
Storage.shared.migrateStep7()
Storage.shared.migrationStep.value = 7
}

if Storage.shared.migrationStep.value < 8 {
Storage.shared.migrateStep8()
Storage.shared.migrationStep.value = 8
}
}

@objc func appDidBecomeActive() {
Expand Down
56 changes: 55 additions & 1 deletion LoopFollow/ViewControllers/MoreMenuViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,64 @@ class MoreMenuViewController: UIViewController {
presentSimpleAlert(title: "No Logs Available", message: "There are no logs to share.")
return
}
let avc = UIActivityViewController(activityItems: files, applicationActivities: nil)

let noticeView = ShareLogNoticeView(
onCancel: { [weak self] in
self?.dismiss(animated: true)
},
onShare: { [weak self] noticeText in
self?.dismiss(animated: true) {
self?.presentLogShareSheet(noticeText: noticeText, logFiles: files)
}
}
)
let host = UIHostingController(rootView: noticeView)
host.overrideUserInterfaceStyle = Storage.shared.appearanceMode.value.userInterfaceStyle
host.modalPresentationStyle = .formSheet
present(host, animated: true)
}

private func presentLogShareSheet(noticeText: String, logFiles: [URL]) {
var items: [Any] = logFiles
if let noticeURL = writeShareNoticeFile(text: noticeText) {
items.insert(noticeURL, at: 0)
}
let avc = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(avc, animated: true)
}

private func writeShareNoticeFile(text: String) -> URL? {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd_HHmm"
let timestamp = formatter.string(from: Date())

let version = AppVersionManager().version()
let branchAndSha = BuildDetails.default.branchAndSha

let body = text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
? "(no description provided)"
: text

let contents = """
LoopFollow Log Share Notice
Date: \(ISO8601DateFormatter().string(from: Date()))
App version: \(version) (\(branchAndSha))

User description:
\(body)
"""

let url = FileManager.default.temporaryDirectory
.appendingPathComponent("ShareNotice_\(timestamp).txt")
do {
try contents.write(to: url, atomically: true, encoding: .utf8)
return url
} catch {
LogManager.shared.log(category: .general, message: "Failed to write share notice file: \(error)")
return nil
}
}

private func openURL(_ urlString: String) {
if let url = URL(string: urlString) {
UIApplication.shared.open(url)
Expand Down
Loading