🚀 Full reformat of presentation and domain layers

* Implemented new custom Navigation based on Voyager
* Bug fixes
* Scrollbars for almost all screens
* Better structure for screens
* Smoother animations
* Various small UI changes
This commit is contained in:
Acclorite 2024-12-22 11:21:26 +02:00
parent 85712cf566
commit 8ddb378f47
464 changed files with 12824 additions and 11768 deletions

View file

@ -24,7 +24,7 @@
android:roundIcon="@mipmap/app_icon_round"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Start.Splash"
android:theme="@style/Splash"
android:requestLegacyExternalStorage="true"
android:enableOnBackInvokedCallback="true"
android:usesCleartextTraffic="false"
@ -33,11 +33,11 @@
tools:ignore="DataExtractionRules">
<activity
android:name=".Activity"
android:name=".ui.main.MainActivity"
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|locale|density|uiMode"
android:exported="true"
android:theme="@style/Theme.Start.Splash">
android:theme="@style/Splash">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View file

@ -1,224 +0,0 @@
package ua.acclorite.book_story
import android.annotation.SuppressLint
import android.database.CursorWindow
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.ime
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.hilt.android.AndroidEntryPoint
import ua.acclorite.book_story.presentation.core.components.navigation_bar.NavigationBar
import ua.acclorite.book_story.presentation.core.components.navigation_rail.NavigationRail
import ua.acclorite.book_story.presentation.core.navigation.NavigationHost
import ua.acclorite.book_story.presentation.core.navigation.Screen
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainViewModel
import ua.acclorite.book_story.presentation.screens.about.AboutScreenRoot
import ua.acclorite.book_story.presentation.screens.about.nested.credits.CreditsScreenRoot
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.LicenseInfoScreenRoot
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.LicensesScreenRoot
import ua.acclorite.book_story.presentation.screens.book_info.BookInfoScreenRoot
import ua.acclorite.book_story.presentation.screens.browse.BrowseScreenRoot
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
import ua.acclorite.book_story.presentation.screens.help.HelpScreenRoot
import ua.acclorite.book_story.presentation.screens.history.HistoryScreenRoot
import ua.acclorite.book_story.presentation.screens.history.data.HistoryViewModel
import ua.acclorite.book_story.presentation.screens.library.LibraryScreenRoot
import ua.acclorite.book_story.presentation.screens.library.data.LibraryViewModel
import ua.acclorite.book_story.presentation.screens.reader.ReaderScreenRoot
import ua.acclorite.book_story.presentation.screens.settings.SettingsScreenRoot
import ua.acclorite.book_story.presentation.screens.settings.data.SettingsViewModel
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.AppearanceSettingsRoot
import ua.acclorite.book_story.presentation.screens.settings.nested.browse.BrowseSettingsRoot
import ua.acclorite.book_story.presentation.screens.settings.nested.general.GeneralSettingsRoot
import ua.acclorite.book_story.presentation.screens.settings.nested.reader.ReaderSettingsRoot
import ua.acclorite.book_story.presentation.screens.start.StartScreenRoot
import ua.acclorite.book_story.presentation.ui.BookStoryTheme
import ua.acclorite.book_story.presentation.ui.Transitions
import ua.acclorite.book_story.presentation.ui.isDark
import ua.acclorite.book_story.presentation.ui.isPureDark
import java.lang.reflect.Field
@Suppress("unused")
@SuppressLint("DiscouragedPrivateApi")
@AndroidEntryPoint
class Activity : AppCompatActivity() {
// Initializing all required viewModels on app startup
private val mainViewModel: MainViewModel by viewModels()
private val libraryViewModel: LibraryViewModel by viewModels()
private val historyViewModel: HistoryViewModel by viewModels()
private val browseViewModel: BrowseViewModel by viewModels()
private val settingsViewModel: SettingsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// New Cursor size for Room
try {
val field: Field = CursorWindow::class.java.getDeclaredField("sCursorWindowSize")
field.isAccessible = true
field.set(null, 100 * 1024 * 1024)
} catch (e: Exception) {
e.printStackTrace()
}
// Initializing all variables
mainViewModel.onEvent(
MainEvent.OnInit(
libraryViewModelReady = libraryViewModel.isReady,
settingsViewModelReady = settingsViewModel.isReady
)
)
// Splash screen
installSplashScreen().apply {
setKeepOnScreenCondition {
!mainViewModel.isReady.value
}
}
// Edge to edge
enableEdgeToEdge()
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val state = MainViewModel.getState()
val isLoaded = mainViewModel.isReady.collectAsStateWithLifecycle()
val density = LocalDensity.current
val imeInsets = WindowInsets.ime
val focusManager = LocalFocusManager.current
val isKeyboardVisible by remember {
derivedStateOf {
imeInsets.getBottom(density) > 0
}
}
LaunchedEffect(isKeyboardVisible) {
if (!isKeyboardVisible) {
focusManager.clearFocus()
}
}
if (isLoaded.value) {
BookStoryTheme(
theme = state.value.theme,
isDark = state.value.darkTheme.isDark(),
isPureDark = state.value.pureDark.isPureDark(this),
themeContrast = state.value.themeContrast
) {
NavigationHost(
startScreen = if (state.value.showStartScreen) Screen.Start
else Screen.Library
) {
navigation(
screens = arrayOf(
Screen.Library.getRoute(),
Screen.History.getRoute(),
Screen.Browse.getRoute()
),
bottomBar = {
NavigationBar()
},
navigationRail = {
NavigationRail()
}
) {
// Library
composable<Screen.Library>(
enterAnim = Transitions.FadeTransitionIn,
exitAnim = Transitions.FadeTransitionOut
) {
LibraryScreenRoot()
}
// History
composable<Screen.History>(
enterAnim = Transitions.FadeTransitionIn,
exitAnim = Transitions.FadeTransitionOut
) {
HistoryScreenRoot()
}
// Browse
composable<Screen.Browse>(
enterAnim = Transitions.FadeTransitionIn,
exitAnim = Transitions.FadeTransitionOut
) {
BrowseScreenRoot()
}
}
// Book Info
composable<Screen.BookInfo> {
BookInfoScreenRoot(it)
}
// Reader
composable<Screen.Reader> {
ReaderScreenRoot(it)
}
// Settings
composable<Screen.Settings> {
SettingsScreenRoot()
}
// Nested settings categories
composable<Screen.Settings.General> {
GeneralSettingsRoot()
}
composable<Screen.Settings.Appearance> {
AppearanceSettingsRoot()
}
composable<Screen.Settings.ReaderSettings> {
ReaderSettingsRoot()
}
composable<Screen.Settings.BrowseSettings> {
BrowseSettingsRoot()
}
// About screen
composable<Screen.About> {
AboutScreenRoot()
}
// Nested about categories
composable<Screen.About.Licenses> {
LicensesScreenRoot()
}
composable<Screen.About.LicenseInfo> {
LicenseInfoScreenRoot(it)
}
composable<Screen.About.Credits> {
CreditsScreenRoot()
}
// Help screen
composable<Screen.Help> {
HelpScreenRoot(it)
}
// Start screen
composable<Screen.Start>(enterAnim = Transitions.FadeTransitionIn) {
StartScreenRoot()
}
}
}
}
}
}
}

View file

@ -4,7 +4,7 @@ import androidx.room.TypeConverter
import com.google.common.reflect.TypeToken
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.reader.Chapter
class ChapterConverter {
@TypeConverter

View file

@ -5,8 +5,8 @@ import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import ua.acclorite.book_story.data.local.converter.ChapterConverter
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.library.category.Category
import ua.acclorite.book_story.domain.reader.Chapter
@Entity
@TypeConverters(ChapterConverter::class)

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.data.mapper.book
import ua.acclorite.book_story.data.local.dto.BookEntity
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.library.book.Book
interface BookMapper {
suspend fun toBookEntity(book: Book): BookEntity

View file

@ -3,8 +3,8 @@ package ua.acclorite.book_story.data.mapper.book
import android.net.Uri
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.local.dto.BookEntity
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.ui.UIText
import javax.inject.Inject
class BookMapperImpl @Inject constructor() : BookMapper {

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.data.mapper.color_preset
import ua.acclorite.book_story.data.local.dto.ColorPresetEntity
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
interface ColorPresetMapper {
suspend fun toColorPresetEntity(colorPreset: ColorPreset, order: Int): ColorPresetEntity

View file

@ -2,7 +2,7 @@ package ua.acclorite.book_story.data.mapper.color_preset
import androidx.compose.ui.graphics.Color
import ua.acclorite.book_story.data.local.dto.ColorPresetEntity
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
import javax.inject.Inject
class ColorPresetMapperImpl @Inject constructor() : ColorPresetMapper {

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.data.mapper.history
import ua.acclorite.book_story.data.local.dto.HistoryEntity
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
interface HistoryMapper {
suspend fun toHistoryEntity(history: History): HistoryEntity

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.data.mapper.history
import ua.acclorite.book_story.data.local.dto.HistoryEntity
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
import javax.inject.Inject
class HistoryMapperImpl @Inject constructor() : HistoryMapper {

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.data.parser
import ua.acclorite.book_story.domain.model.BookWithCover
import ua.acclorite.book_story.domain.library.book.BookWithCover
import java.io.File

View file

@ -6,7 +6,7 @@ import ua.acclorite.book_story.data.parser.fb2.Fb2FileParser
import ua.acclorite.book_story.data.parser.html.HtmlFileParser
import ua.acclorite.book_story.data.parser.pdf.PdfFileParser
import ua.acclorite.book_story.data.parser.txt.TxtFileParser
import ua.acclorite.book_story.domain.model.BookWithCover
import ua.acclorite.book_story.domain.library.book.BookWithCover
import java.io.File
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.data.parser
import ua.acclorite.book_story.domain.model.ChapterWithText
import ua.acclorite.book_story.domain.reader.ChapterWithText
import ua.acclorite.book_story.domain.util.Resource
import java.io.File

View file

@ -7,9 +7,9 @@ import ua.acclorite.book_story.data.parser.fb2.Fb2TextParser
import ua.acclorite.book_story.data.parser.html.HtmlTextParser
import ua.acclorite.book_story.data.parser.pdf.PdfTextParser
import ua.acclorite.book_story.data.parser.txt.TxtTextParser
import ua.acclorite.book_story.domain.model.ChapterWithText
import ua.acclorite.book_story.domain.reader.ChapterWithText
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import java.io.File
import javax.inject.Inject
@ -70,12 +70,4 @@ class TextParserImpl @Inject constructor(
}
}
}
}
}

View file

@ -7,10 +7,10 @@ import kotlinx.coroutines.withContext
import org.jsoup.Jsoup
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.FileParser
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.BookWithCover
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.library.book.BookWithCover
import ua.acclorite.book_story.domain.library.category.Category
import ua.acclorite.book_story.domain.ui.UIText
import java.io.File
import java.util.zip.ZipFile
import javax.inject.Inject

View file

@ -15,10 +15,10 @@ import org.jsoup.Jsoup
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.DocumentParser
import ua.acclorite.book_story.data.parser.TextParser
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.model.ChapterWithText
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.reader.ChapterWithText
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.util.addAll
import ua.acclorite.book_story.presentation.core.util.clearMarkdown
import java.io.File

View file

@ -6,10 +6,10 @@ import org.w3c.dom.Document
import org.w3c.dom.Element
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.FileParser
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.BookWithCover
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.library.book.BookWithCover
import ua.acclorite.book_story.domain.library.category.Category
import ua.acclorite.book_story.domain.ui.UIText
import java.io.File
import javax.inject.Inject
import javax.xml.parsers.DocumentBuilderFactory

View file

@ -8,10 +8,10 @@ import org.w3c.dom.Element
import org.w3c.dom.NodeList
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.TextParser
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.model.ChapterWithText
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.reader.ChapterWithText
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.util.clearAllMarkdown
import java.io.File
import javax.inject.Inject

View file

@ -3,10 +3,10 @@ package ua.acclorite.book_story.data.parser.html
import org.jsoup.Jsoup
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.FileParser
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.BookWithCover
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.library.book.BookWithCover
import ua.acclorite.book_story.domain.library.category.Category
import ua.acclorite.book_story.domain.ui.UIText
import java.io.File
import javax.inject.Inject

View file

@ -6,10 +6,10 @@ import org.jsoup.Jsoup
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.DocumentParser
import ua.acclorite.book_story.data.parser.TextParser
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.model.ChapterWithText
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.reader.ChapterWithText
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.util.clearAllMarkdown
import java.io.File
import javax.inject.Inject

View file

@ -5,10 +5,10 @@ import com.tom_roush.pdfbox.android.PDFBoxResourceLoader
import com.tom_roush.pdfbox.pdmodel.PDDocument
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.FileParser
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.BookWithCover
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.library.book.BookWithCover
import ua.acclorite.book_story.domain.library.category.Category
import ua.acclorite.book_story.domain.ui.UIText
import java.io.File
import javax.inject.Inject
@ -27,7 +27,7 @@ class PdfFileParser @Inject constructor(
if (isNullOrBlank()) UIText.StringResource(R.string.unknown_author)
else UIText.StringValue(this)
}
val description = document.documentInformation.subject ?: null
val description = document.documentInformation.subject
document.close()

View file

@ -8,10 +8,10 @@ import com.tom_roush.pdfbox.text.PDFTextStripper
import kotlinx.coroutines.yield
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.TextParser
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.model.ChapterWithText
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.reader.ChapterWithText
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.util.clearAllMarkdown
import java.io.File
import javax.inject.Inject

View file

@ -2,10 +2,10 @@ package ua.acclorite.book_story.data.parser.txt
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.FileParser
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.BookWithCover
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.library.book.BookWithCover
import ua.acclorite.book_story.domain.library.category.Category
import ua.acclorite.book_story.domain.ui.UIText
import java.io.File
import javax.inject.Inject

View file

@ -6,10 +6,10 @@ import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.parser.TextParser
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.model.ChapterWithText
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.reader.ChapterWithText
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.util.clearAllMarkdown
import java.io.BufferedReader
import java.io.File

View file

@ -16,14 +16,14 @@ import ua.acclorite.book_story.data.mapper.book.BookMapper
import ua.acclorite.book_story.data.parser.FileParser
import ua.acclorite.book_story.data.parser.MarkdownParser
import ua.acclorite.book_story.data.parser.TextParser
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.BookWithText
import ua.acclorite.book_story.domain.model.BookWithTextAndCover
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.library.book.BookWithText
import ua.acclorite.book_story.domain.library.book.BookWithTextAndCover
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.repository.BookRepository
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.CoverImage
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.File

View file

@ -2,7 +2,7 @@ package ua.acclorite.book_story.data.repository
import ua.acclorite.book_story.data.local.room.BookDao
import ua.acclorite.book_story.data.mapper.color_preset.ColorPresetMapper
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
import ua.acclorite.book_story.domain.repository.ColorPresetRepository
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -9,7 +9,7 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import ua.acclorite.book_story.data.local.data_store.DataStore
import ua.acclorite.book_story.domain.repository.DataStoreRepository
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.ui.main.MainState
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -7,14 +7,14 @@ import ua.acclorite.book_story.data.local.room.BookDao
import ua.acclorite.book_story.data.mapper.book.BookMapper
import ua.acclorite.book_story.data.parser.FileParser
import ua.acclorite.book_story.data.parser.TextParser
import ua.acclorite.book_story.domain.model.BookWithTextAndCover
import ua.acclorite.book_story.domain.model.NullableBook
import ua.acclorite.book_story.domain.model.NullableBook.NotNull
import ua.acclorite.book_story.domain.model.NullableBook.Null
import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.domain.browse.SelectableFile
import ua.acclorite.book_story.domain.library.book.BookWithTextAndCover
import ua.acclorite.book_story.domain.library.book.NullableBook
import ua.acclorite.book_story.domain.library.book.NullableBook.NotNull
import ua.acclorite.book_story.domain.library.book.NullableBook.Null
import ua.acclorite.book_story.domain.repository.FileSystemRepository
import ua.acclorite.book_story.domain.ui.UIText
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.constants.provideSupportedExtensions
import java.io.File

View file

@ -2,7 +2,7 @@ package ua.acclorite.book_story.data.repository
import ua.acclorite.book_story.data.local.room.BookDao
import ua.acclorite.book_story.data.mapper.history.HistoryMapper
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
import ua.acclorite.book_story.domain.repository.HistoryRepository
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.about
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.about
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.ui.UIText
@Immutable
data class Credit(
@ -9,4 +9,4 @@ data class Credit(
val source: String?,
val credits: List<UIText>,
val website: String?
)
)

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.data
package ua.acclorite.book_story.domain.browse
import androidx.compose.runtime.Immutable

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.data
package ua.acclorite.book_story.domain.browse
enum class BrowseLayout {
LIST, GRID

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.browse.data
package ua.acclorite.book_story.domain.browse
enum class BrowseSortOrder {
NAME,

View file

@ -0,0 +1,11 @@
package ua.acclorite.book_story.domain.browse
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.ui.UIText
import java.io.File
@Immutable
data class FileWithTitle(
val title: UIText,
val file: File
)

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.browse
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.Selected

View file

@ -1,13 +1,12 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.help
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.text.AnnotatedString
import ua.acclorite.book_story.domain.util.OnNavigate
@Immutable
data class HelpTip(
@StringRes val title: Int,
val description: @Composable AnnotatedString.Builder.(onNavigate: OnNavigate, fromStart: Boolean) -> Unit
val description: @Composable AnnotatedString.Builder.() -> Unit
)

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.history
import androidx.compose.runtime.Immutable

View file

@ -1,6 +1,7 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.history
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.library.book.Book
@Immutable
data class History(

View file

@ -0,0 +1,31 @@
package ua.acclorite.book_story.domain.library.book
import android.net.Uri
import android.os.Parcelable
import androidx.compose.runtime.Immutable
import kotlinx.parcelize.Parcelize
import ua.acclorite.book_story.domain.library.category.Category
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.ui.UIText
@Parcelize
@Immutable
data class Book(
val id: Int = 0,
val title: String,
val author: UIText,
val description: String?,
val textPath: String,
val filePath: String,
val coverImage: Uri?,
val scrollIndex: Int,
val scrollOffset: Int,
val progress: Float,
val chapters: List<Chapter> = emptyList(),
val lastOpened: Long?,
val category: Category,
) : Parcelable

View file

@ -0,0 +1,10 @@
package ua.acclorite.book_story.domain.library.book
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.CoverImage
@Immutable
data class BookWithCover(
val book: Book,
val coverImage: CoverImage?
)

View file

@ -0,0 +1,9 @@
package ua.acclorite.book_story.domain.library.book
import androidx.compose.runtime.Immutable
@Immutable
data class BookWithText(
val book: Book,
val text: List<String>
)

View file

@ -0,0 +1,11 @@
package ua.acclorite.book_story.domain.library.book
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.CoverImage
@Immutable
data class BookWithTextAndCover(
val book: Book,
val coverImage: CoverImage?,
val text: List<String>
)

View file

@ -0,0 +1,28 @@
package ua.acclorite.book_story.domain.library.book
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.ui.UIText
@Immutable
sealed class NullableBook(
val bookWithTextAndCover: BookWithTextAndCover?,
val fileName: String?,
val message: UIText?
) {
class NotNull(
bookWithTextAndCover: BookWithTextAndCover
) : NullableBook(
bookWithTextAndCover = bookWithTextAndCover,
fileName = null,
message = null
)
class Null(
fileName: String,
message: UIText?
) : NullableBook(
bookWithTextAndCover = null,
fileName = fileName,
message = message
)
}

View file

@ -0,0 +1,10 @@
package ua.acclorite.book_story.domain.library.book
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.Selected
@Immutable
data class SelectableBook(
val data: Book,
val selected: Selected
)

View file

@ -0,0 +1,10 @@
package ua.acclorite.book_story.domain.library.book
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.Selected
@Immutable
data class SelectableNullableBook(
val data: NullableBook,
val selected: Selected
)

View file

@ -0,0 +1,11 @@
package ua.acclorite.book_story.domain.library.category
import androidx.compose.runtime.Immutable
@Immutable
enum class Category {
READING,
ALREADY_READ,
PLANNING,
DROPPED
}

View file

@ -0,0 +1,10 @@
package ua.acclorite.book_story.domain.library.category
import ua.acclorite.book_story.domain.library.book.SelectableBook
import ua.acclorite.book_story.domain.ui.UIText
data class CategoryWithBooks(
val category: Category,
val title: UIText,
val books: List<SelectableBook>
)

View file

@ -1,70 +0,0 @@
package ua.acclorite.book_story.domain.model
import android.net.Uri
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.CoverImage
import ua.acclorite.book_story.domain.util.UIText
@Immutable
data class Book(
val id: Int = 0,
val title: String,
val author: UIText,
val description: String?,
val textPath: String,
val filePath: String,
val coverImage: Uri?,
val scrollIndex: Int,
val scrollOffset: Int,
val progress: Float,
val chapters: List<Chapter> = emptyList(),
val lastOpened: Long?,
val category: Category,
)
@Immutable
data class BookWithText(
val book: Book,
val text: List<String>
)
@Immutable
data class BookWithCover(
val book: Book,
val coverImage: CoverImage?
)
@Immutable
data class BookWithTextAndCover(
val book: Book,
val coverImage: CoverImage?,
val text: List<String>
)
@Immutable
sealed class NullableBook(
val bookWithTextAndCover: BookWithTextAndCover?,
val fileName: String?,
val message: UIText?
) {
class NotNull(
bookWithTextAndCover: BookWithTextAndCover
) : NullableBook(
bookWithTextAndCover = bookWithTextAndCover,
fileName = null,
message = null
)
class Null(
fileName: String,
message: UIText?
) : NullableBook(
bookWithTextAndCover = null,
fileName = fileName,
message = message
)
}

View file

@ -1,15 +0,0 @@
package ua.acclorite.book_story.domain.model
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.Selected
@Immutable
enum class Category {
READING, ALREADY_READ, PLANNING, DROPPED
}
@Immutable
data class CategorizedBooks(
val category: Category,
val books: List<Pair<Book, Selected>>
)

View file

@ -1,11 +0,0 @@
package ua.acclorite.book_story.domain.model
import android.os.Parcelable
import androidx.compose.runtime.Immutable
import kotlinx.parcelize.Parcelize
@Parcelize
@Immutable
data class ReaderLine(
val line: String
) : Parcelable

View file

@ -0,0 +1,76 @@
package ua.acclorite.book_story.domain.navigator
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlin.collections.plus
@HiltViewModel(assistedFactory = Navigator.Factory::class)
class Navigator @AssistedInject constructor(
private val savedStateHandle: SavedStateHandle,
@Assisted private val initialScreen: Screen
) : ViewModel() {
val items = savedStateHandle.getStateFlow("items", mutableListOf<Screen>(initialScreen))
private fun StateFlow<MutableList<Screen>>.removeLast() {
savedStateHandle["items"] = value.dropLast(1)
}
private fun StateFlow<MutableList<Screen>>.add(item: Screen) {
savedStateHandle["items"] = value + item
}
val lastItem = items.map {
it.lastOrNull() ?: initialScreen
}.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = initialScreen
)
val lastEvent = savedStateHandle.getStateFlow("stack_event", StackEvent.Default)
private fun StateFlow<StackEvent>.change(stackEvent: StackEvent) {
savedStateHandle["stack_event"] = stackEvent
}
fun push(
targetScreen: Screen,
popping: Boolean = false,
saveInBackStack: Boolean = true
) {
if (lastItem.value::class == targetScreen::class) return
if (!saveInBackStack) items.removeLast()
lastEvent.change(
if (popping) StackEvent.Pop
else StackEvent.Default
)
if (lastItem.value::class == targetScreen::class) items.removeLast()
items.add(targetScreen)
}
fun pop(popping: Boolean = true) {
if (items.value.count() > 1) {
lastEvent.change(
if (popping) StackEvent.Pop
else StackEvent.Default
)
items.removeLast()
}
}
@AssistedFactory
interface Factory {
fun create(startScreen: Screen): Navigator
}
}

View file

@ -1,12 +1,13 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.navigator
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.presentation.core.navigation.Screen
import kotlinx.serialization.Serializable
@Serializable
@Immutable
data class NavigationItem(
data class NavigatorItem(
val screen: Screen,
@StringRes val title: Int,
@StringRes val tooltip: Int,

View file

@ -0,0 +1,8 @@
package ua.acclorite.book_story.domain.navigator
import androidx.compose.runtime.Composable
interface Screen {
@Composable
fun Content()
}

View file

@ -0,0 +1,10 @@
package ua.acclorite.book_story.domain.navigator
import androidx.compose.runtime.Immutable
import kotlinx.serialization.Serializable
@Immutable
@Serializable
enum class StackEvent {
Default, Pop
}

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.reader
import android.os.Parcelable
import androidx.compose.runtime.Immutable
@ -11,10 +11,4 @@ data class Chapter(
val title: String,
val startIndex: Int,
val endIndex: Int
) : Parcelable
@Immutable
data class ChapterWithText(
val chapter: Chapter,
val text: List<String>
)
) : Parcelable

View file

@ -0,0 +1,9 @@
package ua.acclorite.book_story.domain.reader
import androidx.compose.runtime.Immutable
@Immutable
data class ChapterWithText(
val chapter: Chapter,
val text: List<String>
)

View file

@ -0,0 +1,9 @@
package ua.acclorite.book_story.domain.reader
import androidx.compose.runtime.Immutable
@Immutable
data class Checkpoint(
val index: Int,
val offset: Int
)

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.reader
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color

View file

@ -1,8 +1,8 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.reader
import androidx.compose.runtime.Immutable
import androidx.compose.ui.text.font.FontFamily
import ua.acclorite.book_story.domain.util.UIText
import ua.acclorite.book_story.domain.ui.UIText
@Immutable
data class FontWithName(

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.data
package ua.acclorite.book_story.domain.reader
import android.content.pm.ActivityInfo

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.reader.data
package ua.acclorite.book_story.domain.reader
enum class ReaderTextAlignment {
START, JUSTIFY, CENTER, END

View file

@ -1,10 +1,10 @@
package ua.acclorite.book_story.domain.repository
import androidx.compose.ui.text.AnnotatedString
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.BookWithText
import ua.acclorite.book_story.domain.model.BookWithTextAndCover
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.library.book.BookWithText
import ua.acclorite.book_story.domain.library.book.BookWithTextAndCover
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.util.CoverImage
import ua.acclorite.book_story.domain.util.Resource

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.repository
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
interface ColorPresetRepository {

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.domain.repository
import androidx.datastore.preferences.core.Preferences
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.ui.main.MainState
interface DataStoreRepository {

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.domain.repository
import ua.acclorite.book_story.domain.model.NullableBook
import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.domain.browse.SelectableFile
import ua.acclorite.book_story.domain.library.book.NullableBook
import java.io.File
interface FileSystemRepository {

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.repository
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
interface HistoryRepository {

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.domain.model
package ua.acclorite.book_story.domain.ui
import androidx.compose.runtime.Immutable
import androidx.compose.ui.text.TextStyle

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.domain.util
package ua.acclorite.book_story.domain.ui
import android.content.Context
import android.os.Parcelable

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.Chapter
import ua.acclorite.book_story.domain.reader.Chapter
import ua.acclorite.book_story.domain.repository.BookRepository
import ua.acclorite.book_story.domain.util.Resource
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import ua.acclorite.book_story.domain.repository.HistoryRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.BookWithTextAndCover
import ua.acclorite.book_story.domain.library.book.BookWithTextAndCover
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.BookWithText
import ua.acclorite.book_story.domain.library.book.BookWithText
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.book
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.library.book.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import ua.acclorite.book_story.domain.util.CoverImage
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.color_preset
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
import ua.acclorite.book_story.domain.repository.ColorPresetRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.color_preset
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
import ua.acclorite.book_story.domain.repository.ColorPresetRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.color_preset
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
import ua.acclorite.book_story.domain.repository.ColorPresetRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.color_preset
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
import ua.acclorite.book_story.domain.repository.ColorPresetRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.color_preset
import ua.acclorite.book_story.domain.model.ColorPreset
import ua.acclorite.book_story.domain.reader.ColorPreset
import ua.acclorite.book_story.domain.repository.ColorPresetRepository
import javax.inject.Inject

View file

@ -1,7 +1,7 @@
package ua.acclorite.book_story.domain.use_case.data_store
import ua.acclorite.book_story.domain.repository.DataStoreRepository
import ua.acclorite.book_story.presentation.data.MainState
import ua.acclorite.book_story.ui.main.MainState
import javax.inject.Inject
class GetAllSettings @Inject constructor(

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.file_system
import ua.acclorite.book_story.domain.model.NullableBook
import ua.acclorite.book_story.domain.library.book.NullableBook
import ua.acclorite.book_story.domain.repository.FileSystemRepository
import java.io.File
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.file_system
import ua.acclorite.book_story.domain.model.SelectableFile
import ua.acclorite.book_story.domain.browse.SelectableFile
import ua.acclorite.book_story.domain.repository.FileSystemRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.history
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
import ua.acclorite.book_story.domain.repository.HistoryRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.history
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
import ua.acclorite.book_story.domain.repository.HistoryRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.history
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
import ua.acclorite.book_story.domain.repository.HistoryRepository
import javax.inject.Inject

View file

@ -1,6 +1,6 @@
package ua.acclorite.book_story.domain.use_case.history
import ua.acclorite.book_story.domain.model.History
import ua.acclorite.book_story.domain.history.History
import ua.acclorite.book_story.domain.repository.HistoryRepository
import javax.inject.Inject

View file

@ -1,5 +1,7 @@
package ua.acclorite.book_story.domain.util
import ua.acclorite.book_story.domain.ui.UIText
sealed class Resource<T>(val data: T? = null, val message: UIText? = null) {
class Success<T>(data: T?) : Resource<T>(data)
class Error<T>(message: UIText, data: T? = null) : Resource<T>(data, message)

View file

@ -1,10 +1,10 @@
package ua.acclorite.book_story.domain.util
import android.graphics.Bitmap
import ua.acclorite.book_story.presentation.core.navigation.Navigator
typealias CoverImage = Bitmap
typealias Selected = Boolean
typealias ID = Int
typealias Route = String
typealias OnNavigate = (Navigator.() -> Unit) -> Unit
typealias BottomSheet = String
typealias Dialog = String
typealias Drawer = String

View file

@ -1,26 +0,0 @@
package ua.acclorite.book_story.domain.util
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.StateFlow
abstract class UIViewModel<S, E> : ViewModel() {
abstract val state: StateFlow<S>
abstract fun onEvent(event: E)
companion object {
@Composable
inline fun <reified V : UIViewModel<S, E>, S, E> getState(): State<S> {
return hiltViewModel<V>().state.collectAsStateWithLifecycle()
}
@Composable
inline fun <reified V : UIViewModel<S, E>, S, E> getEvent(): (E) -> Unit {
return hiltViewModel<V>()::onEvent
}
}
}

View file

@ -1,19 +1,13 @@
package ua.acclorite.book_story.presentation.screens.about.components.badges
package ua.acclorite.book_story.presentation.about
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.domain.model.Badge
import ua.acclorite.book_story.domain.about.Badge
import ua.acclorite.book_story.presentation.core.components.common.IconButton
/**
* About Badge Item.
*
* @param badge [Badge].
* @param onClick OnClick callback.
*/
@Composable
fun AboutBadgeItem(
badge: Badge,

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.presentation.screens.about.components.badges
package ua.acclorite.book_story.presentation.about
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -10,24 +10,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.constants.provideAboutBadges
import ua.acclorite.book_story.presentation.core.util.showToast
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
import ua.acclorite.book_story.ui.about.AboutEvent
/**
* About Badges.
* Links all ways to contact me.
*
* @param verticalPadding Vertical item padding.
*/
@Composable
fun AboutBadges(verticalPadding: Dp = 18.dp) {
val onEvent = AboutViewModel.getEvent()
fun AboutBadges(
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit
) {
val context = LocalContext.current
Box(
@ -36,11 +29,14 @@ fun AboutBadges(verticalPadding: Dp = 18.dp) {
) {
LazyRow(
Modifier
.padding(horizontal = 18.dp, vertical = verticalPadding),
.padding(18.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(13.dp)
) {
items(Constants.provideAboutBadges(), key = { it.id }) { badge ->
items(
Constants.provideAboutBadges(),
key = { it.id }
) { badge ->
AboutBadgeItem(badge = badge) {
when (badge.id) {
"tryzub" -> {
@ -50,14 +46,10 @@ fun AboutBadges(verticalPadding: Dp = 18.dp) {
else -> {
badge.url?.let {
onEvent(
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
it,
context,
noAppsFound = {
context.getString(R.string.error_no_browser)
.showToast(context = context, longToast = false)
}
page = it,
context = context
)
)
}

View file

@ -0,0 +1,43 @@
package ua.acclorite.book_story.presentation.about
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
import ua.acclorite.book_story.domain.util.Dialog
import ua.acclorite.book_story.ui.about.AboutEvent
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AboutContent(
scrollBehavior: TopAppBarScrollBehavior,
dialog: Dialog? = null,
updateLoading: Boolean,
updateInfo: LatestReleaseInfo?,
listState: LazyListState,
checkForUpdate: (AboutEvent.OnCheckForUpdate) -> Unit,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
dismissDialog: (AboutEvent.OnDismissDialog) -> Unit,
navigateToLicenses: () -> Unit,
navigateToCredits: () -> Unit,
navigateBack: () -> Unit
) {
AboutDialog(
dialog = dialog,
updateInfo = updateInfo,
navigateToBrowserPage = navigateToBrowserPage,
dismissDialog = dismissDialog
)
AboutScaffold(
scrollBehavior = scrollBehavior,
updateLoading = updateLoading,
listState = listState,
checkForUpdate = checkForUpdate,
navigateToBrowserPage = navigateToBrowserPage,
navigateToLicenses = navigateToLicenses,
navigateToCredits = navigateToCredits,
navigateBack = navigateBack
)
}

View file

@ -0,0 +1,25 @@
package ua.acclorite.book_story.presentation.about
import androidx.compose.runtime.Composable
import ua.acclorite.book_story.data.remote.dto.LatestReleaseInfo
import ua.acclorite.book_story.domain.util.Dialog
import ua.acclorite.book_story.ui.about.AboutEvent
import ua.acclorite.book_story.ui.about.AboutScreen
@Composable
fun AboutDialog(
dialog: Dialog? = null,
updateInfo: LatestReleaseInfo?,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
dismissDialog: (AboutEvent.OnDismissDialog) -> Unit
) {
when (dialog) {
AboutScreen.UPDATE_DIALOG -> {
AboutUpdateDialog(
updateInfo = updateInfo,
navigateToBrowserPage = navigateToBrowserPage,
dismissDialog = dismissDialog
)
}
}
}

View file

@ -1,4 +1,4 @@
package ua.acclorite.book_story.presentation.screens.about.components
package ua.acclorite.book_story.presentation.about
import androidx.compose.animation.core.EaseInOut
import androidx.compose.animation.core.tween
@ -21,20 +21,8 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import ua.acclorite.book_story.presentation.core.components.progress_indicator.CircularProgressIndicator
import ua.acclorite.book_story.presentation.ui.FadeTransitionPreservingSpace
import ua.acclorite.book_story.ui.theme.FadeTransitionPreservingSpace
/**
* About Screen Item.
* Clickable item, that has title, description and loading state.
*
* @param modifier [Modifier].
* @param title Title of the item.
* @param description Description of the item.
* @param verticalPadding Vertical item padding.
* @param showLoading Whether loading indicator is shown.
* @param isOnClickEnabled Whether this item is clickable.
* @param onClick OnClick callback.
*/
@Composable
fun AboutItem(
modifier: Modifier = Modifier,

View file

@ -0,0 +1,181 @@
package ua.acclorite.book_story.presentation.about
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.common.LazyColumnWithScrollbar
import ua.acclorite.book_story.presentation.core.constants.Constants
import ua.acclorite.book_story.presentation.core.constants.provideContributorsPage
import ua.acclorite.book_story.presentation.core.constants.provideIssuesPage
import ua.acclorite.book_story.presentation.core.constants.provideReleasesPage
import ua.acclorite.book_story.presentation.core.constants.provideTranslationPage
import ua.acclorite.book_story.ui.about.AboutEvent
@Composable
fun AboutLayout(
updateLoading: Boolean,
paddingValues: PaddingValues,
listState: LazyListState,
checkForUpdate: (AboutEvent.OnCheckForUpdate) -> Unit,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateToLicenses: () -> Unit,
navigateToCredits: () -> Unit
) {
val context = LocalContext.current
LazyColumnWithScrollbar(
Modifier
.fillMaxSize()
.padding(top = paddingValues.calculateTopPadding()),
state = listState
) {
item {
Spacer(modifier = Modifier.height(16.dp))
}
item {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Icon(
painterResource(id = R.drawable.app_icon),
contentDescription = stringResource(id = R.string.app_icon_content_desc),
modifier = Modifier
.padding(14.dp)
.size(120.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
item {
Spacer(modifier = Modifier.height(36.dp))
HorizontalDivider(
modifier = Modifier.fillMaxWidth(),
color = MaterialTheme.colorScheme.outlineVariant
)
}
item {
AboutItem(
title = stringResource(id = R.string.app_version_option),
description = buildAnnotatedString {
append(
stringResource(
id = R.string.app_version_option_desc_1,
context.getString(R.string.app_version)
)
)
append("\n")
append(stringResource(id = R.string.app_version_option_desc_2))
},
showLoading = updateLoading
) {
checkForUpdate(
AboutEvent.OnCheckForUpdate(
context = context
)
)
}
}
item {
AboutItem(
title = stringResource(id = R.string.report_bug_option),
description = null
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = Constants.provideIssuesPage(),
context = context
)
)
}
}
item {
AboutItem(
title = stringResource(id = R.string.contributors_option),
description = null
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = Constants.provideContributorsPage(),
context = context
)
)
}
}
item {
AboutItem(
title = stringResource(id = R.string.whats_new_option),
description = null
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = Constants.provideReleasesPage(),
context = context
)
)
}
}
item {
AboutItem(
title = stringResource(id = R.string.licenses_option),
description = null
) {
navigateToLicenses()
}
}
item {
AboutItem(
title = stringResource(id = R.string.credits_option),
description = null
) {
navigateToCredits()
}
}
item {
AboutItem(
title = stringResource(id = R.string.help_translate_option),
description = null
) {
navigateToBrowserPage(
AboutEvent.OnNavigateToBrowserPage(
page = Constants.provideTranslationPage(),
context = context
)
)
}
}
item {
AboutBadges(
navigateToBrowserPage = navigateToBrowserPage
)
}
}
}

View file

@ -0,0 +1,52 @@
package ua.acclorite.book_story.presentation.about
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import ua.acclorite.book_story.ui.about.AboutEvent
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AboutScaffold(
scrollBehavior: TopAppBarScrollBehavior,
updateLoading: Boolean,
listState: LazyListState,
checkForUpdate: (AboutEvent.OnCheckForUpdate) -> Unit,
navigateToBrowserPage: (AboutEvent.OnNavigateToBrowserPage) -> Unit,
navigateToLicenses: () -> Unit,
navigateToCredits: () -> Unit,
navigateBack: () -> Unit
) {
Scaffold(
Modifier
.fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection)
.windowInsetsPadding(WindowInsets.navigationBars),
containerColor = MaterialTheme.colorScheme.surface,
topBar = {
AboutTopBar(
scrollBehavior = scrollBehavior,
navigateBack = navigateBack
)
}
) { paddingValues ->
AboutLayout(
updateLoading = updateLoading,
paddingValues = paddingValues,
listState = listState,
checkForUpdate = checkForUpdate,
navigateToBrowserPage = navigateToBrowserPage,
navigateToLicenses = navigateToLicenses,
navigateToCredits = navigateToCredits
)
}
}

Some files were not shown because too many files have changed in this diff Show more