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 class ProfileFragmentViewModel( state: SavedStateHandle, userRepository: UserRepository, friendshipRepository: FriendshipRepository, storiesRepository: StoriesRepository, mediaRepository: MediaRepository, graphQLRepository: GraphQLRepository, accountRepository: AccountRepository, 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>>() { var user: Resource = Resource.loading(null) var stateUsername: Resource = Resource.loading(null) init { addSource(currentUser) { currentUser -> this.user = currentUser value = currentUser to stateUsername } addSource(state.getLiveData("username")) { username -> this.stateUsername = Resource.success(username.substringAfter('@')) value = user to this.stateUsername } // trigger currentUserAndStateUsernameLiveData switch map with a state username success resource if (!state.contains("username")) { this.stateUsername = Resource.success(null) value = user to this.stateUsername } } } private val profileFetchControlledRunner = ControlledRunner() val profile: LiveData> = currentUserAndStateUsernameLiveData.switchMap { val (userResource, stateUsernameResource) = it liveData>(context = viewModelScope.coroutineContext + ioDispatcher) { if (userResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) { emit(Resource.loading(null)) return@liveData } val user = userResource.data val stateUsername = stateUsernameResource.data if (stateUsername.isNullOrBlank()) { emit(Resource.success(user)) 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 } } 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 '`@`' */ val username: LiveData = Transformations.map(profile) { return@map when (it.status) { Resource.Status.LOADING, Resource.Status.ERROR -> "" Resource.Status.SUCCESS -> it.data?.username ?: "" } } init { // Log.d(TAG, "${state.keys()} $userRepository $friendshipRepository $storiesRepository $mediaRepository") } fun setCurrentUser(currentUser: Resource) { _currentUser.postValue(currentUser) } fun shareDm(result: RankedRecipient) { if (messageManager == null) { messageManager = DirectMessagesManager } // messageManager?.sendMedia(result, mediaId, BroadcastItemType.PROFILE, viewModelScope) } fun shareDm(recipients: Set) { if (messageManager == null) { messageManager = DirectMessagesManager } // messageManager?.sendMedia(recipients, mediaId, BroadcastItemType.PROFILE, viewModelScope) } } @Suppress("UNCHECKED_CAST") class ProfileFragmentViewModelFactory( private val userRepository: UserRepository, private val friendshipRepository: FriendshipRepository, private val storiesRepository: StoriesRepository, private val mediaRepository: MediaRepository, private val graphQLRepository: GraphQLRepository, private val accountRepository: AccountRepository, private val favoriteRepository: FavoriteRepository, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null, ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) { override fun create( key: String, modelClass: Class, handle: SavedStateHandle, ): T { return ProfileFragmentViewModel( handle, userRepository, friendshipRepository, storiesRepository, mediaRepository, graphQLRepository, accountRepository, favoriteRepository, Dispatchers.IO, ) as T } }