diff --git a/novawallet.xcodeproj/project.pbxproj b/novawallet.xcodeproj/project.pbxproj index f71bbfba8..03a140c58 100644 --- a/novawallet.xcodeproj/project.pbxproj +++ b/novawallet.xcodeproj/project.pbxproj @@ -756,6 +756,7 @@ 2D077E472BFDCB8700FE422E /* ManualBackupChainTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D077E462BFDCB8700FE422E /* ManualBackupChainTableViewCell.swift */; }; 2D221CBE2BF753620053E36C /* ContentSizedCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D221CBD2BF753620053E36C /* ContentSizedCollectionView.swift */; }; 2D221CC02BF753C30053E36C /* MnemonicWordCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D221CBF2BF753C30053E36C /* MnemonicWordCollectionCell.swift */; }; + 2D8684602C0086A500A663D2 /* ManualBackupKeyListViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D86845F2C0086A500A663D2 /* ManualBackupKeyListViewModelFactory.swift */; }; 2D8684552BFF7F6000A663D2 /* ExportPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8684542BFF7F6000A663D2 /* ExportPresenter.swift */; }; 2D8684582BFF8CE600A663D2 /* BaseExportPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8684572BFF8CE600A663D2 /* BaseExportPresenter.swift */; }; 2D86845E2BFF9DA000A663D2 /* BackupAttentionInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D86845D2BFF9DA000A663D2 /* BackupAttentionInteractor.swift */; }; @@ -5515,6 +5516,7 @@ 2D077E462BFDCB8700FE422E /* ManualBackupChainTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualBackupChainTableViewCell.swift; sourceTree = ""; }; 2D221CBD2BF753620053E36C /* ContentSizedCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSizedCollectionView.swift; sourceTree = ""; }; 2D221CBF2BF753C30053E36C /* MnemonicWordCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicWordCollectionCell.swift; sourceTree = ""; }; + 2D86845F2C0086A500A663D2 /* ManualBackupKeyListViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualBackupKeyListViewModelFactory.swift; sourceTree = ""; }; 2D8684542BFF7F6000A663D2 /* ExportPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportPresenter.swift; sourceTree = ""; }; 2D8684572BFF8CE600A663D2 /* BaseExportPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseExportPresenter.swift; sourceTree = ""; }; 2D86845D2BFF9DA000A663D2 /* BackupAttentionInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupAttentionInteractor.swift; sourceTree = ""; }; @@ -11189,6 +11191,7 @@ 2D077E462BFDCB8700FE422E /* ManualBackupChainTableViewCell.swift */, 2CB02245B44D5D1036626E24 /* ManualBackupKeyListWireframe.swift */, A6D59DCB2D2148A8DFF4F099 /* ManualBackupKeyListPresenter.swift */, + 2D86845F2C0086A500A663D2 /* ManualBackupKeyListViewModelFactory.swift */, 8CA7AD4DAF33E6758420BBD6 /* ManualBackupKeyListInteractor.swift */, 0E1AB5FA9D24E5E53E703005 /* ManualBackupKeyListViewController.swift */, 29CD41F03830F4967EF06F91 /* ManualBackupKeyListViewLayout.swift */, @@ -24542,6 +24545,7 @@ 84EBC55124F660A700459D15 /* EventVisitor.swift in Sources */, 0C37AFD32B598D8C00009ECA /* ProxySignConfirmationInteractor.swift in Sources */, 842B2FCF2947352A002829B6 /* UITextField+Attributes.swift in Sources */, + 2D8684602C0086A500A663D2 /* ManualBackupKeyListViewModelFactory.swift in Sources */, 8452585127ABC531004F9082 /* AssetsSettingsViewModel.swift in Sources */, 0C8AF7BA2B3806B700005AC9 /* ChainModel+Pdc20.swift in Sources */, 0C1BE1A62A47FC240010933C /* MultistakingSyncServiceFactory.swift in Sources */, diff --git a/novawallet/Common/View/ChainAccount/ChainAccountView.swift b/novawallet/Common/View/ChainAccount/ChainAccountView.swift index 4bca3b894..695ca9481 100644 --- a/novawallet/Common/View/ChainAccount/ChainAccountView.swift +++ b/novawallet/Common/View/ChainAccount/ChainAccountView.swift @@ -20,7 +20,7 @@ final class ChainAccountView: UIView { view.valueBottom.apply(style: .caption1Secondary) view.valueBottom.textAlignment = .left view.valueBottom.numberOfLines = 0 - view.valueBottom.isHidden + view.valueBottom.isHidden = true view.spacing = 4 } diff --git a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListPresenter.swift b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListPresenter.swift index 75069f477..0d2b40f3d 100644 --- a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListPresenter.swift +++ b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListPresenter.swift @@ -8,9 +8,8 @@ final class ManualBackupKeyListPresenter { let interactor: ManualBackupKeyListInteractorInputProtocol private let metaAccount: MetaAccountModel + private let viewModelFactory: ManualBackupKeyListViewModelFactory private let walletViewModelFactory = WalletAccountViewModelFactory() - private let networkViewModelFactory: NetworkViewModelFactoryProtocol - private let localizationManager: LocalizationManagerProtocol private let logger: Logger private var chains: [ChainModel.Id: ChainModel] = [:] @@ -18,17 +17,15 @@ final class ManualBackupKeyListPresenter { init( interactor: ManualBackupKeyListInteractorInputProtocol, wireframe: ManualBackupKeyListWireframeProtocol, + viewModelFactory: ManualBackupKeyListViewModelFactory, metaAccount: MetaAccountModel, - networkViewModelFactory: NetworkViewModelFactoryProtocol, - localizationManager: LocalizationManagerProtocol, logger: Logger ) { self.interactor = interactor self.wireframe = wireframe + self.viewModelFactory = viewModelFactory self.metaAccount = metaAccount - self.networkViewModelFactory = networkViewModelFactory self.logger = logger - self.localizationManager = localizationManager } } @@ -45,14 +42,14 @@ extension ManualBackupKeyListPresenter: ManualBackupKeyListPresenterProtocol { view?.updateNavbar(with: walletViewModel) } - func didTapDefaultKey() { + func activateDefaultKey() { wireframe.showDefaultAccountBackup( from: view, with: metaAccount ) } - func didTapCustomKey(with chainId: ChainModel.Id) { + func activateCustomKey(with chainId: ChainModel.Id) { guard let chain = chains[chainId] else { return } wireframe.showCustomKeyAccountBackup(from: view, with: metaAccount, chain: chain) @@ -69,7 +66,10 @@ extension ManualBackupKeyListPresenter: ManualBackupKeyListInteractorOutputProto chains, for: metaAccount ) - let viewModel = createViewModel(from: sortedChains) + let viewModel = viewModelFactory.createViewModel( + from: sortedChains.defaultChains, + sortedChains.customChains + ) view?.update(with: viewModel) } @@ -78,78 +78,21 @@ extension ManualBackupKeyListPresenter: ManualBackupKeyListInteractorOutputProto // MARK: Private private extension ManualBackupKeyListPresenter { - func createViewModel(from sortedChains: SortedChains) -> ManualBackupKeyListViewLayout.Model { - let listHeaderText = R.string.localizable.chainAccountsListHeader( - preferredLanguages: localizationManager.selectedLocale.rLanguages - ) - - return .init( - listHeaderText: listHeaderText, - accountsSections: [ - createDefaultChainsSection(for: sortedChains.defaultChains), - createCustomChainsSection(for: sortedChains.customChains) - ] - ) - } - - func createDefaultChainsSection(for chains: [ChainModel]) -> ManualBackupKeyListViewLayout.Sections { - let defaultChainsHeaderText = R.string.localizable.chainAccountsListDefaultHeader( - preferredLanguages: localizationManager.selectedLocale.rLanguages - ) - - let defaultChainsTitleText = R.string.localizable.chainAccountsListDefaultTitle( - preferredLanguages: localizationManager.selectedLocale.rLanguages - ) - - return .defaultKeys( - .init( - headerText: defaultChainsHeaderText.uppercased(), - accounts: [ - .init( - title: defaultChainsTitleText, - subtitle: formattedString(for: chains) - ) - ] - ) - ) - } - - func createCustomChainsSection(for chains: [ChainModel]) -> ManualBackupKeyListViewLayout.Sections { - let customChainsViewModels = chains - .compactMap { [weak self] chain -> ManualBackupKeyListViewLayout.CustomAccount? in - guard let self else { return .none } - - return ManualBackupKeyListViewLayout.CustomAccount( - network: networkViewModelFactory.createViewModel(from: chain), - chainId: chain.chainId - ) - } - - let customChainsHeaderText = R.string.localizable.chainAccountsListCustomHeader( - preferredLanguages: localizationManager.selectedLocale.rLanguages - ) - - return .customKeys( - .init( - headerText: customChainsHeaderText.uppercased(), - accounts: customChainsViewModels - ) - ) - } - func sorted( _ chains: [ChainModel.Id: ChainModel], for metaAccount: MetaAccountModel ) -> SortedChains { - let defaultChainsIds = Set(metaAccount.chainAccounts.map(\.chainId)) + let accountsChainIds = Set(metaAccount.chainAccounts.map(\.chainId)) var defaultChains: [ChainModel] = [] var customChains: [ChainModel] = [] chains.forEach { chain in - defaultChainsIds.contains(chain.key) - ? customChains.append(chain.value) - : defaultChains.append(chain.value) + if accountsChainIds.contains(chain.key) { + customChains.append(chain.value) + } else if let _ = metaAccount.fetch(for: chain.value.accountRequest()) { + defaultChains.append(chain.value) + } } defaultChains.sort { ChainModelCompator.defaultComparator(chain1: $0, chain2: $1) } @@ -160,36 +103,6 @@ private extension ManualBackupKeyListPresenter { customChains: customChains ) } - - func formattedString(for defaultChains: [ChainModel]) -> String { - let chainsToMention = defaultChains.count > 1 - ? defaultChains.prefix(2) - : defaultChains.prefix(1) - let separator = ", " - let restCount = defaultChains.count - chainsToMention.count - - return chainsToMention - .map(\.name) - .reduce(into: "") { partialResult, name in - guard !partialResult.isEmpty else { - partialResult += name - - return - } - - var result = [partialResult, name].joined(separator: separator) - - if chainsToMention.last?.name == name, restCount > 0 { - let othersString = R.string.localizable.chainAccountsListDefaultSubtitle( - restCount, - preferredLanguages: localizationManager.selectedLocale.rLanguages - ) - result = [result, othersString].joined(separator: separator) - } - - partialResult = result - } - } } private extension ManualBackupKeyListPresenter { diff --git a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListProtocols.swift b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListProtocols.swift index f92a52164..2664d36d4 100644 --- a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListProtocols.swift +++ b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListProtocols.swift @@ -7,8 +7,8 @@ protocol ManualBackupKeyListViewProtocol: ControllerBackedProtocol { protocol ManualBackupKeyListPresenterProtocol: AnyObject { func setup() - func didTapDefaultKey() - func didTapCustomKey(with chainId: ChainModel.Id) + func activateDefaultKey() + func activateCustomKey(with chainId: ChainModel.Id) } protocol ManualBackupKeyListInteractorInputProtocol: AnyObject { diff --git a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewController.swift b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewController.swift index 04368104e..620524f36 100644 --- a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewController.swift +++ b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewController.swift @@ -121,10 +121,10 @@ extension ManualBackupKeyListViewController: UITableViewDataSource, UITableViewD switch viewModel.accountsSections[indexPath.section] { case .defaultKeys: - presenter.didTapDefaultKey() + presenter.activateDefaultKey() case let .customKeys(model): let id = model.accounts[indexPath.row].chainId - presenter.didTapCustomKey(with: id) + presenter.activateCustomKey(with: id) } } } diff --git a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewFactory.swift b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewFactory.swift index 5ecc690d5..9e6f7fd0f 100644 --- a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewFactory.swift +++ b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewFactory.swift @@ -6,7 +6,10 @@ import SoraFoundation struct ManualBackupKeyListViewFactory { static func createView(with metaAccount: MetaAccountModel) -> ManualBackupKeyListViewProtocol? { let chainRegistry = ChainRegistryFacade.sharedRegistry - let networkViewModelFactory = NetworkViewModelFactory() + let viewModelFactory = ManualBackupKeyListViewModelFactory( + localizationManager: LocalizationManager.shared, + networkViewModelFactory: NetworkViewModelFactory() + ) let interactor = ManualBackupKeyListInteractor( chainRegistry: chainRegistry @@ -17,9 +20,8 @@ struct ManualBackupKeyListViewFactory { let presenter = ManualBackupKeyListPresenter( interactor: interactor, wireframe: wireframe, + viewModelFactory: viewModelFactory, metaAccount: metaAccount, - networkViewModelFactory: networkViewModelFactory, - localizationManager: LocalizationManager.shared, logger: Logger.shared ) diff --git a/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewModelFactory.swift b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewModelFactory.swift new file mode 100644 index 000000000..c619a73fc --- /dev/null +++ b/novawallet/Modules/ManualBackupKeyList/ManualBackupKeyListViewModelFactory.swift @@ -0,0 +1,107 @@ +import Foundation +import SoraFoundation + +class ManualBackupKeyListViewModelFactory { + private let localizationManager: LocalizationManagerProtocol + private let networkViewModelFactory: NetworkViewModelFactoryProtocol + + init( + localizationManager: LocalizationManagerProtocol, + networkViewModelFactory: NetworkViewModelFactoryProtocol + ) { + self.localizationManager = localizationManager + self.networkViewModelFactory = networkViewModelFactory + } + + func createViewModel( + from defaultChains: [ChainModel], + _ customChains: [ChainModel] + ) -> ManualBackupKeyListViewLayout.Model { + let listHeaderText = R.string.localizable.chainAccountsListHeader( + preferredLanguages: localizationManager.selectedLocale.rLanguages + ) + + var sections: [ManualBackupKeyListViewLayout.Sections] = [] + + if !defaultChains.isEmpty { + sections.append(createDefaultChainsSection(for: defaultChains)) + } + + if !customChains.isEmpty { + sections.append(createCustomChainsSection(for: customChains)) + } + + return .init( + listHeaderText: listHeaderText, + accountsSections: sections + ) + } +} + +// MARK: Private + +private extension ManualBackupKeyListViewModelFactory { + func createDefaultChainsSection(for chains: [ChainModel]) -> ManualBackupKeyListViewLayout.Sections { + let defaultChainsHeaderText = R.string.localizable.chainAccountsListDefaultHeader( + preferredLanguages: localizationManager.selectedLocale.rLanguages + ) + + let defaultChainsTitleText = R.string.localizable.chainAccountsListDefaultTitle( + preferredLanguages: localizationManager.selectedLocale.rLanguages + ) + + return .defaultKeys( + .init( + headerText: defaultChainsHeaderText.uppercased(), + accounts: [ + .init( + title: defaultChainsTitleText, + subtitle: formattedString(for: chains) + ) + ] + ) + ) + } + + func createCustomChainsSection(for chains: [ChainModel]) -> ManualBackupKeyListViewLayout.Sections { + let customChainsViewModels = chains + .compactMap { [weak self] chain -> ManualBackupKeyListViewLayout.CustomAccount? in + guard let self else { return .none } + + return ManualBackupKeyListViewLayout.CustomAccount( + network: networkViewModelFactory.createViewModel(from: chain), + chainId: chain.chainId + ) + } + + let customChainsHeaderText = R.string.localizable.chainAccountsListCustomHeader( + preferredLanguages: localizationManager.selectedLocale.rLanguages + ) + + return .customKeys( + .init( + headerText: customChainsHeaderText.uppercased(), + accounts: customChainsViewModels + ) + ) + } + + func formattedString(for defaultChains: [ChainModel]) -> String { + let chainsToMention = defaultChains.count > 1 + ? defaultChains.prefix(2) + : defaultChains.prefix(1) + let restCount = defaultChains.count - chainsToMention.count + let othersString = R.string.localizable.chainAccountsListDefaultSubtitle( + restCount, + preferredLanguages: localizationManager.selectedLocale.rLanguages + ) + + var joinedChains = chainsToMention + .map(\.name) + .joined(with: String.CompoundSeparator.commaSpace) + + return restCount > 0 + ? [joinedChains, othersString].joined(with: .commaSpace) + : joinedChains + } +}