diff --git a/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt b/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt index 166977b2..bfd7908f 100644 --- a/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt +++ b/v4/app/src/main/java/exchange/dydx/trading/AppModule.kt @@ -46,6 +46,7 @@ import exchange.dydx.platformui.designSystem.theme.ThemeConfig import exchange.dydx.platformui.designSystem.theme.ThemeSettings import exchange.dydx.trading.common.AppConfig import exchange.dydx.trading.common.AppConfigImpl +import exchange.dydx.trading.common.di.CoroutineScopes import exchange.dydx.trading.common.theme.DydxTheme import exchange.dydx.trading.common.theme.DydxThemeImpl import exchange.dydx.trading.feature.shared.PreferenceKeys @@ -57,6 +58,7 @@ import exchange.dydx.trading.integration.cosmos.CosmosV4ClientWebview import exchange.dydx.trading.integration.cosmos.CosmosV4WebviewClientProtocol import exchange.dydx.utilities.utils.JsonUtils import exchange.dydx.utilities.utils.SharedPreferencesStore +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.serialization.json.Json import javax.inject.Singleton @@ -109,10 +111,13 @@ interface AppModule { fun provideLanguageKey(): String = PreferenceKeys.Language @Provides - fun providePlatformInfo(): PlatformInfo = + fun providePlatformInfo( + @CoroutineScopes.App appScope: CoroutineScope, + ): PlatformInfo = PlatformInfo( snackbarHostState = SnackbarHostState(), infoType = MutableStateFlow(PlatformInfo.InfoType.Info), + appScope = appScope, ) @Provides diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/di/CoroutineDispatchers.kt b/v4/common/src/main/java/exchange/dydx/trading/common/di/CoroutineDispatchers.kt new file mode 100644 index 00000000..1d042b65 --- /dev/null +++ b/v4/common/src/main/java/exchange/dydx/trading/common/di/CoroutineDispatchers.kt @@ -0,0 +1,31 @@ +package exchange.dydx.trading.common.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import javax.inject.Qualifier + +object CoroutineDispatchers { + + @Qualifier annotation class Main + + @Qualifier annotation class IO + + @Qualifier annotation class Default +} + +@Module +@InstallIn(SingletonComponent::class) +object CoroutineDispatchersModule { + @Provides @CoroutineDispatchers.Main + fun provideMain(): CoroutineDispatcher = Dispatchers.Main + + @Provides @CoroutineDispatchers.IO + fun provideIO(): CoroutineDispatcher = Dispatchers.IO + + @Provides @CoroutineDispatchers.Default + fun provideDefault(): CoroutineDispatcher = Dispatchers.Default +} diff --git a/v4/common/src/main/java/exchange/dydx/trading/common/di/CoroutineScopes.kt b/v4/common/src/main/java/exchange/dydx/trading/common/di/CoroutineScopes.kt new file mode 100644 index 00000000..5f0f5115 --- /dev/null +++ b/v4/common/src/main/java/exchange/dydx/trading/common/di/CoroutineScopes.kt @@ -0,0 +1,40 @@ +package exchange.dydx.trading.common.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.ViewModelLifecycle +import dagger.hilt.android.components.ViewModelComponent +import dagger.hilt.android.scopes.ViewModelScoped +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import javax.inject.Qualifier +import javax.inject.Singleton + +object CoroutineScopes { + @Qualifier annotation class App + + @Qualifier annotation class ViewModel +} + +@Module +@InstallIn(SingletonComponent::class) +object AppScopeModule { + @Provides @CoroutineScopes.App @Singleton + fun provideScope(): CoroutineScope = MainScope() +} + +@Module +@InstallIn(ViewModelComponent::class) +object ViewModelScopeModule { + @Provides @CoroutineScopes.ViewModel @ViewModelScoped + fun provideScope(viewModelLifecycle: ViewModelLifecycle): CoroutineScope { + val scope = MainScope() + viewModelLifecycle.addOnClearedListener { + scope.cancel() + } + return scope + } +} diff --git a/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/reportissue/DydxReportIssueViewModel.kt b/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/reportissue/DydxReportIssueViewModel.kt index 0aee74da..f802dd8e 100644 --- a/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/reportissue/DydxReportIssueViewModel.kt +++ b/v4/feature/profile/src/main/java/exchange/dydx/trading/feature/profile/reportissue/DydxReportIssueViewModel.kt @@ -11,11 +11,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext import exchange.dydx.abacus.protocols.LocalizerProtocol import exchange.dydx.platformui.components.PlatformInfo import exchange.dydx.trading.common.DydxViewModel +import exchange.dydx.trading.common.di.CoroutineDispatchers import exchange.dydx.trading.common.navigation.DydxRouter import exchange.dydx.utilities.utils.EmailUtils import exchange.dydx.utilities.utils.FileUtils import exchange.dydx.utilities.utils.LogCatReader -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map @@ -30,6 +31,7 @@ class DydxReportIssueViewModel @Inject constructor( private val localizer: LocalizerProtocol, private val router: DydxRouter, @ApplicationContext private val context: Context, + @CoroutineDispatchers.IO private val ioDispatcher: CoroutineDispatcher, val platformInfo: PlatformInfo, ) : ViewModel(), DydxViewModel { @@ -44,7 +46,7 @@ class DydxReportIssueViewModel @Inject constructor( viewModelScope.launch { var logUri: Uri? = null - withContext(Dispatchers.IO) { + withContext(ioDispatcher) { // add a delay to show the loading text kotlinx.coroutines.delay(500) logUri = createLog() diff --git a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/analytics/OnboardingAnalytics.kt b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/analytics/OnboardingAnalytics.kt index 41d0133c..ac1de8d9 100644 --- a/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/analytics/OnboardingAnalytics.kt +++ b/v4/feature/shared/src/main/java/exchange/dydx/trading/feature/shared/analytics/OnboardingAnalytics.kt @@ -1,9 +1,11 @@ package exchange.dydx.trading.feature.shared.analytics import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.di.CoroutineDispatchers +import exchange.dydx.trading.common.di.CoroutineScopes import exchange.dydx.trading.integration.analytics.Tracking -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.MainScope +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.take @@ -15,6 +17,8 @@ import javax.inject.Singleton class OnboardingAnalytics @Inject constructor( private val tracker: Tracking, private val abacusStateManager: AbacusStateManagerProtocol, + @CoroutineScopes.App private val appScope: CoroutineScope, + @CoroutineDispatchers.IO private val ioDispatcher: CoroutineDispatcher, ) { // The three main OnboardingStates: // - Disconnected @@ -46,7 +50,7 @@ class OnboardingAnalytics @Inject constructor( DEPOSIT_FUNDS("DepositFunds") } - private val scope = MainScope() + Dispatchers.IO + private val scope = appScope + ioDispatcher fun log(step: OnboardingSteps) { abacusStateManager.state.currentWallet diff --git a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/streams/TradeStream.kt b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/streams/TradeStream.kt index 16729366..0cbed37d 100644 --- a/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/streams/TradeStream.kt +++ b/v4/feature/trade/src/main/java/exchange/dydx/trading/feature/trade/streams/TradeStream.kt @@ -4,6 +4,7 @@ import dagger.hilt.android.scopes.ActivityRetainedScoped import exchange.dydx.abacus.output.SubaccountOrder import exchange.dydx.abacus.state.model.TradeInputField import exchange.dydx.dydxstatemanager.AbacusStateManagerProtocol +import exchange.dydx.trading.common.di.CoroutineScopes import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -12,7 +13,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.coroutines.newSingleThreadContext import javax.inject.Inject interface TradeStreaming { @@ -28,10 +28,9 @@ interface MutableTradeStreaming : TradeStreaming { @ActivityRetainedScoped class TradeStream @Inject constructor( val abacusStateManager: AbacusStateManagerProtocol, + @CoroutineScopes.App val appScope: CoroutineScope ) : MutableTradeStreaming { - private val streamScope = CoroutineScope(newSingleThreadContext("TradeStream")) - private var _submissionStatus: MutableStateFlow = MutableStateFlow(null) override val submissionStatus: Flow = _submissionStatus @@ -59,7 +58,7 @@ class TradeStream @Inject constructor( override fun submitTrade() { _submissionStatus.update { null } - streamScope.launch { + appScope.launch { val tradeInput = abacusStateManager.state.tradeInput.first() ?: return@launch abacusStateManager.placeOrder { submissionStatus -> @@ -73,7 +72,7 @@ class TradeStream @Inject constructor( override fun closePosition() { _submissionStatus.update { null } - streamScope.launch { + appScope.launch { val closePositionInput = abacusStateManager.state.closePositionInput.first() ?: return@launch abacusStateManager.closePosition { submissionStatus -> diff --git a/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt b/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt index 929bd128..e89c4971 100644 --- a/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt +++ b/v4/integration/cosmos/src/main/java/exchange/dydx/trading/integration/cosmos/CosmosV4ClientWebview.kt @@ -4,6 +4,8 @@ import android.app.Application import android.util.Log import exchange.dydx.integration.javascript.JavascriptApiImpl import exchange.dydx.integration.javascript.JavascriptRunnerV4 +import exchange.dydx.trading.common.di.CoroutineScopes +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.runBlocking import java.io.IOException import java.util.Locale @@ -17,11 +19,12 @@ private const val TAG = "CosmosV4ClientWebview" @Singleton class CosmosV4ClientWebview @Inject constructor( application: Application, + @CoroutineScopes.App appScope: CoroutineScope, ) : CosmosV4WebviewClientProtocol, JavascriptApiImpl( context = application, description = WEBVIEW_FILENAME, - runner = JavascriptRunnerV4.runnerFromFile(application, WEBVIEW_FILENAME) + runner = JavascriptRunnerV4.runnerFromFile(appScope, application, WEBVIEW_FILENAME) ?: throw IOException("Fatal, unable to load runner from: $WEBVIEW_FILENAME"), ) { diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt index 4a0e10ff..8e94edb9 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/AbacusStateManager.kt @@ -44,12 +44,12 @@ import exchange.dydx.dydxstatemanager.clientState.wallets.DydxWalletInstance import exchange.dydx.dydxstatemanager.clientState.wallets.DydxWalletStateManagerProtocol import exchange.dydx.dydxstatemanager.protocolImplementations.UIImplementationsExtensions import exchange.dydx.trading.common.R +import exchange.dydx.trading.common.di.CoroutineScopes import exchange.dydx.trading.common.featureflags.DydxFeatureFlag import exchange.dydx.trading.common.featureflags.DydxFeatureFlags import exchange.dydx.trading.integration.cosmos.CosmosV4ClientProtocol import exchange.dydx.utilities.utils.SharedPreferencesStore import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first @@ -130,6 +130,7 @@ class AbacusStateManager @Inject constructor( private val preferencesStore: SharedPreferencesStore, @EnvKey private val envKey: String, private val featureFlags: DydxFeatureFlags, + @CoroutineScopes.App private val appScope: CoroutineScope, parser: ParserProtocol, ) : AbacusStateManagerProtocol, StateNotificationProtocol { @@ -429,7 +430,7 @@ class AbacusStateManager @Inject constructor( } private fun start() { - CoroutineScope(Dispatchers.Main).launch { + appScope.launch { val currentWallet = walletStateManager.state.first()?.currentWallet if (currentWallet != null) { val ethereumAddress = currentWallet.ethereumAddress diff --git a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt index 4650047d..9e606b28 100644 --- a/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt +++ b/v4/integration/dydxStateManager/src/main/java/exchange/dydx/dydxstatemanager/protocolImplementations/AbacusThreadingImp.kt @@ -2,41 +2,35 @@ package exchange.dydx.dydxstatemanager.protocolImplementations import exchange.dydx.abacus.protocols.ThreadingProtocol import exchange.dydx.abacus.protocols.ThreadingType -import kotlinx.coroutines.Dispatchers +import exchange.dydx.abacus.protocols.ThreadingType.abacus +import exchange.dydx.abacus.protocols.ThreadingType.main +import exchange.dydx.abacus.protocols.ThreadingType.network +import exchange.dydx.trading.common.di.CoroutineDispatchers +import exchange.dydx.trading.common.di.CoroutineScopes +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import kotlinx.coroutines.plus +import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton @OptIn(ExperimentalCoroutinesApi::class) -class AbacusThreadingImp @Inject constructor() : ThreadingProtocol { - private val mainScope = MainScope() - - // Abacus runs lots of computations, but needs to be run without parallelism - private val abacusScope = MainScope() + Dispatchers.Default.limitedParallelism(1) - private val networkScope = MainScope() + Dispatchers.IO +class AbacusThreadingImp @Inject constructor( + @CoroutineScopes.App private val appScope: CoroutineScope, + @CoroutineDispatchers.IO private val ioDispatcher: CoroutineDispatcher, + @CoroutineDispatchers.Default private val defaultDispatcher: CoroutineDispatcher, +) : ThreadingProtocol { override fun async(type: ThreadingType, block: () -> Unit) { - when (type) { - ThreadingType.main -> - mainScope - .launch { - block() - } - - ThreadingType.abacus -> - abacusScope - .launch { - block() - } - - ThreadingType.network -> - networkScope - .launch { - block() - } + appScope.launch { + when (type) { + main -> block() + // Abacus runs lots of computations, but needs to be run without parallelism + abacus -> withContext(defaultDispatcher.limitedParallelism(1)) { block() } + network -> withContext(ioDispatcher) { block() } + } } } } diff --git a/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV3.kt b/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV3.kt index 47554815..3e60531a 100644 --- a/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV3.kt +++ b/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV3.kt @@ -6,14 +6,11 @@ import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import timber.log.Timber import java.io.InputStream -import java.time.ZonedDateTime private const val TAG: String = "JavascriptRunner(V3)" @@ -35,19 +32,20 @@ private fun loadAsset(context: Context, fileName: String?): String? { class JavascriptRunnerV3 constructor( private val scriptDescription: String, private val scriptInitializationCode: String, - private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO), + private val scope: CoroutineScope, ) : JavascriptRunner { override val initialized = MutableStateFlow(false) companion object { fun runnerFromFile( + scope: CoroutineScope, context: Context, file: String, ): JavascriptRunnerV3? { val script = loadAsset(context, file) if (script != null) { - return JavascriptRunnerV3(file, script) + return JavascriptRunnerV3(file, script, scope) } return null } @@ -85,7 +83,7 @@ class JavascriptRunnerV3 constructor( } override fun onPageFinished(view: WebView, weburl: String) { - coroutineScope.launch { + scope.launch { initializeJavascriptEnvironment() } } @@ -102,28 +100,17 @@ class JavascriptRunnerV3 constructor( Timber.tag(TAG).w("Unable to run script, assign a webview first.\n%s", script) return } - CoroutineScope(Main).launch { - try { - val time = ZonedDateTime.now() - webview.evaluateJavascript(script) { resultString: String -> - try { - Timber.tag(TAG).i("Evaluated javascript: %s", resultString) - -// if (SDK_INT >= O) { -// val interval = Duration.between(time, ZonedDateTime.now()) -// Timber.tag(TAG).i("Javascript time interval: $interval") -// } - -// val replacedResult = (resultString as? String)?.replace("\"", "") - - callback.invoke(JavascriptRunnerResult(resultString)) - } catch (e: Exception) { - Timber.tag(TAG).e(e) - } + try { + webview.evaluateJavascript(script) { resultString: String -> + try { + Timber.tag(TAG).i("Evaluated javascript: %s", resultString) + callback.invoke(JavascriptRunnerResult(resultString)) + } catch (e: Exception) { + Timber.tag(TAG).e(e) } - } catch (e: Exception) { - Timber.tag(TAG).e(e) } + } catch (e: Exception) { + Timber.tag(TAG).e(e) } } diff --git a/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV4.kt b/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV4.kt index a4d1ccdd..9eded924 100644 --- a/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV4.kt +++ b/v4/integration/javascript/src/main/java/exchange/dydx/integration/javascript/JavascriptRunnerV4.kt @@ -8,7 +8,6 @@ import android.webkit.WebView import android.webkit.WebViewClient import exchange.dydx.trading.common.AppConfig import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import timber.log.Timber @@ -21,6 +20,7 @@ val LOGGER = if (DO_LOG) Timber.tag(TAG) else null class JavascriptRunnerV4 constructor( private val scriptDescription: String, private val scriptInitializationCode: String, + private val scope: CoroutineScope, ) : JavascriptRunner { override val initialized = MutableStateFlow(false) @@ -29,12 +29,13 @@ class JavascriptRunnerV4 constructor( companion object { fun runnerFromFile( + scope: CoroutineScope, context: Context, file: String, ): JavascriptRunnerV4? { val script = JavascriptUtils.loadAsset(context, file) if (script != null) { - return JavascriptRunnerV4(file, script) + return JavascriptRunnerV4(file, script, scope) } return null } @@ -121,7 +122,7 @@ class JavascriptRunnerV4 constructor( val pattern = Pattern.compile("""^"(.*)"\$""") private fun launchJs(script: String, callback: ResultCallback) { - CoroutineScope(Main).launch { + scope.launch { webappInterface.callback = callback val localWebview = webview if (localWebview == null) { diff --git a/v4/integration/starkex/src/main/java/exchange/dydx/integration/starkex/StarkexLib.kt b/v4/integration/starkex/src/main/java/exchange/dydx/integration/starkex/StarkexLib.kt index 0d028c52..a0706bfb 100644 --- a/v4/integration/starkex/src/main/java/exchange/dydx/integration/starkex/StarkexLib.kt +++ b/v4/integration/starkex/src/main/java/exchange/dydx/integration/starkex/StarkexLib.kt @@ -4,6 +4,8 @@ import android.app.Application import dagger.hilt.android.scopes.ActivityRetainedScoped import exchange.dydx.integration.javascript.JavascriptApiImpl import exchange.dydx.integration.javascript.JavascriptRunnerV3 +import exchange.dydx.trading.common.di.CoroutineScopes +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.runBlocking import timber.log.Timber import java.io.IOException @@ -14,11 +16,12 @@ private const val STARKEX_FILENAME: String = "starkex-lib.js" @ActivityRetainedScoped class StarkexLib @Inject constructor( application: Application, + @CoroutineScopes.App appScope: CoroutineScope, ) : JavascriptApiImpl( context = application, description = STARKEX_FILENAME, - runner = JavascriptRunnerV3.runnerFromFile(application, STARKEX_FILENAME) + runner = JavascriptRunnerV3.runnerFromFile(appScope, application, STARKEX_FILENAME) ?: throw IOException("Fatal, unable to load runner from: $STARKEX_FILENAME"), ) { @@ -60,9 +63,10 @@ private const val STARKEX_ETH_FILENAME: String = "starkex-eth.js" @ActivityRetainedScoped class StarkexEth @Inject constructor( application: Application, + @CoroutineScopes.App appScope: CoroutineScope, ) : JavascriptApiImpl( context = application, description = STARKEX_ETH_FILENAME, - runner = JavascriptRunnerV3.runnerFromFile(application, STARKEX_ETH_FILENAME) + runner = JavascriptRunnerV3.runnerFromFile(appScope, application, STARKEX_ETH_FILENAME) ?: throw IOException("Fatal, unable to load runner from: $STARKEX_ETH_FILENAME"), ) diff --git a/v4/platformUI/src/main/java/exchange/dydx/platformui/components/PlatformInfo.kt b/v4/platformUI/src/main/java/exchange/dydx/platformui/components/PlatformInfo.kt index b352457a..9c1bd30b 100644 --- a/v4/platformUI/src/main/java/exchange/dydx/platformui/components/PlatformInfo.kt +++ b/v4/platformUI/src/main/java/exchange/dydx/platformui/components/PlatformInfo.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.graphics.Color import exchange.dydx.platformui.designSystem.theme.ThemeColor import exchange.dydx.platformui.designSystem.theme.color import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.serialization.json.JsonNull.content @@ -53,7 +52,8 @@ fun PlatformInfoScaffold( data class PlatformInfo( internal val snackbarHostState: SnackbarHostState, - internal val infoType: MutableStateFlow + internal val infoType: MutableStateFlow, + private val appScope: CoroutineScope, ) { enum class InfoType { Error, Info, Warning; @@ -82,7 +82,7 @@ data class PlatformInfo( duration: SnackbarDuration = SnackbarDuration.Short, ) { infoType.value = type - CoroutineScope(Dispatchers.Main).launch { + appScope.launch { val result = snackbarHostState.showSnackbar( message = if (title.isNullOrBlank()) message else title + "\n" + message, actionLabel = buttonTitle,