Compare commits

...

2 Commits

Author SHA1 Message Date
Shadowfacts 4124e8f87c Assorted changes 2024-04-01 18:48:54 -04:00
Shadowfacts d636f23ef3 Assorted changes 2023-09-22 20:12:56 -04:00
10 changed files with 60 additions and 22 deletions

View File

@ -337,7 +337,7 @@
attributes = { attributes = {
BuildIndependentTargetsInParallel = 1; BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1300; LastSwiftUpdateCheck = 1300;
LastUpgradeCheck = 1300; LastUpgradeCheck = 1530;
TargetAttributes = { TargetAttributes = {
D61479D226D0AF1C00710B79 = { D61479D226D0AF1C00710B79 = {
CreatedOnToolsVersion = 13.0; CreatedOnToolsVersion = 13.0;
@ -522,6 +522,7 @@
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
@ -583,6 +584,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -617,7 +619,7 @@
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -648,7 +650,7 @@
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@ -666,6 +668,7 @@
D6147A0926D141F800710B79 /* Debug */ = { D6147A0926D141F800710B79 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
@ -673,6 +676,7 @@
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -682,6 +686,7 @@
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.OTPKit; PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.OTPKit;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -696,6 +701,7 @@
D6147A0A26D141F800710B79 /* Release */ = { D6147A0A26D141F800710B79 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
@ -703,6 +709,7 @@
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -712,6 +719,7 @@
"@loader_path/Frameworks", "@loader_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17";
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.OTPKit; PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.OTPKit;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;

View File

@ -7,12 +7,12 @@
<key>OTP.xcscheme_^#shared#^_</key> <key>OTP.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>1</integer> <integer>0</integer>
</dict> </dict>
<key>OTPKit.xcscheme_^#shared#^_</key> <key>OTPKit.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>0</integer> <integer>1</integer>
</dict> </dict>
</dict> </dict>
</dict> </dict>

View File

@ -43,6 +43,15 @@ struct KeyData: Codable {
} }
} }
mutating func addOrUpdateFolders(_ folders: [Folder]) {
for f in folders {
guard !self.folders.contains(where: { $0.id == f.id }) else {
continue
}
self.folders.append(f)
}
}
mutating func updateKey(entryID id: UUID, newKey: TOTPKey) { mutating func updateKey(entryID id: UUID, newKey: TOTPKey) {
guard let index = entries.firstIndex(where: { $0.id == id }) else { guard let index = entries.firstIndex(where: { $0.id == id }) else {
return return

View File

@ -51,6 +51,7 @@ class KeyStore: ObservableObject {
data = newStore data = newStore
} else { } else {
data.addOrUpdateEntries(newStore.entries) data.addOrUpdateEntries(newStore.entries)
data.addOrUpdateFolders(newStore.folders)
} }
} }

View File

@ -18,7 +18,7 @@ struct AddQRView: View {
@State private var isShowingConfirmView = false @State private var isShowingConfirmView = false
var body: some View { var body: some View {
NavigationView { NavigationStack {
CodeScannerView(codeTypes: [.qr], scanMode: .once) { (result) in CodeScannerView(codeTypes: [.qr], scanMode: .once) { (result) in
switch result { switch result {
case .success(let code): case .success(let code):
@ -44,15 +44,9 @@ struct AddQRView: View {
} }
} }
} }
.overlay { .navigationDestination(isPresented: $isShowingConfirmView) {
NavigationLink(isActive: $isShowingConfirmView) { EditKeyForm(editingKey: scannedKey, showCancelButton: false, dismiss: dismiss)
EditKeyForm(editingKey: scannedKey, showCancelButton: false, dismiss: dismiss) .navigationTitle("Add Key")
.navigationTitle("Add Key")
} label: {
// EmptyView because this is only used to trigger programatic navigation
EmptyView()
}
} }
} }
.alert("Unable to get OTP key from QR code", isPresented: $isPresentingScanFailedAlert) { .alert("Unable to get OTP key from QR code", isPresented: $isPresentingScanFailedAlert) {
@ -84,6 +78,8 @@ extension AddQRView {
return "Scanner: Bad Input" return "Scanner: Bad Input"
case .scanner(.badOutput): case .scanner(.badOutput):
return "Scanner: Bad Output" return "Scanner: Bad Output"
case .scanner(.initError(let e)):
return ("Scanner: Initialization: \(e)")
} }
} }
} }

View File

@ -67,10 +67,12 @@ struct AddURLForm: View {
.disabled(!isValid) .disabled(!isValid)
} }
} }
.onChange(of: inputURL, perform: self.updateExtractedKey(inputURL:)) .onChange(of: inputURL) {
self.updateExtractedKey()
}
} }
private func updateExtractedKey(inputURL: String) { private func updateExtractedKey() {
let text = inputURL.trimmingCharacters(in: .whitespacesAndNewlines) let text = inputURL.trimmingCharacters(in: .whitespacesAndNewlines)
if !text.isEmpty, if !text.isEmpty,
let components = URLComponents(string: inputURL), let components = URLComponents(string: inputURL),

View File

@ -23,7 +23,7 @@ struct AppView: View {
} }
var body: some View { var body: some View {
NavigationView { NavigationSplitView {
List { List {
if searchQuery.isEmpty { if searchQuery.isEmpty {
KeysSection(codeHolder: entryHolder) KeysSection(codeHolder: entryHolder)
@ -39,7 +39,7 @@ struct AppView: View {
KeysSection(codeHolder: allEntriesHolder, searchQuery: searchQuery) KeysSection(codeHolder: allEntriesHolder, searchQuery: searchQuery)
} }
} }
.listStyle(.insetGrouped) .listStyle(.sidebar)
.navigationTitle("OTP") .navigationTitle("OTP")
.toolbar { .toolbar {
ToolbarItem(placement: .navigationBarLeading) { ToolbarItem(placement: .navigationBarLeading) {
@ -54,6 +54,8 @@ struct AppView: View {
AddKeyButton(folderID: nil, canAddFolder: true) AddKeyButton(folderID: nil, canAddFolder: true)
} }
} }
} detail: {
ContentUnavailableView("No Folder", systemImage: "folder.fill")
} }
.searchable(text: $searchQuery) .searchable(text: $searchQuery)
.sheet(isPresented: $isPresentingPreferences, content: self.preferencesSheet) .sheet(isPresented: $isPresentingPreferences, content: self.preferencesSheet)

View File

@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import UniformTypeIdentifiers
struct FoldersSection: View { struct FoldersSection: View {
@ObservedObject private var store: KeyStore = .shared @ObservedObject private var store: KeyStore = .shared
@ -15,6 +16,20 @@ struct FoldersSection: View {
Section { Section {
ForEach(store.sortedFolders) { (folder) in ForEach(store.sortedFolders) { (folder) in
FolderRow(store: store, folder: folder) FolderRow(store: store, folder: folder)
.onDrop(of: [.text], isTargeted: nil) { providers in
guard let provider = providers.first,
provider.canLoadObject(ofClass: NSString.self) else {
return false
}
provider.loadObject(ofClass: NSString.self) { object, _ in
guard let str = object as? NSString,
let id = UUID(uuidString: str as String) else {
return
}
store.moveEntryToFolder(entryID: id, folderID: folder.id)
}
return true
}
} }
.onDelete { (indices) in .onDelete { (indices) in
let folderIDs = indices.map { store.sortedFolders[$0].id } let folderIDs = indices.map { store.sortedFolders[$0].id }

View File

@ -79,6 +79,9 @@ struct KeyView: View {
} }
} }
.tint(Color(UIColor.label)) .tint(Color(UIColor.label))
#if targetEnvironment(macCatalyst)
.padding(.vertical, 8)
#endif
} }
private func progress(at date: Date) -> Double { private func progress(at date: Date) -> Double {

View File

@ -35,9 +35,11 @@ struct KeysSection: View {
ForEach(filteredEntries) { (entry) in ForEach(filteredEntries) { (entry) in
KeyView(key: entry.key, currentCode: entry.code) KeyView(key: entry.key, currentCode: entry.code)
// disabled because dropping onto list rows does not work :/ // disabled because dropping onto list rows does not work :/
// .onDrag { .onDrag {
// NSItemProvider(object: entry.id.uuidString as NSString) let provider = NSItemProvider()
// } provider.registerObject(entry.id.uuidString as NSString, visibility: .ownProcess)
return provider
}
.contextMenu { .contextMenu {
self.keyMenu(entry: entry) self.keyMenu(entry: entry)
} }