[98] [Scaffold] Full Wallet History (#376)

- concept of activities instead of transactions
- the drawer is now fully universal and data driven, rendering some View, going to some detail View
- unit tests work again
- refactor of Activity -> WalletEvent
- WalletEvent view builders simplified
- transactions & wallet events timestamps instead of Date type
- review comments resolved
- missing 'Transactions' terms updated to 'WalletEvents'
This commit is contained in:
Lukas Korba 2022-06-23 11:05:34 +02:00 committed by GitHub
parent 675a067437
commit e321114214
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 465 additions and 320 deletions

View File

@ -112,7 +112,7 @@
9E39115E284E3E350073DD9A /* secantUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A2526B364180058B01E /* secantUITests.swift */; };
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */; };
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; };
9E5BF63F2819542C00BA3F17 /* TransactionHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* TransactionHistoryTests.swift */; };
9E5BF63F2819542C00BA3F17 /* WalletEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */; };
9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */; };
9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF643281FEC9900BA3F17 /* SendTests.swift */; };
9E5BF6462821028C00BA3F17 /* WrappedUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */; };
@ -146,6 +146,8 @@
9EAB466F285A0468002904A0 /* _URLRouting in Frameworks */ = {isa = PBXBuildFile; productRef = 9EAB466E285A0468002904A0 /* _URLRouting */; };
9EAB4671285A1C77002904A0 /* DeeplinkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAB4670285A1C77002904A0 /* DeeplinkHandler.swift */; };
9EAB4676285B5C7C002904A0 /* DeeplinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAB4675285B5C7C002904A0 /* DeeplinkTests.swift */; };
9EAB46782860A1D2002904A0 /* WalletEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAB46772860A1D2002904A0 /* WalletEvent.swift */; };
9EAB467A2861EA6A002904A0 /* TransactionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAB46792861EA6A002904A0 /* TransactionRowView.swift */; };
9EAFEB822805793200199FC9 /* AppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB812805793200199FC9 /* AppTests.swift */; };
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; };
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; };
@ -166,9 +168,9 @@
9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8139B27F47AED0075AF48 /* InitializationState.swift */; };
F9322DC0273B555C00C105B5 /* NavigationLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9322DBF273B555C00C105B5 /* NavigationLinks.swift */; };
F93673D62742CB840099C6AF /* Previews.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93673D52742CB840099C6AF /* Previews.swift */; };
F96B41E7273B501F0021B49A /* TransactionHistoryFlowStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* TransactionHistoryFlowStore.swift */; };
F96B41E7273B501F0021B49A /* WalletEventsFlowStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E3273B501F0021B49A /* WalletEventsFlowStore.swift */; };
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E5273B501F0021B49A /* TransactionDetailView.swift */; };
F96B41E9273B501F0021B49A /* TransactionHistoryFlowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* TransactionHistoryFlowView.swift */; };
F96B41E9273B501F0021B49A /* WalletEventsFlowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41E6273B501F0021B49A /* WalletEventsFlowView.swift */; };
F96B41EB273B50520021B49A /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; };
F9971A4D27680DC400A2DB75 /* AppStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A4A27680DC400A2DB75 /* AppStore.swift */; };
F9971A4E27680DC400A2DB75 /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A4C27680DC400A2DB75 /* AppView.swift */; };
@ -319,7 +321,7 @@
9E4DC6DF27C409A100E657F4 /* NeumorphicDesignModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphicDesignModifier.swift; sourceTree = "<group>"; };
9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = "<group>"; };
9E5BF63B2818305D00BA3F17 /* TransactionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionState.swift; sourceTree = "<group>"; };
9E5BF63E2819542C00BA3F17 /* TransactionHistoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionHistoryTests.swift; sourceTree = "<group>"; };
9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEventsTests.swift; sourceTree = "<group>"; };
9E5BF640281FD7B600BA3F17 /* TransactionFailedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionFailedView.swift; sourceTree = "<group>"; };
9E5BF643281FEC9900BA3F17 /* SendTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendTests.swift; sourceTree = "<group>"; };
9E5BF6452821028C00BA3F17 /* WrappedUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedUserDefaults.swift; sourceTree = "<group>"; };
@ -350,6 +352,8 @@
9EAB46692859F42E002904A0 /* WrappedDeeplinkHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedDeeplinkHandler.swift; sourceTree = "<group>"; };
9EAB4670285A1C77002904A0 /* DeeplinkHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinkHandler.swift; sourceTree = "<group>"; };
9EAB4675285B5C7C002904A0 /* DeeplinkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeeplinkTests.swift; sourceTree = "<group>"; };
9EAB46772860A1D2002904A0 /* WalletEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEvent.swift; sourceTree = "<group>"; };
9EAB46792861EA6A002904A0 /* TransactionRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionRowView.swift; sourceTree = "<group>"; };
9EAFEB812805793200199FC9 /* AppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTests.swift; sourceTree = "<group>"; };
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
@ -369,9 +373,9 @@
F93673D52742CB840099C6AF /* Previews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Previews.swift; sourceTree = "<group>"; };
F93874ED273C4DE200F0E875 /* HomeStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeStore.swift; sourceTree = "<group>"; };
F93874EF273C4DE200F0E875 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
F96B41E3273B501F0021B49A /* TransactionHistoryFlowStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryFlowStore.swift; sourceTree = "<group>"; };
F96B41E3273B501F0021B49A /* WalletEventsFlowStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletEventsFlowStore.swift; sourceTree = "<group>"; };
F96B41E5273B501F0021B49A /* TransactionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetailView.swift; sourceTree = "<group>"; };
F96B41E6273B501F0021B49A /* TransactionHistoryFlowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionHistoryFlowView.swift; sourceTree = "<group>"; };
F96B41E6273B501F0021B49A /* WalletEventsFlowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletEventsFlowView.swift; sourceTree = "<group>"; };
F96B41EA273B50520021B49A /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
F9971A4A27680DC400A2DB75 /* AppStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStore.swift; sourceTree = "<group>"; };
F9971A4C27680DC400A2DB75 /* AppView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = "<group>"; };
@ -516,7 +520,7 @@
9E391122283E4C970073DD9A /* ImportWalletTests */,
9E01F8262833CD84000EFC57 /* ScanTests */,
9E5BF642281FEC8700BA3F17 /* SendTests */,
9E5BF63D281953F900BA3F17 /* TransactionHistoryTests */,
9E5BF63D281953F900BA3F17 /* WalletEventsTests */,
9EAFEB802805791400199FC9 /* AppTests */,
9EF8135927ECC25E0075AF48 /* UtilTests */,
0DFE93E4272CB6D0000FCCA5 /* RecoveryPhraseValidationTests */,
@ -577,6 +581,7 @@
9E7FE0D6282D286500C374E8 /* RecoveryPhrase.swift */,
9E7FE0DC282D298900C374E8 /* ValidationWord.swift */,
9E7FE0E5282E7B1100C374E8 /* StoredWallet.swift */,
9EAB46772860A1D2002904A0 /* WalletEvent.swift */,
);
path = Models;
sourceTree = "<group>";
@ -673,7 +678,7 @@
F9971A5B27680DF600A2DB75 /* Scan */,
F9C165B62740403600592F76 /* SendFlow */,
F9971A6127680DFE00A2DB75 /* Settings */,
F96B41E2273B501F0021B49A /* TransactionHistoryFlow */,
F96B41E2273B501F0021B49A /* WalletEventsFlow */,
9E7FE0E4282E753700C374E8 /* RecoveryPhraseDisplay */,
9E7FE0E3282E751A00C374E8 /* RecoveryPhraseValidationFlow */,
6654C73C2715A3FA00901167 /* OnboardingFlow */,
@ -816,12 +821,12 @@
path = SnapshotTests;
sourceTree = "<group>";
};
9E5BF63D281953F900BA3F17 /* TransactionHistoryTests */ = {
9E5BF63D281953F900BA3F17 /* WalletEventsTests */ = {
isa = PBXGroup;
children = (
9E5BF63E2819542C00BA3F17 /* TransactionHistoryTests.swift */,
9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */,
);
path = TransactionHistoryTests;
path = WalletEventsTests;
sourceTree = "<group>";
};
9E5BF642281FEC8700BA3F17 /* SendTests */ = {
@ -1087,20 +1092,21 @@
path = Home;
sourceTree = "<group>";
};
F96B41E2273B501F0021B49A /* TransactionHistoryFlow */ = {
F96B41E2273B501F0021B49A /* WalletEventsFlow */ = {
isa = PBXGroup;
children = (
F96B41E3273B501F0021B49A /* TransactionHistoryFlowStore.swift */,
F96B41E6273B501F0021B49A /* TransactionHistoryFlowView.swift */,
F96B41E3273B501F0021B49A /* WalletEventsFlowStore.swift */,
F96B41E6273B501F0021B49A /* WalletEventsFlowView.swift */,
F96B41E4273B501F0021B49A /* Views */,
);
path = TransactionHistoryFlow;
path = WalletEventsFlow;
sourceTree = "<group>";
};
F96B41E4273B501F0021B49A /* Views */ = {
isa = PBXGroup;
children = (
F96B41E5273B501F0021B49A /* TransactionDetailView.swift */,
9EAB46792861EA6A002904A0 /* TransactionRowView.swift */,
);
path = Views;
sourceTree = "<group>";
@ -1396,7 +1402,7 @@
0D35CC46277A36E00074316A /* ScrollableWhenScaled.swift in Sources */,
9E39114A2848EEB90073DD9A /* UserPreferencesStorage.swift in Sources */,
9EAB466A2859F42E002904A0 /* WrappedDeeplinkHandler.swift in Sources */,
F96B41E9273B501F0021B49A /* TransactionHistoryFlowView.swift in Sources */,
F96B41E9273B501F0021B49A /* WalletEventsFlowView.swift in Sources */,
9E01F8202833861A000EFC57 /* WrappedCaptureDevice.swift in Sources */,
2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */,
9E7FE0DB282D28F100C374E8 /* WrappedPasteboard.swift in Sources */,
@ -1414,6 +1420,7 @@
9E7FE0CF282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift in Sources */,
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */,
0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */,
9EAB467A2861EA6A002904A0 /* TransactionRowView.swift in Sources */,
0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift in Sources */,
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */,
0D535FE2271F9476009A9E3E /* EnumeratedChip.swift in Sources */,
@ -1465,7 +1472,7 @@
9E2AC10127D8EF0B0042AA47 /* WrappedMnemonic.swift in Sources */,
9E7FE0D7282D286500C374E8 /* RecoveryPhrase.swift in Sources */,
660558F7270C862F009D6954 /* Fonts+Generated.swift in Sources */,
F96B41E7273B501F0021B49A /* TransactionHistoryFlowStore.swift in Sources */,
F96B41E7273B501F0021B49A /* WalletEventsFlowStore.swift in Sources */,
9E7FE0E6282E7B1100C374E8 /* StoredWallet.swift in Sources */,
9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */,
F9971A5A27680DDE00A2DB75 /* RequestView.swift in Sources */,
@ -1505,6 +1512,7 @@
F9971A5427680DD000A2DB75 /* ProfileView.swift in Sources */,
F9971A6027680DF600A2DB75 /* ScanStore.swift in Sources */,
9EF8139127F191BF0075AF48 /* WrappedWalletStorage.swift in Sources */,
9EAB46782860A1D2002904A0 /* WalletEvent.swift in Sources */,
0DFE93E1272C9ECB000FCCA5 /* RecoveryPhraseBackupView.swift in Sources */,
9E69A24D27FB002800A55317 /* WelcomeStore.swift in Sources */,
F9C165CB2741AB5D00592F76 /* SendFlowView.swift in Sources */,
@ -1542,7 +1550,7 @@
9E391132284644580073DD9A /* AppInitializationTests.swift in Sources */,
9E9ECC9928589E150099D5A2 /* RecoveryPhraseDisplaySnapshotTests.swift in Sources */,
9E391124283E4CAC0073DD9A /* ImportWalletTests.swift in Sources */,
9E5BF63F2819542C00BA3F17 /* TransactionHistoryTests.swift in Sources */,
9E5BF63F2819542C00BA3F17 /* WalletEventsTests.swift in Sources */,
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */,
0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */,
9EAB4676285B5C7C002904A0 /* DeeplinkTests.swift in Sources */,

View File

@ -28,7 +28,7 @@ struct HomeState: Equatable {
var scanState: ScanState
var synchronizerStatus: String
var totalBalance: Zatoshi
var transactionHistoryState: TransactionHistoryFlowState
var walletEventsState: WalletEventsFlowState
var verifiedBalance: Zatoshi
}
@ -43,12 +43,12 @@ enum HomeAction: Equatable {
case send(SendFlowAction)
case scan(ScanAction)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case transactionHistory(TransactionHistoryFlowAction)
case walletEvents(WalletEventsFlowAction)
case updateBalance(Balance)
case updateDrawer(DrawerOverlay)
case updateRoute(HomeState.Route?)
case updateSynchronizerStatus
case updateTransactions([TransactionState])
case updateWalletEvents([WalletEvent])
}
// MARK: Environment
@ -106,7 +106,7 @@ extension HomeReducer {
return .merge(
environment.SDKSynchronizer.getAllClearedTransactions()
.receive(on: environment.scheduler)
.map(HomeAction.updateTransactions)
.map(HomeAction.updateWalletEvents)
.eraseToEffect(),
environment.SDKSynchronizer.getShieldedBalance()
@ -128,10 +128,10 @@ extension HomeReducer {
case .updateDrawer(let drawerOverlay):
state.drawerOverlay = drawerOverlay
state.transactionHistoryState.isScrollable = drawerOverlay == .full ? true : false
state.walletEventsState.isScrollable = drawerOverlay == .full ? true : false
return .none
case .updateTransactions(let transactions):
case .updateWalletEvents(let walletEvents):
return .none
case .updateSynchronizerStatus:
@ -148,13 +148,13 @@ extension HomeReducer {
case .request(let action):
return .none
case .transactionHistory(.updateRoute(.all)):
case .walletEvents(.updateRoute(.all)):
return state.drawerOverlay != .full ? Effect(value: .updateDrawer(.full)) : .none
case .transactionHistory(.updateRoute(.latest)):
case .walletEvents(.updateRoute(.latest)):
return state.drawerOverlay != .partial ? Effect(value: .updateDrawer(.partial)) : .none
case .transactionHistory(let historyAction):
case .walletEvents(let historyAction):
return .none
case .send(.updateRoute(.done)):
@ -175,11 +175,11 @@ extension HomeReducer {
}
}
private static let historyReducer: HomeReducer = TransactionHistoryFlowReducer.default.pullback(
state: \HomeState.transactionHistoryState,
action: /HomeAction.transactionHistory,
private static let historyReducer: HomeReducer = WalletEventsFlowReducer.default.pullback(
state: \HomeState.walletEventsState,
action: /HomeAction.walletEvents,
environment: { environment in
TransactionHistoryFlowEnvironment(
WalletEventsFlowEnvironment(
scheduler: environment.scheduler,
SDKSynchronizer: environment.SDKSynchronizer
)
@ -228,10 +228,10 @@ extension HomeReducer {
// MARK: - Store
extension HomeStore {
func historyStore() -> TransactionHistoryFlowStore {
func historyStore() -> WalletEventsFlowStore {
self.scope(
state: \.transactionHistoryState,
action: HomeAction.transactionHistory
state: \.walletEventsState,
action: HomeAction.walletEvents
)
}
@ -296,7 +296,7 @@ extension HomeState {
scanState: .placeholder,
synchronizerStatus: "",
totalBalance: Zatoshi.zero,
transactionHistoryState: .emptyPlaceHolder,
walletEventsState: .emptyPlaceHolder,
verifiedBalance: Zatoshi.zero
)
}

View File

@ -30,7 +30,7 @@ struct HomeView: View {
if proxy.size.height > 0 {
Drawer(overlay: viewStore.bindingForDrawer(), maxHeight: proxy.size.height) {
VStack {
TransactionHistoryFlowView(store: store.historyStore())
WalletEventsFlowView(store: store.historyStore())
.padding(.top, 10)
Spacer()

View File

@ -16,7 +16,7 @@ struct SandboxState: Equatable {
case scan
case request
}
var transactionHistoryState: TransactionHistoryFlowState
var walletEventsState: WalletEventsFlowState
var profileState: ProfileState
var route: Route?
}
@ -25,7 +25,7 @@ struct SandboxState: Equatable {
enum SandboxAction: Equatable {
case updateRoute(SandboxState.Route?)
case transactionHistory(TransactionHistoryFlowAction)
case walletEvents(WalletEventsFlowAction)
case profile(ProfileAction)
case reset
}
@ -42,18 +42,18 @@ extension SandboxReducer {
case let .updateRoute(route):
state.route = route
return .none
case let .transactionHistory(transactionHistoryAction):
return TransactionHistoryFlowReducer
case let .walletEvents(walletEventsAction):
return WalletEventsFlowReducer
.default
.run(
&state.transactionHistoryState,
transactionHistoryAction,
TransactionHistoryFlowEnvironment(
&state.walletEventsState,
walletEventsAction,
WalletEventsFlowEnvironment(
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
SDKSynchronizer: LiveWrappedSDKSynchronizer()
)
)
.map(SandboxAction.transactionHistory)
.map(SandboxAction.walletEvents)
case let .profile(profileAction):
return ProfileReducer
.default
@ -72,10 +72,10 @@ extension SandboxReducer {
// MARK: - Store
extension SandboxStore {
func historyStore() -> TransactionHistoryFlowStore {
func historyStore() -> WalletEventsFlowStore {
self.scope(
state: \.transactionHistoryState,
action: SandboxAction.transactionHistory
state: \.walletEventsState,
action: SandboxAction.walletEvents
)
}
@ -92,15 +92,15 @@ extension SandboxStore {
extension SandboxViewStore {
func toggleSelectedTransaction() {
let isAlreadySelected = (self.selectedTranactionID != nil)
let transcation = self.transactionHistoryState.transactions[5]
let newRoute = isAlreadySelected ? nil : TransactionHistoryFlowState.Route.showTransaction(transcation)
send(.transactionHistory(.updateRoute(newRoute)))
let walletEvent = self.walletEventsState.walletEvents[5]
let newRoute = isAlreadySelected ? nil : WalletEventsFlowState.Route.showWalletEvent(walletEvent)
send(.walletEvents(.updateRoute(newRoute)))
}
var selectedTranactionID: String? {
self.transactionHistoryState
self.walletEventsState
.route
.flatMap(/TransactionHistoryFlowState.Route.showTransaction)
.flatMap(/WalletEventsFlowState.Route.showWalletEvent)
.map(\.id)
}
@ -119,7 +119,7 @@ extension SandboxViewStore {
extension SandboxState {
static var placeholder: Self {
.init(
transactionHistoryState: .placeHolder,
walletEventsState: .placeHolder,
profileState: .placeholder,
route: nil
)
@ -130,7 +130,7 @@ extension SandboxStore {
static var placeholder: SandboxStore {
SandboxStore(
initialState: SandboxState(
transactionHistoryState: .placeHolder,
walletEventsState: .placeHolder,
profileState: .placeholder,
route: nil
),

View File

@ -22,7 +22,7 @@ struct SandboxView: View {
@ViewBuilder func view(for route: SandboxState.Route) -> some View {
switch route {
case .history:
TransactionHistoryFlowView(store: store.historyStore())
WalletEventsFlowView(store: store.historyStore())
case .send:
SendFlowView(
store: .init(
@ -94,7 +94,7 @@ struct SandboxView: View {
isPresented: viewStore.bindingForRoute(.history),
content: {
NavigationView {
TransactionHistoryFlowView(store: store.historyStore())
WalletEventsFlowView(store: store.historyStore())
.toolbar {
ToolbarItem {
Button("Done") { viewStore.send(.updateRoute(nil)) }

View File

@ -1,161 +0,0 @@
import ComposableArchitecture
import SwiftUI
typealias TransactionHistoryFlowReducer = Reducer<TransactionHistoryFlowState, TransactionHistoryFlowAction, TransactionHistoryFlowEnvironment>
typealias TransactionHistoryFlowStore = Store<TransactionHistoryFlowState, TransactionHistoryFlowAction>
typealias TransactionHistoryFlowViewStore = ViewStore<TransactionHistoryFlowState, TransactionHistoryFlowAction>
// MARK: - State
struct TransactionHistoryFlowState: Equatable {
enum Route: Equatable {
case latest
case all
case showTransaction(TransactionState)
}
var route: Route?
var isScrollable = false
var transactions: IdentifiedArrayOf<TransactionState>
}
// MARK: - Action
enum TransactionHistoryFlowAction: Equatable {
case onAppear
case onDisappear
case updateRoute(TransactionHistoryFlowState.Route?)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case updateTransactions([TransactionState])
}
// MARK: - Environment
struct TransactionHistoryFlowEnvironment {
let scheduler: AnySchedulerOf<DispatchQueue>
let SDKSynchronizer: WrappedSDKSynchronizer
}
// MARK: - Reducer
extension TransactionHistoryFlowReducer {
private struct CancelId: Hashable {}
static let `default` = TransactionHistoryFlowReducer { state, action, environment in
switch action {
case .onAppear:
return environment.SDKSynchronizer.stateChanged
.map(TransactionHistoryFlowAction.synchronizerStateChanged)
.eraseToEffect()
.cancellable(id: CancelId(), cancelInFlight: true)
case .onDisappear:
return Effect.cancel(id: CancelId())
case .synchronizerStateChanged(.synced):
return environment.SDKSynchronizer.getAllTransactions()
.receive(on: environment.scheduler)
.map(TransactionHistoryFlowAction.updateTransactions)
.eraseToEffect()
case .synchronizerStateChanged(let synchronizerState):
return .none
case .updateTransactions(let transactions):
let sortedTransactions = transactions
.sorted(by: { lhs, rhs in
lhs.date > rhs.date
})
state.transactions = IdentifiedArrayOf(uniqueElements: sortedTransactions)
return .none
case let .updateRoute(route):
state.route = route
return .none
}
}
}
// MARK: - ViewStore
extension TransactionHistoryFlowViewStore {
private typealias Route = TransactionHistoryFlowState.Route
func bindingForSelectingTransaction(_ transaction: TransactionState) -> Binding<Bool> {
self.binding(
get: { $0.route.map(/TransactionHistoryFlowState.Route.showTransaction) == transaction },
send: { isActive in
TransactionHistoryFlowAction.updateRoute( isActive ? TransactionHistoryFlowState.Route.showTransaction(transaction) : nil)
}
)
}
}
// MARK: Placeholders
extension TransactionState {
static var placeholder: Self {
.init(
date: Date.init(timeIntervalSince1970: 1234567),
id: "2",
status: .paid(success: true),
subtitle: "",
zecAmount: Zatoshi(amount: 25)
)
}
}
extension TransactionHistoryFlowState {
static var placeHolder: Self {
.init(transactions: .placeholder)
}
static var emptyPlaceHolder: Self {
.init(transactions: [])
}
}
extension TransactionHistoryFlowStore {
static var placeholder: Store<TransactionHistoryFlowState, TransactionHistoryFlowAction> {
return Store(
initialState: .placeHolder,
reducer: .default,
environment: TransactionHistoryFlowEnvironment(
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
SDKSynchronizer: LiveWrappedSDKSynchronizer()
)
)
}
static var demoWithSelectedTransaction: Store<TransactionHistoryFlowState, TransactionHistoryFlowAction> {
let transactions = IdentifiedArrayOf<TransactionState>.placeholder
return Store(
initialState: TransactionHistoryFlowState(
route: .showTransaction(transactions[3]),
transactions: transactions
),
reducer: .default.debug(),
environment: TransactionHistoryFlowEnvironment(
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
SDKSynchronizer: LiveWrappedSDKSynchronizer()
)
)
}
}
extension IdentifiedArrayOf where Element == TransactionState {
static var placeholder: IdentifiedArrayOf<TransactionState> {
return .init(
uniqueElements: (0..<30).map {
TransactionState(
date: Date.init(timeIntervalSince1970: 1234567),
id: String($0),
status: .paid(success: true),
subtitle: "",
zecAmount: Zatoshi(amount: 25)
)
}
)
}
}

View File

@ -0,0 +1,57 @@
//
// TransactionRowView.swift
// secant-testnet
//
// Created by Lukáš Korba on 21.06.2022.
//
import SwiftUI
struct TransactionRowView: View {
var transaction: TransactionState
var body: some View {
HStack {
Circle()
.foregroundColor(.white)
.frame(width: 30, height: 30, alignment: .center)
VStack {
HStack {
Text(transaction.status == .received ? "Unknown paid you" : "You sent to")
.font(.system(size: 14))
.fontWeight(.bold)
Spacer()
Text(transaction.status == .received ? "+" : "")
+ Text("\(transaction.zecAmount.decimalString()) ZEC")
}
HStack {
Text(transaction.address)
.font(.system(size: 14))
.fontWeight(.thin)
.truncationMode(.middle)
.lineLimit(1)
Spacer(minLength: 80)
Text("$145")
.font(.system(size: 14))
.fontWeight(.thin)
}
}
}
}
}
struct SendTransactionRowView_Previews: PreviewProvider {
static var previews: some View {
VStack {
TransactionRowView(transaction: .placeholder)
}
.padding()
.preferredColorScheme(.dark)
.previewLayout(.fixed(width: 428, height: 60))
}
}

View File

@ -0,0 +1,146 @@
import ComposableArchitecture
import SwiftUI
typealias WalletEventsFlowReducer = Reducer<WalletEventsFlowState, WalletEventsFlowAction, WalletEventsFlowEnvironment>
typealias WalletEventsFlowStore = Store<WalletEventsFlowState, WalletEventsFlowAction>
typealias WalletEventsFlowViewStore = ViewStore<WalletEventsFlowState, WalletEventsFlowAction>
// MARK: - State
struct WalletEventsFlowState: Equatable {
enum Route: Equatable {
case latest
case all
case showWalletEvent(WalletEvent)
}
var route: Route?
var isScrollable = false
var walletEvents = IdentifiedArrayOf<WalletEvent>.placeholder
}
// MARK: - Action
enum WalletEventsFlowAction: Equatable {
case onAppear
case onDisappear
case updateRoute(WalletEventsFlowState.Route?)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case updateWalletEvents([WalletEvent])
}
// MARK: - Environment
struct WalletEventsFlowEnvironment {
let scheduler: AnySchedulerOf<DispatchQueue>
let SDKSynchronizer: WrappedSDKSynchronizer
}
// MARK: - Reducer
extension WalletEventsFlowReducer {
private struct CancelId: Hashable {}
static let `default` = WalletEventsFlowReducer { state, action, environment in
switch action {
case .onAppear:
return environment.SDKSynchronizer.stateChanged
.map(WalletEventsFlowAction.synchronizerStateChanged)
.eraseToEffect()
.cancellable(id: CancelId(), cancelInFlight: true)
case .onDisappear:
return Effect.cancel(id: CancelId())
case .synchronizerStateChanged(.synced):
return environment.SDKSynchronizer.getAllTransactions()
.receive(on: environment.scheduler)
.map(WalletEventsFlowAction.updateWalletEvents)
.eraseToEffect()
case .synchronizerStateChanged(let synchronizerState):
return .none
case .updateWalletEvents(let walletEvents):
let sortedWalletEvents = walletEvents
.sorted(by: { lhs, rhs in
lhs.timestamp > rhs.timestamp
})
state.walletEvents = IdentifiedArrayOf(uniqueElements: sortedWalletEvents)
return .none
case let .updateRoute(route):
state.route = route
return .none
}
}
}
// MARK: - ViewStore
extension WalletEventsFlowViewStore {
private typealias Route = WalletEventsFlowState.Route
func bindingForSelectingWalletEvent(_ walletEvent: WalletEvent) -> Binding<Bool> {
self.binding(
get: { $0.route.map(/WalletEventsFlowState.Route.showWalletEvent) == walletEvent },
send: { isActive in
WalletEventsFlowAction.updateRoute( isActive ? WalletEventsFlowState.Route.showWalletEvent(walletEvent) : nil)
}
)
}
}
// MARK: Placeholders
extension TransactionState {
static var placeholder: Self {
.init(
id: "2",
status: .paid(success: true),
subtitle: "",
timestamp: 1234567,
zecAmount: Zatoshi(amount: 25)
)
}
}
extension WalletEventsFlowState {
static var placeHolder: Self {
.init(walletEvents: .placeholder)
}
static var emptyPlaceHolder: Self {
.init(walletEvents: [])
}
}
extension WalletEventsFlowStore {
static var placeholder: Store<WalletEventsFlowState, WalletEventsFlowAction> {
return Store(
initialState: .placeHolder,
reducer: .default,
environment: WalletEventsFlowEnvironment(
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
SDKSynchronizer: LiveWrappedSDKSynchronizer()
)
)
}
}
extension IdentifiedArrayOf where Element == TransactionState {
static var placeholder: IdentifiedArrayOf<TransactionState> {
return .init(
uniqueElements: (0..<30).map {
TransactionState(
id: String($0),
status: .paid(success: true),
subtitle: "",
timestamp: 1234567,
zecAmount: Zatoshi(amount: 25)
)
}
)
}
}

View File

@ -1,8 +1,8 @@
import SwiftUI
import ComposableArchitecture
struct TransactionHistoryFlowView: View {
let store: TransactionHistoryFlowStore
struct WalletEventsFlowView: View {
let store: WalletEventsFlowStore
var body: some View {
UITableView.appearance().backgroundColor = .clear
@ -14,11 +14,11 @@ struct TransactionHistoryFlowView: View {
if viewStore.isScrollable {
List {
transactionsList(with: viewStore)
walletEventsList(with: viewStore)
}
.listStyle(.sidebar)
} else {
transactionsList(with: viewStore)
walletEventsList(with: viewStore)
.padding(.horizontal, 32)
}
}
@ -28,36 +28,16 @@ struct TransactionHistoryFlowView: View {
}
}
extension TransactionHistoryFlowView {
func transactionsList(with viewStore: TransactionHistoryFlowViewStore) -> some View {
ForEach(viewStore.transactions) { transaction in
WithStateBinding(binding: viewStore.bindingForSelectingTransaction(transaction)) { active in
extension WalletEventsFlowView {
func walletEventsList(with viewStore: WalletEventsFlowViewStore) -> some View {
ForEach(viewStore.walletEvents) { walletEvent in
WithStateBinding(binding: viewStore.bindingForSelectingWalletEvent(walletEvent)) { active in
VStack {
HStack {
Text(transaction.date.asHumanReadable())
.font(.system(size: 12))
.fontWeight(.thin)
Spacer()
Text(transaction.subtitle)
.font(.system(size: 12))
.fontWeight(.thin)
.foregroundColor(transaction.subtitle == "pending" ? .red : .green)
}
HStack {
Text(transaction.status == .received ? "Recevied" : "Sent")
Spacer()
Text(transaction.status == .received ? "+" : "")
+ Text("\(transaction.zecAmount.decimalString()) ZEC")
}
walletEvent.rowView()
}
.navigationLink(
isActive: active,
destination: { TransactionDetailView(transaction: transaction) }
destination: { walletEvent.detailView() }
)
.foregroundColor(Asset.Colors.Text.body.color)
.listRowBackground(Color.clear)
@ -65,7 +45,7 @@ extension TransactionHistoryFlowView {
}
}
func header(with viewStore: TransactionHistoryFlowViewStore) -> some View {
func header(with viewStore: WalletEventsFlowViewStore) -> some View {
HStack(spacing: 0) {
VStack {
Button("Latest") {
@ -95,7 +75,7 @@ extension TransactionHistoryFlowView {
struct TransactionView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
TransactionHistoryFlowView(store: .placeholder)
WalletEventsFlowView(store: .placeholder)
.preferredColorScheme(.dark)
}
}

View File

@ -13,6 +13,7 @@ struct TransactionState: Equatable, Identifiable {
enum Status: Equatable {
case paid(success: Bool)
case received
case failed
}
var expirationHeight = -1
@ -21,16 +22,18 @@ struct TransactionState: Equatable, Identifiable {
var shielded = true
var zAddress: String?
var date: Date
var id: String
var status: Status
var subtitle: String
var timestamp: TimeInterval
var zecAmount: Zatoshi
var address: String { zAddress ?? "" }
}
extension TransactionState {
init(confirmedTransaction: ConfirmedTransactionEntity, sent: Bool = false) {
date = Date(timeIntervalSince1970: confirmedTransaction.blockTimeInSeconds)
timestamp = confirmedTransaction.blockTimeInSeconds
id = confirmedTransaction.transactionEntity.transactionId.toHexStringTxId()
shielded = true
status = sent ? .paid(success: confirmedTransaction.minedHeight > 0) : .received
@ -44,7 +47,7 @@ extension TransactionState {
}
init(pendingTransaction: PendingTransactionEntity, latestBlockHeight: BlockHeight? = nil) {
date = Date(timeIntervalSince1970: pendingTransaction.createTime)
timestamp = pendingTransaction.createTime
id = pendingTransaction.rawTransactionId?.toHexStringTxId() ?? String(pendingTransaction.createTime)
shielded = true
status = .paid(success: pendingTransaction.isSubmitSuccess)
@ -63,11 +66,11 @@ extension TransactionState {
extension TransactionState {
static func placeholder(
date: Date,
amount: Zatoshi,
shielded: Bool = true,
status: Status = .received,
subtitle: String = "",
timestamp: TimeInterval,
uuid: String = UUID().debugDescription
) -> TransactionState {
.init(
@ -76,10 +79,10 @@ extension TransactionState {
minedHeight: -1,
shielded: shielded,
zAddress: nil,
date: date,
id: uuid,
status: status,
subtitle: subtitle,
timestamp: timestamp,
zecAmount: status == .received ? amount : Zatoshi(amount: -amount.amount)
)
}

View File

@ -0,0 +1,98 @@
//
// WalletEvent.swift
// secant-testnet
//
// Created by Lukáš Korba on 20.06.2022.
//
import Foundation
import ComposableArchitecture
import SwiftUI
import ZcashLightClientKit
// MARK: - Model
struct WalletEvent: Equatable, Identifiable {
enum WalletEventState: Equatable {
case send(TransactionState)
case pending(TransactionState)
case received(TransactionState)
case failed(TransactionState)
case shielded(Zatoshi)
case walletImport(BlockHeight)
}
let id: String
let state: WalletEventState
var timestamp: TimeInterval
}
// MARK: - Rows
extension WalletEvent {
@ViewBuilder func rowView() -> some View {
switch state {
case .send(let transaction):
TransactionRowView(transaction: transaction)
case .pending:
Text("pending wallet event")
case .received:
Text("received wallet event")
case .failed:
Text("failed wallet event")
case .shielded(let zatoshi):
Text("shielded wallet event \(zatoshi.decimalString())")
case .walletImport:
Text("wallet import wallet event")
}
}
}
// MARK: - Details
extension WalletEvent {
@ViewBuilder func detailView() -> some View {
switch state {
case .send(let transaction):
TransactionDetailView(transaction: transaction)
case .pending:
Text("pending transaction detail")
case .received:
Text("received transaction detail")
case .failed:
Text("failed transaction detail")
case .shielded(let zatoshi):
Text("shielded \(zatoshi.decimalString()) detail")
case .walletImport:
Text("wallet import wallet event")
}
}
}
// MARK: - Placeholders
private extension WalletEvent {
static func randomWalletEventState() -> WalletEvent.WalletEventState {
switch Int.random(in: 0..<5) {
case 1: return .received(.placeholder)
case 2: return .failed(.placeholder)
case 3: return .shielded(Zatoshi(amount: 234_000_000))
case 4: return .walletImport(BlockHeight(1_629_724))
default: return .send(.placeholder)
}
}
}
extension IdentifiedArrayOf where Element == WalletEvent {
static var placeholder: IdentifiedArrayOf<WalletEvent> {
return .init(
uniqueElements: (0..<30).map {
WalletEvent(
id: String($0),
state: WalletEvent.randomWalletEventState(),
timestamp: 1234567
)
}
)
}
}

View File

@ -48,9 +48,9 @@ protocol WrappedSDKSynchronizer {
func status() -> String
func getShieldedBalance() -> Effect<Balance, Never>
func getAllClearedTransactions() -> Effect<[TransactionState], Never>
func getAllPendingTransactions() -> Effect<[TransactionState], Never>
func getAllTransactions() -> Effect<[TransactionState], Never>
func getAllClearedTransactions() -> Effect<[WalletEvent], Never>
func getAllPendingTransactions() -> Effect<[WalletEvent], Never>
func getAllTransactions() -> Effect<[WalletEvent], Never>
func getTransparentAddress(account: Int) -> TransparentAddress?
func getShieldedAddress(account: Int) -> SaplingShieldedAddress?
@ -160,37 +160,40 @@ class LiveWrappedSDKSynchronizer: WrappedSDKSynchronizer {
return .none
}
func getAllClearedTransactions() -> Effect<[TransactionState], Never> {
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
if let clearedTransactions = try? synchronizer?.allClearedTransactions() {
return Effect(value: clearedTransactions.map {
TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllPendingTransactions() -> Effect<[TransactionState], Never> {
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
if let pendingTransactions = try? synchronizer?.allPendingTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight {
return Effect(value: pendingTransactions.map {
// TODO: - can we initialize it with latestBlockHeight: = nil?
TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllTransactions() -> Effect<[TransactionState], Never> {
func getAllTransactions() -> Effect<[WalletEvent], Never> {
if let pendingTransactions = try? synchronizer?.allPendingTransactions(),
let clearedTransactions = try? synchronizer?.allClearedTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight {
let clearedTxs = clearedTransactions.map {
TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
let clearedTxs: [WalletEvent] = clearedTransactions.map {
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil))
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
let pendingTxs = pendingTransactions.map {
TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
let pendingTxs: [WalletEvent] = pendingTransactions.map {
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
let txs = clearedTxs.filter { cleared in
@ -290,7 +293,7 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
return Effect(value: Balance(verified: 12345000, total: 12345000))
}
func getAllClearedTransactions() -> Effect<[TransactionState], Never> {
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false), uuid: "1"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2), uuid: "2"),
@ -302,19 +305,20 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
return Effect(
value:
mocked.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
let transaction = TransactionState.placeholder(
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllPendingTransactions() -> Effect<[TransactionState], Never> {
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039606, amount: Zatoshi(amount: 6), status: .paid(success: false), subtitle: "pending"),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(amount: 7), subtitle: "pending"),
@ -325,18 +329,19 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
return Effect(
value:
mocked.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
let transaction = TransactionState.placeholder(
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle
subtitle: $0.subtitle,
timestamp: $0.date
)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllTransactions() -> Effect<[TransactionState], Never> {
func getAllTransactions() -> Effect<[WalletEvent], Never> {
return .merge(
getAllClearedTransactions(),
getAllPendingTransactions()
@ -363,10 +368,10 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
minedHeight: 50,
shielded: true,
zAddress: "tteafadlamnelkqe",
date: Date.init(timeIntervalSince1970: 1234567),
id: "id",
status: .paid(success: true),
subtitle: "sub",
timestamp: 1234567,
zecAmount: Zatoshi(amount: 10)
)
@ -398,7 +403,7 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
return .none
}
func getAllClearedTransactions() -> Effect<[TransactionState], Never> {
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(amount: 1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(amount: 2), uuid: "bb22"),
@ -410,19 +415,20 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
return Effect(
value:
mocked.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
let transaction = TransactionState.placeholder(
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllPendingTransactions() -> Effect<[TransactionState], Never> {
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(
date: 1651039606,
@ -439,19 +445,20 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
return Effect(
value:
mocked.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
let transaction = TransactionState.placeholder(
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllTransactions() -> Effect<[TransactionState], Never> {
func getAllTransactions() -> Effect<[WalletEvent], Never> {
return .merge(
getAllClearedTransactions(),
getAllPendingTransactions()

View File

@ -74,18 +74,19 @@ class HomeTests: XCTestCase {
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4), uuid: "4"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5), uuid: "5")
]
let transactions = transactionsHelper.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
let walletEvents: [WalletEvent] = transactionsHelper.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
store.receive(.updateTransactions(transactions))
store.receive(.updateWalletEvents(walletEvents))
// ad 3.
let balance = Balance(verified: 12_345_000, total: 12_345_000)
@ -96,7 +97,7 @@ class HomeTests: XCTestCase {
}
}
func testTransactionHistoryPartial_to_FullDrawer() throws {
func testWalletEventsPartial_to_FullDrawer() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
@ -118,7 +119,7 @@ class HomeTests: XCTestCase {
scanState: .placeholder,
synchronizerStatus: "",
totalBalance: Zatoshi.zero,
transactionHistoryState: .emptyPlaceHolder,
walletEventsState: .emptyPlaceHolder,
verifiedBalance: Zatoshi.zero
)
@ -128,17 +129,17 @@ class HomeTests: XCTestCase {
environment: testEnvironment
)
store.send(.transactionHistory(.updateRoute(.all))) { state in
state.transactionHistoryState.route = .all
store.send(.walletEvents(.updateRoute(.all))) { state in
state.walletEventsState.route = .all
}
store.receive(.updateDrawer(.full)) { state in
state.drawerOverlay = .full
state.transactionHistoryState.isScrollable = true
state.walletEventsState.isScrollable = true
}
}
func testTransactionHistoryFull_to_PartialDrawer() throws {
func testWalletEventsFull_to_PartialDrawer() throws {
// setup the store and environment to be fully mocked
let testScheduler = DispatchQueue.test
@ -160,7 +161,7 @@ class HomeTests: XCTestCase {
scanState: .placeholder,
synchronizerStatus: "",
totalBalance: Zatoshi.zero,
transactionHistoryState: .emptyPlaceHolder,
walletEventsState: .emptyPlaceHolder,
verifiedBalance: Zatoshi.zero
)
@ -170,13 +171,13 @@ class HomeTests: XCTestCase {
environment: testEnvironment
)
store.send(.transactionHistory(.updateRoute(.latest))) { state in
state.transactionHistoryState.route = .latest
store.send(.walletEvents(.updateRoute(.latest))) { state in
state.walletEventsState.route = .latest
}
store.receive(.updateDrawer(.partial)) { state in
state.drawerOverlay = .partial
state.transactionHistoryState.isScrollable = false
state.walletEventsState.isScrollable = false
}
}

View File

@ -64,10 +64,10 @@ class SendTests: XCTestCase {
minedHeight: 50,
shielded: true,
zAddress: "tteafadlamnelkqe",
date: Date.init(timeIntervalSince1970: 1234567),
id: "id",
status: .paid(success: true),
subtitle: "sub",
timestamp: 1234567,
zecAmount: Zatoshi(amount: 10)
)

View File

@ -18,15 +18,16 @@ class HomeSnapshotTests: XCTestCase {
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4), uuid: "4"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5), uuid: "5")
]
let transactions = transactionsHelper.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
let transactions: [WalletEvent] = transactionsHelper.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
let balance = Balance(verified: 12_345_000, total: 12_345_000)
@ -40,7 +41,7 @@ class HomeSnapshotTests: XCTestCase {
scanState: .placeholder,
synchronizerStatus: "",
totalBalance: Zatoshi(amount: balance.total),
transactionHistoryState: .init(transactions: IdentifiedArrayOf(uniqueElements: transactions)),
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: transactions)),
verifiedBalance: Zatoshi(amount: balance.verified)
),
reducer: .default,
@ -56,7 +57,7 @@ class HomeSnapshotTests: XCTestCase {
// all transactions
ViewStore(store).send(.updateDrawer(.full))
addAttachments(
name: "\(#function)_fullTransactionHistory",
name: "\(#function)_fullWalletEvents",
HomeView(store: store)
)
}

View File

@ -1,5 +1,5 @@
//
// TransactionHistoryTests.swift
// WalletEventsTests.swift
// secantTests
//
// Created by Lukáš Korba on 27.04.2022.
@ -9,22 +9,22 @@ import XCTest
@testable import secant_testnet
import ComposableArchitecture
class TransactionHistoryTests: XCTestCase {
class WalletEventsTests: XCTestCase {
static let testScheduler = DispatchQueue.test
let testEnvironment = TransactionHistoryFlowEnvironment(
let testEnvironment = WalletEventsFlowEnvironment(
scheduler: testScheduler.eraseToAnyScheduler(),
SDKSynchronizer: TestWrappedSDKSynchronizer()
)
func testSynchronizerSubscription() throws {
let store = TestStore(
initialState: TransactionHistoryFlowState(
initialState: WalletEventsFlowState(
route: .latest,
isScrollable: true,
transactions: []
walletEvents: []
),
reducer: TransactionHistoryFlowReducer.default,
reducer: WalletEventsFlowReducer.default,
environment: testEnvironment
)
@ -55,26 +55,31 @@ class TransactionHistoryTests: XCTestCase {
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(amount: 9), subtitle: "pending", uuid: "ii99")
]
let transactions = mocked.map {
TransactionState.placeholder(
date: Date.init(timeIntervalSince1970: $0.date),
let walletEvents: [WalletEvent] = mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
shielded: $0.shielded,
status: $0.status,
subtitle: $0.subtitle,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(
id: transaction.id,
state: transaction.subtitle == "pending" ? .pending(transaction) : .send(transaction),
timestamp: transaction.timestamp
)
}
let identifiedTransactions = IdentifiedArrayOf(uniqueElements: transactions)
let identifiedTransactions = IdentifiedArrayOf(uniqueElements: walletEvents)
let store = TestStore(
initialState: TransactionHistoryFlowState(
initialState: WalletEventsFlowState(
route: .latest,
isScrollable: true,
transactions: identifiedTransactions
walletEvents: identifiedTransactions
),
reducer: TransactionHistoryFlowReducer.default,
reducer: WalletEventsFlowReducer.default,
environment: testEnvironment
)
@ -82,16 +87,16 @@ class TransactionHistoryTests: XCTestCase {
Self.testScheduler.advance(by: 0.01)
store.receive(.updateTransactions(transactions)) { state in
store.receive(.updateWalletEvents(walletEvents)) { state in
let receivedTransactions = IdentifiedArrayOf(
uniqueElements:
transactions
walletEvents
.sorted(by: { lhs, rhs in
lhs.date > rhs.date
lhs.timestamp > rhs.timestamp
})
)
state.transactions = receivedTransactions
state.walletEvents = receivedTransactions
}
}
}