Skip to content
Draft
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
54 changes: 24 additions & 30 deletions Sources/MobileMinter/MobileMinter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
8B44221029B25A2B00157A65 /* DelegateAnnouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B4421F329B25A2B00157A65 /* DelegateAnnouncer.swift */; };
8B7699C92947A435005EEF2B /* web3.swift in Frameworks */ = {isa = PBXBuildFile; productRef = 8B7699C82947A435005EEF2B /* web3.swift */; };
8BA24F5129254FAF00A72F31 /* BlockLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA24F5029254FAF00A72F31 /* BlockLoadingView.swift */; };
8BB82C6B2A0D224300121CF3 /* MintProjectToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB82C6A2A0D224300121CF3 /* MintProjectToken.swift */; };
8BB82C6D2A0D2E8B00121CF3 /* OpenProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB82C6C2A0D2E8B00121CF3 /* OpenProject.swift */; };
8BDA6A572925E378006261E6 /* Haptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A643D891286EFD8200C7208E /* Haptics.swift */; };
8BDA6A5D2925E389006261E6 /* ConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A643D88B286EEDF600C7208E /* ConfirmationView.swift */; };
8BF9606229B7AF4D008FFAE9 /* PaymentViewControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BF9606129B7AF4D008FFAE9 /* PaymentViewControllerView.swift */; };
Expand All @@ -49,7 +51,7 @@
A643D892286EFD8200C7208E /* Haptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A643D891286EFD8200C7208E /* Haptics.swift */; };
A64ECE8327E0DF8B0064CB4D /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64ECE8227E0DF8B0064CB4D /* Tests_iOS.swift */; };
A64ECE8527E0DF8B0064CB4D /* Tests_iOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64ECE8427E0DF8B0064CB4D /* Tests_iOSLaunchTests.swift */; };
A64ECE9227E0DF8B0064CB4D /* TXLess_MintApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64ECE6A27E0DF890064CB4D /* TXLess_MintApp.swift */; };
A64ECE9227E0DF8B0064CB4D /* MobileMinterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64ECE6A27E0DF890064CB4D /* MobileMinterApp.swift */; };
A64ECE9427E0DF8B0064CB4D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64ECE6B27E0DF890064CB4D /* ContentView.swift */; };
A64ECE9627E0DF8B0064CB4D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A64ECE6C27E0DF8B0064CB4D /* Assets.xcassets */; };
A6562FFA2846B483008EC448 /* QRCode in Frameworks */ = {isa = PBXBuildFile; productRef = A6562FF92846B483008EC448 /* QRCode */; };
Expand Down Expand Up @@ -121,20 +123,19 @@
8B4421F229B25A2B00157A65 /* DiscoveryMethodViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiscoveryMethodViewController.swift; sourceTree = "<group>"; };
8B4421F329B25A2B00157A65 /* DelegateAnnouncer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateAnnouncer.swift; sourceTree = "<group>"; };
8BA24F5029254FAF00A72F31 /* BlockLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockLoadingView.swift; sourceTree = "<group>"; };
8BB82C6A2A0D224300121CF3 /* MintProjectToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MintProjectToken.swift; sourceTree = "<group>"; };
8BB82C6C2A0D2E8B00121CF3 /* OpenProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenProject.swift; sourceTree = "<group>"; };
8BF9606129B7AF4D008FFAE9 /* PaymentViewControllerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentViewControllerView.swift; sourceTree = "<group>"; };
A643D888286EEB5300C7208E /* KeychainSwiftDistrib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainSwiftDistrib.swift; sourceTree = "<group>"; };
A643D88B286EEDF600C7208E /* ConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationView.swift; sourceTree = "<group>"; };
A643D891286EFD8200C7208E /* Haptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptics.swift; sourceTree = "<group>"; };
A64ECE6A27E0DF890064CB4D /* TXLess_MintApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TXLess_MintApp.swift; sourceTree = "<group>"; };
A64ECE6A27E0DF890064CB4D /* MobileMinterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileMinterApp.swift; sourceTree = "<group>"; };
A64ECE6B27E0DF890064CB4D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
A64ECE6C27E0DF8B0064CB4D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A64ECE7127E0DF8B0064CB4D /* TXLess Mint.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TXLess Mint.app"; sourceTree = BUILT_PRODUCTS_DIR; };
A64ECE7927E0DF8B0064CB4D /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = "<group>"; };
A64ECE7E27E0DF8B0064CB4D /* Tests iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
A64ECE8227E0DF8B0064CB4D /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = "<group>"; };
A64ECE8427E0DF8B0064CB4D /* Tests_iOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOSLaunchTests.swift; sourceTree = "<group>"; };
A64ECE8E27E0DF8B0064CB4D /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = "<group>"; };
A64ECE9027E0DF8B0064CB4D /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = "<group>"; };
A6AFC80D28401C3D005BFC1A /* TXLess-Mint--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "TXLess-Mint--iOS--Info.plist"; sourceTree = "<group>"; };
A6AFC80E284022A8005BFC1A /* APIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIClient.swift; sourceTree = "<group>"; };
A6B47DCD27E3ED3D009919E0 /* MintingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MintingView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -206,6 +207,15 @@
path = Stripe;
sourceTree = "<group>";
};
8BB82C672A0D1C8100121CF3 /* Shortcuts */ = {
isa = PBXGroup;
children = (
8BB82C6A2A0D224300121CF3 /* MintProjectToken.swift */,
8BB82C6C2A0D2E8B00121CF3 /* OpenProject.swift */,
);
path = Shortcuts;
sourceTree = "<group>";
};
8BF9606029B7AF3B008FFAE9 /* Payments */ = {
isa = PBXGroup;
children = (
Expand All @@ -219,9 +229,7 @@
children = (
A6AFC80D28401C3D005BFC1A /* TXLess-Mint--iOS--Info.plist */,
A64ECE6927E0DF890064CB4D /* Shared */,
A64ECE7827E0DF8B0064CB4D /* macOS */,
A64ECE8127E0DF8B0064CB4D /* Tests iOS */,
A64ECE8D27E0DF8B0064CB4D /* Tests macOS */,
A64ECE7227E0DF8B0064CB4D /* Products */,
A6B47DC527E3EA8F009919E0 /* Frameworks */,
);
Expand All @@ -230,9 +238,10 @@
A64ECE6927E0DF890064CB4D /* Shared */ = {
isa = PBXGroup;
children = (
8BB82C672A0D1C8100121CF3 /* Shortcuts */,
8BF9606029B7AF3B008FFAE9 /* Payments */,
8B4421CB29B2543600157A65 /* Stripe */,
A64ECE6A27E0DF890064CB4D /* TXLess_MintApp.swift */,
A64ECE6A27E0DF890064CB4D /* MobileMinterApp.swift */,
A6B47DD327E3F3AF009919E0 /* ProjectsView.swift */,
A64ECE6B27E0DF890064CB4D /* ContentView.swift */,
A6B47DD027E3EE42009919E0 /* LoginView.swift */,
Expand All @@ -257,14 +266,6 @@
name = Products;
sourceTree = "<group>";
};
A64ECE7827E0DF8B0064CB4D /* macOS */ = {
isa = PBXGroup;
children = (
A64ECE7927E0DF8B0064CB4D /* macOS.entitlements */,
);
path = macOS;
sourceTree = "<group>";
};
A64ECE8127E0DF8B0064CB4D /* Tests iOS */ = {
isa = PBXGroup;
children = (
Expand All @@ -274,15 +275,6 @@
path = "Tests iOS";
sourceTree = "<group>";
};
A64ECE8D27E0DF8B0064CB4D /* Tests macOS */ = {
isa = PBXGroup;
children = (
A64ECE8E27E0DF8B0064CB4D /* Tests_macOS.swift */,
A64ECE9027E0DF8B0064CB4D /* Tests_macOSLaunchTests.swift */,
);
path = "Tests macOS";
sourceTree = "<group>";
};
A6B47DC527E3EA8F009919E0 /* Frameworks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -421,14 +413,15 @@
8B44220429B25A2B00157A65 /* Value1MultilineCell.swift in Sources */,
8B44220629B25A2B00157A65 /* EventDisplayingViewController.swift in Sources */,
8B44220329B25A2B00157A65 /* StartSetReaderDisplayViewController.swift in Sources */,
8BB82C6B2A0D224300121CF3 /* MintProjectToken.swift in Sources */,
8B4421CD29B2553300157A65 /* ReaderViewController.swift in Sources */,
8B44220E29B25A2B00157A65 /* ReaderRegistrationViewController.swift in Sources */,
8B44220529B25A2B00157A65 /* StartRefundViewController.swift in Sources */,
8B4421F929B25A2B00157A65 /* LogEventViewController.swift in Sources */,
8B44220129B25A2B00157A65 /* StartPaymentViewController.swift in Sources */,
8B4421F429B25A2B00157A65 /* RefundViewController.swift in Sources */,
A6B47DD127E3EE42009919E0 /* LoginView.swift in Sources */,
A64ECE9227E0DF8B0064CB4D /* TXLess_MintApp.swift in Sources */,
A64ECE9227E0DF8B0064CB4D /* MobileMinterApp.swift in Sources */,
A6B47DE127E53C8F009919E0 /* MintingEmbedView.swift in Sources */,
8B44220C29B25A2B00157A65 /* PaymentViewController.swift in Sources */,
8B4421FF29B25A2B00157A65 /* UIView+Layout.swift in Sources */,
Expand All @@ -453,6 +446,7 @@
8B44220B29B25A2B00157A65 /* CustomViews.swift in Sources */,
8BA24F5129254FAF00A72F31 /* BlockLoadingView.swift in Sources */,
8B44220229B25A2B00157A65 /* TableViewController+StripeTerminal.swift in Sources */,
8BB82C6D2A0D2E8B00121CF3 /* OpenProject.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -601,7 +595,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 61;
CURRENT_PROJECT_VERSION = 62;
DEVELOPMENT_TEAM = VR4Q2LK67K;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand All @@ -619,7 +613,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -641,7 +635,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 61;
CURRENT_PROJECT_VERSION = 62;
DEVELOPMENT_TEAM = VR4Q2LK67K;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand All @@ -659,7 +653,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
2 changes: 1 addition & 1 deletion Sources/MobileMinter/Shared/ConfirmationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct ConfirmationView: View {
}

var body: some View {
NavigationView {
NavigationStack {
Group {
if isConfirmed && (paidWithCard) {
VStack {
Expand Down
51 changes: 27 additions & 24 deletions Sources/MobileMinter/Shared/ContentView.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//
// ContentView.swift
// Shared
//
// Created by Shantanu Bala on 3/15/22.
//

import SwiftUI

enum ScreenID {
Expand All @@ -14,32 +7,42 @@ enum ScreenID {
}

struct ContentView: View {
// the current authentication token for the user
@State var currentToken: String? = nil

// the current PBAB project being minted
@State var currentProject: Project? = nil

// the current screen displayed to the user
@State var currentScreen: ScreenID = .login

var body: some View {
NavigationView {
if currentScreen == .login {
LoginView(currentToken: $currentToken, currentScreen: $currentScreen).navigationTitle(
"Login")
} else if currentScreen == .projects {
NavigationStack {
currentView.navigationTitle(currentViewTitle)
}
}

private var currentView: some View {
switch currentScreen {
case .login:
return AnyView(LoginView(currentToken: $currentToken, currentScreen: $currentScreen))
case .projects:
return AnyView(
ProjectsView(
currentToken: $currentToken, currentProject: $currentProject,
currentScreen: $currentScreen
).navigationTitle("Projects")
} else if currentScreen == .minting {
currentScreen: $currentScreen))
case .minting:
return AnyView(
MintingView(
currentToken: $currentToken, currentScreen: $currentScreen,
currentProject: $currentProject
).navigationTitle(currentProject?.title ?? "Minting")
}
}.navigationViewStyle(StackNavigationViewStyle())
currentProject: $currentProject))
}
}

private var currentViewTitle: String {
switch currentScreen {
case .login:
return "Login"
case .projects:
return "Projects"
case .minting:
return currentProject?.title ?? "Minting"
}
}
}

Expand Down
96 changes: 46 additions & 50 deletions Sources/MobileMinter/Shared/LoginView.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//
// LoginView.swift
// TXLess Mint
//
// Created by Shantanu Bala on 3/17/22.
//

import BetterSafariView
import LocalAuthentication
import MintingKit
Expand All @@ -15,66 +8,69 @@ struct LoginView: View {
@Binding var currentToken: String?
@Binding var currentScreen: ScreenID
let keychain = KeychainSwift()

var body: some View {
Form {
Button("Sign in") {
startingWebAuthenticationSession = true
}
}.onAppear {
DispatchQueue.main.async {
guard let lastOpened = UserDefaults.standard.object(forKey: "LastOpened") as? Date else {
return
}
guard
let elapsed = Calendar.current.dateComponents([.day], from: lastOpened, to: Date()).day
else {
return
}
if elapsed >= 6 {
return
}
if let t = keychain.get("authToken") {
self.currentToken = t
let context = LAContext()
var error: NSError?

// check whether biometric authentication is possible
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
// it's possible, so go ahead and use it
let reason = "We need to unlock your data."

context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason
) { success, authenticationError in
// authentication has now completed
if success {
currentScreen = .projects
} else {
// there was a problem
}
}
} else {
// no biometrics
}
}
}
}
.onAppear(perform: retrieveSavedAuthenticationToken)
.webAuthenticationSession(isPresented: $startingWebAuthenticationSession) {
WebAuthenticationSession(
url: URL(string: "https://minting-api.artblocks.io/app/?appauth=true")!,
callbackURLScheme: "txlessauth"
) { callbackURL, error in
currentToken = callbackURL?.host
if let t = currentToken {
handleWebAuthentication(callbackURL: callbackURL)
}
}
}

private func retrieveSavedAuthenticationToken() {
guard let lastOpened = UserDefaults.standard.object(forKey: "LastOpened") as? Date else {
return
}

let elapsed = daysElapsed(from: lastOpened, to: Date())

if elapsed < 6, let savedToken = keychain.get("authToken") {
currentToken = savedToken
authenticateUser()
}
}

private func daysElapsed(from startDate: Date, to endDate: Date) -> Int {
Calendar.current.dateComponents([.day], from: startDate, to: endDate).day ?? 0
}

private func authenticateUser() {
let context = LAContext()
var error: NSError?

if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "We need to unlock your data."
) { success, authenticationError in
if success {
DispatchQueue.main.async {
keychain.set(t, forKey: "authToken")
UserDefaults.standard.set(Date(), forKey: "LastOpened")
currentScreen = .projects
}
currentScreen = .projects
}
}
}
}

private func handleWebAuthentication(callbackURL: URL?) {
currentToken = callbackURL?.host
if let t = currentToken {
DispatchQueue.main.async {
keychain.set(t, forKey: "authToken")
UserDefaults.standard.set(Date(), forKey: "LastOpened")
currentScreen = .projects
}
}
}
}

struct LoginView_Previews: PreviewProvider {
Expand Down
Loading