mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-06-15 11:21:05 -04:00
Try to fix molecule issues
We were running the presenting on the IoDispatcher and this seemed to have caused random crashes.
This commit is contained in:
@@ -71,15 +71,17 @@ class MyAppsViewModel @Inject constructor(
|
||||
|
||||
private val searchQuery = savedStateHandle.getMutableStateFlow("query", "")
|
||||
private val sortOrder = savedStateHandle.getMutableStateFlow("sort", AppListSortOrder.NAME)
|
||||
val myAppsModel: StateFlow<MyAppsModel> = moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
MyAppsPresenter(
|
||||
appUpdatesFlow = updates,
|
||||
appInstallStatesFlow = appInstallManager.appInstallStates,
|
||||
appsWithIssuesFlow = updatesManager.appsWithIssues,
|
||||
installedAppsFlow = installedAppItems,
|
||||
searchQueryFlow = searchQuery,
|
||||
sortOrderFlow = sortOrder,
|
||||
)
|
||||
val myAppsModel: StateFlow<MyAppsModel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
MyAppsPresenter(
|
||||
appUpdatesFlow = updates,
|
||||
appInstallStatesFlow = appInstallManager.appInstallStates,
|
||||
appsWithIssuesFlow = updatesManager.appsWithIssues,
|
||||
installedAppsFlow = installedAppItems,
|
||||
searchQueryFlow = searchQuery,
|
||||
sortOrderFlow = sortOrder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAll() {
|
||||
|
||||
@@ -10,7 +10,8 @@ import androidx.annotation.UiThread
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.application
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.cash.molecule.RecompositionMode.Immediate
|
||||
import app.cash.molecule.AndroidUiDispatcher
|
||||
import app.cash.molecule.RecompositionMode.ContextClock
|
||||
import app.cash.molecule.launchMolecule
|
||||
import coil3.SingletonImageLoader
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -54,22 +55,25 @@ class AppDetailsViewModel @Inject constructor(
|
||||
private val log = KotlinLogging.logger { }
|
||||
private val packageInfoFlow = MutableStateFlow<AppInfo?>(null)
|
||||
private val currentRepoIdFlow = MutableStateFlow<Long?>(null)
|
||||
private val moleculeScope =
|
||||
CoroutineScope(viewModelScope.coroutineContext + AndroidUiDispatcher.Main)
|
||||
|
||||
val appDetails: StateFlow<AppDetailsItem?> = viewModelScope.launchMolecule(
|
||||
context = scope.coroutineContext, mode = Immediate,
|
||||
) {
|
||||
DetailsPresenter(
|
||||
db = db,
|
||||
repoManager = repoManager,
|
||||
repoPreLoader = repoPreLoader,
|
||||
updateChecker = updateChecker,
|
||||
settingsManager = settingsManager,
|
||||
appInstallManager = appInstallManager,
|
||||
viewModel = this,
|
||||
packageInfoFlow = packageInfoFlow,
|
||||
currentRepoIdFlow = currentRepoIdFlow,
|
||||
appsWithIssuesFlow = updatesManager.appsWithIssues,
|
||||
)
|
||||
val appDetails: StateFlow<AppDetailsItem?> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
DetailsPresenter(
|
||||
db = db,
|
||||
scope = scope,
|
||||
repoManager = repoManager,
|
||||
repoPreLoader = repoPreLoader,
|
||||
updateChecker = updateChecker,
|
||||
settingsManager = settingsManager,
|
||||
appInstallManager = appInstallManager,
|
||||
viewModel = this,
|
||||
packageInfoFlow = packageInfoFlow,
|
||||
currentRepoIdFlow = currentRepoIdFlow,
|
||||
appsWithIssuesFlow = updatesManager.appsWithIssues,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setAppDetails(packageName: String) {
|
||||
|
||||
@@ -5,13 +5,19 @@ import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.core.content.pm.PackageInfoCompat.getLongVersionCode
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.lifecycle.asFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.fdroid.UpdateChecker
|
||||
import org.fdroid.database.App
|
||||
import org.fdroid.database.AppPrefs
|
||||
import org.fdroid.database.AppVersion
|
||||
import org.fdroid.database.FDroidDatabase
|
||||
import org.fdroid.database.Repository
|
||||
import org.fdroid.index.RepoManager
|
||||
@@ -30,6 +36,7 @@ private const val TAG = "DetailsPresenter"
|
||||
@Composable
|
||||
fun DetailsPresenter(
|
||||
db: FDroidDatabase,
|
||||
scope: CoroutineScope,
|
||||
repoManager: RepoManager,
|
||||
repoPreLoader: RepoPreLoader,
|
||||
updateChecker: UpdateChecker,
|
||||
@@ -45,41 +52,56 @@ fun DetailsPresenter(
|
||||
val packageInfo = packagePair.packageInfo
|
||||
val currentRepoId = currentRepoIdFlow.collectAsState().value
|
||||
val appsWithIssues = appsWithIssuesFlow.collectAsState().value
|
||||
val app = if (currentRepoId == null) {
|
||||
val flow = remember {
|
||||
db.getAppDao().getApp(packageName).asFlow()
|
||||
val appDao = db.getAppDao()
|
||||
val app = produceState<App?>(null, currentRepoId) {
|
||||
withContext(scope.coroutineContext) {
|
||||
if (currentRepoId == null) {
|
||||
val flow = appDao.getApp(packageName).asFlow()
|
||||
flow.collect { value = it }
|
||||
} else {
|
||||
value = appDao.getApp(currentRepoId, packageName)
|
||||
}
|
||||
}
|
||||
flow.collectAsState(null).value
|
||||
} else {
|
||||
db.getAppDao().getApp(currentRepoId, packageName)
|
||||
} ?: return null
|
||||
val repo = repoManager.getRepository(app.repoId) ?: return null
|
||||
val repositories = remember(packageName) {
|
||||
val repos = db.getAppDao().getRepositoryIdsForApp(packageName).mapNotNull { repoId ->
|
||||
repoManager.getRepository(repoId)
|
||||
}.value ?: return null
|
||||
val repo = produceState<Repository?>(null) {
|
||||
withContext(scope.coroutineContext) {
|
||||
value = repoManager.getRepository(app.repoId)
|
||||
}
|
||||
// show repo chooser only if
|
||||
// * app is in more than one repo, or
|
||||
// * app is from a non-default repo
|
||||
if (repos.size > 1) repos
|
||||
else if (repo.address in repoPreLoader.defaultRepoAddresses) emptyList()
|
||||
else repos
|
||||
}
|
||||
}.value ?: return null
|
||||
val repositories = produceState(emptyList(), packageName) {
|
||||
withContext(scope.coroutineContext) {
|
||||
val repos = appDao.getRepositoryIdsForApp(packageName).mapNotNull { repoId ->
|
||||
repoManager.getRepository(repoId)
|
||||
}
|
||||
// show repo chooser only if
|
||||
// * app is in more than one repo, or
|
||||
// * app is from a non-default repo
|
||||
value = if (repos.size > 1) repos
|
||||
else if (repo.address in repoPreLoader.defaultRepoAddresses) emptyList()
|
||||
else repos
|
||||
}
|
||||
}.value
|
||||
val installState =
|
||||
appInstallManager.getAppFlow(packageName).collectAsState(InstallState.Unknown).value
|
||||
|
||||
val versionsFlow = remember(currentRepoId) {
|
||||
if (currentRepoId == null) {
|
||||
db.getVersionDao().getAppVersions(app.repoId, packageName).asFlow()
|
||||
} else {
|
||||
db.getVersionDao().getAppVersions(currentRepoId, packageName).asFlow()
|
||||
val versions = produceState<List<AppVersion>?>(null, currentRepoId) {
|
||||
withContext(scope.coroutineContext) {
|
||||
if (currentRepoId == null) {
|
||||
db.getVersionDao().getAppVersions(app.repoId, packageName).asFlow().collect {
|
||||
value = it
|
||||
}
|
||||
} else {
|
||||
db.getVersionDao().getAppVersions(currentRepoId, packageName).asFlow().collect {
|
||||
value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val versions = versionsFlow.collectAsState(null).value
|
||||
val appPrefsFlow = remember(packageName) {
|
||||
db.getAppPrefsDao().getAppPrefs(packageName).asFlow()
|
||||
}
|
||||
val appPrefs = appPrefsFlow.collectAsState(null).value
|
||||
}.value
|
||||
val appPrefs = produceState<AppPrefs?>(null, packageName) {
|
||||
withContext(scope.coroutineContext) {
|
||||
db.getAppPrefsDao().getAppPrefs(packageName).asFlow().collect { value = it }
|
||||
}
|
||||
}.value
|
||||
val preferredRepoId = remember(packageName, appPrefs) {
|
||||
appPrefs?.preferredRepoId ?: app.repoId // DB loads preferred repo first, so we remember it
|
||||
}
|
||||
@@ -123,10 +145,11 @@ fun DetailsPresenter(
|
||||
}
|
||||
val authorName = app.authorName
|
||||
val authorHasMoreThanOneApp = if (authorName == null) false else {
|
||||
val flow = remember(authorName) {
|
||||
db.getAppDao().hasAuthorMoreThanOneApp(authorName).asFlow()
|
||||
}
|
||||
flow.collectAsState(false).value
|
||||
produceState(false) {
|
||||
withContext(scope.coroutineContext) {
|
||||
db.getAppDao().hasAuthorMoreThanOneApp(authorName).asFlow().collect { value = it }
|
||||
}
|
||||
}.value
|
||||
}
|
||||
val issue = remember(appsWithIssues) {
|
||||
appsWithIssues?.find { it.packageName == packageName }?.issue
|
||||
|
||||
@@ -48,7 +48,8 @@ class DiscoverViewModel @Inject constructor(
|
||||
) : AndroidViewModel(app) {
|
||||
|
||||
private val log = KotlinLogging.logger { }
|
||||
private val scope = CoroutineScope(viewModelScope.coroutineContext + AndroidUiDispatcher.Main)
|
||||
private val moleculeScope =
|
||||
CoroutineScope(viewModelScope.coroutineContext + AndroidUiDispatcher.Main)
|
||||
private val collator = Collator.getInstance(Locale.getDefault())
|
||||
|
||||
val numUpdates = updatesManager.numUpdates
|
||||
@@ -78,15 +79,17 @@ class DiscoverViewModel @Inject constructor(
|
||||
private val searchResults = MutableStateFlow<SearchResults?>(null)
|
||||
|
||||
val localeList = LocaleListCompat.getDefault()
|
||||
val discoverModel: StateFlow<DiscoverModel> = scope.launchMolecule(mode = ContextClock) {
|
||||
DiscoverPresenter(
|
||||
newAppsFlow = newApps,
|
||||
recentlyUpdatedAppsFlow = recentlyUpdatedApps,
|
||||
categoriesFlow = categories,
|
||||
repositoriesFlow = repoManager.repositoriesState,
|
||||
searchResultsFlow = searchResults,
|
||||
lastRepoUpdate = settingsManager.lastRepoUpdate,
|
||||
)
|
||||
val discoverModel: StateFlow<DiscoverModel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
DiscoverPresenter(
|
||||
newAppsFlow = newApps,
|
||||
recentlyUpdatedAppsFlow = recentlyUpdatedApps,
|
||||
categoriesFlow = categories,
|
||||
repositoriesFlow = repoManager.repositoriesState,
|
||||
searchResultsFlow = searchResults,
|
||||
lastRepoUpdate = settingsManager.lastRepoUpdate,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun search(term: String) = withContext(ioScope.coroutineContext) {
|
||||
|
||||
@@ -43,7 +43,8 @@ class AppListViewModel @Inject constructor(
|
||||
private val onboardingManager: OnboardingManager,
|
||||
) : AndroidViewModel(app), AppListActions {
|
||||
|
||||
private val scope = CoroutineScope(viewModelScope.coroutineContext + AndroidUiDispatcher.Main)
|
||||
private val moleculeScope =
|
||||
CoroutineScope(viewModelScope.coroutineContext + AndroidUiDispatcher.Main)
|
||||
|
||||
private val localeList = LocaleListCompat.getDefault()
|
||||
private val apps = MutableStateFlow<List<AppListItem>?>(null)
|
||||
@@ -77,17 +78,19 @@ class AppListViewModel @Inject constructor(
|
||||
private val filteredRepositoryIds = MutableStateFlow<Set<Long>>(emptySet())
|
||||
val showOnboarding = onboardingManager.showFilterOnboarding
|
||||
|
||||
val appListModel: StateFlow<AppListModel> = scope.launchMolecule(mode = ContextClock) {
|
||||
AppListPresenter(
|
||||
appsFlow = apps,
|
||||
sortByFlow = sortBy,
|
||||
filterIncompatibleFlow = filterIncompatible,
|
||||
categoriesFlow = categories,
|
||||
filteredCategoryIdsFlow = filteredCategoryIds,
|
||||
repositoriesFlow = repositories,
|
||||
filteredRepositoryIdsFlow = filteredRepositoryIds,
|
||||
searchQueryFlow = query,
|
||||
)
|
||||
val appListModel: StateFlow<AppListModel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
AppListPresenter(
|
||||
appsFlow = apps,
|
||||
sortByFlow = sortBy,
|
||||
filterIncompatibleFlow = filterIncompatible,
|
||||
categoriesFlow = categories,
|
||||
filteredCategoryIdsFlow = filteredCategoryIds,
|
||||
repositoriesFlow = repositories,
|
||||
filteredRepositoryIdsFlow = filteredRepositoryIds,
|
||||
searchQueryFlow = query,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -53,14 +53,16 @@ class RepositoriesViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
// define below init, because this only defines repoSortingMap
|
||||
val model: StateFlow<RepositoryModel> = moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
RepositoriesPresenter(
|
||||
context = application,
|
||||
repositoriesFlow = repos,
|
||||
repoSortingMapFlow = repoSortingMap,
|
||||
showOnboardingFlow = showOnboarding,
|
||||
lastUpdateFlow = settingsManager.lastRepoUpdateFlow,
|
||||
)
|
||||
val model: StateFlow<RepositoryModel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
RepositoriesPresenter(
|
||||
context = application,
|
||||
repositoriesFlow = repos,
|
||||
repoSortingMapFlow = repoSortingMap,
|
||||
showOnboardingFlow = showOnboarding,
|
||||
lastUpdateFlow = settingsManager.lastRepoUpdateFlow,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRepositoriesChanged(repositories: List<Repository>) {
|
||||
|
||||
@@ -56,14 +56,16 @@ class RepoDetailsViewModel @Inject constructor(
|
||||
private val archiveStateFlow = MutableStateFlow(UNKNOWN)
|
||||
private val showOnboarding = onboardingManager.showRepoDetailsOnboarding
|
||||
|
||||
val model: StateFlow<RepoDetailsModel> = moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
RepoDetailsPresenter(
|
||||
repoFlow = repoFlow,
|
||||
numAppsFlow = numAppsFlow,
|
||||
archiveStateFlow = archiveStateFlow,
|
||||
showOnboardingFlow = showOnboarding,
|
||||
proxyConfig = settingsManager.proxyConfig,
|
||||
)
|
||||
val model: StateFlow<RepoDetailsModel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
moleculeScope.launchMolecule(mode = ContextClock) {
|
||||
RepoDetailsPresenter(
|
||||
repoFlow = repoFlow,
|
||||
numAppsFlow = numAppsFlow,
|
||||
archiveStateFlow = archiveStateFlow,
|
||||
showOnboardingFlow = showOnboarding,
|
||||
proxyConfig = settingsManager.proxyConfig,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setRepoId(repoId: Long) {
|
||||
|
||||
Reference in New Issue
Block a user