@@ -2,10 +2,16 @@ package com.ninecraft.booket.feature.library
22
33import androidx.compose.runtime.Composable
44import androidx.compose.runtime.getValue
5+ import androidx.compose.runtime.mutableIntStateOf
56import androidx.compose.runtime.mutableStateOf
7+ import androidx.compose.runtime.rememberCoroutineScope
68import androidx.compose.runtime.setValue
9+ import com.ninecraft.booket.core.data.api.repository.BookRepository
10+ import com.ninecraft.booket.core.model.LibraryBookContentModel
11+ import com.ninecraft.booket.core.ui.component.FooterState
712import com.ninecraft.booket.feature.screens.LibraryScreen
813import com.ninecraft.booket.feature.screens.SettingsScreen
14+ import com.orhanobut.logger.Logger
915import com.slack.circuit.codegen.annotations.CircuitInject
1016import com.slack.circuit.retained.rememberRetained
1117import com.slack.circuit.runtime.Navigator
@@ -16,40 +22,77 @@ import dagger.assisted.AssistedInject
1622import dagger.hilt.android.components.ActivityRetainedComponent
1723import kotlinx.collections.immutable.persistentListOf
1824import kotlinx.collections.immutable.toPersistentList
25+ import kotlinx.coroutines.launch
1926
2027class LibraryPresenter @AssistedInject constructor(
2128 @Assisted private val navigator : Navigator ,
29+ private val repository : BookRepository ,
2230) : Presenter<LibraryUiState> {
31+ companion object {
32+ private const val PAGE_SIZE = 10
33+ private const val START_INDEX = 0
34+ }
2335
2436 @Composable
2537 override fun present (): LibraryUiState {
26- var isLoading by rememberRetained { mutableStateOf(false ) }
38+ val scope = rememberCoroutineScope()
39+
40+ var uiState by rememberRetained { mutableStateOf<UiState >(UiState .Idle ) }
41+ var footerState by rememberRetained { mutableStateOf<FooterState >(FooterState .Idle ) }
42+ var filterChips by rememberRetained {
43+ mutableStateOf(LibraryFilterOption .entries.map { LibraryFilterChip (option = it, count = 0 ) }.toPersistentList())
44+ }
45+ var currentFilter by rememberRetained { mutableStateOf(LibraryFilterOption .TOTAL ) }
46+ var books by rememberRetained { mutableStateOf(persistentListOf<LibraryBookContentModel >()) }
2747 var sideEffect by rememberRetained { mutableStateOf<LibrarySideEffect ?>(null ) }
28- var chipElements by rememberRetained {
29- mutableStateOf(
30- persistentListOf(
31- FilterChipState (
32- title = BookStatus .TOTAL ,
33- count = 10 ,
34- isSelected = true ,
35- ),
36- FilterChipState (
37- title = BookStatus .BEFORE_READING ,
38- count = 15 ,
39- isSelected = false ,
40- ),
41- FilterChipState (
42- title = BookStatus .READING ,
43- count = 2 ,
44- isSelected = false ,
45- ),
46- FilterChipState (
47- title = BookStatus .COMPLETED ,
48- count = 5 ,
49- isSelected = false ,
50- ),
51- ),
52- )
48+
49+ var currentPage by rememberRetained { mutableIntStateOf(START_INDEX ) }
50+ var isLastPage by rememberRetained { mutableStateOf(false ) }
51+
52+ fun getLibraryBooks (status : String? , page : Int , size : Int ) {
53+ scope.launch {
54+ if (page == START_INDEX ) {
55+ uiState = UiState .Loading
56+ } else {
57+ footerState = FooterState .Loading
58+ }
59+
60+ repository.getLibrary(status = status, page = page, size = size)
61+ .onSuccess { result ->
62+ filterChips = filterChips.map { chip ->
63+ when (chip.option) {
64+ LibraryFilterOption .TOTAL -> chip.copy(count = result.totalCount)
65+ LibraryFilterOption .BEFORE_READING -> chip.copy(count = result.beforeReadingCount)
66+ LibraryFilterOption .READING -> chip.copy(count = result.readingCount)
67+ LibraryFilterOption .COMPLETED -> chip.copy(count = result.completedCount)
68+ }
69+ }.toPersistentList()
70+
71+ books = if (result.books.page.number == START_INDEX ) {
72+ result.books.content.toPersistentList()
73+ } else {
74+ (books + result.books.content).toPersistentList()
75+ }
76+
77+ currentPage = page
78+ isLastPage = result.books.page.number == result.books.page.totalPages - 1
79+
80+ if (page == START_INDEX ) {
81+ uiState = UiState .Success
82+ } else {
83+ footerState = if (isLastPage) FooterState .End else FooterState .Idle
84+ }
85+ }
86+ .onFailure { exception ->
87+ Logger .d(exception)
88+ val errorMessage = exception.message ? : " 알 수 없는 오류가 발생했습니다."
89+ if (page == START_INDEX ) {
90+ uiState = UiState .Error (errorMessage)
91+ } else {
92+ footerState = FooterState .Error (errorMessage)
93+ }
94+ }
95+ }
5396 }
5497
5598 fun handleEvent (event : LibraryUiEvent ) {
@@ -63,21 +106,36 @@ class LibraryPresenter @AssistedInject constructor(
63106 }
64107
65108 is LibraryUiEvent .OnFilterClick -> {
66- chipElements = chipElements.map {
67- if (it.title == event.bookStatus) {
68- it.copy(isSelected = true )
69- } else {
70- it.copy(isSelected = false )
71- }
72- }.toPersistentList()
73- // TODO: 필터에 해당하는 도서 목록을 불러오는 로직이 들어가야 함
109+ currentFilter = event.filterOption
110+ getLibraryBooks(status = currentFilter.getApiValue(), page = START_INDEX , size = PAGE_SIZE )
111+ }
112+
113+ is LibraryUiEvent .OnBookClick -> {
114+ // TODO: 상세 화면으로 이동
115+ }
116+
117+ is LibraryUiEvent .OnLoadMore -> {
118+ if (footerState !is FooterState .Loading && ! isLastPage) {
119+ getLibraryBooks(status = currentFilter.getApiValue(), page = currentPage + 1 , size = PAGE_SIZE )
120+ }
121+ }
122+
123+ is LibraryUiEvent .OnRetryClick -> {
124+ if (currentPage == START_INDEX ) {
125+ getLibraryBooks(status = currentFilter.getApiValue(), page = currentPage, size = PAGE_SIZE )
126+ } else {
127+ getLibraryBooks(status = currentFilter.getApiValue(), page = currentPage + 1 , size = PAGE_SIZE )
128+ }
74129 }
75130 }
76131 }
77132
78133 return LibraryUiState (
79- isLoading = isLoading,
80- filterElements = chipElements,
134+ uiState = uiState,
135+ footerState = footerState,
136+ filterChips = filterChips,
137+ currentFilter = currentFilter,
138+ books = books,
81139 sideEffect = sideEffect,
82140 eventSink = ::handleEvent,
83141 )
0 commit comments