Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.flipcash.app.backupkey.internal

import com.flipcash.app.accesskey.BaseAccessKeyViewModel
import com.flipcash.app.core.storage.MediaSaver
import com.flipcash.libs.coroutines.DispatcherProvider
import com.flipcash.services.user.UserManager
import com.getcode.libs.qr.QRCodeGenerator
import com.getcode.opencode.managers.MnemonicManager
Expand All @@ -20,12 +21,14 @@ internal class BackupKeyScreenViewModel @Inject constructor(
mediaSaver: MediaSaver,
userManager: UserManager,
qrCodeGenerator: QRCodeGenerator,
dispatchers: DispatcherProvider,
) : BaseAccessKeyViewModel(
resources,
mnemonicManager,
mediaSaver,
userManager,
qrCodeGenerator
qrCodeGenerator,
dispatchers,
) {
suspend fun saveImage(): Result<Unit> = saveBitmapToFile().map {
delay(150)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.flipcash.app.login.internal
import com.flipcash.app.accesskey.BaseAccessKeyViewModel
import com.flipcash.app.analytics.Button
import com.flipcash.app.analytics.FlipcashAnalyticsService
import com.flipcash.libs.coroutines.DispatcherProvider
import com.flipcash.app.auth.AuthManager
import com.flipcash.app.core.storage.MediaSaver
import com.flipcash.app.featureflags.FeatureFlag
Expand All @@ -25,11 +26,12 @@ internal class LoginAccessKeyViewModel @Inject constructor(
qrCodeGenerator: QRCodeGenerator,
mediaSaver: MediaSaver,
userManager: UserManager,
dispatchers: DispatcherProvider,
private val userFlags: UserFlagsCoordinator,
private val featureFlags: FeatureFlagController,
private val authManager: AuthManager,
private val analytics: FlipcashAnalyticsService,
): BaseAccessKeyViewModel(resources, mnemonicManager, mediaSaver, userManager, qrCodeGenerator) {
): BaseAccessKeyViewModel(resources, mnemonicManager, mediaSaver, userManager, qrCodeGenerator, dispatchers) {

suspend fun onWroteDownInstead(): Result<Boolean> {
trackButton(Button.WroteAccessKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import com.getcode.crypt.MnemonicPhrase
import com.getcode.manager.BottomBarAction
import com.getcode.manager.BottomBarManager
import com.getcode.opencode.managers.MnemonicManager
import com.flipcash.libs.coroutines.DispatcherProvider
import com.getcode.util.resources.ResourceHelper
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -42,6 +41,7 @@ internal class SeedInputViewModel @Inject constructor(
private val userFlags: UserFlagsCoordinator,
private val resources: ResourceHelper,
private val mnemonicManager: MnemonicManager,
private val dispatchers: DispatcherProvider,
) : ViewModel() {
val uiFlow = MutableStateFlow(SeedInputUiModel())
private val mnemonicCode = mnemonicManager.mnemonicCode
Expand Down Expand Up @@ -78,7 +78,7 @@ internal class SeedInputViewModel @Inject constructor(
uiFlow.value.wordsString.trim().replace(Regex("(\\s)+"), " ").lowercase(Locale.getDefault()).split(" ")
val mnemonic = MnemonicPhrase.newInstance(userWordList) ?: return

CoroutineScope(Dispatchers.IO).launch {
viewModelScope.launch(dispatchers.IO) {
val entropyB64: String
try {
entropyB64 = mnemonicManager.getEncodedBase64(mnemonic)
Expand Down
1 change: 0 additions & 1 deletion apps/flipcash/features/userflags/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ android {

dependencies {
implementation(project(":apps:flipcash:shared:userflags"))
implementation(project(":libs:coroutines"))
implementation(project(":libs:messaging"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.viewModelScope
import com.flipcash.app.core.storage.MediaSaver
import com.flipcash.libs.coroutines.DispatcherProvider
import com.flipcash.app.theme.internal.Flipcash2ColorSpec
import com.flipcash.services.user.UserManager
import com.flipcash.shared.accesskey.R
Expand All @@ -27,8 +28,6 @@ import com.getcode.util.resources.ResourceHelper
import com.getcode.utils.decodeBase64
import androidx.lifecycle.ViewModel
import com.getcode.view.LoadingSuccessState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
Expand Down Expand Up @@ -63,7 +62,8 @@ abstract class BaseAccessKeyViewModel(
private val mnemonicManager: MnemonicManager,
private val mediaSaver: MediaSaver,
userManager: UserManager,
private val qrCodeGenerator: QRCodeGenerator
private val qrCodeGenerator: QRCodeGenerator,
protected val dispatchers: DispatcherProvider,
) : ViewModel() {
val uiFlow = MutableStateFlow(AccessKeyUiModel())

Expand All @@ -88,7 +88,7 @@ abstract class BaseAccessKeyViewModel(
wordsFormatted = wordsFormatted
)

CoroutineScope(Dispatchers.IO).launch {
viewModelScope.launch(dispatchers.IO) {
val accessKeyBitmap = createBitmapForExport(words = words, entropyB64 = entropyB64)
val accessKeyBitmapDisplay =
createBitmapForExport(drawBackground = true, words, entropyB64)
Expand Down Expand Up @@ -128,7 +128,7 @@ abstract class BaseAccessKeyViewModel(
val bitmap = uiFlow.value.accessKeyBitmap
?: return Result.failure(IllegalStateException("No access key?"))

return withContext(Dispatchers.IO) {
return withContext(dispatchers.IO) {
runCatching {
val date: DateFormat = SimpleDateFormat("yyy-MM-dd-h-mm", Locale.CANADA)
val filename = "Flipcash-Recovery-${date.format(Date())}.png"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.flipcash.app.appsettings.inject
import android.content.Context
import com.flipcash.app.appsettings.AppSettingsController
import com.flipcash.app.appsettings.internal.InternalAppSettingsController
import com.flipcash.libs.coroutines.DispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -17,5 +18,6 @@ object AppSettingModule {
@Singleton
fun providesAppSettingsController(
@ApplicationContext context: Context,
): AppSettingsController = InternalAppSettingsController(context)
dispatchers: DispatcherProvider,
): AppSettingsController = InternalAppSettingsController(context, dispatchers)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import androidx.datastore.preferences.preferencesDataStoreFile
import com.flipcash.app.appsettings.AppSetting
import com.flipcash.app.appsettings.AppSettingValue
import com.flipcash.app.appsettings.AppSettingsController
import com.flipcash.libs.coroutines.DispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
Expand All @@ -22,14 +22,15 @@ import kotlinx.coroutines.launch

class InternalAppSettingsController(
private val context: Context,
private val dispatchers: DispatcherProvider,
) : AppSettingsController {

companion object {
private val AppSettingValue.booleanPreferenceKey
get() = booleanPreferencesKey(key)
}

private val dataScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val dataScope: CoroutineScope = CoroutineScope(SupervisorJob() + dispatchers.IO)
private val appSettings = PreferenceDataStoreFactory.create(
corruptionHandler = ReplaceFileCorruptionHandler(
produceNewData = { emptyPreferences() }
Expand Down Expand Up @@ -66,7 +67,7 @@ class InternalAppSettingsController(
// TODO: analytics
}

dataScope.launch(Dispatchers.IO) {
dataScope.launch {
appSettings.edit { prefs ->
prefs[setting.booleanPreferenceKey] = value
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.flipcash.app.core.android.VersionInfo
import com.flipcash.app.updates.AppUpdateController
import com.flipcash.app.updates.internal.GooglePlayAppUpdateController
import com.flipcash.app.userflags.UserFlagsCoordinator
import com.flipcash.services.user.UserManager
import com.flipcash.libs.coroutines.DispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -23,5 +23,6 @@ object AppUpdateModule {
@ActivityContext context: Context,
versionInfo: VersionInfo,
userFlagsCoordinator: UserFlagsCoordinator,
): AppUpdateController = GooglePlayAppUpdateController(context, versionInfo, userFlagsCoordinator)
dispatchers: DispatcherProvider,
): AppUpdateController = GooglePlayAppUpdateController(context, versionInfo, userFlagsCoordinator, dispatchers)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateOptions
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.UpdateAvailability
import com.flipcash.libs.coroutines.DispatcherProvider
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -35,8 +35,9 @@ class GooglePlayAppUpdateController @Inject constructor(
private val context: Context,
private val versionInfo: VersionInfo,
private val userFlags: UserFlagsCoordinator,
private val dispatchers: DispatcherProvider,
) : AppUpdateController {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val scope = CoroutineScope(dispatchers.IO + SupervisorJob())

private val appUpdateManager: AppUpdateManager = AppUpdateManagerFactory.create(context)

Expand Down
1 change: 1 addition & 0 deletions apps/flipcash/shared/authentication/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ dependencies {
testImplementation(kotlin("test"))
testImplementation(libs.bundles.unit.testing)
testImplementation(libs.robolectric)
testImplementation(testFixtures(project(":libs:coroutines")))
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import com.getcode.opencode.controllers.TokenController
import com.getcode.opencode.model.core.ID
import com.getcode.utils.TraceManager
import com.getcode.utils.TraceType
import com.flipcash.libs.coroutines.DispatcherProvider
import com.getcode.utils.network.retryable
import com.getcode.utils.trace
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
Expand All @@ -49,8 +49,9 @@ class AuthManager @Inject constructor(
private val appSettings: AppSettingsCoordinator,
private val userFlags: UserFlagsCoordinator,
private val contactCoordinator: ContactCoordinator,
private val dispatchers: DispatcherProvider,
// private val analytics: AnalyticsService,
) : CoroutineScope by CoroutineScope(Dispatchers.IO) {
) : CoroutineScope by CoroutineScope(dispatchers.IO) {

init {
// Persist onboarding completion when the composable transitions Onboarding → Ready.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import com.getcode.opencode.managers.MnemonicManager
import com.getcode.opencode.model.core.ID
import com.getcode.utils.base58
import com.getcode.utils.encodeBase64
import com.flipcash.libs.coroutines.DispatcherProvider
import com.getcode.vendor.Base58
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
Expand All @@ -46,6 +46,7 @@ class PassphraseCredentialManager @Inject constructor(
private val userManager: UserManager,
private val mnemonicManager: MnemonicManager,
private val featureFlags: FeatureFlagController,
private val dispatchers: DispatcherProvider,
) {
companion object {
private val temporaryEntropyKey = stringPreferencesKey("temporaryEntropy")
Expand All @@ -64,7 +65,7 @@ class PassphraseCredentialManager @Inject constructor(

private val credentialLookupCache = mutableMapOf<String, PasswordCredential>()

private val dataScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val dataScope: CoroutineScope = CoroutineScope(SupervisorJob() + dispatchers.IO)

private val storage = PreferenceDataStoreFactory.create(
corruptionHandler = ReplaceFileCorruptionHandler(
Expand Down Expand Up @@ -369,7 +370,7 @@ class PassphraseCredentialManager @Inject constructor(
return storage.data
.mapNotNull { preferences -> preferences[selectedAccountIdKey] }
.map { accountId ->
withContext(Dispatchers.IO) {
withContext(dispatchers.IO) {
getMetadata(accountId)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import com.flipcash.libs.coroutines.TestDispatcherProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -42,7 +43,8 @@ import kotlin.test.assertTrue
@RunWith(RobolectricTestRunner::class)
class AuthManagerTest {

private val testDispatcher = UnconfinedTestDispatcher()
private val dispatchers = TestDispatcherProvider(UnconfinedTestDispatcher())
private val testDispatcher get() = dispatchers.testDispatcher

private val credentialManager: PassphraseCredentialManager = mockk(relaxed = true)
private val userManager: UserManager = mockk(relaxed = true)
Expand Down Expand Up @@ -90,6 +92,7 @@ class AuthManagerTest {
appSettings = appSettings,
userFlags = userFlags,
contactCoordinator = contactCoordinator,
dispatchers = dispatchers,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.flipcash.services.models.chat.PointerType
import com.flipcash.services.models.chat.TypingNotification
import com.flipcash.services.models.chat.TypingState
import com.flipcash.app.tokens.TokenCoordinator
import com.flipcash.libs.coroutines.DispatcherProvider
import com.flipcash.services.user.UserManager
import com.getcode.opencode.model.accounts.AccountCluster
import com.getcode.opencode.providers.SessionListener
Expand All @@ -43,7 +44,6 @@ import com.getcode.utils.network.NetworkConnectivityListener
import com.getcode.utils.decodeBase58
import com.getcode.utils.trace
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
Expand Down Expand Up @@ -82,21 +82,24 @@ class ChatCoordinator @Inject constructor(
private val userManager: UserManager,
private val tokenCoordinator: TokenCoordinator,
private val featureFlags: FeatureFlagController,
private val dispatchers: DispatcherProvider,
) : SessionListener, DefaultLifecycleObserver {

companion object {
private const val TAG = "ChatCoordinator"
private val HEARTBEAT_INTERVAL = 30.seconds
}

private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val supervisorJob = SupervisorJob()
private val scope = CoroutineScope(dispatchers.IO + supervisorJob)
private val cluster = MutableStateFlow<AccountCluster?>(null)
private val _state = MutableStateFlow(ChatState())
private var syncJob: Job? = null
private var flagObserverJob: Job? = null
private var eventStreamCollectJob: Job? = null
private var feedObserverJob: Job? = null
private var heartbeatJob: Job? = null
private var networkObserverJob: Job? = null
private var backgroundedActiveChat: ChatId? = null

val state: StateFlow<ChatState>
Expand Down Expand Up @@ -148,7 +151,7 @@ class ChatCoordinator @Inject constructor(
init {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)

cluster.filterNotNull()
networkObserverJob = cluster.filterNotNull()
.flatMapLatest { networkObserver.state }
.distinctUntilChanged()
.filter { it.connected }
Expand Down Expand Up @@ -337,12 +340,14 @@ class ChatCoordinator @Inject constructor(
syncJob?.cancel()
flagObserverJob?.cancel()
feedObserverJob?.cancel()
networkObserverJob?.cancel()
feedObserverJob = null
_state.value = ChatState()
cluster.value = null
metadataDataSource.clear()
messageDataSource.clear()
memberDataSource.clear()
supervisorJob.cancel()
trace(tag = TAG, message = "reset complete", type = TraceType.Process)
}

Expand Down
Loading
Loading