diff --git a/androidVariant/src/main/java/org/comixedproject/variant/android/MainActivity.kt b/androidVariant/src/main/java/org/comixedproject/variant/android/MainActivity.kt index a3b3a66..5347a0a 100644 --- a/androidVariant/src/main/java/org/comixedproject/variant/android/MainActivity.kt +++ b/androidVariant/src/main/java/org/comixedproject/variant/android/MainActivity.kt @@ -93,6 +93,8 @@ class MainActivity : ComponentActivity() { variantViewModel.username = username variantViewModel.password = password }, + onToggleFiltering = { toggle -> variantViewModel.toggleFiltering(toggle) }, + onUpdateFilterText = { text -> variantViewModel.updateFilterText(text) }, ) } } diff --git a/androidVariant/src/main/java/org/comixedproject/variant/android/view/HomeView.kt b/androidVariant/src/main/java/org/comixedproject/variant/android/view/HomeView.kt index adeaeaa..c7e9cb7 100644 --- a/androidVariant/src/main/java/org/comixedproject/variant/android/view/HomeView.kt +++ b/androidVariant/src/main/java/org/comixedproject/variant/android/view/HomeView.kt @@ -57,6 +57,8 @@ fun HomeView( username: String, password: String, onLoadDirectory: (String, Boolean) -> Unit, + onToggleFiltering: (Boolean) -> Unit, + onUpdateFilterText: (String) -> Unit, onDownloadFile: (String, String) -> Unit, onReadComicBook: (ComicBook?) -> Unit, onSetSelectionMode: (Boolean) -> Unit, @@ -128,6 +130,8 @@ fun HomeView( onLoadDirectory = { path, reload -> onLoadDirectory(path, reload) }, onDownloadFile = { path, filename -> onDownloadFile(path, filename) }, modifier = Modifier.fillMaxSize(), + onToggleFiltering = onToggleFiltering, + onUpdateFilterText = onUpdateFilterText, ) AppDestination.SETTINGS -> @@ -139,8 +143,8 @@ fun HomeView( Log.info( TAG, "Updating server settings: address=${address} username=${username} password=${ - password.first() - }*****", + password.first() + }*****", ) onSaveSettings(address, username, password) currentDestination = AppDestination.COMICS @@ -160,7 +164,7 @@ fun HomeViewPreview() { HomeView( COMIC_BOOK_LIST.get(0), COMIC_BOOK_LIST, - BrowsingState("", "", "", listOf(), listOf()), + BrowsingState("", "", false, "", "", listOf(), listOf()), false, false, listOf(), @@ -168,6 +172,8 @@ fun HomeViewPreview() { "reader@comixedproject.org", "my!password", onLoadDirectory = { _, _ -> }, + onToggleFiltering = {}, + onUpdateFilterText = {}, onDownloadFile = { _, _ -> }, onReadComicBook = { _ -> }, onSetSelectionMode = { _ -> }, diff --git a/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookListView.kt b/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookListView.kt index c4d581d..bd310a2 100644 --- a/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookListView.kt +++ b/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookListView.kt @@ -29,14 +29,12 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.pluralStringResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.comixedproject.variant.android.COMIC_BOOK_LIST import org.comixedproject.variant.android.R import org.comixedproject.variant.android.VariantTheme import org.comixedproject.variant.model.library.ComicBook -import org.comixedproject.variant.platform.Log private val TAG = "ComicBookListView" @@ -55,26 +53,21 @@ fun ComicBookListView( ) }, content = { padding -> - if (comicBookList.isEmpty()) { - Log.debug(TAG, "No comics to display") - Text(stringResource(R.string.emptyComicListText)) - } else { - LazyVerticalStaggeredGrid( - columns = StaggeredGridCells.Adaptive(minSize = 128.dp), - verticalItemSpacing = 4.dp, - horizontalArrangement = Arrangement.spacedBy(4.dp), - content = { - items(comicBookList) { comicBook -> - ComicBookListItemView( - comicBook, - selectionList.contains(comicBook.path), - onClick = { onClick(it) }, - ) - } - }, - modifier = modifier.padding(padding), - ) - } + LazyVerticalStaggeredGrid( + columns = StaggeredGridCells.Adaptive(minSize = 128.dp), + verticalItemSpacing = 4.dp, + horizontalArrangement = Arrangement.spacedBy(4.dp), + content = { + items(comicBookList) { comicBook -> + ComicBookListItemView( + comicBook, + selectionList.contains(comicBook.path), + onClick = { onClick(it) }, + ) + } + }, + modifier = modifier.padding(padding), + ) }, modifier = modifier.padding(8.dp), ) @@ -85,3 +78,9 @@ fun ComicBookListView( fun ComicBookListViewPreview() { VariantTheme { ComicBookListView(COMIC_BOOK_LIST, emptyList(), onClick = {}) } } + +@Composable +@Preview +fun ComicBookListViewPreviewWithOneComics() { + VariantTheme { ComicBookListView(listOf(COMIC_BOOK_LIST[0]), emptyList(), onClick = {}) } +} diff --git a/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookView.kt b/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookView.kt index 66373ca..9d20eb7 100644 --- a/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookView.kt +++ b/androidVariant/src/main/java/org/comixedproject/variant/android/view/comics/ComicBookView.kt @@ -110,7 +110,7 @@ fun ComicBookViewPreview() { @Composable @Preview -fun ComicBookViewWithSelectionsPreview() { +fun ComicBookViewPreviewWithSelections() { VariantTheme { ComicBookView( COMIC_BOOK_LIST, diff --git a/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/BrowseServerView.kt b/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/BrowseServerView.kt index 4bfde37..d76bcd4 100644 --- a/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/BrowseServerView.kt +++ b/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/BrowseServerView.kt @@ -30,6 +30,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable @@ -37,7 +38,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.toLowerCase import androidx.compose.ui.tooling.preview.Preview import org.comixedproject.variant.android.DIRECTORY_LIST import org.comixedproject.variant.android.R @@ -49,18 +52,27 @@ import org.comixedproject.variant.platform.Log private const val TAG = "BrowseServerView" +fun checkFiltering(filterText: String, entry: DirectoryEntry): Boolean { + return entry.title.toLowerCase(Locale.current).contains(filterText) || + entry.filename.toLowerCase(Locale.current).contains(filterText) +} + @OptIn(ExperimentalMaterial3Api::class) @Composable fun BrowseServerView( path: String, title: String, parentPath: String, + filtering: Boolean, + filterText: String, contents: List, comicBookList: List, downloadingState: List, loading: Boolean, onLoadDirectory: (String, Boolean) -> Unit, onDownloadFile: (String, String) -> Unit, + onToggleFiltering: (Boolean) -> Unit, + onUpdateFilterText: (String) -> Unit, modifier: Modifier = Modifier, ) { val pullToRefreshState = rememberPullToRefreshState() @@ -78,19 +90,45 @@ fun BrowseServerView( Icon(painterResource(R.drawable.ic_back), contentDescription = parentPath) } - val displayedTitle = - when (title.isEmpty()) { - false -> title - true -> stringResource(R.string.rootDirectoryTitle) - } + if (filtering) { + TextField( + value = filterText, + placeholder = { Text(stringResource(R.string.filter_list_placeholder)) }, + maxLines = 1, + onValueChange = { text -> + Log.debug(TAG, "Filter text=${text}") + onUpdateFilterText(text) + }, + modifier = Modifier.weight(1f), + ) + } else { + val displayedTitle = + when (title.isEmpty()) { + false -> title + true -> stringResource(R.string.rootDirectoryTitle) + } + + Text( + displayedTitle, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.headlineMedium, + modifier = Modifier.weight(1f), + ) + } - Text( - "${displayedTitle} [${downloadingState.size}]", - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.headlineMedium, - modifier = Modifier.weight(1f), - ) + IconButton( + onClick = { + Log.debug(TAG, "Toggle filtering") + onToggleFiltering(!filtering) + }, + enabled = !parentPath.isEmpty(), + ) { + Icon( + painterResource(R.drawable.ic_filter_text), + contentDescription = stringResource(R.string.filter_list), + ) + } } }, content = { padding -> @@ -100,7 +138,11 @@ fun BrowseServerView( onRefresh = { onLoadDirectory(path, true) }, content = { LazyColumn(modifier = Modifier.padding(padding).fillMaxWidth()) { - items(contents) { entry -> + items( + contents.filter { + !filtering || checkFiltering(filterText.toLowerCase(Locale.current), it) + } + ) { entry -> when (entry.isDirectory) { true -> DirectoryItemView( @@ -128,56 +170,92 @@ fun BrowseServerView( @Composable @Preview -fun BrowseServerViewPreviewDirectories() { +fun BrowseServerViewPreviewWithFiles() { + var directory = DIRECTORY_LIST.filter { it.isDirectory }.first() + VariantTheme { + BrowseServerView( + "http://www.comixedproject.org:7171", + directory.title, + directory.parent, + false, + "", + DIRECTORY_LIST.filter { !it.isDirectory }, + emptyList(), + emptyList(), + false, + onLoadDirectory = { _, _ -> }, + onDownloadFile = { _, _ -> }, + onToggleFiltering = {}, + onUpdateFilterText = {}, + ) + } +} + +@Composable +@Preview +fun BrowseServerViewPreviewWithDirectories() { VariantTheme { BrowseServerView( "http://www.comixedproject.org:7171", "", "", + false, + "", DIRECTORY_LIST.filter { it.isDirectory }, emptyList(), emptyList(), false, onLoadDirectory = { _, _ -> }, onDownloadFile = { _, _ -> }, + onToggleFiltering = {}, + onUpdateFilterText = {}, ) } } @Composable @Preview -fun BrowseServerViewPreviewFiles() { +fun BrowseServerViewPreviewRefreshing() { val directory = DIRECTORY_LIST.get(0) VariantTheme { BrowseServerView( "http://www.comixedproject.org:7171", directory.title, directory.parent, + false, + "", DIRECTORY_LIST.filter { !it.isDirectory }, emptyList(), emptyList(), - false, + true, onLoadDirectory = { _, _ -> }, onDownloadFile = { _, _ -> }, + onToggleFiltering = {}, + onUpdateFilterText = {}, ) } } @Composable @Preview -fun BrowseServerViewPreviewRefreshing() { +fun BrowseServerViewPreviewFiltering() { val directory = DIRECTORY_LIST.get(0) + val filterText = directory.title.substring(0, 3) VariantTheme { BrowseServerView( "http://www.comixedproject.org:7171", directory.title, directory.parent, - DIRECTORY_LIST.filter { !it.isDirectory }, + true, + filterText, + DIRECTORY_LIST, emptyList(), emptyList(), true, onLoadDirectory = { _, _ -> }, onDownloadFile = { _, _ -> }, + onToggleFiltering = {}, + onUpdateFilterText = {}, ) } } diff --git a/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/ServerView.kt b/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/ServerView.kt index 052b2de..5dbf940 100644 --- a/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/ServerView.kt +++ b/androidVariant/src/main/java/org/comixedproject/variant/android/view/server/ServerView.kt @@ -35,12 +35,16 @@ fun ServerView( loading: Boolean, onLoadDirectory: (String, Boolean) -> Unit, onDownloadFile: (String, String) -> Unit, + onToggleFiltering: (Boolean) -> Unit, + onUpdateFilterText: (String) -> Unit, modifier: Modifier = Modifier, ) { BrowseServerView( browsingState.currentPath, browsingState.title, browsingState.parentPath, + browsingState.filtering, + browsingState.filterText, browsingState.contents, comicBookList, browsingState.downloadingState, @@ -48,6 +52,8 @@ fun ServerView( modifier = modifier, onLoadDirectory = { path, reload -> onLoadDirectory(path, reload) }, onDownloadFile = { path, filename -> onDownloadFile(path, filename) }, + onToggleFiltering = onToggleFiltering, + onUpdateFilterText = onUpdateFilterText, ) } @@ -56,11 +62,13 @@ fun ServerView( fun ServerViewPreview() { VariantTheme { ServerView( - BrowsingState("", "", "", listOf(), listOf()), + BrowsingState("", "", false, "", "", listOf(), listOf()), COMIC_BOOK_LIST, false, onLoadDirectory = { _, _ -> }, onDownloadFile = { _, _ -> }, + onToggleFiltering = {}, + onUpdateFilterText = {}, ) } } diff --git a/androidVariant/src/main/res/drawable/ic_back.xml b/androidVariant/src/main/res/drawable/ic_back.xml index e917c27..a9b9ac2 100644 --- a/androidVariant/src/main/res/drawable/ic_back.xml +++ b/androidVariant/src/main/res/drawable/ic_back.xml @@ -1,9 +1,10 @@ + android:viewportHeight="98.86"> + android:pathData="M0,49.43l48.93,49.43V74.23c30.94,-6.41 55.39,0.66 73.95,24.19c-3.22,-48.4 -36.29,-71.76 -73.95,-73.31V0L0,49.43L0,49.43L0,49.43z" + android:fillType="evenOdd"/> diff --git a/androidVariant/src/main/res/drawable/ic_filter_text.xml b/androidVariant/src/main/res/drawable/ic_filter_text.xml new file mode 100644 index 0000000..27c9434 --- /dev/null +++ b/androidVariant/src/main/res/drawable/ic_filter_text.xml @@ -0,0 +1,10 @@ + + + diff --git a/androidVariant/src/main/res/values/strings.xml b/androidVariant/src/main/res/values/strings.xml index e48340b..cf0cd85 100644 --- a/androidVariant/src/main/res/values/strings.xml +++ b/androidVariant/src/main/res/values/strings.xml @@ -10,7 +10,6 @@ [Root Directory] Comics Settings - No comic books to show... %1$s MB Cancel Go to the previous page. @@ -19,6 +18,8 @@ Mark selected comics as read. Mark selected comics as unread. Delete selected comics from device. + Filter the contents of this list + Enter some filter text... %d Comic Book %d Comic Books diff --git a/iosVariant/Translations.xcstrings b/iosVariant/Translations.xcstrings index dfc9481..ab6816d 100644 --- a/iosVariant/Translations.xcstrings +++ b/iosVariant/Translations.xcstrings @@ -1,28 +1,32 @@ { "sourceLanguage" : "en", "strings" : { - "%lld Comic Books" : { + "browse-server.label.filter-text" : { + "comment" : "A label for a text field used to filter items in a list.", + "extractionState" : "extracted_with_value", + "isCommentAutoGenerated" : true, "localizations" : { "en" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "%lld Comic Book" - } - }, - "other" : { - "stringUnit" : { - "state" : "new", - "value" : "%lld Comic Books" - } - } - } + "stringUnit" : { + "state" : "new", + "value" : "Enter filter text..." + } + } + } + }, + "comic-book-list-view.label.navigation-title" : { + "comment" : "The navigation title for the comic book list view, showing the number of comic books.", + "extractionState" : "extracted_with_value", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%lld Comic Books" } } } } }, - "version" : "1.0" + "version" : "1.1" } \ No newline at end of file diff --git a/iosVariant/iosVariant/HomeView.swift b/iosVariant/iosVariant/HomeView.swift index bbae849..fc7fbfc 100644 --- a/iosVariant/iosVariant/HomeView.swift +++ b/iosVariant/iosVariant/HomeView.swift @@ -84,6 +84,8 @@ struct HomeView: View { ServerView( comicBookList: self.variantViewModel.comicBookList, currentPath: self.variantViewModel.browsingState.currentPath, + filtering: self.variantViewModel.browsingState.filtering, + filterText: self.variantViewModel.browsingState.filterText, title: self.variantViewModel.browsingState.title, parentPath: self.variantViewModel.browsingState.parentPath, directoryContents: self.variantViewModel.browsingState.contents, @@ -100,6 +102,14 @@ struct HomeView: View { reload: reload ) }, + onToggleFilter: { toggle in + Log().debug(tag: TAG, message: "Toggling filter: \(toggle)") + self.variantViewModel.toggleFiltering(toggle: toggle) + }, + onUpdateFilterText: { filterText in + Log().debug(tag: TAG, message: "Filter text: \(filterText)") + self.variantViewModel.updateFilterText(text: filterText) + }, onDownloadFile: { path, filename in Task { Log().debug( diff --git a/iosVariant/iosVariant/Views/Comics/ComicBookListView.swift b/iosVariant/iosVariant/Views/Comics/ComicBookListView.swift index 380c646..1dc7318 100644 --- a/iosVariant/iosVariant/Views/Comics/ComicBookListView.swift +++ b/iosVariant/iosVariant/Views/Comics/ComicBookListView.swift @@ -50,8 +50,8 @@ struct ComicBookListView: View { } .navigationTitle( String( - localized: "\(comicBookCount) Comic Books", - table: "Translations" + localized: "comic-book-list-view.label.navigation-title", + defaultValue: "\(comicBookCount) Comic Books", table: "Translations" ) ) } diff --git a/iosVariant/iosVariant/Views/Server/BrowseServerView.swift b/iosVariant/iosVariant/Views/Server/BrowseServerView.swift index c29f1fd..ea4bb54 100644 --- a/iosVariant/iosVariant/Views/Server/BrowseServerView.swift +++ b/iosVariant/iosVariant/Views/Server/BrowseServerView.swift @@ -16,6 +16,7 @@ * along with this program. If not, see */ +import Combine import KMPObservableViewModelSwiftUI import SwiftUI import shared @@ -25,15 +26,52 @@ private let TAG = "BrowseServerView" struct BrowseServerView: View { let comicBookList: [ComicBook] let path: String + let filtering: Bool + let filterText: String let title: String let parentPath: String let directoryContents: [DirectoryEntry] let downloadingState: [DownloadingState] let loading: Bool let onLoadDirectory: (String, Bool) -> Void + let onToggleFilter: (Bool) -> Void + let onUpdateFilterText: (String) -> Void let onDownloadFile: (String, String) -> Void @State private var selected: DirectoryEntry? + @State private var filterTextValue: String = "" + + init( + comicBookList: [ComicBook], + path: String, + filtering: Bool, + filterText: String, + title: String, + parentPath: String, + directoryContents: [DirectoryEntry], + downloadingState: [DownloadingState], + loading: Bool, + onLoadDirectory: @escaping (String, Bool) -> Void, + onToggleFilter: @escaping (Bool) -> Void, + onUpdateFilterText: @escaping (String) -> Void, + onDownloadFile: @escaping (String, String) -> Void + ) { + self.comicBookList = comicBookList + self.path = path + self.filtering = filtering + self.filterText = filterText + self.title = title + self.parentPath = parentPath + self.directoryContents = directoryContents + self.downloadingState = downloadingState + self.loading = loading + self.onLoadDirectory = onLoadDirectory + self.onToggleFilter = onToggleFilter + self.onUpdateFilterText = onUpdateFilterText + self.onDownloadFile = onDownloadFile + self.selected = selected + self.filterTextValue = filterText + } var displayableTitle: String { if title.isEmpty { @@ -42,10 +80,20 @@ struct BrowseServerView: View { return title } + var filteredContents: [DirectoryEntry] { + return self.directoryContents.filter { + !self.filtering || self.filterText.isEmpty + || ($0.title.lowercased().contains(self.filterText.lowercased()) + || $0.path.lowercased().contains( + self.filterText.lowercased() + )) + } + } + var body: some View { NavigationStack { ZStack { - List(directoryContents, id: \.id, selection: $selected) { + List(filteredContents, id: \.id, selection: $selected) { entry in if entry.isDirectory { DirectoryItemView( @@ -91,19 +139,50 @@ struct BrowseServerView: View { } .navigationTitle(displayableTitle) .toolbar { - if parentPath != "" { - ToolbarItem(placement: .topBarLeading) { - Button { - Log().info( - tag: TAG, - message: "Loading parent: \(parentPath)" + ToolbarItem(placement: .topBarLeading) { + HStack { + if parentPath != "" { + Button { + Log().info( + tag: TAG, + message: "Loading parent: \(parentPath)" + ) + onLoadDirectory(parentPath, false) + } label: { + Image("back") + } + } + + if filtering { + TextField( + String( + localized: + "browse-server.label.filter-text", + defaultValue: "Enter filter text...", table: "Translations" + ), + text: $filterTextValue ) - onLoadDirectory(parentPath, false) - } label: { - Image("back") + .disableAutocorrection(true) + .autocapitalization(.none) + .onReceive(Just(filterText)) { _ in + onUpdateFilterText( + $filterTextValue.wrappedValue + ) + } + } } } + + ToolbarItem(placement: .topBarTrailing) { + Button { + onToggleFilter(!filtering) + } label: { + Image( + systemName: "line.3.horizontal.decrease.circle" + ) + } + } } } } @@ -113,12 +192,16 @@ struct BrowseServerView: View { BrowseServerView( comicBookList: COMIC_BOOK_LIST, path: "/reader/v1/publishers", + filtering: false, + filterText: "", title: "", parentPath: "/reader/v1", directoryContents: DIRECTORY_LIST.filter { $0.isDirectory }, downloadingState: [], loading: false, onLoadDirectory: { _, _ in }, + onToggleFilter: { _ in }, + onUpdateFilterText: { _ in }, onDownloadFile: { _, _ in } ) } @@ -127,12 +210,16 @@ struct BrowseServerView: View { BrowseServerView( comicBookList: COMIC_BOOK_LIST, path: "/reader/v1", + filtering: false, + filterText: "", title: "", parentPath: "", directoryContents: DIRECTORY_LIST.filter { $0.isDirectory == false }, downloadingState: [], loading: false, onLoadDirectory: { _, _ in }, + onToggleFilter: { _ in }, + onUpdateFilterText: { _ in }, onDownloadFile: { _, _ in } ) } @@ -141,12 +228,34 @@ struct BrowseServerView: View { BrowseServerView( comicBookList: COMIC_BOOK_LIST, path: "/reader/v1", + filtering: false, + filterText: "", + title: "", + parentPath: "", + directoryContents: DIRECTORY_LIST.filter { $0.isDirectory }, + downloadingState: [], + loading: true, + onLoadDirectory: { _, _ in }, + onToggleFilter: { _ in }, + onUpdateFilterText: { _ in }, + onDownloadFile: { _, _ in } + ) +} + +#Preview("filtering") { + BrowseServerView( + comicBookList: COMIC_BOOK_LIST, + path: "/reader/v1", + filtering: true, + filterText: DIRECTORY_LIST.filter { $0.isDirectory }[0].title, title: "", parentPath: "", directoryContents: DIRECTORY_LIST.filter { $0.isDirectory }, downloadingState: [], loading: true, onLoadDirectory: { _, _ in }, + onToggleFilter: { _ in }, + onUpdateFilterText: { _ in }, onDownloadFile: { _, _ in } ) } diff --git a/iosVariant/iosVariant/Views/Server/ServerView.swift b/iosVariant/iosVariant/Views/Server/ServerView.swift index b4c3906..0810469 100644 --- a/iosVariant/iosVariant/Views/Server/ServerView.swift +++ b/iosVariant/iosVariant/Views/Server/ServerView.swift @@ -25,6 +25,8 @@ private let TAG = "ServerView" struct ServerView: View { let comicBookList: [ComicBook] let currentPath: String + let filtering: Bool + let filterText: String let title: String let parentPath: String let directoryContents: [DirectoryEntry] @@ -32,18 +34,24 @@ struct ServerView: View { let loading: Bool let onLoadDirectory: (String, Bool) -> Void + let onToggleFilter: (Bool) -> Void + let onUpdateFilterText: (String) -> Void let onDownloadFile: (String, String) -> Void var body: some View { BrowseServerView( comicBookList: comicBookList, path: currentPath, + filtering: filtering, + filterText: filterText, title: title, parentPath: parentPath, directoryContents: directoryContents, downloadingState: downloadingState, loading: loading, onLoadDirectory: onLoadDirectory, + onToggleFilter: onToggleFilter, + onUpdateFilterText: onUpdateFilterText, onDownloadFile: onDownloadFile ) } @@ -53,12 +61,16 @@ struct ServerView: View { ServerView( comicBookList: COMIC_BOOK_LIST, currentPath: "The current path", + filtering: false, + filterText: "", title: "The Title", parentPath: "The parent path", directoryContents: DIRECTORY_LIST, downloadingState: [], loading: false, onLoadDirectory: { _, _ in }, + onToggleFilter: { _ in }, + onUpdateFilterText: { _ in }, onDownloadFile: { _, _ in } ) } diff --git a/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/BrowsingState.kt b/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/BrowsingState.kt index 4706f60..bb665ce 100644 --- a/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/BrowsingState.kt +++ b/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/BrowsingState.kt @@ -24,6 +24,8 @@ import org.comixedproject.variant.model.state.DownloadingState data class BrowsingState( val currentPath: String, val parentPath: String, + val filtering: Boolean, + val filterText: String, val title: String, val contents: List, val downloadingState: List, diff --git a/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/VariantViewModel.kt b/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/VariantViewModel.kt index 5916db3..7b877bf 100644 --- a/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/VariantViewModel.kt +++ b/shared/src/commonMain/kotlin/org/comixedproject/variant/viewmodel/VariantViewModel.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import org.comixedproject.variant.adaptor.ArchiveAPI import org.comixedproject.variant.database.repository.DirectoryRepository @@ -129,6 +130,8 @@ open class VariantViewModel(val settings: Settings, val directoryRepository: Dir BrowsingState( settings.getString(LAST_DIRECTORY_SETTING, READER_ROOT), "", + false, + "", "", emptyList(), emptyList(), @@ -193,6 +196,8 @@ open class VariantViewModel(val settings: Settings, val directoryRepository: Dir BrowsingState( path, directory?.parent ?: "", + _browsingState.value.filtering, + _browsingState.value.filterText, directory?.title ?: "", contents, _browsingState.value.downloadingState, @@ -202,6 +207,38 @@ open class VariantViewModel(val settings: Settings, val directoryRepository: Dir } } + fun toggleFiltering(toggle: Boolean) { + viewModelScope.launch(Dispatchers.Main) { + _browsingState.tryEmit( + BrowsingState( + _browsingState.value.currentPath, + _browsingState.value.parentPath, + toggle, + "", + _browsingState.value.title, + _browsingState.value.contents, + _browsingState.value.downloadingState, + ) + ) + } + } + + fun updateFilterText(text: String) { + viewModelScope.launch(Dispatchers.Main) { + _browsingState.tryEmit( + BrowsingState( + _browsingState.value.currentPath, + _browsingState.value.parentPath, + _browsingState.value.filtering, + text, + _browsingState.value.title, + _browsingState.value.contents, + _browsingState.value.downloadingState, + ) + ) + } + } + fun downloadFile(path: String, filename: String) { Log.debug(TAG, "Starting download: path=${path}") val address = this.address @@ -354,6 +391,8 @@ open class VariantViewModel(val settings: Settings, val directoryRepository: Dir BrowsingState( browsingState.currentPath, browsingState.parentPath, + browsingState.filtering, + browsingState.filterText, browsingState.title, browsingState.contents, state,