Skip to content

Commit 45be28e

Browse files
Improve lifecycle handling of restoring account and a few dependency injection optimisations (#1739)
* Improve lifecycle handling of restoring account and a few dependency injection optimisations * Not crashing when accounts are loaded
1 parent 0b61914 commit 45be28e

File tree

8 files changed

+168
-174
lines changed

8 files changed

+168
-174
lines changed

app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ class ConfigToDatabaseSync @Inject constructor(
8080
private val lokiAPIDatabase: LokiAPIDatabase,
8181
private val receivedMessageHashDatabase: ReceivedMessageHashDatabase,
8282
private val clock: SnodeClock,
83-
private val preferences: TextSecurePreferences,
8483
private val conversationRepository: ConversationRepository,
8584
private val mmsSmsDatabase: MmsSmsDatabase,
8685
private val lokiMessageDatabase: LokiMessageDatabase,

app/src/main/java/org/thoughtcrime/securesms/onboarding/loadaccount/LoadAccountActivity.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import android.view.View
55
import androidx.activity.viewModels
66
import androidx.compose.runtime.collectAsState
77
import androidx.compose.runtime.getValue
8+
import androidx.lifecycle.Lifecycle
89
import androidx.lifecycle.lifecycleScope
10+
import androidx.lifecycle.repeatOnLifecycle
911
import dagger.hilt.android.AndroidEntryPoint
12+
import kotlinx.coroutines.flow.first
1013
import kotlinx.coroutines.launch
1114
import network.loki.messenger.R
1215
import org.session.libsession.utilities.TextSecurePreferences
@@ -44,9 +47,11 @@ class LoadAccountActivity : BaseActionBarActivity() {
4447
supportActionBar?.setTitle(R.string.loadAccount)
4548
prefs.setConfigurationMessageSynced(false)
4649

50+
4751
lifecycleScope.launch {
48-
viewModel.events.collect {
49-
loadAccountManager.load(it.mnemonic)
52+
repeatOnLifecycle(Lifecycle.State.STARTED) {
53+
val mnemonic = viewModel.events.first().mnemonic
54+
loadAccountManager.load(mnemonic)
5055
start<MessageNotificationsActivity>()
5156
}
5257
}
Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,46 @@
11
package org.thoughtcrime.securesms.onboarding.manager
22

3-
import android.app.Application
3+
import kotlinx.coroutines.Dispatchers
4+
import kotlinx.coroutines.withContext
45
import network.loki.messenger.libsession_util.PRIORITY_HIDDEN
5-
import org.session.libsession.snode.SnodeModule
66
import org.session.libsession.utilities.ConfigFactoryProtocol
7-
import org.session.libsession.utilities.TextSecurePreferences
87
import org.session.libsignal.database.LokiAPIDatabaseProtocol
8+
import org.session.libsignal.utilities.Log
99
import org.thoughtcrime.securesms.auth.LoggedInState
1010
import org.thoughtcrime.securesms.auth.LoginStateRepository
1111
import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase
1212
import org.thoughtcrime.securesms.util.VersionDataFetcher
1313
import javax.inject.Inject
14-
import javax.inject.Singleton
1514

16-
@Singleton
1715
class CreateAccountManager @Inject constructor(
1816
private val versionDataFetcher: VersionDataFetcher,
1917
private val configFactory: ConfigFactoryProtocol,
2018
private val receivedMessageHashDatabase: ReceivedMessageHashDatabase,
2119
private val loginStateRepository: LoginStateRepository,
20+
private val database: LokiAPIDatabaseProtocol,
2221
) {
23-
private val database: LokiAPIDatabaseProtocol
24-
get() = SnodeModule.shared.storage
22+
suspend fun createAccount(displayName: String) {
23+
withContext(Dispatchers.Default) {
24+
// This is here to resolve a case where the app restarts before a user completes onboarding
25+
// which can result in an invalid database state
26+
database.clearAllLastMessageHashes()
27+
receivedMessageHashDatabase.removeAll()
2528

26-
fun createAccount(displayName: String) {
27-
// This is here to resolve a case where the app restarts before a user completes onboarding
28-
// which can result in an invalid database state
29-
database.clearAllLastMessageHashes()
30-
receivedMessageHashDatabase.removeAll()
29+
loginStateRepository.update { oldState ->
30+
if (oldState != null) {
31+
Log.wtf("CreateAccountManager", "Tried to create account when already logged in!")
32+
return@update oldState
33+
}
3134

32-
loginStateRepository.update { oldState ->
33-
require(oldState == null) {
34-
"Attempting to create a new account when one already exists!"
35+
LoggedInState.generate(seed = null)
3536
}
3637

37-
LoggedInState.generate(seed = null)
38-
}
38+
configFactory.withMutableUserConfigs {
39+
it.userProfile.setName(displayName)
40+
it.userProfile.setNtsPriority(PRIORITY_HIDDEN)
41+
}
3942

40-
configFactory.withMutableUserConfigs {
41-
it.userProfile.setName(displayName)
42-
it.userProfile.setNtsPriority(PRIORITY_HIDDEN)
43+
versionDataFetcher.startTimedVersionCheck()
4344
}
44-
45-
versionDataFetcher.startTimedVersionCheck()
4645
}
4746
}

app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,35 @@
11
package org.thoughtcrime.securesms.onboarding.manager
22

3-
import android.content.Context
4-
import kotlinx.coroutines.CoroutineScope
53
import kotlinx.coroutines.Dispatchers
6-
import kotlinx.coroutines.Job
7-
import kotlinx.coroutines.launch
8-
import org.session.libsession.snode.SnodeModule
4+
import kotlinx.coroutines.withContext
95
import org.session.libsession.utilities.TextSecurePreferences
106
import org.session.libsignal.database.LokiAPIDatabaseProtocol
7+
import org.session.libsignal.utilities.Log
118
import org.thoughtcrime.securesms.auth.LoggedInState
129
import org.thoughtcrime.securesms.auth.LoginStateRepository
1310
import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase
1411
import org.thoughtcrime.securesms.util.VersionDataFetcher
1512
import javax.inject.Inject
16-
import javax.inject.Singleton
1713

18-
@Singleton
1914
class LoadAccountManager @Inject constructor(
20-
@param:dagger.hilt.android.qualifiers.ApplicationContext private val context: Context,
2115
private val prefs: TextSecurePreferences,
2216
private val versionDataFetcher: VersionDataFetcher,
2317
private val receivedMessageHashDatabase: ReceivedMessageHashDatabase,
2418
private val loginStateRepository: LoginStateRepository,
25-
) {
2619
private val database: LokiAPIDatabaseProtocol
27-
get() = SnodeModule.shared.storage
28-
29-
private var restoreJob: Job? = null
30-
31-
private val scope = CoroutineScope(Dispatchers.IO)
32-
33-
fun load(seed: ByteArray) {
34-
// only have one sync job running at a time (prevent QR from trying to spawn a new job)
35-
if (restoreJob?.isActive == true) return
20+
) {
3621

37-
restoreJob = scope.launch {
22+
suspend fun load(seed: ByteArray) {
23+
withContext(Dispatchers.Default) {
3824
// This is here to resolve a case where the app restarts before a user completes onboarding
3925
// which can result in an invalid database state
4026
database.clearAllLastMessageHashes()
4127
receivedMessageHashDatabase.removeAll()
4228

43-
loginStateRepository.update {
44-
require(it == null) {
45-
"Attempting to restore an account when one already exists!"
29+
loginStateRepository.update { old ->
30+
if (old != null) {
31+
Log.wtf("LoadAccountManager", "Tried to load account when already logged in!")
32+
return@update old
4633
}
4734

4835
LoggedInState.generate(seed)

app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModelProvider
77
import androidx.lifecycle.viewModelScope
88
import dagger.assisted.Assisted
99
import dagger.assisted.AssistedInject
10-
import kotlinx.coroutines.Dispatchers
1110
import kotlinx.coroutines.flow.MutableSharedFlow
1211
import kotlinx.coroutines.flow.MutableStateFlow
1312
import kotlinx.coroutines.flow.asSharedFlow
@@ -37,7 +36,7 @@ internal class MessageNotificationsViewModel(
3736
}
3837

3938
fun onContinue() {
40-
viewModelScope.launch(Dispatchers.IO) {
39+
viewModelScope.launch {
4140
if (state is State.CreateAccount) createAccountManager.createAccount(state.displayName)
4241

4342
prefs.setPushEnabled(uiStates.value.pushEnabled)

app/src/main/java/org/thoughtcrime/securesms/pro/ProDetailsRepository.kt

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.onStart
1313
import kotlinx.coroutines.flow.stateIn
1414
import org.session.libsession.snode.SnodeClock
1515
import org.session.libsignal.utilities.Log
16+
import org.thoughtcrime.securesms.auth.LoginStateRepository
1617
import org.thoughtcrime.securesms.debugmenu.DebugLogGroup
1718
import org.thoughtcrime.securesms.dependencies.ManagerScope
1819
import org.thoughtcrime.securesms.pro.api.ProDetails
@@ -27,6 +28,7 @@ class ProDetailsRepository @Inject constructor(
2728
private val db: ProDatabase,
2829
private val snodeClock: SnodeClock,
2930
@ManagerScope scope: CoroutineScope,
31+
loginStateRepository: LoginStateRepository,
3032
) {
3133
sealed interface LoadState {
3234
val lastUpdated: Pair<ProDetails, Instant>?
@@ -46,31 +48,33 @@ class ProDetailsRepository @Inject constructor(
4648
}
4749

4850

49-
val loadState: StateFlow<LoadState> = combine(
50-
FetchProDetailsWorker.watch(application)
51-
.map { it.state }
52-
.distinctUntilChanged(),
51+
val loadState: StateFlow<LoadState> = loginStateRepository.flowWithLoggedInState {
52+
combine(
53+
FetchProDetailsWorker.watch(application)
54+
.map { it.state }
55+
.distinctUntilChanged(),
5356

54-
db.proDetailsChangeNotification
55-
.onStart { emit(Unit) }
56-
.map { db.getProDetailsAndLastUpdated() }
57-
) { state, last ->
58-
when (state) {
59-
WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED -> LoadState.Loading(last, waitingForNetwork = true)
60-
WorkInfo.State.RUNNING -> LoadState.Loading(last, waitingForNetwork = false)
61-
WorkInfo.State.SUCCEEDED -> {
62-
if (last != null) {
63-
Log.d(DebugLogGroup.PRO_DATA.label, "Successfully fetched Pro details from backend")
64-
LoadState.Loaded(last)
65-
} else {
66-
// This should never happen, but just in case...
67-
LoadState.Error(null)
57+
db.proDetailsChangeNotification
58+
.onStart { emit(Unit) }
59+
.map { db.getProDetailsAndLastUpdated() }
60+
) { state, last ->
61+
when (state) {
62+
WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED -> LoadState.Loading(last, waitingForNetwork = true)
63+
WorkInfo.State.RUNNING -> LoadState.Loading(last, waitingForNetwork = false)
64+
WorkInfo.State.SUCCEEDED -> {
65+
if (last != null) {
66+
Log.d(DebugLogGroup.PRO_DATA.label, "Successfully fetched Pro details from backend")
67+
LoadState.Loaded(last)
68+
} else {
69+
// This should never happen, but just in case...
70+
LoadState.Error(null)
71+
}
6872
}
69-
}
7073

71-
WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> LoadState.Error(last)
74+
WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> LoadState.Error(last)
75+
}
7276
}
73-
}.stateIn(scope, SharingStarted.Eagerly, LoadState.Init)
77+
} .stateIn(scope, SharingStarted.Eagerly, LoadState.Init)
7478

7579

7680
/**

0 commit comments

Comments
 (0)