// // ContentView.swift // MyPass // // Created by Christophe Vila on 21/05/2026. // import SwiftUI import MyPassCore #if os(macOS) import AppKit #endif struct ContentView: View { @StateObject private var session = VaultSession() var body: some View { SwiftUI.Group { if session.isLocked { UnlockView(vm: UnlockViewModel(session: session)) } else { vaultView } } .onReceive( NotificationCenter.default.publisher(for: sceneBackgroundNotification) ) { _ in session.lock() } } @ViewBuilder private var vaultView: some View { let vaultVM = VaultViewModel(session: session) #if os(macOS) MacVaultView(vm: vaultVM) #else NavigationStack { if let root = session.database?.root { GroupBrowserView(vm: vaultVM, group: root) } } #endif } private var sceneBackgroundNotification: Notification.Name { #if os(iOS) UIScene.didEnterBackgroundNotification #else NSApplication.didResignActiveNotification #endif } } // MARK: - macOS 3-column layout #if os(macOS) private struct MacVaultView: View { @ObservedObject var vm: VaultViewModel @State private var selectedGroup: MyPassCore.Group? @State private var selectedEntry: Entry? @State private var showAddEntry = false private var activeGroup: MyPassCore.Group? { selectedGroup ?? vm.session.database?.root } var body: some View { NavigationSplitView { GroupSidebarView(vm: vm, selectedGroup: $selectedGroup) } content: { if vm.isSearching { SearchView(vm: vm, selectedEntry: $selectedEntry) } else if let group = activeGroup { entryList(for: group) } else { Text("Select a group").foregroundStyle(.secondary) } } detail: { if let entry = selectedEntry { EntryDetailView(entry: entry, session: vm.session) } else { Text("Select an entry").foregroundStyle(.secondary) } } .searchable(text: $vm.searchQuery, prompt: "Search all entries…") .sheet(isPresented: $showAddEntry) { if let group = activeGroup { EntryEditView(vm: EntryEditViewModel(session: vm.session, groupId: group.id)) } } } private func entryList(for group: MyPassCore.Group) -> some View { List(group.entries, selection: $selectedEntry) { entry in EntryListRow(entry: entry).tag(entry) } .navigationTitle(group.name) .toolbar { ToolbarItem { Button { showAddEntry = true } label: { Image(systemName: "plus") } } } } } private struct GroupSidebarView: View { @ObservedObject var vm: VaultViewModel @Binding var selectedGroup: MyPassCore.Group? var body: some View { List(selection: $selectedGroup) { if let root = vm.session.database?.root { GroupNode(group: root) } } .navigationTitle("Groups") } } private struct GroupNode: View { let group: MyPassCore.Group var body: some View { if group.subgroups.isEmpty { Label(group.name, systemImage: "folder").tag(group) } else { DisclosureGroup { ForEach(group.subgroups) { sub in GroupNode(group: sub) } } label: { Label(group.name, systemImage: "folder").tag(group) } } } } #endif