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

[AN/USER] 안드로이드 네비게이션을 적용한다. (#758) #781

Merged
merged 40 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
64e8759
feat: 학교 상세 도메인 모델 추가
EmilyCh0 Feb 9, 2024
8469319
feat: SchoolRepository 정의
EmilyCh0 Feb 9, 2024
5ef793a
feat: 테스트용 FakeSchool 추가
EmilyCh0 Feb 9, 2024
2aa3b2b
feat: SchoolDefaultRepository 임시 구현 (Fake 데이터 리턴)
EmilyCh0 Feb 9, 2024
61d0ffe
feat: 학교 상세 dto 추가
EmilyCh0 Feb 10, 2024
b48c2bf
feat: SchoolDetailUiState 정의
EmilyCh0 Feb 10, 2024
6eb05c6
feat: SchoolDetailViewModel 구현
EmilyCh0 Feb 10, 2024
d6cd393
feat: SchoolDetailFragment 구현
EmilyCh0 Feb 12, 2024
bc6da8e
feat: UiState 분리
EmilyCh0 Feb 12, 2024
d1f1f2b
feat: FakeSchool 소셜미디어 데이터 추가
EmilyCh0 Feb 17, 2024
f74935f
feat: SchoolDetailFragment 뷰 수정
EmilyCh0 Feb 17, 2024
5e724f6
move: 패키지 위치 변경
EmilyCh0 Feb 17, 2024
3832327
feat: 상단 뒤로 가기 버튼
EmilyCh0 Feb 18, 2024
cc91707
feat: schoolId 추가
EmilyCh0 Feb 18, 2024
a025518
fix: dday view 수정
EmilyCh0 Feb 18, 2024
b555447
feat: 북마크
EmilyCh0 Feb 20, 2024
60357d6
Merge branch 'dev' into feat/#697
EmilyCh0 Feb 20, 2024
2a85266
fix: resolve conflict
EmilyCh0 Feb 20, 2024
702afb4
refactor: ktlint check
EmilyCh0 Feb 20, 2024
48848ad
refactor: ktlint check
EmilyCh0 Feb 20, 2024
7a30d1c
refactor: Fake 리포지토리명 변경
EmilyCh0 Feb 24, 2024
f1c5aec
refactor: id 카멜케이스로 수정
EmilyCh0 Feb 24, 2024
52824ad
refactor: FakeSchoolRepository 주석 삭제
EmilyCh0 Feb 24, 2024
99f08bc
chore(presentation): 네비게이션 의존성 추가
re4rk Feb 24, 2024
99f71f4
feat(home): 네비게이션 그래프 추가
re4rk Feb 24, 2024
15d7e1e
chore(presentation): safeargs 추가
re4rk Feb 25, 2024
a20b566
feat(presentation): 컨트롤러 사용하도록 추가
re4rk Feb 25, 2024
3c508c6
feat(presentation): 탭마다 스택이 쌓이도록 변경
re4rk Feb 25, 2024
924436c
feat(presentation): 네비게이션 적용
re4rk Feb 25, 2024
5bebc61
Merge remote-tracking branch 'origin/dev' into feat/#758
re4rk Mar 5, 2024
160cd19
Merge remote-tracking branch 'origin/dev' into feat/#758
re4rk Mar 8, 2024
a1618b0
refactor(Artist): 네비게이션 시 리프래시 되지 않도록 수정
re4rk Mar 8, 2024
aef1700
refactor(FestivalDetail): 네비게이션 시 리프래시 되지 않도록 수정
re4rk Mar 8, 2024
722c78b
refactor(FestivalDetail): 불필요한 코드 제거
re4rk Mar 9, 2024
1e5d425
refactor(FestivalDetail): 불필요한 코드 제거
re4rk Mar 9, 2024
9794fe7
refactor(SchoolDetail): 사용하지 않는 모듈 제거
re4rk Mar 13, 2024
9f720ad
refactor(presentation): 불필요한 바인드 분기 제거
re4rk Mar 13, 2024
89b9b80
refactor(home): 화면마다 변경되게 변경
re4rk Mar 13, 2024
18973de
refactor(home): 화면 이동이 모두 백스택에 쌓이도록 변경
re4rk Mar 13, 2024
ac3c717
feat: 축제 목록에서 축제 상세 이동 시 애니메이션 추가
SeongHoonC Mar 13, 2024
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
2 changes: 2 additions & 0 deletions android/festago/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ plugins {
id("com.google.dagger.hilt.android") version "2.44" apply false

id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10" apply false

id("androidx.navigation.safeargs") version "2.5.3" apply false
}

allprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class FakeArtistRepository @Inject constructor() : ArtistRepository {
"예시 페스티벌 1",
LocalDate.parse("2024-05-01"),
LocalDate.parse("2024-05-03"),
"https://source.unsplash.com/random/300×${300 + index++}",
"https://source.unsplash.com/random/300×${300}",
School(
1,
"예시 학교",
Expand Down
2 changes: 1 addition & 1 deletion android/festago/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
android.defaults.buildfeatures.buildconfig=true
7 changes: 7 additions & 0 deletions android/festago/presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
id("kotlin-kapt")
id("org.jlleitschuh.gradle.ktlint")
id("com.google.dagger.hilt.android")
id("androidx.navigation.safeargs")
}

android {
Expand Down Expand Up @@ -54,6 +55,12 @@ dependencies {
implementation(project(":common"))
implementation(project(":domain"))

// Feature module Support
implementation("androidx.navigation:navigation-dynamic-features-fragment:2.7.7")
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")

// Testing Navigation
androidTestImplementation("androidx.navigation:navigation-testing:2.7.7")
// android
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.6.1")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.festago.festago.presentation.ui.artistdetail

sealed interface ArtistDetailEvent {
class ShowArtistDetail(val artistId: Long) : ArtistDetailEvent

class ShowFestivalDetail(val festivalId: Long) : ArtistDetailEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import com.festago.festago.presentation.R
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.festago.festago.presentation.databinding.FragmentArtistDetailBinding
import com.festago.festago.presentation.databinding.ItemMediaBinding
import com.festago.festago.presentation.ui.artistdetail.adapter.festival.ArtistDetailAdapter
Expand All @@ -25,14 +24,9 @@ class ArtistDetailFragment : Fragment() {

private val vm: ArtistDetailViewModel by viewModels()

private val adapter = ArtistDetailAdapter { artistId ->
// TODO: Navigation으로 변경
requireActivity().supportFragmentManager.commit {
add(R.id.fcvHomeContainer, newInstance(artistId))
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN)
addToBackStack(null)
}
}
private val args: ArtistDetailFragmentArgs by navArgs()

private val adapter = ArtistDetailAdapter()

override fun onCreateView(
inflater: LayoutInflater,
Expand All @@ -45,8 +39,7 @@ class ArtistDetailFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val id = requireArguments().getLong(KEY_ID)
initView(id)
initView(args.artistId)
initObserve()
}

Expand All @@ -70,6 +63,11 @@ class ArtistDetailFragment : Fragment() {
updateUi(it)
}
}
repeatOnStarted(viewLifecycleOwner) {
vm.event.collect {
handleEvent(it)
}
}
}

private fun updateUi(uiState: ArtistDetailUiState) = when (uiState) {
Expand All @@ -95,6 +93,22 @@ class ArtistDetailFragment : Fragment() {
}
}

private fun handleEvent(event: ArtistDetailEvent) = when (event) {
is ArtistDetailEvent.ShowArtistDetail -> {
findNavController().navigate(
ArtistDetailFragmentDirections.actionArtistDetailFragmentSelf(event.artistId),
)
}

is ArtistDetailEvent.ShowFestivalDetail -> {
findNavController().navigate(
ArtistDetailFragmentDirections.actionArtistDetailFragmentToFestivalDetailFragment(
event.festivalId,
),
)
}
}

private fun startBrowser(url: String) {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
Expand All @@ -105,16 +119,4 @@ class ArtistDetailFragment : Fragment() {
_binding = null
super.onDestroyView()
}

companion object {
private const val KEY_ID = "ID"

fun newInstance(
id: Long,
) = ArtistDetailFragment().apply {
arguments = Bundle().apply {
putLong(KEY_ID, id)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistDetailUiSt
import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistUiState
import com.festago.festago.presentation.ui.artistdetail.uistate.FestivalItemUiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -18,11 +21,16 @@ import javax.inject.Inject
class ArtistDetailViewModel @Inject constructor(
private val artistRepository: ArtistRepository,
) : ViewModel() {
private val _event: MutableSharedFlow<ArtistDetailEvent> = MutableSharedFlow()
val event: SharedFlow<ArtistDetailEvent> = _event.asSharedFlow()

private val _uiState: MutableStateFlow<ArtistDetailUiState> =
MutableStateFlow(ArtistDetailUiState.Loading)
val uiState: StateFlow<ArtistDetailUiState> = _uiState.asStateFlow()

fun loadArtistDetail(id: Long) {
fun loadArtistDetail(id: Long, refresh: Boolean = false) {
if (!refresh && _uiState.value is ArtistDetailUiState.Success) return

viewModelScope.launch {
runCatching {
_uiState.value = ArtistDetailUiState.Success(
Expand All @@ -43,7 +51,21 @@ class ArtistDetailViewModel @Inject constructor(
startDate = it.startDate,
endDate = it.endDate,
artists = it.artists.map { artist ->
ArtistUiState(artist.id, artist.name, artist.imageUrl)
ArtistUiState(
id = artist.id,
name = artist.name,
imageUrl = artist.imageUrl,
onArtistDetailClick = { id ->
viewModelScope.launch {
_event.emit(ArtistDetailEvent.ShowArtistDetail(id))
}
},
)
},
onFestivalDetailClick = { id ->
viewModelScope.launch {
_event.emit(ArtistDetailEvent.ShowFestivalDetail(id))
}
},
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistUiState

class ArtistAdapter(
private val onArtistClick: (Long) -> Unit,
) : ListAdapter<ArtistUiState, ArtistViewHolder>(diffUtil) {
class ArtistAdapter() : ListAdapter<ArtistUiState, ArtistViewHolder>(diffUtil) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArtistViewHolder {
return ArtistViewHolder.of(parent, onArtistClick)
return ArtistViewHolder.of(parent)
}

override fun onBindViewHolder(holder: ArtistViewHolder, position: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,20 @@ import androidx.recyclerview.widget.RecyclerView
import com.festago.festago.presentation.databinding.ItemArtistDetailArtistBinding
import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistUiState

class ArtistViewHolder(
private val binding: ItemArtistDetailArtistBinding,
onArtistClick: (Long) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
binding.artist?.id?.let(onArtistClick)
}
}

class ArtistViewHolder(private val binding: ItemArtistDetailArtistBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: ArtistUiState) {
binding.artist = item
}

companion object {
fun of(parent: ViewGroup, onArtistClick: (Long) -> Unit): ArtistViewHolder {
fun of(parent: ViewGroup): ArtistViewHolder {
val binding = ItemArtistDetailArtistBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false,
)
return ArtistViewHolder(binding, onArtistClick)
return ArtistViewHolder(binding)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.festago.festago.presentation.ui.artistdetail.uistate.FestivalItemUiState

class ArtistDetailAdapter(
private val onArtistClick: (Long) -> Unit,
) : ListAdapter<FestivalItemUiState, ArtistDetailFestivalViewHolder>(diffUtil) {
class ArtistDetailAdapter : ListAdapter<FestivalItemUiState, ArtistDetailFestivalViewHolder>(diffUtil) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArtistDetailFestivalViewHolder {
return ArtistDetailFestivalViewHolder.of(parent, onArtistClick)
return ArtistDetailFestivalViewHolder.of(parent)
}

override fun onBindViewHolder(holder: ArtistDetailFestivalViewHolder, position: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ import com.festago.festago.presentation.ui.artistdetail.adapter.artistlist.Artis
import com.festago.festago.presentation.ui.artistdetail.uistate.FestivalItemUiState
import java.time.LocalDate

class ArtistDetailFestivalViewHolder(
private val binding: ItemArtistDetailFestivalBinding,
onArtistClick: (Long) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {
private val artistAdapter = ArtistAdapter(onArtistClick)
class ArtistDetailFestivalViewHolder(private val binding: ItemArtistDetailFestivalBinding) :
RecyclerView.ViewHolder(binding.root) {
private val artistAdapter = ArtistAdapter()

init {
binding.rvFestivalArtists.adapter = artistAdapter
Expand Down Expand Up @@ -89,13 +87,13 @@ class ArtistDetailFestivalViewHolder(
}

companion object {
fun of(parent: ViewGroup, onArtistClick: (Long) -> Unit): ArtistDetailFestivalViewHolder {
fun of(parent: ViewGroup): ArtistDetailFestivalViewHolder {
val binding = ItemArtistDetailFestivalBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false,
)
return ArtistDetailFestivalViewHolder(binding, onArtistClick)
return ArtistDetailFestivalViewHolder(binding)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ data class ArtistUiState(
val id: Long,
val name: String,
val imageUrl: String,
val onArtistDetailClick: (Long) -> Unit,
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ data class FestivalItemUiState(
val endDate: LocalDate,
val imageUrl: String,
val artists: List<ArtistUiState>,
val onFestivalDetailClick: (Long) -> Unit,
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.festago.festago.presentation.R
import com.festago.festago.presentation.databinding.FragmentFestivalDetailBinding
import com.festago.festago.presentation.databinding.ItemMediaBinding
import com.festago.festago.presentation.ui.artistdetail.ArtistDetailFragment
import com.festago.festago.presentation.ui.festivaldetail.adapter.stage.StageListAdapter
import com.festago.festago.presentation.ui.festivaldetail.uiState.FestivalDetailUiState
import com.festago.festago.presentation.util.repeatOnStarted
Expand All @@ -25,12 +24,13 @@ import java.time.LocalDate

@AndroidEntryPoint
class FestivalDetailFragment : Fragment() {

private var _binding: FragmentFestivalDetailBinding? = null
private val binding get() = _binding!!

private val vm: FestivalDetailViewModel by viewModels()

private val args: FestivalDetailFragmentArgs by navArgs()

private lateinit var adapter: StageListAdapter

override fun onCreateView(
Expand All @@ -49,7 +49,7 @@ class FestivalDetailFragment : Fragment() {
}

private fun initView() {
val id = requireArguments().getLong(FESTIVAL_ID)
val id = args.festivalId
adapter = StageListAdapter()
binding.rvStageList.adapter = adapter
vm.loadFestivalDetail(id)
Expand Down Expand Up @@ -144,11 +144,11 @@ class FestivalDetailFragment : Fragment() {
private fun handleEvent(event: FestivalDetailEvent) {
when (event) {
is FestivalDetailEvent.ShowArtistDetail -> {
requireActivity().supportFragmentManager.commit {
add(R.id.fcvHomeContainer, ArtistDetailFragment.newInstance(event.artistId))
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN)
addToBackStack(null)
}
findNavController().navigate(
FestivalDetailFragmentDirections.actionFestivalDetailFragmentToArtistDetailFragment(
event.artistId,
),
)
}
}
}
Expand All @@ -157,14 +157,4 @@ class FestivalDetailFragment : Fragment() {
_binding = null
super.onDestroyView()
}

companion object {
private const val FESTIVAL_ID = "FESTIVAL_ID"

fun newInstance(id: Long) = FestivalDetailFragment().apply {
arguments = Bundle().apply {
putLong(FESTIVAL_ID, id)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ class FestivalDetailViewModel @Inject constructor(
private val _event = MutableSharedFlow<FestivalDetailEvent>()
val event: SharedFlow<FestivalDetailEvent> = _event.asSharedFlow()

fun loadFestivalDetail(festivalId: Long) {
fun loadFestivalDetail(festivalId: Long, refresh: Boolean = false) {
if (!refresh && _uiState.value is FestivalDetailUiState.Success) return

Comment on lines +37 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예전에 기억이 잘 안나서 그런데
새로고침 요청이 아니고 Success 라면 서버에 재요청하지 않으려고 막은거였죠?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 initView() 호출될때마다 불필요하게 load 다시하는 거 막으려고 했던거로 기억합니당

viewModelScope.launch {
festivalRepository.loadFestivalDetail(festivalId).onSuccess { festivalDetail ->
_uiState.value = festivalDetail.toSuccessUiState()
Expand Down
Loading
Loading