| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // LoopFollow
- // MoreMenuView.swift
- import SwiftUI
- import UIKit
- struct MoreMenuView: View {
- @State private var pendingRoute: MenuRoute?
- @State private var latestVersion: String?
- @State private var versionTint: Color = .secondary
- @State private var alertTitle = ""
- @State private var alertMessage = ""
- @State private var showAlert = false
- @State private var currentVersion: String = AppVersionManager().version()
- var body: some View {
- List {
- // Settings
- Section {
- NavigationLink(value: SettingsRoute.settings) {
- Label("Settings", systemImage: "gearshape")
- }
- }
- // Features
- Section("Features") {
- ForEach(TabItem.featureOrder) { item in
- FullRowButton(showsChevron: true) {
- let tabs = Storage.shared.orderedTabBarItems()
- if let tabIndex = tabs.firstIndex(of: item) {
- Observable.shared.selectedTabIndex.value = tabIndex
- } else {
- pendingRoute = MenuRoute(item)
- }
- } label: {
- Label(item.displayName, systemImage: item.icon)
- }
- }
- }
- // Logging
- Section("Logging") {
- FullRowButton(showsChevron: true) { pendingRoute = .log } label: {
- Label("View Log", systemImage: "doc.text.magnifyingglass")
- }
- FullRowButton { shareLogs() } label: {
- Label("Share Logs", systemImage: "square.and.arrow.up")
- }
- }
- // Support & Community
- Section("Support & Community") {
- Link(destination: URL(string: "https://loopfollowdocs.org/")!) {
- HStack {
- Label("LoopFollow Docs", systemImage: "book")
- Spacer()
- Image(systemName: "arrow.up.right.square")
- .foregroundStyle(.tertiary)
- }
- }
- Link(destination: URL(string: "https://discord.gg/KQgk3gzuYU")!) {
- HStack {
- Label("Loop and Learn Discord", systemImage: "bubble.left.and.bubble.right")
- Spacer()
- Image(systemName: "arrow.up.right.square")
- .foregroundStyle(.tertiary)
- }
- }
- Link(destination: URL(string: "https://www.facebook.com/groups/loopfollowlnl")!) {
- HStack {
- Label("LoopFollow Facebook Group", systemImage: "person.2.fill")
- Spacer()
- Image(systemName: "arrow.up.right.square")
- .foregroundStyle(.tertiary)
- }
- }
- }
- // Build Information
- Section("Build Information") {
- buildInfoRow(title: "Version", value: currentVersion, color: versionTint)
- buildInfoRow(title: "Latest version", value: latestVersion ?? "Fetching…", color: .secondary)
- let build = BuildDetails.default
- if !(build.isMacApp() || build.isSimulatorBuild()) {
- buildInfoRow(
- title: build.expirationHeaderString,
- value: dateTimeUtils.formattedDate(from: build.calculateExpirationDate()),
- color: .secondary
- )
- }
- buildInfoRow(title: "Built", value: dateTimeUtils.formattedDate(from: build.buildDate()), color: .secondary)
- buildInfoRow(title: "Branch", value: build.branchAndSha, color: .secondary)
- }
- }
- .listStyle(.insetGrouped)
- .navigationTitle("Menu")
- .navigationBarTitleDisplayMode(.large)
- .task {
- await fetchVersionInfo()
- }
- .alert(alertTitle, isPresented: $showAlert) {
- Button("OK", role: .cancel) {}
- } message: {
- Text(alertMessage)
- }
- .navigationDestination(for: SettingsRoute.self) { $0.destination }
- .navigationDestination(
- isPresented: Binding(
- get: { pendingRoute != nil },
- set: { if !$0 { pendingRoute = nil } }
- )
- ) {
- if let route = pendingRoute {
- route.destination
- }
- }
- }
- // MARK: - Helpers
- private func buildInfoRow(title: String, value: String, color: Color) -> some View {
- HStack {
- Text(title)
- Spacer()
- Text(value)
- .foregroundStyle(color)
- }
- }
- private func shareLogs() {
- let files = LogManager.shared.logFilesForTodayAndYesterday()
- guard !files.isEmpty else {
- alertTitle = "No Logs Available"
- alertMessage = "There are no logs to share."
- showAlert = true
- return
- }
- let avc = UIActivityViewController(activityItems: files, applicationActivities: nil)
- UIApplication.shared.topMost?.present(avc, animated: true)
- }
- private func fetchVersionInfo() async {
- let mgr = AppVersionManager()
- let (latest, newer, blacklisted) = await mgr.checkForNewVersionAsync()
- latestVersion = latest ?? "Unknown"
- versionTint = blacklisted ? .red
- : newer ? .orange
- : latest == currentVersion ? .green
- : .secondary
- }
- }
- // MARK: – Full-row tappable button
- private struct FullRowButton<Label: View>: View {
- var showsChevron: Bool = false
- let action: () -> Void
- @ViewBuilder let label: () -> Label
- var body: some View {
- Button(action: action) {
- HStack {
- label()
- Spacer(minLength: 0)
- if showsChevron {
- Image(systemName: "chevron.right")
- .font(.footnote.weight(.semibold))
- .foregroundStyle(.tertiary)
- }
- }
- .contentShape(Rectangle())
- }
- .buttonStyle(.plain)
- }
- }
- // MARK: – Menu routing
- enum MenuRoute: Hashable {
- case home
- case alarms
- case remote
- case nightscout
- case snoozer
- case treatments
- case stats
- case log
- init(_ item: TabItem) {
- switch item {
- case .home: self = .home
- case .alarms: self = .alarms
- case .remote: self = .remote
- case .nightscout: self = .nightscout
- case .snoozer: self = .snoozer
- case .treatments: self = .treatments
- case .stats: self = .stats
- }
- }
- @ViewBuilder
- var destination: some View {
- switch self {
- case .home: HomeContentView(isModal: true)
- case .alarms: AlarmsContainerView(embedsInNavigationStack: false)
- case .remote: RemoteContentView()
- case .nightscout: NightscoutContentView()
- case .snoozer: SnoozerView()
- case .treatments: TreatmentsView()
- case .stats: AggregatedStatsContentView(mainViewController: MainViewController.shared)
- case .log: LogView()
- }
- }
- }
|