Skip to content

Commit

Permalink
🚧
Browse files Browse the repository at this point in the history
  • Loading branch information
schachi5000 committed Jul 5, 2024
1 parent 8c6516b commit e9373e2
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 91 deletions.
6 changes: 3 additions & 3 deletions shared/src/commonMain/kotlin/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ val repositories = module {
val viewModels = module {
viewModelOf(::AppViewModel)
viewModelOf(::MainViewModel)
viewModelOf(::MyDecksViewModel)
viewModelOf(::NewDeckViewModel)
viewModelOf(::SettingsViewModel)
viewModelOf(::SearchViewModel)
viewModelOf(::SpotlightViewModel)
viewModelOf(::PackSelectionViewModel)
singleOf(::MyDecksViewModel)
singleOf(::SettingsViewModel)
singleOf(::SpotlightViewModel)
}

@Composable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package net.schacher.mcc.shared.design.compose

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.PagerState
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import net.schacher.mcc.shared.design.theme.ContentPadding

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PagerHeader(
pageLabels: List<String>,
pagerState: PagerState,
modifier: Modifier = Modifier,
fontSize: TextUnit = 24.sp,
onLabelClick: (Int) -> Unit
) {
val scope = rememberCoroutineScope()

val selectedItem = pagerState.currentPage
val state = rememberLazyListState()

scope.launch {
if (selectedItem > 0) {
state.scrollToItem(selectedItem - 1, 0)
} else {
state.scrollToItem(selectedItem, 0)
}
}

LazyRow(
modifier = modifier,
state = state,
userScrollEnabled = false
) {
pageLabels.forEachIndexed { index, it ->
item {
val selected = selectedItem == index

Text(
text = it,
fontSize = fontSize,
color = MaterialTheme.colors.onBackground,
fontWeight = FontWeight.SemiBold,
modifier = Modifier
.padding(
start = if (index == 0) ContentPadding else 0.dp,
end = 12.dp
)
.noRippleClickable { onLabelClick(index) }
.alpha(if (selected) 1f else 0.35f),
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ private val primary = Color(0xFF0074df)
val LightColorScheme = lightColors(
primary = primary,
onPrimary = Color(0xFFFFFFFF),
background = Color(0xffededed),
background = Color(0xFFFFFFFF),
onBackground = Color(0xFF000000),
surface = Color(0xFFFFFFFF),
surface = Color(0xfff0f0f0),
onSurface = Color(0xFF000000),
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,63 @@
package net.schacher.mcc.shared.screens.main

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.BottomNavigation
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Scaffold
import androidx.compose.material.SnackbarHost
import androidx.compose.material.SnackbarHostState
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Settings
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import marvelchampionscompanion.shared.generated.resources.Res
import marvelchampionscompanion.shared.generated.resources.decks
import marvelchampionscompanion.shared.generated.resources.ic_deck
import marvelchampionscompanion.shared.generated.resources.ic_search
import marvelchampionscompanion.shared.generated.resources.ic_spotlight
import marvelchampionscompanion.shared.generated.resources.more
import marvelchampionscompanion.shared.generated.resources.search
import marvelchampionscompanion.shared.generated.resources.spotlight
import net.schacher.mcc.shared.design.compose.BackHandler
import net.schacher.mcc.shared.design.compose.CardInfo
import net.schacher.mcc.shared.design.compose.FreeBottomSheetContainer
import net.schacher.mcc.shared.design.compose.PagerHeader
import net.schacher.mcc.shared.design.compose.blurByBottomSheet
import net.schacher.mcc.shared.design.theme.ButtonSize
import net.schacher.mcc.shared.design.theme.ContentPadding
import net.schacher.mcc.shared.design.theme.DefaultShape
import net.schacher.mcc.shared.model.Card
import net.schacher.mcc.shared.platform.isIOs
import net.schacher.mcc.shared.screens.deck.DeckScreen
import net.schacher.mcc.shared.screens.main.MainViewModel.Event.CardsDatabaseSyncFailed
import net.schacher.mcc.shared.screens.main.MainViewModel.Event.DatabaseSynced
Expand All @@ -68,16 +72,18 @@ import net.schacher.mcc.shared.screens.main.MainViewModel.UiState.MainScreen.Set
import net.schacher.mcc.shared.screens.main.MainViewModel.UiState.MainScreen.Spotlight
import net.schacher.mcc.shared.screens.main.MainViewModel.UiState.SubScreen.CardMenu
import net.schacher.mcc.shared.screens.mydecks.MyDecksScreen
import net.schacher.mcc.shared.screens.mydecks.animateHorizontalAlignmentAsState
import net.schacher.mcc.shared.screens.newdeck.NewDeckScreen
import net.schacher.mcc.shared.screens.packselection.PackSelectionScreen
import net.schacher.mcc.shared.screens.search.SearchScreen
import net.schacher.mcc.shared.screens.settings.SettingsScreen
import net.schacher.mcc.shared.screens.spotlight.SpotlightScreen
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.koinInject

@OptIn(ExperimentalMaterialApi::class, ExperimentalResourceApi::class)
@OptIn(
ExperimentalMaterialApi::class, ExperimentalResourceApi::class,
ExperimentalFoundationApi::class
)
@Composable
fun MainScreen(viewModel: MainViewModel = koinInject()) {
val state = viewModel.state.collectAsState()
Expand Down Expand Up @@ -114,27 +120,15 @@ fun MainScreen(viewModel: MainViewModel = koinInject()) {
modifier = Modifier.fillMaxSize().blurByBottomSheet(sheetState),
backgroundColor = MaterialTheme.colors.background,
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
bottomBar = {
BottomBar(state.value.mainScreen.tabIndex, state.value.canShowMyDeckScreen) {
viewModel.onTabSelected(it)
}
}
) {
Box(
modifier = Modifier.padding(it)
) {
AnimatedContent(
targetState = state.value.mainScreen.tabIndex,
transitionSpec = {
if (targetState > initialState) {
(slideInHorizontally { width -> width } + fadeIn()).togetherWith(
slideOutHorizontally { width -> -width } + fadeOut())
} else {
(slideInHorizontally { width -> -width } + fadeIn()).togetherWith(
slideOutHorizontally { width -> width } + fadeOut())
}
}) { state ->
when (state) {
val pageLabels = listOf("Decks", "Spotlight", "Settings")
val pagerState = rememberPagerState(pageCount = { pageLabels.size })

HorizontalPager(state = pagerState) { page ->
when (page) {
0 -> MyDecksScreen(
onDeckClick = { viewModel.onDeckClicked(it) },
onAddDeckClick = { viewModel.onNewDeckClicked() }
Expand All @@ -144,11 +138,7 @@ fun MainScreen(viewModel: MainViewModel = koinInject()) {
viewModel.onDeckClicked(it)
}

2 -> SearchScreen {
viewModel.onCardClicked(it)
}

3 -> SettingsScreen(
2 -> SettingsScreen(
onPackSelectionClick = {
viewModel.onPackSelectionClicked()
},
Expand All @@ -157,6 +147,34 @@ fun MainScreen(viewModel: MainViewModel = koinInject()) {
})
}
}

PagerHeader(
modifier = Modifier.fillMaxWidth()
.background(shade)
.statusBarsPadding()
.padding(
top = ContentPadding,
bottom = ContentPadding + 16.dp
),
pageLabels = pageLabels,
pagerState = pagerState,
) {
scope.launch {
pagerState.animateScrollToPage(it)
}
}

if (pagerState.currentPage < 2) {
ScrollDependingTextButton(
text = "Suche",
icon = { Icon(Icons.Rounded.Search, contentDescription = "Search cards") },
expanded = true,
modifier = Modifier.align(Alignment.BottomCenter),
onClick = {

}
)
}
}
}
}
Expand Down Expand Up @@ -219,47 +237,57 @@ fun MainScreen(viewModel: MainViewModel = koinInject()) {
}
}

private val shade: Brush
@Composable
get() = Brush.verticalGradient(
colorStops = arrayOf(
0f to MaterialTheme.colors.background.copy(alpha = 1f),
0.75f to MaterialTheme.colors.background.copy(alpha = 1f),
1f to MaterialTheme.colors.background.copy(alpha = 0.0f)
)
)

@OptIn(ExperimentalResourceApi::class)
@Composable
fun BottomBar(selectedTabIndex: Int, canShowMyDecks: Boolean = true, onTabSelected: (Int) -> Unit) {
BottomNavigation(
modifier = Modifier
.fillMaxWidth()
.heightIn(72.dp)
.graphicsLayer {
clip = true
shape = RoundedCornerShape(topEnd = 16.dp, topStart = 16.dp)
},
elevation = 0.dp,
backgroundColor = MaterialTheme.colors.surface,
fun ScrollDependingTextButton(
text: String,
icon: @Composable () -> Unit,
expanded: Boolean,
modifier: Modifier = Modifier,
onClick: () -> Unit
) {
var horizontalBias by remember { mutableStateOf(1f) }
val alignment by animateHorizontalAlignmentAsState(horizontalBias)

horizontalBias = if (expanded) 0f else 1f

Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = alignment
) {
Row(Modifier.fillMaxWidth().padding(bottom = if (isIOs()) 16.dp else 0.dp)) {
if (canShowMyDecks) {
DefaultBottomNavigationItem(
label = stringResource(Res.string.decks),
icon = Res.drawable.ic_deck,
selected = (selectedTabIndex == 0),
onClick = { onTabSelected(0) },
)
FloatingActionButton(
onClick = onClick,
modifier = Modifier
.padding(vertical = 16.dp, horizontal = ContentPadding)
.sizeIn(maxHeight = ButtonSize, minWidth = ButtonSize)
.padding(horizontal = 8.dp),
contentColor = MaterialTheme.colors.onPrimary,
backgroundColor = MaterialTheme.colors.primary,
shape = DefaultShape
) {
Row(
modifier = Modifier.fillMaxHeight().padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
icon()

AnimatedVisibility(visible = expanded) {
Text(
modifier = Modifier.padding(start = 8.dp),
text = text
)
}
}
DefaultBottomNavigationItem(
label = stringResource(Res.string.spotlight),
icon = Res.drawable.ic_spotlight,
selected = (selectedTabIndex == 1),
onClick = { onTabSelected(1) },
)
DefaultBottomNavigationItem(
label = stringResource(Res.string.search),
icon = Res.drawable.ic_search,
selected = (selectedTabIndex == 2),
onClick = { onTabSelected(2) },
)
DefaultBottomNavigationItem(
label = stringResource(Res.string.more),
icon = { Icon(Icons.Rounded.Settings, "Settings") },
selected = (selectedTabIndex == 3),
onClick = { onTabSelected(3) },
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fun MyDecksScreen(
) {
items(entries.size) { index ->
if (index == 0) {
Spacer(Modifier.statusBarsPadding().height(ContentPadding))
Spacer(Modifier.statusBarsPadding().height(ContentPadding + 72.dp))
}

when (val entry = entries[index]) {
Expand Down Expand Up @@ -185,7 +185,7 @@ fun AddDeckButton(modifier: Modifier, expanded: Boolean, onClick: () -> Unit) {
}

@Composable
private fun animateHorizontalAlignmentAsState(targetBiasValue: Float): State<BiasAlignment.Horizontal> {
fun animateHorizontalAlignmentAsState(targetBiasValue: Float): State<BiasAlignment.Horizontal> {
val bias by animateFloatAsState(targetBiasValue)
return derivedStateOf { BiasAlignment.Horizontal(bias) }
}
Expand Down
Loading

0 comments on commit e9373e2

Please sign in to comment.