Compare commits

..

No commits in common. "94c39fb5c54c5acf43549096dc8e44d01edc1173" and "02e229042a5119a8772dfa3334acb556dd5e67df" have entirely different histories.

6 changed files with 32 additions and 73 deletions

View File

@ -89,6 +89,7 @@
D60265472744012A00C77599 /* AddKeyButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddKeyButton.swift; sourceTree = "<group>"; };
D60265492744014500C77599 /* AllKeysView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllKeysView.swift; sourceTree = "<group>"; };
D602654B274401F800C77599 /* OTP.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OTP.entitlements; sourceTree = "<group>"; };
D604B0F82744036F00960D31 /* CodeScanner */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CodeScanner; path = ../CodeScanner; sourceTree = "<group>"; };
D60E9D6D26D1998B009A4537 /* OTPGeneratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OTPGeneratorTests.swift; sourceTree = "<group>"; };
D60E9D7126D1A863009A4537 /* KeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyView.swift; sourceTree = "<group>"; };
D60E9D7326D1ABF9009A4537 /* CircularProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressView.swift; sourceTree = "<group>"; };
@ -158,6 +159,7 @@
D604B0F72744036F00960D31 /* Packages */ = {
isa = PBXGroup;
children = (
D604B0F82744036F00960D31 /* CodeScanner */,
);
name = Packages;
sourceTree = "<group>";

View File

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

View File

@ -24,13 +24,11 @@ struct AddKeyButton: View {
var body: some View {
Menu {
Section {
#if !targetEnvironment(macCatalyst)
Button {
isPresentingScanner = true
} label: {
Label("Scan QR", systemImage: "qrcode.viewfinder")
}
#endif
Button {
isPresentingAddURLSheet = true
} label: {
@ -57,14 +55,11 @@ struct AddKeyButton: View {
}
.menuStyle(.borderlessButton)
.accessibilityLabel("Add Key")
#if !targetEnvironment(macCatalyst)
.sheet(isPresented: $isPresentingScanner, content: self.scannerSheet)
#endif
.sheet(isPresented: $isPresentingManualAddFormSheet, content: self.manualAddFormSheet)
.sheet(isPresented: $isPresentingAddURLSheet, content: self.addURLSheet)
}
#if !targetEnvironment(macCatalyst)
private func scannerSheet() -> some View {
AddQRView() { (action) in
self.isPresentingScanner = false
@ -76,7 +71,6 @@ struct AddKeyButton: View {
}
}
}
#endif
private func addURLSheet() -> some View {
NavigationView {

View File

@ -5,7 +5,6 @@
// Created by Shadowfacts on 8/22/21.
//
#if !targetEnvironment(macCatalyst)
import SwiftUI
import CodeScanner
import OTPKit
@ -94,4 +93,3 @@ struct AddQRView_Previews: PreviewProvider {
AddQRView() { (_) in }
}
}
#endif

View File

@ -12,7 +12,6 @@ struct KeyView: View {
let key: TOTPKey
let currentCode: TOTPCode
@State private var copying = false
@State private var copiedLabelWidth: CGFloat = 0
private var formattedCode: String {
let code = currentCode.code
@ -28,48 +27,36 @@ struct KeyView: View {
var body: some View {
Button(action: self.copy) {
HStack {
ZStack {
HStack {
VStack(alignment: .leading) {
Text(key.issuer)
.font(.title3)
if let label = key.label, !label.isEmpty {
Text(label)
.font(.footnote)
}
}
Spacer()
}
VStack(alignment: .leading) {
Text(key.issuer)
.font(.title3)
HStack {
Spacer()
Text(formattedCode)
.font(.system(.title2, design: .monospaced))
.opacity(copying ? 0 : 1)
Text("Copied!")
.font(.title2)
.opacity(copying ? 1 : 0)
// this nonsense shouldn't be necessary, but the transition only works the first time a code is copied, for some indiscernible reason
.background(GeometryReader { proxy in
Color.clear
.preference(key: CopiedLabelWidth.self, value: proxy.size.width)
.onPreferenceChange(CopiedLabelWidth.self) { newValue in
copiedLabelWidth = newValue
}
})
if let label = key.label, !label.isEmpty {
Text(label)
.font(.footnote)
}
.offset(x: copying ? 0 : copiedLabelWidth)
.clipped()
}
.padding(.trailing, 8)
Spacer()
if copying {
Text("Copied!")
.font(.title2)
.transition(.move(edge: .trailing).combined(with: .opacity))
} else {
Text(formattedCode)
.font(.system(.title2, design: .monospaced))
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)).combined(with: .opacity))
}
// Text("\(currentCode.validUntil, style: .relative)")
// .font(.body.monospacedDigit())
// I don't think this TimelineView should be necessary since the CodeHolder timer fires every .5 seconds
TimelineView(.animation) { (ctx) in
ZStack {
CircularProgressView(progress: progress(at: ctx.date), colorChangeThreshold: 5.0 / Double(key.period))
CircularProgressView(progress: progress(at: Date()), colorChangeThreshold: 5.0 / Double(key.period))
Text(Int(round(currentCode.validUntil.timeIntervalSinceNow)).description)
.font(.caption.monospacedDigit())
@ -92,22 +79,12 @@ struct KeyView: View {
withAnimation(.easeInOut(duration: 0.5)) {
copying = true
}
// .easeInOut(duration: 0.5).delay(0.65) does not work any more
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(650)) {
withAnimation(.easeInOut(duration: 0.5)) {
copying = false
}
withAnimation(.easeInOut(duration: 0.5).delay(0.65)) {
copying = false
}
}
}
private struct CopiedLabelWidth: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct KeyView_Previews: PreviewProvider {
static var key: TOTPKey {
TOTPKey(urlComponents: URLComponents(string: "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example")!)!

View File

@ -46,22 +46,14 @@ struct PreferencesView: View {
}
.fileExporter(isPresented: $isPresentingExport, document: BackupDocument(data: store.data), contentType: .propertyList, defaultFilename: "OTPBackup") { (_) in
}
.fileImporter(isPresented: $isPresentingImport, allowedContentTypes: [.propertyList]) { (result) in
.fileImporter(isPresented: $isPresentingImport, allowedContentTypes: [.propertyList], allowsMultipleSelection: false) { (result) in
switch result {
case let .failure(error):
self.importFailedError = error
self.isPresentingImportFailedAlert = true
case let .success(url):
guard url.startAccessingSecurityScopedResource() else {
self.importFailedError = ImportError.accessingSecurityScopedResource
self.isPresentingImportFailedAlert = true
return
}
defer {
url.stopAccessingSecurityScopedResource()
}
case let .success(urls):
do {
let backup = try BackupDocument(url: url)
let backup = try BackupDocument(url: urls.first!)
store.updateFromStore(backup.data, replaceExisting: clearBeforeImport)
dismiss()
} catch {
@ -79,10 +71,6 @@ struct PreferencesView: View {
}
}
enum ImportError: LocalizedError {
case accessingSecurityScopedResource
}
}
struct PreferencesView_Previews: PreviewProvider {