Skip to content

Instantly share code, notes, and snippets.

@Nimrodda
Last active April 14, 2020 15:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nimrodda/2161e44e6f0d0ab157bc4eedbaec87c6 to your computer and use it in GitHub Desktop.
Save Nimrodda/2161e44e6f0d0ab157bc4eedbaec87c6 to your computer and use it in GitHub Desktop.
class WorkoutSummaryViewModel : ViewModel() {
private val _viewState = MutableLiveData<WorkoutSummaryViewState>()
val viewState: LiveData<WorkoutSummaryViewState> = _viewState
// Conflated channel makes sure that we always keep only the last emitted item
// other channels have been omitted for brevity
private val likesItemChannel = ConflatedBroadcastChannel<ViewState<LikesItem>>()
fun loadData(workoutId: Int) {
viewModelScope.launch {
combine(
listOf(
loadCoverImages(workoutId),
loadLikesItem(workoutId),
loadComments(workoutId),
loadWorkoutSummary(workoutId)
)
) { arrayOfViewStates ->
// The order in the array is the same as in the combine function
WorkoutSummaryViewState(
coverImagesItem = arrayOfViewStates[0] as ViewState<CoverImagesItem>,
likesItem = arrayOfViewStates[1] as ViewState<LikesItem>,
commentsItem = arrayOfViewStates[2] as ViewState<CommentsItem>,
workoutSummaryItem = arrayOfViewStates[3] as ViewState<WorkoutSummaryItem>
)
}.conflate()
.flowOn(Dispatchers.Default)
.onEach { viewState ->
// Updating view state
_viewState.value = viewState
}
.collect()
}
}
private fun loadLikesItem(
workoutId: Int,
initialState: ViewState<LikesItem> = ViewState.Loading()
): Flow<ViewState<LikesItem>> {
viewModelScope.launch {
// Initial state, empty
likesItemChannel.send(initialState)
try {
// Load likes from db/server. likesRepository.loadLikes() is Main Safe.
// NOTE: Make sure your API is Main Safe or wrap call with withContext(Dispatchers.IO).
val loadedLikes = likesRepository.loadLikes(workoutId)
likesItemChannel.send(
ViewState.Loaded(
LikesItem(
// bind below props from loaded data,
hasUserLiked = loadedLikes.hasUserLiked,
likesCount = loadLikes.likesCount,
userAvatars = loadedLikes.userAvatars,
onClickHandler = ::onLikeClicked
)
)
)
} catch (e: Exception) {
likesItemChannel.send(ViewState.Error(e, initialState.data))
}
}
return likesItemChannel.asFlow()
}
private fun onLikeClicked(workoutId: Int) {
viewModelScope.launch {
// We can get the old state from the channel
val oldState = likesItemChannel.asFlow().first() // TODO try-catch needed here
try {
// store like in local DB. storeLike() is Main Safe.
likesRepository.storeLike(workoutId)
// Reload likes, but this time we provide the previous state as initial state
// By reloading, we get a fresh state from the local DB
loadLikesItem(workoutId, oldState)
} catch (e: Exception) {
// Emit an error with the previous state. This way we can show an error,
// while still keeping the previous state in place.
likesItemChannel.send(ViewState.Error(e, oldState.data))
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment