diff --git a/Podfile b/Podfile index b8316f57d2..ac1147fb24 100644 --- a/Podfile +++ b/Podfile @@ -4,6 +4,7 @@ platform :ios, '14.0' abstract_target 'novawalletAll' do use_frameworks! + pod 'DSF_QRCode', '~> 18.0.0' pod 'SubstrateSdk', :git => 'https://github.com/nova-wallet/substrate-sdk-ios.git', :tag => '3.2.2' pod 'SwiftLint' pod 'R.swift', :inhibit_warnings => true diff --git a/Podfile.lock b/Podfile.lock index a8efe01b18..f21b7b1040 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -643,6 +643,9 @@ PODS: - Cuckoo (1.7.1): - Cuckoo/Swift (= 1.7.1) - Cuckoo/Swift (1.7.1) + - DSF_QRCode (18.0.0): + - SwiftImageReadWrite (~> 1.6.1) + - SwiftQRCodeGenerator (~> 2.0.2) - EthereumSignTypedDataUtil (0.1.2): - BigInt (~> 5.0) - CryptoSwift (~> 1.4) @@ -894,7 +897,9 @@ PODS: - SwiftAlgorithms (1.0.0) - SwiftDraw (0.18.0) - SwiftFormat/CLI (0.47.13) + - SwiftImageReadWrite (1.6.1) - SwiftLint (0.43.1) + - SwiftQRCodeGenerator (2.0.2) - SwiftRLP (1.1): - BigInt (~> 5.0) - SwiftyBeaver (1.9.3) @@ -959,6 +964,7 @@ PODS: DEPENDENCIES: - CDMarkdownKit (from `https://github.com/nova-wallet/CDMarkdownKit.git`, tag `2.5.2`) - Cuckoo + - DSF_QRCode (~> 18.0.0) - EthereumSignTypedDataUtil (from `https://github.com/ERussel/EthereumSignTypedDataUtil.git`, tag `0.1.3`) - FirebaseAppCheck - FirebaseAuth @@ -996,6 +1002,7 @@ SPEC REPOS: - BoringSSL-GRPC - CryptoSwift - Cuckoo + - DSF_QRCode - FirebaseAppCheck - FirebaseAppCheckInterop - FirebaseAuth @@ -1031,7 +1038,9 @@ SPEC REPOS: - SwiftAlgorithms - SwiftDraw - SwiftFormat + - SwiftImageReadWrite - SwiftLint + - SwiftQRCodeGenerator - SwiftyBeaver - TweetNacl - Web3Core @@ -1122,6 +1131,7 @@ SPEC CHECKSUMS: CDMarkdownKit: ed78cf1a5c57dbe362054633a0f802f0534531ff CryptoSwift: c4f2debceb38bf44c80659afe009f71e23e4a082 Cuckoo: 9e258d68137c411df47c6390f72901d5276b4f03 + DSF_QRCode: 3fe0acc968ce8588a9b1dabb9557a364472f5aaf EthereumSignTypedDataUtil: ae4e33b21e51ee046a86a65b02843090b8bbd3f9 FirebaseAppCheck: 4bb8047366c2c975583c9eff94235f8f2c5b342d FirebaseAppCheckInterop: e81bdb1cdb82f8e0cef353ba5018a8402682032c @@ -1165,7 +1175,9 @@ SPEC CHECKSUMS: SwiftAlgorithms: 38dda4731d19027fdeee1125f973111bf3386b53 SwiftDraw: f63484562ddd30d9682b5576acc1d98acc2bec8f SwiftFormat: 73573b89257437c550b03d934889725fbf8f75e5 + SwiftImageReadWrite: 69f6521a74fdddbb61b2cc844150ec64de2cb4c7 SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52 + SwiftQRCodeGenerator: cc02fed209335064d0b0dd61b2a0874b9bc6bd5a SwiftRLP: f58417bfceecd45394fc619ccad14cf16e4ae6c1 SwiftyBeaver: 2e8acd6fc90c6d0a27055867a290794926d57c02 TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6 @@ -1176,6 +1188,6 @@ SPEC CHECKSUMS: ZMarkupParser: a92d31ba40695b790f1da5fec98c3d4505341aff ZNSTextAttachment: 1ddd53660a8d3c42dbb716bf6866ffce22c44181 -PODFILE CHECKSUM: 4bc772db3908838fe1eb2aeeed28f5f2a60d65ed +PODFILE CHECKSUM: d8eff34136ac9f5e9008628c43040eaebe7d557d COCOAPODS: 1.15.2 diff --git a/novawallet.xcodeproj/project.pbxproj b/novawallet.xcodeproj/project.pbxproj index 505eff0930..2aa23f82df 100644 --- a/novawallet.xcodeproj/project.pbxproj +++ b/novawallet.xcodeproj/project.pbxproj @@ -932,6 +932,9 @@ 2D1C5D262C25ACE900E2DBDD /* CustomNetworkEditPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1C5D252C25ACE900E2DBDD /* CustomNetworkEditPresenter.swift */; }; 2D1C5D282C25ACF200E2DBDD /* CustomNetworkEditInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1C5D272C25ACF200E2DBDD /* CustomNetworkEditInteractor.swift */; }; 2D1C5D2A2C25AD0200E2DBDD /* CustomNetworkPresenterProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1C5D292C25AD0200E2DBDD /* CustomNetworkPresenterProtocols.swift */; }; + 2D1D66002CD80A4D009C6C2F /* IconRetrieveOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1D65FF2CD80A4D009C6C2F /* IconRetrieveOperationFactory.swift */; }; + 2D1D66022CD82330009C6C2F /* KingfisherIconRetrieveOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1D66012CD82330009C6C2F /* KingfisherIconRetrieveOperationFactory.swift */; }; + 2D1D66062CD92209009C6C2F /* QRDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1D66052CD92209009C6C2F /* QRDisplayView.swift */; }; 2D2F6F522C50E52D005020EF /* VotingCurveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D2F6F512C50E52D005020EF /* VotingCurveTests.swift */; }; 2D32BE122C6A49900047F520 /* ExtrinsicAssetConversionFeeEstimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32BE052C6A49900047F520 /* ExtrinsicAssetConversionFeeEstimator.swift */; }; 2D32BE132C6A49900047F520 /* ExtrinsicAssetConversionFeeInstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D32BE062C6A49900047F520 /* ExtrinsicAssetConversionFeeInstaller.swift */; }; @@ -958,6 +961,7 @@ 2D389B172C875B3200256C7B /* SwipeGovInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D389B162C875B3200256C7B /* SwipeGovInteractor.swift */; }; 2D442CEF2CD0F7DF00A0380F /* AppearanceDepending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D442CEE2CD0F7DF00A0380F /* AppearanceDepending.swift */; }; 2D442CF12CD0F85400A0380F /* IconAppearanceDepending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D442CF02CD0F85400A0380F /* IconAppearanceDepending.swift */; }; + 2D442CF32CD103ED00A0380F /* BarcodeCreationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D442CF22CD103ED00A0380F /* BarcodeCreationError.swift */; }; 2D4F55012CCA60E900B65B76 /* AssetsSearchCollectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4F55002CCA60E900B65B76 /* AssetsSearchCollectionManager.swift */; }; 2D4F55032CCA60F300B65B76 /* AssetsSearchCollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4F55022CCA60F300B65B76 /* AssetsSearchCollectionViewDataSource.swift */; }; 2D4F55052CCA611200B65B76 /* AssetsSearchCollectionViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4F55042CCA611200B65B76 /* AssetsSearchCollectionViewDelegate.swift */; }; @@ -1069,6 +1073,13 @@ 2DAF539E2C1B2AA00076B4B6 /* NodePingOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAF539D2C1B2AA00076B4B6 /* NodePingOperationFactory.swift */; }; 2DB18C8F2C36AB1C00A93C75 /* LightChainsFetchFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB18C8E2C36AB1C00A93C75 /* LightChainsFetchFactory.swift */; }; 2DB18C922C36D1BC00A93C75 /* PreConfiguredChainFetchFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB18C912C36D1BC00A93C75 /* PreConfiguredChainFetchFactory.swift */; }; + 2DBB28852CD2811200DFA0AD /* QRCodeWithLogoFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB28842CD2811200DFA0AD /* QRCodeWithLogoFactory.swift */; }; + 2DBB28872CD2934500DFA0AD /* AssetIconURLFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB28862CD2934500DFA0AD /* AssetIconURLFactory.swift */; }; + 2DBB28892CD42ACB00DFA0AD /* QRCreationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB28882CD42ACB00DFA0AD /* QRCreationOperation.swift */; }; + 2DBB288D2CD42C5200DFA0AD /* QRCreationOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB288C2CD42C5200DFA0AD /* QRCreationOperationFactory.swift */; }; + 2DBB288F2CD42E8D00DFA0AD /* IconType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB288E2CD42E8D00DFA0AD /* IconType.swift */; }; + 2DBB28912CD42ED800DFA0AD /* IconInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB28902CD42ED800DFA0AD /* IconInfo.swift */; }; + 2DBB28932CD432FB00DFA0AD /* CopyAddressPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB28922CD432FB00DFA0AD /* CopyAddressPresentable.swift */; }; 2DC075E32BF24AB400868563 /* CheckBoxIconDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC075E22BF24AB400868563 /* CheckBoxIconDetailsView.swift */; }; 2DC075E92BF4B97900868563 /* BackupAttentionTableTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DC075E82BF4B97900868563 /* BackupAttentionTableTitleView.swift */; }; 2DCC875B2C5820BA0028C3CA /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849013D424A927E2008F705E /* Logger.swift */; }; @@ -3498,8 +3509,8 @@ 84BB3CF3267D24B000676FFE /* UIStackView+Manage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB3CF2267D24B000676FFE /* UIStackView+Manage.swift */; }; 84BB3CF8267D276D00676FFE /* CrowdloanTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB3CF7267D276D00676FFE /* CrowdloanTableViewCell.swift */; }; 84BB3D00267D364D00676FFE /* CompoundOperationWrapper+Dependency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB3CFF267D364D00676FFE /* CompoundOperationWrapper+Dependency.swift */; }; - 84BC703F289DBE6C008A9758 /* QRCreationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BC703E289DBE6C008A9758 /* QRCreationOperation.swift */; }; - 84BC7041289DBF62008A9758 /* QRDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BC7040289DBF62008A9758 /* QRDisplayView.swift */; }; + 84BC703F289DBE6C008A9758 /* QRWithLogoCreationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BC703E289DBE6C008A9758 /* QRWithLogoCreationOperation.swift */; }; + 84BC7041289DBF62008A9758 /* QRWithLogoDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BC7040289DBF62008A9758 /* QRWithLogoDisplayView.swift */; }; 84BC7043289EEF85008A9758 /* NoSigningSupportWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BC7042289EEF85008A9758 /* NoSigningSupportWrapper.swift */; }; 84BC7045289EFF44008A9758 /* TransactionDisplayCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BC7044289EFF44008A9758 /* TransactionDisplayCode.swift */; }; 84BC7047289EFFFA008A9758 /* ChainWalletDisplayAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BC7046289EFFFA008A9758 /* ChainWalletDisplayAddress.swift */; }; @@ -3737,7 +3748,7 @@ 84D9C8F328ADA42F007FB23B /* Data+Chunk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D9C8F228ADA42F007FB23B /* Data+Chunk.swift */; }; 84DA03D12758AA6800E8B326 /* BaseAccountImportWireframe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA03D02758AA6800E8B326 /* BaseAccountImportWireframe.swift */; }; 84DA03D427592FAA00E8B326 /* ChainAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA03D327592FAA00E8B326 /* ChainAccountView.swift */; }; - 84DA03D62759341200E8B326 /* ChainAccountControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA03D52759341200E8B326 /* ChainAccountControl.swift */; }; + 84DA03D62759341200E8B326 /* AccountAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA03D52759341200E8B326 /* AccountAddressView.swift */; }; 84DA03D82759362100E8B326 /* ChainAccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA03D72759362100E8B326 /* ChainAccountViewModel.swift */; }; 84DA03DB275A31B500E8B326 /* DAppListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA03DA275A31B500E8B326 /* DAppListHeaderView.swift */; }; 84DA3B1224C6D29100B5E27F /* RuntimeVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DA3B1124C6D29100B5E27F /* RuntimeVersion.swift */; }; @@ -4310,7 +4321,6 @@ 88E74E8829538BF8008031A3 /* AssetReceiveInteractorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E74E8729538BF8008031A3 /* AssetReceiveInteractorError.swift */; }; 88E74E8A29538C1F008031A3 /* NovaAccountShareFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E74E8929538C1F008031A3 /* NovaAccountShareFactoryProtocol.swift */; }; 88E74E8C29538C36008031A3 /* QRCodeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E74E8B29538C36008031A3 /* QRCodeInfo.swift */; }; - 88E74E8E29539E18008031A3 /* QRCreationOperationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E74E8D29539E18008031A3 /* QRCreationOperationFactory.swift */; }; 88E8CF5E28E3789600C90112 /* CrowdloanEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E8CF5D28E3789600C90112 /* CrowdloanEmptyView.swift */; }; 88F19DDE28D8D0A100F6E459 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F19DDD28D8D0A100F6E459 /* Either.swift */; }; 88F19DE028D8D0F600F6E459 /* LoadableViewModelState+Addition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88F19DDF28D8D0F600F6E459 /* LoadableViewModelState+Addition.swift */; }; @@ -6109,6 +6119,9 @@ 2D1C5D252C25ACE900E2DBDD /* CustomNetworkEditPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNetworkEditPresenter.swift; sourceTree = ""; }; 2D1C5D272C25ACF200E2DBDD /* CustomNetworkEditInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNetworkEditInteractor.swift; sourceTree = ""; }; 2D1C5D292C25AD0200E2DBDD /* CustomNetworkPresenterProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNetworkPresenterProtocols.swift; sourceTree = ""; }; + 2D1D65FF2CD80A4D009C6C2F /* IconRetrieveOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconRetrieveOperationFactory.swift; sourceTree = ""; }; + 2D1D66012CD82330009C6C2F /* KingfisherIconRetrieveOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KingfisherIconRetrieveOperationFactory.swift; sourceTree = ""; }; + 2D1D66052CD92209009C6C2F /* QRDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRDisplayView.swift; sourceTree = ""; }; 2D2F6F512C50E52D005020EF /* VotingCurveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VotingCurveTests.swift; sourceTree = ""; }; 2D32BE052C6A49900047F520 /* ExtrinsicAssetConversionFeeEstimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtrinsicAssetConversionFeeEstimator.swift; sourceTree = ""; }; 2D32BE062C6A49900047F520 /* ExtrinsicAssetConversionFeeInstaller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtrinsicAssetConversionFeeInstaller.swift; sourceTree = ""; }; @@ -6135,6 +6148,7 @@ 2D389B162C875B3200256C7B /* SwipeGovInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeGovInteractor.swift; sourceTree = ""; }; 2D442CEE2CD0F7DF00A0380F /* AppearanceDepending.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceDepending.swift; sourceTree = ""; }; 2D442CF02CD0F85400A0380F /* IconAppearanceDepending.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconAppearanceDepending.swift; sourceTree = ""; }; + 2D442CF22CD103ED00A0380F /* BarcodeCreationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarcodeCreationError.swift; sourceTree = ""; }; 2D4F55002CCA60E900B65B76 /* AssetsSearchCollectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetsSearchCollectionManager.swift; sourceTree = ""; }; 2D4F55022CCA60F300B65B76 /* AssetsSearchCollectionViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetsSearchCollectionViewDataSource.swift; sourceTree = ""; }; 2D4F55042CCA611200B65B76 /* AssetsSearchCollectionViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetsSearchCollectionViewDelegate.swift; sourceTree = ""; }; @@ -6245,6 +6259,13 @@ 2DAF539D2C1B2AA00076B4B6 /* NodePingOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodePingOperationFactory.swift; sourceTree = ""; }; 2DB18C8E2C36AB1C00A93C75 /* LightChainsFetchFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightChainsFetchFactory.swift; sourceTree = ""; }; 2DB18C912C36D1BC00A93C75 /* PreConfiguredChainFetchFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreConfiguredChainFetchFactory.swift; sourceTree = ""; }; + 2DBB28842CD2811200DFA0AD /* QRCodeWithLogoFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeWithLogoFactory.swift; sourceTree = ""; }; + 2DBB28862CD2934500DFA0AD /* AssetIconURLFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetIconURLFactory.swift; sourceTree = ""; }; + 2DBB28882CD42ACB00DFA0AD /* QRCreationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCreationOperation.swift; sourceTree = ""; }; + 2DBB288C2CD42C5200DFA0AD /* QRCreationOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCreationOperationFactory.swift; sourceTree = ""; }; + 2DBB288E2CD42E8D00DFA0AD /* IconType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconType.swift; sourceTree = ""; }; + 2DBB28902CD42ED800DFA0AD /* IconInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconInfo.swift; sourceTree = ""; }; + 2DBB28922CD432FB00DFA0AD /* CopyAddressPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyAddressPresentable.swift; sourceTree = ""; }; 2DC075E22BF24AB400868563 /* CheckBoxIconDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxIconDetailsView.swift; sourceTree = ""; }; 2DC075E82BF4B97900868563 /* BackupAttentionTableTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupAttentionTableTitleView.swift; sourceTree = ""; }; 2DC70DB97D2E9350022A899B /* TokensManagePresenter.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TokensManagePresenter.swift; sourceTree = ""; }; @@ -8621,8 +8642,8 @@ 84BB3CF2267D24B000676FFE /* UIStackView+Manage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackView+Manage.swift"; sourceTree = ""; }; 84BB3CF7267D276D00676FFE /* CrowdloanTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrowdloanTableViewCell.swift; sourceTree = ""; }; 84BB3CFF267D364D00676FFE /* CompoundOperationWrapper+Dependency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CompoundOperationWrapper+Dependency.swift"; sourceTree = ""; }; - 84BC703E289DBE6C008A9758 /* QRCreationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCreationOperation.swift; sourceTree = ""; }; - 84BC7040289DBF62008A9758 /* QRDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRDisplayView.swift; sourceTree = ""; }; + 84BC703E289DBE6C008A9758 /* QRWithLogoCreationOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRWithLogoCreationOperation.swift; sourceTree = ""; }; + 84BC7040289DBF62008A9758 /* QRWithLogoDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRWithLogoDisplayView.swift; sourceTree = ""; }; 84BC7042289EEF85008A9758 /* NoSigningSupportWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoSigningSupportWrapper.swift; sourceTree = ""; }; 84BC7044289EFF44008A9758 /* TransactionDisplayCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionDisplayCode.swift; sourceTree = ""; }; 84BC7046289EFFFA008A9758 /* ChainWalletDisplayAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainWalletDisplayAddress.swift; sourceTree = ""; }; @@ -8864,7 +8885,7 @@ 84D9C8F228ADA42F007FB23B /* Data+Chunk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Chunk.swift"; sourceTree = ""; }; 84DA03D02758AA6800E8B326 /* BaseAccountImportWireframe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAccountImportWireframe.swift; sourceTree = ""; }; 84DA03D327592FAA00E8B326 /* ChainAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainAccountView.swift; sourceTree = ""; }; - 84DA03D52759341200E8B326 /* ChainAccountControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainAccountControl.swift; sourceTree = ""; }; + 84DA03D52759341200E8B326 /* AccountAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountAddressView.swift; sourceTree = ""; }; 84DA03D72759362100E8B326 /* ChainAccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainAccountViewModel.swift; sourceTree = ""; }; 84DA03DA275A31B500E8B326 /* DAppListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAppListHeaderView.swift; sourceTree = ""; }; 84DA3B1124C6D29100B5E27F /* RuntimeVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeVersion.swift; sourceTree = ""; }; @@ -9445,7 +9466,6 @@ 88E74E8729538BF8008031A3 /* AssetReceiveInteractorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetReceiveInteractorError.swift; sourceTree = ""; }; 88E74E8929538C1F008031A3 /* NovaAccountShareFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NovaAccountShareFactoryProtocol.swift; sourceTree = ""; }; 88E74E8B29538C36008031A3 /* QRCodeInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeInfo.swift; sourceTree = ""; }; - 88E74E8D29539E18008031A3 /* QRCreationOperationFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCreationOperationFactory.swift; sourceTree = ""; }; 88E8CF5D28E3789600C90112 /* CrowdloanEmptyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrowdloanEmptyView.swift; sourceTree = ""; }; 88F19DDD28D8D0A100F6E459 /* Either.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = ""; }; 88F19DDF28D8D0F600F6E459 /* LoadableViewModelState+Addition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoadableViewModelState+Addition.swift"; sourceTree = ""; }; @@ -12482,6 +12502,15 @@ path = Interactor; sourceTree = ""; }; + 2D1D65FE2CD80A33009C6C2F /* IconRetrieve */ = { + isa = PBXGroup; + children = ( + 2D1D65FF2CD80A4D009C6C2F /* IconRetrieveOperationFactory.swift */, + 2D1D66012CD82330009C6C2F /* KingfisherIconRetrieveOperationFactory.swift */, + ); + path = IconRetrieve; + sourceTree = ""; + }; 2D32BE112C6A49900047F520 /* FeeManaging */ = { isa = PBXGroup; children = ( @@ -12540,6 +12569,14 @@ path = Appearance; sourceTree = ""; }; + 2D442CF42CD182F600A0380F /* View */ = { + isa = PBXGroup; + children = ( + 84DA03D52759341200E8B326 /* AccountAddressView.swift */, + ); + path = View; + sourceTree = ""; + }; 2D4F54FF2CCA60C500B65B76 /* AssetsSearchCollectionManager */ = { isa = PBXGroup; children = ( @@ -12894,6 +12931,28 @@ path = View; sourceTree = ""; }; + 2DBB288A2CD42B5100DFA0AD /* Logo */ = { + isa = PBXGroup; + children = ( + 84BC7040289DBF62008A9758 /* QRWithLogoDisplayView.swift */, + 84BC703E289DBE6C008A9758 /* QRWithLogoCreationOperation.swift */, + 2DBB28842CD2811200DFA0AD /* QRCodeWithLogoFactory.swift */, + 2DBB288E2CD42E8D00DFA0AD /* IconType.swift */, + 2DBB28902CD42ED800DFA0AD /* IconInfo.swift */, + ); + path = Logo; + sourceTree = ""; + }; + 2DBB288B2CD42B6800DFA0AD /* NoLogo */ = { + isa = PBXGroup; + children = ( + 2DBB28882CD42ACB00DFA0AD /* QRCreationOperation.swift */, + 2DBB288C2CD42C5200DFA0AD /* QRCreationOperationFactory.swift */, + 2D1D66052CD92209009C6C2F /* QRDisplayView.swift */, + ); + path = NoLogo; + sourceTree = ""; + }; 2DC075E12BF249F000868563 /* View */ = { isa = PBXGroup; children = ( @@ -17565,6 +17624,7 @@ 849013D124A92686008F705E /* Common */ = { isa = PBXGroup; children = ( + 2D1D65FE2CD80A33009C6C2F /* IconRetrieve */, 0CCDB2E92B74DA33007BC5D6 /* Graphs */, 0C9C642B2A8CE2D4004DC078 /* SystemAccounts */, 0C463FCE2A592ACD003E71C9 /* Effects */, @@ -17836,6 +17896,7 @@ 842D1E8324D197C900C30A7A /* KeyboardAdoptable.swift */, 84754C842510A1A400854599 /* ModalAlertPresenting.swift */, 849DEC6025EE13CE00C64C19 /* AddressOptionsPresentable.swift */, + 2DBB28922CD432FB00DFA0AD /* CopyAddressPresentable.swift */, 847DD8DB26034B99003DE053 /* LocalizableViewProtocol.swift */, 842348E82614F6EA002127AF /* SkeletonLoadable.swift */, 2D6AED5A2C05C3BB001A0A15 /* MnemonicFetching.swift */, @@ -19782,8 +19843,9 @@ 84BC703D289DBE48008A9758 /* QRCreation */ = { isa = PBXGroup; children = ( - 84BC703E289DBE6C008A9758 /* QRCreationOperation.swift */, - 84BC7040289DBF62008A9758 /* QRDisplayView.swift */, + 2DBB288A2CD42B5100DFA0AD /* Logo */, + 2DBB288B2CD42B6800DFA0AD /* NoLogo */, + 2D442CF22CD103ED00A0380F /* BarcodeCreationError.swift */, ); path = QRCreation; sourceTree = ""; @@ -20489,7 +20551,6 @@ isa = PBXGroup; children = ( 84DA03D327592FAA00E8B326 /* ChainAccountView.swift */, - 84DA03D52759341200E8B326 /* ChainAccountControl.swift */, 84DA03D72759362100E8B326 /* ChainAccountViewModel.swift */, 846B748E28B41C6B00C39B93 /* ChainAccountTableViewCell.swift */, ); @@ -21870,6 +21931,7 @@ children = ( 84ABB3302A16150400B5E95A /* AccountShareFactory.swift */, 84ABB32F2A16150400B5E95A /* AssetReceiveInfo.swift */, + 2DBB28862CD2934500DFA0AD /* AssetIconURLFactory.swift */, 84ABB3312A16150400B5E95A /* LegacyWalletQRCoderFactory.swift */, 88E74E8729538BF8008031A3 /* AssetReceiveInteractorError.swift */, 88E74E8B29538C36008031A3 /* QRCodeInfo.swift */, @@ -23330,6 +23392,7 @@ EC1A579A3747EB16688DAEBF /* AssetReceive */ = { isa = PBXGroup; children = ( + 2D442CF42CD182F600A0380F /* View */, 88E74E8629538BEB008031A3 /* Model */, 47042BBA5082B1EC1D017B56 /* AssetReceiveProtocols.swift */, 6C1179A25C22AF0875A1ADCD /* AssetReceiveWireframe.swift */, @@ -23339,7 +23402,6 @@ 73DE88ED69EF6E4F4F6612D8 /* AssetReceiveViewLayout.swift */, 1C5C524FD0E3E7E5113F325D /* AssetReceiveViewFactory.swift */, 88E74E8929538C1F008031A3 /* NovaAccountShareFactoryProtocol.swift */, - 88E74E8D29539E18008031A3 /* QRCreationOperationFactory.swift */, ); path = AssetReceive; sourceTree = ""; @@ -24871,6 +24933,7 @@ 8887813C28B62B0A00E7290F /* FlexibleSpaceView.swift in Sources */, 0C13D3132A80D06B0054BB6F /* StakingRecommendationValidationFactory.swift in Sources */, 77A0B2ED2A3B7F3300CBF653 /* DAppCollectionViewCell.swift in Sources */, + 2D1D66022CD82330009C6C2F /* KingfisherIconRetrieveOperationFactory.swift in Sources */, 77DB70EE2B709E4300288F26 /* Web3AlertRemoteModel.swift in Sources */, 8471538D2653B29100CB91D8 /* ChangeRewardDestinationViewModelFactory.swift in Sources */, 84BE207825E7D62100B4748C /* ActiveEraInfo.swift in Sources */, @@ -25667,6 +25730,7 @@ 0C3205E82A898195002EB914 /* EvmValidationErrorPresentable.swift in Sources */, 84CE69E82566750D00559427 /* ByteLengthProcessor.swift in Sources */, 8499FEDA27BFDB8C00712589 /* NFTStreamableSource.swift in Sources */, + 2DBB28852CD2811200DFA0AD /* QRCodeWithLogoFactory.swift in Sources */, 0CE360212C33F398006A6CE4 /* GraphModel+Dijkstra.swift in Sources */, 849976BE27B269A400B14A6C /* DAppTransports.swift in Sources */, 842876A724AE049B00D91AD8 /* SelectionListProtocols.swift in Sources */, @@ -25887,10 +25951,12 @@ 8418167528251BBC0007684A /* StorageListSyncResult.swift in Sources */, 84DD261929ACA9880032A598 /* BagList+CodingPath.swift in Sources */, 8465DA3F298EEC6C00C7CFF1 /* GovernanceAddDelegationTracksInteractor.swift in Sources */, + 2DBB288D2CD42C5200DFA0AD /* QRCreationOperationFactory.swift in Sources */, 845B823429C8FF0700D187CB /* EtherscanNativeOperationFactory.swift in Sources */, 88D997B028ABC8C0006135A5 /* YourContributionsTableViewCell.swift in Sources */, 849ABE5B2627739400011A2A /* ListReducing.swift in Sources */, 0C7CF4CC2BD374930015DD45 /* CloudBackupFileManaging.swift in Sources */, + 2DBB28912CD42ED800DFA0AD /* IconInfo.swift in Sources */, 88C5F07C297EE79C001CCADE /* Release.swift in Sources */, 845B89222959620000EE25B0 /* SecurityLayerPresenter.swift in Sources */, 849E07F4284A04F400DE0440 /* ParaStkAccountSubscribeHandlingFactory.swift in Sources */, @@ -26009,7 +26075,7 @@ 8425EA9025EA7E5800C307C9 /* ElectedValidatorInfo.swift in Sources */, 0C500B222B05102900ABEE70 /* AssetTxPaymentPallet.swift in Sources */, 84216FCF28264A1E00479375 /* ParaStakingRewardCalculatorEngine.swift in Sources */, - 84DA03D62759341200E8B326 /* ChainAccountControl.swift in Sources */, + 84DA03D62759341200E8B326 /* AccountAddressView.swift in Sources */, 84329ED02832461D0020BC1C /* TimeInterval+Localization.swift in Sources */, 846AF8442525BE0100868F37 /* Price.swift in Sources */, 84468A072866530100BCBE00 /* AssetStorageInfoOperationFactory.swift in Sources */, @@ -26605,7 +26671,7 @@ 77A6F5CB2A30BD71004AFD1A /* BaseKiltTransferAssetRecipientRepository.swift in Sources */, 8442003628EA9DF100C49C4A /* VoteViewFactory.swift in Sources */, 770F57882A8A2CE0005FD7C1 /* StakingSelectPoolViewStyles.swift in Sources */, - 84BC7041289DBF62008A9758 /* QRDisplayView.swift in Sources */, + 84BC7041289DBF62008A9758 /* QRWithLogoDisplayView.swift in Sources */, 2AC7BC7E2731604C001D99B0 /* ChainAccountChanged.swift in Sources */, 845B07F329159C15005785D3 /* Democracy+CodingPath.swift in Sources */, 0CD846152BDA525B0026B5CA /* CloudBackupMessageSheetViewFactory.swift in Sources */, @@ -26734,6 +26800,7 @@ 848B3000286EDE3800465BA2 /* ParaIdOperationFactory.swift in Sources */, 84F47D4B2666EF1C00F7647A /* KaruraStatementData.swift in Sources */, 0CEB6B4D2CA6676900609DC2 /* VoteCardPanState.swift in Sources */, + 2DBB28932CD432FB00DFA0AD /* CopyAddressPresentable.swift in Sources */, 84D8753A28EB0A93004065BD /* GovernanceChainSettings.swift in Sources */, 8443FE24255586230092893D /* ExportMnemonicConfirmProtocols.swift in Sources */, 77DF404B2B7FFBA400ABDB53 /* ChainNotificationsSettingsProtocols.swift in Sources */, @@ -26817,7 +26884,7 @@ 84E25BE427E5F71900290BF1 /* AccountFieldStateViewModel.swift in Sources */, 84452F4E25D5BB1C00F47EC5 /* RuntimeCoderFactory.swift in Sources */, 773A375C2B3B5901006AC4AA /* ProxyMessageSheetPresenter.swift in Sources */, - 84BC703F289DBE6C008A9758 /* QRCreationOperation.swift in Sources */, + 84BC703F289DBE6C008A9758 /* QRWithLogoCreationOperation.swift in Sources */, 84452A6025D037AE00F47EC5 /* ChainStorage+Decodable.swift in Sources */, 8849AD6E29C4690F00F4F7FF /* String+CheckLength.swift in Sources */, 847C963525534E41002D288F /* UIFactory.swift in Sources */, @@ -27140,6 +27207,7 @@ 8463A73825E3AA47003B8160 /* AccountInfo.swift in Sources */, ABA3D873BBECB7F4BD670872 /* ExportSeedPresenter.swift in Sources */, 8476D39B27F43E30004D9A7A /* PhishingSiteVerifier.swift in Sources */, + 2D1D66002CD80A4D009C6C2F /* IconRetrieveOperationFactory.swift in Sources */, 0C7CF4D62BD4E5350015DD45 /* KeychainProxy.swift in Sources */, 9A6A55297F41DAE45071BF57 /* ExportSeedInteractor.swift in Sources */, 0CE933C02C068BF2000F3EFE /* AddAccount+ImportOptionsPresenter.swift in Sources */, @@ -27425,6 +27493,7 @@ 7725062C2A1C99DB00E653DB /* ReferendumSearchViewLayout.swift in Sources */, 84466B3528B6731B00FA1E0D /* LedgerSigningWrapper.swift in Sources */, 0C38B4FB2B7A5DB500882A8B /* ExtrinsicProcessor+HydraSwapMatching.swift in Sources */, + 2DBB288F2CD42E8D00DFA0AD /* IconType.swift in Sources */, F4223ED127329767003D8E4E /* AcalaTransferRequest.swift in Sources */, 8473F4B8282BFFF8007CC55A /* StakingRelaychainInteractor+Subscription.swift in Sources */, 841E2E5027381B2A00F250C1 /* AccountInfoSubscriptionHandlingFactory.swift in Sources */, @@ -27446,6 +27515,7 @@ 0CEB6B3A2CA4597600609DC2 /* Multilocation+AccountId.swift in Sources */, 84468A0F2867ADA400BCBE00 /* XcmTransferRequest.swift in Sources */, 848DAF022822AA1100D56F55 /* ParachainStakingCollatorService.swift in Sources */, + 2DBB28892CD42ACB00DFA0AD /* QRCreationOperation.swift in Sources */, F4FDA0FD26A57860003D753B /* EraCountdown.swift in Sources */, 844CB56C26F9C5C900396E13 /* WalletLocalSubscriptionHandler.swift in Sources */, AEA0C8B6267BABCC00F9666F /* SelectedValidatorListViewModel.swift in Sources */, @@ -27649,6 +27719,7 @@ 2D008A682CC57BFB00BFEE99 /* AssetListCollectionViewDelegate.swift in Sources */, 84FD91B029B08F7A007851D3 /* BaseTableSearchViewController.swift in Sources */, 1BEADE77C6236CB3BF719A47 /* CrowdloanContributionSetupViewFactory.swift in Sources */, + 2DBB28872CD2934500DFA0AD /* AssetIconURLFactory.swift in Sources */, 7719019F2AE6C9DC00D9C918 /* SwapElementView.swift in Sources */, 845353BD2886EB1A006C871A /* ButtonLargeControl.swift in Sources */, 8466781827EC9CDA007935D3 /* PersistTransferDetails.swift in Sources */, @@ -28435,11 +28506,13 @@ 84D184EA2A04D9980060C1BD /* StackStatusCell.swift in Sources */, 21B297239CC294307EF20B58 /* ParaStkYieldBoostSetupInteractor.swift in Sources */, 0CCE25212A44306200286709 /* TransactionHistoryPhishingFilter.swift in Sources */, + 2D1D66062CD92209009C6C2F /* QRDisplayView.swift in Sources */, 77A6F5C62A2F17BE004AFD1A /* SendAssetOperationPresenter.swift in Sources */, 0C495B902B4290A900B8D339 /* Proxy+Call.swift in Sources */, 846DA55B2A20A56D006CD6C1 /* OffchainMultistakingUpdateService.swift in Sources */, 0C3205CA2A895489002EB914 /* EvmGasLimitProvider.swift in Sources */, 7764E2772BA43DA1002F54AA /* SettingsSectionFooterView.swift in Sources */, + 2D442CF32CD103ED00A0380F /* BarcodeCreationError.swift in Sources */, AE180C8B30831C9BAA39763A /* ParaStkYieldBoostSetupViewController.swift in Sources */, 8824D4222902D92F0022D778 /* ReferendumFullDetailsInteractor.swift in Sources */, 88B1862A28EF30A600D49854 /* YourVoteView.swift in Sources */, @@ -28497,7 +28570,6 @@ 89724EA9F732D0C967253597 /* ReferendumOnChainVotersViewFactory.swift in Sources */, 1F496969FEE3E160BABDAC66 /* ReferendumVoteSetupProtocols.swift in Sources */, F1BED07F67119E1BD052952A /* ReferendumVoteSetupWireframe.swift in Sources */, - 88E74E8E29539E18008031A3 /* QRCreationOperationFactory.swift in Sources */, 04D86D5341406305E60F6D18 /* ReferendumVoteSetupInteractor.swift in Sources */, 2CEFF4C2574F0AABE0E9BF89 /* BaseReferendumVoteSetupViewController.swift in Sources */, 0C59E8CF2AA5D744001E11F3 /* PooledBalanceUpdatingState.swift in Sources */, diff --git a/novawallet/Assets.xcassets/colors/text/colorTextPrimaryOnWhite.colorset/Contents.json b/novawallet/Assets.xcassets/colors/text/colorTextPrimaryOnWhite.colorset/Contents.json new file mode 100644 index 0000000000..7e8f38fe3b --- /dev/null +++ b/novawallet/Assets.xcassets/colors/text/colorTextPrimaryOnWhite.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x00", + "red" : "0x00" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/novawallet/Assets.xcassets/colors/text/colorTextSecondaryOnWhite.colorset/Contents.json b/novawallet/Assets.xcassets/colors/text/colorTextSecondaryOnWhite.colorset/Contents.json new file mode 100644 index 0000000000..ce333dda06 --- /dev/null +++ b/novawallet/Assets.xcassets/colors/text/colorTextSecondaryOnWhite.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.560", + "blue" : "0x00", + "green" : "0x00", + "red" : "0x00" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/novawallet/Common/Extension/UIKit/RoundedView+Styles.swift b/novawallet/Common/Extension/UIKit/RoundedView+Styles.swift index 7a8d1db8e0..3972d57798 100644 --- a/novawallet/Common/Extension/UIKit/RoundedView+Styles.swift +++ b/novawallet/Common/Extension/UIKit/RoundedView+Styles.swift @@ -25,6 +25,7 @@ extension RoundedView { func applyControlBackgroundStyle() { strokeColor = R.color.colorContainerBorder()! highlightedStrokeColor = .clear + shadowOpacity = .zero fillColor = .clear highlightedFillColor = R.color.colorCellBackgroundPressed()! } diff --git a/novawallet/Common/Extension/UIKit/Style/UILabel+Style.swift b/novawallet/Common/Extension/UIKit/Style/UILabel+Style.swift index a8a2712ce9..160c121009 100644 --- a/novawallet/Common/Extension/UIKit/Style/UILabel+Style.swift +++ b/novawallet/Common/Extension/UIKit/Style/UILabel+Style.swift @@ -31,6 +31,11 @@ extension UILabel.Style { font: .regularFootnote ) + static let footnoteSecondaryOnWhite = UILabel.Style( + textColor: R.color.colorTextSecondaryOnWhite(), + font: .regularFootnote + ) + static let semiboldSubhedlineSecondary = UILabel.Style( textColor: R.color.colorTextSecondary(), font: .semiBoldSubheadline @@ -151,6 +156,11 @@ extension UILabel.Style { font: .regularSubheadline ) + static let regularSubhedlinePrimaryOnWhite = UILabel.Style( + textColor: R.color.colorTextPrimaryOnWhite(), + font: .regularSubheadline + ) + static let regularSubhedlineInactive = UILabel.Style( textColor: R.color.colorIconInactive(), font: .regularSubheadline diff --git a/novawallet/Common/Extension/UIKit/UIImage+Drawing.swift b/novawallet/Common/Extension/UIKit/UIImage+Drawing.swift index a4e4a4b43c..bef402c07c 100644 --- a/novawallet/Common/Extension/UIKit/UIImage+Drawing.swift +++ b/novawallet/Common/Extension/UIKit/UIImage+Drawing.swift @@ -1,6 +1,11 @@ import UIKit extension UIImage { + enum Shape { + case rectangle + case circle + } + static func background( from color: UIColor, size: CGSize = CGSize(width: 1.0, height: 1.0), @@ -25,6 +30,29 @@ extension UIImage { return image } + func redrawWithBackground( + color: UIColor, + shape: Shape = .rectangle + ) -> UIImage { + let renderer = UIGraphicsImageRenderer(size: size, format: UIGraphicsImageRendererFormat.default()) + + return renderer.image { context in + let rect = CGRect(origin: .zero, size: size) + + color.setFill() + + switch shape { + case .rectangle: + context.fill(rect) + + case .circle: + context.cgContext.fillEllipse(in: rect) + } + + self.draw(in: rect) + } + } + func crop(targetSize: CGSize, cornerRadius: CGFloat, contentScale: CGFloat) -> UIImage? { guard size.width > 0, size.height > 0 else { return nil diff --git a/novawallet/Common/IconRetrieve/IconRetrieveOperationFactory.swift b/novawallet/Common/IconRetrieve/IconRetrieveOperationFactory.swift new file mode 100644 index 0000000000..3ecbc96961 --- /dev/null +++ b/novawallet/Common/IconRetrieve/IconRetrieveOperationFactory.swift @@ -0,0 +1,18 @@ +import Foundation +import Kingfisher +import Operation_iOS + +protocol IconRetrieveOperationFactoryProtocol { + func checkCacheOperation(using cacheKey: String) -> ClosureOperation + + func downloadImageOperation( + using logoInfo: IconInfo + ) -> AsyncClosureOperation + + func retrieveImageOperation(using cacheKey: String) -> AsyncClosureOperation +} + +enum ImageRetrievingError: Error { + case logoDownloadError + case logoRetrievingError +} diff --git a/novawallet/Common/IconRetrieve/KingfisherIconRetrieveOperationFactory.swift b/novawallet/Common/IconRetrieve/KingfisherIconRetrieveOperationFactory.swift new file mode 100644 index 0000000000..afffceeb08 --- /dev/null +++ b/novawallet/Common/IconRetrieve/KingfisherIconRetrieveOperationFactory.swift @@ -0,0 +1,111 @@ +import Foundation +import Kingfisher +import Operation_iOS + +struct KingfisherIconRetrieveOperationFactory { + let imageManager: KingfisherManager + let operationQueue: OperationQueue + + init( + imageManager: KingfisherManager = KingfisherManager.shared, + operationQueue: OperationQueue + ) { + self.imageManager = imageManager + self.operationQueue = operationQueue + } +} + +// MARK: IconRetrieveOperationFactoryProtocol + +extension KingfisherIconRetrieveOperationFactory: IconRetrieveOperationFactoryProtocol { + func checkCacheOperation(using cacheKey: String) -> Operation_iOS.ClosureOperation { + let cache = imageManager.cache + + return ClosureOperation { + let cachedType = cache.imageCachedType(forKey: cacheKey) + + return cachedType.cached + } + } + + func downloadImageOperation(using iconInfo: IconInfo) -> Operation_iOS.AsyncClosureOperation { + let downloader = imageManager.downloader + + return AsyncClosureOperation { resultClosure in + let scale = UIScreen.main.scale + + let scaledSize = CGSize( + width: iconInfo.size.width * scale, + height: iconInfo.size.height * scale + ) + + guard let url = iconInfo.url else { + resultClosure(.failure(ImageRetrievingError.logoDownloadError)) + + return + } + + let processor = SVGImageProcessor() |> ResizingImageProcessor(referenceSize: scaledSize) + + let options: KingfisherOptionsInfo = [.processor(processor)] + + downloader.downloadImage(with: url, options: options) { result in + var resultImage: UIImage + + switch result { + case let .success(imageResult) where imageResult.image.cgImage != nil: + resultImage = imageResult.image + default: + resultClosure(.failure(ImageRetrievingError.logoDownloadError)) + + return + } + + let sizeBeforeProcessing = resultImage.size + + if case .remoteTransparent = iconInfo.type { + resultImage = resultImage.redrawWithBackground( + color: R.color.colorTextPrimaryOnWhite()!, + shape: .circle + ) + } + + if let cacheKey = iconInfo.type?.cacheKey { + let cacheOptions = KingfisherOptionsInfo.cacheOptions + + imageManager.cache.store( + resultImage, + forKey: cacheKey, + options: KingfisherParsedOptionsInfo(cacheOptions) + ) + } + + resultClosure(.success(resultImage)) + } + } + } + + func retrieveImageOperation(using cacheKey: String) -> Operation_iOS.AsyncClosureOperation { + let cache = imageManager.cache + + return AsyncClosureOperation { resultClosure in + cache.retrieveImage(forKey: cacheKey) { result in + if + case let .success(cacheResult) = result, + let image = cacheResult.image { + resultClosure(.success(image)) + } else { + resultClosure(.failure(ImageRetrievingError.logoRetrievingError)) + } + } + } + } +} + +extension KingfisherOptionsInfo { + static let cacheOptions: KingfisherOptionsInfo = [ + .cacheSerializer(RemoteImageSerializer.shared), + .cacheOriginalImage, + .diskCacheExpiration(.days(1)) + ] +} diff --git a/novawallet/Common/Protocols/CopyAddressPresentable.swift b/novawallet/Common/Protocols/CopyAddressPresentable.swift new file mode 100644 index 0000000000..443beb4dfe --- /dev/null +++ b/novawallet/Common/Protocols/CopyAddressPresentable.swift @@ -0,0 +1,26 @@ +import UIKit + +protocol CopyAddressPresentable { + func copyAddress( + from view: ControllerBackedProtocol?, + address: String, + locale: Locale + ) +} + +extension CopyAddressPresentable where Self: ModalAlertPresenting { + func copyAddress( + from view: ControllerBackedProtocol?, + address: String, + locale: Locale + ) { + UIPasteboard.general.string = address + + let title = R.string.localizable.commonAddressCoppied(preferredLanguages: locale.rLanguages) + + presentSuccessNotification( + title, + from: view + ) + } +} diff --git a/novawallet/Common/QRCreation/BarcodeCreationError.swift b/novawallet/Common/QRCreation/BarcodeCreationError.swift new file mode 100644 index 0000000000..b73fc80a32 --- /dev/null +++ b/novawallet/Common/QRCreation/BarcodeCreationError.swift @@ -0,0 +1,5 @@ +enum BarcodeCreationError: Error { + case generatorUnavailable + case generatedImageInvalid + case bitmapImageCreationFailed +} diff --git a/novawallet/Common/QRCreation/Logo/IconInfo.swift b/novawallet/Common/QRCreation/Logo/IconInfo.swift new file mode 100644 index 0000000000..bf8f2332f2 --- /dev/null +++ b/novawallet/Common/QRCreation/Logo/IconInfo.swift @@ -0,0 +1,38 @@ +import Foundation +import UIKit + +struct IconInfo { + let size: CGSize + let type: IconType? + + var url: URL? { + type?.url + } + + func byChangingToLocal(_ image: UIImage) -> IconInfo? { + switch type { + case .remoteColored: + IconInfo( + size: size, + type: .localColored(image) + ) + case .remoteTransparent: + IconInfo( + size: size, + type: .localTransparent(image) + ) + default: + IconInfo( + size: size, + type: nil + ) + } + } + + func withNoLogo() -> IconInfo { + IconInfo( + size: size, + type: nil + ) + } +} diff --git a/novawallet/Common/QRCreation/Logo/IconType.swift b/novawallet/Common/QRCreation/Logo/IconType.swift new file mode 100644 index 0000000000..a48db68a9b --- /dev/null +++ b/novawallet/Common/QRCreation/Logo/IconType.swift @@ -0,0 +1,33 @@ +import Foundation +import UIKit + +enum IconType { + case remoteColored(URL?) + case remoteTransparent(URL?) + case localColored(UIImage) + case localTransparent(UIImage) + + var url: URL? { + switch self { + case let .remoteColored(url), let .remoteTransparent(url): + return url + default: + return nil + } + } + + var cacheKey: String? { + guard let url else { return nil } + + return url.absoluteString + String(describing: self) + } + + var image: UIImage? { + switch self { + case let .localColored(image), let .localTransparent(image): + return image + default: + return nil + } + } +} diff --git a/novawallet/Common/QRCreation/Logo/QRCodeWithLogoFactory.swift b/novawallet/Common/QRCreation/Logo/QRCodeWithLogoFactory.swift new file mode 100644 index 0000000000..0cf716db1b --- /dev/null +++ b/novawallet/Common/QRCreation/Logo/QRCodeWithLogoFactory.swift @@ -0,0 +1,257 @@ +import Foundation +import Kingfisher +import Operation_iOS + +protocol QRCodeWithLogoFactoryProtocol { + func createQRCode( + with payload: Data, + logoInfo: IconInfo?, + qrSize: CGSize, + partialResultClosure: @escaping (Result) -> Void, + completion: @escaping (Result) -> Void + ) +} + +final class QRCodeWithLogoFactory { + enum QRCreationResult: Equatable { + case full(UIImage) + case noLogo(UIImage) + + var image: UIImage { + switch self { + case let .full(image), let .noLogo(image): + return image + } + } + } + + let iconRetrievingFactory: IconRetrieveOperationFactoryProtocol + let operationQueue: OperationQueue + let callbackQueue: DispatchQueue + let logger: LoggerProtocol + + init( + iconRetrievingFactory: IconRetrieveOperationFactoryProtocol, + operationQueue: OperationQueue, + callbackQueue: DispatchQueue, + logger: LoggerProtocol + ) { + self.iconRetrievingFactory = iconRetrievingFactory + self.operationQueue = operationQueue + self.callbackQueue = callbackQueue + self.logger = logger + } +} + +// MARK: QRCodeFactoryProtocol + +extension QRCodeWithLogoFactory: QRCodeWithLogoFactoryProtocol { + func createQRCode( + with payload: Data, + logoInfo: IconInfo?, + qrSize: CGSize, + partialResultClosure: @escaping (Result) -> Void, + completion: @escaping (Result) -> Void + ) { + let wrapper = createQRWrapper( + with: payload, + logoInfo: logoInfo, + qrSize: qrSize, + partialResultClosure: partialResultClosure + ) + + novawallet.execute( + wrapper: wrapper, + inOperationQueue: operationQueue, + runningCallbackIn: callbackQueue, + callbackClosure: completion + ) + } +} + +// MARK: Private + +private extension QRCodeWithLogoFactory { + struct QRWrapperResult { + let wrapper: CompoundOperationWrapper + let remoteLogo: Bool + } + + func checkCacheOperation(using logoInfo: IconInfo?) -> BaseOperation { + if let cacheKey = logoInfo?.type?.cacheKey { + iconRetrievingFactory.checkCacheOperation(using: cacheKey) + } else { + .createWithResult(false) + } + } + + func createQRWrapper( + with payload: Data, + logoInfo: IconInfo?, + qrSize: CGSize, + partialResultClosure: @escaping (Result) -> Void + ) -> CompoundOperationWrapper { + var checkCacheOperation = checkCacheOperation(using: logoInfo) + + let wrapper: CompoundOperationWrapper = OperationCombiningService.compoundNonOptionalWrapper( + operationManager: OperationManager(operationQueue: operationQueue) + ) { [weak self] in + guard let self, let logoInfo else { + return .createWithError(BaseOperationError.parentOperationCancelled) + } + + let cached = try checkCacheOperation.extractNoCancellableResultData() + let usesCache: Bool = logoInfo.type?.cacheKey != nil + + return if !cached, usesCache { + createDownloadLogoQRWrapper( + payload: payload, + logoInfo: logoInfo, + qrSize: qrSize, + partialResultClosure: partialResultClosure + ) + } else if let cacheKey = logoInfo.type?.cacheKey { + createCachedLogoQRWrapper( + cacheKey: cacheKey, + payload: payload, + logoInfo: logoInfo, + qrSize: qrSize + ) + } else if let image = logoInfo.type?.image { + createQRResultWrapper( + using: .createWithResult(image), + payload: payload, + logoInfo: logoInfo, + qrSize: qrSize + ) + } else { + createQRResultWrapper( + using: nil, + payload: payload, + logoInfo: logoInfo, + qrSize: qrSize + ) + } + } + + let dependencies = [checkCacheOperation].compactMap { $0 } + + wrapper.addDependency(operations: dependencies) + + return wrapper.insertingHead(operations: dependencies) + } + + func createDownloadLogoQRWrapper( + payload: Data, + logoInfo: IconInfo, + qrSize: CGSize, + partialResultClosure: @escaping (Result) -> Void + ) -> CompoundOperationWrapper { + let logoOperation = iconRetrievingFactory.downloadImageOperation(using: logoInfo) + + let partialQRWrapper = createQRResultWrapper( + using: nil, + payload: payload, + logoInfo: logoInfo, + qrSize: qrSize + ) + + partialQRWrapper.targetOperation.completionBlock = { [weak self] in + guard let self else { return } + + dispatchInQueueWhenPossible(callbackQueue) { + do { + let value = try partialQRWrapper.targetOperation.extractNoCancellableResultData() + partialResultClosure(.success(value)) + } catch { + partialResultClosure(.failure(error)) + } + } + } + + let completeQRWrapper = createQRResultWrapper( + using: logoOperation, + payload: payload, + logoInfo: logoInfo, + qrSize: qrSize + ) + + completeQRWrapper.targetOperation.addDependency(partialQRWrapper.targetOperation) + + return completeQRWrapper.insertingHead(operations: partialQRWrapper.allOperations) + } + + func createCachedLogoQRWrapper( + cacheKey: String, + payload: Data, + logoInfo: IconInfo, + qrSize: CGSize + ) -> CompoundOperationWrapper { + let logoOperation = iconRetrievingFactory.retrieveImageOperation(using: cacheKey) + + return createQRResultWrapper( + using: logoOperation, + payload: payload, + logoInfo: logoInfo, + qrSize: qrSize + ) + } + + func createQRResultWrapper( + using logoOperation: BaseOperation?, + payload: Data, + logoInfo: IconInfo, + qrSize: CGSize + ) -> CompoundOperationWrapper { + var qrImageWrapper: CompoundOperationWrapper + qrImageWrapper = OperationCombiningService.compoundNonOptionalWrapper( + operationManager: OperationManager(operationQueue: operationQueue) + ) { [weak self] in + guard let self else { + return .createWithError(BaseOperationError.parentOperationCancelled) + } + + var updatedLogoInfo: IconInfo? = logoInfo + + if let logoOperation { + do { + let logoImage = try logoOperation.extractNoCancellableResultData() + updatedLogoInfo = logoInfo.byChangingToLocal(logoImage) + } catch { + logger.error(error.localizedDescription) + } + } else { + updatedLogoInfo = updatedLogoInfo?.withNoLogo() + } + + let qrCodeOperation = QRWithLogoCreationOperation( + payload: payload, + qrSize: qrSize, + logoInfo: updatedLogoInfo + ) + + return CompoundOperationWrapper(targetOperation: qrCodeOperation) + } + + if let logoOperation { + qrImageWrapper.addDependency(operations: [logoOperation]) + qrImageWrapper = qrImageWrapper.insertingHead(operations: [logoOperation]) + } + + let resultMappingOperation = ClosureOperation { + let qrImage = try qrImageWrapper.targetOperation.extractNoCancellableResultData() + + let result: QRCreationResult = if logoOperation != nil { + .full(qrImage) + } else { + .noLogo(qrImage) + } + + return result + } + + resultMappingOperation.addDependency(qrImageWrapper.targetOperation) + + return qrImageWrapper.insertingTail(operation: resultMappingOperation) + } +} diff --git a/novawallet/Common/QRCreation/Logo/QRWithLogoCreationOperation.swift b/novawallet/Common/QRCreation/Logo/QRWithLogoCreationOperation.swift new file mode 100644 index 0000000000..6817e2587e --- /dev/null +++ b/novawallet/Common/QRCreation/Logo/QRWithLogoCreationOperation.swift @@ -0,0 +1,86 @@ +import Foundation +import CoreImage +import Operation_iOS +import Kingfisher +import QRCode + +final class QRWithLogoCreationOperation: BaseOperation { + let payloadClosure: () throws -> Data + let qrSize: CGSize + let scale: CGFloat + let logoInfo: IconInfo? + + init( + payload: Data, + qrSize: CGSize, + scale: CGFloat = UIScreen.main.scale, + logoInfo: IconInfo? = nil + ) { + payloadClosure = { payload } + self.qrSize = qrSize + self.scale = scale + self.logoInfo = logoInfo + + super.init() + } + + init( + qrSize: CGSize, + logoInfo: IconInfo? = nil, + scale: CGFloat = UIScreen.main.scale, + payloadClosure: @escaping () throws -> Data + ) { + self.qrSize = qrSize + self.logoInfo = logoInfo + self.scale = scale + self.payloadClosure = payloadClosure + } + + override func performAsync(_ callback: @escaping (Result) -> Void) throws { + let data = try payloadClosure() + let qrDoc = QRCode.Document(data: data) + qrDoc.design.backgroundColor(UIColor.white.cgColor) + qrDoc.design.shape.eye = QRCode.EyeShape.Squircle() + qrDoc.design.shape.onPixels = QRCode.PixelShape.Circle(insetFraction: 0.2) + qrDoc.design.style.onPixels = QRCode.FillStyle.Solid(UIColor.black.cgColor) + qrDoc.design.shape.offPixels = nil + qrDoc.design.style.offPixels = nil + + switch logoInfo?.type { + case let .localColored(image): + qrDoc.logoTemplate = QRCode.LogoTemplate.CircleCenter( + image: image.cgImage!, + inset: 0 + ) + case let .localTransparent(image): + qrDoc.logoTemplate = QRCode.LogoTemplate.CircleCenter( + image: image.cgImage!, + inset: 20 + ) + default: + break + } + + try createQRImage(from: qrDoc, callback) + } + + private func createQRImage( + from qrDoc: QRCode.Document, + _ callback: @escaping (Result) -> Void + ) throws { + let scaledSize = CGSize( + width: qrSize.width * scale, + height: qrSize.height * scale + ) + + guard let cgImage = qrDoc.cgImage(scaledSize) else { + throw BarcodeCreationError.bitmapImageCreationFailed + } + + callback(.success(UIImage(cgImage: cgImage))) + } +} + +extension CGSize { + static let qrLogoSize: CGSize = .init(width: 80, height: 80) +} diff --git a/novawallet/Common/QRCreation/Logo/QRWithLogoDisplayView.swift b/novawallet/Common/QRCreation/Logo/QRWithLogoDisplayView.swift new file mode 100644 index 0000000000..a6a59a25ad --- /dev/null +++ b/novawallet/Common/QRCreation/Logo/QRWithLogoDisplayView.swift @@ -0,0 +1,93 @@ +import UIKit +import SoraUI + +final class QRWithLogoDisplayView: UIView { + let backgroundView: RoundedView = { + let view = RoundedView() + view.fillColor = .white + view.cornerRadius = 24.0 + return view + }() + + let noLogoQRImageView = UIImageView() + let fullQRImageView = UIImageView() + + var viewModel: QRCodeWithLogoFactory.QRCreationResult? + + var contentInsets: CGFloat = 8.0 { + didSet { + [noLogoQRImageView, fullQRImageView].forEach { + $0.snp.updateConstraints { make in + make.edges.equalToSuperview().inset(contentInsets) + } + } + } + } + + var cornerRadius: CGFloat { + get { + backgroundView.cornerRadius + } + + set { + backgroundView.cornerRadius = newValue + } + } + + private let appearAnimator: ViewAnimatorProtocol = FadeAnimator( + from: 0.0, + to: 1.0, + duration: 0.3, + delay: 0.0, + options: [.curveLinear] + ) + + override init(frame: CGRect) { + super.init(frame: frame) + + backgroundColor = .clear + + setupLayout() + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func bind(viewModel: QRCodeWithLogoFactory.QRCreationResult) { + switch viewModel { + case let .noLogo(image): + noLogoQRImageView.image = image + case let .full(image) where self.viewModel != nil && self.viewModel != viewModel: + fullQRImageView.image = image + + appearAnimator.animate( + view: fullQRImageView, + completionBlock: nil + ) + case let .full(image): + fullQRImageView.image = image + } + + self.viewModel = viewModel + } + + private func setupLayout() { + addSubview(backgroundView) + backgroundView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + [ + noLogoQRImageView, + fullQRImageView + ] + .forEach { view in + addSubview(view) + view.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(contentInsets) + } + } + } +} diff --git a/novawallet/Common/QRCreation/QRCreationOperation.swift b/novawallet/Common/QRCreation/NoLogo/QRCreationOperation.swift similarity index 100% rename from novawallet/Common/QRCreation/QRCreationOperation.swift rename to novawallet/Common/QRCreation/NoLogo/QRCreationOperation.swift diff --git a/novawallet/Modules/AssetReceive/QRCreationOperationFactory.swift b/novawallet/Common/QRCreation/NoLogo/QRCreationOperationFactory.swift similarity index 100% rename from novawallet/Modules/AssetReceive/QRCreationOperationFactory.swift rename to novawallet/Common/QRCreation/NoLogo/QRCreationOperationFactory.swift diff --git a/novawallet/Common/QRCreation/QRDisplayView.swift b/novawallet/Common/QRCreation/NoLogo/QRDisplayView.swift similarity index 100% rename from novawallet/Common/QRCreation/QRDisplayView.swift rename to novawallet/Common/QRCreation/NoLogo/QRDisplayView.swift diff --git a/novawallet/Common/View/ChainAccount/ChainAccountControl.swift b/novawallet/Common/View/ChainAccount/ChainAccountControl.swift deleted file mode 100644 index 2e8079bc2c..0000000000 --- a/novawallet/Common/View/ChainAccount/ChainAccountControl.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Foundation -import SoraUI - -final class ChainAccountControl: BackgroundedContentControl { - let chainAccountView = ChainAccountView() - let roundedBackgroundView: RoundedView = { - let roundedView = UIFactory.default.createRoundedBackgroundView() - roundedView.applyControlBackgroundStyle() - return roundedView - }() - - override init(frame: CGRect) { - super.init(frame: frame) - - contentInsets = UIEdgeInsets( - top: 7.0, - left: 18.0, - bottom: 7.0, - right: 18.0 - ) - - backgroundView = roundedBackgroundView - contentView = chainAccountView - contentView?.isUserInteractionEnabled = false - backgroundView?.isUserInteractionEnabled = false - - contentView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] - } - - override func layoutSubviews() { - super.layoutSubviews() - - backgroundView?.frame = bounds - - contentView?.frame = CGRect( - x: bounds.minX + contentInsets.left, - y: bounds.minY + contentInsets.top, - width: bounds.width - contentInsets.left - contentInsets.right, - height: bounds.height - contentInsets.top - contentInsets.bottom - ) - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/novawallet/Common/ViewModel/RemoteImageViewModel.swift b/novawallet/Common/ViewModel/RemoteImageViewModel.swift index 57ec6740f0..0eca17d9ad 100644 --- a/novawallet/Common/ViewModel/RemoteImageViewModel.swift +++ b/novawallet/Common/ViewModel/RemoteImageViewModel.swift @@ -32,11 +32,8 @@ extension RemoteImageViewModel: ImageViewModelProtocol { var options: KingfisherOptionsInfo = [ .processor(processor), - .scaleFactor(UIScreen.main.scale), - .cacheSerializer(RemoteImageSerializer.shared), - .cacheOriginalImage, - .diskCacheExpiration(.days(1)) - ] + .scaleFactor(UIScreen.main.scale) + ] + KingfisherOptionsInfo.cacheOptions if let renderingMode = settings.renderingMode { let imageModifier = RenderingModeImageModifier(renderingMode: renderingMode) diff --git a/novawallet/Modules/AssetReceive/AssetReceiveInteractor.swift b/novawallet/Modules/AssetReceive/AssetReceiveInteractor.swift index 79979249ed..ebece54f2f 100644 --- a/novawallet/Modules/AssetReceive/AssetReceiveInteractor.swift +++ b/novawallet/Modules/AssetReceive/AssetReceiveInteractor.swift @@ -5,9 +5,10 @@ final class AssetReceiveInteractor: AnyCancellableCleaning { weak var presenter: AssetReceiveInteractorOutputProtocol! let chainAsset: ChainAsset + let qrCodeFactory: QRCodeWithLogoFactoryProtocol let qrCoderFactory: NovaWalletQRCoderFactoryProtocol - let qrCodeCreationOperationFactory: QRCreationOperationFactoryProtocol let metaChainAccountResponse: MetaChainAccountResponse + let appearanceFacade: AppearanceFacadeProtocol private let operationQueue: OperationQueue private var currentQRCodeOperation: CancellableCall? @@ -16,13 +17,15 @@ final class AssetReceiveInteractor: AnyCancellableCleaning { metaChainAccountResponse: MetaChainAccountResponse, chainAsset: ChainAsset, qrCoderFactory: NovaWalletQRCoderFactoryProtocol, - qrCodeCreationOperationFactory: QRCreationOperationFactoryProtocol, + qrCodeFactory: QRCodeWithLogoFactoryProtocol, + appearanceFacade: AppearanceFacadeProtocol, operationQueue: OperationQueue ) { self.metaChainAccountResponse = metaChainAccountResponse self.chainAsset = chainAsset self.qrCoderFactory = qrCoderFactory - self.qrCodeCreationOperationFactory = qrCodeCreationOperationFactory + self.qrCodeFactory = qrCodeFactory + self.appearanceFacade = appearanceFacade self.operationQueue = operationQueue } @@ -41,33 +44,35 @@ final class AssetReceiveInteractor: AnyCancellableCleaning { return } - let qrCreationOperation = qrCodeCreationOperationFactory.createOperation( - payload: payload, - qrSize: size + let qrLogoType = AssetIconURLFactory.createQRLogoURL( + for: chainAsset.asset.icon, + iconAppearance: appearanceFacade.selectedIconAppearance ) - qrCreationOperation.completionBlock = { [weak self] in - DispatchQueue.main.async { - guard let self = self, self.currentQRCodeOperation === qrCreationOperation else { - return - } - - self.currentQRCodeOperation = nil + let logoInfo = IconInfo( + size: .qrLogoSize, + type: qrLogoType + ) - do { - let qrImage = try qrCreationOperation.extractNoCancellableResultData() - self.presenter.didReceive(qrCodeInfo: .init( - image: qrImage, - encodingData: receiverInfo - )) - } catch { - self.presenter.didReceive(error: .generatingQRCode) - } + let resultClosure: (Result) -> Void = { [weak self] result in + switch result { + case let .success(qrCode): + self?.presenter.didReceive(qrCodeInfo: .init( + result: qrCode, + encodingData: receiverInfo + )) + case .failure: + self?.presenter.didReceive(error: .generatingQRCode) } } - currentQRCodeOperation = qrCreationOperation - operationQueue.addOperation(qrCreationOperation) + qrCodeFactory.createQRCode( + with: payload, + logoInfo: logoInfo, + qrSize: size, + partialResultClosure: resultClosure, + completion: resultClosure + ) } } diff --git a/novawallet/Modules/AssetReceive/AssetReceivePresenter.swift b/novawallet/Modules/AssetReceive/AssetReceivePresenter.swift index 362e384920..0ba326dbf2 100644 --- a/novawallet/Modules/AssetReceive/AssetReceivePresenter.swift +++ b/novawallet/Modules/AssetReceive/AssetReceivePresenter.swift @@ -9,6 +9,7 @@ final class AssetReceivePresenter { let interactor: AssetReceiveInteractorInputProtocol let iconGenerator: IconGenerating let accountShareFactory: NovaAccountShareFactoryProtocol + let networkViewModelFactory: NetworkViewModelFactoryProtocol let localizationManager: LocalizationManagerProtocol let logger: LoggerProtocol? @@ -17,12 +18,14 @@ final class AssetReceivePresenter { private var account: MetaChainAccountResponse? private var chain: ChainModel? private var qrCodeSize: CGSize? + private var token: String? init( interactor: AssetReceiveInteractorInputProtocol, wireframe: AssetReceiveWireframeProtocol, iconGenerator: IconGenerating, accountShareFactory: NovaAccountShareFactoryProtocol, + networkViewModelFactory: NetworkViewModelFactoryProtocol, localizationManager: LocalizationManagerProtocol, logger: LoggerProtocol? ) { @@ -31,6 +34,7 @@ final class AssetReceivePresenter { self.iconGenerator = iconGenerator self.accountShareFactory = accountShareFactory self.logger = logger + self.networkViewModelFactory = networkViewModelFactory self.localizationManager = localizationManager } @@ -48,6 +52,34 @@ final class AssetReceivePresenter { ) return viewModel } + + private func provideNetwork() { + guard let chain else { return } + + let networkViewModel = networkViewModelFactory.createViewModel(from: chain) + + view?.didReceive(networkViewModel: networkViewModel) + } + + private func provideAddress() { + guard + let account, + let chain, + let token + else { + return + } + let addressViewModel = AccountAddressViewModel( + walletName: account.chainAccount.name, + address: account.chainAccount.toAddress() + ) + + view?.didReceive( + addressViewModel: addressViewModel, + networkName: chain.name, + token: token + ) + } } extension AssetReceivePresenter: AssetReceivePresenterProtocol { @@ -66,7 +98,7 @@ extension AssetReceivePresenter: AssetReceivePresenterProtocol { } let sharingItems = accountShareFactory.createSources( for: qrCodeInfo.encodingData, - qrImage: qrCodeInfo.image + qrImage: qrCodeInfo.result.image ) wireframe.share( @@ -76,17 +108,14 @@ extension AssetReceivePresenter: AssetReceivePresenterProtocol { ) } - func presentAccountOptions() { - guard let view = view, - let address = account?.chainAccount.toAddress(), - let chain = chain else { + func copyAddress() { + guard let address = account?.chainAccount.toAddress() else { return } - wireframe.presentAccountOptions( + wireframe.copyAddress( from: view, address: address, - chain: chain, locale: localizationManager.selectedLocale ) } @@ -100,21 +129,15 @@ extension AssetReceivePresenter: AssetReceiveInteractorOutputProtocol { ) { self.account = account self.chain = chain + self.token = token - let chainAccountViewModel = createChainAccountViewModel( - for: account.chainAccount.accountId, - chain: chain - ) - - view?.didReceive( - chainAccountViewModel: chainAccountViewModel, - token: token - ) + provideNetwork() + provideAddress() } func didReceive(qrCodeInfo: QRCodeInfo) { self.qrCodeInfo = qrCodeInfo - view?.didReceive(qrImage: qrCodeInfo.image) + view?.didReceive(qrResult: qrCodeInfo.result) } func didReceive(error: AssetReceiveInteractorError) { diff --git a/novawallet/Modules/AssetReceive/AssetReceiveProtocols.swift b/novawallet/Modules/AssetReceive/AssetReceiveProtocols.swift index 837df72928..6aebe14252 100644 --- a/novawallet/Modules/AssetReceive/AssetReceiveProtocols.swift +++ b/novawallet/Modules/AssetReceive/AssetReceiveProtocols.swift @@ -1,15 +1,20 @@ import UIKit protocol AssetReceiveViewProtocol: ControllerBackedProtocol { - func didReceive(chainAccountViewModel: ChainAccountViewModel, token: String) - func didReceive(qrImage: UIImage) + func didReceive(networkViewModel: NetworkViewModel) + func didReceive( + addressViewModel: AccountAddressViewModel, + networkName: String, + token: String + ) + func didReceive(qrResult: QRCodeWithLogoFactory.QRCreationResult) } protocol AssetReceivePresenterProtocol: AnyObject { func setup() func set(qrCodeSize: CGSize) func share() - func presentAccountOptions() + func copyAddress() } protocol AssetReceiveInteractorInputProtocol: AnyObject { @@ -23,5 +28,5 @@ protocol AssetReceiveInteractorOutputProtocol: AnyObject { func didReceive(error: AssetReceiveInteractorError) } -protocol AssetReceiveWireframeProtocol: AnyObject, SharingPresentable, AddressOptionsPresentable, - ErrorPresentable, AlertPresentable, CommonRetryable {} +protocol AssetReceiveWireframeProtocol: AnyObject, SharingPresentable, + ErrorPresentable, AlertPresentable, CommonRetryable, ModalAlertPresenting, CopyAddressPresentable {} diff --git a/novawallet/Modules/AssetReceive/AssetReceiveViewController.swift b/novawallet/Modules/AssetReceive/AssetReceiveViewController.swift index 758595b745..e72a426eea 100644 --- a/novawallet/Modules/AssetReceive/AssetReceiveViewController.swift +++ b/novawallet/Modules/AssetReceive/AssetReceiveViewController.swift @@ -6,7 +6,6 @@ final class AssetReceiveViewController: UIViewController, ViewHolder { let presenter: AssetReceivePresenterProtocol private var cachedBoundsWidth: CGFloat? - private var token: String = "" init( presenter: AssetReceivePresenterProtocol, @@ -47,43 +46,86 @@ final class AssetReceiveViewController: UIViewController, ViewHolder { private func setupLocalization() { let languages = selectedLocale.rLanguages - rootView.titleLabel.text = R.string.localizable.walletReceiveDescription(preferredLanguages: languages) + rootView.shareButton.imageWithTitleView?.title = R.string.localizable.walletReceiveShareTitle( preferredLanguages: languages ) - update(token: token) + rootView.accountAddressView.copyButton.imageWithTitleView?.title = R.string.localizable.commonCopyAddress( + preferredLanguages: languages + ).capitalized } private func setupHandlers() { - rootView.shareButton.addTarget(self, action: #selector(didTapShare), for: .touchUpInside) - rootView.accountDetailsView.addTarget(self, action: #selector(didTapOnAccount), for: .touchUpInside) + rootView.shareButton.addTarget( + self, + action: #selector(didTapShare), + for: .touchUpInside + ) + rootView.accountAddressView.copyButton.addTarget( + self, + action: #selector(didTapCopyAddress), + for: .touchUpInside + ) } - private func update(token: String) { - self.token = token - navigationItem.title = R.string.localizable.walletReceiveTitleFormat( + private func updateTitleDetails( + chainName: String, + token: String + ) { + let languages = selectedLocale.rLanguages + + rootView.titleLabel.text = R.string.localizable.walletReceiveTitleFormat( token, - preferredLanguages: selectedLocale.rLanguages + preferredLanguages: languages ) + + rootView.detailsLabel.text = R.string.localizable.walletReceiveDetailsFormat( + token, + chainName, + preferredLanguages: languages + ) + } + + private func updateNavigationBar() { + navigationItem.titleView = rootView.chainView } @objc private func didTapShare() { presenter.share() } - @objc private func didTapOnAccount() { - presenter.presentAccountOptions() + @objc private func didTapCopyAddress() { + presenter.copyAddress() } } +struct AccountAddressViewModel { + let walletName: String? + let address: String? +} + extension AssetReceiveViewController: AssetReceiveViewProtocol { - func didReceive(chainAccountViewModel: ChainAccountViewModel, token: String) { - rootView.accountDetailsView.chainAccountView.bind(viewModel: chainAccountViewModel) - update(token: token) + func didReceive( + addressViewModel: AccountAddressViewModel, + networkName: String, + token: String + ) { + updateTitleDetails( + chainName: networkName, + token: token + ) + + rootView.accountAddressView.titleLabel.text = addressViewModel.walletName + rootView.accountAddressView.addressLabel.text = addressViewModel.address + } + + func didReceive(qrResult: QRCodeWithLogoFactory.QRCreationResult) { + rootView.qrView.bind(viewModel: qrResult) } - func didReceive(qrImage: UIImage) { - rootView.qrView.imageView.image = qrImage + func didReceive(networkViewModel: NetworkViewModel) { + rootView.chainView.bind(viewModel: networkViewModel) + updateNavigationBar() } } diff --git a/novawallet/Modules/AssetReceive/AssetReceiveViewFactory.swift b/novawallet/Modules/AssetReceive/AssetReceiveViewFactory.swift index fb2a4ba912..36ab842b98 100644 --- a/novawallet/Modules/AssetReceive/AssetReceiveViewFactory.swift +++ b/novawallet/Modules/AssetReceive/AssetReceiveViewFactory.swift @@ -13,12 +13,24 @@ struct AssetReceiveViewFactory { publicKey: chainAccount.publicKey ) + let operationQueue = OperationManagerFacade.sharedDefaultQueue + + let imageRetreiveOperationFactory = KingfisherIconRetrieveOperationFactory(operationQueue: operationQueue) + + let qrCodeFactory = QRCodeWithLogoFactory( + iconRetrievingFactory: imageRetreiveOperationFactory, + operationQueue: operationQueue, + callbackQueue: .main, + logger: Logger.shared + ) + let interactor = AssetReceiveInteractor( metaChainAccountResponse: metaChainAccountResponse, chainAsset: chainAsset, qrCoderFactory: qrCoderFactory, - qrCodeCreationOperationFactory: QRCreationOperationFactory(), - operationQueue: OperationManagerFacade.sharedDefaultQueue + qrCodeFactory: qrCodeFactory, + appearanceFacade: AppearanceFacade.shared, + operationQueue: operationQueue ) let wireframe = AssetReceiveWireframe() let localizationManager = LocalizationManager.shared @@ -28,11 +40,14 @@ struct AssetReceiveViewFactory { localizationManager: localizationManager ) + let networkViewModelFactory = NetworkViewModelFactory() + let presenter = AssetReceivePresenter( interactor: interactor, wireframe: wireframe, iconGenerator: PolkadotIconGenerator(), accountShareFactory: accountShareFactory, + networkViewModelFactory: networkViewModelFactory, localizationManager: localizationManager, logger: Logger.shared ) diff --git a/novawallet/Modules/AssetReceive/AssetReceiveViewLayout.swift b/novawallet/Modules/AssetReceive/AssetReceiveViewLayout.swift index c60ab8ff76..f1d8a53e0d 100644 --- a/novawallet/Modules/AssetReceive/AssetReceiveViewLayout.swift +++ b/novawallet/Modules/AssetReceive/AssetReceiveViewLayout.swift @@ -1,4 +1,5 @@ import UIKit +import SoraUI import SnapKit final class AssetReceiveViewLayout: UIView { @@ -10,13 +11,35 @@ final class AssetReceiveViewLayout: UIView { return view }() - let accountDetailsView: ChainAccountControl = .create { - $0.chainAccountView.actionIconView.image = R.image.iconMore()?.tinted(with: R.color.colorIconSecondary()!) + let qrContainerView: RoundedView = .create { view in + view.fillColor = .white + view.cornerRadius = Constants.qrContainerCornerRadius } - let titleLabel = UILabel(style: .semiboldBodyPrimary, textAlignment: .center) - let qrView: QRDisplayView = .create { $0.contentInsets = Constants.qrViewContentInsets } - let shareButton: TriangularedButton = .create { $0.applyDefaultStyle() } + let chainView = AssetListChainView() + + let accountAddressView = AccountAddressView() + + let titleLabel: UILabel = .create { view in + view.apply(style: .title3Primary) + view.textAlignment = .center + } + + let detailsLabel: UILabel = .create { view in + view.apply(style: .footnoteSecondary) + view.numberOfLines = 0 + view.textAlignment = .center + } + + let qrView: QRWithLogoDisplayView = .create { + $0.contentInsets = Constants.qrViewContentInsets + $0.backgroundView.shadowOpacity = .zero + } + + let shareButton: TriangularedButton = .create { + $0.applyDefaultStyle() + $0.imageWithTitleView?.iconImage = R.image.iconShare() + } override init(frame: CGRect) { super.init(frame: frame) @@ -36,52 +59,59 @@ final class AssetReceiveViewLayout: UIView { $0.top.equalTo(safeAreaLayoutGuide.snp.top) $0.leading.trailing.bottom.equalToSuperview() } - containerView.stackView.addArrangedSubview(accountDetailsView) containerView.stackView.addArrangedSubview(titleLabel) - containerView.stackView.addArrangedSubview(qrView) + containerView.stackView.addArrangedSubview(detailsLabel) + containerView.stackView.addArrangedSubview(qrContainerView) - accountDetailsView.snp.makeConstraints { - $0.height.equalTo(Constants.accountDetailsViewHeight) - $0.width.equalToSuperview().inset(Constants.containerHorizontalOffset) - } + qrContainerView.addSubview(qrView) + qrContainerView.addSubview(accountAddressView) qrView.snp.makeConstraints { - $0.width.equalToSuperview().multipliedBy(Constants.qrViewSizeRatio) + $0.width.equalTo(containerView).multipliedBy(Constants.qrViewSizeRatio) $0.width.equalTo(Constants.qrViewPlaceHolderWidth).priority(.high) + $0.top.leading.trailing.equalToSuperview() $0.height.equalTo(qrView.snp.width) } + accountAddressView.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview().inset(UIConstants.horizontalInset) + make.top.equalTo(qrView.snp.bottom).offset(Constants.qrViewContentInsets) + make.bottom.equalToSuperview().inset(Constants.addressViewBottomInset) + } + + containerView.stackView.setCustomSpacing( + Constants.titleDetailsVerticalSpace, + after: titleLabel + ) containerView.stackView.setCustomSpacing( - Constants.accountDetailsTitleVerticalSpace, - after: accountDetailsView + Constants.detailsQRVerticalSpace, + after: detailsLabel ) - containerView.stackView.setCustomSpacing(Constants.titleQRVerticalSpace, after: titleLabel) addSubview(shareButton) shareButton.snp.makeConstraints { - $0.top.equalTo(qrView.snp.bottom).offset(Constants.shareButtonTopOffset).priority(.high) - $0.leading.equalTo(qrView.snp.leading) - $0.trailing.equalTo(qrView.snp.trailing) + $0.leading.trailing.equalToSuperview().inset(UIConstants.horizontalInset) $0.height.equalTo(Constants.shareButtonHeight) - $0.bottom.lessThanOrEqualTo(safeAreaLayoutGuide.snp.bottom) + $0.bottom.equalTo(safeAreaLayoutGuide.snp.bottom) } } } extension AssetReceiveViewLayout { enum Constants { - static let qrViewSizeRatio: CGFloat = 0.75 + static let qrContainerCornerRadius: CGFloat = 16 + static let qrViewSizeRatio: CGFloat = 0.8 static let qrViewPlaceHolderWidth: CGFloat = 280 static let qrCodeMinimumWidth: CGFloat = 120 - static let accountDetailsViewHeight: CGFloat = 52 - static let accountDetailsTitleVerticalSpace: CGFloat = 52 - static let titleQRVerticalSpace: CGFloat = 36 + static let detailsQRVerticalSpace: CGFloat = 36 + static let titleDetailsVerticalSpace: CGFloat = 4 static let qrViewContentInsets: CGFloat = 8 static let containerHorizontalOffset: CGFloat = 16 static let shareButtonTopOffset: CGFloat = 24 static let shareButtonHeight: CGFloat = 52 + static let addressViewBottomInset: CGFloat = 12 static let containerInsets = UIEdgeInsets( - top: 16, + top: 40, left: containerHorizontalOffset, bottom: Constants.shareButtonHeight + shareButtonTopOffset, right: containerHorizontalOffset diff --git a/novawallet/Modules/AssetReceive/Model/AssetIconURLFactory.swift b/novawallet/Modules/AssetReceive/Model/AssetIconURLFactory.swift new file mode 100644 index 0000000000..3d06a35ce1 --- /dev/null +++ b/novawallet/Modules/AssetReceive/Model/AssetIconURLFactory.swift @@ -0,0 +1,37 @@ +import Foundation + +enum AssetIconURLFactory { + static func createURL( + for iconName: String?, + iconAppearance: AppearanceIconsOptions + ) -> URL? { + guard let iconName else { return nil } + + return switch iconAppearance { + case .white: + URL(string: ApplicationConfig.shared.whiteAppearanceIconsPath + iconName) + case .colored: + URL(string: ApplicationConfig.shared.coloredAppearanceIconsPath + iconName) + } + } + + static func createQRLogoURL( + for iconName: String?, + iconAppearance: AppearanceIconsOptions + ) -> IconType? { + guard let iconName else { return nil } + + switch iconAppearance { + case .white: + let path = ApplicationConfig.shared.whiteAppearanceIconsPath + let url = URL(string: path + iconName) + + return .remoteTransparent(url) + case .colored: + let path = ApplicationConfig.shared.coloredAppearanceIconsPath + let url = URL(string: path + iconName) + + return .remoteColored(url) + } + } +} diff --git a/novawallet/Modules/AssetReceive/Model/QRCodeInfo.swift b/novawallet/Modules/AssetReceive/Model/QRCodeInfo.swift index 605a92e2b9..bac7173c5d 100644 --- a/novawallet/Modules/AssetReceive/Model/QRCodeInfo.swift +++ b/novawallet/Modules/AssetReceive/Model/QRCodeInfo.swift @@ -1,6 +1,6 @@ import UIKit struct QRCodeInfo { - let image: UIImage + let result: QRCodeWithLogoFactory.QRCreationResult let encodingData: AssetReceiveInfo } diff --git a/novawallet/Modules/AssetReceive/View/AccountAddressView.swift b/novawallet/Modules/AssetReceive/View/AccountAddressView.swift new file mode 100644 index 0000000000..8a1120e106 --- /dev/null +++ b/novawallet/Modules/AssetReceive/View/AccountAddressView.swift @@ -0,0 +1,61 @@ +import Foundation +import SoraUI + +final class AccountAddressView: UIView { + let titleLabel: UILabel = .create { view in + view.apply(style: .regularSubhedlinePrimaryOnWhite) + view.textAlignment = .center + } + + let addressLabel: UILabel = .create { view in + view.apply(style: .footnoteSecondaryOnWhite) + view.textAlignment = .center + view.numberOfLines = 0 + } + + let copyButton: TriangularedButton = .create { view in + view.applyEnabledStyle( + colored: .clear, + textColor: R.color.colorButtonTextAccent()! + ) + + view.imageWithTitleView?.iconImage = R.image.iconActionCopy()?.tinted( + with: R.color.colorIconAccent()! + )?.kf.resize(to: .init(width: 16, height: 16)) + + view.imageWithTitleView?.titleFont = .caption1 + } + + override init(frame: CGRect) { + super.init(frame: frame) + + setupLayout() + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupLayout() { + let container = UIStackView.vStack( + alignment: .center, + spacing: 4, + [ + titleLabel, + addressLabel, + copyButton + ] + ) + + addSubview(container) + + container.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + copyButton.snp.makeConstraints { make in + make.height.equalTo(32) + } + } +} diff --git a/novawallet/Modules/Nft/ViewModel/NftImageViewModel.swift b/novawallet/Modules/Nft/ViewModel/NftImageViewModel.swift index 22b70b2fe7..e4ec2a4f93 100644 --- a/novawallet/Modules/Nft/ViewModel/NftImageViewModel.swift +++ b/novawallet/Modules/Nft/ViewModel/NftImageViewModel.swift @@ -56,11 +56,8 @@ final class NftImageViewModel: NftMediaViewModelProtocol { var options: KingfisherOptionsInfo = [ .processor(compoundProcessor), .scaleFactor(UIScreen.main.scale), - .cacheSerializer(RemoteImageSerializer.shared), - .cacheOriginalImage, - .onlyLoadFirstFrame, - .diskCacheExpiration(.days(1)) - ] + .onlyLoadFirstFrame + ] + KingfisherOptionsInfo.cacheOptions if animated { options.append(.transition(.fade(0.25))) diff --git a/novawallet/Modules/ParitySigner/TransactionQr/ParitySignerTxQrInteractor.swift b/novawallet/Modules/ParitySigner/TransactionQr/ParitySignerTxQrInteractor.swift index e2f9cd22c7..d54ff3bc76 100644 --- a/novawallet/Modules/ParitySigner/TransactionQr/ParitySignerTxQrInteractor.swift +++ b/novawallet/Modules/ParitySigner/TransactionQr/ParitySignerTxQrInteractor.swift @@ -36,7 +36,10 @@ final class ParitySignerTxQrInteractor { self.operationQueue = operationQueue } - private func provideTransactionCode(for size: CGSize, account: ChainAccountResponse) { + private func provideTransactionCode( + for size: CGSize, + account: ChainAccountResponse + ) { let messageWrapper = messageOperationFactory.createTransaction( for: signingData, accountId: account.accountId, diff --git a/novawallet/en.lproj/Localizable.strings b/novawallet/en.lproj/Localizable.strings index 44821d5e06..6bf7868edc 100644 --- a/novawallet/en.lproj/Localizable.strings +++ b/novawallet/en.lproj/Localizable.strings @@ -1765,3 +1765,5 @@ "settings.appearance.token.icons.title" = "Token icons"; "settings.appearance.token.icons.option.white" = "White"; "settings.appearance.token.icons.option.colored" = "Colored"; +"wallet.receive.details.format" = "Send only %@ token and tokens in %@ network to this address, or you might lose your funds"; +"common.address.coppied" = "Address copied";