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

Step2 - GitHub(데이터 레이어) #35

Open
wants to merge 3 commits into
base: mystoryg
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# android-github-compose

## Step2 구현 목록

- [x] 저장소 목록을 보여주는 화면 구현
- [x] 상단바 추가
- [x] 저장소 목록 추가
- [x] 저장소 목록 데이터를 관리하는 ViewModel 구현
- [x] 저장소 목록 화면에 데이터 연동
- [x] 저장소 목록 화면 리팩터링
- [x] 피드백 반영

## Step2 진행 중 의식의 흐름
-

## Step1 구현 목록

- [x] retrofit 설정
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
android:theme="@style/Theme.Github"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".ui.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Github">
Expand Down
57 changes: 0 additions & 57 deletions app/src/main/java/nextstep/github/MainActivity.kt

This file was deleted.

5 changes: 2 additions & 3 deletions app/src/main/java/nextstep/github/data/NextStepRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ package nextstep.github.data
import nextstep.github.model.NextStepRepositoryEntity
import nextstep.github.network.NextStepGithubService

class NextStepRepository(private val nextStepGithubService: NextStepGithubService) :
NextStepGithubService {
override suspend fun getRepositories(organization: String): List<NextStepRepositoryEntity> {
class NextStepRepository(private val nextStepGithubService: NextStepGithubService) {
suspend fun getRepositories(organization: String): List<NextStepRepositoryEntity> {
return nextStepGithubService.getRepositories(organization)
}
}
31 changes: 31 additions & 0 deletions app/src/main/java/nextstep/github/ui/RepositoryListActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nextstep.github.ui

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import nextstep.github.ui.theme.GithubTheme
import nextstep.github.viewmodel.RepositoryListViewModel

class MainActivity : ComponentActivity() {
private val viewModel: RepositoryListViewModel by viewModels { RepositoryListViewModel.Factory }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
GithubTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
RepositoryListScreen(viewModel = viewModel)
}
}
}
}
}
112 changes: 112 additions & 0 deletions app/src/main/java/nextstep/github/ui/RepositoryListScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package nextstep.github.ui

import android.annotation.SuppressLint
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
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.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import nextstep.github.model.NextStepRepositoryEntity
import nextstep.github.viewmodel.RepositoryListViewModel

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter", "CoroutineCreationDuringComposition")
@Composable
fun RepositoryListScreen(viewModel: RepositoryListViewModel) {
val repositories by viewModel.repositories.collectAsState()

RepositoryListScreen(repositories = repositories)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RepositoryListScreen(repositories: List<NextStepRepositoryEntity>) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
text = "NEXTSTEP Repositories",
style = MaterialTheme.typography.titleLarge,
)
},
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
)
},
containerColor = MaterialTheme.colorScheme.background
) { innerPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = innerPadding
) {
items(repositories) { repository ->
RepositoryItem(repository = repository)
HorizontalDivider()
}
}
}
}

@Composable
fun RepositoryItem(repository: NextStepRepositoryEntity) {
Column(
modifier = Modifier
.padding(8.dp)
.background(MaterialTheme.colorScheme.surface)
) {
Text(
text = repository.fullName ?: "No name available",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
)
Text(
text = repository.description ?: "No description available",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
)
}
}

@Preview(showBackground = true, name = "Stateless 케이스")
@Composable
fun RepositoryListScreenPreview() {
RepositoryListScreen(
repositories = listOf(
NextStepRepositoryEntity(
fullName = "next-step/nextstep-study",
description = "NextStep의 자바 백엔드 스터디 저장소"
),
NextStepRepositoryEntity(
fullName = "next-step/nextstep-docs",
description = "NextStep의 공식 문서 저장소"
)
)
)
}

@Preview
@Composable
fun RepositoryItemPreview() {
RepositoryItem(
repository = NextStepRepositoryEntity(
fullName = "next-step/nextstep-study",
description = "NextStep의 자바 백엔드 스터디 저장소"
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package nextstep.github.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import nextstep.github.GitHubApplication
import nextstep.github.data.NextStepRepository
import nextstep.github.model.NextStepRepositoryEntity

class RepositoryListViewModel(private val nextStepRepository: NextStepRepository) : ViewModel() {

private val _repositories = MutableStateFlow<List<NextStepRepositoryEntity>>(emptyList())
val repositories: StateFlow<List<NextStepRepositoryEntity>> = _repositories.asStateFlow()

init {
viewModelScope.launch {
getRepositories("next-step")
}
}

private suspend fun getRepositories(organization: String) {
val repositoryEntities = nextStepRepository.getRepositories(organization)
_repositories.update { repositoryEntities }
}

companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val nextStepRepository = (this[APPLICATION_KEY] as GitHubApplication)
.appContainer
.nextStepRepository
RepositoryListViewModel(nextStepRepository)
}
}
}
}