mirror of
				https://github.com/KokaKiwi/BarInsta
				synced 2025-10-31 03:25:34 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/austinhuang0131/instagrabber
This commit is contained in:
		
						commit
						38cc804a3f
					
				| @ -1,14 +1,13 @@ | ||||
| package awais.instagrabber.models | ||||
| 
 | ||||
| import awais.instagrabber.utils.TextUtils | ||||
| import java.util.* | ||||
| 
 | ||||
| data class HighlightModel( | ||||
|     val title: String?, | ||||
|     val id: String, | ||||
|     val thumbnailUrl: String, | ||||
|     val timestamp: Long, | ||||
|     val mediaCount: Int | ||||
|     val title: String? = null, | ||||
|     val id: String = "", | ||||
|     val thumbnailUrl: String = "", | ||||
|     val timestamp: Long = 0, | ||||
|     val mediaCount: Int = 0, | ||||
| ) { | ||||
|     val dateTime: String | ||||
|         get() = TextUtils.epochSecondToString(timestamp) | ||||
|  | ||||
| @ -5,14 +5,14 @@ import awais.instagrabber.models.stickers.* | ||||
| import java.io.Serializable | ||||
| 
 | ||||
| data class StoryModel( | ||||
|     val storyMediaId: String?, | ||||
|     val storyUrl: String?, | ||||
|     var thumbnail: String?, | ||||
|     val itemType: MediaItemType?, | ||||
|     val timestamp: Long, | ||||
|     val username: String?, | ||||
|     val userId: Long, | ||||
|     val canReply: Boolean | ||||
|     val storyMediaId: String? = null, | ||||
|     val storyUrl: String? = null, | ||||
|     var thumbnail: String? = null, | ||||
|     val itemType: MediaItemType? = null, | ||||
|     val timestamp: Long = 0, | ||||
|     val username: String? = null, | ||||
|     val userId: Long = 0, | ||||
|     val canReply: Boolean = false, | ||||
| ) : Serializable { | ||||
|     var videoUrl: String? = null | ||||
|     var tappableShortCode: String? = null | ||||
|  | ||||
| @ -8,8 +8,11 @@ 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.HighlightModel | ||||
| import awais.instagrabber.models.Resource | ||||
| import awais.instagrabber.models.StoryModel | ||||
| import awais.instagrabber.models.enums.FavoriteType | ||||
| import awais.instagrabber.repositories.requests.StoryViewerOptions | ||||
| import awais.instagrabber.repositories.responses.User | ||||
| import awais.instagrabber.repositories.responses.directmessages.RankedRecipient | ||||
| import awais.instagrabber.utils.ControlledRunner | ||||
| @ -22,7 +25,7 @@ class ProfileFragmentViewModel( | ||||
|     state: SavedStateHandle, | ||||
|     userRepository: UserRepository, | ||||
|     friendshipRepository: FriendshipRepository, | ||||
|     storiesRepository: StoriesRepository, | ||||
|     private val storiesRepository: StoriesRepository, | ||||
|     mediaRepository: MediaRepository, | ||||
|     graphQLRepository: GraphQLRepository, | ||||
|     accountRepository: AccountRepository, | ||||
| @ -61,27 +64,21 @@ class ProfileFragmentViewModel( | ||||
| 
 | ||||
|     private val profileFetchControlledRunner = ControlledRunner<User?>() | ||||
|     val profile: LiveData<Resource<User?>> = currentUserAndStateUsernameLiveData.switchMap { | ||||
|         val (userResource, stateUsernameResource) = it | ||||
|         val (currentUserResource, stateUsernameResource) = it | ||||
|         liveData<Resource<User?>>(context = viewModelScope.coroutineContext + ioDispatcher) { | ||||
|             if (userResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) { | ||||
|             if (currentUserResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) { | ||||
|                 emit(Resource.loading(null)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             val user = userResource.data | ||||
|             val currentUser = currentUserResource.data | ||||
|             val stateUsername = stateUsernameResource.data | ||||
|             if (stateUsername.isNullOrBlank()) { | ||||
|                 emit(Resource.success(user)) | ||||
|                 emit(Resource.success(currentUser)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             try { | ||||
|                 val fetchedUser = profileFetchControlledRunner.cancelPreviousThenRun { | ||||
|                     return@cancelPreviousThenRun if (user != null) { | ||||
|                         val tempUser = userRepository.getUsernameInfo(stateUsername) // logged in | ||||
|                         tempUser.friendshipStatus = userRepository.getUserFriendship(tempUser.pk) | ||||
|                         return@cancelPreviousThenRun tempUser | ||||
|                     } else { | ||||
|                         graphQLRepository.fetchUser(stateUsername) // anonymous | ||||
|                     } | ||||
|                     return@cancelPreviousThenRun fetchUser(currentUser, userRepository, stateUsername, graphQLRepository) | ||||
|                 } | ||||
|                 emit(Resource.success(fetchedUser)) | ||||
|                 if (fetchedUser != null) { | ||||
| @ -94,6 +91,81 @@ class ProfileFragmentViewModel( | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private val storyFetchControlledRunner = ControlledRunner<List<StoryModel>?>() | ||||
|     val userStories: LiveData<Resource<List<StoryModel>?>> = profile.switchMap { userResource -> | ||||
|         liveData<Resource<List<StoryModel>?>>(context = viewModelScope.coroutineContext + ioDispatcher) { | ||||
|             // don't fetch if not logged in | ||||
|             if (isLoggedIn.value != true) { | ||||
|                 emit(Resource.success(null)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             if (userResource.status == Resource.Status.LOADING) { | ||||
|                 emit(Resource.loading(null)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             val user = userResource.data | ||||
|             if (user == null) { | ||||
|                 emit(Resource.success(null)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             try { | ||||
|                 val fetchedStories = storyFetchControlledRunner.cancelPreviousThenRun { fetchUserStory(user) } | ||||
|                 emit(Resource.success(fetchedStories)) | ||||
|             } catch (e: Exception) { | ||||
|                 emit(Resource.error(e.message, null)) | ||||
|                 Log.e(TAG, "fetching story: ", e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private val highlightsFetchControlledRunner = ControlledRunner<List<HighlightModel>?>() | ||||
|     val userHighlights: LiveData<Resource<List<HighlightModel>?>> = profile.switchMap { userResource -> | ||||
|         liveData<Resource<List<HighlightModel>?>>(context = viewModelScope.coroutineContext + ioDispatcher) { | ||||
|             // don't fetch if not logged in | ||||
|             if (isLoggedIn.value != true) { | ||||
|                 emit(Resource.success(null)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             if (userResource.status == Resource.Status.LOADING) { | ||||
|                 emit(Resource.loading(null)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             val user = userResource.data | ||||
|             if (user == null) { | ||||
|                 emit(Resource.success(null)) | ||||
|                 return@liveData | ||||
|             } | ||||
|             try { | ||||
|                 val fetchedHighlights = highlightsFetchControlledRunner.cancelPreviousThenRun { fetchUserHighlights(user) } | ||||
|                 emit(Resource.success(fetchedHighlights)) | ||||
|             } catch (e: Exception) { | ||||
|                 emit(Resource.error(e.message, null)) | ||||
|                 Log.e(TAG, "fetching story: ", e) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun fetchUser( | ||||
|         currentUser: User?, | ||||
|         userRepository: UserRepository, | ||||
|         stateUsername: String, | ||||
|         graphQLRepository: GraphQLRepository | ||||
|     ) = if (currentUser != null) { | ||||
|         // logged in | ||||
|         val tempUser = userRepository.getUsernameInfo(stateUsername) | ||||
|         tempUser.friendshipStatus = userRepository.getUserFriendship(tempUser.pk) | ||||
|         tempUser | ||||
|     } else { | ||||
|         // anonymous | ||||
|         graphQLRepository.fetchUser(stateUsername) | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun fetchUserStory(fetchedUser: User): List<StoryModel> = storiesRepository.getUserStory( | ||||
|         StoryViewerOptions.forUser(fetchedUser.pk, fetchedUser.fullName) | ||||
|     ) | ||||
| 
 | ||||
|     private suspend fun fetchUserHighlights(fetchedUser: User): List<HighlightModel> = storiesRepository.fetchHighlights(fetchedUser.pk) | ||||
| 
 | ||||
|     private suspend fun checkAndInsertFavorite(fetchedUser: User) { | ||||
|         try { | ||||
|             val favorite = favoriteRepository.getFavorite(fetchedUser.username, FavoriteType.USER) | ||||
|  | ||||
| @ -19,7 +19,7 @@ import org.json.JSONArray | ||||
| import org.json.JSONObject | ||||
| import java.util.* | ||||
| 
 | ||||
| class StoriesRepository(private val service: StoriesService) { | ||||
| open class StoriesRepository(private val service: StoriesService) { | ||||
| 
 | ||||
|     suspend fun fetch(mediaId: Long): StoryModel { | ||||
|         val response = service.fetch(mediaId) | ||||
| @ -99,7 +99,7 @@ class StoriesRepository(private val service: StoriesService) { | ||||
|         return sort(feedStoryModels) | ||||
|     } | ||||
| 
 | ||||
|     suspend fun fetchHighlights(profileId: Long): List<HighlightModel> { | ||||
|     open suspend fun fetchHighlights(profileId: Long): List<HighlightModel> { | ||||
|         val response = service.fetchHighlights(profileId) | ||||
|         val highlightsReel = JSONObject(response).getJSONArray("tray") | ||||
|         val length = highlightsReel.length() | ||||
| @ -150,7 +150,7 @@ class StoriesRepository(private val service: StoriesService) { | ||||
|         return ArchiveFetchResponse(highlightModels, data.getBoolean("more_available"), data.getString("max_id")) | ||||
|     } | ||||
| 
 | ||||
|     suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> { | ||||
|     open suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> { | ||||
|         val url = buildUrl(options) ?: return emptyList() | ||||
|         val response = service.getUserStory(url) | ||||
|         val isLocOrHashtag = options.type == StoryViewerOptions.Type.LOCATION || options.type == StoryViewerOptions.Type.HASHTAG | ||||
|  | ||||
| @ -17,9 +17,7 @@ open class UserServiceAdapter : UserService { | ||||
|         TODO("Not yet implemented") | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun getUserFriendship(uid: Long): FriendshipStatus { | ||||
|         TODO("Not yet implemented") | ||||
|     } | ||||
|     override suspend fun getUserFriendship(uid: Long): FriendshipStatus = FriendshipStatus() | ||||
| 
 | ||||
|     override suspend fun search(timezoneOffset: Float, query: String): UserSearchResponse { | ||||
|         TODO("Not yet implemented") | ||||
|  | ||||
| @ -11,8 +11,11 @@ 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.HighlightModel | ||||
| import awais.instagrabber.models.Resource | ||||
| import awais.instagrabber.models.StoryModel | ||||
| import awais.instagrabber.models.enums.FavoriteType | ||||
| import awais.instagrabber.repositories.requests.StoryViewerOptions | ||||
| import awais.instagrabber.repositories.responses.FriendshipStatus | ||||
| import awais.instagrabber.repositories.responses.User | ||||
| import awais.instagrabber.webservices.* | ||||
| @ -279,6 +282,54 @@ internal class ProfileFragmentViewModelTest { | ||||
|         while (profile.status == Resource.Status.LOADING) { | ||||
|             profile = viewModel.profile.getOrAwaitValue() | ||||
|         } | ||||
|         assertEquals(true, viewModel.isFavorite.getOrAwaitValue()) | ||||
|         assertTrue(updateFavoriteCalled) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @ExperimentalCoroutinesApi | ||||
|     @Test | ||||
|     fun `should fetch user stories and highlights when logged in`() { | ||||
|         val state = SavedStateHandle( | ||||
|             mutableMapOf<String, Any?>( | ||||
|                 "username" to testPublicUser.username | ||||
|             ) | ||||
|         ) | ||||
|         val testUserStories = listOf(StoryModel()) | ||||
|         val testUserHighlights = listOf(HighlightModel()) | ||||
|         val userRepository = object : UserRepository(UserServiceAdapter()) { | ||||
|             override suspend fun getUsernameInfo(username: String): User = testPublicUser | ||||
|         } | ||||
|         val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) { | ||||
|             override suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> = testUserStories | ||||
|             override suspend fun fetchHighlights(profileId: Long): List<HighlightModel> = testUserHighlights | ||||
|         } | ||||
|         val viewModel = ProfileFragmentViewModel( | ||||
|             state, | ||||
|             userRepository, | ||||
|             FriendshipRepository(FriendshipServiceAdapter()), | ||||
|             storiesRepository, | ||||
|             MediaRepository(MediaServiceAdapter()), | ||||
|             GraphQLRepository(GraphQLServiceAdapter()), | ||||
|             AccountRepository(AccountDataSource(AccountDaoAdapter())), | ||||
|             FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())), | ||||
|             coroutineScope.dispatcher, | ||||
|         ) | ||||
|         viewModel.setCurrentUser(Resource.success(User())) | ||||
|         assertEquals(true, viewModel.isLoggedIn.getOrAwaitValue()) | ||||
|         var profile = viewModel.profile.getOrAwaitValue() | ||||
|         while (profile.status == Resource.Status.LOADING) { | ||||
|             profile = viewModel.profile.getOrAwaitValue() | ||||
|         } | ||||
|         var userStories = viewModel.userStories.getOrAwaitValue() | ||||
|         while (userStories.status == Resource.Status.LOADING) { | ||||
|             userStories = viewModel.userStories.getOrAwaitValue() | ||||
|         } | ||||
|         assertEquals(testUserStories, userStories.data) | ||||
|         var userHighlights = viewModel.userHighlights.getOrAwaitValue() | ||||
|         while (userHighlights.status == Resource.Status.LOADING) { | ||||
|             userHighlights = viewModel.userHighlights.getOrAwaitValue() | ||||
|         } | ||||
|         assertEquals(testUserHighlights, userHighlights.data) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user