Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#22] entire UI #23

Merged
merged 8 commits into from
Jan 20, 2024
9 changes: 9 additions & 0 deletions app/src/main/kotlin/org/cazait/cazaitandroid/ui/MainScreen.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.cazait.cazaitandroid.ui

import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
Expand Down Expand Up @@ -71,6 +72,13 @@ internal fun MainScreen(
)
}
}
val onShowHttpErrorSnackbar: (stringResId: Int) -> Unit = { stringResId ->
coroutineScope.launch {
snackbarHostState.showSnackbar(
localContextResource.getString(stringResId)
)
}
}

Scaffold(
content = { padding ->
Expand Down Expand Up @@ -108,6 +116,7 @@ internal fun MainScreen(
)
signInNavGraph(
onSignInSuccess = { navigator.navigateHome() },
onShowHttpErrorSnackbar = onShowHttpErrorSnackbar,
onShowErrorSnackbar = onShowErrorSnackbar,
)
cafeDetailNavGraph.buildNavGraph(
Expand Down
5 changes: 3 additions & 2 deletions build-logic/src/main/kotlin/cazait.android.library.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import org.cazait.cazaitandroid.configureCoroutineAndroid
import org.cazait.cazaitandroid.configureHiltAndroid
import org.cazait.cazaitandroid.configureCoroutineAndroid
import org.cazait.cazaitandroid.configureHiltAndroid
import org.cazait.cazaitandroid.configureKotest
import org.cazait.cazaitandroid.configureKotlinAndroid

plugins {
id("com.android.library")
id("cazait.verify.detekt")
id("cazait.exception")
}

configureKotlinAndroid()
Expand Down
3 changes: 3 additions & 0 deletions build-logic/src/main/kotlin/cazait.exception.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import org.cazait.cazaitandroid.configureCazaitException

configureCazaitException()
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.cazait.cazaitandroid

import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies

internal fun Project.configureCazaitException() {
dependencies {
"implementation"(project(":core:http-handle"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,35 @@ import androidx.compose.ui.tooling.preview.Preview
import org.cazait.cazaitandroid.core.designsystem.R
import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CazaitTopBar(
title: @Composable () -> Unit,
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
CenterAlignedTopAppBar(
title = title,
scrollBehavior = scrollBehavior,
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CazaitTopBar(
@StringRes title: Int,
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
CenterAlignedTopAppBar(
title = {
Text(
text = stringResource(id = title),
style = CazaitTheme.typography.titleLargeB,
)
},
scrollBehavior = scrollBehavior,
)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CazaitBackTopBar(
Expand All @@ -27,7 +56,7 @@ fun CazaitBackTopBar(
title = title,
navigationIcon = {
IconButton(
onClick = onBackButtonClick
onClick = onBackButtonClick,
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back),
Expand All @@ -50,7 +79,7 @@ fun CazaitBackTopBar(
title = {
Text(
text = stringResource(id = title),
style = CazaitTheme.typography.titleLargeB
style = CazaitTheme.typography.titleLargeB,
)
},
navigationIcon = {
Expand Down Expand Up @@ -78,4 +107,16 @@ private fun PreviewCazaitBackTopBar() {
onBackButtonClick = {},
)
}
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Composable
private fun PreviewCazaitTopBar() {
CazaitTheme {
CazaitTopBar(
title = { Text(text = "리뷰 쓰기", style = CazaitTheme.typography.titleLargeB) },
)
}
}
3 changes: 3 additions & 0 deletions core/http-handle/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins {
id("cazait.coroutine.library")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.cazait.cazaitandroid.core.httphandle

class CazaitHttpException(
message: String,
val code: Int,
) : RuntimeException(message)
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.cazait.cazaitandroid.core.repo.signin

import org.cazait.cazaitandroid.core.httphandle.CazaitHttpException
import org.cazait.cazaitandroid.core.repo.signin.api.SignInRepository
import org.cazait.cazaitandroid.core.repo.signin.api.model.AccountName
import org.cazait.cazaitandroid.core.repo.signin.api.model.Password
import org.cazait.cazaitandroid.core.repo.signin.api.model.UserInformation
import org.cazait.cazaitandroid.core.repo.signin.mapper.toData
import org.cazait.cazaitandroid.core.repo.signin.network.SignInApi
import org.cazait.cazaitandroid.core.repo.signin.network.model.SignInRequest
import retrofit2.HttpException
import javax.inject.Inject

internal class DefaultSignInRepository @Inject constructor(
Expand All @@ -15,7 +17,11 @@ internal class DefaultSignInRepository @Inject constructor(
override suspend fun postSignIn(
accountName: AccountName,
password: Password,
): UserInformation = signInApi.postSignIn(
SignInRequest(accountName.name, password.word),
).data.toData()
): UserInformation = try {
signInApi.postSignIn(
SignInRequest(accountName.name, password.word),
).data.toData()
} catch (e: HttpException) {
throw CazaitHttpException(e.message(), e.code())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
Expand All @@ -43,6 +48,7 @@ import me.onebone.toolbar.CollapsingToolbarScope
import me.onebone.toolbar.ScrollStrategy
import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
import org.cazait.cazaitandroid.core.designsystem.component.NetworkImage
import org.cazait.cazaitandroid.core.designsystem.component.noRippleClickable
import org.cazait.cazaitandroid.core.designsystem.theme.Black
import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme
import org.cazait.cazaitandroid.core.designsystem.theme.SunsetOrange
Expand Down Expand Up @@ -215,21 +221,30 @@ private fun MenuReviewContent(
menus: CafeMenus,
reviews: CafeReviews,
) {
val pagerState = rememberPagerState(initialPage = 0) { 2 }

var pageState by remember { mutableIntStateOf(0) }
val pagerState = rememberPagerState(initialPage = pageState) { 2 }
LaunchedEffect(pageState) {
pagerState.scrollToPage(pageState)
}
Column(
modifier = Modifier
.fillMaxSize()
.background(color = MaterialTheme.colorScheme.primaryContainer),
) {
MenuReviewTabs(pagerState)
MenuReviewTabs(
pagerState,
onClickTab = { pageState = it },
)
MenuReviewPager(pagerState, menus, reviews)
}
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun MenuReviewTabs(pagerState: PagerState) {
private fun MenuReviewTabs(
pagerState: PagerState,
onClickTab: (tabNumber: Int) -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
Expand All @@ -239,10 +254,12 @@ private fun MenuReviewTabs(pagerState: PagerState) {
TabItem(
title = R.string.cafe_menu,
isSelected = pagerState.currentPage == 0,
onClick = { onClickTab(0) },
)
TabItem(
title = R.string.score_and_review,
isSelected = pagerState.currentPage == 1,
onClick = { onClickTab(1) },
)
}
}
Expand All @@ -251,11 +268,13 @@ private fun MenuReviewTabs(pagerState: PagerState) {
private fun TabItem(
title: Int,
isSelected: Boolean,
onClick: () -> Unit,
) {
Text(
text = stringResource(title),
style = CazaitTheme.typography.titleMediumB,
color = if (isSelected) SunsetOrange else Black,
modifier = Modifier.noRippleClickable { onClick() },
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridItemScope
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
Expand All @@ -36,7 +32,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import org.cazait.cazaitandroid.core.designsystem.theme.Black
import org.cazait.cazaitandroid.core.designsystem.theme.CazaitTheme
Expand All @@ -57,7 +52,6 @@ internal fun HomeScreen(
.padding(padding)
.background(color = MaterialTheme.colorScheme.primaryContainer)
.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(20.dp),
) {
HomeTopBar()
HomeContent(
Expand Down Expand Up @@ -102,40 +96,46 @@ private fun HomeContent(
uiState: HomeUiState,
onClickCafe: (Cafe) -> Unit,
) {
when (uiState) {
is HomeUiState.Success -> CongestionCafeGrid(
cafes = uiState.congestionCafes.asList().toImmutableList(),
onClickCafe = onClickCafe,
)
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
item {
Spacer(modifier = Modifier.height(12.dp))
HomeColumnTitle()
}

else -> Unit
if (uiState is HomeUiState.Success) {
val cafes = uiState.congestionCafes.asList()
val chunks = cafes.chunked(2).toImmutableList()
items(chunks) { chunk ->
CafeRow(chunk = chunk, onClickCafe = onClickCafe)
}
}
}
}

@Composable
private fun CongestionCafeGrid(
cafes: ImmutableList<CongestionCafe>,
private fun CafeRow(
chunk: List<CongestionCafe>,
onClickCafe: (Cafe) -> Unit,
) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
.padding(horizontal = 20.dp)
.padding(bottom = 32.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
header { HomeColumnTitle() }
items(
items = cafes,
key = { item: CongestionCafe -> item.cafe.id.toString() },
) { congestionCafe ->
chunk.forEach { congestionCafe ->
HomeCongestionCafeItem(
congestionCafe = congestionCafe,
onClick = { onClickCafe(congestionCafe.cafe) },
modifier = Modifier.weight(1f),
)
}
footer { Spacer(modifier = Modifier.height(32.dp)) }
if (chunk.size == 1) {
Spacer(modifier = Modifier.weight(1f))
}
}
}

Expand Down Expand Up @@ -177,23 +177,12 @@ private fun SearchingTextField(
}
}

private fun LazyGridScope.header(
content: @Composable LazyGridItemScope.() -> Unit,
) {
item(span = { GridItemSpan(this.maxLineSpan) }, content = content)
}

private fun LazyGridScope.footer(
content: @Composable LazyGridItemScope.() -> Unit,
) {
item(span = { GridItemSpan(this.maxLineSpan) }, content = content)
}

@Composable
private fun HomeColumnTitle() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.padding(bottom = 12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Expand Down
Loading
Loading