From 63aed76f780ba802ee88e00c616960f88e394434 Mon Sep 17 00:00:00 2001 From: Justin Guo Date: Wed, 16 Apr 2025 19:41:45 -0400 Subject: [PATCH 1/6] Add handling of 403 for new user --- .../android/viewmodel/onboarding/LandingViewModel.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/LandingViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/LandingViewModel.kt index 3bf44320..d8b71cd9 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/LandingViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/LandingViewModel.kt @@ -27,6 +27,7 @@ import com.google.android.gms.auth.api.signin.GoogleSignInClient import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch +import retrofit2.HttpException import javax.inject.Inject @HiltViewModel @@ -169,6 +170,16 @@ class LandingViewModel @Inject constructor( userInfoRepository.storeUserFromUserObject(user) rootNavigationRepository.navigate(ResellRootRoute.MAIN) } + } catch (e: HttpException) { + if (e.code() == 403) { + // 403 indicates that we need to create a new user. + rootNavigationRepository.navigate(ResellRootRoute.ONBOARDING) + } + else { + Log.e("LandingViewModel", "Error getting user: ", e) + onSignInFailed(showSheet = true) + rootConfirmationRepository.showError() + } } catch (e: Exception) { Log.e("LandingViewModel", "Error getting user: ", e) onSignInFailed(showSheet = false) From 81d2b30c90fee792c7a53b5b364fb6b41776239e Mon Sep 17 00:00:00 2001 From: Justin Guo Date: Wed, 16 Apr 2025 19:43:49 -0400 Subject: [PATCH 2/6] Fix potential json error --- .../cornellappdev/resell/android/model/api/UserApiService.kt | 2 +- .../android/viewmodel/onboarding/VenmoFieldViewModel.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/api/UserApiService.kt b/app/src/main/java/com/cornellappdev/resell/android/model/api/UserApiService.kt index eee71ec4..a8d79180 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/api/UserApiService.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/api/UserApiService.kt @@ -30,7 +30,7 @@ interface UserApiService { suspend fun logoutUser(@Body body: LogoutBody) @POST("user/create") - suspend fun createUser(@Body createUserBody: CreateUserBody): UserResponse + suspend fun createUser(@Body createUserBody: CreateUserBody): User } data class LogoutBody( diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt index 88e824cd..b0e04f7c 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt @@ -110,7 +110,7 @@ class VenmoFieldViewModel @Inject constructor( val googleUser = googleAuthRepository.accountOrNull()!! try { // TODO: venmo should be a field here, if not skipped. - val response = resellAuthRepository.createUser( + val user = resellAuthRepository.createUser( CreateUserBody( username = stateValue().username, netid = googleUser.email!!.split("@")[0], @@ -126,7 +126,7 @@ class VenmoFieldViewModel @Inject constructor( ) ) - userInfoRepository.storeUserFromUserObject(response.user) + userInfoRepository.storeUserFromUserObject(user) rootNavigationRepository.navigate(ResellRootRoute.MAIN) rootNavigationSheetRepository.showBottomSheet(RootSheet.Welcome) } catch (e: Exception) { From 8cae37a7b9b41e0247b6d11f9f60c12deac93755 Mon Sep 17 00:00:00 2001 From: Justin Guo Date: Sat, 26 Apr 2025 18:06:21 -0400 Subject: [PATCH 3/6] Fix user not being saved on creation --- .../resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt index e1f4ac39..1c5a3b9d 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt @@ -125,7 +125,7 @@ class VenmoFieldViewModel @Inject constructor( ) ) - userInfoRepository.storeUserFromUserObject(user) + userInfoRepository.storeUserFromUserObject(response) rootNavigationRepository.navigate(ResellRootRoute.MAIN) rootNavigationSheetRepository.showBottomSheet(RootSheet.Welcome) } catch (e: Exception) { From a9b60b931668f1cc91e82f0155069aa95719e0fb Mon Sep 17 00:00:00 2001 From: Justin Guo Date: Sat, 26 Apr 2025 18:11:58 -0400 Subject: [PATCH 4/6] Fix edit profile not saving user info --- .../resell/android/model/api/SettingsApiService.kt | 2 +- .../resell/android/model/login/ResellAuthRepository.kt | 2 +- .../resell/android/model/settings/SettingsRepository.kt | 7 +++++-- .../android/viewmodel/onboarding/VenmoFieldViewModel.kt | 4 ++-- .../android/viewmodel/settings/EditProfileViewModel.kt | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/api/SettingsApiService.kt b/app/src/main/java/com/cornellappdev/resell/android/model/api/SettingsApiService.kt index 71be5f37..12a8b358 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/api/SettingsApiService.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/api/SettingsApiService.kt @@ -18,7 +18,7 @@ interface SettingsApiService { suspend fun sendFeedback(@Body feedback: Feedback) @POST("user") - suspend fun editUser(@Body user: EditUser) + suspend fun editUser(@Body user: EditUser): UserResponse } data class EditUser( diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/login/ResellAuthRepository.kt b/app/src/main/java/com/cornellappdev/resell/android/model/login/ResellAuthRepository.kt index 52f0b856..3fc1a4e9 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/login/ResellAuthRepository.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/login/ResellAuthRepository.kt @@ -14,7 +14,7 @@ class ResellAuthRepository @Inject constructor( private val firebaseMessagingRepository: FirebaseMessagingRepository, private val googleAuthRepository: GoogleAuthRepository, ) { - suspend fun createUser(createUserBody: CreateUserBody) = + suspend fun createUser(createUserBody: CreateUserBody): User = retrofitInstance.userApi.createUser(createUserBody) /** diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/settings/SettingsRepository.kt b/app/src/main/java/com/cornellappdev/resell/android/model/settings/SettingsRepository.kt index 5e3e1a32..361ef62c 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/settings/SettingsRepository.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/settings/SettingsRepository.kt @@ -6,6 +6,7 @@ import com.cornellappdev.resell.android.model.api.Feedback import com.cornellappdev.resell.android.model.api.ReportPostBody import com.cornellappdev.resell.android.model.api.ReportProfileBody import com.cornellappdev.resell.android.model.api.RetrofitInstance +import com.cornellappdev.resell.android.model.api.UserResponse import com.cornellappdev.resell.android.model.core.UserInfoRepository import com.cornellappdev.resell.android.model.login.FireStoreRepository import com.cornellappdev.resell.android.model.profile.ProfileRepository @@ -64,8 +65,8 @@ class SettingsRepository @Inject constructor( venmo: String, bio: String, image: ImageBitmap? - ) { - retrofitInstance.settingsApi.editUser( + ): UserResponse { + val response = retrofitInstance.settingsApi.editUser( EditUser( username = username, venmoHandle = venmo, @@ -75,5 +76,7 @@ class SettingsRepository @Inject constructor( ) profileRepository.fetchInternalProfile(userInfoRepository.getUserId()!!) + + return response } } diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt index 1c5a3b9d..3be8a883 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/onboarding/VenmoFieldViewModel.kt @@ -108,7 +108,7 @@ class VenmoFieldViewModel @Inject constructor( viewModelScope.launch { val googleUser = googleAuthRepository.accountOrNull()!! try { - val response = resellAuthRepository.createUser( + val user = resellAuthRepository.createUser( CreateUserBody( username = stateValue().username, netid = googleUser.email!!.split("@")[0], @@ -125,7 +125,7 @@ class VenmoFieldViewModel @Inject constructor( ) ) - userInfoRepository.storeUserFromUserObject(response) + userInfoRepository.storeUserFromUserObject(user) rootNavigationRepository.navigate(ResellRootRoute.MAIN) rootNavigationSheetRepository.showBottomSheet(RootSheet.Welcome) } catch (e: Exception) { diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/settings/EditProfileViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/settings/EditProfileViewModel.kt index 3c27375d..3117b2ab 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/settings/EditProfileViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/settings/EditProfileViewModel.kt @@ -89,13 +89,14 @@ class EditProfileViewModel @Inject constructor( viewModelScope.launch { try { - settingsRepository.editProfile( + val response = settingsRepository.editProfile( username = stateValue().username, venmo = stateValue().venmo, bio = stateValue().bio, image = stateValue().imageBitmap?.asImageBitmap() ) settingsNavigationRepository.popBackStack() + userInfoRepository.storeUserFromUserObject(response.user) confirmationRepository.showSuccess("Profile updated successfully!") } catch (e: Exception) { applyMutation { From caf941e51a71d862d6fe2977ab1ee38b70ffc1dd Mon Sep 17 00:00:00 2001 From: Justin Guo Date: Sat, 26 Apr 2025 20:32:52 -0400 Subject: [PATCH 5/6] Implement log out if multiple failed auth --- .../android/model/api/RetrofitInstance.kt | 42 +++++++++++++++++-- .../model/login/FirebaseAuthRepository.kt | 6 ++- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/api/RetrofitInstance.kt b/app/src/main/java/com/cornellappdev/resell/android/model/api/RetrofitInstance.kt index 182ebc76..a300587d 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/api/RetrofitInstance.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/api/RetrofitInstance.kt @@ -4,11 +4,17 @@ import android.util.Log import com.cornellappdev.resell.android.BuildConfig import com.cornellappdev.resell.android.model.core.UserInfoRepository import com.cornellappdev.resell.android.model.login.FirebaseAuthRepository +import com.cornellappdev.resell.android.model.login.GoogleAuthRepository +import com.cornellappdev.resell.android.ui.screens.root.ResellRootRoute +import com.cornellappdev.resell.android.viewmodel.navigation.RootNavigationRepository +import com.cornellappdev.resell.android.viewmodel.root.RootConfirmationRepository import kotlinx.coroutines.runBlocking import okhttp3.Authenticator import okhttp3.Interceptor import okhttp3.OkHttpClient +import okhttp3.Response import okhttp3.ResponseBody +import okhttp3.ResponseBody.Companion.toResponseBody import okhttp3.logging.HttpLoggingInterceptor import org.json.JSONObject import retrofit2.Retrofit @@ -20,6 +26,9 @@ import javax.inject.Singleton class RetrofitInstance @Inject constructor( private val firebaseAuthRepository: FirebaseAuthRepository, private val userInfoRepository: UserInfoRepository, + private val googleAuthRepository: GoogleAuthRepository, + private val rootNavigationRepository: RootNavigationRepository, + private val rootConfirmationRepository: RootConfirmationRepository, ) { private var cachedToken: String? = null @@ -65,7 +74,7 @@ class RetrofitInstance @Inject constructor( // Get the `errors` response try { - val jsonObject = JSONObject(responseBody) + val jsonObject = JSONObject(responseBody ?: "") val errors = jsonObject.optJSONArray("errors") if (errors != null) { Log.e("OkHttpErrorResponse", "Errors: $errors") @@ -76,13 +85,25 @@ class RetrofitInstance @Inject constructor( } response.newBuilder() - .body(ResponseBody.create(response.body?.contentType(), responseBody ?: "")) + .body((responseBody ?: "").toResponseBody(response.body?.contentType())) .build() } private val authenticator = Authenticator { _, response -> - // Ping firebase for a refresh. - val accessToken = runBlocking { firebaseAuthRepository.getFirebaseAccessToken() } + if (responseCount(response) >= 2) { + // Already retried once, still getting 401 — force sign out + runBlocking { + googleAuthRepository.signOut() + rootNavigationRepository.navigate(ResellRootRoute.LANDING) + rootConfirmationRepository.showError( + message = "Authentication Failed. Please try signing in again!" + ) + } + return@Authenticator null // Give up — don't retry again + } + + // Ping firebase for a refresh. Force refresh. + val accessToken = runBlocking { firebaseAuthRepository.getFirebaseAccessToken(true) } cachedToken = accessToken if (accessToken != null) { response.request.newBuilder() @@ -94,6 +115,19 @@ class RetrofitInstance @Inject constructor( } } + /** + * Helper to count how many times we've already retried this request. + */ + private fun responseCount(response: Response): Int { + var count = 1 + var priorResponse = response.priorResponse + while (priorResponse != null) { + count++ + priorResponse = priorResponse.priorResponse + } + return count + } + // Build OkHttpClient with the dynamic auth interceptor private val okHttpClient = OkHttpClient.Builder().apply { if (BuildConfig.DEBUG) { diff --git a/app/src/main/java/com/cornellappdev/resell/android/model/login/FirebaseAuthRepository.kt b/app/src/main/java/com/cornellappdev/resell/android/model/login/FirebaseAuthRepository.kt index a49cf095..89563d67 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/model/login/FirebaseAuthRepository.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/model/login/FirebaseAuthRepository.kt @@ -38,9 +38,11 @@ class FirebaseAuthRepository @Inject constructor( * * If retrieved successfully, will also store the access token in [UserInfoRepository] * for use in the retrofit interceptor. + * + * @param forceRefresh Whether to force a firebase refresh of the access token. */ - suspend fun getFirebaseAccessToken(): String? { - val token = firebaseAuth.currentUser?.getIdToken(false)?.await()?.token + suspend fun getFirebaseAccessToken(forceRefresh: Boolean = false): String? { + val token = firebaseAuth.currentUser?.getIdToken(forceRefresh)?.await()?.token if (token == null) { Log.e("FirebaseAuthRepository", "Access token is null.") return null From 274d42559beb4c476045d60a25bb577c040b3952 Mon Sep 17 00:00:00 2001 From: Justin Guo Date: Sat, 26 Apr 2025 20:51:43 -0400 Subject: [PATCH 6/6] Add chat header loading states and fix home screen "race condition" --- .../chat/messages/MessageCardLoading.kt | 84 +++++++++++++++++++ .../chat/messages/MessagesScrollLoading.kt | 32 +++++++ .../chat/messages/ResellMessagesScroll.kt | 2 - .../android/ui/screens/main/MessagesScreen.kt | 6 +- .../android/viewmodel/main/HomeViewModel.kt | 12 +++ .../viewmodel/main/MessagesViewModel.kt | 6 ++ 6 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessageCardLoading.kt create mode 100644 app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessagesScrollLoading.kt diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessageCardLoading.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessageCardLoading.kt new file mode 100644 index 00000000..498b0016 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessageCardLoading.kt @@ -0,0 +1,84 @@ +package com.cornellappdev.resell.android.ui.components.chat.messages + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.resell.android.ui.components.main.ProfilePictureView +import com.cornellappdev.resell.android.ui.theme.ResellPreview +import com.cornellappdev.resell.android.util.LocalInfiniteShimmer + +@Composable +fun MessageCardLoading(modifier: Modifier = Modifier) { + Row( + modifier = modifier + .height(64.dp) + .fillMaxWidth() + .background(Color.White) + .padding(horizontal = 24.dp, vertical = 11.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Row { + ProfilePictureView( + imageUrl = "", + modifier = Modifier + .fillMaxHeight() + .aspectRatio(1f) + .size(32.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + } + + Column( + modifier = Modifier + .fillMaxHeight() + .weight(1f) + .padding(end = 16.dp), + verticalArrangement = Arrangement.SpaceEvenly + ) { + Row( + modifier = Modifier.height(25.dp), + verticalAlignment = Alignment.CenterVertically + ) { + LoadingBlob(modifier = Modifier + .height(height = 20.dp) + .weight(1f)) + } + Spacer(modifier = Modifier.height(1.dp)) + LoadingBlob(modifier = Modifier.size(width = 100.dp, height = 20.dp)) + } + } +} + +@Composable +private fun LoadingBlob( + modifier: Modifier = Modifier +) { + Surface( + modifier = modifier, + shape = RoundedCornerShape(100.dp), + color = LocalInfiniteShimmer.current + ) {} +} + +@Preview +@Composable +private fun MessageCardLoadingPreview() = ResellPreview { + MessageCardLoading() +} diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessagesScrollLoading.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessagesScrollLoading.kt new file mode 100644 index 00000000..ff11a425 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/MessagesScrollLoading.kt @@ -0,0 +1,32 @@ +package com.cornellappdev.resell.android.ui.components.chat.messages + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.cornellappdev.resell.android.ui.theme.ResellPreview + +@Composable +fun MessagesScrollLoading( + modifier: Modifier = Modifier, + count: Int, +) { + LazyColumn( + contentPadding = PaddingValues( + bottom = 100.dp, + ), + modifier = modifier, + ) { + items(count = count) { + MessageCardLoading() + } + } +} + +@Preview +@Composable +private fun MessagesScrollLoadingPreview() = ResellPreview { + MessagesScrollLoading(count = 3) +} diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/ResellMessagesScroll.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/ResellMessagesScroll.kt index d48cf947..b5ddf052 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/ResellMessagesScroll.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/components/chat/messages/ResellMessagesScroll.kt @@ -18,13 +18,11 @@ fun ResellMessagesScroll( onChatPressed: (ChatHeaderData) -> Unit, listState: LazyListState, modifier: Modifier = Modifier, - paddedTop: Dp = 0.dp, ) { LazyColumn( state = listState, contentPadding = PaddingValues( bottom = 100.dp, - top = paddedTop, ), modifier = modifier, ) { diff --git a/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/MessagesScreen.kt b/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/MessagesScreen.kt index c6286492..d5f11e1d 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/MessagesScreen.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/ui/screens/main/MessagesScreen.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.cornellappdev.resell.android.model.classes.ResellApiState import com.cornellappdev.resell.android.ui.components.chat.messages.MessageTag +import com.cornellappdev.resell.android.ui.components.chat.messages.MessagesScrollLoading import com.cornellappdev.resell.android.ui.components.chat.messages.ResellMessagesScroll import com.cornellappdev.resell.android.ui.theme.Padding import com.cornellappdev.resell.android.ui.theme.Style @@ -69,7 +70,10 @@ fun MessagesScreen( ) { when (chatUiState.loadedState) { ResellApiState.Loading -> { - // TODO Loading State + MessagesScrollLoading( + modifier = Modifier.fillMaxSize(), + count = chatUiState.numLoadingChats + ) } ResellApiState.Error -> { diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/HomeViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/HomeViewModel.kt index 0edfec82..0a8ce2b2 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/HomeViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/HomeViewModel.kt @@ -69,6 +69,10 @@ class HomeViewModel @Inject constructor( viewModelScope.launch { try { val posts = resellPostRepository.getPostsByPage(1) + + // if current filter changed since then, do nothing + if (stateValue().activeFilter != HomeFilter.RECENT) return@launch + applyMutation { copy( listings = posts.map { it.toListing() }, @@ -103,6 +107,10 @@ class HomeViewModel @Inject constructor( val posts = resellPostRepository.getPostsByFilter( filter.name ) + + // if current filter changed since then, do nothing + if (stateValue().activeFilter != filter) return@launch + applyMutation { copy( listings = posts.map { it.toListing() }, @@ -140,6 +148,10 @@ class HomeViewModel @Inject constructor( val newPage = resellPostRepository.getPostsByPage(stateValue().page).map { it.toListing() } + + // if current filter changed since then, do nothing + if (stateValue().activeFilter != HomeFilter.RECENT) return@launch + applyMutation { copy( listings = stateValue().listings + newPage, diff --git a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/MessagesViewModel.kt b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/MessagesViewModel.kt index e77cbb1c..109e5576 100644 --- a/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/MessagesViewModel.kt +++ b/app/src/main/java/com/cornellappdev/resell/android/viewmodel/main/MessagesViewModel.kt @@ -58,6 +58,12 @@ class MessagesViewModel @Inject constructor( } } + val numLoadingChats: Int + get() = when (chatType) { + ChatType.Purchases -> 4 + ChatType.Offers -> 2 + } + val loadedState: ResellApiState get() = filteredChats.toResellApiState()