diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 413ccb49..8ce173de 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -87,7 +87,6 @@ import awais.instagrabber.repositories.requests.StoryViewerOptions.Type; import awais.instagrabber.repositories.requests.directmessages.ThreadIdOrUserIds; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.StoryStickerResponse; -import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CoroutineUtilsKt; @@ -102,9 +101,7 @@ import awais.instagrabber.webservices.DirectMessagesService; import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; +import kotlinx.coroutines.Dispatchers; import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_THRESHOLD; import static awais.instagrabber.customviews.helpers.SwipeGestureListener.SWIPE_VELOCITY_THRESHOLD; @@ -220,40 +217,32 @@ public class StoryViewerFragment extends Fragment { new AlertDialog.Builder(context) .setTitle(R.string.reply_story) .setView(input) - .setPositiveButton(R.string.confirm, (d, w) -> { - final Call createThreadRequest = - directMessagesService.createThread(Collections.singletonList(currentStory.getUserId()), null); - createThreadRequest.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - if (!response.isSuccessful() || response.body() == null) { + .setPositiveButton(R.string.confirm, (d, w) -> directMessagesService.createThread( + Collections.singletonList(currentStory.getUserId()), + null, + CoroutineUtilsKt.getContinuation((thread, throwable) -> { + if (throwable != null) { + Log.e(TAG, "onOptionsItemSelected: ", throwable); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); return; } - final DirectThread thread = response.body(); directMessagesService.broadcastStoryReply( ThreadIdOrUserIds.of(thread.getThreadId()), input.getText().toString(), currentStory.getStoryMediaId(), String.valueOf(currentStory.getUserId()), - CoroutineUtilsKt.getContinuation((directThreadBroadcastResponse, throwable) -> { - if (throwable != null) { + CoroutineUtilsKt.getContinuation((directThreadBroadcastResponse, throwable1) -> { + if (throwable1 != null) { Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - Log.e(TAG, "onFailure: ", throwable); + Log.e(TAG, "onFailure: ", throwable1); return; } Toast.makeText(context, R.string.answered_story, Toast.LENGTH_SHORT).show(); - }) + }, Dispatchers.getIO()) ); - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - } - }); - }) + }, Dispatchers.getIO()) + )) .setNegativeButton(R.string.cancel, null) .show(); return true; 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 e692da19..7e892024 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -84,9 +84,9 @@ import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.UserProfileContextLink; -import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.DownloadUtils; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; @@ -99,9 +99,7 @@ import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.StoriesService; import awais.instagrabber.webservices.UserService; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; +import kotlinx.coroutines.Dispatchers; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG; @@ -1078,30 +1076,24 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe if (!disableDm) { profileDetailsBinding.btnDM.setOnClickListener(v -> { profileDetailsBinding.btnDM.setEnabled(false); - final Call createThreadRequest = - directMessagesService.createThread(Collections.singletonList(profileModel.getPk()), null); - createThreadRequest.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - profileDetailsBinding.btnDM.setEnabled(true); - if (!response.isSuccessful() || response.body() == null) { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - return; - } - final InboxManager inboxManager = DirectMessagesManager.INSTANCE.getInboxManager(); - final DirectThread thread = response.body(); - if (!inboxManager.containsThread(thread.getThreadId())) { - thread.setTemp(true); - inboxManager.addThread(thread, 0); - } - fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername()); - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - } - }); + directMessagesService.createThread( + Collections.singletonList(profileModel.getPk()), + null, + CoroutineUtilsKt.getContinuation((thread, throwable) -> { + if (throwable != null) { + Log.e(TAG, "setupCommonListeners: ", throwable); + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + return; + } + profileDetailsBinding.btnDM.setEnabled(true); + final InboxManager inboxManager = DirectMessagesManager.INSTANCE.getInboxManager(); + if (!inboxManager.containsThread(thread.getThreadId())) { + thread.setTemp(true); + inboxManager.addThread(thread, 0); + } + fragmentActivity.navigateToThread(thread.getThreadId(), profileModel.getUsername()); + }, Dispatchers.getIO()) + ); }); } profileDetailsBinding.mainProfileImage.setOnClickListener(v -> { diff --git a/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt b/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt index 08452a97..fafc6e6e 100644 --- a/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/DirectMessagesManager.kt @@ -22,10 +22,6 @@ import awais.instagrabber.webservices.DirectMessagesService.Companion.getInstanc import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response -import java.io.IOException import java.util.* object DirectMessagesManager { @@ -72,43 +68,7 @@ object DirectMessagesManager { return getInstance(threadId, pending, currentUser, contentResolver, viewerId, csrfToken, deviceUuid) } - fun createThread( - userPk: Long, - callback: ((DirectThread) -> Unit)?, - ) { - val createThreadRequest = service.createThread(listOf(userPk), null) - createThreadRequest.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - val errorBody = response.errorBody() - if (errorBody != null) { - try { - val string = errorBody.string() - val msg = String.format(Locale.US, - "onResponse: url: %s, responseCode: %d, errorBody: %s", - call.request().url().toString(), - response.code(), - string) - Log.e(TAG, msg) - } catch (e: IOException) { - Log.e(TAG, "onResponse: ", e) - } - return - } - Log.e(TAG, "onResponse: request was not successful and response error body was null") - return - } - val thread = response.body() - if (thread == null) { - Log.e(TAG, "onResponse: thread is null") - return - } - callback?.invoke(thread) - } - - override fun onFailure(call: Call, t: Throwable) {} - }) - } + suspend fun createThread(userPk: Long): DirectThread = service.createThread(listOf(userPk), null) fun sendMedia(recipients: Set, mediaId: String, scope: CoroutineScope) { val resultsCount = intArrayOf(0) @@ -136,12 +96,18 @@ object DirectMessagesManager { ) { if (recipient.thread == null && recipient.user != null) { // create thread and forward - createThread(recipient.user.pk) { (threadId) -> - val threadIdTemp = threadId ?: return@createThread - sendMedia(threadIdTemp, mediaId, scope) { - if (refreshInbox) { - inboxManager.refresh(scope) + scope.launch(Dispatchers.IO) { + try { + val (threadId) = createThread(recipient.user.pk) + val threadIdTemp = threadId ?: return@launch + sendMedia(threadIdTemp, mediaId, scope) { + if (refreshInbox) { + inboxManager.refresh(scope) + } + callback?.invoke() } + } catch (e: Exception) { + Log.e(TAG, "sendMedia: ", e) callback?.invoke() } } diff --git a/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt b/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt index 04499b7e..11c51abd 100644 --- a/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt +++ b/app/src/main/java/awais/instagrabber/managers/ThreadManager.kt @@ -155,79 +155,23 @@ class ThreadManager private constructor( _fetching.postValue(error(e.message, null)) hasOlder = false } - - // chatsRequest?.enqueue(object : Callback { - // override fun onResponse(call: Call, response: Response) { - // val feedResponse = response.body() - // if (feedResponse == null) { - // fetching.postValue(error(R.string.generic_null_response, null)) - // Log.e(TAG, "onResponse: response was null!") - // return - // } - // if (feedResponse.status != null && feedResponse.status != "ok") { - // fetching.postValue(error(R.string.generic_not_ok_response, null)) - // return - // } - // val thread = feedResponse.thread - // if (thread == null) { - // fetching.postValue(error("thread is null!", null)) - // return - // } - // setThread(thread) - // fetching.postValue(success(Any())) - // } - // - // override fun onFailure(call: Call, t: Throwable) { - // Log.e(TAG, "Failed fetching dm chats", t) - // fetching.postValue(error(t.message, null)) - // hasOlder = false - // } - // }) } if (cursor == null) { - fetchPendingRequests() + fetchPendingRequests(scope) } } - fun fetchPendingRequests() { + fun fetchPendingRequests(scope: CoroutineScope) { val isGroup = isGroup.value if (isGroup == null || !isGroup) return - val request = service.participantRequests(threadId, 1, null) - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (!response.isSuccessful) { - if (response.errorBody() != null) { - try { - val string = response.errorBody()?.string() ?: "" - val msg = String.format(Locale.US, - "onResponse: url: %s, responseCode: %d, errorBody: %s", - call.request().url().toString(), - response.code(), - string) - Log.e(TAG, msg) - } catch (e: IOException) { - Log.e(TAG, "onResponse: ", e) - } - return - } - Log.e(TAG, "onResponse: request was not successful and response error body was null") - return - } - val body = response.body() - if (body == null) { - Log.e(TAG, "onResponse: response body was null") - return - } - _pendingRequests.postValue(body) + scope.launch(Dispatchers.IO) { + try { + val response = service.participantRequests(threadId, 1) + _pendingRequests.postValue(response) + } catch (e: Exception) { + Log.e(TAG, "fetchPendingRequests: ", e) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - } - }) + } } private fun setThread(thread: DirectThread, skipItems: Boolean) { @@ -628,7 +572,7 @@ class ThreadManager private constructor( return data } - fun unsend(item: DirectItem): LiveData> { + fun unsend(item: DirectItem, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() val index = removeItem(item) val itemId = item.itemId @@ -636,45 +580,46 @@ class ThreadManager private constructor( data.postValue(error("itemId is null", null)) return data } - val request = service.deleteItem(threadId, itemId) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful) { - // Log.d(TAG, "onResponse: " + response.body()); - return - } + scope.launch(Dispatchers.IO) { + try { + service.deleteItem(threadId, itemId) + } catch (e: Exception) { // add the item back if unsuccessful addItems(index, listOf(item)) - if (response.errorBody() != null) { - handleErrorBody(call, response, data) - return - } - data.postValue(error(R.string.generic_failed_request, item)) + data.postValue(error(e.message, item)) + Log.e(TAG, "unsend: ", e) } - - override fun onFailure(call: Call, t: Throwable) { - data.postValue(error(t.message, item)) - Log.e(TAG, "enqueueRequest: onFailure: ", t) - } - }) + } return data } - fun forward(recipients: Set, itemToForward: DirectItem) { + fun forward( + recipients: Set, + itemToForward: DirectItem, + scope: CoroutineScope, + ) { for (recipient in recipients) { - forward(recipient, itemToForward) + forward(recipient, itemToForward, scope) } } - fun forward(recipient: RankedRecipient, itemToForward: DirectItem) { + fun forward( + recipient: RankedRecipient, + itemToForward: DirectItem, + scope: CoroutineScope, + ) { if (recipient.thread == null && recipient.user != null) { - // create thread and forward - DirectMessagesManager.createThread(recipient.user.pk) { forward(it, itemToForward) } + scope.launch(Dispatchers.IO) { + // create thread and forward + val thread = DirectMessagesManager.createThread(recipient.user.pk) + forward(thread, itemToForward, scope) + } + return } if (recipient.thread != null) { // just forward val thread = recipient.thread - forward(thread, itemToForward) + forward(thread, itemToForward, scope) } } @@ -683,7 +628,11 @@ class ThreadManager private constructor( _replyToItem.postValue(item) } - private fun forward(thread: DirectThread, itemToForward: DirectItem): LiveData> { + private fun forward( + thread: DirectThread, + itemToForward: DirectItem, + scope: CoroutineScope, + ): LiveData> { val data = MutableLiveData>() val forwardItemId = itemToForward.itemId if (forwardItemId == null) { @@ -707,116 +656,48 @@ class ThreadManager private constructor( data.postValue(error("threadId is null", null)) return data } - val request = service.forward(thread.threadId, - itemTypeName, - threadId, - forwardItemId) - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - data.postValue(success(Any())) - return - } - val errorBody = response.errorBody() - if (errorBody != null) { - try { - val string = errorBody.string() - val msg = String.format(Locale.US, - "onResponse: url: %s, responseCode: %d, errorBody: %s", - call.request().url().toString(), - response.code(), - string) - Log.e(TAG, msg) - data.postValue(error(msg, null)) - } catch (e: IOException) { - Log.e(TAG, "onResponse: ", e) - data.postValue(error(e.message, null)) - } - return - } - val msg = "onResponse: request was not successful and response error body was null" - Log.e(TAG, msg) - data.postValue(error(msg, null)) + scope.launch(Dispatchers.IO) { + try { + service.forward( + thread.threadId, + itemTypeName, + threadId, + forwardItemId + ) + data.postValue(success(Any())) + } catch (e: Exception) { + Log.e(TAG, "forward: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - fun acceptRequest(): LiveData> { + fun acceptRequest(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() - val request = service.approveRequest(threadId) - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (!response.isSuccessful) { - try { - val string = response.errorBody()?.string() ?: "" - val msg = String.format(Locale.US, - "onResponse: url: %s, responseCode: %d, errorBody: %s", - call.request().url().toString(), - response.code(), - string) - Log.e(TAG, msg) - data.postValue(error(msg, null)) - return - } catch (e: IOException) { - Log.e(TAG, "onResponse: ", e) - } - return - } + scope.launch(Dispatchers.IO) { + try { + service.approveRequest(threadId) data.postValue(success(Any())) + } catch (e: Exception) { + Log.e(TAG, "acceptRequest: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - fun declineRequest(): LiveData> { + fun declineRequest(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() - val request = service.declineRequest(threadId) - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (!response.isSuccessful) { - try { - val string = response.errorBody()?.string() ?: "" - val msg = String.format(Locale.US, - "onResponse: url: %s, responseCode: %d, errorBody: %s", - call.request().url().toString(), - response.code(), - string) - Log.e(TAG, msg) - data.postValue(error(msg, null)) - return - } catch (e: IOException) { - Log.e(TAG, "onResponse: ", e) - } - return - } + scope.launch(Dispatchers.IO) { + try { + service.declineRequest(threadId) data.postValue(success(Any())) + } catch (e: Exception) { + Log.e(TAG, "declineRequest: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } @@ -1105,32 +986,40 @@ class ThreadManager private constructor( } } - fun updateTitle(newTitle: String): LiveData> { + fun updateTitle(newTitle: String, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() - val addUsersRequest = service.updateTitle(threadId, newTitle.trim { it <= ' ' }) - handleDetailsChangeRequest(data, addUsersRequest) + scope.launch(Dispatchers.IO) { + try { + val response = service.updateTitle(threadId, newTitle.trim()) + handleDetailsChangeResponse(data, response) + } catch (e: Exception) { + } + } return data } - fun addMembers(users: Set): LiveData> { + fun addMembers(users: Set, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() - val addUsersRequest = service.addUsers( - threadId, - users.map { obj: User -> obj.pk } - ) - handleDetailsChangeRequest(data, addUsersRequest) + scope.launch(Dispatchers.IO) { + try { + val response = service.addUsers( + threadId, + users.map { obj: User -> obj.pk } + ) + handleDetailsChangeResponse(data, response) + } catch (e: Exception) { + Log.e(TAG, "addMembers: ", e) + data.postValue(error(e.message, null)) + } + } return data } - fun removeMember(user: User): LiveData> { + fun removeMember(user: User, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() - val request = service.removeUsers(threadId, setOf(user.pk)) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } + scope.launch(Dispatchers.IO) { + try { + service.removeUsers(threadId, setOf(user.pk)) data.postValue(success(Any())) var activeUsers = users.value var leftUsersValue = leftUsers.value @@ -1147,13 +1036,11 @@ class ThreadManager private constructor( } val updatedLeftUsers = updatedLeftUsersBuilder.build() setThreadUsers(updatedActiveUsers, updatedLeftUsers) + } catch (e: Exception) { + Log.e(TAG, "removeMember: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } @@ -1162,70 +1049,60 @@ class ThreadManager private constructor( return adminUserIdsValue != null && adminUserIdsValue.contains(user.pk) } - fun makeAdmin(user: User): LiveData> { + fun makeAdmin(user: User, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() if (isAdmin(user)) return data - val request = service.addAdmins(threadId, setOf(user.pk)) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } + scope.launch(Dispatchers.IO) { + try { + service.addAdmins(threadId, setOf(user.pk)) val currentAdminIds = adminUserIds.value val updatedAdminIds = ImmutableList.builder() .addAll(currentAdminIds ?: emptyList()) .add(user.pk) .build() - val currentThread = thread.value ?: return + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.adminUserIds = updatedAdminIds inboxManager.setThread(threadId, thread) } catch (e: CloneNotSupportedException) { - Log.e(TAG, "onResponse: ", e) + Log.e(TAG, "makeAdmin: ", e) } + data.postValue(success(Any())) + } catch (e: Exception) { + Log.e(TAG, "makeAdmin: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - fun removeAdmin(user: User): LiveData> { + fun removeAdmin(user: User, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() if (!isAdmin(user)) return data - val request = service.removeAdmins(threadId, setOf(user.pk)) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } - val currentAdmins = adminUserIds.value ?: return + scope.launch(Dispatchers.IO) { + try { + service.removeAdmins(threadId, setOf(user.pk)) + val currentAdmins = adminUserIds.value ?: return@launch val updatedAdminUserIds = currentAdmins.filter { userId1: Long -> userId1 != user.pk } - val currentThread = thread.value ?: return + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.adminUserIds = updatedAdminUserIds inboxManager.setThread(threadId, thread) } catch (e: CloneNotSupportedException) { - Log.e(TAG, "onResponse: ", e) + Log.e(TAG, "removeAdmin: ", e) } + data.postValue(success(Any())) + } catch (e: Exception) { + Log.e(TAG, "removeAdmin: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - fun mute(): LiveData> { + fun mute(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) val muted = isMuted.value @@ -1233,33 +1110,27 @@ class ThreadManager private constructor( data.postValue(success(Any())) return data } - val request = service.mute(threadId) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } + scope.launch(Dispatchers.IO) { + try { + service.mute(threadId) data.postValue(success(Any())) - val currentThread = thread.value ?: return + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.muted = true inboxManager.setThread(threadId, thread) } catch (e: CloneNotSupportedException) { - Log.e(TAG, "onResponse: ", e) + Log.e(TAG, "mute: ", e) } + } catch (e: Exception) { + Log.e(TAG, "mute: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - fun unmute(): LiveData> { + fun unmute(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) val muted = isMuted.value @@ -1267,33 +1138,27 @@ class ThreadManager private constructor( data.postValue(success(Any())) return data } - val request = service.unmute(threadId) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } + scope.launch(Dispatchers.IO) { + try { + service.unmute(threadId) data.postValue(success(Any())) - val currentThread = thread.value ?: return + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.muted = false inboxManager.setThread(threadId, thread) } catch (e: CloneNotSupportedException) { - Log.e(TAG, "onResponse: ", e) + Log.e(TAG, "unmute: ", e) } + } catch (e: Exception) { + Log.e(TAG, "unmute: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - fun muteMentions(): LiveData> { + fun muteMentions(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) val mentionsMuted = isMentionsMuted.value @@ -1301,33 +1166,27 @@ class ThreadManager private constructor( data.postValue(success(Any())) return data } - val request = service.muteMentions(threadId) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } + scope.launch(Dispatchers.IO) { + try { + service.muteMentions(threadId) data.postValue(success(Any())) - val currentThread = thread.value ?: return + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.mentionsMuted = true inboxManager.setThread(threadId, thread) } catch (e: CloneNotSupportedException) { - Log.e(TAG, "onResponse: ", e) + Log.e(TAG, "muteMentions: ", e) } + } catch (e: Exception) { + Log.e(TAG, "muteMentions: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - fun unmuteMentions(): LiveData> { + fun unmuteMentions(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) val mentionsMuted = isMentionsMuted.value @@ -1335,29 +1194,23 @@ class ThreadManager private constructor( data.postValue(success(Any())) return data } - val request = service.unmuteMentions(threadId) - request.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } + scope.launch(Dispatchers.IO) { + try { + service.unmuteMentions(threadId) data.postValue(success(Any())) - val currentThread = thread.value ?: return + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.mentionsMuted = false inboxManager.setThread(threadId, thread) } catch (e: CloneNotSupportedException) { - Log.e(TAG, "onResponse: ", e) + Log.e(TAG, "unmuteMentions: ", e) } + } catch (e: Exception) { + Log.e(TAG, "unmuteMentions: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } @@ -1421,33 +1274,41 @@ class ThreadManager private constructor( return data } - fun approveUsers(users: List): LiveData> { + fun approveUsers(users: List, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) - val approveUsersRequest = service.approveParticipantRequests( - threadId, - users.map { obj: User -> obj.pk } - ) - handleDetailsChangeRequest(data, approveUsersRequest, object : OnSuccessAction { - override fun onSuccess() { + scope.launch(Dispatchers.IO) { + try { + val response = service.approveParticipantRequests( + threadId, + users.map { obj: User -> obj.pk } + ) + handleDetailsChangeResponse(data, response) pendingUserApproveDenySuccessAction(users) + } catch (e: Exception) { + Log.e(TAG, "approveUsers: ", e) + data.postValue(error(e.message, null)) } - }) + } return data } - fun denyUsers(users: List): LiveData> { + fun denyUsers(users: List, scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) - val approveUsersRequest = service.declineParticipantRequests( - threadId, - users.map { obj: User -> obj.pk } - ) - handleDetailsChangeRequest(data, approveUsersRequest, object : OnSuccessAction { - override fun onSuccess() { + scope.launch(Dispatchers.IO) { + try { + val response = service.declineParticipantRequests( + threadId, + users.map { obj: User -> obj.pk } + ) + handleDetailsChangeResponse(data, response) pendingUserApproveDenySuccessAction(users) + } catch (e: Exception) { + Log.e(TAG, "denyUsers: ", e) + data.postValue(error(e.message, null)) } - }) + } return data } @@ -1467,7 +1328,7 @@ class ThreadManager private constructor( } } - fun approvalRequired(): LiveData> { + fun approvalRequired(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) val approvalRequiredToJoin = isApprovalRequiredToJoin.value @@ -1475,10 +1336,11 @@ class ThreadManager private constructor( data.postValue(success(Any())) return data } - val request = service.approvalRequired(threadId) - handleDetailsChangeRequest(data, request, object : OnSuccessAction { - override fun onSuccess() { - val currentThread = thread.value ?: return + scope.launch(Dispatchers.IO) { + try { + val response = service.approvalRequired(threadId) + handleDetailsChangeResponse(data, response) + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.approvalRequiredForNewMembers = true @@ -1486,12 +1348,15 @@ class ThreadManager private constructor( } catch (e: CloneNotSupportedException) { Log.e(TAG, "onResponse: ", e) } + } catch (e: Exception) { + Log.e(TAG, "approvalRequired: ", e) + data.postValue(error(e.message, null)) } - }) + } return data } - fun approvalNotRequired(): LiveData> { + fun approvalNotRequired(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) val approvalRequiredToJoin = isApprovalRequiredToJoin.value @@ -1499,10 +1364,11 @@ class ThreadManager private constructor( data.postValue(success(Any())) return data } - val request = service.approvalNotRequired(threadId) - handleDetailsChangeRequest(data, request, object : OnSuccessAction { - override fun onSuccess() { - val currentThread = thread.value ?: return + scope.launch(Dispatchers.IO) { + try { + val request = service.approvalNotRequired(threadId) + handleDetailsChangeResponse(data, request) + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.approvalRequiredForNewMembers = false @@ -1510,26 +1376,37 @@ class ThreadManager private constructor( } catch (e: CloneNotSupportedException) { Log.e(TAG, "onResponse: ", e) } + } catch (e: Exception) { + Log.e(TAG, "approvalNotRequired: ", e) + data.postValue(error(e.message, null)) } - }) + } return data } - fun leave(): LiveData> { + fun leave(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) - val request = service.leave(threadId) - handleDetailsChangeRequest(data, request) + scope.launch(Dispatchers.IO) { + try { + val request = service.leave(threadId) + handleDetailsChangeResponse(data, request) + } catch (e: Exception) { + Log.e(TAG, "leave: ", e) + data.postValue(error(e.message, null)) + } + } return data } - fun end(): LiveData> { + fun end(scope: CoroutineScope): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) - val request = service.end(threadId) - handleDetailsChangeRequest(data, request, object : OnSuccessAction { - override fun onSuccess() { - val currentThread = thread.value ?: return + scope.launch(Dispatchers.IO) { + try { + val request = service.end(threadId) + handleDetailsChangeResponse(data, request) + val currentThread = thread.value ?: return@launch try { val thread = currentThread.clone() as DirectThread thread.inputMode = 1 @@ -1537,95 +1414,57 @@ class ThreadManager private constructor( } catch (e: CloneNotSupportedException) { Log.e(TAG, "onResponse: ", e) } + } catch (e: Exception) { + Log.e(TAG, "leave: ", e) + data.postValue(error(e.message, null)) } - }) + } return data } - private fun handleDetailsChangeRequest( + private fun handleDetailsChangeResponse( data: MutableLiveData>, - request: Call, - action: OnSuccessAction? = null, + response: DirectThreadDetailsChangeResponse, ) { - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } - val changeResponse = response.body() - if (changeResponse == null) { - data.postValue(error(R.string.generic_null_response, null)) - return - } - data.postValue(success(Any())) - val thread = changeResponse.thread - if (thread != null) { - setThread(thread, true) - } - action?.onSuccess() - } - - override fun onFailure(call: Call, t: Throwable) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + data.postValue(success(Any())) + val thread = response.thread + if (thread != null) { + setThread(thread, true) + } } - fun markAsSeen(directItem: DirectItem, scope: CoroutineScope): LiveData> { + fun markAsSeen( + directItem: DirectItem, + scope: CoroutineScope, + ): LiveData> { val data = MutableLiveData>() data.postValue(loading(null)) - val request = service.markAsSeen(threadId, directItem) - if (request == null) { - data.postValue(error("request was null", null)) - return data - } - request.enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (currentUser == null) return - if (!response.isSuccessful) { - handleErrorBody(call, response, data) - return - } - val seenResponse = response.body() - if (seenResponse == null) { + scope.launch(Dispatchers.IO) { + try { + val response = service.markAsSeen(threadId, directItem) + if (response == null) { data.postValue(error(R.string.generic_null_response, null)) - return + return@launch } + if (currentUser == null) return@launch inboxManager.fetchUnseenCount(scope) - val payload = seenResponse.payload ?: return + val payload = response.payload ?: return@launch val timestamp = payload.timestamp - val thread = thread.value ?: return + val thread = thread.value ?: return@launch val currentLastSeenAt = thread.lastSeenAt val lastSeenAt = if (currentLastSeenAt == null) HashMap() else HashMap(currentLastSeenAt) lastSeenAt[currentUser.pk] = DirectThreadLastSeenAt(timestamp, directItem.itemId) thread.lastSeenAt = lastSeenAt setThread(thread, true) data.postValue(success(Any())) + } catch (e: Exception) { + Log.e(TAG, "markAsSeen: ", e) + data.postValue(error(e.message, null)) } - - override fun onFailure( - call: Call, - t: Throwable, - ) { - Log.e(TAG, "onFailure: ", t) - data.postValue(error(t.message, null)) - } - }) + } return data } - private interface OnSuccessAction { - fun onSuccess() - } - companion object { private val TAG = ThreadManager::class.java.simpleName private val LOCK = Any() diff --git a/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt b/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt index b3a89804..8eae2d16 100644 --- a/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt +++ b/app/src/main/java/awais/instagrabber/repositories/DirectMessagesRepository.kt @@ -1,7 +1,6 @@ package awais.instagrabber.repositories import awais.instagrabber.repositories.responses.directmessages.* -import retrofit2.Call import retrofit2.http.* interface DirectMessagesRepository { @@ -29,154 +28,154 @@ interface DirectMessagesRepository { @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/add_user/") - fun addUsers( + suspend fun addUsers( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/remove_users/") - fun removeUsers( + suspend fun removeUsers( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/update_title/") - fun updateTitle( + suspend fun updateTitle( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/add_admins/") - fun addAdmins( + suspend fun addAdmins( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/remove_admins/") - fun removeAdmins( + suspend fun removeAdmins( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/items/{itemId}/delete/") - fun deleteItem( + suspend fun deleteItem( @Path("threadId") threadId: String, @Path("itemId") itemId: String, @FieldMap form: Map, - ): Call + ): String @GET("/api/v1/direct_v2/ranked_recipients/") - fun rankedRecipients(@QueryMap queryMap: Map): Call + suspend fun rankedRecipients(@QueryMap queryMap: Map): RankedRecipientsResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/broadcast/forward/") - fun forward(@FieldMap form: Map): Call + suspend fun forward(@FieldMap form: Map): DirectThreadBroadcastResponse @FormUrlEncoded @POST("/api/v1/direct_v2/create_group_thread/") - fun createThread(@FieldMap signedForm: Map): Call + suspend fun createThread(@FieldMap signedForm: Map): DirectThread @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/mute/") - fun mute( + suspend fun mute( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/unmute/") - fun unmute( + suspend fun unmute( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/mute_mentions/") - fun muteMentions( + suspend fun muteMentions( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/unmute_mentions/") - fun unmuteMentions( + suspend fun unmuteMentions( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @GET("/api/v1/direct_v2/threads/{threadId}/participant_requests/") - fun participantRequests( + suspend fun participantRequests( @Path("threadId") threadId: String, @Query("page_size") pageSize: Int, @Query("cursor") cursor: String?, - ): Call + ): DirectThreadParticipantRequestsResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/approve_participant_requests/") - fun approveParticipantRequests( + suspend fun approveParticipantRequests( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/deny_participant_requests/") - fun declineParticipantRequests( + suspend fun declineParticipantRequests( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/approval_required_for_new_members/") - fun approvalRequired( + suspend fun approvalRequired( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/approval_not_required_for_new_members/") - fun approvalNotRequired( + suspend fun approvalNotRequired( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/leave/") - fun leave( + suspend fun leave( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/remove_all_users/") - fun end( + suspend fun end( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): DirectThreadDetailsChangeResponse @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/approve/") - fun approveRequest( + suspend fun approveRequest( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/decline/") - fun declineRequest( + suspend fun declineRequest( @Path("threadId") threadId: String, @FieldMap form: Map, - ): Call + ): String @FormUrlEncoded @POST("/api/v1/direct_v2/threads/{threadId}/items/{itemId}/seen/") - fun markItemSeen( + suspend fun markItemSeen( @Path("threadId") threadId: String, @Path("itemId") itemId: String, @FieldMap form: Map, - ): Call + ): DirectItemSeenResponse } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt index 65588ee1..70b3c361 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectSettingsViewModel.kt @@ -83,23 +83,23 @@ class DirectSettingsViewModel( fun isViewerAdmin(): LiveData = threadManager.isViewerAdmin - fun updateTitle(newTitle: String): LiveData> = threadManager.updateTitle(newTitle) + fun updateTitle(newTitle: String): LiveData> = threadManager.updateTitle(newTitle, viewModelScope) - fun addMembers(users: Set): LiveData> = threadManager.addMembers(users) + fun addMembers(users: Set): LiveData> = threadManager.addMembers(users, viewModelScope) - fun removeMember(user: User): LiveData> = threadManager.removeMember(user) + fun removeMember(user: User): LiveData> = threadManager.removeMember(user, viewModelScope) - private fun makeAdmin(user: User): LiveData> = threadManager.makeAdmin(user) + private fun makeAdmin(user: User): LiveData> = threadManager.makeAdmin(user, viewModelScope) - private fun removeAdmin(user: User): LiveData> = threadManager.removeAdmin(user) + private fun removeAdmin(user: User): LiveData> = threadManager.removeAdmin(user, viewModelScope) - fun mute(): LiveData> = threadManager.mute() + fun mute(): LiveData> = threadManager.mute(viewModelScope) - fun unmute(): LiveData> = threadManager.unmute() + fun unmute(): LiveData> = threadManager.unmute(viewModelScope) - fun muteMentions(): LiveData> = threadManager.muteMentions() + fun muteMentions(): LiveData> = threadManager.muteMentions(viewModelScope) - fun unmuteMentions(): LiveData> = threadManager.unmuteMentions() + fun unmuteMentions(): LiveData> = threadManager.unmuteMentions(viewModelScope) private fun blockUser(user: User): LiveData> = threadManager.blockUser(user, viewModelScope) @@ -109,17 +109,17 @@ class DirectSettingsViewModel( private fun unRestrictUser(user: User): LiveData> = threadManager.unRestrictUser(user, viewModelScope) - fun approveUsers(users: List): LiveData> = threadManager.approveUsers(users) + fun approveUsers(users: List): LiveData> = threadManager.approveUsers(users, viewModelScope) - fun denyUsers(users: List): LiveData> = threadManager.denyUsers(users) + fun denyUsers(users: List): LiveData> = threadManager.denyUsers(users, viewModelScope) - fun approvalRequired(): LiveData> = threadManager.approvalRequired() + fun approvalRequired(): LiveData> = threadManager.approvalRequired(viewModelScope) - fun approvalNotRequired(): LiveData> = threadManager.approvalNotRequired() + fun approvalNotRequired(): LiveData> = threadManager.approvalNotRequired(viewModelScope) - fun leave(): LiveData> = threadManager.leave() + fun leave(): LiveData> = threadManager.leave(viewModelScope) - fun end(): LiveData> = threadManager.end() + fun end(): LiveData> = threadManager.end(viewModelScope) fun createUserOptions(user: User?): ArrayList> { val options: ArrayList> = ArrayList() diff --git a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt index 621481f9..f7626af3 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.kt @@ -144,7 +144,7 @@ class DirectThreadViewModel( } fun unsend(item: DirectItem): LiveData> { - return threadManager.unsend(item) + return threadManager.unsend(item, viewModelScope) } fun sendAnimatedMedia(giphyGif: GiphyGif): LiveData> { @@ -161,11 +161,11 @@ class DirectThreadViewModel( } fun forward(recipients: Set, itemToForward: DirectItem) { - threadManager.forward(recipients, itemToForward) + threadManager.forward(recipients, itemToForward, viewModelScope) } fun forward(recipient: RankedRecipient, itemToForward: DirectItem) { - threadManager.forward(recipient, itemToForward) + threadManager.forward(recipient, itemToForward, viewModelScope) } fun setReplyToItem(item: DirectItem?) { @@ -174,11 +174,11 @@ class DirectThreadViewModel( } fun acceptRequest(): LiveData> { - return threadManager.acceptRequest() + return threadManager.acceptRequest(viewModelScope) } fun declineRequest(): LiveData> { - return threadManager.declineRequest() + return threadManager.declineRequest(viewModelScope) } fun markAsSeen(): LiveData> { @@ -225,6 +225,6 @@ class DirectThreadViewModel( val csrfToken = getCsrfTokenFromCookie(cookie) require(!csrfToken.isNullOrBlank() && viewerId != 0L && deviceUuid.isNotBlank()) { "User is not logged in!" } threadManager = DirectMessagesManager.getThreadManager(threadId, pending, currentUser, contentResolver) - threadManager.fetchPendingRequests() + threadManager.fetchPendingRequests(viewModelScope) } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java index dc8f2c6f..51ab0e9b 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java @@ -26,14 +26,15 @@ import awais.instagrabber.models.Resource; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.UserSearchResponse; import awais.instagrabber.repositories.responses.directmessages.RankedRecipient; -import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; +import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.Debouncer; import awais.instagrabber.utils.RankedRecipientsCache; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.webservices.DirectMessagesService; import awais.instagrabber.webservices.UserService; +import kotlinx.coroutines.Dispatchers; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; @@ -95,37 +96,23 @@ public class UserSearchViewModel extends ViewModel { private void updateRankedRecipientCache() { rankedRecipientsCache.setUpdateInitiated(true); - final Call request = directMessagesService.rankedRecipients(null, null, null); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - if (!response.isSuccessful()) { - handleErrorResponse(response, false); - rankedRecipientsCache.setFailed(true); + directMessagesService.rankedRecipients( + null, + null, + null, + CoroutineUtilsKt.getContinuation((response, throwable) -> { + if (throwable != null) { + Log.e(TAG, "updateRankedRecipientCache: ", throwable); + rankedRecipientsCache.setUpdateInitiated(false); + rankedRecipientsCache.setFailed(true); + continueSearchIfRequired(); + return; + } + rankedRecipientsCache.setResponse(response); rankedRecipientsCache.setUpdateInitiated(false); continueSearchIfRequired(); - return; - } - if (response.body() == null) { - Log.e(TAG, "onResponse: response body is null"); - rankedRecipientsCache.setUpdateInitiated(false); - rankedRecipientsCache.setFailed(true); - continueSearchIfRequired(); - return; - } - rankedRecipientsCache.setResponse(response.body()); - rankedRecipientsCache.setUpdateInitiated(false); - continueSearchIfRequired(); - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - Log.e(TAG, "onFailure: ", t); - rankedRecipientsCache.setUpdateInitiated(false); - rankedRecipientsCache.setFailed(true); - continueSearchIfRequired(); - } - }); + }, Dispatchers.getIO()) + ); } private void continueSearchIfRequired() { @@ -189,39 +176,22 @@ public class UserSearchViewModel extends ViewModel { } private void rankedRecipientSearch() { - searchRequest = directMessagesService.rankedRecipients(searchMode.getName(), showGroups, currentQuery); - //noinspection unchecked - handleRankedRecipientRequest((Call) searchRequest); - } - - - private void handleRankedRecipientRequest(@NonNull final Call request) { - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - if (!response.isSuccessful()) { - handleErrorResponse(response, true); - searchRequest = null; - return; - } - final RankedRecipientsResponse rankedRecipientsResponse = response.body(); - if (rankedRecipientsResponse == null) { - recipients.postValue(Resource.error(R.string.generic_null_response, getCachedRecipients())); - searchRequest = null; - return; - } - final List list = rankedRecipientsResponse.getRankedRecipients(); - recipients.postValue(Resource.success(mergeResponseWithCache(list))); - searchRequest = null; - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - Log.e(TAG, "onFailure: ", t); - recipients.postValue(Resource.error(t.getMessage(), getCachedRecipients())); - searchRequest = null; - } - }); + directMessagesService.rankedRecipients( + searchMode.getName(), + showGroups, + currentQuery, + CoroutineUtilsKt.getContinuation((response, throwable) -> { + if (throwable != null) { + Log.e(TAG, "rankedRecipientSearch: ", throwable); + recipients.postValue(Resource.error(throwable.getMessage(), getCachedRecipients())); + return; + } + final List list = response.getRankedRecipients(); + if (list != null) { + recipients.postValue(Resource.success(mergeResponseWithCache(list))); + } + }, Dispatchers.getIO()) + ); } private void handleRequest(@NonNull final Call request) { diff --git a/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt b/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt index 2ebe7cf0..8005ad10 100644 --- a/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt +++ b/app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.kt @@ -8,7 +8,6 @@ import awais.instagrabber.utils.TextUtils.extractUrls import awais.instagrabber.utils.TextUtils.isEmpty import awais.instagrabber.utils.Utils import org.json.JSONArray -import retrofit2.Call import java.util.* class DirectMessagesService private constructor( @@ -183,10 +182,10 @@ class DirectMessagesService private constructor( return repository.broadcast(broadcastOptions.itemType.value, signedForm) } - fun addUsers( + suspend fun addUsers( threadId: String, userIds: Collection, - ): Call { + ): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -195,10 +194,10 @@ class DirectMessagesService private constructor( return repository.addUsers(threadId, form) } - fun removeUsers( + suspend fun removeUsers( threadId: String, userIds: Collection, - ): Call { + ): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -207,10 +206,10 @@ class DirectMessagesService private constructor( return repository.removeUsers(threadId, form) } - fun updateTitle( + suspend fun updateTitle( threadId: String, title: String, - ): Call { + ): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -219,10 +218,10 @@ class DirectMessagesService private constructor( return repository.updateTitle(threadId, form) } - fun addAdmins( + suspend fun addAdmins( threadId: String, userIds: Collection, - ): Call { + ): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -231,10 +230,10 @@ class DirectMessagesService private constructor( return repository.addAdmins(threadId, form) } - fun removeAdmins( + suspend fun removeAdmins( threadId: String, userIds: Collection, - ): Call { + ): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -243,10 +242,10 @@ class DirectMessagesService private constructor( return repository.removeAdmins(threadId, form) } - fun deleteItem( + suspend fun deleteItem( threadId: String, itemId: String, - ): Call { + ): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -254,11 +253,11 @@ class DirectMessagesService private constructor( return repository.deleteItem(threadId, itemId, form) } - fun rankedRecipients( + suspend fun rankedRecipients( mode: String?, showThreads: Boolean?, query: String?, - ): Call { + ): RankedRecipientsResponse { // String correctedMode = mode; // if (TextUtils.isEmpty(mode) || (!mode.equals("raven") && !mode.equals("reshare"))) { // correctedMode = "raven"; @@ -276,12 +275,12 @@ class DirectMessagesService private constructor( return repository.rankedRecipients(queryMap) } - fun forward( + suspend fun forward( toThreadId: String, itemType: String, fromThreadId: String, itemId: String, - ): Call { + ): DirectThreadBroadcastResponse { val form = mapOf( "action" to "forward_item", "thread_id" to toThreadId, @@ -292,10 +291,10 @@ class DirectMessagesService private constructor( return repository.forward(form) } - fun createThread( + suspend fun createThread( userIds: List, threadTitle: String?, - ): Call { + ): DirectThread { val userIdStringList = userIds.map { it.toString() } val form = mutableMapOf( "_csrftoken" to csrfToken, @@ -310,7 +309,7 @@ class DirectMessagesService private constructor( return repository.createThread(signedForm) } - fun mute(threadId: String): Call { + suspend fun mute(threadId: String): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid @@ -318,7 +317,7 @@ class DirectMessagesService private constructor( return repository.mute(threadId, form) } - fun unmute(threadId: String): Call { + suspend fun unmute(threadId: String): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -326,7 +325,7 @@ class DirectMessagesService private constructor( return repository.unmute(threadId, form) } - fun muteMentions(threadId: String): Call { + suspend fun muteMentions(threadId: String): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -334,7 +333,7 @@ class DirectMessagesService private constructor( return repository.muteMentions(threadId, form) } - fun unmuteMentions(threadId: String): Call { + suspend fun unmuteMentions(threadId: String): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -342,18 +341,18 @@ class DirectMessagesService private constructor( return repository.unmuteMentions(threadId, form) } - fun participantRequests( + suspend fun participantRequests( threadId: String, pageSize: Int, - cursor: String?, - ): Call { + cursor: String? = null, + ): DirectThreadParticipantRequestsResponse { return repository.participantRequests(threadId, pageSize, cursor) } - fun approveParticipantRequests( + suspend fun approveParticipantRequests( threadId: String, userIds: List, - ): Call { + ): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -363,10 +362,10 @@ class DirectMessagesService private constructor( return repository.approveParticipantRequests(threadId, form) } - fun declineParticipantRequests( + suspend fun declineParticipantRequests( threadId: String, userIds: List, - ): Call { + ): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -375,7 +374,7 @@ class DirectMessagesService private constructor( return repository.declineParticipantRequests(threadId, form) } - fun approvalRequired(threadId: String): Call { + suspend fun approvalRequired(threadId: String): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -383,7 +382,7 @@ class DirectMessagesService private constructor( return repository.approvalRequired(threadId, form) } - fun approvalNotRequired(threadId: String): Call { + suspend fun approvalNotRequired(threadId: String): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -391,7 +390,7 @@ class DirectMessagesService private constructor( return repository.approvalNotRequired(threadId, form) } - fun leave(threadId: String): Call { + suspend fun leave(threadId: String): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -399,7 +398,7 @@ class DirectMessagesService private constructor( return repository.leave(threadId, form) } - fun end(threadId: String): Call { + suspend fun end(threadId: String): DirectThreadDetailsChangeResponse { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -424,7 +423,7 @@ class DirectMessagesService private constructor( return repository.fetchPendingInbox(queryMap) } - fun approveRequest(threadId: String): Call { + suspend fun approveRequest(threadId: String): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -432,7 +431,7 @@ class DirectMessagesService private constructor( return repository.approveRequest(threadId, form) } - fun declineRequest(threadId: String): Call { + suspend fun declineRequest(threadId: String): String { val form = mapOf( "_csrftoken" to csrfToken, "_uuid" to deviceUuid, @@ -440,10 +439,10 @@ class DirectMessagesService private constructor( return repository.declineRequest(threadId, form) } - fun markAsSeen( + suspend fun markAsSeen( threadId: String, directItem: DirectItem, - ): Call? { + ): DirectItemSeenResponse? { val itemId = directItem.itemId ?: return null val form = mapOf( "_csrftoken" to csrfToken,