-
Notifications
You must be signed in to change notification settings - Fork 12
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
π 3λ¨κ³ - GitHub(UI μν) #33
Open
simuelunbo
wants to merge
11
commits into
next-step:simuelunbo
Choose a base branch
from
simuelunbo:step3
base: simuelunbo
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
a3b37f8
refactor: λ¬Έμμ΄ string resourceλ‘ κ΄λ¦¬ μ²λ¦¬
simuelunbo 8326b49
refactor: nullable λ°μ΄ν° nonNull ννλ‘ λ³κ²½
simuelunbo ae15834
feat: Github μ μ₯μ UI μν μ²λ¦¬ μΆκ°
simuelunbo e1d476b
refactor: GithubReposSuccessμμ GithubRepoListλ‘ μ§κ΄μ μΈ λ€μ΄λ°μΌλ‘ λ³κ²½
simuelunbo a00a8a9
refactor: Github 리μ€νΈ νΈμΆ μλ¬μ λΉ νλ©΄μΌλ‘ λ
ΈμΆ λ³κ²½
simuelunbo fa8a784
refactor: GitHub μ μ₯μ λͺ©λ‘ μλ¬ μ²λ¦¬ λ° λΉ μν νμ λ‘μ§ κ°μ
simuelunbo 120e696
refactor: μλ¬ μ²λ¦¬ λ‘μ§ κ°μ λ° μ½λ£¨ν΄ μμΈ μ²λ¦¬ λ°©μ μμ
simuelunbo 54780cc
feat: LoadingIndicator ν
μ€νΈ νκ·Έ μΆκ°
0005fc1
test: GithubRepositoryListScreen UI ν
μ€νΈ μΆκ°
7671241
chore: test dependency μΆκ°
02bc6e1
test: GithubRepoViewModel ν
μ€νΈ μΆκ°
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
app/src/androidTest/java/nextstep/github/ui/github/GithubRepoListScreenKtTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package nextstep.github.ui.github | ||
|
||
|
||
import androidx.compose.material3.SnackbarHostState | ||
import androidx.compose.runtime.LaunchedEffect | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.test.junit4.createComposeRule | ||
import androidx.compose.ui.test.onNodeWithTag | ||
import androidx.compose.ui.test.onNodeWithText | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import nextstep.github.model.GithubRepo | ||
import nextstep.github.ui.theme.GithubTheme | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class GithubRepositoryListScreenTest { | ||
|
||
@get:Rule | ||
val composeTestRule = createComposeRule() | ||
|
||
@Test | ||
fun λ‘λ©μ€μΌλ_LoadingIndicatorκ°_λμ¨λ€() { | ||
// Given | ||
val uiState = GithubRepoUiState(isLoading = true) | ||
|
||
// When | ||
composeTestRule.setContent { | ||
GithubTheme { | ||
GithubRepositoryListScreen( | ||
uiState = uiState, snackBarHostState = SnackbarHostState() | ||
) | ||
} | ||
} | ||
|
||
// Then | ||
composeTestRule.onNodeWithTag("LoadingIndicator").assertExists() | ||
} | ||
|
||
@Test | ||
fun μ μ₯μλ₯Ό_μ±κ³΅μ μΌλ‘_κ°μ Έμμλ_μ μ₯μ리μ€νΈκ°_보μ¬μ§λ€() { | ||
// Given | ||
val repositories = listOf( | ||
GithubRepo(1, "NextStep/Test", "ν μ€νΈ μ μ₯μ"), | ||
GithubRepo(2, "NextStep/Test2", "ν μ€νΈ μ μ₯μ2"), | ||
GithubRepo(3, "NextStep/Test3", "ν μ€νΈ μ μ₯μ3") | ||
) | ||
val uiState = GithubRepoUiState( | ||
isLoading = false, repositories = repositories | ||
) | ||
|
||
// When | ||
composeTestRule.setContent { | ||
GithubTheme { | ||
GithubRepositoryListScreen( | ||
uiState = uiState, snackBarHostState = SnackbarHostState() | ||
) | ||
} | ||
} | ||
|
||
// Then | ||
repositories.forEach { repo -> | ||
composeTestRule.onNodeWithText(repo.fullName).assertExists() | ||
val description = repo.description | ||
if (description != null) { | ||
composeTestRule.onNodeWithText(description).assertExists() | ||
} | ||
} | ||
} | ||
|
||
@Test | ||
fun μλ¬κ°_λ°μνλ©΄_μ€λ΅λ°κ°_λμ¨λ€() { | ||
// Given | ||
val uiState = GithubRepoUiState() | ||
|
||
// When | ||
composeTestRule.setContent { | ||
val snackBarHostState = remember { SnackbarHostState() } | ||
LaunchedEffect(true) { | ||
snackBarHostState.showSnackbar( | ||
message = "μλ¬ λ©μΈμ§", actionLabel = "μ¬μλ" | ||
) | ||
} | ||
GithubTheme { | ||
GithubRepositoryListScreen( | ||
uiState = uiState, snackBarHostState = snackBarHostState | ||
) | ||
} | ||
} | ||
|
||
// Then | ||
composeTestRule.waitForIdle() | ||
composeTestRule.onNodeWithText("μλ¬ λ©μΈμ§").assertExists() | ||
composeTestRule.onNodeWithText("μ¬μλ").assertExists() | ||
|
||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,80 @@ | ||
package nextstep.github.ui.github | ||
|
||
import android.util.Log | ||
import androidx.compose.foundation.layout.PaddingValues | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.lazy.LazyColumn | ||
import androidx.compose.foundation.lazy.items | ||
import androidx.compose.material3.CenterAlignedTopAppBar | ||
import androidx.compose.material3.Divider | ||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.HorizontalDivider | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.material3.SnackbarHost | ||
import androidx.compose.material3.SnackbarHostState | ||
import androidx.compose.material3.SnackbarResult | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.LaunchedEffect | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.rememberCoroutineScope | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.platform.LocalContext | ||
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 kotlinx.coroutines.launch | ||
import nextstep.github.R | ||
import nextstep.github.model.GithubRepo | ||
import nextstep.github.ui.github.component.GithubRepoItem | ||
import nextstep.github.ui.github.component.GithubRepoLoading | ||
import nextstep.github.ui.github.component.GithubRepoList | ||
import nextstep.github.ui.theme.GithubTheme | ||
|
||
@Composable | ||
fun GithubRepositoryListScreen( | ||
modifier: Modifier = Modifier, | ||
viewModel: GithubRepoViewModel = viewModel(factory = GithubRepoViewModel.Factory), | ||
) { | ||
val repositoryList by viewModel.githubRepositories.collectAsStateWithLifecycle() | ||
GithubRepositoryListScreen(repositories = repositoryList) | ||
val uiState by viewModel.githubUiState.collectAsStateWithLifecycle() | ||
val context = LocalContext.current | ||
val snackBarHostState = remember { SnackbarHostState() } | ||
val coroutineScope = rememberCoroutineScope() | ||
|
||
LaunchedEffect(true) { | ||
viewModel.errorFlow.collect { | ||
coroutineScope.launch { | ||
val snackBarResult = snackBarHostState.showSnackbar( | ||
message = context.getString(R.string.error_message), | ||
actionLabel = context.getString(R.string.error_retry) | ||
) | ||
if (snackBarResult == SnackbarResult.ActionPerformed) viewModel.retryGithubRepo() | ||
} | ||
} | ||
} | ||
GithubRepositoryListScreen( | ||
uiState = uiState, | ||
snackBarHostState = snackBarHostState, | ||
) | ||
} | ||
|
||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Composable | ||
private fun GithubRepositoryListScreen( | ||
repositories: List<GithubRepo>, | ||
fun GithubRepositoryListScreen( | ||
uiState: GithubRepoUiState, | ||
snackBarHostState: SnackbarHostState, | ||
modifier: Modifier = Modifier | ||
) { | ||
Scaffold( | ||
topBar = { | ||
CenterAlignedTopAppBar( | ||
title = { Text("NEXTSTEP Repositories") } | ||
title = { Text(stringResource(R.string.top_bar_title)) } | ||
) | ||
} | ||
}, | ||
snackbarHost = { SnackbarHost(hostState = snackBarHostState)} | ||
) { | ||
LazyColumn( | ||
modifier = modifier | ||
.fillMaxSize() | ||
.padding(it), | ||
) { | ||
items(repositories) {repo -> | ||
GithubRepoItem(githubRepo = repo) | ||
HorizontalDivider() | ||
} | ||
if (uiState.isLoading) { | ||
GithubRepoLoading(modifier = Modifier.padding(it)) | ||
} | ||
GithubRepoList( | ||
modifier = Modifier.padding(it), | ||
repositories = uiState.repositories | ||
) | ||
} | ||
} | ||
|
||
|
@@ -63,14 +83,50 @@ private fun GithubRepositoryListScreen( | |
private fun GithubRepositoryListScreenPreview() { | ||
GithubTheme { | ||
GithubRepositoryListScreen( | ||
repositories = listOf( | ||
GithubRepo(1, "NextStep/Test", "ν μ€νΈ μ μ₯μ"), | ||
GithubRepo(2, "NextStep/Test2", "ν μ€νΈ μ μ₯μ2"), | ||
GithubRepo(3, "NextStep/Test3", "ν μ€νΈ μ μ₯μ3"), | ||
GithubRepo(4, "NextStep/Test4", "ν μ€νΈ μ μ₯μ4"), | ||
GithubRepo(5, "NextStep/Test5", "ν μ€νΈ μ μ₯μ5"), | ||
GithubRepo(6, "NextStep/Test6", "ν μ€νΈ μ μ₯μ6"), | ||
uiState = GithubRepoUiState( | ||
isLoading = false, | ||
repositories = listOf( | ||
GithubRepo(1, "NextStep/Test", "ν μ€νΈ μ μ₯μ"), | ||
GithubRepo(2, "NextStep/Test2", "ν μ€νΈ μ μ₯μ2"), | ||
GithubRepo(3, "NextStep/Test3", "ν μ€νΈ μ μ₯μ3"), | ||
GithubRepo(4, "NextStep/Test4", "ν μ€νΈ μ μ₯μ4"), | ||
GithubRepo(5, "NextStep/Test5", "ν μ€νΈ μ μ₯μ5"), | ||
GithubRepo(6, "NextStep/Test6", "ν μ€νΈ μ μ₯μ6"), | ||
), | ||
), | ||
snackBarHostState = SnackbarHostState() | ||
) | ||
} | ||
} | ||
|
||
@Preview(showBackground = true) | ||
@Composable | ||
private fun GithubRepositoryListScreenErrorPreview() { | ||
val coroutineScope = rememberCoroutineScope() | ||
val snackBarHostState = remember { SnackbarHostState() } | ||
LaunchedEffect(Unit) { | ||
coroutineScope.launch { | ||
snackBarHostState.showSnackbar( | ||
message = "μλ¬ λ©μΈμ§", | ||
actionLabel = "μ¬μλ" | ||
) | ||
} | ||
} | ||
Comment on lines
+105
to
+114
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μ κΈ°μ€μ 무μμΈκ°μ :) μ΄λ²€νΈλ€μ ui ν μ€νΈλ‘ μμ±ν΄λ³΄λ©΄ μ΄λ¨κΉμ? :) |
||
GithubTheme { | ||
GithubRepositoryListScreen( | ||
uiState = GithubRepoUiState(isLoading = false), | ||
snackBarHostState = snackBarHostState | ||
) | ||
} | ||
} | ||
|
||
@Preview(showBackground = true) | ||
@Composable | ||
private fun GithubRepositoryListScreenLoadingPreview() { | ||
GithubTheme { | ||
GithubRepositoryListScreen( | ||
uiState = GithubRepoUiState(isLoading = true), | ||
snackBarHostState = SnackbarHostState() | ||
) | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
app/src/main/java/nextstep/github/ui/github/GithubRepoUiState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package nextstep.github.ui.github | ||
|
||
import nextstep.github.model.GithubRepo | ||
|
||
data class GithubRepoUiState ( | ||
val isLoading: Boolean = false, | ||
val repositories: List<GithubRepo> = emptyList(), | ||
) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
κ°μ μκ°μμλ λ€λ£¬ λ΄μ©μ΄κΈ΄νλ°,
https://medium.com/androiddevelopers/viewmodel-one-off-event-antipatterns-16a1da869b95
μ€λ³΄λμ μκ²¬μ΄ κΆκΈν©λλ€! (μκ²¬λ§ μ£Όμ λ μ’μκ±° κ°μμ!)