diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index d6ae80c65..be4582d86 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -7,11 +7,11 @@
-
+
-
+
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/ProjectConfig.kt b/buildSrc/src/main/kotlin/ProjectConfig.kt
index ea1345004..c9e7c67c8 100644
--- a/buildSrc/src/main/kotlin/ProjectConfig.kt
+++ b/buildSrc/src/main/kotlin/ProjectConfig.kt
@@ -2,7 +2,7 @@ object ProjectConfig {
const val minSdk = 23
const val targetSdk = 31
const val compileSdk = 31
- const val versionName = "0.1.15"
- const val versionCode = 16
+ const val versionName = "0.1.16"
+ const val versionCode = 17
const val applicationId = "ir.kazemcodes.infinityreader"
}
\ No newline at end of file
diff --git a/data/src/main/java/org/ireader/data/local/dao/LibraryBookDao.kt b/data/src/main/java/org/ireader/data/local/dao/LibraryBookDao.kt
index b79d0af9f..6ec509250 100644
--- a/data/src/main/java/org/ireader/data/local/dao/LibraryBookDao.kt
+++ b/data/src/main/java/org/ireader/data/local/dao/LibraryBookDao.kt
@@ -165,7 +165,10 @@ interface LibraryBookDao {
@Query("SELECT sourceId FROM library GROUP BY sourceId ORDER BY COUNT(sourceId) DESC")
suspend fun findFavoriteSourceIds(): List
- @Query("DELETE FROM library WHERE favorite = 0")
+ @Query("""
+ DELETE FROM library
+ WHERE favorite = 0
+ """)
suspend fun deleteExploredBooks()
@Query("UPDATE library SET tableId = 0 WHERE tableId != 0 AND favorite = 1")
diff --git a/domain/src/main/java/org/ireader/domain/feature_services/downloaderService/DownloaderService.kt b/domain/src/main/java/org/ireader/domain/feature_services/downloaderService/DownloaderService.kt
index 6e36dc9f2..767a11a60 100644
--- a/domain/src/main/java/org/ireader/domain/feature_services/downloaderService/DownloaderService.kt
+++ b/domain/src/main/java/org/ireader/domain/feature_services/downloaderService/DownloaderService.kt
@@ -21,6 +21,7 @@ import org.ireader.domain.feature_services.notification.Notifications.CHANNEL_DO
import org.ireader.domain.feature_services.notification.Notifications.ID_DOWNLOAD_CHAPTER_COMPLETE
import org.ireader.domain.feature_services.notification.Notifications.ID_DOWNLOAD_CHAPTER_ERROR
import org.ireader.domain.feature_services.notification.Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS
+import org.ireader.domain.models.entities.Chapter
import org.ireader.domain.models.entities.SavedDownload
import org.ireader.domain.repository.LocalBookRepository
import org.ireader.domain.repository.LocalChapterRepository
@@ -46,6 +47,8 @@ class DownloadService @AssistedInject constructor(
const val DOWNLOADER_SERVICE_NAME = "DOWNLOAD_SERVICE"
const val DOWNLOADER_BOOK_ID = "book_id"
const val DOWNLOADER_SOURCE_ID = "sourceId"
+ const val DOWNLOADER_Chapters_IDS = "chapterIds"
+ const val DOWNLOADER_BOOKS_IDS = "booksIds"
}
@@ -55,6 +58,8 @@ class DownloadService @AssistedInject constructor(
val bookId = inputData.getLong("book_id", 0)
val sourceId = inputData.getLong("sourceId", 0)
+ val downloadIds = inputData.getLongArray(DOWNLOADER_Chapters_IDS)?.distinct()
+ val booksIds = inputData.getLongArray(DOWNLOADER_BOOKS_IDS)?.distinct()
val bookResource = bookRepo.subscribeBookById(bookId).first()
?: throw IllegalArgumentException(
"Invalid bookId as argument: $bookId"
@@ -75,7 +80,22 @@ class DownloadService @AssistedInject constructor(
val source = extensions.get(sourceId)?.source
- val chapters = chapterRepo.subscribeChaptersByBookId(bookId).first()
+ val chapters = mutableListOf()
+
+ if (booksIds?.isNotEmpty() == true) {
+ booksIds.forEach {
+ chapters.addAll(chapterRepo.findChaptersByBookId(it))
+ }
+ } else {
+ chapters.addAll(chapterRepo.findChaptersByBookId(bookId).filter {
+ if (downloadIds != null) {
+ it.id in downloadIds
+ } else {
+ true
+ }
+ })
+ }
+
val cancelDownloadIntent = WorkManager.getInstance(applicationContext)
.createCancelPendingIntent(id)
@@ -140,6 +160,7 @@ class DownloadService @AssistedInject constructor(
delay(1000)
}
}
+
} catch (e: Exception) {
Timber.e("getNotifications: Failed to download ${bookResource.title}")
notify(
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/ChapterDetailScreen.kt b/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/ChapterDetailScreen.kt
index 6afdf4ff2..de53e425b 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/ChapterDetailScreen.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/ChapterDetailScreen.kt
@@ -6,6 +6,7 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
@@ -13,10 +14,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.BookmarkBorder
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.Done
-import androidx.compose.material.icons.filled.GetApp
+import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -25,6 +23,7 @@ import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -53,6 +52,7 @@ fun ChapterDetailScreen(
navController: NavController = rememberNavController(),
) {
val book = vm.book
+ val context = LocalContext.current
val scrollState = rememberLazyListState()
val focusManager = LocalFocusManager.current
// LaunchedEffect(key1 = true) {
@@ -179,26 +179,55 @@ fun ChapterDetailScreen(
}
when {
vm.hasSelection -> {
- Row(modifier = Modifier
- .fillMaxWidth()
- .height(60.dp)
- .align(Alignment.BottomCenter)
- .padding(8.dp)
- .background(MaterialTheme.colors.background)
- .border(width = 1.dp, color = MaterialTheme.colors.onBackground.copy(.4f)),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(80.dp)
+ .align(Alignment.BottomCenter)
+ .padding(8.dp)
+ .background(MaterialTheme.colors.background)
+ .border(width = 1.dp,
+ color = MaterialTheme.colors.onBackground.copy(.1f))
+ .clickable(enabled = false) {},
) {
- AppIconButton(imageVector = Icons.Default.GetApp,
- title = "Download",
- onClick = { /*TODO*/ })
- AppIconButton(imageVector = Icons.Default.BookmarkBorder,
- title = "Bookmark",
- onClick = { /*TODO*/ })
- AppIconButton(imageVector = Icons.Default.Done,
- title = "Mark as read",
- onClick = { /*TODO*/ })
+ Row(modifier = Modifier
+ .fillMaxSize(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ AppIconButton(imageVector = Icons.Default.GetApp,
+ title = "Download",
+ onClick = {
+ vm.downloadChapters(context = context)
+ vm.selection.clear()
+ })
+ AppIconButton(imageVector = Icons.Default.BookmarkBorder,
+ title = "Bookmark",
+ onClick = {
+ vm.insertChapters(vm.chapters.filter { it.id in vm.selection }
+ .map { it.copy(bookmark = !it.bookmark) })
+ vm.selection.clear()
+ })
+
+ AppIconButton(imageVector = if (vm.chapters.filter { it.read }
+ .map { it.id }
+ .containsAll(vm.selection)) Icons.Default.DoneOutline else Icons.Default.Done,
+ title = "Mark as read",
+ onClick = {
+ vm.insertChapters(vm.chapters.filter { it.id in vm.selection }
+ .map { it.copy(read = !it.read) })
+ vm.selection.clear()
+ })
+ AppIconButton(imageVector = Icons.Default.PlaylistAddCheck,
+ title = "Mark Previous as read",
+ onClick = {
+ vm.insertChapters(vm.chapters.filter { it.id <= vm.selection.maxOrNull() ?: 0 }
+ .map { it.copy(read = true) })
+ vm.selection.clear()
+ })
+ }
}
+
}
}
}
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/viewmodel/ChapterDetailViewModel.kt b/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/viewmodel/ChapterDetailViewModel.kt
index fc4dcbd4c..9508375e5 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/viewmodel/ChapterDetailViewModel.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_detail/presentation/chapter_detail/viewmodel/ChapterDetailViewModel.kt
@@ -1,8 +1,10 @@
package org.ireader.presentation.feature_detail.presentation.chapter_detail.viewmodel
+import android.content.Context
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import androidx.work.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -13,7 +15,10 @@ import kotlinx.coroutines.launch
import org.ireader.core.R
import org.ireader.core.utils.UiEvent
import org.ireader.core.utils.UiText
+import org.ireader.domain.feature_services.downloaderService.DownloadService
+import org.ireader.domain.feature_services.downloaderService.DownloadService.Companion.DOWNLOADER_Chapters_IDS
import org.ireader.domain.models.entities.Book
+import org.ireader.domain.models.entities.Chapter
import org.ireader.domain.ui.NavigationArgs
import org.ireader.domain.use_cases.local.DeleteUseCase
import org.ireader.domain.use_cases.local.LocalGetChapterUseCase
@@ -127,6 +132,40 @@ class ChapterDetailViewModel @Inject constructor(
}
}
+ fun insertChapters(chapters: List) {
+ viewModelScope.launch(Dispatchers.IO) {
+ insertUseCases.insertChapters(chapters)
+ }
+ }
+
+ lateinit var work: OneTimeWorkRequest
+ fun downloadChapters(context: Context) {
+
+ book?.let { book ->
+ work =
+ OneTimeWorkRequestBuilder().apply {
+ setInputData(
+ Data.Builder().apply {
+ putLong(DownloadService.DOWNLOADER_BOOK_ID,
+ book.id)
+ putLong(DownloadService.DOWNLOADER_SOURCE_ID,
+ book.sourceId)
+ putLongArray(DOWNLOADER_Chapters_IDS,
+ this@ChapterDetailViewModel.selection.toLongArray())
+ }.build()
+ )
+ addTag(DownloadService.DOWNLOADER_SERVICE_NAME)
+ }.build()
+ WorkManager.getInstance(context).enqueueUniqueWork(
+ DownloadService.DOWNLOADER_SERVICE_NAME.plus(
+ book.id + book.sourceId),
+ ExistingWorkPolicy.REPLACE,
+ work
+ )
+ }
+
+ }
+
suspend fun showSnackBar(message: UiText?) {
_eventFlow.emit(
UiEvent.ShowSnackbar(
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_explore/presentation/browse/ExploreScreen.kt b/presentation/src/main/java/org/ireader/presentation/feature_explore/presentation/browse/ExploreScreen.kt
index 15e079e9c..519cf066f 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_explore/presentation/browse/ExploreScreen.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_explore/presentation/browse/ExploreScreen.kt
@@ -192,7 +192,7 @@ fun ExploreScreen(
navController = navController,
isLocal = false,
gridState = gridState,
- onBookTap = { book ->
+ onClick = { book ->
navController.navigate(
route = BookDetailScreenSpec.buildRoute(sourceId = book.sourceId,
bookId = book.id)
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreen.kt b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreen.kt
index c8c2516dc..fd882c013 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreen.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreen.kt
@@ -3,14 +3,23 @@ package org.ireader.presentation.feature_library.presentation
import androidx.compose.animation.Crossfade
import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.material.icons.filled.DoneOutline
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
@@ -24,6 +33,7 @@ import org.ireader.core_ui.ui.LoadingScreen
import org.ireader.presentation.feature_library.presentation.components.BottomTabComposable
import org.ireader.presentation.feature_library.presentation.components.LayoutComposable
import org.ireader.presentation.feature_library.presentation.viewmodel.LibraryViewModel
+import org.ireader.presentation.presentation.reusable_composable.AppIconButton
import org.ireader.presentation.ui.BookDetailScreenSpec
import org.ireader.presentation.ui.ReaderScreenSpec
@@ -48,6 +58,7 @@ fun LibraryScreen(
val gridState = rememberLazyGridState()
val lazyListState = rememberLazyListState()
+ val context = LocalContext.current
LaunchedEffect(key1 = true) {
vm.getLibraryBooks()
@@ -82,7 +93,7 @@ fun LibraryScreen(
Crossfade(targetState = Pair(vm.isLoading, vm.isEmpty)) { (isLoading, isEmpty) ->
when {
isLoading -> LoadingScreen()
- isEmpty -> EmptyScreen(UiText.DynamicString("There is no book is Library, you can add books in the Explore screen."))
+ isEmpty && vm.filters.isEmpty() -> EmptyScreen(UiText.DynamicString("There is no book is Library, you can add books in the Explore screen."))
else -> LayoutComposable(
books = vm.books,
layout = vm.layout,
@@ -90,6 +101,7 @@ fun LibraryScreen(
isLocal = true,
gridState = gridState,
scrollState = lazyListState,
+ selection = vm.selection,
goToLatestChapter = { book ->
navController.navigate(
ReaderScreenSpec.buildRoute(
@@ -99,17 +111,70 @@ fun LibraryScreen(
)
)
},
- onBookTap = { book ->
- navController.navigate(
- route = BookDetailScreenSpec.buildRoute(
- sourceId = book.sourceId,
- bookId = book.id)
- )
+ onClick = { book ->
+ if (vm.hasSelection) {
+ if (book.id in vm.selection) {
+ vm.selection.remove(book.id)
+ } else {
+ vm.selection.add(book.id)
+ }
+
+ } else {
+ navController.navigate(
+ route = BookDetailScreenSpec.buildRoute(
+ sourceId = book.sourceId,
+ bookId = book.id)
+ )
+ }
+
+ },
+ onLongClick = {
+ vm.selection.add(it.id)
},
histories = vm.histories
)
}
}
+ when {
+ vm.hasSelection -> {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(80.dp)
+ .align(Alignment.BottomCenter)
+ .padding(8.dp)
+ .background(MaterialTheme.colors.background)
+ .border(width = 1.dp,
+ color = MaterialTheme.colors.onBackground.copy(.1f))
+ .clickable(enabled = false) {},
+ ) {
+ Row(modifier = Modifier
+ .fillMaxSize(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ AppIconButton(imageVector = Icons.Default.Done,
+ title = "Mark as read",
+ onClick = {
+ vm.markAsRead()
+ // vm.selection.clear()
+ })
+ AppIconButton(imageVector = Icons.Default.DoneOutline,
+ title = "Mark as Not read",
+ onClick = {
+ vm.markAsNotRead()
+ })
+ AppIconButton(imageVector = Icons.Default.Delete,
+ title = "Mark Previous as read",
+ onClick = {
+ vm.deleteBooks()
+ })
+ }
+ }
+
+ }
+ }
+
}
}
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreenTopBar.kt b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreenTopBar.kt
index e57108676..f3186c2bf 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreenTopBar.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/LibraryScreenTopBar.kt
@@ -62,13 +62,7 @@ fun LibraryScreenTopBar(
},
)
}
- AppIconButton(
- imageVector = Icons.Default.Refresh,
- title = "Refresh",
- onClick = {
- vm.refreshUpdate(context = context)
- },
- )
+
AppIconButton(
imageVector = Icons.Default.Sort,
title = "Filter",
@@ -89,7 +83,13 @@ fun LibraryScreenTopBar(
vm.onEvent(LibraryEvents.ToggleSearchMode(true))
},
)
-
+ AppIconButton(
+ imageVector = Icons.Default.Refresh,
+ title = "Refresh",
+ onClick = {
+ vm.refreshUpdate(context = context)
+ },
+ )
},
navigationIcon = if (vm.inSearchMode) {
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/DisplayScreen.kt b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/DisplayScreen.kt
index a3f650cf1..11b6eb8ad 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/DisplayScreen.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/DisplayScreen.kt
@@ -2,13 +2,16 @@ package org.ireader.presentation.feature_library.presentation.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
import org.ireader.domain.models.layouts
import org.ireader.presentation.feature_library.presentation.viewmodel.LibraryEvents
import org.ireader.presentation.feature_library.presentation.viewmodel.LibraryViewModel
+import org.ireader.presentation.feature_sources.presentation.extension.composables.TextSection
@Composable
@@ -18,6 +21,11 @@ fun DisplayScreen(viewModel: LibraryViewModel) {
.fillMaxSize()
.background(MaterialTheme.colors.background)
) {
+ TextSection(
+ text = "DISPLAY MODE",
+ padding = PaddingValues(vertical = 8.dp, horizontal = 16.dp),
+ style = MaterialTheme.typography.subtitle1,
+ )
layouts.forEach { layout ->
RadioButtonWithTitleComposable(
text = layout.title,
@@ -27,5 +35,10 @@ fun DisplayScreen(viewModel: LibraryViewModel) {
}
)
}
+// Spacer(modifier = Modifier.height(8.dp))
+// TextSection(text = "BADGES")
+// Spacer(modifier = Modifier.height(8.dp))
+// TextSection(text = "TABS")
+// Spacer(modifier = Modifier.height(8.dp))
}
}
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/LayoutComposables.kt b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/LayoutComposables.kt
index 7866efa9e..5a00a1e9c 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/LayoutComposables.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/LayoutComposables.kt
@@ -21,7 +21,9 @@ fun LayoutComposable(
books: List = emptyList(),
lazyBook: LazyPagingItems? = null,
histories: List = emptyList(),
- onBookTap: (book: Book) -> Unit,
+ onClick: (book: Book) -> Unit,
+ onLongClick: (Book) -> Unit = {},
+ selection: List = emptyList(),
layout: LayoutType,
scrollState: LazyListState,
gridState: androidx.compose.foundation.lazy.grid.LazyGridState,
@@ -37,16 +39,21 @@ fun LayoutComposable(
books = books,
lazyBooks = lazyBook,
onClick = { book ->
- onBookTap(book)
- }, scrollState = gridState,
+ onClick(book)
+ },
+ selection = selection,
+ onLongClick = { onLongClick(it) },
+ scrollState = gridState,
isLocal = isLocal,
goToLatestChapter = { goToLatestChapter(it) }, histories = histories)
}
is LayoutType.ListLayout -> {
LinearListDisplay(books = books, onClick = { book ->
- onBookTap(book)
+ onClick(book)
}, scrollState = scrollState,
isLocal = isLocal,
+ selection = selection,
+ onLongClick = { onLongClick(it) },
lazyBooks = lazyBook,
goToLatestChapter = { goToLatestChapter(it) })
}
@@ -54,9 +61,11 @@ fun LayoutComposable(
CompactGridLayoutComposable(
books = books,
onClick = { book ->
- onBookTap(book)
+ onClick(book)
}, scrollState = gridState,
isLocal = isLocal,
+ selection = selection,
+ onLongClick = { onLongClick(it) },
lazyBooks = lazyBook,
goToLatestChapter = { goToLatestChapter(it) }, histories = histories)
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/TwoTextinRow.kt b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/TwoTextinRow.kt
index 6389ff49c..faca8493b 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/TwoTextinRow.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/components/TwoTextinRow.kt
@@ -17,7 +17,7 @@ fun RadioButtonWithTitleComposable(
) {
Row(modifier = modifier
.fillMaxWidth()
- .padding(vertical = 8.dp),
+ .padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start) {
RadioButton(selected = selected, onClick = { onClick() })
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryScreenState.kt b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryScreenState.kt
index 5cea52496..bc63a8762 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryScreenState.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryScreenState.kt
@@ -27,6 +27,8 @@ interface LibraryState {
var filters: SnapshotStateList
var currentScrollState: Int
var histories: List
+ var selection: SnapshotStateList
+ val hasSelection: Boolean
}
open class LibraryStateImpl @Inject constructor() : LibraryState {
@@ -43,6 +45,8 @@ open class LibraryStateImpl @Inject constructor() : LibraryState {
override var filters: SnapshotStateList = mutableStateListOf()
override var currentScrollState by mutableStateOf(0)
override var histories by mutableStateOf>(emptyList())
+ override var selection: SnapshotStateList = mutableStateListOf()
+ override val hasSelection: Boolean by derivedStateOf { selection.isNotEmpty() }
}
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryViewModel.kt b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryViewModel.kt
index b7f31288c..957950d85 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryViewModel.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_library/presentation/viewmodel/LibraryViewModel.kt
@@ -8,6 +8,7 @@ import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@@ -16,8 +17,12 @@ import org.ireader.domain.feature_services.LibraryUpdatesService
import org.ireader.domain.models.DisplayMode
import org.ireader.domain.models.FilterType
import org.ireader.domain.models.SortType
+import org.ireader.domain.models.entities.Book
import org.ireader.domain.use_cases.history.HistoryUseCase
+import org.ireader.domain.use_cases.local.DeleteUseCase
import org.ireader.domain.use_cases.local.LocalGetBookUseCases
+import org.ireader.domain.use_cases.local.LocalGetChapterUseCase
+import org.ireader.domain.use_cases.local.LocalInsertUseCases
import org.ireader.domain.use_cases.preferences.reader_preferences.LibraryLayoutTypeUseCase
import org.ireader.domain.use_cases.preferences.reader_preferences.SortersUseCase
import javax.inject.Inject
@@ -26,6 +31,9 @@ import javax.inject.Inject
@HiltViewModel
class LibraryViewModel @Inject constructor(
private val localGetBookUseCases: LocalGetBookUseCases,
+ private val insertUseCases: LocalInsertUseCases,
+ private val deleteUseCase: DeleteUseCase,
+ private val localGetChapterUseCase: LocalGetChapterUseCase,
private val libraryLayoutUseCase: LibraryLayoutTypeUseCase,
private val sortersUseCase: SortersUseCase,
private val historyUseCase: HistoryUseCase,
@@ -71,6 +79,10 @@ class LibraryViewModel @Inject constructor(
}
+ fun addBooksToSelection(book: Book) {
+ selection.add(book.id)
+ }
+
private var getBooksJob: Job? = null
fun getLibraryBooks() {
@@ -101,6 +113,63 @@ class LibraryViewModel @Inject constructor(
this.layout = layoutType.layout
}
+// lateinit var downloadWork: OneTimeWorkRequest
+// fun downloadChapters(book: List,context: Context) {
+// downloadWork =
+// OneTimeWorkRequestBuilder().apply {
+// setInputData(
+// Data.Builder().apply {
+// putLong(DownloadService.DOWNLOADER_BOOK_ID,
+// book.id)
+// putLong(DownloadService.DOWNLOADER_SOURCE_ID,
+// book.sourceId)
+// }.build()
+// )
+// addTag(DownloadService.DOWNLOADER_SERVICE_NAME)
+// }.build()
+// WorkManager.getInstance(context).enqueueUniqueWork(
+// DownloadService.DOWNLOADER_SERVICE_NAME.plus(
+// book.id + book.sourceId),
+// ExistingWorkPolicy.REPLACE,
+// downloadWork
+// )
+//
+//
+// }
+
+ fun markAsRead() {
+ viewModelScope.launch(Dispatchers.IO) {
+ selection.forEach { bookId ->
+ val chapters = localGetChapterUseCase.findChaptersByBookId(bookId)
+ insertUseCases.insertChapters(chapters.map { it.copy(read = true) })
+
+ }
+ selection.clear()
+ }
+
+ }
+
+ fun markAsNotRead() {
+ viewModelScope.launch(Dispatchers.IO) {
+ selection.forEach { bookId ->
+ val chapters = localGetChapterUseCase.findChaptersByBookId(bookId)
+ insertUseCases.insertChapters(chapters.map { it.copy(read = false) })
+ }
+ selection.clear()
+ }
+
+ }
+
+ fun deleteBooks() {
+ viewModelScope.launch(Dispatchers.IO) {
+ selection.forEach { bookId ->
+ deleteUseCase.deleteBookById(bookId)
+ }
+ selection.clear()
+ }
+
+
+ }
private fun readLayoutTypeAndFilterTypeAndSortType() {
val sortType = sortersUseCase.read()
diff --git a/presentation/src/main/java/org/ireader/presentation/feature_sources/presentation/extension/composables/TextSection.kt b/presentation/src/main/java/org/ireader/presentation/feature_sources/presentation/extension/composables/TextSection.kt
index 4a9db8af2..da5767a49 100644
--- a/presentation/src/main/java/org/ireader/presentation/feature_sources/presentation/extension/composables/TextSection.kt
+++ b/presentation/src/main/java/org/ireader/presentation/feature_sources/presentation/extension/composables/TextSection.kt
@@ -1,5 +1,6 @@
package org.ireader.presentation.feature_sources.presentation.extension.composables
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@@ -10,22 +11,25 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
@Composable
fun TextSection(
text: String,
toUpper: Boolean = true,
+ padding: PaddingValues = PaddingValues(16.dp),
+ style: TextStyle = MaterialTheme.typography.subtitle2,
) {
Row(
modifier = Modifier
.fillMaxWidth()
- .padding(16.dp),
+ .padding(padding),
verticalAlignment = Alignment.CenterVertically
) {
Text(
if (toUpper) text.uppercase() else text,
- style = MaterialTheme.typography.subtitle2,
+ style = style,
color = LocalContentColor.current.copy(alpha = ContentAlpha.medium)
)
}
diff --git a/presentation/src/main/java/org/ireader/presentation/presentation/components/ChapterItemListComposable.kt b/presentation/src/main/java/org/ireader/presentation/presentation/components/ChapterItemListComposable.kt
index 965c6b97f..12c012b93 100644
--- a/presentation/src/main/java/org/ireader/presentation/presentation/components/ChapterItemListComposable.kt
+++ b/presentation/src/main/java/org/ireader/presentation/presentation/components/ChapterItemListComposable.kt
@@ -3,6 +3,7 @@ package org.ireader.presentation.presentation.components
import androidx.compose.foundation.combinedClickable
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.material.icons.filled.PublishedWithChanges
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -30,6 +31,15 @@ fun ChapterListItemComposable(
onLongClick = { onLongClick() }
)
.selectedBackground(isSelected),
+ icon = if (chapter.bookmark) {
+ {
+ Icon(
+ imageVector = Icons.Default.Bookmark,
+ contentDescription = "Bookmarked",
+ tint = MaterialTheme.colors.primary,
+ )
+ }
+ } else null,
text = {
Text(
text = chapter.title,
diff --git a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/BookImage.kt b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/BookImage.kt
index 4e11f3477..e59f446e2 100644
--- a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/BookImage.kt
+++ b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/BookImage.kt
@@ -1,8 +1,9 @@
package org.ireader.presentation.presentation.layouts
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
@@ -13,7 +14,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -22,27 +22,35 @@ import org.ireader.domain.feature_services.io.BookCover
import org.ireader.domain.models.entities.Book
import org.ireader.presentation.presentation.components.BookImageComposable
+@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BookImage(
modifier: Modifier = Modifier,
- onClick: (Book) -> Unit,
+ onClick: (Book) -> Unit = {},
+ onLongClick: (Book) -> Unit = {},
book: Book,
ratio: Float = 3f / 4f,
+ selected: Boolean = false,
badge: @Composable BoxScope.() -> Unit,
) {
Box(
modifier = modifier
.fillMaxSize()
.padding(8.dp)
- .clickable(role = Role.Button) { onClick(book) },
+ .combinedClickable(
+ onClick = { onClick(book) },
+ onLongClick = { onLongClick(book) }
+ )
+ .border(3.dp,
+ if (selected) MaterialTheme.colors.primary else MaterialTheme.colors.onBackground.copy(
+ alpha = .1f)),
) {
BookImageComposable(
modifier = Modifier
.aspectRatio(ratio)
.fillMaxWidth()
.clip(RoundedCornerShape(4.dp))
- .border(2.dp,
- MaterialTheme.colors.onBackground.copy(alpha = .1f))
+
.align(Alignment.Center),
image = BookCover.from(book),
)
diff --git a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/CompactGrid.kt b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/CompactGrid.kt
index adf0fcac9..8603c1477 100644
--- a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/CompactGrid.kt
+++ b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/CompactGrid.kt
@@ -23,7 +23,9 @@ fun CompactGridLayoutComposable(
lazyBooks: LazyPagingItems?,
books: List,
histories: List,
+ selection: List = emptyList(),
onClick: (book: Book) -> Unit,
+ onLongClick: (book: Book) -> Unit = {},
scrollState: LazyGridState = rememberLazyGridState(),
isLocal: Boolean,
goToLatestChapter: (book: Book) -> Unit,
@@ -38,7 +40,9 @@ fun CompactGridLayoutComposable(
items(lazyBooks) { book ->
if (book != null) {
BookImage(
- onClick = { onClick(book) }, book = book, ratio = 6f / 9f
+ onClick = { onClick(book) }, book = book, ratio = 6f / 9f,
+ selected = book.id in selection,
+ onLongClick = { onLongClick(book) },
) {
if (isLocal && histories.find { it.bookId == book.id }?.readAt != 0L) {
GoToLastReadComposable(onClick = { goToLatestChapter(book) })
@@ -52,7 +56,9 @@ fun CompactGridLayoutComposable(
items(count = books.size) { index ->
BookImage(
- onClick = { onClick(books[index]) }, book = books[index], ratio = 6f / 9f
+ onClick = { onClick(books[index]) }, book = books[index], ratio = 6f / 9f,
+ selected = books[index].id in selection,
+ onLongClick = { onLongClick(books[index]) },
) {
if (isLocal && histories.find { it.bookId == books[index].id }?.readAt != 0L) {
GoToLastReadComposable(onClick = { goToLatestChapter(books[index]) })
diff --git a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/GridLayoutComposable.kt b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/GridLayoutComposable.kt
index 50bbb5433..abf4418bb 100644
--- a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/GridLayoutComposable.kt
+++ b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/GridLayoutComposable.kt
@@ -19,7 +19,9 @@ fun GridLayoutComposable(
lazyBooks: LazyPagingItems?,
books: List,
histories: List,
+ selection: List = emptyList(),
onClick: (book: Book) -> Unit,
+ onLongClick: (book: Book) -> Unit = {},
scrollState: androidx.compose.foundation.lazy.grid.LazyGridState,
isLocal: Boolean,
goToLatestChapter: (book: Book) -> Unit,
@@ -33,7 +35,10 @@ fun GridLayoutComposable(
items(lazyBooks) { book ->
if (book != null) {
BookImage(
- onClick = { onClick(book) }, book = book, ratio = 6f / 10f
+ onClick = { onClick(book) },
+ onLongClick = { onLongClick(book) },
+ book = book, ratio = 6f / 10f,
+ selected = book.id in selection
) {
if (book.lastUpdated > 1 && isLocal && histories.find { it.bookId == book.id }?.readAt != 0L) {
GoToLastReadComposable(onClick = { goToLatestChapter(book) })
@@ -44,7 +49,9 @@ fun GridLayoutComposable(
} else {
items(count = books.size) { index ->
BookImage(
- onClick = { onClick(books[index]) }, book = books[index], ratio = 6f / 10f
+ onClick = { onClick(books[index]) }, book = books[index], ratio = 6f / 10f,
+ selected = books[index].id in selection,
+ onLongClick = { onLongClick(books[index]) },
) {
if (books[index].lastUpdated > 1 && isLocal && histories.find { it.bookId == books[index].id }?.readAt != 0L) {
GoToLastReadComposable(onClick = { goToLatestChapter(books[index]) })
diff --git a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/LineatListView.kt b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/LineatListView.kt
index 0d767928f..c2bc80aaa 100644
--- a/presentation/src/main/java/org/ireader/presentation/presentation/layouts/LineatListView.kt
+++ b/presentation/src/main/java/org/ireader/presentation/presentation/layouts/LineatListView.kt
@@ -1,7 +1,8 @@
package org.ireader.presentation.presentation.layouts
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
@@ -26,6 +27,7 @@ import org.ireader.presentation.presentation.components.BookImageComposable
fun LinearBookItem(
modifier: Modifier = Modifier,
title: String,
+ selected: Boolean = false,
book: Book,
) {
@@ -42,7 +44,9 @@ fun LinearBookItem(
modifier = modifier
.aspectRatio(3f / 4f)
.clip(RoundedCornerShape(4.dp))
- .border(.2.dp, MaterialTheme.colors.onBackground.copy(alpha = .1f)))
+ .border(.2.dp,
+ if (selected) MaterialTheme.colors.primary.copy(alpha = .5f) else MaterialTheme.colors.onBackground.copy(
+ alpha = .1f)))
Spacer(modifier = Modifier.width(15.dp))
Text(
text = title,
@@ -55,11 +59,14 @@ fun LinearBookItem(
}
+@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LinearListDisplay(
lazyBooks: LazyPagingItems?,
books: List,
+ selection: List = emptyList(),
onClick: (book: Book) -> Unit,
+ onLongClick: (book: Book) -> Unit = {},
scrollState: LazyListState = rememberLazyListState(),
isLocal: Boolean,
goToLatestChapter: (book: Book) -> Unit,
@@ -71,9 +78,11 @@ fun LinearListDisplay(
LinearBookItem(
title = book.title,
book = book,
- modifier = Modifier.clickable {
- onClick(book)
- }
+ modifier = Modifier.combinedClickable(
+ onClick = { onClick(book) },
+ onLongClick = { onLongClick(book) },
+ ),
+ selected = book.id in selection
)
}
@@ -83,9 +92,11 @@ fun LinearListDisplay(
LinearBookItem(
title = books[index].title,
book = books[index],
- modifier = Modifier.clickable {
- onClick(books[index])
- }
+ modifier = Modifier.combinedClickable(
+ onClick = { onClick(books[index]) },
+ onLongClick = { onClick(books[index]) },
+ ),
+ selected = books[index].id in selection
)
}
}
diff --git a/presentation/src/main/java/org/ireader/presentation/presentation/reusable_composable/TopAppBarReusableComposables.kt b/presentation/src/main/java/org/ireader/presentation/presentation/reusable_composable/TopAppBarReusableComposables.kt
index f57d0514f..8c95aaf54 100644
--- a/presentation/src/main/java/org/ireader/presentation/presentation/reusable_composable/TopAppBarReusableComposables.kt
+++ b/presentation/src/main/java/org/ireader/presentation/presentation/reusable_composable/TopAppBarReusableComposables.kt
@@ -1,5 +1,6 @@
package org.ireader.presentation.presentation.reusable_composable
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@@ -140,6 +141,7 @@ fun CaptionTextComposable(
)
}
+@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AppIconButton(
modifier: Modifier = Modifier,