Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.ninecraft.booket.core.data.api.repository

import com.ninecraft.booket.core.model.HomeModel

interface HomeRepository {
suspend fun getHome(): Result<HomeModel>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package com.ninecraft.booket.core.data.impl.di

import com.ninecraft.booket.core.data.api.repository.AuthRepository
import com.ninecraft.booket.core.data.api.repository.BookRepository
import com.ninecraft.booket.core.data.api.repository.HomeRepository
import com.ninecraft.booket.core.data.api.repository.RecordRepository
import com.ninecraft.booket.core.data.api.repository.UserRepository
import com.ninecraft.booket.core.data.impl.repository.DefaultAuthRepository
import com.ninecraft.booket.core.data.impl.repository.DefaultBookRepository
import com.ninecraft.booket.core.data.impl.repository.DefaultHomeRepository
import com.ninecraft.booket.core.data.impl.repository.DefaultRecordRepository
import com.ninecraft.booket.core.data.impl.repository.DefaultUserRepository
import dagger.Binds
Expand Down Expand Up @@ -33,4 +35,8 @@ internal abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindRecordRepository(defaultRecordRepository: DefaultRecordRepository): RecordRepository

@Binds
@Singleton
abstract fun bindHomeRepository(defaultHomeRepository: DefaultHomeRepository): HomeRepository
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@ import com.ninecraft.booket.core.model.BookDetailModel
import com.ninecraft.booket.core.model.BookSearchModel
import com.ninecraft.booket.core.model.BookSummaryModel
import com.ninecraft.booket.core.model.BookUpsertModel
import com.ninecraft.booket.core.model.HomeModel
import com.ninecraft.booket.core.model.LibraryBookSummaryModel
import com.ninecraft.booket.core.model.LibraryBooksModel
import com.ninecraft.booket.core.model.LibraryModel
import com.ninecraft.booket.core.model.PageInfoModel
import com.ninecraft.booket.core.model.RecentBookModel
import com.ninecraft.booket.core.model.RecordRegisterModel
import com.ninecraft.booket.core.model.UserProfileModel
import com.ninecraft.booket.core.network.response.BookDetailResponse
import com.ninecraft.booket.core.network.response.BookSearchResponse
import com.ninecraft.booket.core.network.response.BookSummary
import com.ninecraft.booket.core.network.response.BookUpsertResponse
import com.ninecraft.booket.core.network.response.HomeResponse
import com.ninecraft.booket.core.network.response.LibraryBookSummary
import com.ninecraft.booket.core.network.response.LibraryBooks
import com.ninecraft.booket.core.network.response.LibraryResponse
import com.ninecraft.booket.core.network.response.PageInfo
import com.ninecraft.booket.core.network.response.RecentBook
import com.ninecraft.booket.core.network.response.RecordRegisterResponse
import com.ninecraft.booket.core.network.response.UserProfileResponse

Expand Down Expand Up @@ -150,3 +154,21 @@ internal fun RecordRegisterResponse.toModel(): RecordRegisterModel {
updatedAt = updatedAt,
)
}

internal fun HomeResponse.toModel(): HomeModel {
return HomeModel(
recentBooks = recentBooks.map { it.toModel() },
)
}

internal fun RecentBook.toModel(): RecentBookModel {
return RecentBookModel(
userBookId = userBookId,
title = title,
author = author,
publisher = publisher,
coverImageUrl = coverImageUrl,
lastRecordedAt = lastRecordedAt,
recordCount = recordCount,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ninecraft.booket.core.data.impl.repository

import com.ninecraft.booket.core.common.utils.runSuspendCatching
import com.ninecraft.booket.core.data.api.repository.HomeRepository
import com.ninecraft.booket.core.data.impl.mapper.toModel
import com.ninecraft.booket.core.network.service.ReedService
import javax.inject.Inject

class DefaultHomeRepository @Inject constructor(
private val service: ReedService,
) : HomeRepository {
override suspend fun getHome() = runSuspendCatching {
service.getHome().toModel()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.ninecraft.booket.core.model

import androidx.compose.runtime.Stable

@Stable
data class HomeModel(
val recentBooks: List<RecentBookModel> = emptyList(),
)

@Stable
data class RecentBookModel(
val userBookId: String = "",
val title: String = "",
val author: String = "",
val publisher: String = "",
val coverImageUrl: String = "",
val lastRecordedAt: String = "",
val recordCount: Int = 0,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ninecraft.booket.core.network.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class HomeResponse(
@SerialName("recentBooks")
val recentBooks: List<RecentBook>,
)

@Serializable
data class RecentBook(
@SerialName("userBookId")
val userBookId: String,
@SerialName("title")
val title: String,
@SerialName("author")
val author: String,
@SerialName("publisher")
val publisher: String,
@SerialName("coverImageUrl")
val coverImageUrl: String,
@SerialName("lastRecordedAt")
val lastRecordedAt: String,
@SerialName("recordCount")
val recordCount: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.ninecraft.booket.core.network.request.TermsAgreementRequest
import com.ninecraft.booket.core.network.response.BookDetailResponse
import com.ninecraft.booket.core.network.response.BookSearchResponse
import com.ninecraft.booket.core.network.response.BookUpsertResponse
import com.ninecraft.booket.core.network.response.HomeResponse
import com.ninecraft.booket.core.network.response.LibraryResponse
import com.ninecraft.booket.core.network.response.LoginResponse
import com.ninecraft.booket.core.network.response.RecordRegisterResponse
Expand Down Expand Up @@ -76,4 +77,10 @@ interface ReedService {
@Path("userBookId") userBookId: String,
@Body recordRegisterRequest: RecordRegisterRequest,
): RecordRegisterResponse

// Home (auth required)
@GET("api/v1/home")
suspend fun getHome(
@Query("limit") limit: Int = 3,
): HomeResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.ninecraft.booket.feature.home

import android.widget.Toast
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.skydoves.compose.effects.RememberedEffect

@Composable
internal fun HandleHomeSideEffects(
state: HomeUiState,
) {
val context = LocalContext.current

RememberedEffect(state.sideEffect) {
when (state.sideEffect) {
is HomeSideEffect.ShowToast -> {
Toast.makeText(context, state.sideEffect.message, Toast.LENGTH_SHORT).show()
}

null -> {}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,65 @@
package com.ninecraft.booket.feature.home

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import com.ninecraft.booket.core.common.utils.handleException
import com.ninecraft.booket.core.data.api.repository.HomeRepository
import com.ninecraft.booket.core.model.RecentBookModel
import com.ninecraft.booket.feature.screens.BookDetailScreen
import com.ninecraft.booket.feature.screens.HomeScreen
import com.ninecraft.booket.feature.screens.LoginScreen
import com.ninecraft.booket.feature.screens.RecordScreen
import com.ninecraft.booket.feature.screens.SearchScreen
import com.ninecraft.booket.feature.screens.SettingsScreen
import com.orhanobut.logger.Logger
import com.slack.circuit.codegen.annotations.CircuitInject
import com.slack.circuit.retained.rememberRetained
import com.slack.circuit.runtime.Navigator
import com.slack.circuit.runtime.presenter.Presenter
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.components.ActivityRetainedComponent
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.launch

@Suppress("unused")
class HomePresenter @AssistedInject constructor(
@Assisted private val navigator: Navigator,
private val repository: HomeRepository,
) : Presenter<HomeUiState> {

@Composable
override fun present(): HomeUiState {
val scope = rememberCoroutineScope()
var sideEffect by rememberRetained { mutableStateOf<HomeSideEffect?>(null) }
var recentBooks by rememberRetained { mutableStateOf(persistentListOf<RecentBookModel>()) }

fun getHome() {
scope.launch {
repository.getHome()
.onSuccess { result ->
recentBooks = result.recentBooks.toPersistentList()
}.onFailure { exception ->
val handleErrorMessage = { message: String ->
Logger.e(message)
sideEffect = HomeSideEffect.ShowToast(message)
}

handleException(
exception = exception,
onError = handleErrorMessage,
onLoginRequired = {
navigator.resetRoot(LoginScreen)
},
)
}
}
}

fun handleEvent(event: HomeUiEvent) {
when (event) {
Expand All @@ -35,7 +72,7 @@ class HomePresenter @AssistedInject constructor(
}

is HomeUiEvent.OnRecordButtonClick -> {
navigator.goTo(RecordScreen(""))
navigator.goTo(RecordScreen(event.userBookId))
}

is HomeUiEvent.OnBookDetailClick -> {
Expand All @@ -44,7 +81,12 @@ class HomePresenter @AssistedInject constructor(
}
}

LaunchedEffect(true) {
getHome()
}

return HomeUiState(
recentBooks = recentBooks,
eventSink = ::handleEvent,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.ninecraft.booket.core.designsystem.theme.HomeBg
import com.ninecraft.booket.core.designsystem.theme.ReedTheme
import com.ninecraft.booket.core.designsystem.theme.White
import com.ninecraft.booket.feature.home.component.BookCard
import com.ninecraft.booket.feature.home.component.EmptyBookCard
import com.ninecraft.booket.feature.home.component.HomeBanner
import com.ninecraft.booket.feature.home.component.HomeHeader
import com.ninecraft.booket.feature.screens.HomeScreen
Expand All @@ -40,6 +41,8 @@ internal fun HomeUi(
state: HomeUiState,
modifier: Modifier = Modifier,
) {
HandleHomeSideEffects(state = state)

Column(
modifier = modifier.fillMaxSize(),
) {
Expand Down Expand Up @@ -83,11 +86,6 @@ internal fun HomeContent(
state: HomeUiState,
modifier: Modifier = Modifier,
) {
val dummyBooks = listOf(
Book("여름은 오래 그곳에 남아", "마쓰이에 마사시", "비채", "https://image.aladin.co.kr/product/7492/9/cover200/8934972203_1.jpg", 3),
Book("여름은 오래 그곳에 남아", "마쓰이에 마사시", "비채", "https://image.aladin.co.kr/product/7492/9/cover200/8934972203_1.jpg", 3),
Book("여름은 오래 그곳에 남아", "마쓰이에 마사시", "비채", "https://image.aladin.co.kr/product/7492/9/cover200/8934972203_1.jpg", 3),
)
Column(
modifier = modifier
.fillMaxSize()
Expand All @@ -101,38 +99,49 @@ internal fun HomeContent(
style = ReedTheme.typography.headline2Medium,
)
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing3))
val pagerState = rememberPagerState(pageCount = { dummyBooks.size })
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = ReedTheme.spacing.spacing5),
pageSpacing = ReedTheme.spacing.spacing5,
) { page ->
BookCard(
bookInfo = dummyBooks[page],
onBookDetailClick = {
state.eventSink(HomeUiEvent.OnBookDetailClick)
},
onRecordButtonClick = {
state.eventSink(HomeUiEvent.OnRecordButtonClick)

if (state.recentBooks.isEmpty()) {
EmptyBookCard(
onBookRegisterClick = {
state.eventSink(HomeUiEvent.OnBookRegisterClick)
},
modifier = Modifier.padding(ReedTheme.spacing.spacing5),
)
}
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing5))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
repeat(pagerState.pageCount) { iteration ->
val color = if (pagerState.currentPage == iteration) ReedTheme.colors.bgPrimary else ReedTheme.colors.bgSecondaryPressed
Box(
modifier = Modifier
.size(12.dp)
.padding(3.dp)
.clip(CircleShape)
.background(color),
} else {
val pagerState = rememberPagerState(pageCount = { state.recentBooks.size })

HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = ReedTheme.spacing.spacing5),
pageSpacing = ReedTheme.spacing.spacing5,
) { page ->
BookCard(
recentBookInfo = state.recentBooks[page],
onBookDetailClick = {
state.eventSink(HomeUiEvent.OnBookDetailClick)
},
onRecordButtonClick = {
state.eventSink(HomeUiEvent.OnRecordButtonClick(state.recentBooks[page].userBookId))
},
)
}
Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing5))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
repeat(pagerState.pageCount) { iteration ->
val color = if (pagerState.currentPage == iteration) ReedTheme.colors.bgPrimary else ReedTheme.colors.bgSecondaryPressed
Box(
modifier = Modifier
.size(12.dp)
.padding(3.dp)
.clip(CircleShape)
.background(color),
)
}
}
}
}
}
Expand Down
Loading
Loading