From 74679aa3d89f0101e8e02bf8069b6b5069d34da1 Mon Sep 17 00:00:00 2001 From: antonijzelinskij Date: Tue, 26 Nov 2024 11:18:30 +0100 Subject: [PATCH 1/4] DApp bottom sheet entry point --- .../app/root/data/browser/TabsRepository.kt | 16 +++ .../nova/app/root/di/RootDependencies.kt | 54 ++++---- .../app/root/domain/SplitScreenInteractor.kt | 15 +++ .../navigators/dApp/DAppNavigator.kt | 18 ++- .../deepLinking/DeepLinkingNavigator.kt | 3 +- .../governance/GovernanceNavigator.kt | 5 +- .../splitScreen/SplitScreenFragment.kt | 13 ++ .../splitScreen/SplitScreenViewModel.kt | 40 +++++- .../di/SplitScreenFragmentModule.kt | 23 +++- .../main/res/layout/fragment_split_screen.xml | 45 ++++++- .../res/navigation/dapp_browser_graph.xml | 6 +- .../main/res/navigation/root_nav_graph.xml | 10 ++ .../main/res/drawable/bg_dapp_entry_point.xml | 8 ++ common/src/main/res/values/strings.xml | 1 + .../nova/feature_dapp_api/DAppRouter.kt | 3 +- .../browser/main/DAppBrowserPayload.kt | 13 ++ .../browser/main/DAppBrowserFragment.kt | 3 +- .../browser/main/DAppBrowserViewModel.kt | 10 +- .../browser/main/di/DAppBrowserComponent.kt | 3 +- .../browser/main/di/DAppBrowserModule.kt | 5 +- .../presentation/main/MainDAppViewModel.kt | 3 +- .../search/DAppSearchViewModel.kt | 3 +- .../presentation/tab/BrowserTabsFragment.kt | 20 +-- .../presentation/tab/BrowserTabsViewModel.kt | 4 +- .../presentation/tab/Dialogs.kt | 23 ++++ .../utils/tabs/BrowserTabPoolService.kt | 116 ----------------- .../utils/tabs/RealBrowserTabPoolService.kt | 119 ++++++++++++++++++ .../presentation/GovernanceRouter.kt | 2 +- .../more/MoreStakingOptionsViewModel.kt | 3 +- 29 files changed, 398 insertions(+), 189 deletions(-) create mode 100644 app/src/main/java/io/novafoundation/nova/app/root/data/browser/TabsRepository.kt create mode 100644 app/src/main/java/io/novafoundation/nova/app/root/domain/SplitScreenInteractor.kt create mode 100644 common/src/main/res/drawable/bg_dapp_entry_point.xml create mode 100644 feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/presentation/browser/main/DAppBrowserPayload.kt create mode 100644 feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/Dialogs.kt create mode 100644 feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/RealBrowserTabPoolService.kt diff --git a/app/src/main/java/io/novafoundation/nova/app/root/data/browser/TabsRepository.kt b/app/src/main/java/io/novafoundation/nova/app/root/data/browser/TabsRepository.kt new file mode 100644 index 0000000000..11686b93eb --- /dev/null +++ b/app/src/main/java/io/novafoundation/nova/app/root/data/browser/TabsRepository.kt @@ -0,0 +1,16 @@ +package io.novafoundation.nova.app.root.data.browser + +import io.novafoundation.nova.common.utils.mapList +import io.novafoundation.nova.core_db.dao.BrowserTabsDao +import kotlinx.coroutines.flow.Flow + +class TabsRepository(private val tabsDao: BrowserTabsDao) { + + fun observeTabIds(): Flow> { + return tabsDao.observeAllTabs().mapList { it.id } + } + + suspend fun removeAllTabs() { + tabsDao.removeAllTabs() + } +} diff --git a/app/src/main/java/io/novafoundation/nova/app/root/di/RootDependencies.kt b/app/src/main/java/io/novafoundation/nova/app/root/di/RootDependencies.kt index a70d6508e9..6067fcc04f 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/di/RootDependencies.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/di/RootDependencies.kt @@ -3,6 +3,7 @@ package io.novafoundation.nova.app.root.di import android.content.Context import coil.ImageLoader import io.novafoundation.nova.common.data.network.AppLinksProvider +import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin import io.novafoundation.nova.common.mixin.api.NetworkStateMixin import io.novafoundation.nova.common.resources.ContextManager import io.novafoundation.nova.common.resources.ResourceManager @@ -12,6 +13,7 @@ import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate import io.novafoundation.nova.common.utils.sequrity.BackgroundAccessObserver import io.novafoundation.nova.common.utils.systemCall.SystemCallExecutor import io.novafoundation.nova.common.view.bottomSheet.action.ActionBottomSheetLauncherFactory +import io.novafoundation.nova.core_db.dao.BrowserTabsDao import io.novafoundation.nova.feature_account_api.data.events.MetaAccountChangesEventBus import io.novafoundation.nova.feature_account_api.data.proxy.ProxySyncService import io.novafoundation.nova.feature_account_api.data.proxy.validation.ProxyExtrinsicValidationRequestBus @@ -39,6 +41,34 @@ import kotlinx.coroutines.flow.MutableStateFlow interface RootDependencies { + val systemCallExecutor: SystemCallExecutor + + val contextManager: ContextManager + + val walletConnectService: WalletConnectService + + val imageLoader: ImageLoader + + val automaticInteractionGate: AutomaticInteractionGate + + val walletConnectSessionsUseCase: WalletConnectSessionsUseCase + + val pushNotificationsInteractor: PushNotificationsInteractor + + val rootDeepLinkHandler: RootDeepLinkHandler + + val welcomePushNotificationsInteractor: WelcomePushNotificationsInteractor + + val applyLocalSnapshotToCloudBackupUseCase: ApplyLocalSnapshotToCloudBackupUseCase + + val actionBottomSheetLauncherFactory: ActionBottomSheetLauncherFactory + + val tabsDao: BrowserTabsDao + + val balancesUpdateSystem: BalancesUpdateSystem + + val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory + fun updateNotificationsInteractor(): UpdateNotificationsInteractor fun contributionsInteractor(): ContributionsInteractor @@ -59,8 +89,6 @@ interface RootDependencies { fun currencyInteractor(): CurrencyInteractor - val balancesUpdateSystem: BalancesUpdateSystem - fun stakingRepository(): StakingRepository fun chainRegistry(): ChainRegistry @@ -86,26 +114,4 @@ interface RootDependencies { fun proxyHaveEnoughFeeValidationFactory(): ProxyHaveEnoughFeeValidationFactory fun context(): Context - - val systemCallExecutor: SystemCallExecutor - - val contextManager: ContextManager - - val walletConnectService: WalletConnectService - - val imageLoader: ImageLoader - - val automaticInteractionGate: AutomaticInteractionGate - - val walletConnectSessionsUseCase: WalletConnectSessionsUseCase - - val pushNotificationsInteractor: PushNotificationsInteractor - - val rootDeepLinkHandler: RootDeepLinkHandler - - val welcomePushNotificationsInteractor: WelcomePushNotificationsInteractor - - val applyLocalSnapshotToCloudBackupUseCase: ApplyLocalSnapshotToCloudBackupUseCase - - val actionBottomSheetLauncherFactory: ActionBottomSheetLauncherFactory } diff --git a/app/src/main/java/io/novafoundation/nova/app/root/domain/SplitScreenInteractor.kt b/app/src/main/java/io/novafoundation/nova/app/root/domain/SplitScreenInteractor.kt new file mode 100644 index 0000000000..a6065b6fc9 --- /dev/null +++ b/app/src/main/java/io/novafoundation/nova/app/root/domain/SplitScreenInteractor.kt @@ -0,0 +1,15 @@ +package io.novafoundation.nova.app.root.domain + +import io.novafoundation.nova.app.root.data.browser.TabsRepository +import kotlinx.coroutines.flow.Flow + +class SplitScreenInteractor(val repository: TabsRepository) { + + fun observeTabIds(): Flow> { + return repository.observeTabIds() + } + + suspend fun removeAllTabs() { + repository.removeAllTabs() + } +} diff --git a/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/dApp/DAppNavigator.kt b/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/dApp/DAppNavigator.kt index 3412ca6081..527758bb04 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/dApp/DAppNavigator.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/dApp/DAppNavigator.kt @@ -6,6 +6,7 @@ import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator import io.novafoundation.nova.feature_dapp_api.DAppRouter import io.novafoundation.nova.feature_dapp_impl.presentation.addToFavourites.AddToFavouritesFragment import io.novafoundation.nova.feature_dapp_api.presentation.addToFavorites.AddToFavouritesPayload +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.presentation.browser.main.DAppBrowserFragment import io.novafoundation.nova.feature_dapp_impl.presentation.search.DappSearchFragment import io.novafoundation.nova.feature_dapp_impl.presentation.search.SearchPayload @@ -16,7 +17,7 @@ class DAppNavigator( override fun openChangeAccount() = performNavigation(R.id.action_open_switch_wallet) - override fun openDAppBrowser(initialUrl: String) { + override fun openDAppBrowser(payload: DAppBrowserPayload) { // Close deapp browser if it is already opened // TODO it's better to provide new url to existing browser val currentDestination = rootNavigationHolder.navController?.currentDestination @@ -27,7 +28,7 @@ class DAppNavigator( R.id.dappTabsFragment -> R.id.action_dappTabsFragment_to_dapp_browser_graph else -> R.id.action_open_dappBrowser } - performNavigation(destinationId, DAppBrowserFragment.getBundle(initialUrl)) + performNavigation(destinationId, DAppBrowserFragment.getBundle(payload)) } override fun openDappSearch() { @@ -50,9 +51,16 @@ class DAppNavigator( actionId = R.id.action_mainFragment_to_authorizedDAppsFragment ) - override fun openTabs() = performNavigation( - actionId = R.id.action_DAppBrowserFragment_to_browserTabsFragment - ) + override fun openTabs() { + val currentDestination = rootNavigationHolder.navController?.currentDestination + + val destinationId = when (currentDestination?.id) { + R.id.dappBrowserFragment -> R.id.action_DAppBrowserFragment_to_browserTabsFragment + else -> R.id.action_open_dappTabs + } + + performNavigation(destinationId) + } override fun finishTabs() = performNavigation( actionId = R.id.action_finish_tabs_fragment diff --git a/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/deepLinking/DeepLinkingNavigator.kt b/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/deepLinking/DeepLinkingNavigator.kt index ba6b61782a..1ec84ac14a 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/deepLinking/DeepLinkingNavigator.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/deepLinking/DeepLinkingNavigator.kt @@ -4,6 +4,7 @@ import io.novafoundation.nova.feature_account_api.presenatation.account.add.Impo import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter import io.novafoundation.nova.feature_assets.presentation.AssetsRouter import io.novafoundation.nova.feature_dapp_api.DAppRouter +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_deep_linking.presentation.handling.DeepLinkingRouter import io.novafoundation.nova.feature_governance_api.presentation.referenda.details.ReferendumDetailsPayload import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter @@ -21,7 +22,7 @@ class DeepLinkingNavigator( } override fun openDAppBrowser(url: String) { - dAppRouter.openDAppBrowser(url) + dAppRouter.openDAppBrowser(DAppBrowserPayload.Address(url)) } override fun openImportAccountScreen(importAccountPayload: ImportAccountPayload) { diff --git a/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/governance/GovernanceNavigator.kt b/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/governance/GovernanceNavigator.kt index 253a581ce4..22ba6ff15a 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/governance/GovernanceNavigator.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/navigation/navigators/governance/GovernanceNavigator.kt @@ -6,6 +6,7 @@ import io.novafoundation.nova.app.root.navigation.navigators.BaseNavigator import io.novafoundation.nova.app.root.navigation.holders.NavigationHolder import io.novafoundation.nova.app.root.navigation.navigators.Navigator import io.novafoundation.nova.common.utils.showBrowser +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.presentation.browser.main.DAppBrowserFragment import io.novafoundation.nova.feature_governance_impl.BuildConfig import io.novafoundation.nova.feature_governance_impl.presentation.GovernanceRouter @@ -207,9 +208,9 @@ class GovernanceNavigator( args = DescriptionFragment.getBundle(payload) ) - override fun openDAppBrowser(initialUrl: String) = performNavigation( + override fun openDAppBrowser(url: String) = performNavigation( actionId = R.id.action_referendumDetailsFragment_to_DAppBrowserGraph, - args = DAppBrowserFragment.getBundle(initialUrl) + args = DAppBrowserFragment.getBundle(DAppBrowserPayload.Address(url)) ) override fun openReferendumDescription(payload: DescriptionPayload) = performNavigation( diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt index 6a8f79b8ed..c9792efd51 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.isVisible import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import io.novafoundation.nova.app.R @@ -12,7 +13,11 @@ import io.novafoundation.nova.app.root.di.RootComponent import io.novafoundation.nova.app.root.navigation.holders.MainNavigationHolder import io.novafoundation.nova.common.base.BaseFragment import io.novafoundation.nova.common.di.FeatureUtils +import io.novafoundation.nova.feature_dapp_impl.presentation.tab.setupCloseAllDappTabsDialogue import javax.inject.Inject +import kotlinx.android.synthetic.main.fragment_split_screen.dappEntryPoint +import kotlinx.android.synthetic.main.fragment_split_screen.dappEntryPointClose +import kotlinx.android.synthetic.main.fragment_split_screen.dappEntryPointText class SplitScreenFragment : BaseFragment() { @@ -43,9 +48,17 @@ class SplitScreenFragment : BaseFragment() { } override fun initViews() { + dappEntryPoint.setOnClickListener { viewModel.onTabsClicked() } + dappEntryPointClose.setOnClickListener { viewModel.onTabsCloseClicked() } } override fun subscribe(viewModel: SplitScreenViewModel) { + setupCloseAllDappTabsDialogue(viewModel.closeAllTabsConfirmation) + + viewModel.dappTabsQuantity.observe { + dappEntryPoint.isVisible = it > 0 + dappEntryPointText.text = getString(R.string.dapp_entry_point_title, it) + } } private val mainNavController: NavController by lazy { diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenViewModel.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenViewModel.kt index dff814183f..c1e4b04fa4 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenViewModel.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenViewModel.kt @@ -1,5 +1,43 @@ package io.novafoundation.nova.app.root.presentation.splitScreen +import io.novafoundation.nova.app.root.domain.SplitScreenInteractor import io.novafoundation.nova.common.base.BaseViewModel +import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin +import io.novafoundation.nova.common.mixin.actionAwaitable.awaitAction +import io.novafoundation.nova.common.mixin.actionAwaitable.confirmingAction +import io.novafoundation.nova.feature_dapp_api.DAppRouter +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch -class SplitScreenViewModel() : BaseViewModel() +class SplitScreenViewModel( + private val interactor: SplitScreenInteractor, + private val router: DAppRouter, + private val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory, +) : BaseViewModel() { + + val closeAllTabsConfirmation = actionAwaitableMixinFactory.confirmingAction() + + private val tabIdsFlow = interactor.observeTabIds() + .shareInBackground() + + val dappTabsQuantity = tabIdsFlow.map { it.size } + + fun onTabsClicked() = launch { + val tabIds = tabIdsFlow.first() + + if (tabIds.size == 1) { + val payload = DAppBrowserPayload.Tab(tabIds.single()) + router.openDAppBrowser(payload) + } else { + router.openTabs() + } + } + + fun onTabsCloseClicked() = launch { + closeAllTabsConfirmation.awaitAction() + + interactor.removeAllTabs() + } +} diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/di/SplitScreenFragmentModule.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/di/SplitScreenFragmentModule.kt index 380343e921..cf783f5cb1 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/di/SplitScreenFragmentModule.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/di/SplitScreenFragmentModule.kt @@ -6,9 +6,14 @@ import androidx.lifecycle.ViewModelProvider import dagger.Module import dagger.Provides import dagger.multibindings.IntoMap +import io.novafoundation.nova.app.root.data.browser.TabsRepository +import io.novafoundation.nova.app.root.domain.SplitScreenInteractor import io.novafoundation.nova.app.root.presentation.splitScreen.SplitScreenViewModel import io.novafoundation.nova.common.di.viewmodel.ViewModelKey import io.novafoundation.nova.common.di.viewmodel.ViewModelModule +import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin +import io.novafoundation.nova.core_db.dao.BrowserTabsDao +import io.novafoundation.nova.feature_dapp_api.DAppRouter @Module( includes = [ @@ -17,11 +22,25 @@ import io.novafoundation.nova.common.di.viewmodel.ViewModelModule ) class SplitScreenFragmentModule { + @Provides + fun provideTabQuantityRepository(tabsDao: BrowserTabsDao): TabsRepository { + return TabsRepository(tabsDao) + } + + @Provides + fun provideInteractor(repository: TabsRepository): SplitScreenInteractor { + return SplitScreenInteractor(repository) + } + @Provides @IntoMap @ViewModelKey(SplitScreenViewModel::class) - fun provideViewModel(): ViewModel { - return SplitScreenViewModel() + fun provideViewModel( + interactor: SplitScreenInteractor, + dAppRouter: DAppRouter, + actionAwaitableMixinFactory: ActionAwaitableMixin.Factory, + ): ViewModel { + return SplitScreenViewModel(interactor, dAppRouter, actionAwaitableMixinFactory) } @Provides diff --git a/app/src/main/res/layout/fragment_split_screen.xml b/app/src/main/res/layout/fragment_split_screen.xml index e5bd526226..bd1d6da2e9 100644 --- a/app/src/main/res/layout/fragment_split_screen.xml +++ b/app/src/main/res/layout/fragment_split_screen.xml @@ -1,15 +1,52 @@ - + android:layout_height="match_parent" + android:animateLayoutChanges="true" + android:orientation="vertical"> - \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/dapp_browser_graph.xml b/app/src/main/res/navigation/dapp_browser_graph.xml index 768dcc0f6d..3c20baf09a 100644 --- a/app/src/main/res/navigation/dapp_browser_graph.xml +++ b/app/src/main/res/navigation/dapp_browser_graph.xml @@ -68,9 +68,13 @@ app:popExitAnim="@anim/fragment_close_exit" /> diff --git a/app/src/main/res/navigation/root_nav_graph.xml b/app/src/main/res/navigation/root_nav_graph.xml index c8b8daca90..27ce611e23 100644 --- a/app/src/main/res/navigation/root_nav_graph.xml +++ b/app/src/main/res/navigation/root_nav_graph.xml @@ -13,6 +13,8 @@ + + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 63690f45af..442dc80725 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ + %d DApps Close All DApps? All opened tabs in DApp browser will be closed. diff --git a/feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/DAppRouter.kt b/feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/DAppRouter.kt index e584699c2d..be53b95a59 100644 --- a/feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/DAppRouter.kt +++ b/feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/DAppRouter.kt @@ -2,12 +2,13 @@ package io.novafoundation.nova.feature_dapp_api import io.novafoundation.nova.common.navigation.ReturnableRouter import io.novafoundation.nova.feature_dapp_api.presentation.addToFavorites.AddToFavouritesPayload +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload interface DAppRouter : ReturnableRouter { fun openChangeAccount() - fun openDAppBrowser(initialUrl: String) + fun openDAppBrowser(payload: DAppBrowserPayload) fun openDappSearch() diff --git a/feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/presentation/browser/main/DAppBrowserPayload.kt b/feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/presentation/browser/main/DAppBrowserPayload.kt new file mode 100644 index 0000000000..49bb9dcd5f --- /dev/null +++ b/feature-dapp-api/src/main/java/io/novafoundation/nova/feature_dapp_api/presentation/browser/main/DAppBrowserPayload.kt @@ -0,0 +1,13 @@ +package io.novafoundation.nova.feature_dapp_api.presentation.browser.main + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +interface DAppBrowserPayload : Parcelable { + + @Parcelize + class Tab(val id: String) : DAppBrowserPayload + + @Parcelize + class Address(val address: String) : DAppBrowserPayload +} diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserFragment.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserFragment.kt index db28b175f6..cdf52b7c4b 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserFragment.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserFragment.kt @@ -17,6 +17,7 @@ import io.novafoundation.nova.common.utils.applyStatusBarInsets import io.novafoundation.nova.common.utils.makeGone import io.novafoundation.nova.common.utils.makeVisible import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.R import io.novafoundation.nova.feature_dapp_impl.di.DAppFeatureComponent import io.novafoundation.nova.feature_dapp_impl.domain.browser.isSecure @@ -55,7 +56,7 @@ class DAppBrowserFragment : BaseFragment(), OptionsBottomS private const val PAYLOAD = "DAppBrowserFragment.Payload" - fun getBundle(initialUrl: String) = bundleOf(PAYLOAD to initialUrl) + fun getBundle(payload: DAppBrowserPayload) = bundleOf(PAYLOAD to payload) } @Inject diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserViewModel.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserViewModel.kt index 88374e1274..3078d0b5fe 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserViewModel.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/DAppBrowserViewModel.kt @@ -18,6 +18,7 @@ import io.novafoundation.nova.feature_dapp_impl.domain.browser.BrowserPage import io.novafoundation.nova.feature_dapp_impl.domain.browser.BrowserPageAnalyzed import io.novafoundation.nova.feature_dapp_impl.domain.browser.DappBrowserInteractor import io.novafoundation.nova.feature_dapp_api.presentation.addToFavorites.AddToFavouritesPayload +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.presentation.browser.options.DAppOptionsPayload import io.novafoundation.nova.feature_dapp_impl.presentation.common.favourites.RemoveFavouritesPayload import io.novafoundation.nova.feature_dapp_impl.presentation.search.DAppSearchCommunicator @@ -72,7 +73,7 @@ class DAppBrowserViewModel( private val dAppInteractor: DappInteractor, private val interactor: DappBrowserInteractor, private val dAppSearchRequester: DAppSearchRequester, - private val initialUrl: String, + private val payload: DAppBrowserPayload, private val selectedAccountUseCase: SelectedAccountUseCase, private val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory, private val chainRegistry: ChainRegistry, @@ -135,8 +136,11 @@ class DAppBrowserViewModel( watchDangerousWebsites() launch { - // TODO: We should create tab before this screen open - browserTabPoolService.createNewTabAsCurrentTab(initialUrl) + when (payload) { + is DAppBrowserPayload.Tab -> browserTabPoolService.selectTab(payload.id) + + is DAppBrowserPayload.Address -> browserTabPoolService.createNewTabAsCurrentTab(payload.address) + } } } diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserComponent.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserComponent.kt index a0b10d780e..a53cfdc724 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserComponent.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserComponent.kt @@ -5,6 +5,7 @@ import dagger.BindsInstance import dagger.Subcomponent import io.novafoundation.nova.common.di.scope.ScreenScope import io.novafoundation.nova.feature_dapp_impl.presentation.browser.main.DAppBrowserFragment +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload @Subcomponent( modules = [ @@ -19,7 +20,7 @@ interface DAppBrowserComponent { fun create( @BindsInstance fragment: Fragment, - @BindsInstance initialUrl: String + @BindsInstance payload: DAppBrowserPayload ): DAppBrowserComponent } diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserModule.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserModule.kt index ff23efa634..44922ba201 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserModule.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/browser/main/di/DAppBrowserModule.kt @@ -14,6 +14,7 @@ import io.novafoundation.nova.core_db.dao.BrowserHostSettingsDao import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase import io.novafoundation.nova.feature_dapp_api.data.repository.BrowserHostSettingsRepository import io.novafoundation.nova.feature_dapp_api.DAppRouter +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.data.repository.FavouritesDAppRepository import io.novafoundation.nova.feature_dapp_impl.data.repository.PhishingSitesRepository import io.novafoundation.nova.feature_dapp_impl.data.repository.RealBrowserHostSettingsRepository @@ -68,7 +69,7 @@ class DAppBrowserModule { selectedAccountUseCase: SelectedAccountUseCase, signRequester: ExternalSignCommunicator, searchRequester: DAppSearchCommunicator, - initialUrl: String, + payload: DAppBrowserPayload, extensionStoreFactory: ExtensionStoreFactory, dAppInteractor: DappInteractor, actionAwaitableMixinFactory: ActionAwaitableMixin.Factory, @@ -82,7 +83,7 @@ class DAppBrowserModule { selectedAccountUseCase = selectedAccountUseCase, signRequester = signRequester, dAppSearchRequester = searchRequester, - initialUrl = initialUrl, + payload = payload, extensionStoreFactory = extensionStoreFactory, actionAwaitableMixinFactory = actionAwaitableMixinFactory, chainRegistry = chainRegistry, diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/main/MainDAppViewModel.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/main/MainDAppViewModel.kt index d434a19add..904acb746b 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/main/MainDAppViewModel.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/main/MainDAppViewModel.kt @@ -13,6 +13,7 @@ import io.novafoundation.nova.common.utils.withLoading import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase import io.novafoundation.nova.feature_dapp_api.data.model.DappCategory import io.novafoundation.nova.feature_dapp_api.DAppRouter +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.data.mappers.mapDappModelToDApp import io.novafoundation.nova.feature_dapp_impl.data.mappers.mapDappToDappModel import io.novafoundation.nova.feature_dapp_impl.domain.DappInteractor @@ -104,7 +105,7 @@ class MainDAppViewModel( } fun dappClicked(dapp: DappModel) { - router.openDAppBrowser(dapp.url) + router.openDAppBrowser(DAppBrowserPayload.Address(dapp.url)) } fun searchClicked() { diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/search/DAppSearchViewModel.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/search/DAppSearchViewModel.kt index 13575ee2cc..9d2b6c4b45 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/search/DAppSearchViewModel.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/search/DAppSearchViewModel.kt @@ -13,6 +13,7 @@ import io.novafoundation.nova.common.utils.Event import io.novafoundation.nova.common.utils.inBackground import io.novafoundation.nova.common.utils.sendEvent import io.novafoundation.nova.feature_dapp_api.DAppRouter +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.R import io.novafoundation.nova.feature_dapp_impl.domain.search.DappSearchGroup import io.novafoundation.nova.feature_dapp_impl.domain.search.DappSearchResult @@ -124,7 +125,7 @@ class DAppSearchViewModel( router.finishDappSearch() } - SearchPayload.Request.OPEN_NEW_URL -> router.openDAppBrowser(newUrl) + SearchPayload.Request.OPEN_NEW_URL -> router.openDAppBrowser(DAppBrowserPayload.Address(newUrl)) } } } diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsFragment.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsFragment.kt index 517d543842..936d7dc796 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsFragment.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsFragment.kt @@ -9,7 +9,6 @@ import coil.ImageLoader import io.novafoundation.nova.common.base.BaseFragment import io.novafoundation.nova.common.di.FeatureUtils import io.novafoundation.nova.common.utils.applyStatusBarInsets -import io.novafoundation.nova.common.view.dialog.warningDialog import io.novafoundation.nova.feature_dapp_api.di.DAppFeatureApi import io.novafoundation.nova.feature_dapp_impl.R import io.novafoundation.nova.feature_dapp_impl.di.DAppFeatureComponent @@ -57,7 +56,7 @@ class BrowserTabsFragment : BaseFragment(), BrowserTabsAda } override fun subscribe(viewModel: BrowserTabsViewModel) { - setupCloseAllTabsDialogue() + setupCloseAllDappTabsDialogue(viewModel.closeAllTabsConfirmation) viewModel.tabsFlow.observe { adapter.submitList(it) @@ -71,21 +70,4 @@ class BrowserTabsFragment : BaseFragment(), BrowserTabsAda override fun tabCloseClicked(item: BrowserTabRvItem) { viewModel.closeTab(item.tabId) } - - private fun setupCloseAllTabsDialogue() { - viewModel.closeAllTabsConfirmation.awaitableActionLiveData.observeEvent { event -> - warningDialog( - context = providedContext, - onPositiveClick = { event.onSuccess(Unit) }, - positiveTextRes = R.string.browser_tabs_close_all, - negativeTextRes = R.string.common_cancel, - onNegativeClick = { event.onCancel() }, - styleRes = R.style.AccentNegativeAlertDialogTheme_Reversed - ) { - setTitle(R.string.close_dapp_tabs_title) - - setMessage(R.string.close_dapp_tabs_message) - } - } - } } diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsViewModel.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsViewModel.kt index 3951940598..d12ee324e1 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsViewModel.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/BrowserTabsViewModel.kt @@ -6,6 +6,7 @@ import io.novafoundation.nova.common.mixin.actionAwaitable.awaitAction import io.novafoundation.nova.common.mixin.actionAwaitable.confirmingAction import io.novafoundation.nova.common.utils.mapList import io.novafoundation.nova.feature_dapp_api.DAppRouter +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_dapp_impl.presentation.search.DAppSearchRequester import io.novafoundation.nova.feature_dapp_impl.presentation.search.SearchPayload import io.novafoundation.nova.feature_dapp_impl.utils.tabs.BrowserTabPoolService @@ -33,8 +34,7 @@ class BrowserTabsViewModel( } fun openTab(tabId: String) = launch { - browserTabPoolService.selectTab(tabId) - router.back() + router.openDAppBrowser(DAppBrowserPayload.Tab(tabId)) } fun closeTab(tabId: String) = launch { diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/Dialogs.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/Dialogs.kt new file mode 100644 index 0000000000..06626093f3 --- /dev/null +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/presentation/tab/Dialogs.kt @@ -0,0 +1,23 @@ +package io.novafoundation.nova.feature_dapp_impl.presentation.tab + +import io.novafoundation.nova.common.base.BaseFragment +import io.novafoundation.nova.common.mixin.actionAwaitable.ConfirmationAwaitable +import io.novafoundation.nova.common.view.dialog.warningDialog +import io.novafoundation.nova.feature_dapp_impl.R + +fun BaseFragment<*>.setupCloseAllDappTabsDialogue(mixin: ConfirmationAwaitable) { + mixin.awaitableActionLiveData.observeEvent { event -> + warningDialog( + context = providedContext, + onPositiveClick = { event.onSuccess(Unit) }, + positiveTextRes = R.string.browser_tabs_close_all, + negativeTextRes = R.string.common_cancel, + onNegativeClick = { event.onCancel() }, + styleRes = R.style.AccentNegativeAlertDialogTheme_Reversed + ) { + setTitle(R.string.close_dapp_tabs_title) + + setMessage(R.string.close_dapp_tabs_message) + } + } +} diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/BrowserTabPoolService.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/BrowserTabPoolService.kt index 8c54ea00da..88af3f2f18 100644 --- a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/BrowserTabPoolService.kt +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/BrowserTabPoolService.kt @@ -1,20 +1,7 @@ package io.novafoundation.nova.feature_dapp_impl.utils.tabs -import io.novafoundation.nova.common.utils.Urls -import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.BrowserTab -import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.CurrentTabState -import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.PageSession -import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.PageSessionFactory -import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.PageSnapshot import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.TabsState -import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.withNameOnly -import java.util.Date -import java.util.UUID import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map interface BrowserTabPoolService { @@ -32,106 +19,3 @@ interface BrowserTabPoolService { suspend fun removeAllTabs() } - -class RealBrowserTabPoolService( - private val browserTabStorage: BrowserTabStorage, - private val pageSnapshotBuilder: PageSnapshotBuilder, - private val tabMemoryRestrictionService: TabMemoryRestrictionService, - private val pageSessionFactory: PageSessionFactory -) : BrowserTabPoolService { - - private val availableSessionsCount = tabMemoryRestrictionService.getMaximumActiveSessions() - - private val selectedTabIdFlow = MutableStateFlow(null) - - private val allTabsFlow = browserTabStorage.observeTabs() - .map { tabs -> tabs.associateBy { it.id } } - - private val activeSessions = mutableMapOf() - - override val tabStateFlow = combine( - selectedTabIdFlow, - allTabsFlow - ) { selectedTabId, allTabs -> - TabsState( - tabs = allTabs.values.toList(), - selectedTab = currentTabState(selectedTabId, allTabs) - ) - } - - override fun selectTab(tabId: String?) { - val oldTabId = selectedTabIdFlow.value - detachSession(oldTabId) - - selectedTabIdFlow.value = tabId - } - - override fun detachCurrentSession() { - selectTab(null) - } - - override suspend fun createNewTabAsCurrentTab(url: String) { - val tab = BrowserTab( - id = UUID.randomUUID().toString(), - pageSnapshot = PageSnapshot.withNameOnly(Urls.domainOf(url)), - currentUrl = url, - creationTime = Date() - ) - - browserTabStorage.saveTab(tab) - selectTab(tab.id) - } - - override suspend fun removeTab(tabId: String) { - if (tabId == selectedTabIdFlow.value) { - selectTab(null) - } - - browserTabStorage.removeTab(tabId) - } - - override suspend fun removeAllTabs() { - selectTab(null) - browserTabStorage.removeAllTabs() - } - - override suspend fun makeCurrentTabSnapshot() { - val currentTab = selectedTabIdFlow.first() - val pageSession = activeSessions[currentTab] - - if (pageSession != null) { - val snapshot = pageSnapshotBuilder.getPageSnapshot(pageSession) - browserTabStorage.savePageSnapshot(pageSession.tabId, snapshot) - } - } - - private fun addNewSession(tab: BrowserTab): PageSession { - if (activeSessions.size >= availableSessionsCount) { - removeOldestSession() - } - - val session = pageSessionFactory.create(tabId = tab.id, sessionStartTime = Date(), startUrl = tab.currentUrl) - activeSessions[tab.id] = session - return session - } - - private fun removeOldestSession() { - val sessionToDestroy = activeSessions.values.minBy { it.sessionStartTime.time } - sessionToDestroy.destroySession() - activeSessions.remove(sessionToDestroy.tabId) - } - - private fun detachSession(tabId: String?) { - val sessionToDetach = activeSessions[tabId] - sessionToDetach?.detachSession() - } - - private fun currentTabState(selectedTabId: String?, allTabs: Map): CurrentTabState { - val tabId = selectedTabId ?: return CurrentTabState.NotSelected - val tab = allTabs[tabId] ?: return CurrentTabState.NotSelected - return CurrentTabState.Selected( - tab, - activeSessions[tabId] ?: addNewSession(tab) - ) - } -} diff --git a/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/RealBrowserTabPoolService.kt b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/RealBrowserTabPoolService.kt new file mode 100644 index 0000000000..f782ca7452 --- /dev/null +++ b/feature-dapp-impl/src/main/java/io/novafoundation/nova/feature_dapp_impl/utils/tabs/RealBrowserTabPoolService.kt @@ -0,0 +1,119 @@ +package io.novafoundation.nova.feature_dapp_impl.utils.tabs + +import io.novafoundation.nova.common.utils.Urls +import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.BrowserTab +import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.CurrentTabState +import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.PageSession +import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.PageSessionFactory +import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.PageSnapshot +import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.TabsState +import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.withNameOnly +import java.util.Date +import java.util.UUID +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map + +class RealBrowserTabPoolService( + private val browserTabStorage: BrowserTabStorage, + private val pageSnapshotBuilder: PageSnapshotBuilder, + private val tabMemoryRestrictionService: TabMemoryRestrictionService, + private val pageSessionFactory: PageSessionFactory +) : BrowserTabPoolService { + + private val availableSessionsCount = tabMemoryRestrictionService.getMaximumActiveSessions() + + private val selectedTabIdFlow = MutableStateFlow(null) + + private val allTabsFlow = browserTabStorage.observeTabs() + .map { tabs -> tabs.associateBy { it.id } } + + private val activeSessions = mutableMapOf() + + override val tabStateFlow = combine( + selectedTabIdFlow, + allTabsFlow + ) { selectedTabId, allTabs -> + TabsState( + tabs = allTabs.values.toList(), + selectedTab = currentTabState(selectedTabId, allTabs) + ) + } + + override fun selectTab(tabId: String?) { + val oldTabId = selectedTabIdFlow.value + detachSession(oldTabId) + + selectedTabIdFlow.value = tabId + } + + override fun detachCurrentSession() { + selectTab(null) + } + + override suspend fun createNewTabAsCurrentTab(url: String) { + val tab = BrowserTab( + id = UUID.randomUUID().toString(), + pageSnapshot = PageSnapshot.withNameOnly(Urls.domainOf(url)), + currentUrl = url, + creationTime = Date() + ) + + browserTabStorage.saveTab(tab) + selectTab(tab.id) + } + + override suspend fun removeTab(tabId: String) { + if (tabId == selectedTabIdFlow.value) { + selectTab(null) + } + + browserTabStorage.removeTab(tabId) + } + + override suspend fun removeAllTabs() { + selectTab(null) + browserTabStorage.removeAllTabs() + } + + override suspend fun makeCurrentTabSnapshot() { + val currentTab = selectedTabIdFlow.first() + val pageSession = activeSessions[currentTab] + + if (pageSession != null) { + val snapshot = pageSnapshotBuilder.getPageSnapshot(pageSession) + browserTabStorage.savePageSnapshot(pageSession.tabId, snapshot) + } + } + + private fun addNewSession(tab: BrowserTab): PageSession { + if (activeSessions.size >= availableSessionsCount) { + removeOldestSession() + } + + val session = pageSessionFactory.create(tabId = tab.id, sessionStartTime = Date(), startUrl = tab.currentUrl) + activeSessions[tab.id] = session + return session + } + + private fun removeOldestSession() { + val sessionToDestroy = activeSessions.values.minBy { it.sessionStartTime.time } + sessionToDestroy.destroySession() + activeSessions.remove(sessionToDestroy.tabId) + } + + private fun detachSession(tabId: String?) { + val sessionToDetach = activeSessions[tabId] + sessionToDetach?.detachSession() + } + + private fun currentTabState(selectedTabId: String?, allTabs: Map): CurrentTabState { + val tabId = selectedTabId ?: return CurrentTabState.NotSelected + val tab = allTabs[tabId] ?: return CurrentTabState.NotSelected + return CurrentTabState.Selected( + tab, + activeSessions[tabId] ?: addNewSession(tab) + ) + } +} diff --git a/feature-governance-impl/src/main/java/io/novafoundation/nova/feature_governance_impl/presentation/GovernanceRouter.kt b/feature-governance-impl/src/main/java/io/novafoundation/nova/feature_governance_impl/presentation/GovernanceRouter.kt index ae5e9c01f0..dc7ceaeffa 100644 --- a/feature-governance-impl/src/main/java/io/novafoundation/nova/feature_governance_impl/presentation/GovernanceRouter.kt +++ b/feature-governance-impl/src/main/java/io/novafoundation/nova/feature_governance_impl/presentation/GovernanceRouter.kt @@ -27,7 +27,7 @@ interface GovernanceRouter : ReturnableRouter { fun openReferendaFilters() - fun openDAppBrowser(initialUrl: String) + fun openDAppBrowser(url: String) fun openReferendumDescription(payload: DescriptionPayload) diff --git a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/presentation/dashboard/more/MoreStakingOptionsViewModel.kt b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/presentation/dashboard/more/MoreStakingOptionsViewModel.kt index f0f84aae58..8e6739303a 100644 --- a/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/presentation/dashboard/more/MoreStakingOptionsViewModel.kt +++ b/feature-staking-impl/src/main/java/io/novafoundation/nova/feature_staking_impl/presentation/dashboard/more/MoreStakingOptionsViewModel.kt @@ -3,6 +3,7 @@ package io.novafoundation.nova.feature_staking_impl.presentation.dashboard.more import io.novafoundation.nova.common.base.BaseViewModel import io.novafoundation.nova.common.domain.map import io.novafoundation.nova.feature_dapp_api.DAppRouter +import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload import io.novafoundation.nova.feature_staking_api.domain.dashboard.StakingDashboardInteractor import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.AggregatedStakingDashboardOption import io.novafoundation.nova.feature_staking_api.domain.dashboard.model.MoreStakingOptions @@ -53,7 +54,7 @@ class MoreStakingOptionsViewModel( } fun onBrowserStakingItemClicked(item: StakingDAppModel) = launch { - dappRouter.openDAppBrowser(item.url) + dappRouter.openDAppBrowser(DAppBrowserPayload.Address(item.url)) } private fun syncDApps() = launch { From ff7f02950a04f89f4fca5c717ea4564b89fc91d8 Mon Sep 17 00:00:00 2001 From: antonijzelinskij Date: Tue, 26 Nov 2024 11:40:05 +0100 Subject: [PATCH 2/4] Added round corners for the main container --- .../RoundCornersOutlineProvider.kt | 14 +++++++++++++ .../splitScreen/SplitScreenFragment.kt | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/RoundCornersOutlineProvider.kt diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/RoundCornersOutlineProvider.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/RoundCornersOutlineProvider.kt new file mode 100644 index 0000000000..541531fadc --- /dev/null +++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/RoundCornersOutlineProvider.kt @@ -0,0 +1,14 @@ +package io.novafoundation.nova.app.root.presentation.splitScreen + +import android.graphics.Outline +import android.view.View +import android.view.ViewOutlineProvider + +class RoundCornersOutlineProvider( + private val cornerRadius: Float +) : ViewOutlineProvider() { + + override fun getOutline(view: View, outline: Outline) { + outline.setRoundRect(0, 0, view.width, view.height, cornerRadius) + } +} diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt index c9792efd51..c8c3838842 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt @@ -1,9 +1,13 @@ package io.novafoundation.nova.app.root.presentation.splitScreen +import android.graphics.Outline +import android.graphics.drawable.ShapeDrawable +import android.graphics.drawable.shapes.RoundRectShape import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.ViewOutlineProvider import androidx.core.view.isVisible import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment @@ -18,6 +22,7 @@ import javax.inject.Inject import kotlinx.android.synthetic.main.fragment_split_screen.dappEntryPoint import kotlinx.android.synthetic.main.fragment_split_screen.dappEntryPointClose import kotlinx.android.synthetic.main.fragment_split_screen.dappEntryPointText +import kotlinx.android.synthetic.main.fragment_split_screen.mainNavHost class SplitScreenFragment : BaseFragment() { @@ -50,12 +55,20 @@ class SplitScreenFragment : BaseFragment() { override fun initViews() { dappEntryPoint.setOnClickListener { viewModel.onTabsClicked() } dappEntryPointClose.setOnClickListener { viewModel.onTabsCloseClicked() } + } override fun subscribe(viewModel: SplitScreenViewModel) { setupCloseAllDappTabsDialogue(viewModel.closeAllTabsConfirmation) viewModel.dappTabsQuantity.observe { + val shouldBeVisible = it > 0 + val isVisibilityChanged = dappEntryPoint.isVisible != shouldBeVisible + + if (isVisibilityChanged) { + mainNavHost.outlineProvider = RoundCornersOutlineProvider(getOutlineCornerRadius(shouldBeVisible)) + } + dappEntryPoint.isVisible = it > 0 dappEntryPointText.text = getString(R.string.dapp_entry_point_title, it) } @@ -66,4 +79,12 @@ class SplitScreenFragment : BaseFragment() { navHostFragment.navController } + + private fun getOutlineCornerRadius(isRounded: Boolean): Float { + return if (isRounded) { + 12f + } else { + 0f + } + } } From 4d491f0e1645fa2b8de2b5b67afc02ed2a36cd19 Mon Sep 17 00:00:00 2001 From: antonijzelinskij Date: Tue, 26 Nov 2024 11:52:54 +0100 Subject: [PATCH 3/4] Fixed back navigation --- app/src/main/res/navigation/dapp_browser_graph.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/res/navigation/dapp_browser_graph.xml b/app/src/main/res/navigation/dapp_browser_graph.xml index 3c20baf09a..9b54e9b0c5 100644 --- a/app/src/main/res/navigation/dapp_browser_graph.xml +++ b/app/src/main/res/navigation/dapp_browser_graph.xml @@ -70,11 +70,20 @@ + + From 9a82d303169fb0680aad1b14ff9c98ffb5fec8c5 Mon Sep 17 00:00:00 2001 From: antonijzelinskij Date: Tue, 26 Nov 2024 11:54:10 +0100 Subject: [PATCH 4/4] Update SplitScreenFragment.kt --- .../app/root/presentation/splitScreen/SplitScreenFragment.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt index c8c3838842..97ca261bfc 100644 --- a/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt +++ b/app/src/main/java/io/novafoundation/nova/app/root/presentation/splitScreen/SplitScreenFragment.kt @@ -1,13 +1,9 @@ package io.novafoundation.nova.app.root.presentation.splitScreen -import android.graphics.Outline -import android.graphics.drawable.ShapeDrawable -import android.graphics.drawable.shapes.RoundRectShape import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewOutlineProvider import androidx.core.view.isVisible import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment @@ -55,7 +51,6 @@ class SplitScreenFragment : BaseFragment() { override fun initViews() { dappEntryPoint.setOnClickListener { viewModel.onTabsClicked() } dappEntryPointClose.setOnClickListener { viewModel.onTabsCloseClicked() } - } override fun subscribe(viewModel: SplitScreenViewModel) {