<lambda>null1 package com.airbnb.lottie.sample.compose.lottiefiles
2
3 import android.util.Log
4 import androidx.compose.foundation.layout.Box
5 import androidx.compose.foundation.layout.Column
6 import androidx.compose.foundation.layout.fillMaxWidth
7 import androidx.compose.foundation.layout.padding
8 import androidx.compose.foundation.lazy.LazyColumn
9 import androidx.compose.foundation.lazy.itemsIndexed
10 import androidx.compose.material.FloatingActionButton
11 import androidx.compose.material.Icon
12 import androidx.compose.material.icons.Icons
13 import androidx.compose.material.icons.filled.Repeat
14 import androidx.compose.runtime.Composable
15 import androidx.compose.runtime.SideEffect
16 import androidx.compose.runtime.getValue
17 import androidx.compose.ui.Alignment
18 import androidx.compose.ui.Modifier
19 import androidx.compose.ui.graphics.Color
20 import androidx.compose.ui.unit.dp
21 import androidx.navigation.NavController
22 import com.airbnb.lottie.sample.compose.Route
23 import com.airbnb.lottie.sample.compose.api.AnimationDataV2
24 import com.airbnb.lottie.sample.compose.api.LottieFilesApi
25 import com.airbnb.lottie.sample.compose.composables.AnimationRow
26 import com.airbnb.lottie.sample.compose.dagger.AssistedViewModelFactory
27 import com.airbnb.lottie.sample.compose.dagger.daggerMavericksViewModelFactory
28 import com.airbnb.mvrx.MavericksState
29 import com.airbnb.mvrx.MavericksViewModel
30 import com.airbnb.mvrx.MavericksViewModelFactory
31 import com.airbnb.mvrx.compose.collectAsState
32 import com.airbnb.mvrx.compose.mavericksViewModel
33 import dagger.assisted.Assisted
34 import dagger.assisted.AssistedFactory
35 import dagger.assisted.AssistedInject
36 import kotlinx.coroutines.Job
37 import kotlinx.coroutines.launch
38
39 enum class LottieFilesMode {
40 Recent,
41 Popular,
42 }
43
44 data class LottieFilesRecentAndPopularState(
45 val mode: LottieFilesMode = LottieFilesMode.Recent,
46 val results: List<AnimationDataV2> = emptyList(),
47 val currentPage: Int = 1,
48 val lastPage: Int = 0,
49 val fetchException: Boolean = false,
50 ) : MavericksState
51
52 class LottieFilesRecentAndPopularViewModel @AssistedInject constructor(
53 @Assisted initialState: LottieFilesRecentAndPopularState,
54 private val api: LottieFilesApi,
55 ) : MavericksViewModel<LottieFilesRecentAndPopularState>(initialState) {
56 private var fetchJob: Job? = null
57
58 init {
<lambda>null59 onEach(LottieFilesRecentAndPopularState::mode) {
60 setState { copy(results = emptyList(), currentPage = 0, lastPage = 1, fetchException = false) }
61 withState {
62 fetchNextPage()
63 }
64 }
65 }
66
fetchNextPagenull67 fun fetchNextPage() = withState { state ->
68 fetchJob?.cancel()
69 if (state.currentPage >= state.lastPage) return@withState
70 fetchJob = viewModelScope.launch {
71 val response = try {
72 Log.d(TAG, "Fetching page ${state.currentPage + 1}")
73 when (state.mode) {
74 LottieFilesMode.Recent -> api.getRecent(state.currentPage + 1)
75 LottieFilesMode.Popular -> api.getPopular(state.currentPage + 1)
76 }
77 } catch (e: Exception) {
78 Log.w(TAG, "Failed to fetch from Lottie Files.", e)
79 setState { copy(fetchException = true) }
80 return@launch
81 }
82 setState {
83 copy(
84 results = results + response.data.map(::AnimationDataV2),
85 currentPage = response.current_page,
86 lastPage = response.last_page,
87 fetchException = false
88 )
89 }
90 }
91 }
92
<lambda>null93 fun setMode(mode: LottieFilesMode) = setState { copy(mode = mode) }
94
95 @AssistedFactory
96 interface Factory : AssistedViewModelFactory<LottieFilesRecentAndPopularViewModel, LottieFilesRecentAndPopularState> {
createnull97 override fun create(initialState: LottieFilesRecentAndPopularState): LottieFilesRecentAndPopularViewModel
98 }
99
100 companion object :
101 MavericksViewModelFactory<LottieFilesRecentAndPopularViewModel, LottieFilesRecentAndPopularState> by daggerMavericksViewModelFactory() {
102 private const val TAG = "LottieFilesVM"
103 }
104 }
105
106 @Composable
LottieFilesRecentAndPopularPagenull107 fun LottieFilesRecentAndPopularPage(navController: NavController, mode: LottieFilesMode) {
108 val viewModel: LottieFilesRecentAndPopularViewModel = mavericksViewModel()
109 val state by viewModel.collectAsState()
110 SideEffect {
111 viewModel.setMode(mode)
112 }
113 LottieFilesRecentAndPopularPage(
114 state,
115 viewModel::fetchNextPage,
116 onAnimationClicked = { data ->
117 navController.navigate(Route.Player.forUrl(data.file, backgroundColor = data.bg_color))
118 }
119 )
120 }
121
122 @Composable
LottieFilesRecentAndPopularPagenull123 fun LottieFilesRecentAndPopularPage(
124 state: LottieFilesRecentAndPopularState,
125 fetchNextPage: () -> Unit,
126 onAnimationClicked: (AnimationDataV2) -> Unit,
127 modifier: Modifier = Modifier,
128 ) {
129 Box(
130 modifier = Modifier
131 .fillMaxWidth()
132 ) {
133 Column(
134 modifier = Modifier.then(modifier)
135 ) {
136 LazyColumn(
137 modifier = Modifier.weight(1f)
138 ) {
139 itemsIndexed(state.results) { index, result ->
140 if (index == state.results.size - 1) {
141 SideEffect(fetchNextPage)
142 }
143 AnimationRow(
144 title = result.title,
145 previewUrl = result.preview_url ?: "",
146 previewBackgroundColor = result.bgColor,
147 onClick = { onAnimationClicked(result) }
148 )
149 }
150 }
151 }
152 if (state.fetchException) {
153 FloatingActionButton(
154 onClick = fetchNextPage,
155 content = {
156 Icon(
157 imageVector = Icons.Filled.Repeat,
158 tint = Color.White,
159 contentDescription = null
160 )
161 },
162 modifier = Modifier
163 .align(Alignment.BottomCenter)
164 .padding(bottom = 24.dp)
165 )
166 }
167 }
168 }