From 63df44624e5118b71315a9941a864161bdb47c52 Mon Sep 17 00:00:00 2001 From: Ammar Githam Date: Thu, 24 Jun 2021 20:41:48 +0900 Subject: [PATCH] Add update favorite logic in ProfileFragmentViewModel and corresponding test --- .../fragments/main/ProfileFragment.java | 52 +++++++++---------- .../viewmodels/ProfileFragmentViewModel.kt | 36 ++++++++++++- .../awais/instagrabber/common/Adapters.kt | 26 +++------- .../ProfileFragmentViewModelTest.kt | 52 ++++++++++++++++++- 4 files changed, 117 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index 96d97d3e..e2a1d2a5 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -3,7 +3,6 @@ package awais.instagrabber.fragments.main; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; import android.graphics.Typeface; import android.os.Bundle; import android.os.Handler; @@ -54,7 +53,6 @@ import java.util.Objects; import java.util.Set; import awais.instagrabber.R; -import awais.instagrabber.UserSearchNavGraphDirections; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.HighlightsAdapter; @@ -69,8 +67,6 @@ import awais.instagrabber.db.repositories.FavoriteRepository; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.ProfilePicDialogFragment; import awais.instagrabber.fragments.PostViewV2Fragment; -import awais.instagrabber.fragments.UserSearchFragment; -import awais.instagrabber.fragments.UserSearchFragmentDirections; import awais.instagrabber.managers.DirectMessagesManager; import awais.instagrabber.managers.InboxManager; import awais.instagrabber.models.HighlightModel; @@ -131,7 +127,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private MenuItem blockMenuItem, restrictMenuItem, chainingMenuItem; private MenuItem muteStoriesMenuItem, mutePostsMenuItem, removeFollowerMenuItem; private MenuItem shareLinkMenuItem; -// private MenuItem shareDmMenuItem; + // private MenuItem shareDmMenuItem; private boolean accountIsUpdated = false; private boolean postsSetupDone = false; private Set selectedFeedModels; @@ -470,10 +466,10 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe if (shareLinkMenuItem != null) { shareLinkMenuItem.setVisible(profileModel != null && !TextUtils.isEmpty(profileModel.getUsername())); } -// shareDmMenuItem = menu.findItem(R.id.share_dm); -// if (shareDmMenuItem != null) { -// shareDmMenuItem.setVisible(profileModel != null && profileModel.getPk() != 0L); -// } + // shareDmMenuItem = menu.findItem(R.id.share_dm); + // if (shareDmMenuItem != null) { + // shareDmMenuItem.setVisible(profileModel != null && profileModel.getPk() != 0L); + // } } @Override @@ -586,20 +582,20 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe startActivity(Intent.createChooser(sharingIntent, null)); return true; } else if (itemId == R.id.share_dm) { -// final UserSearchNavGraphDirections.ActionGlobalUserSearch actionGlobalUserSearch = UserSearchFragmentDirections -// .actionGlobalUserSearch() -// .setTitle(getString(R.string.share)) -// .setActionLabel(getString(R.string.send)) -// .setShowGroups(true) -// .setMultiple(true) -// .setSearchMode(UserSearchFragment.SearchMode.RAVEN); -// final NavController navController = NavHostFragment.findNavController(ProfileFragment.this); -// try { -// navController.navigate(actionGlobalUserSearch); -// } catch (Exception e) { -// Log.e(TAG, "setupShare: ", e); -// } -// return true; + // final UserSearchNavGraphDirections.ActionGlobalUserSearch actionGlobalUserSearch = UserSearchFragmentDirections + // .actionGlobalUserSearch() + // .setTitle(getString(R.string.share)) + // .setActionLabel(getString(R.string.send)) + // .setShowGroups(true) + // .setMultiple(true) + // .setSearchMode(UserSearchFragment.SearchMode.RAVEN); + // final NavController navController = NavHostFragment.findNavController(ProfileFragment.this); + // try { + // navController.navigate(actionGlobalUserSearch); + // } catch (Exception e) { + // Log.e(TAG, "setupShare: ", e); + // } + // return true; return false; } return super.onOptionsItemSelected(item); @@ -750,7 +746,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe if (throwable != null || favorite == null) { profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_outline_star_plus_24); profileDetailsBinding.favChip.setText(R.string.add_to_favorites); - Log.e(TAG, "setProfileDetails: ", throwable); + if (throwable != null) { + Log.e(TAG, "setProfileDetails: ", throwable); + } return; } profileDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); @@ -1063,9 +1061,9 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe if (shareLinkMenuItem != null) { shareLinkMenuItem.setVisible(!TextUtils.isEmpty(profileModel.getUsername())); } -// if (shareDmMenuItem != null) { -// shareDmMenuItem.setVisible(profileModel.getPk() != 0L); -// } + // if (shareDmMenuItem != null) { + // shareDmMenuItem.setVisible(profileModel.getPk() != 0L); + // } } } diff --git a/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt index 69d513aa..cd150915 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt @@ -1,15 +1,19 @@ package awais.instagrabber.viewmodels import android.os.Bundle +import android.util.Log import androidx.lifecycle.* import androidx.savedstate.SavedStateRegistryOwner +import awais.instagrabber.db.entities.Favorite import awais.instagrabber.db.repositories.AccountRepository import awais.instagrabber.db.repositories.FavoriteRepository import awais.instagrabber.managers.DirectMessagesManager import awais.instagrabber.models.Resource +import awais.instagrabber.models.enums.FavoriteType import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.directmessages.RankedRecipient import awais.instagrabber.utils.ControlledRunner +import awais.instagrabber.utils.extensions.TAG import awais.instagrabber.webservices.* import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -22,14 +26,16 @@ class ProfileFragmentViewModel( mediaRepository: MediaRepository, graphQLRepository: GraphQLRepository, accountRepository: AccountRepository, - favoriteRepository: FavoriteRepository, + private val favoriteRepository: FavoriteRepository, ioDispatcher: CoroutineDispatcher, ) : ViewModel() { private val _currentUser = MutableLiveData>(Resource.loading(null)) + private val _isFavorite = MutableLiveData(false) private var messageManager: DirectMessagesManager? = null val currentUser: LiveData> = _currentUser val isLoggedIn: LiveData = currentUser.map { it.data != null } + val isFavorite: LiveData = _isFavorite private val currentUserAndStateUsernameLiveData: LiveData, Resource>> = object : MediatorLiveData, Resource>>() { @@ -78,12 +84,40 @@ class ProfileFragmentViewModel( } } emit(Resource.success(fetchedUser)) + if (fetchedUser != null) { + checkAndInsertFavorite(fetchedUser) + } } catch (e: Exception) { emit(Resource.error(e.message, null)) + Log.e(TAG, "fetching user: ", e) } } } + private suspend fun checkAndInsertFavorite(fetchedUser: User) { + try { + val favorite = favoriteRepository.getFavorite(fetchedUser.username, FavoriteType.USER) + if (favorite == null) { + _isFavorite.postValue(false) + return + } + _isFavorite.postValue(true) + favoriteRepository.insertOrUpdateFavorite( + Favorite( + favorite.id, + fetchedUser.username, + FavoriteType.USER, + fetchedUser.fullName, + fetchedUser.profilePicUrl, + favorite.dateAdded + ) + ) + } catch (e: Exception) { + _isFavorite.postValue(false) + Log.e(TAG, "checkAndInsertFavorite: ", e) + } + } + /** * Username of profile without '`@`' */ diff --git a/app/src/test/java/awais/instagrabber/common/Adapters.kt b/app/src/test/java/awais/instagrabber/common/Adapters.kt index e3153c6c..580bc07a 100644 --- a/app/src/test/java/awais/instagrabber/common/Adapters.kt +++ b/app/src/test/java/awais/instagrabber/common/Adapters.kt @@ -156,28 +156,16 @@ open class AccountDaoAdapter : AccountDao { } } -open class FavoriteDaoAdapter: FavoriteDao { - override suspend fun getAllFavorites(): List { - TODO("Not yet implemented") - } +open class FavoriteDaoAdapter : FavoriteDao { + override suspend fun getAllFavorites(): List = emptyList() - override suspend fun findFavoriteByQueryAndType(query: String, type: FavoriteType): Favorite? { - TODO("Not yet implemented") - } + override suspend fun findFavoriteByQueryAndType(query: String, type: FavoriteType): Favorite? = null - override suspend fun insertFavorites(vararg favorites: Favorite) { - TODO("Not yet implemented") - } + override suspend fun insertFavorites(vararg favorites: Favorite) {} - override suspend fun updateFavorites(vararg favorites: Favorite) { - TODO("Not yet implemented") - } + override suspend fun updateFavorites(vararg favorites: Favorite) {} - override suspend fun deleteFavorites(vararg favorites: Favorite) { - TODO("Not yet implemented") - } + override suspend fun deleteFavorites(vararg favorites: Favorite) {} - override suspend fun deleteAllFavorites() { - TODO("Not yet implemented") - } + override suspend fun deleteAllFavorites() {} } \ No newline at end of file diff --git a/app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt b/app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt index 48e694ae..fb851c7e 100644 --- a/app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt +++ b/app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt @@ -7,10 +7,12 @@ import awais.instagrabber.MainCoroutineScopeRule import awais.instagrabber.common.* import awais.instagrabber.db.datasources.AccountDataSource import awais.instagrabber.db.datasources.FavoriteDataSource +import awais.instagrabber.db.entities.Favorite import awais.instagrabber.db.repositories.AccountRepository import awais.instagrabber.db.repositories.FavoriteRepository import awais.instagrabber.getOrAwaitValue import awais.instagrabber.models.Resource +import awais.instagrabber.models.enums.FavoriteType import awais.instagrabber.repositories.responses.FriendshipStatus import awais.instagrabber.repositories.responses.User import awais.instagrabber.webservices.* @@ -18,9 +20,9 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import org.json.JSONException import org.junit.Rule import org.junit.Test -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.* import org.junit.runner.RunWith +import java.time.LocalDateTime @RunWith(AndroidJUnit4::class) internal class ProfileFragmentViewModelTest { @@ -233,4 +235,50 @@ internal class ProfileFragmentViewModelTest { } assertEquals(testPublicUser1, profile.data) } + + @ExperimentalCoroutinesApi + @Test + fun `should update favorite in db if fetched user is a favorite`() { + val state = SavedStateHandle( + mutableMapOf( + "username" to testPublicUser.username + ) + ) + val favorite = Favorite( + 1, + testPublicUser.username, + FavoriteType.USER, + testPublicUser.username, + "test url", + LocalDateTime.now() + ) + val graphQLRepository = object : GraphQLRepository(GraphQLServiceAdapter()) { + override suspend fun fetchUser(username: String): User = testPublicUser + } + var updateFavoriteCalled = false + val favoriteRepository = FavoriteRepository(FavoriteDataSource(object : FavoriteDaoAdapter() { + override suspend fun findFavoriteByQueryAndType(query: String, type: FavoriteType): Favorite = favorite + override suspend fun updateFavorites(vararg favorites: Favorite) { + updateFavoriteCalled = true + } + })) + val viewModel = ProfileFragmentViewModel( + state, + UserRepository(UserServiceAdapter()), + FriendshipRepository(FriendshipServiceAdapter()), + StoriesRepository(StoriesServiceAdapter()), + MediaRepository(MediaServiceAdapter()), + graphQLRepository, + AccountRepository(AccountDataSource(AccountDaoAdapter())), + favoriteRepository, + coroutineScope.dispatcher, + ) + viewModel.setCurrentUser(Resource.success(null)) + assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue()) + var profile = viewModel.profile.getOrAwaitValue() + while (profile.status == Resource.Status.LOADING) { + profile = viewModel.profile.getOrAwaitValue() + } + assertTrue(updateFavoriteCalled) + } } \ No newline at end of file