Skip to content

Commit f57a2be

Browse files
committed
Add SwipeRefresh for bookmark screen
1 parent 17cd7f7 commit f57a2be

8 files changed

Lines changed: 127 additions & 14 deletions

File tree

app/src/main/java/com/sample/tmdb/ui/TMDbApp.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ private fun NavGraphBuilder.navigationScreens(navController: NavController, scaf
179179
hiltViewModel(),
180180
{ navController.navigate("${MainDestinations.TMDB_MOVIE_DETAIL_ROUTE}/${it.id}") },
181181
{ navController.navigate("${MainDestinations.TMDB_TV_SHOW_DETAIL_ROUTE}/${it.id}") },
182+
scaffoldState,
182183
)
183184
}
184185
composable(route = HomeSections.SETTING_SECTION.route) {

common/src/main/java/com/sample/tmdb/common/base/BaseRepository.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import kotlinx.coroutines.flow.Flow
88
abstract class BaseRepository<T>(context: Context, ioDispatcher: CoroutineDispatcher) :
99
CoreBaseRepository<T>(context, ioDispatcher) {
1010

11-
protected abstract suspend fun getSuccessResult(): T
11+
protected abstract suspend fun getSuccessResult(isRefreshing: Boolean = false): T
1212

1313
fun getResult(isRefreshing: Boolean = false): Flow<Async<T>> = execute(isRefreshing) {
14-
getSuccessResult()
14+
getSuccessResult(isRefreshing)
1515
}
1616
}

core/data/src/main/java/com/sample/tmdb/data/repository/movie/detail/BookmarkMovieRepository.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,43 @@ package com.sample.tmdb.data.repository.movie.detail
33
import android.content.Context
44
import com.sample.tmdb.common.base.BaseRepository
55
import com.sample.tmdb.data.di.IoDispatcher
6+
import com.sample.tmdb.data.network.MovieService
7+
import com.sample.tmdb.data.response.asMovieDomainModel
8+
import com.sample.tmdb.data.source.entity.asDatabaseModel
69
import com.sample.tmdb.data.source.entity.asDomainModel
710
import com.sample.tmdb.data.source.local.MovieDao
811
import com.sample.tmdb.domain.model.Movie
912
import dagger.hilt.android.qualifiers.ApplicationContext
1013
import javax.inject.Inject
1114
import javax.inject.Singleton
1215
import kotlinx.coroutines.CoroutineDispatcher
16+
import kotlinx.coroutines.async
17+
import kotlinx.coroutines.awaitAll
18+
import kotlinx.coroutines.coroutineScope
1319

1420
@Singleton
1521
class BookmarkMovieRepository @Inject constructor(
1622
private val movieDao: MovieDao,
23+
private val movieApi: MovieService,
1724
@ApplicationContext context: Context,
1825
@IoDispatcher ioDispatcher: CoroutineDispatcher,
1926
) : BaseRepository<List<@JvmSuppressWildcards Movie>>(context, ioDispatcher) {
20-
override suspend fun getSuccessResult(): List<Movie> = movieDao.getBookmarks().asDomainModel()
27+
28+
override suspend fun getSuccessResult(isRefreshing: Boolean): List<Movie> {
29+
if (isRefreshing) {
30+
coroutineScope {
31+
val jobs = movieDao.getBookmarks().asDomainModel().map { movie ->
32+
async {
33+
movieDao.addBookmark(
34+
movieApi.fetchMovieDetail(movie.id)
35+
.asMovieDomainModel()
36+
.asDatabaseModel(),
37+
)
38+
}
39+
}
40+
jobs.awaitAll()
41+
}
42+
}
43+
return movieDao.getBookmarks().asDomainModel()
44+
}
2145
}

core/data/src/main/java/com/sample/tmdb/data/repository/tvshow/detail/BookmarkTVShowRepository.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,43 @@ package com.sample.tmdb.data.repository.tvshow.detail
33
import android.content.Context
44
import com.sample.tmdb.common.base.BaseRepository
55
import com.sample.tmdb.data.di.IoDispatcher
6+
import com.sample.tmdb.data.network.TVShowService
7+
import com.sample.tmdb.data.response.asTVShowDomainModel
8+
import com.sample.tmdb.data.source.entity.asDatabaseModel
69
import com.sample.tmdb.data.source.entity.asDomainModel
710
import com.sample.tmdb.data.source.local.TVShowDao
811
import com.sample.tmdb.domain.model.TVShow
912
import dagger.hilt.android.qualifiers.ApplicationContext
1013
import javax.inject.Inject
1114
import javax.inject.Singleton
1215
import kotlinx.coroutines.CoroutineDispatcher
16+
import kotlinx.coroutines.async
17+
import kotlinx.coroutines.awaitAll
18+
import kotlinx.coroutines.coroutineScope
1319

1420
@Singleton
1521
class BookmarkTVShowRepository @Inject constructor(
1622
private val tvShowDao: TVShowDao,
23+
private val tvShowApi: TVShowService,
1724
@ApplicationContext context: Context,
1825
@IoDispatcher ioDispatcher: CoroutineDispatcher,
1926
) : BaseRepository<List<@JvmSuppressWildcards TVShow>>(context, ioDispatcher) {
20-
override suspend fun getSuccessResult(): List<TVShow> = tvShowDao.getBookmarks().asDomainModel()
27+
28+
override suspend fun getSuccessResult(isRefreshing: Boolean): List<TVShow> {
29+
if (isRefreshing) {
30+
coroutineScope {
31+
val jobs = tvShowDao.getBookmarks().asDomainModel().map { tvShow ->
32+
async {
33+
tvShowDao.addBookmark(
34+
tvShowApi.fetchTVSeriesDetail(tvShow.id)
35+
.asTVShowDomainModel()
36+
.asDatabaseModel(),
37+
)
38+
}
39+
}
40+
jobs.awaitAll()
41+
}
42+
}
43+
return tvShowDao.getBookmarks().asDomainModel()
44+
}
2145
}

core/data/src/main/java/com/sample/tmdb/data/response/DetailDTO.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import com.sample.tmdb.common.utils.Constants.TITLE
1313
import com.sample.tmdb.common.utils.Constants.VOTE_AVERAGE
1414
import com.sample.tmdb.common.utils.Constants.VOTE_COUNT
1515
import com.sample.tmdb.domain.model.Genre
16+
import com.sample.tmdb.domain.model.Movie
1617
import com.sample.tmdb.domain.model.MovieDetails
1718
import com.sample.tmdb.domain.model.SpokenLanguage
19+
import com.sample.tmdb.domain.model.TVShow
1820
import com.sample.tmdb.domain.model.TVShowDetails
1921
import com.squareup.moshi.Json
2022
import com.squareup.moshi.JsonClass
@@ -142,6 +144,28 @@ fun TvDetailResponse.asDomainModel(): TVShowDetails = TVShowDetails(
142144
voteCount,
143145
)
144146

147+
fun MovieDetailResponse.asMovieDomainModel() = Movie(
148+
id = id,
149+
overview = overview,
150+
releaseDate = releaseDate,
151+
posterUrl = posterPath?.let { String.format(BASE_WIDTH_342_PATH, it) },
152+
backdropUrl = backdropPath?.let { String.format(BASE_WIDTH_342_PATH, it) },
153+
name = title,
154+
voteAverage = voteAverage,
155+
voteCount = voteCount,
156+
)
157+
158+
fun TvDetailResponse.asTVShowDomainModel() = TVShow(
159+
id = id,
160+
overview = overview,
161+
releaseDate = releaseDate,
162+
posterUrl = posterPath?.let { String.format(BASE_WIDTH_342_PATH, it) },
163+
backdropUrl = backdropPath?.let { String.format(BASE_WIDTH_342_PATH, it) },
164+
name = title,
165+
voteAverage = voteAverage,
166+
voteCount = voteCount,
167+
)
168+
145169
private fun List<GenreResponse>.asGenreDomainModel(): List<Genre> = map {
146170
Genre(it.id, it.name)
147171
}

core/domain/src/main/java/com/sample/tmdb/domain/repository/BaseFeedRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ abstract class BaseFeedRepository<T : TMDbItem>(context: Context, ioDispatcher:
2929

3030
protected abstract fun getLatestResId(): Int
3131

32-
override suspend fun getSuccessResult(): List<FeedWrapper> {
32+
override suspend fun getSuccessResult(isRefreshing: Boolean): List<FeedWrapper> {
3333
val trendingDeferred: Deferred<List<T>>
3434
val nowPlayingDeferred: Deferred<List<T>>
3535
val popularDeferred: Deferred<List<T>>

features/feature-bookmark/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ dependencies {
5050
implementation(Deps.composeUi)
5151
implementation(Deps.composeFoundation)
5252
implementation(Deps.composeMaterial)
53+
implementation(Deps.swiperefresh)
5354
testImplementation(project(BuildModules.COMMON_TEST))
5455
testImplementation(Deps.junit4)
5556
testImplementation(Deps.mockk)

features/feature-bookmark/src/main/java/com/sample/tmdb/bookmark/BookmarkScreen.kt

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
1616
import androidx.compose.foundation.pager.HorizontalPager
1717
import androidx.compose.foundation.pager.rememberPagerState
1818
import androidx.compose.material.MaterialTheme
19+
import androidx.compose.material.ScaffoldState
1920
import androidx.compose.material.Tab
2021
import androidx.compose.material.TabRow
2122
import androidx.compose.material.Text
@@ -31,6 +32,10 @@ import androidx.compose.ui.res.stringResource
3132
import androidx.compose.ui.text.font.FontWeight
3233
import androidx.compose.ui.text.style.TextAlign
3334
import androidx.compose.ui.unit.dp
35+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
36+
import com.google.accompanist.swiperefresh.SwipeRefresh
37+
import com.google.accompanist.swiperefresh.SwipeRefreshIndicator
38+
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
3439
import com.sample.tmdb.common.R as commonR
3540
import com.sample.tmdb.common.base.BaseViewModel
3641
import com.sample.tmdb.common.model.TMDbItem
@@ -52,6 +57,7 @@ fun BookmarkScreen(
5257
tvShowViewModel: BookmarkTVShowViewModel,
5358
onMovieClicked: (TMDbItem) -> Unit,
5459
onTVShowClicked: (TMDbItem) -> Unit,
60+
scaffoldState: ScaffoldState,
5561
) {
5662
val coroutineScope: CoroutineScope = rememberCoroutineScope()
5763
val tabs = remember { MediaTab.entries.toTypedArray() }
@@ -73,8 +79,17 @@ fun BookmarkScreen(
7379
verticalAlignment = Alignment.Top,
7480
) { page ->
7581
when (page) {
76-
MediaTab.Movies.ordinal -> MoviesTabContent(movieViewModel, onMovieClicked)
77-
MediaTab.TVShows.ordinal -> TVShowsTabContent(tvShowViewModel, onTVShowClicked)
82+
MediaTab.Movies.ordinal -> MoviesTabContent(
83+
movieViewModel,
84+
onMovieClicked,
85+
scaffoldState,
86+
)
87+
88+
MediaTab.TVShows.ordinal -> TVShowsTabContent(
89+
tvShowViewModel,
90+
onTVShowClicked,
91+
scaffoldState,
92+
)
7893
}
7994
}
8095
TabRow(
@@ -103,20 +118,30 @@ fun BookmarkScreen(
103118
}
104119

105120
@Composable
106-
private fun MoviesTabContent(viewModel: BookmarkMovieViewModel, onClick: (TMDbItem) -> Unit) {
121+
private fun MoviesTabContent(
122+
viewModel: BookmarkMovieViewModel,
123+
onClick: (TMDbItem) -> Unit,
124+
scaffoldState: ScaffoldState,
125+
) {
107126
TabContent(
108127
viewModel = viewModel,
109128
onClick = onClick,
110129
textResourceId = commonR.string.movies,
130+
scaffoldState = scaffoldState,
111131
)
112132
}
113133

114134
@Composable
115-
private fun TVShowsTabContent(viewModel: BookmarkTVShowViewModel, onClick: (TMDbItem) -> Unit) {
135+
private fun TVShowsTabContent(
136+
viewModel: BookmarkTVShowViewModel,
137+
onClick: (TMDbItem) -> Unit,
138+
scaffoldState: ScaffoldState,
139+
) {
116140
TabContent(
117141
viewModel = viewModel,
118142
onClick = onClick,
119143
textResourceId = commonR.string.tv_series,
144+
scaffoldState = scaffoldState,
120145
)
121146
}
122147

@@ -125,13 +150,27 @@ private fun <T : TMDbItem> TabContent(
125150
viewModel: BaseViewModel<List<T>>,
126151
onClick: (TMDbItem) -> Unit,
127152
@StringRes textResourceId: Int,
153+
scaffoldState: ScaffoldState,
128154
) {
129155
viewModel.refresh()
130-
Content(viewModel = viewModel) {
131-
if (it.isEmpty()) {
132-
EmptyView(textResourceId = textResourceId)
133-
} else {
134-
TabContent(items = it, onClick = onClick)
156+
Content(viewModel = viewModel, scaffoldState = scaffoldState) {
157+
SwipeRefresh(
158+
state = rememberSwipeRefreshState(viewModel.state.collectAsStateWithLifecycle().value.isRefreshing),
159+
onRefresh = { viewModel.refresh(true) },
160+
indicator = { state, trigger ->
161+
SwipeRefreshIndicator(
162+
state,
163+
trigger,
164+
)
165+
},
166+
modifier = Modifier.fillMaxSize(),
167+
indicatorPadding = PaddingValues(top = TMDb_104_dp),
168+
) {
169+
if (it.isEmpty()) {
170+
EmptyView(textResourceId = textResourceId)
171+
} else {
172+
TabContent(items = it, onClick = onClick)
173+
}
135174
}
136175
}
137176
}

0 commit comments

Comments
 (0)