diff --git a/CHANGELOG.md b/CHANGELOG.md index d5f83132..5206f8a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [Unreleased] +### Fixed +- Restoration of torrent adding parameters (download directory, etc) when adding torrent file + ## [2.13.0] - 2025-12-29 ### Added - Support of adding multiple torrent links diff --git a/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileFragment.kt b/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileFragment.kt index 0f54bbcb..5c1c6302 100644 --- a/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileFragment.kt +++ b/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileFragment.kt @@ -102,7 +102,7 @@ class AddTorrentFileFragment : ComposeFragment() { needStoragePermission = model.needStoragePermission, loadTorrentFile = model::load, - loadingState = model.loadingState, + loadingState = model.loadingState.collectAsStateWithLifecycle(), downloadDirectory = model.downloadDirectory, allDownloadDirectories = model.allDownloadDirectories, downloadDirectoryFreeSpace = model.downloadDirectoryFreeSpace.collectAsStateWithLifecycle(), diff --git a/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileModel.kt b/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileModel.kt index 8061844a..7e775092 100644 --- a/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileModel.kt +++ b/app/src/main/kotlin/org/equeim/tremotesf/ui/addtorrent/AddTorrentFileModel.kt @@ -18,16 +18,23 @@ import androidx.compose.runtime.mutableStateOf import androidx.core.os.BundleCompat import androidx.core.os.bundleOf import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.application import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi import androidx.lifecycle.viewmodel.compose.saveable import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize import org.equeim.tremotesf.R import org.equeim.tremotesf.rpc.GlobalRpcClient import org.equeim.tremotesf.rpc.RpcRequestError +import org.equeim.tremotesf.rpc.RpcRequestState import org.equeim.tremotesf.rpc.requests.addTorrentFile import org.equeim.tremotesf.rpc.requests.checkIfTorrentsExist import org.equeim.tremotesf.rpc.requests.torrentproperties.addTorrentTrackers @@ -56,7 +63,7 @@ interface AddTorrentFileModel { data object Aborted : LoadingState } - val loadingState: State + val loadingState: StateFlow val needStoragePermission: Boolean val filesTree: TorrentFilesTree @@ -74,7 +81,18 @@ class AddTorrentFileModelImpl( override val needStoragePermission: Boolean = uri.scheme == ContentResolver.SCHEME_FILE && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q - override val loadingState = mutableStateOf(LoadingState.Initial) + private val torrentLoadingState = MutableStateFlow(LoadingState.Initial) + override val loadingState: StateFlow = combine(torrentLoadingState, initialRpcInputs) { torrentLoadingState, initialRpcInputs -> + if (torrentLoadingState is LoadingState.Loaded) { + when (initialRpcInputs) { + is RpcRequestState.Error -> LoadingState.InitialRpcInputsError(initialRpcInputs.error) + is RpcRequestState.Loading -> LoadingState.Loading + is RpcRequestState.Loaded -> torrentLoadingState + } + } else { + torrentLoadingState + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), LoadingState.Initial) private var fd: AssetFileDescriptor? = null @@ -106,45 +124,39 @@ class AddTorrentFileModelImpl( } override fun load() { - if (loadingState.value == LoadingState.Initial) { - Timber.i("load: loading $uri") - loadingState.value = LoadingState.Loading - viewModelScope.launch { - doLoad(uri, getApplication()) - } - } else { + if (!torrentLoadingState.compareAndSet(LoadingState.Initial, LoadingState.Loading)) { Timber.e("load: loadingState is not Initial") + return + } + viewModelScope.launch { + Timber.i("load: loading $uri") + torrentLoadingState.value = doLoad(uri, application) } } - private suspend fun doLoad(uri: Uri, context: Context) = withContext(Dispatchers.IO) { + private suspend fun doLoad(uri: Uri, context: Context): LoadingState = withContext(Dispatchers.IO) { Timber.d("Parsing torrent file from URI $uri") val fd = try { context.contentResolver.openAssetFileDescriptor(uri, "r") } catch (e: Exception) { Timber.e(e, "Failed to open file descriptor") - loadingState.value = LoadingState.FileLoadingError.ReadingError - return@withContext + return@withContext LoadingState.FileLoadingError.ReadingError } if (fd == null) { Timber.e("File descriptor is null") - loadingState.value = LoadingState.FileLoadingError.ReadingError - return@withContext + return@withContext LoadingState.FileLoadingError.ReadingError } var closeFd = true try { val parseResult = try { TorrentFileParser.parseTorrentFile(fd.fileDescriptor) } catch (_: FileReadException) { - loadingState.value = LoadingState.FileLoadingError.ReadingError - return@withContext + return@withContext LoadingState.FileLoadingError.ReadingError } catch (_: FileIsTooLargeException) { - loadingState.value = LoadingState.FileLoadingError.FileIsTooLarge - return@withContext + return@withContext LoadingState.FileLoadingError.FileIsTooLarge } catch (_: FileParseException) { - loadingState.value = LoadingState.FileLoadingError.ParsingError - return@withContext + return@withContext LoadingState.FileLoadingError.ParsingError } Timber.d("Parsed torrent file from URI $uri, its info hash is ${parseResult.infoHashV1}") @@ -154,11 +166,10 @@ class AddTorrentFileModelImpl( checkIfTorrentExists() if (checkIfTorrentExists()) { - loadingState.value = LoadingState.Aborted - return@withContext + return@withContext LoadingState.Aborted } - try { + return@withContext try { val (rootNode, files) = TorrentFileParser.createFilesTree(parseResult) withContext(Dispatchers.Main) { closeFd = false @@ -182,11 +193,10 @@ class AddTorrentFileModelImpl( } } } - loadingState.value = LoadingState.Loaded(torrentName) + LoadingState.Loaded(torrentName) } } catch (_: FileParseException) { - loadingState.value = LoadingState.FileLoadingError.ParsingError - return@withContext + LoadingState.FileLoadingError.ParsingError } } finally { if (closeFd) {