[#1270] Synchronizer error in the alert view
- Changelog updated - SyncProgress reducer has been refactored to the latest TCA - Progress label is tappable when an error occurs and shows an alert view with the details
This commit is contained in:
parent
6d8096c20e
commit
0ad73b905d
|
@ -9,6 +9,7 @@ directly impact users rather than highlighting other crucial architectural updat
|
|||
### Added
|
||||
- Expanded transaction lists all text memos.
|
||||
- Biometric lock is used to protect Delete Zashi, Export Private Data and Send features.
|
||||
- Tapping on the error message label in the sync progress shows an alert view with the details of the error.
|
||||
|
||||
## 1.1 build 6 (2024-05-09)
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public struct BalanceBreakdownReducer: Reducer {
|
|||
public var partialProposalErrorState: PartialProposalError.State
|
||||
public var pendingTransactions: Zatoshi
|
||||
public var shieldedBalance: Zatoshi
|
||||
public var syncProgressState: SyncProgressReducer.State
|
||||
public var syncProgressState: SyncProgress.State
|
||||
public var transparentBalance: Zatoshi
|
||||
public var walletBalancesState: WalletBalances.State
|
||||
|
||||
|
@ -65,7 +65,7 @@ public struct BalanceBreakdownReducer: Reducer {
|
|||
partialProposalErrorState: PartialProposalError.State,
|
||||
pendingTransactions: Zatoshi,
|
||||
shieldedBalance: Zatoshi = .zero,
|
||||
syncProgressState: SyncProgressReducer.State,
|
||||
syncProgressState: SyncProgress.State,
|
||||
transparentBalance: Zatoshi = .zero,
|
||||
walletBalancesState: WalletBalances.State
|
||||
) {
|
||||
|
@ -96,7 +96,7 @@ public struct BalanceBreakdownReducer: Reducer {
|
|||
case shieldFundsPartial([String], [String])
|
||||
case shieldFundsSuccess
|
||||
case synchronizerStateChanged(RedactableSynchronizerState)
|
||||
case syncProgress(SyncProgressReducer.Action)
|
||||
case syncProgress(SyncProgress.Action)
|
||||
case updateBalances(AccountBalance?)
|
||||
case updateDestination(BalanceBreakdownReducer.State.Destination?)
|
||||
case updateHintBoxVisibility(Bool)
|
||||
|
@ -116,7 +116,7 @@ public struct BalanceBreakdownReducer: Reducer {
|
|||
|
||||
public var body: some Reducer<State, Action> {
|
||||
Scope(state: \.syncProgressState, action: /Action.syncProgress) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
|
||||
Scope(state: \.partialProposalErrorState, action: /Action.partialProposalError) {
|
||||
|
|
|
@ -26,7 +26,7 @@ public struct HomeReducer: Reducer {
|
|||
public var isRestoringWallet = false
|
||||
public var migratingDatabase = true
|
||||
public var scanState: Scan.State
|
||||
public var syncProgressState: SyncProgressReducer.State
|
||||
public var syncProgressState: SyncProgress.State
|
||||
public var walletConfig: WalletConfig
|
||||
public var transactionListState: TransactionListReducer.State
|
||||
public var walletBalancesState: WalletBalances.State
|
||||
|
@ -36,7 +36,7 @@ public struct HomeReducer: Reducer {
|
|||
isRestoringWallet: Bool = false,
|
||||
migratingDatabase: Bool = true,
|
||||
scanState: Scan.State,
|
||||
syncProgressState: SyncProgressReducer.State,
|
||||
syncProgressState: SyncProgress.State,
|
||||
transactionListState: TransactionListReducer.State,
|
||||
walletBalancesState: WalletBalances.State,
|
||||
walletConfig: WalletConfig
|
||||
|
@ -65,7 +65,7 @@ public struct HomeReducer: Reducer {
|
|||
case showSynchronizerErrorAlert(ZcashError)
|
||||
case synchronizerStateChanged(RedactableSynchronizerState)
|
||||
case syncFailed(ZcashError)
|
||||
case syncProgress(SyncProgressReducer.Action)
|
||||
case syncProgress(SyncProgress.Action)
|
||||
case updateTransactionList([TransactionState])
|
||||
case transactionList(TransactionListReducer.Action)
|
||||
case walletBalances(WalletBalances.Action)
|
||||
|
@ -85,7 +85,7 @@ public struct HomeReducer: Reducer {
|
|||
}
|
||||
|
||||
Scope(state: \.syncProgressState, action: /Action.syncProgress) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
|
||||
Scope(state: \.walletBalancesState, action: /Action.walletBalances) {
|
||||
|
|
|
@ -14,12 +14,14 @@ import Models
|
|||
import SDKSynchronizer
|
||||
import Utils
|
||||
|
||||
public typealias SyncProgressStore = Store<SyncProgressReducer.State, SyncProgressReducer.Action>
|
||||
|
||||
public struct SyncProgressReducer: Reducer {
|
||||
@Reducer
|
||||
public struct SyncProgress {
|
||||
private let CancelId = UUID()
|
||||
|
||||
public struct State: Equatable {
|
||||
@ObservableState
|
||||
public struct State: Equatable {
|
||||
@Presents public var alert: AlertState<Action>?
|
||||
public var lastKnownErrorMessage: String?
|
||||
public var lastKnownSyncPercentage: Float = 0
|
||||
public var synchronizerStatusSnapshot: SyncStatusSnapshot
|
||||
public var syncStatusMessage = ""
|
||||
|
@ -38,10 +40,12 @@ public struct SyncProgressReducer: Reducer {
|
|||
}
|
||||
|
||||
public init(
|
||||
lastKnownErrorMessage: String? = nil,
|
||||
lastKnownSyncPercentage: Float,
|
||||
synchronizerStatusSnapshot: SyncStatusSnapshot,
|
||||
syncStatusMessage: String = ""
|
||||
) {
|
||||
self.lastKnownErrorMessage = lastKnownErrorMessage
|
||||
self.lastKnownSyncPercentage = lastKnownSyncPercentage
|
||||
self.synchronizerStatusSnapshot = synchronizerStatusSnapshot
|
||||
self.syncStatusMessage = syncStatusMessage
|
||||
|
@ -49,6 +53,8 @@ public struct SyncProgressReducer: Reducer {
|
|||
}
|
||||
|
||||
public enum Action: Equatable {
|
||||
case alert(PresentationAction<Action>)
|
||||
case errorMessageTapped
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case synchronizerStateChanged(RedactableSynchronizerState)
|
||||
|
@ -74,6 +80,19 @@ public struct SyncProgressReducer: Reducer {
|
|||
case .onDisappear:
|
||||
return .cancel(id: CancelId)
|
||||
|
||||
case .alert(.presented(let action)):
|
||||
return Effect.send(action)
|
||||
|
||||
case .alert(.dismiss):
|
||||
state.alert = nil
|
||||
return .none
|
||||
|
||||
case .errorMessageTapped:
|
||||
if let errorMessage = state.lastKnownErrorMessage {
|
||||
state.alert = AlertState.errorMessage(errorMessage)
|
||||
}
|
||||
return .none
|
||||
|
||||
case .synchronizerStateChanged(let latestState):
|
||||
let snapshot = SyncStatusSnapshot.snapshotFor(state: latestState.data.syncStatus)
|
||||
if snapshot.syncStatus != state.synchronizerStatusSnapshot.syncStatus {
|
||||
|
@ -83,6 +102,8 @@ public struct SyncProgressReducer: Reducer {
|
|||
state.lastKnownSyncPercentage = progress
|
||||
}
|
||||
|
||||
state.lastKnownErrorMessage = nil
|
||||
|
||||
switch snapshot.syncStatus {
|
||||
case .syncing:
|
||||
state.syncStatusMessage = L10n.Balances.syncing
|
||||
|
@ -90,6 +111,7 @@ public struct SyncProgressReducer: Reducer {
|
|||
state.lastKnownSyncPercentage = 1
|
||||
state.syncStatusMessage = L10n.Balances.synced
|
||||
case .error, .unprepared:
|
||||
state.lastKnownErrorMessage = snapshot.message
|
||||
#if DEBUG
|
||||
state.syncStatusMessage = snapshot.message
|
||||
#else
|
||||
|
@ -104,21 +126,14 @@ public struct SyncProgressReducer: Reducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Store
|
||||
// MARK: Alerts
|
||||
|
||||
extension SyncProgressStore {
|
||||
public static var initial = SyncProgressStore(
|
||||
initialState: .initial
|
||||
) {
|
||||
SyncProgressReducer()
|
||||
extension AlertState where Action == SyncProgress.Action {
|
||||
public static func errorMessage(_ message: String) -> AlertState {
|
||||
AlertState {
|
||||
TextState(L10n.Sync.Alert.title)
|
||||
} message: {
|
||||
TextState(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension SyncProgressReducer.State {
|
||||
public static let initial = SyncProgressReducer.State(
|
||||
lastKnownSyncPercentage: 0,
|
||||
synchronizerStatusSnapshot: .initial
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,20 +13,21 @@ import UIComponents
|
|||
import Models
|
||||
|
||||
public struct SyncProgressView: View {
|
||||
var store: SyncProgressStore
|
||||
@Perception.Bindable var store: StoreOf<SyncProgress>
|
||||
|
||||
public init(store: SyncProgressStore) {
|
||||
public init(store: StoreOf<SyncProgress>) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(store, observe: { $0 }) { viewStore in
|
||||
WithPerceptionTracking {
|
||||
VStack(spacing: 5) {
|
||||
if viewStore.isSyncing {
|
||||
if store.isSyncing {
|
||||
HStack {
|
||||
Text(viewStore.syncStatusMessage)
|
||||
Text(store.syncStatusMessage)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 10))
|
||||
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
// Frame height 0 is expected value because we want SwiftUI to ignore it
|
||||
// for the vertical placement computation.
|
||||
ProgressView()
|
||||
|
@ -34,21 +35,35 @@ public struct SyncProgressView: View {
|
|||
.frame(width: 11, height: 0)
|
||||
}
|
||||
} else {
|
||||
Text(viewStore.syncStatusMessage)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 10))
|
||||
.padding(.horizontal, 35)
|
||||
if let errorMessage = store.lastKnownErrorMessage {
|
||||
Button {
|
||||
store.send(.errorMessageTapped)
|
||||
} label: {
|
||||
Text(store.syncStatusMessage)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 10))
|
||||
.padding(.horizontal, 35)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
} else {
|
||||
Text(store.syncStatusMessage)
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 10))
|
||||
.padding(.horizontal, 35)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
}
|
||||
|
||||
Text(String(format: "%0.1f%%", viewStore.syncingPercentage * 100))
|
||||
Text(String(format: "%0.1f%%", store.syncingPercentage * 100))
|
||||
.font(.custom(FontFamily.Inter.black.name, size: 10))
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
ProgressView(value: viewStore.syncingPercentage, total: 1.0)
|
||||
ProgressView(value: store.syncingPercentage, total: 1.0)
|
||||
.progressViewStyle(ZashiSyncingProgressStyle())
|
||||
}
|
||||
.onAppear { viewStore.send(.onAppear) }
|
||||
.onDisappear { viewStore.send(.onDisappear) }
|
||||
.onAppear { store.send(.onAppear) }
|
||||
.onDisappear { store.send(.onDisappear) }
|
||||
.alert($store.scope(state: \.alert, action: \.alert))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,15 +71,34 @@ public struct SyncProgressView: View {
|
|||
#Preview {
|
||||
SyncProgressView(
|
||||
store:
|
||||
SyncProgressStore(
|
||||
StoreOf<SyncProgress>(
|
||||
initialState: .init(
|
||||
lastKnownSyncPercentage: Float(0.43),
|
||||
synchronizerStatusSnapshot: SyncStatusSnapshot(.syncing(0.41)),
|
||||
syncStatusMessage: "Syncing"
|
||||
)
|
||||
) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
)
|
||||
.background(.red)
|
||||
}
|
||||
|
||||
// MARK: - Store
|
||||
|
||||
extension SyncProgress {
|
||||
public static var initial = StoreOf<SyncProgress>(
|
||||
initialState: .initial
|
||||
) {
|
||||
SyncProgress()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension SyncProgress.State {
|
||||
public static let initial = SyncProgress.State(
|
||||
lastKnownSyncPercentage: 0,
|
||||
synchronizerStatusSnapshot: .initial
|
||||
)
|
||||
}
|
||||
|
|
|
@ -587,6 +587,10 @@ public enum L10n {
|
|||
}
|
||||
}
|
||||
public enum Sync {
|
||||
public enum Alert {
|
||||
/// Error
|
||||
public static let title = L10n.tr("Localizable", "sync.alert.title", fallback: "Error")
|
||||
}
|
||||
public enum Message {
|
||||
/// Error: %@
|
||||
public static func error(_ p1: Any) -> String {
|
||||
|
|
|
@ -156,6 +156,7 @@ Sharing this private data is irrevocable — once you have shared this private d
|
|||
"sync.message.error" = "Error: %@";
|
||||
"sync.message.stopped" = "Stopped";
|
||||
"sync.message.sync" = "%@%% Synced";
|
||||
"sync.alert.title" = "Error";
|
||||
|
||||
// MARK: - Common & Shared
|
||||
"general.back" = "Back";
|
||||
|
|
|
@ -17,12 +17,12 @@ import Generated
|
|||
final class SyncProgressTests: XCTestCase {
|
||||
func testSyncingData() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SyncProgressReducer.State(
|
||||
initialState: SyncProgress.State(
|
||||
lastKnownSyncPercentage: 0.0,
|
||||
synchronizerStatusSnapshot: .snapshotFor(state: .syncing(0.513))
|
||||
)
|
||||
) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
|
||||
XCTAssertTrue(store.state.isSyncing)
|
||||
|
@ -31,12 +31,12 @@ final class SyncProgressTests: XCTestCase {
|
|||
|
||||
func testlastKnownSyncingPercentage_Zero() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SyncProgressReducer.State(
|
||||
initialState: SyncProgress.State(
|
||||
lastKnownSyncPercentage: 0.0,
|
||||
synchronizerStatusSnapshot: .placeholder
|
||||
)
|
||||
) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
|
||||
XCTAssertEqual(store.state.lastKnownSyncPercentage, 0)
|
||||
|
@ -45,12 +45,12 @@ final class SyncProgressTests: XCTestCase {
|
|||
|
||||
func testlastKnownSyncingPercentage_MoreThanZero() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SyncProgressReducer.State(
|
||||
initialState: SyncProgress.State(
|
||||
lastKnownSyncPercentage: 0.15,
|
||||
synchronizerStatusSnapshot: .placeholder
|
||||
)
|
||||
) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
|
||||
XCTAssertEqual(store.state.lastKnownSyncPercentage, 0.15)
|
||||
|
@ -59,12 +59,12 @@ final class SyncProgressTests: XCTestCase {
|
|||
|
||||
func testlastKnownSyncingPercentage_FromSyncedState() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SyncProgressReducer.State(
|
||||
initialState: SyncProgress.State(
|
||||
lastKnownSyncPercentage: 0.15,
|
||||
synchronizerStatusSnapshot: .snapshotFor(state: .syncing(0.513))
|
||||
)
|
||||
) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
|
||||
var syncState: SynchronizerState = .zero
|
||||
|
@ -80,12 +80,12 @@ final class SyncProgressTests: XCTestCase {
|
|||
|
||||
func testlastKnownSyncingPercentage_FromSyncingState() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SyncProgressReducer.State(
|
||||
initialState: SyncProgress.State(
|
||||
lastKnownSyncPercentage: 0.15,
|
||||
synchronizerStatusSnapshot: .snapshotFor(state: .syncing(0.513))
|
||||
)
|
||||
) {
|
||||
SyncProgressReducer()
|
||||
SyncProgress()
|
||||
}
|
||||
|
||||
var syncState: SynchronizerState = .zero
|
||||
|
|
Loading…
Reference in New Issue