-
Notifications
You must be signed in to change notification settings - Fork 0
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
Week4 필수과제 #8
base: develop
Are you sure you want to change the base?
Week4 필수과제 #8
Changes from all commits
a198153
a99a92c
213c47f
7f30c00
b2c94b6
a667532
2c4b75c
7134570
8d825df
3da55e3
b872844
389fae7
ca84c9f
c160499
12771f5
fa459e9
e0afbc9
6b1f0ab
33303b8
f626982
771208b
c21eb75
b86bab9
8262c6f
7f0d641
2d46b36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,16 @@ | ||
import java.util.Properties | ||
|
||
plugins { | ||
alias(libs.plugins.android.application) | ||
alias(libs.plugins.kotlin.android) | ||
alias(libs.plugins.kotlin.compose) | ||
alias(libs.plugins.kotlin.serialization) | ||
} | ||
|
||
val properties = Properties().apply { | ||
load(project.rootProject.file("local.properties").inputStream()) | ||
} | ||
|
||
android { | ||
namespace = "org.sopt.and" | ||
compileSdk = 34 | ||
|
@@ -17,6 +23,7 @@ android { | |
versionName = "1.0" | ||
|
||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||
buildConfigField("String", "BASE_URL", properties["base.url"].toString()) | ||
} | ||
|
||
buildTypes { | ||
|
@@ -37,11 +44,11 @@ android { | |
} | ||
buildFeatures { | ||
compose = true | ||
buildConfig = true | ||
} | ||
} | ||
|
||
dependencies { | ||
|
||
implementation(libs.androidx.core.ktx) | ||
implementation(libs.androidx.lifecycle.runtime.ktx) | ||
implementation(libs.androidx.activity.compose) | ||
|
@@ -53,6 +60,15 @@ dependencies { | |
implementation(libs.lifecycle.viewmodel.compose) | ||
implementation(libs.kotlinx.serialization.json) | ||
implementation(libs.androidx.compose.navigation) | ||
implementation(libs.androidx.runtime.livedata) | ||
// network | ||
implementation(platform(libs.okhttp.bom)) | ||
implementation(libs.okhttp) | ||
implementation(libs.okhttp.logging.interceptor) | ||
implementation(libs.retrofit) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 버전 카탈로그에서 bundles로 묶는 것에 대해 찾아보시면 좋을 것 같아요! 디펜던시 추가할 때 훨씬 코드가 간결해진답니다 |
||
implementation(libs.retrofit.kotlin.serialization.converter) | ||
|
||
implementation(libs.androidx.datastore.preferences) | ||
testImplementation(libs.junit) | ||
androidTestImplementation(libs.androidx.junit) | ||
androidTestImplementation(libs.androidx.espresso.core) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
package org.sopt.and | ||
|
||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.filled.AccountCircle | ||
import androidx.compose.material.icons.filled.Home | ||
import androidx.compose.material.icons.filled.Search | ||
import android.content.Context | ||
import android.widget.Toast | ||
import androidx.annotation.StringRes | ||
import androidx.compose.material3.SnackbarHostState | ||
import androidx.compose.ui.text.input.PasswordVisualTransformation | ||
import androidx.compose.ui.text.input.VisualTransformation | ||
import org.sopt.and.navigation.Routes | ||
import org.sopt.and.navigation.WavveBottomNavigationItem | ||
import androidx.core.content.ContextCompat.getString | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.launch | ||
|
||
object WavveUtils { | ||
const val MIN_PASSWORD_LENGTH = 8 | ||
|
@@ -17,27 +18,6 @@ object WavveUtils { | |
const val SEARCH_SCREEN_INDEX = 1 | ||
const val HOME_SCREEN_INDEX = 0 | ||
|
||
val wavveBottomNavigationItems = listOf<WavveBottomNavigationItem>( | ||
WavveBottomNavigationItem( | ||
label = R.string.bottom_navigation_home_label, | ||
icon = Icons.Default.Home, | ||
route = Routes.Home, | ||
index = 0 | ||
), | ||
WavveBottomNavigationItem( | ||
label = R.string.bottom_navigation_search_label, | ||
icon = Icons.Default.Search, | ||
route = Routes.Search, | ||
index = 1 | ||
), | ||
WavveBottomNavigationItem( | ||
label = R.string.bottom_navigation_my_info_label, | ||
icon = Icons.Default.AccountCircle, | ||
route = Routes.MyInfo(""), | ||
index = 2 | ||
) | ||
) | ||
|
||
val linkableSNS = listOf<Pair<Int, Int>>( | ||
Pair(R.drawable.kakao_talk_icon, R.string.link_kakao_icon_description), | ||
Pair(R.drawable.t_world_icon, R.string.link_tworld_icon_description), | ||
|
@@ -48,4 +28,27 @@ object WavveUtils { | |
|
||
fun transformationPasswordVisual(isVisible: Boolean): VisualTransformation = | ||
if (isVisible) VisualTransformation.None else PasswordVisualTransformation() | ||
|
||
fun showToast( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 미쵸따 토스트 스낵바 둘다 확장함수화 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fun Context.showToast()의 형태로 만들어도 좋을 것 같ㄴㅔ요! |
||
context: Context, | ||
@StringRes message: Int | ||
) = Toast.makeText( | ||
context, | ||
context.getString(message), | ||
Toast.LENGTH_SHORT | ||
).show() | ||
|
||
fun showSnackbar( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 토스트만 확장함수로 만들어 뒀는데 스낵바도 추가해 봐야겠습니다. |
||
scope: CoroutineScope, | ||
context: Context, | ||
snackbarHostState: SnackbarHostState, | ||
@StringRes message: Int | ||
) = scope.launch { | ||
snackbarHostState.showSnackbar( | ||
message = getString( | ||
context, | ||
message | ||
) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,16 +8,16 @@ 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.rememberScrollState | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.foundation.lazy.LazyColumn | ||
import androidx.compose.foundation.lazy.rememberLazyListState | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.collectAsState | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import androidx.lifecycle.compose.collectAsStateWithLifecycle | ||
import androidx.lifecycle.viewmodel.compose.viewModel | ||
import org.sopt.and.R | ||
import org.sopt.and.home.components.HomeBannerPager | ||
|
@@ -31,10 +31,8 @@ import org.sopt.and.ui.theme.Grey100 | |
fun HomeScreen( | ||
innerPadding: PaddingValues | ||
) { | ||
val scrollState = rememberScrollState() | ||
|
||
val homeViewModel = viewModel<HomeViewModel>() | ||
val homeUiState by homeViewModel.uiState.collectAsState() | ||
val homeUiState by homeViewModel.uiState.collectAsStateWithLifecycle() | ||
|
||
Column( | ||
modifier = Modifier | ||
|
@@ -44,23 +42,32 @@ fun HomeScreen( | |
) { | ||
HomeTopBar(genres = homeUiState.genres) | ||
|
||
Column( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.verticalScroll(scrollState) | ||
LazyColumn( | ||
modifier = Modifier.fillMaxWidth(), | ||
state = rememberLazyListState() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HomeScreen에서 rememberLazyListState 객체를 사용할 게 아니라면 굳이 안적어주셔도 되어요. 기본 생성자로 알아서 생성합니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사용하는 함수를 뜯어보는 습관을 들이겠습니다! 들어가보니 기본값으로 제공하네요 |
||
) { | ||
HomeBannerPager(homeUiState.banners) | ||
item { | ||
HomeBannerPager(homeUiState.banners) | ||
} | ||
|
||
Spacer(modifier = Modifier.height(20.dp)) | ||
item { | ||
Spacer(modifier = Modifier.height(20.dp)) | ||
} | ||
|
||
RecommendList( | ||
title = stringResource(R.string.home_picks_of_editor_title), | ||
items = homeUiState.recommends | ||
) | ||
item { | ||
RecommendList( | ||
title = stringResource(R.string.home_picks_of_editor_title), | ||
items = homeUiState.recommends | ||
) | ||
} | ||
|
||
Spacer(modifier = Modifier.height(20.dp)) | ||
item { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. scrollState를 설정해준 Column 안에 LazyRow, LazyColumn들을 선언해주는 방식에서 LazyColumn 안에 items로 LazyRow, LazyColumn들을 선언해주는 것으로 변경한 이유가 있나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제대로 만들면 화면에 출력하는 아이템들이 많아져 스크롤이 길어질꺼 같은데 이때 LazyColumn의 보여지는 composable만 화면에 띄우는게 성능적으로 좋을 것 같았습니다. |
||
Spacer(modifier = Modifier.height(20.dp)) | ||
} | ||
|
||
Top20List(homeUiState.rankers) | ||
item { | ||
Top20List(homeUiState.rankers) | ||
} | ||
} | ||
|
||
HomeBottomCoupon() | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -11,6 +11,7 @@ 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.LazyRow | ||||||||||||
import androidx.compose.material3.Icon | ||||||||||||
import androidx.compose.material3.Text | ||||||||||||
import androidx.compose.runtime.Composable | ||||||||||||
|
@@ -63,15 +64,18 @@ fun HomeTopBar( | |||||||||||
) | ||||||||||||
} | ||||||||||||
|
||||||||||||
Row( | ||||||||||||
LazyRow( | ||||||||||||
modifier = Modifier | ||||||||||||
.fillMaxWidth() | ||||||||||||
.padding(vertical = 10.dp), | ||||||||||||
horizontalArrangement = Arrangement.spacedBy(14.dp) | ||||||||||||
) { | ||||||||||||
genres.forEach { genre -> | ||||||||||||
items( | ||||||||||||
count = genres.size, | ||||||||||||
key = { genres[it] } | ||||||||||||
) { index -> | ||||||||||||
Comment on lines
+73
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. items 확장함수 중에 List 타입을 인자로 넣으면 T 타입을 그대로 뱉어주는 함수가 있어요. 아래처럼 해도 같은 동작할 수 있습니다. (import 주의)
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 세미나에서 이렇게 배웠었는데 안되었던 이유가 import를 잘못해서였군요 ㅠ |
||||||||||||
Text( | ||||||||||||
text = stringResource(genre), | ||||||||||||
text = stringResource(genres[index]), | ||||||||||||
color = Grey200, | ||||||||||||
fontSize = 14.sp | ||||||||||||
) | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,8 @@ import org.sopt.and.ui.theme.Black100 | |
@Composable | ||
fun MyInfoScreen( | ||
paddingValues: PaddingValues, | ||
myEmail: String, | ||
myInfoViewModel: MyInfoViewModel, | ||
myInfoUiState: MyInfoUiState, | ||
modifier: Modifier = Modifier | ||
) { | ||
Column( | ||
|
@@ -33,7 +34,8 @@ fun MyInfoScreen( | |
.padding(paddingValues) | ||
) { | ||
MyInfoProfile( | ||
myEmail = myEmail, | ||
myInfoViewModel = myInfoViewModel, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 뷰모델을 직접적으로 넘겨주는 것보다는 인자들을 분리해서 람다 등을 이용해 필요한 것들만 넘겨주시면 좋을 것 같습니다~! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 첫번째로는 서로 하는 역할을 분리해놨는데 인자로 주면 종속된다(?)라는 점 일 것 같고, |
||
myInfoUiState = myInfoUiState, | ||
modifier = Modifier.weight(0.16f) | ||
) | ||
|
||
|
@@ -73,7 +75,8 @@ fun MyScreenPreview() { | |
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> | ||
MyInfoScreen( | ||
paddingValues = innerPadding, | ||
myEmail = "" | ||
myInfoViewModel = TODO(), | ||
myInfoUiState = TODO() | ||
Comment on lines
+78
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preview 안보일 것 같네요. Preview 대상 Composable 에는 ViewModel 인자를 아예 제거하심이 좋습니다. |
||
) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
package org.sopt.and.myinfo | ||
|
||
data class MyInfoUiState( | ||
val myEmail: String = "" | ||
val myHobby: String = "" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 모든 곳에서 공통으로 사용할 수 있는 UiState를 만들어서 활용하면 좋을 것 같아요! UiState에는 통신이 성공했다는 의미의 Success, Loading, Fail 정도만 있으면 될 것 같구용 그리고 MyInfo는 Entity로 정의해서 활용하고 UiState 이런 식으로 사용하면 좋지 않을까 싶어욤 설명하기 어렵네요ㅜ 이거 이해 잘 안되시면 연락 주세욤 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 우리팀 코드 보면서 많이 본 코드긴한데 연락 드릴게요 ㅎ |
||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,44 @@ | ||
package org.sopt.and.myinfo | ||
|
||
import androidx.lifecycle.ViewModel | ||
import android.app.Application | ||
import androidx.lifecycle.AndroidViewModel | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import org.sopt.and.myinfo.dto.GetHobbyResponseDto | ||
import org.sopt.and.services.ServicePool | ||
import retrofit2.Call | ||
import retrofit2.Callback | ||
import retrofit2.Response | ||
|
||
class MyInfoViewModel(application: Application) : AndroidViewModel(application) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AndroidViewModel(application)에 대해 설명해주실 수 있나용 ㅋ.ㅋ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 뷰모델에 Application을 넣으면 어떻게 되나요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ViewModel안에서는 context를 사용하지 못하잖아요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Context는 뭘까요? |
||
private val userService by lazy { ServicePool.userService(application) } | ||
|
||
class MyInfoViewModel( | ||
) : ViewModel() { | ||
private val _uiState = MutableStateFlow(MyInfoUiState()) | ||
val uiState: StateFlow<MyInfoUiState> = _uiState.asStateFlow() | ||
|
||
fun setMyEmail(email: String) { | ||
_uiState.value = _uiState.value.copy(myEmail = email) | ||
fun setMyHobby(myHobby: String) { | ||
_uiState.value = _uiState.value.copy(myHobby = myHobby) | ||
} | ||
|
||
fun getMyHobby() { | ||
userService.getMyHobby().enqueue( | ||
object : Callback<GetHobbyResponseDto> { | ||
override fun onResponse( | ||
call: Call<GetHobbyResponseDto>, | ||
response: Response<GetHobbyResponseDto> | ||
) { | ||
if (response.isSuccessful) { | ||
response.body()?.result?.hobby?.let { setMyHobby(it) } | ||
} else { | ||
setMyHobby("오류") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 하드코딩. |
||
} | ||
} | ||
|
||
override fun onFailure(call: Call<GetHobbyResponseDto>, t: Throwable) { | ||
// 어떤 처리를 할까요? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 토스트! 토스트! 토스트! |
||
} | ||
} | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,14 +19,19 @@ import androidx.compose.ui.Modifier | |
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.unit.dp | ||
import org.sopt.and.R | ||
import org.sopt.and.myinfo.MyInfoUiState | ||
import org.sopt.and.myinfo.MyInfoViewModel | ||
import org.sopt.and.ui.theme.Grey100 | ||
import org.sopt.and.ui.theme.White100 | ||
|
||
@Composable | ||
fun MyInfoProfile( | ||
myEmail: String, | ||
myInfoViewModel: MyInfoViewModel, | ||
myInfoUiState: MyInfoUiState, | ||
modifier: Modifier = Modifier | ||
) { | ||
myInfoViewModel.getMyHobby() | ||
|
||
Row( | ||
modifier = modifier | ||
.fillMaxWidth() | ||
|
@@ -43,8 +48,8 @@ fun MyInfoProfile( | |
|
||
Spacer(modifier = Modifier.width(5.dp)) | ||
|
||
Text( | ||
text = myEmail, | ||
Text( // 만약에 네트워크 통신이 늦었을 때 얘가 recomposition이 되나요?? | ||
text = if (myInfoUiState.myHobby.isNotEmpty()) myInfoUiState.myHobby else "Loading...", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. flow로 만들어두셨기 때문에 compose가 관찰할 수 있는 데이터가 되었잖아요?! 따라서 새로운 데이터로 변경될 시 해당 Text 컴포넌트는 리컴포지션됩니다! 상태가 변경되면 컴포저블은 해당 컴포넌트만 유아이 업데이트를 하니까요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 캬 확실히 알았습닌다 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 우리 지은이 언제 이렇게 컸지,, |
||
color = White100 | ||
) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요기 아래에 한 줄만 띄워주세요 ㅎ
주석이랑 코드랑 딱 붙어있어서..