Skip to content

Commit

Permalink
added nekos api
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed May 7, 2024
1 parent 6a72fa1 commit 154c098
Show file tree
Hide file tree
Showing 16 changed files with 526 additions and 17 deletions.
1 change: 1 addition & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ kotlin {
implementation(project(":firebase"))
implementation(project(":anilist"))
implementation(project(":model"))
implementation(project(":nekos"))
implementation(project(":settings"))
implementation(project(":trace"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SheetState
Expand Down Expand Up @@ -128,6 +130,50 @@ fun LazyListState.isScrollingUp(): Boolean {
}.value
}

@Composable
fun LazyGridState.isScrollingUp(): Boolean {
var previousIndex by remember(this) {
mutableStateOf(firstVisibleItemIndex)
}
var previousScrollOffset by remember(this) {
mutableStateOf(firstVisibleItemScrollOffset)
}
return remember(this) {
derivedStateOf {
if (previousIndex != firstVisibleItemIndex) {
previousIndex > firstVisibleItemIndex
} else {
previousScrollOffset >= firstVisibleItemScrollOffset
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
}
}
}.value
}

@Composable
fun LazyStaggeredGridState.isScrollingUp(): Boolean {
var previousIndex by remember(this) {
mutableStateOf(firstVisibleItemIndex)
}
var previousScrollOffset by remember(this) {
mutableStateOf(firstVisibleItemScrollOffset)
}
return remember(this) {
derivedStateOf {
if (previousIndex != firstVisibleItemIndex) {
previousIndex > firstVisibleItemIndex
} else {
previousScrollOffset >= firstVisibleItemScrollOffset
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
}
}
}.value
}

/**
* Checks if the modal is currently expanded or a swipe action is in progress to be expanded.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import org.kodein.di.bindSingleton
import org.kodein.di.instance
import dev.datlag.aniflow.common.nullableFirebaseInstance
import dev.datlag.aniflow.model.safeFirstOrNull
import dev.datlag.aniflow.nekos.Nekos
import dev.datlag.aniflow.nekos.NekosRepository
import dev.datlag.aniflow.other.UserHelper
import dev.datlag.aniflow.settings.Settings
import dev.datlag.aniflow.trace.Trace
Expand Down Expand Up @@ -113,6 +115,12 @@ data object NetworkModule {
baseUrl("https://api.trace.moe/")
}.create<Trace>()
}
bindSingleton<Nekos> {
val builder = instance<Ktorfit.Builder>()
builder.build {
baseUrl("https://api.nekosapi.com/v3/")
}.create<Nekos>()
}
bindSingleton<TrendingRepository> {
val appSettings = instance<Settings.PlatformAppSettings>()

Expand Down Expand Up @@ -168,5 +176,13 @@ data object NetworkModule {
trace = instance(),
)
}
bindSingleton<NekosRepository> {
val appSettings = instance<Settings.PlatformAppSettings>()

NekosRepository(
nekos = instance(),
nsfw = appSettings.adultContent
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Wallpaper
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.rounded.Favorite
import androidx.compose.material.icons.rounded.FavoriteBorder
import androidx.compose.material.icons.rounded.Home
import androidx.compose.material.icons.rounded.Wallpaper
import androidx.compose.material.icons.rounded.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
Expand All @@ -29,6 +30,7 @@ import dev.datlag.aniflow.LocalHaze
import dev.datlag.aniflow.SharedRes
import dev.datlag.aniflow.common.isScrollingUp
import dev.datlag.aniflow.ui.navigation.RootConfig
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
Expand Down Expand Up @@ -71,30 +73,48 @@ fun HidingNavigationBar(
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.contentColorFor(NavigationBarDefaults.containerColor)
) {
val isWallpaper = remember(selected) {
selected is NavigationBarState.Wallpaper
}
val isHome = remember(selected) {
selected is NavigationBarState.Home
}
val isFavorites = remember(selected) {
selected is NavigationBarState.Favorite
}

NavigationBarItem(
onClick = {
if (selected !is NavigationBarState.Wallpaper) {
if (!isWallpaper) {
onWallpaper()
}
},
selected = selected is NavigationBarState.Wallpaper,
selected = isWallpaper,
icon = {
Icon(
imageVector = selected.wallpaperIcon,
contentDescription = null
Image(
modifier = Modifier.size(24.dp),
painter = painterResource(
if (isWallpaper) {
SharedRes.images.cat_filled
} else {
SharedRes.images.cat_rounded
}
),
contentDescription = null,
colorFilter = ColorFilter.tint(LocalContentColor.current)
)
},
label = {
Text(text = "Wallpapers")
Text(text = "Nekos")
}
)
NavigationBarItem(
onClick = {
if (selected !is NavigationBarState.Home) {
if (!isHome) {
onHome()
}
},
selected = selected is NavigationBarState.Home,
selected = isHome,
icon = {
Icon(
imageVector = selected.homeIcon,
Expand All @@ -107,11 +127,11 @@ fun HidingNavigationBar(
)
NavigationBarItem(
onClick = {
if (selected !is NavigationBarState.Favorite) {
if (!isFavorites) {
onFavorites()
}
},
selected = selected is NavigationBarState.Favorite,
selected = isFavorites,
icon = {
Icon(
imageVector = selected.favoriteIcon,
Expand Down Expand Up @@ -157,6 +177,9 @@ sealed interface NavigationBarState {
data object Wallpaper : NavigationBarState {
override val unselectedIcon: ImageVector
get() = Icons.Rounded.Wallpaper

override val selectedIcon: ImageVector
get() = Icons.Rounded.Image
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
package dev.datlag.aniflow.ui.navigation.screen.wallpaper

import dev.datlag.aniflow.nekos.NekosRepository
import dev.datlag.aniflow.nekos.model.Rating
import dev.datlag.aniflow.ui.navigation.Component
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

interface WallpaperComponent : Component {

val adultContent: Flow<Boolean>
val rating: StateFlow<Rating>

val state: Flow<NekosRepository.State>

fun viewHome()
fun viewFavorites()

fun filter(rating: Rating)
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,145 @@
package dev.datlag.aniflow.ui.navigation.screen.wallpaper

import androidx.compose.material3.Scaffold
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.FilterList
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.option.OptionDialog
import com.maxkeppeler.sheets.option.models.DisplayMode
import com.maxkeppeler.sheets.option.models.Option
import com.maxkeppeler.sheets.option.models.OptionConfig
import com.maxkeppeler.sheets.option.models.OptionSelection
import dev.chrisbanes.haze.haze
import dev.datlag.aniflow.LocalHaze
import dev.datlag.aniflow.common.isScrollingUp
import dev.datlag.aniflow.common.merge
import dev.datlag.aniflow.nekos.NekosRepository
import dev.datlag.aniflow.nekos.model.Rating
import dev.datlag.aniflow.ui.navigation.screen.component.HidingNavigationBar
import dev.datlag.aniflow.ui.navigation.screen.component.NavigationBarState
import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle
import io.github.aakira.napier.Napier

@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun WallpaperScreen(component: WallpaperComponent) {
val listState = rememberLazyStaggeredGridState()

Scaffold(
floatingActionButton = {
val dialogState = rememberUseCaseState(
visible = false
)
val adultContent by component.adultContent.collectAsStateWithLifecycle(false)
val rating by component.rating.collectAsStateWithLifecycle()

OptionDialog(
state = dialogState,
config = OptionConfig(
mode = DisplayMode.LIST
),
selection = OptionSelection.Single(
options = listOf(
Option(
titleText = "Safe",
selected = rating is Rating.Safe,
),
Option(
titleText = "Suggestive",
selected = rating is Rating.Suggestive,
),
Option(
titleText = "Borderline",
selected = rating is Rating.Borderline,
disabled = !adultContent
),
Option(
titleText = "Explicit",
selected = rating is Rating.Explicit,
disabled = !adultContent
)
),
onSelectOption = { option, _ ->
val filterRating = when (option) {
1 -> Rating.Suggestive
2 -> Rating.Borderline
3 -> Rating.Explicit
else -> Rating.Safe
}
component.filter(filterRating)
}
)
)

ExtendedFloatingActionButton(
onClick = { dialogState.show() },
expanded = listState.isScrollingUp(),
icon = {
Icon(
imageVector = Icons.Rounded.FilterList,
contentDescription = null
)
},
text = {
Text(text = "Filter")
}
)
},
bottomBar = {
HidingNavigationBar(
visible = true,
visible = listState.isScrollingUp(),
selected = NavigationBarState.Wallpaper,
onWallpaper = { },
onHome = component::viewHome,
onFavorites = component::viewFavorites
)
}
) {
) { padding ->
val state by component.state.collectAsStateWithLifecycle(NekosRepository.State.None)

when (val current = state) {
is NekosRepository.State.None -> Text(text = "Loading")
is NekosRepository.State.Error -> Text(text = "Error")
is NekosRepository.State.Success -> {
LazyVerticalStaggeredGrid(
state = listState,
modifier = Modifier.haze(state = LocalHaze.current),
columns = StaggeredGridCells.Adaptive(120.dp),
contentPadding = padding.merge(PaddingValues(16.dp)),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalItemSpacing = 16.dp
) {
items(current.response.items, key = { it.id }) {
Card(
modifier = Modifier.animateItemPlacement(),
onClick = {
Napier.e(it.toString())
}
) {
AsyncImage(
modifier = Modifier.fillMaxSize(),
model = it.imageUrl ?: it.sampleUrl,
contentDescription = null,
contentScale = ContentScale.Crop
)
}
}
}
}
}
}
}
Loading

0 comments on commit 154c098

Please sign in to comment.