|
|
@@ -1,174 +1,372 @@
|
|
|
package io.legado.app.ui.book.import.remote
|
|
|
|
|
|
import android.app.Application
|
|
|
-import androidx.lifecycle.MutableLiveData
|
|
|
+import androidx.compose.runtime.Immutable
|
|
|
+import androidx.lifecycle.SavedStateHandle
|
|
|
+import androidx.lifecycle.viewModelScope
|
|
|
import io.legado.app.base.BaseViewModel
|
|
|
+import io.legado.app.constant.AppConst
|
|
|
import io.legado.app.constant.AppLog
|
|
|
import io.legado.app.constant.BookType
|
|
|
import io.legado.app.data.appDb
|
|
|
+import io.legado.app.data.entities.Book
|
|
|
+import io.legado.app.data.entities.Server
|
|
|
+import io.legado.app.data.repository.RemoteBookRepository
|
|
|
import io.legado.app.exception.NoStackTraceException
|
|
|
import io.legado.app.help.AppWebDav
|
|
|
import io.legado.app.help.config.AppConfig
|
|
|
-import io.legado.app.lib.webdav.Authorization
|
|
|
import io.legado.app.model.analyzeRule.CustomUrl
|
|
|
import io.legado.app.model.localBook.LocalBook
|
|
|
import io.legado.app.model.remote.RemoteBook
|
|
|
import io.legado.app.model.remote.RemoteBookWebDav
|
|
|
+import io.legado.app.ui.widget.components.rules.InteractionState
|
|
|
+import io.legado.app.ui.widget.components.rules.ListUiState
|
|
|
+import io.legado.app.ui.widget.components.rules.SelectableItem
|
|
|
import io.legado.app.utils.AlphanumComparator
|
|
|
+import io.legado.app.utils.ConvertUtils
|
|
|
import io.legado.app.utils.toastOnUi
|
|
|
import kotlinx.coroutines.Dispatchers
|
|
|
-import kotlinx.coroutines.channels.awaitClose
|
|
|
-import kotlinx.coroutines.flow.callbackFlow
|
|
|
+import kotlinx.coroutines.channels.BufferOverflow
|
|
|
+import kotlinx.coroutines.flow.MutableSharedFlow
|
|
|
+import kotlinx.coroutines.flow.MutableStateFlow
|
|
|
+import kotlinx.coroutines.flow.SharedFlow
|
|
|
+import kotlinx.coroutines.flow.SharingStarted
|
|
|
+import kotlinx.coroutines.flow.StateFlow
|
|
|
+import kotlinx.coroutines.flow.combine
|
|
|
import kotlinx.coroutines.flow.flowOn
|
|
|
-import kotlinx.coroutines.flow.map
|
|
|
-import java.util.Collections
|
|
|
+import kotlinx.coroutines.flow.stateIn
|
|
|
+import kotlinx.coroutines.flow.update
|
|
|
+import kotlinx.coroutines.launch
|
|
|
+import kotlinx.coroutines.withContext
|
|
|
|
|
|
-class RemoteBookViewModel(application: Application) : BaseViewModel(application) {
|
|
|
- var sortKey = RemoteBookSort.Default
|
|
|
- var sortAscending = false
|
|
|
- val dirList = arrayListOf<RemoteBook>()
|
|
|
- val permissionDenialLiveData = MutableLiveData<Int>()
|
|
|
+@Immutable
|
|
|
+data class NavigationState(
|
|
|
+ val pathNames: List<String> = emptyList(),
|
|
|
+ val canGoBack: Boolean = false
|
|
|
+)
|
|
|
|
|
|
- var dataCallback: DataCallback? = null
|
|
|
+@Immutable
|
|
|
+data class RemoteBookItemUi(
|
|
|
+ val remoteBook: RemoteBook,
|
|
|
+ val nameWithoutExtension: String,
|
|
|
+ val formattedSize: String,
|
|
|
+ val formattedDate: String,
|
|
|
+ override val id: String = remoteBook.path
|
|
|
+) : SelectableItem<String>
|
|
|
|
|
|
- val dataFlow = callbackFlow<List<RemoteBook>> {
|
|
|
+@Immutable
|
|
|
+data class RemoteBookUiState(
|
|
|
+ override val items: List<RemoteBookItemUi> = emptyList(),
|
|
|
+ override val selectedIds: Set<Any> = emptySet(),
|
|
|
+ override val searchKey: String = "",
|
|
|
+ val interaction: InteractionState = InteractionState(isLoading = true),
|
|
|
+ val navigation: NavigationState = NavigationState(),
|
|
|
+ val sortKey: RemoteBookSort = RemoteBookSort.Default,
|
|
|
+ val sortAscending: Boolean = false,
|
|
|
+ val servers: List<Server> = emptyList(),
|
|
|
+ val selectedServerId: Long = AppConfig.remoteServerId
|
|
|
+) : ListUiState<RemoteBookItemUi> {
|
|
|
+ override val isSearch: Boolean get() = interaction.isSearchMode
|
|
|
+ override val isLoading: Boolean get() = interaction.isLoading
|
|
|
+ val pathNames: List<String> get() = navigation.pathNames
|
|
|
+ val canGoBack: Boolean get() = navigation.canGoBack
|
|
|
+}
|
|
|
|
|
|
- val list = Collections.synchronizedList(ArrayList<RemoteBook>())
|
|
|
+class RemoteBookViewModel(
|
|
|
+ application: Application,
|
|
|
+ @Suppress("UNUSED_PARAMETER")
|
|
|
+ private val savedStateHandle: SavedStateHandle,
|
|
|
+ private val repository: RemoteBookRepository
|
|
|
+) : BaseViewModel(application) {
|
|
|
|
|
|
- dataCallback = object : DataCallback {
|
|
|
+ private data class InternalState(
|
|
|
+ val remoteBooks: List<RemoteBook> = emptyList(),
|
|
|
+ val sortKey: RemoteBookSort = RemoteBookSort.Default,
|
|
|
+ val sortAscending: Boolean = false,
|
|
|
+ val dirList: List<RemoteBook> = emptyList(),
|
|
|
+ val interaction: InteractionState = InteractionState(isLoading = true),
|
|
|
+ val searchKey: String = "",
|
|
|
+ val selectedIds: Set<String> = emptySet(),
|
|
|
+ val remoteBookWebDav: RemoteBookWebDav? = null,
|
|
|
+ val isDefaultWebdav: Boolean = false,
|
|
|
+ val selectedServerId: Long = AppConfig.remoteServerId
|
|
|
+ )
|
|
|
|
|
|
- override fun setItems(remoteFiles: List<RemoteBook>) {
|
|
|
- list.clear()
|
|
|
- list.addAll(remoteFiles)
|
|
|
- trySend(list)
|
|
|
- }
|
|
|
+ private val _state = MutableStateFlow(InternalState())
|
|
|
|
|
|
- override fun addItems(remoteFiles: List<RemoteBook>) {
|
|
|
- list.addAll(remoteFiles)
|
|
|
- trySend(list)
|
|
|
- }
|
|
|
+ val uiState: StateFlow<RemoteBookUiState> = combine(
|
|
|
+ _state,
|
|
|
+ repository.flowLocalBooks(),
|
|
|
+ appDb.serverDao.observeAll()
|
|
|
+ ) { state, localBooks, servers ->
|
|
|
+ val localFileNames = localBooks.map { it.originName }.toSet()
|
|
|
|
|
|
- override fun clear() {
|
|
|
- list.clear()
|
|
|
- trySend(emptyList())
|
|
|
- }
|
|
|
+ val sortedBooks = sortBooks(state.remoteBooks, state.sortKey, state.sortAscending)
|
|
|
+ val filteredBooks = if (state.searchKey.isBlank()) {
|
|
|
+ sortedBooks
|
|
|
+ } else {
|
|
|
+ sortedBooks.filter { it.filename.contains(state.searchKey, ignoreCase = true) }
|
|
|
+ }
|
|
|
|
|
|
- override fun screen(key: String?) {
|
|
|
- if (key.isNullOrBlank()) {
|
|
|
- trySend(list)
|
|
|
+ RemoteBookUiState(
|
|
|
+ items = filteredBooks.map { book ->
|
|
|
+ val isOnShelf = if (book.isDir) false else localFileNames.contains(book.filename)
|
|
|
+ val updatedBook =
|
|
|
+ if (book.isOnBookShelf != isOnShelf) book.copy(isOnBookShelf = isOnShelf) else book
|
|
|
+ RemoteBookItemUi(
|
|
|
+ remoteBook = updatedBook,
|
|
|
+ nameWithoutExtension = book.filename.substringBeforeLast("."),
|
|
|
+ formattedSize = ConvertUtils.formatFileSize(book.size),
|
|
|
+ formattedDate = AppConst.dateFormat.format(book.lastModify)
|
|
|
+ )
|
|
|
+ },
|
|
|
+ selectedIds = state.selectedIds,
|
|
|
+ searchKey = state.searchKey,
|
|
|
+ interaction = state.interaction,
|
|
|
+ navigation = NavigationState(
|
|
|
+ pathNames = listOf(if (state.isDefaultWebdav) "books" else "/").plus(state.dirList.map { it.filename }),
|
|
|
+ canGoBack = state.dirList.isNotEmpty()
|
|
|
+ ),
|
|
|
+ sortKey = state.sortKey,
|
|
|
+ sortAscending = state.sortAscending,
|
|
|
+ servers = servers,
|
|
|
+ selectedServerId = state.selectedServerId
|
|
|
+ )
|
|
|
+ }.flowOn(Dispatchers.Default)
|
|
|
+ .stateIn(
|
|
|
+ scope = viewModelScope,
|
|
|
+ started = SharingStarted.WhileSubscribed(5000),
|
|
|
+ initialValue = RemoteBookUiState()
|
|
|
+ )
|
|
|
+
|
|
|
+ private val _permissionDenialEvent = MutableSharedFlow<Int>(
|
|
|
+ replay = 0,
|
|
|
+ extraBufferCapacity = 1,
|
|
|
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
|
|
|
+ )
|
|
|
+ val permissionDenialEvent: SharedFlow<Int> = _permissionDenialEvent
|
|
|
+
|
|
|
+ fun initData(onSuccess: () -> Unit) {
|
|
|
+ viewModelScope.launch {
|
|
|
+ try {
|
|
|
+ val webDav = repository.createWebDav(AppConfig.remoteServerId)
|
|
|
+ if (webDav != null) {
|
|
|
+ _state.update {
|
|
|
+ it.copy(
|
|
|
+ remoteBookWebDav = webDav,
|
|
|
+ isDefaultWebdav = false,
|
|
|
+ selectedServerId = AppConfig.remoteServerId
|
|
|
+ )
|
|
|
+ }
|
|
|
+ onSuccess()
|
|
|
} else {
|
|
|
- trySend(
|
|
|
- list.filter { it.filename.contains(key) }
|
|
|
- )
|
|
|
+ val defaultWebDav = AppWebDav.defaultBookWebDav
|
|
|
+ ?: throw NoStackTraceException("webDav没有配置")
|
|
|
+ _state.update {
|
|
|
+ it.copy(
|
|
|
+ remoteBookWebDav = defaultWebDav,
|
|
|
+ isDefaultWebdav = true,
|
|
|
+ selectedServerId = AppConst.DEFAULT_WEBDAV_ID
|
|
|
+ )
|
|
|
+ }
|
|
|
+ onSuccess()
|
|
|
}
|
|
|
+ } catch (e: Exception) {
|
|
|
+ context.toastOnUi("初始化webDav出错:${e.localizedMessage}")
|
|
|
+ _state.update { it.copy(interaction = it.interaction.copy(isLoading = false)) }
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- awaitClose {
|
|
|
- dataCallback = null
|
|
|
- }
|
|
|
- }.map { list ->
|
|
|
- if (sortAscending) when (sortKey) {
|
|
|
- RemoteBookSort.Name -> list.sortedWith(compareBy<RemoteBook> { !it.isDir }
|
|
|
- then compareBy(AlphanumComparator) { it.filename })
|
|
|
-
|
|
|
- else -> list.sortedWith(compareBy({ !it.isDir }, { it.lastModify }))
|
|
|
- } else when (sortKey) {
|
|
|
- RemoteBookSort.Name -> list.sortedWith { o1, o2 ->
|
|
|
- val compare = -compareValues(o1.isDir, o2.isDir)
|
|
|
- if (compare == 0) {
|
|
|
- return@sortedWith -AlphanumComparator.compare(o1.filename, o2.filename)
|
|
|
+ fun loadRemoteBookList(path: String? = null) {
|
|
|
+ _state.update { it.copy(interaction = it.interaction.copy(isLoading = true)) }
|
|
|
+ viewModelScope.launch {
|
|
|
+ try {
|
|
|
+ val webDav = _state.value.remoteBookWebDav
|
|
|
+ ?: throw NoStackTraceException("没有配置webDav")
|
|
|
+ val bookList = withContext(Dispatchers.IO) {
|
|
|
+ repository.loadBooks(webDav, path)
|
|
|
}
|
|
|
- return@sortedWith compare
|
|
|
+ _state.update { it.copy(remoteBooks = bookList) }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ AppLog.put("获取webDav书籍出错\n${e.localizedMessage}", e)
|
|
|
+ context.toastOnUi("获取webDav书籍出错\n${e.localizedMessage}")
|
|
|
+ } finally {
|
|
|
+ _state.update { it.copy(interaction = it.interaction.copy(isLoading = false)) }
|
|
|
}
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- else -> list.sortedWith { o1, o2 ->
|
|
|
- val compare = -compareValues(o1.isDir, o2.isDir)
|
|
|
- if (compare == 0) {
|
|
|
- return@sortedWith -compareValues(o1.lastModify, o2.lastModify)
|
|
|
+ suspend fun addToBookshelf(remoteBooks: Set<RemoteBook>): Result<Unit> {
|
|
|
+ _state.update { it.copy(interaction = it.interaction.copy(isUploading = true)) }
|
|
|
+ return try {
|
|
|
+ val webDav = _state.value.remoteBookWebDav
|
|
|
+ ?: throw NoStackTraceException("没有配置webDav")
|
|
|
+
|
|
|
+ withContext(Dispatchers.IO) {
|
|
|
+ remoteBooks.forEach { remoteBook ->
|
|
|
+ val downloadBookUri = repository.downloadBook(webDav, remoteBook)
|
|
|
+ LocalBook.importFiles(downloadBookUri).forEach { book ->
|
|
|
+ book.origin = BookType.webDavTag + CustomUrl(remoteBook.path)
|
|
|
+ .putAttribute("serverID", webDav.serverID)
|
|
|
+ .toString()
|
|
|
+ book.save()
|
|
|
+ }
|
|
|
}
|
|
|
- return@sortedWith compare
|
|
|
}
|
|
|
+
|
|
|
+ _state.update { it.copy(selectedIds = emptySet()) }
|
|
|
+ Result.success(Unit)
|
|
|
+ } catch (e: SecurityException) {
|
|
|
+ _permissionDenialEvent.emit(1)
|
|
|
+ Result.failure(e)
|
|
|
+ } catch (e: Exception) {
|
|
|
+ AppLog.put("导入出错\n${e.localizedMessage}", e)
|
|
|
+ context.toastOnUi("导入出错\n${e.localizedMessage}")
|
|
|
+ Result.failure(e)
|
|
|
+ } finally {
|
|
|
+ _state.update { it.copy(interaction = it.interaction.copy(isUploading = false)) }
|
|
|
}
|
|
|
- }.flowOn(Dispatchers.IO)
|
|
|
+ }
|
|
|
|
|
|
- private var remoteBookWebDav: RemoteBookWebDav? = null
|
|
|
- var isDefaultWebdav = false
|
|
|
+ suspend fun getLocalBook(fileName: String): Book? {
|
|
|
+ return withContext(Dispatchers.IO) {
|
|
|
+ appDb.bookDao.getBookByFileName(fileName)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- fun initData(onSuccess: () -> Unit) {
|
|
|
- execute {
|
|
|
- isDefaultWebdav = false
|
|
|
- appDb.serverDao.get(AppConfig.remoteServerId)?.getWebDavConfig()?.let {
|
|
|
- val authorization = Authorization(it)
|
|
|
- remoteBookWebDav = RemoteBookWebDav(it.url, authorization, AppConfig.remoteServerId)
|
|
|
- return@execute
|
|
|
- }
|
|
|
- isDefaultWebdav = true
|
|
|
- remoteBookWebDav = AppWebDav.defaultBookWebDav
|
|
|
- ?: throw NoStackTraceException("webDav没有配置")
|
|
|
- }.onError {
|
|
|
- context.toastOnUi("初始化webDav出错:${it.localizedMessage}")
|
|
|
- }.onSuccess {
|
|
|
- onSuccess.invoke()
|
|
|
+ fun navigateToDir(book: RemoteBook) {
|
|
|
+ _state.update { it.copy(dirList = it.dirList + book, selectedIds = emptySet()) }
|
|
|
+ loadRemoteBookList(book.path)
|
|
|
+ }
|
|
|
+
|
|
|
+ fun navigateBack() {
|
|
|
+ val lastPath = _state.value.dirList.let {
|
|
|
+ if (it.size > 1) it[it.size - 2].path else null
|
|
|
}
|
|
|
+ _state.update {
|
|
|
+ if (it.dirList.isNotEmpty()) {
|
|
|
+ it.copy(dirList = it.dirList.dropLast(1), selectedIds = emptySet())
|
|
|
+ } else it
|
|
|
+ }
|
|
|
+ loadRemoteBookList(lastPath)
|
|
|
}
|
|
|
|
|
|
- fun loadRemoteBookList(path: String?, loadCallback: (loading: Boolean) -> Unit) {
|
|
|
- executeLazy {
|
|
|
- val bookWebDav = remoteBookWebDav
|
|
|
- ?: throw NoStackTraceException("没有配置webDav")
|
|
|
- dataCallback?.clear()
|
|
|
- val url = path ?: bookWebDav.rootBookUrl
|
|
|
- val bookList = bookWebDav.getRemoteBookList(url)
|
|
|
- dataCallback?.setItems(bookList)
|
|
|
- }.onError {
|
|
|
- AppLog.put("获取webDav书籍出错\n${it.localizedMessage}", it)
|
|
|
- context.toastOnUi("获取webDav书籍出错\n${it.localizedMessage}")
|
|
|
- }.onStart {
|
|
|
- loadCallback.invoke(true)
|
|
|
- }.onFinally {
|
|
|
- loadCallback.invoke(false)
|
|
|
- }.start()
|
|
|
- }
|
|
|
-
|
|
|
- fun addToBookshelf(remoteBooks: HashSet<RemoteBook>, finally: () -> Unit) {
|
|
|
- execute {
|
|
|
- val bookWebDav = remoteBookWebDav
|
|
|
- ?: throw NoStackTraceException("没有配置webDav")
|
|
|
- remoteBooks.forEach { remoteBook ->
|
|
|
- val downloadBookUri = bookWebDav.downloadRemoteBook(remoteBook)
|
|
|
- LocalBook.importFiles(downloadBookUri).forEach { book ->
|
|
|
- book.origin = BookType.webDavTag + CustomUrl(remoteBook.path)
|
|
|
- .putAttribute("serverID", bookWebDav.serverID)
|
|
|
- .toString()
|
|
|
- book.save()
|
|
|
- }
|
|
|
- remoteBook.isOnBookShelf = true
|
|
|
- }
|
|
|
- }.onError {
|
|
|
- AppLog.put("导入出错\n${it.localizedMessage}", it)
|
|
|
- context.toastOnUi("导入出错\n${it.localizedMessage}")
|
|
|
- if (it is SecurityException) {
|
|
|
- permissionDenialLiveData.postValue(1)
|
|
|
+ fun navigateToLevel(index: Int) {
|
|
|
+ if (index < 0) return
|
|
|
+ val currentDirList = _state.value.dirList
|
|
|
+ if (index > currentDirList.size) return
|
|
|
+
|
|
|
+ if (index == 0) {
|
|
|
+ _state.update { it.copy(dirList = emptyList(), selectedIds = emptySet()) }
|
|
|
+ loadRemoteBookList(null)
|
|
|
+ } else {
|
|
|
+ val newDirList = currentDirList.take(index)
|
|
|
+ val targetPath = newDirList.last().path
|
|
|
+ _state.update { it.copy(dirList = newDirList, selectedIds = emptySet()) }
|
|
|
+ loadRemoteBookList(targetPath)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun setSearchMode(isSearch: Boolean) {
|
|
|
+ _state.update {
|
|
|
+ it.copy(
|
|
|
+ interaction = it.interaction.copy(isSearchMode = isSearch),
|
|
|
+ searchKey = if (isSearch) it.searchKey else ""
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun setSearchKey(key: String) {
|
|
|
+ _state.update { it.copy(searchKey = key) }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun updateSort(sortKey: RemoteBookSort, ascending: Boolean) {
|
|
|
+ _state.update { it.copy(sortKey = sortKey, sortAscending = ascending) }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun toggleSort(sortKey: RemoteBookSort) {
|
|
|
+ _state.update { state ->
|
|
|
+ if (state.sortKey == sortKey) {
|
|
|
+ state.copy(sortAscending = !state.sortAscending)
|
|
|
+ } else {
|
|
|
+ state.copy(sortKey = sortKey, sortAscending = true)
|
|
|
}
|
|
|
- }.onFinally {
|
|
|
- finally.invoke()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun updateCallBackFlow(filterKey: String?) {
|
|
|
- dataCallback?.screen(filterKey)
|
|
|
+ fun toggleSelection(id: String) {
|
|
|
+ _state.update {
|
|
|
+ val newSelected = if (id in it.selectedIds) it.selectedIds - id else it.selectedIds + id
|
|
|
+ it.copy(selectedIds = newSelected)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- interface DataCallback {
|
|
|
+ fun selectAllCheckable() {
|
|
|
+ _state.update { state ->
|
|
|
+ val checkableIds = state.remoteBooks
|
|
|
+ .filter { !it.isDir && !it.isOnBookShelf }
|
|
|
+ .map { it.path }
|
|
|
+ .toSet()
|
|
|
+ state.copy(selectedIds = checkableIds)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- fun setItems(remoteFiles: List<RemoteBook>)
|
|
|
+ fun invertSelection() {
|
|
|
+ _state.update { state ->
|
|
|
+ val checkableIds = state.remoteBooks
|
|
|
+ .filter { !it.isDir && !it.isOnBookShelf }
|
|
|
+ .map { it.path }
|
|
|
+ .toSet()
|
|
|
+ state.copy(selectedIds = checkableIds - state.selectedIds)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- fun addItems(remoteFiles: List<RemoteBook>)
|
|
|
+ fun clearSelection() {
|
|
|
+ _state.update { it.copy(selectedIds = emptySet()) }
|
|
|
+ }
|
|
|
|
|
|
- fun clear()
|
|
|
+ fun refreshData() {
|
|
|
+ val currentPath = _state.value.dirList.lastOrNull()?.path
|
|
|
+ loadRemoteBookList(currentPath)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun sortBooks(
|
|
|
+ books: List<RemoteBook>,
|
|
|
+ sortKey: RemoteBookSort,
|
|
|
+ ascending: Boolean
|
|
|
+ ): List<RemoteBook> {
|
|
|
+ return books.sortedWith { o1, o2 ->
|
|
|
+ val dirCompare = compareValues(o1.isDir, o2.isDir)
|
|
|
+ if (dirCompare != 0) return@sortedWith -dirCompare
|
|
|
+
|
|
|
+ val result = when (sortKey) {
|
|
|
+ RemoteBookSort.Name -> AlphanumComparator.compare(o1.filename, o2.filename)
|
|
|
+ else -> compareValues(o1.lastModify, o2.lastModify)
|
|
|
+ }
|
|
|
+ if (ascending) result else -result
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- fun screen(key: String?)
|
|
|
+ fun saveServer(server: Server) {
|
|
|
+ execute {
|
|
|
+ appDb.serverDao.insert(server)
|
|
|
+ }.onSuccess {
|
|
|
+ if (AppConfig.remoteServerId == server.id) {
|
|
|
+ initData { loadRemoteBookList() }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun deleteServer(server: Server) {
|
|
|
+ execute {
|
|
|
+ appDb.serverDao.delete(server)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ fun selectServer(serverId: Long) {
|
|
|
+ AppConfig.remoteServerId = serverId
|
|
|
+ _state.update { it.copy(selectedServerId = serverId, dirList = emptyList()) }
|
|
|
+ initData { loadRemoteBookList() }
|
|
|
}
|
|
|
-}
|
|
|
+}
|