Skip to content

Commit 685e531

Browse files
authored
Merge pull request #1283 from soramitsu/multichain-asset-selection
multichain asset selection module
2 parents a7e1007 + 94c0237 commit 685e531

22 files changed

+695
-6
lines changed

fearless.xcodeproj/project.pbxproj

Lines changed: 108 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import UIKit
2+
3+
extension UIImage {
4+
func invertedImage() -> UIImage? {
5+
guard let cgImage = self.cgImage else { return nil }
6+
let ciImage = CoreImage.CIImage(cgImage: cgImage)
7+
guard let filter = CIFilter(name: "CIColorInvert") else { return nil }
8+
filter.setDefaults()
9+
filter.setValue(ciImage, forKey: kCIInputImageKey)
10+
let context = CIContext(options: nil)
11+
guard let outputImage = filter.outputImage else { return nil }
12+
guard let outputImageCopy = context.createCGImage(outputImage, from: outputImage.extent) else { return nil }
13+
return UIImage(cgImage: outputImageCopy, scale: scale, orientation: .up)
14+
}
15+
}

fearless/Common/ViewController/SelectableList/SelectableListViewLayout.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ final class SelectableListViewLayout: UIView {
7070
emptyView.bind(viewModel: viewModel)
7171
}
7272

73+
func bind(isEmbed: Bool) {
74+
indicator.isHidden = isEmbed
75+
layer.cornerRadius = isEmbed ? 0 : Constants.cornerRadius
76+
}
77+
7378
private func setupLayout() {
7479
layer.cornerRadius = Constants.cornerRadius
7580
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import Foundation
2+
import SSFModels
3+
4+
protocol MultichainAssetFetching {
5+
func fetchAssets(for chain: ChainModel) async throws -> [ChainAsset]
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Foundation
2+
import SSFModels
3+
4+
class OKXMultichainAssetFetching: MultichainAssetFetching {
5+
func fetchAssets(for _: ChainModel) async throws -> [ChainAsset] {
6+
[]
7+
}
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Foundation
2+
import SSFModels
3+
4+
enum MultichainChainFetchingFlow {
5+
case okx
6+
case preset(chainIds: [ChainModel.Id])
7+
}
8+
9+
protocol MultichainChainFetching {
10+
func fetchChains() async throws -> [ChainModel]
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Foundation
2+
import SSFModels
3+
import RobinHood
4+
5+
class OKXMultichainChainFetching: MultichainChainFetching {
6+
private let chainsRepository: AsyncCoreDataRepositoryDefault<ChainModel, CDChain>
7+
8+
init(chainsRepository: AsyncCoreDataRepositoryDefault<ChainModel, CDChain>) {
9+
self.chainsRepository = chainsRepository
10+
}
11+
12+
func fetchChains() async throws -> [ChainModel] {
13+
try await chainsRepository.fetchAll().filter { $0.rank != nil }
14+
}
15+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import UIKit
2+
import SoraFoundation
3+
4+
final class MultichainAssetSelectionAssembly {
5+
static func configureModule(flow: MultichainChainFetchingFlow, wallet: MetaAccountModel, selectAssetModuleOutput: SelectAssetModuleOutput?) -> MultichainAssetSelectionModuleCreationResult? {
6+
let localizationManager = LocalizationManager.shared
7+
8+
let interactor = MultichainAssetSelectionInteractor(
9+
chainFetching: buildChainFetching(flow: flow),
10+
assetFetching: buildAssetFetching(flow: flow)
11+
)
12+
let router = MultichainAssetSelectionRouter()
13+
14+
let presenter = MultichainAssetSelectionPresenter(
15+
interactor: interactor,
16+
router: router,
17+
localizationManager: localizationManager,
18+
viewModelFactory: MultichainAssetSelectionViewModelFactoryImpl(),
19+
logger: Logger.shared,
20+
selectAssetModuleOutput: selectAssetModuleOutput
21+
)
22+
guard let selectAssetModule = createSelectAssetModule(wallet: wallet, moduleOutput: presenter) else {
23+
return nil
24+
}
25+
26+
presenter.selectAssetModuleInput = selectAssetModule.input
27+
28+
let view = MultichainAssetSelectionViewController(
29+
output: presenter,
30+
localizationManager: localizationManager,
31+
selectAssetViewController: selectAssetModule.view.controller
32+
)
33+
34+
return (view, presenter)
35+
}
36+
37+
private static func buildChainFetching(flow _: MultichainChainFetchingFlow) -> MultichainChainFetching {
38+
let chainsRepository = ChainRepositoryFactory().createAsyncRepository()
39+
return OKXMultichainChainFetching(chainsRepository: chainsRepository)
40+
}
41+
42+
private static func buildAssetFetching(flow _: MultichainChainFetchingFlow) -> MultichainAssetFetching {
43+
OKXMultichainAssetFetching()
44+
}
45+
46+
private static func createSelectAssetModule(wallet: MetaAccountModel, moduleOutput: SelectAssetModuleOutput) -> SelectAssetModuleCreationResult? {
47+
SelectAssetAssembly.configureModule(wallet: wallet, selectedAssetId: nil, chainAssets: nil, searchTextsViewModel: .searchAssetPlaceholder, output: moduleOutput, isEmbed: true)
48+
}
49+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import UIKit
2+
import SSFModels
3+
4+
protocol MultichainAssetSelectionInteractorOutput: AnyObject {}
5+
6+
final class MultichainAssetSelectionInteractor {
7+
// MARK: - Private properties
8+
9+
private weak var output: MultichainAssetSelectionInteractorOutput?
10+
private let chainFetching: MultichainChainFetching
11+
private let assetFetching: MultichainAssetFetching
12+
13+
init(chainFetching: MultichainChainFetching, assetFetching: MultichainAssetFetching) {
14+
self.chainFetching = chainFetching
15+
self.assetFetching = assetFetching
16+
}
17+
}
18+
19+
// MARK: - MultichainAssetSelectionInteractorInput
20+
21+
extension MultichainAssetSelectionInteractor: MultichainAssetSelectionInteractorInput {
22+
func setup(with output: MultichainAssetSelectionInteractorOutput) {
23+
self.output = output
24+
}
25+
26+
func fetchChains() async throws -> [ChainModel] {
27+
try await chainFetching.fetchChains()
28+
}
29+
30+
func fetchAssets(for chain: ChainModel) async throws -> [ChainAsset] {
31+
try await assetFetching.fetchAssets(for: chain)
32+
}
33+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import Foundation
2+
import SoraFoundation
3+
import SSFModels
4+
5+
@MainActor
6+
protocol MultichainAssetSelectionViewInput: ControllerBackedProtocol {
7+
func didReceive(viewModels: [ChainSelectionCollectionCellModel])
8+
}
9+
10+
protocol MultichainAssetSelectionInteractorInput: AnyObject {
11+
func setup(with output: MultichainAssetSelectionInteractorOutput)
12+
func fetchChains() async throws -> [ChainModel]
13+
func fetchAssets(for chain: ChainModel) async throws -> [ChainAsset]
14+
}
15+
16+
final class MultichainAssetSelectionPresenter {
17+
// MARK: Private properties
18+
19+
private weak var view: MultichainAssetSelectionViewInput?
20+
private let router: MultichainAssetSelectionRouterInput
21+
private let interactor: MultichainAssetSelectionInteractorInput
22+
private let viewModelFactory: MultichainAssetSelectionViewModelFactory
23+
private let logger: LoggerProtocol
24+
private let selectAssetModuleOutput: SelectAssetModuleOutput?
25+
weak var selectAssetModuleInput: SelectAssetModuleInput?
26+
private var selectedChainId: ChainModel.Id?
27+
private var chains: [ChainModel]?
28+
29+
// MARK: - Constructors
30+
31+
init(
32+
interactor: MultichainAssetSelectionInteractorInput,
33+
router: MultichainAssetSelectionRouterInput,
34+
localizationManager: LocalizationManagerProtocol,
35+
viewModelFactory: MultichainAssetSelectionViewModelFactory,
36+
logger: LoggerProtocol,
37+
selectAssetModuleOutput: SelectAssetModuleOutput?
38+
) {
39+
self.interactor = interactor
40+
self.router = router
41+
self.viewModelFactory = viewModelFactory
42+
self.logger = logger
43+
self.selectAssetModuleOutput = selectAssetModuleOutput
44+
45+
self.localizationManager = localizationManager
46+
}
47+
48+
// MARK: - Private methods
49+
50+
private func provideViewModel() {
51+
Task {
52+
let viewModels = viewModelFactory.buildViewModels(chains: chains.or([]), selectedChainId: selectedChainId)
53+
await view?.didReceive(viewModels: viewModels)
54+
}
55+
}
56+
57+
private func fetchChains() {
58+
Task {
59+
do {
60+
let chains = try await interactor.fetchChains()
61+
self.chains = chains
62+
63+
if selectedChainId == nil {
64+
selectedChainId = chains.first?.chainId
65+
selectAssetModuleInput?.update(with: (chains.first?.chainAssets).or([]))
66+
}
67+
68+
let viewModels = viewModelFactory.buildViewModels(chains: chains, selectedChainId: selectedChainId)
69+
await view?.didReceive(viewModels: viewModels)
70+
} catch {
71+
logger.customError(error)
72+
}
73+
}
74+
}
75+
}
76+
77+
// MARK: - MultichainAssetSelectionViewOutput
78+
79+
extension MultichainAssetSelectionPresenter: MultichainAssetSelectionViewOutput {
80+
func didLoad(view: MultichainAssetSelectionViewInput) {
81+
self.view = view
82+
interactor.setup(with: self)
83+
84+
fetchChains()
85+
}
86+
87+
func didSelect(chain: ChainModel) {
88+
selectAssetModuleInput?.update(with: chain.chainAssets)
89+
selectedChainId = chain.chainId
90+
provideViewModel()
91+
}
92+
93+
func didTapCloseButton() {
94+
router.dismiss(view: view)
95+
}
96+
}
97+
98+
// MARK: - MultichainAssetSelectionInteractorOutput
99+
100+
extension MultichainAssetSelectionPresenter: MultichainAssetSelectionInteractorOutput {}
101+
102+
// MARK: - Localizable
103+
104+
extension MultichainAssetSelectionPresenter: Localizable {
105+
func applyLocalization() {}
106+
}
107+
108+
extension MultichainAssetSelectionPresenter: MultichainAssetSelectionModuleInput {}
109+
110+
extension MultichainAssetSelectionPresenter: SelectAssetModuleOutput {
111+
func assetSelection(didCompleteWith chainAsset: ChainAsset?, contextTag: Int?) {
112+
selectAssetModuleOutput?.assetSelection(didCompleteWith: chainAsset, contextTag: contextTag)
113+
}
114+
}

0 commit comments

Comments
 (0)