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 @@ -80,7 +80,6 @@ class ConfigToDatabaseSync @Inject constructor(
private val lokiAPIDatabase: LokiAPIDatabase,
private val receivedMessageHashDatabase: ReceivedMessageHashDatabase,
private val clock: SnodeClock,
private val preferences: TextSecurePreferences,
private val conversationRepository: ConversationRepository,
private val mmsSmsDatabase: MmsSmsDatabase,
private val lokiMessageDatabase: LokiMessageDatabase,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import android.view.View
import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
Expand Down Expand Up @@ -44,9 +47,11 @@ class LoadAccountActivity : BaseActionBarActivity() {
supportActionBar?.setTitle(R.string.loadAccount)
prefs.setConfigurationMessageSynced(false)


lifecycleScope.launch {
viewModel.events.collect {
loadAccountManager.load(it.mnemonic)
repeatOnLifecycle(Lifecycle.State.STARTED) {
val mnemonic = viewModel.events.first().mnemonic
loadAccountManager.load(mnemonic)
start<MessageNotificationsActivity>()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,46 @@
package org.thoughtcrime.securesms.onboarding.manager

import android.app.Application
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import network.loki.messenger.libsession_util.PRIORITY_HIDDEN
import org.session.libsession.snode.SnodeModule
import org.session.libsession.utilities.ConfigFactoryProtocol
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.database.LokiAPIDatabaseProtocol
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.auth.LoggedInState
import org.thoughtcrime.securesms.auth.LoginStateRepository
import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase
import org.thoughtcrime.securesms.util.VersionDataFetcher
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class CreateAccountManager @Inject constructor(
private val versionDataFetcher: VersionDataFetcher,
private val configFactory: ConfigFactoryProtocol,
private val receivedMessageHashDatabase: ReceivedMessageHashDatabase,
private val loginStateRepository: LoginStateRepository,
private val database: LokiAPIDatabaseProtocol,
) {
private val database: LokiAPIDatabaseProtocol
get() = SnodeModule.shared.storage
suspend fun createAccount(displayName: String) {
withContext(Dispatchers.Default) {
// This is here to resolve a case where the app restarts before a user completes onboarding
// which can result in an invalid database state
database.clearAllLastMessageHashes()
receivedMessageHashDatabase.removeAll()

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

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

LoggedInState.generate(seed = null)
}
configFactory.withMutableUserConfigs {
it.userProfile.setName(displayName)
it.userProfile.setNtsPriority(PRIORITY_HIDDEN)
}

configFactory.withMutableUserConfigs {
it.userProfile.setName(displayName)
it.userProfile.setNtsPriority(PRIORITY_HIDDEN)
versionDataFetcher.startTimedVersionCheck()
}

versionDataFetcher.startTimedVersionCheck()
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,35 @@
package org.thoughtcrime.securesms.onboarding.manager

import android.content.Context
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.session.libsession.snode.SnodeModule
import kotlinx.coroutines.withContext
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.database.LokiAPIDatabaseProtocol
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.auth.LoggedInState
import org.thoughtcrime.securesms.auth.LoginStateRepository
import org.thoughtcrime.securesms.database.ReceivedMessageHashDatabase
import org.thoughtcrime.securesms.util.VersionDataFetcher
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class LoadAccountManager @Inject constructor(
@param:dagger.hilt.android.qualifiers.ApplicationContext private val context: Context,
private val prefs: TextSecurePreferences,
private val versionDataFetcher: VersionDataFetcher,
private val receivedMessageHashDatabase: ReceivedMessageHashDatabase,
private val loginStateRepository: LoginStateRepository,
) {
private val database: LokiAPIDatabaseProtocol
get() = SnodeModule.shared.storage

private var restoreJob: Job? = null

private val scope = CoroutineScope(Dispatchers.IO)

fun load(seed: ByteArray) {
// only have one sync job running at a time (prevent QR from trying to spawn a new job)
if (restoreJob?.isActive == true) return
) {

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

loginStateRepository.update {
require(it == null) {
"Attempting to restore an account when one already exists!"
loginStateRepository.update { old ->
if (old != null) {
Log.wtf("LoadAccountManager", "Tried to load account when already logged in!")
return@update old
}

LoggedInState.generate(seed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
Expand Down Expand Up @@ -37,7 +36,7 @@ internal class MessageNotificationsViewModel(
}

fun onContinue() {
viewModelScope.launch(Dispatchers.IO) {
viewModelScope.launch {
if (state is State.CreateAccount) createAccountManager.createAccount(state.displayName)

prefs.setPushEnabled(uiStates.value.pushEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import org.session.libsession.snode.SnodeClock
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.auth.LoginStateRepository
import org.thoughtcrime.securesms.debugmenu.DebugLogGroup
import org.thoughtcrime.securesms.dependencies.ManagerScope
import org.thoughtcrime.securesms.pro.api.ProDetails
Expand All @@ -27,6 +28,7 @@ class ProDetailsRepository @Inject constructor(
private val db: ProDatabase,
private val snodeClock: SnodeClock,
@ManagerScope scope: CoroutineScope,
loginStateRepository: LoginStateRepository,
) {
sealed interface LoadState {
val lastUpdated: Pair<ProDetails, Instant>?
Expand All @@ -46,31 +48,33 @@ class ProDetailsRepository @Inject constructor(
}


val loadState: StateFlow<LoadState> = combine(
FetchProDetailsWorker.watch(application)
.map { it.state }
.distinctUntilChanged(),
val loadState: StateFlow<LoadState> = loginStateRepository.flowWithLoggedInState {
combine(
FetchProDetailsWorker.watch(application)
.map { it.state }
.distinctUntilChanged(),

db.proDetailsChangeNotification
.onStart { emit(Unit) }
.map { db.getProDetailsAndLastUpdated() }
) { state, last ->
when (state) {
WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED -> LoadState.Loading(last, waitingForNetwork = true)
WorkInfo.State.RUNNING -> LoadState.Loading(last, waitingForNetwork = false)
WorkInfo.State.SUCCEEDED -> {
if (last != null) {
Log.d(DebugLogGroup.PRO_DATA.label, "Successfully fetched Pro details from backend")
LoadState.Loaded(last)
} else {
// This should never happen, but just in case...
LoadState.Error(null)
db.proDetailsChangeNotification
.onStart { emit(Unit) }
.map { db.getProDetailsAndLastUpdated() }
) { state, last ->
when (state) {
WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED -> LoadState.Loading(last, waitingForNetwork = true)
WorkInfo.State.RUNNING -> LoadState.Loading(last, waitingForNetwork = false)
WorkInfo.State.SUCCEEDED -> {
if (last != null) {
Log.d(DebugLogGroup.PRO_DATA.label, "Successfully fetched Pro details from backend")
LoadState.Loaded(last)
} else {
// This should never happen, but just in case...
LoadState.Error(null)
}
}
}

WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> LoadState.Error(last)
WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> LoadState.Error(last)
}
}
}.stateIn(scope, SharingStarted.Eagerly, LoadState.Init)
} .stateIn(scope, SharingStarted.Eagerly, LoadState.Init)


/**
Expand Down
Loading