@@ -18,16 +18,23 @@ import androidx.compose.runtime.mutableStateOf
1818import androidx.core.os.BundleCompat
1919import androidx.core.os.bundleOf
2020import androidx.lifecycle.SavedStateHandle
21+ import androidx.lifecycle.application
2122import androidx.lifecycle.viewModelScope
2223import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
2324import androidx.lifecycle.viewmodel.compose.saveable
2425import kotlinx.coroutines.Dispatchers
26+ import kotlinx.coroutines.flow.MutableStateFlow
27+ import kotlinx.coroutines.flow.SharingStarted
28+ import kotlinx.coroutines.flow.StateFlow
29+ import kotlinx.coroutines.flow.combine
30+ import kotlinx.coroutines.flow.stateIn
2531import kotlinx.coroutines.launch
2632import kotlinx.coroutines.withContext
2733import kotlinx.parcelize.Parcelize
2834import org.equeim.tremotesf.R
2935import org.equeim.tremotesf.rpc.GlobalRpcClient
3036import org.equeim.tremotesf.rpc.RpcRequestError
37+ import org.equeim.tremotesf.rpc.RpcRequestState
3138import org.equeim.tremotesf.rpc.requests.addTorrentFile
3239import org.equeim.tremotesf.rpc.requests.checkIfTorrentsExist
3340import org.equeim.tremotesf.rpc.requests.torrentproperties.addTorrentTrackers
@@ -56,7 +63,7 @@ interface AddTorrentFileModel {
5663 data object Aborted : LoadingState
5764 }
5865
59- val loadingState: State <LoadingState >
66+ val loadingState: StateFlow <LoadingState >
6067 val needStoragePermission: Boolean
6168 val filesTree: TorrentFilesTree
6269
@@ -74,7 +81,18 @@ class AddTorrentFileModelImpl(
7481 override val needStoragePermission: Boolean =
7582 uri.scheme == ContentResolver .SCHEME_FILE && Build .VERSION .SDK_INT <= Build .VERSION_CODES .Q
7683
77- override val loadingState = mutableStateOf<LoadingState >(LoadingState .Initial )
84+ private val torrentLoadingState = MutableStateFlow <LoadingState >(LoadingState .Initial )
85+ override val loadingState: StateFlow <LoadingState > = combine(torrentLoadingState, initialRpcInputs) { torrentLoadingState, initialRpcInputs ->
86+ if (torrentLoadingState is LoadingState .Loaded ) {
87+ when (initialRpcInputs) {
88+ is RpcRequestState .Error -> LoadingState .InitialRpcInputsError (initialRpcInputs.error)
89+ is RpcRequestState .Loading -> LoadingState .Loading
90+ is RpcRequestState .Loaded -> torrentLoadingState
91+ }
92+ } else {
93+ torrentLoadingState
94+ }
95+ }.stateIn(viewModelScope, SharingStarted .WhileSubscribed (), LoadingState .Initial )
7896
7997 private var fd: AssetFileDescriptor ? = null
8098
@@ -106,45 +124,39 @@ class AddTorrentFileModelImpl(
106124 }
107125
108126 override fun load () {
109- if (loadingState.value == LoadingState .Initial ) {
110- Timber .i(" load: loading $uri " )
111- loadingState.value = LoadingState .Loading
112- viewModelScope.launch {
113- doLoad(uri, getApplication())
114- }
115- } else {
127+ if (! torrentLoadingState.compareAndSet(LoadingState .Initial , LoadingState .Loading )) {
116128 Timber .e(" load: loadingState is not Initial" )
129+ return
130+ }
131+ viewModelScope.launch {
132+ Timber .i(" load: loading $uri " )
133+ torrentLoadingState.value = doLoad(uri, application)
117134 }
118135 }
119136
120- private suspend fun doLoad (uri : Uri , context : Context ) = withContext(Dispatchers .IO ) {
137+ private suspend fun doLoad (uri : Uri , context : Context ): LoadingState = withContext(Dispatchers .IO ) {
121138 Timber .d(" Parsing torrent file from URI $uri " )
122139
123140 val fd = try {
124141 context.contentResolver.openAssetFileDescriptor(uri, " r" )
125142 } catch (e: Exception ) {
126143 Timber .e(e, " Failed to open file descriptor" )
127- loadingState.value = LoadingState .FileLoadingError .ReadingError
128- return @withContext
144+ return @withContext LoadingState .FileLoadingError .ReadingError
129145 }
130146 if (fd == null ) {
131147 Timber .e(" File descriptor is null" )
132- loadingState.value = LoadingState .FileLoadingError .ReadingError
133- return @withContext
148+ return @withContext LoadingState .FileLoadingError .ReadingError
134149 }
135150 var closeFd = true
136151 try {
137152 val parseResult = try {
138153 TorrentFileParser .parseTorrentFile(fd.fileDescriptor)
139154 } catch (_: FileReadException ) {
140- loadingState.value = LoadingState .FileLoadingError .ReadingError
141- return @withContext
155+ return @withContext LoadingState .FileLoadingError .ReadingError
142156 } catch (_: FileIsTooLargeException ) {
143- loadingState.value = LoadingState .FileLoadingError .FileIsTooLarge
144- return @withContext
157+ return @withContext LoadingState .FileLoadingError .FileIsTooLarge
145158 } catch (_: FileParseException ) {
146- loadingState.value = LoadingState .FileLoadingError .ParsingError
147- return @withContext
159+ return @withContext LoadingState .FileLoadingError .ParsingError
148160 }
149161
150162 Timber .d(" Parsed torrent file from URI $uri , its info hash is ${parseResult.infoHashV1} " )
@@ -154,11 +166,10 @@ class AddTorrentFileModelImpl(
154166
155167 checkIfTorrentExists()
156168 if (checkIfTorrentExists()) {
157- loadingState.value = LoadingState .Aborted
158- return @withContext
169+ return @withContext LoadingState .Aborted
159170 }
160171
161- try {
172+ return @withContext try {
162173 val (rootNode, files) = TorrentFileParser .createFilesTree(parseResult)
163174 withContext(Dispatchers .Main ) {
164175 closeFd = false
@@ -182,11 +193,10 @@ class AddTorrentFileModelImpl(
182193 }
183194 }
184195 }
185- loadingState.value = LoadingState .Loaded (torrentName)
196+ LoadingState .Loaded (torrentName)
186197 }
187198 } catch (_: FileParseException ) {
188- loadingState.value = LoadingState .FileLoadingError .ParsingError
189- return @withContext
199+ LoadingState .FileLoadingError .ParsingError
190200 }
191201 } finally {
192202 if (closeFd) {
0 commit comments