DirectMessagesRepository migrated to suspend functions

This commit is contained in:
Ammar Githam 2021-06-01 06:45:36 +09:00
parent 0c77611e22
commit cc1741005b
9 changed files with 427 additions and 673 deletions

View File

@ -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<DirectThread> createThreadRequest =
directMessagesService.createThread(Collections.singletonList(currentStory.getUserId()), null);
createThreadRequest.enqueue(new Callback<DirectThread>() {
@Override
public void onResponse(@NonNull final Call<DirectThread> call, @NonNull final Response<DirectThread> 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<DirectThread> 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;

View File

@ -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<DirectThread> createThreadRequest =
directMessagesService.createThread(Collections.singletonList(profileModel.getPk()), null);
createThreadRequest.enqueue(new Callback<DirectThread>() {
@Override
public void onResponse(@NonNull final Call<DirectThread> call, @NonNull final Response<DirectThread> 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<DirectThread> 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 -> {

View File

@ -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<DirectThread?> {
override fun onResponse(call: Call<DirectThread?>, response: Response<DirectThread?>) {
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<DirectThread?>, t: Throwable) {}
})
}
suspend fun createThread(userPk: Long): DirectThread = service.createThread(listOf(userPk), null)
fun sendMedia(recipients: Set<RankedRecipient>, 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()
}
}

View File

@ -155,79 +155,23 @@ class ThreadManager private constructor(
_fetching.postValue(error(e.message, null))
hasOlder = false
}
// chatsRequest?.enqueue(object : Callback<DirectThreadFeedResponse?> {
// override fun onResponse(call: Call<DirectThreadFeedResponse?>, response: Response<DirectThreadFeedResponse?>) {
// 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<DirectThreadFeedResponse?>, 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<DirectThreadParticipantRequestsResponse?> {
override fun onResponse(
call: Call<DirectThreadParticipantRequestsResponse?>,
response: Response<DirectThreadParticipantRequestsResponse?>,
) {
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<DirectThreadParticipantRequestsResponse?>, 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<Resource<Any?>> {
fun unsend(item: DirectItem, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<String?>, t: Throwable) {
data.postValue(error(t.message, item))
Log.e(TAG, "enqueueRequest: onFailure: ", t)
}
})
}
return data
}
fun forward(recipients: Set<RankedRecipient>, itemToForward: DirectItem) {
fun forward(
recipients: Set<RankedRecipient>,
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<Resource<Any?>> {
private fun forward(
thread: DirectThread,
itemToForward: DirectItem,
scope: CoroutineScope,
): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<DirectThreadBroadcastResponse?> {
override fun onResponse(
call: Call<DirectThreadBroadcastResponse?>,
response: Response<DirectThreadBroadcastResponse?>,
) {
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<DirectThreadBroadcastResponse?>, t: Throwable) {
Log.e(TAG, "onFailure: ", t)
data.postValue(error(t.message, null))
}
})
}
return data
}
fun acceptRequest(): LiveData<Resource<Any?>> {
fun acceptRequest(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
val request = service.approveRequest(threadId)
request.enqueue(object : Callback<String?> {
override fun onResponse(
call: Call<String?>,
response: Response<String?>,
) {
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<String?>, t: Throwable) {
Log.e(TAG, "onFailure: ", t)
data.postValue(error(t.message, null))
}
})
}
return data
}
fun declineRequest(): LiveData<Resource<Any?>> {
fun declineRequest(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
val request = service.declineRequest(threadId)
request.enqueue(object : Callback<String?> {
override fun onResponse(
call: Call<String?>,
response: Response<String?>,
) {
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<String?>, 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<Resource<Any?>> {
fun updateTitle(newTitle: String, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<User>): LiveData<Resource<Any?>> {
fun addMembers(users: Set<User>, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<Resource<Any?>> {
fun removeMember(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
val request = service.removeUsers(threadId, setOf(user.pk))
request.enqueue(object : Callback<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<String?>, 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<Resource<Any?>> {
fun makeAdmin(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
if (isAdmin(user)) return data
val request = service.addAdmins(threadId, setOf(user.pk))
request.enqueue(object : Callback<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<Long>()
.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<String?>, t: Throwable) {
Log.e(TAG, "onFailure: ", t)
data.postValue(error(t.message, null))
}
})
}
return data
}
fun removeAdmin(user: User): LiveData<Resource<Any?>> {
fun removeAdmin(user: User, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
if (!isAdmin(user)) return data
val request = service.removeAdmins(threadId, setOf(user.pk))
request.enqueue(object : Callback<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<String?>, t: Throwable) {
Log.e(TAG, "onFailure: ", t)
data.postValue(error(t.message, null))
}
})
}
return data
}
fun mute(): LiveData<Resource<Any?>> {
fun mute(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<String?>, t: Throwable) {
Log.e(TAG, "onFailure: ", t)
data.postValue(error(t.message, null))
}
})
}
return data
}
fun unmute(): LiveData<Resource<Any?>> {
fun unmute(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<String?>, t: Throwable) {
Log.e(TAG, "onFailure: ", t)
data.postValue(error(t.message, null))
}
})
}
return data
}
fun muteMentions(): LiveData<Resource<Any?>> {
fun muteMentions(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<String?>, t: Throwable) {
Log.e(TAG, "onFailure: ", t)
data.postValue(error(t.message, null))
}
})
}
return data
}
fun unmuteMentions(): LiveData<Resource<Any?>> {
fun unmuteMentions(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<String?> {
override fun onResponse(call: Call<String?>, response: Response<String?>) {
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<String?>, 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<User>): LiveData<Resource<Any?>> {
fun approveUsers(users: List<User>, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<User>): LiveData<Resource<Any?>> {
fun denyUsers(users: List<User>, scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<Resource<Any?>> {
fun approvalRequired(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<Resource<Any?>> {
fun approvalNotRequired(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<Resource<Any?>> {
fun leave(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<Resource<Any?>> {
fun end(scope: CoroutineScope): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<Resource<Any?>>,
request: Call<DirectThreadDetailsChangeResponse?>,
action: OnSuccessAction? = null,
response: DirectThreadDetailsChangeResponse,
) {
request.enqueue(object : Callback<DirectThreadDetailsChangeResponse?> {
override fun onResponse(
call: Call<DirectThreadDetailsChangeResponse?>,
response: Response<DirectThreadDetailsChangeResponse?>,
) {
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<DirectThreadDetailsChangeResponse?>, 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<Resource<Any?>> {
fun markAsSeen(
directItem: DirectItem,
scope: CoroutineScope,
): LiveData<Resource<Any?>> {
val data = MutableLiveData<Resource<Any?>>()
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<DirectItemSeenResponse?> {
override fun onResponse(
call: Call<DirectItemSeenResponse?>,
response: Response<DirectItemSeenResponse?>,
) {
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<DirectItemSeenResponse?>,
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()

View File

@ -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<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): DirectThreadDetailsChangeResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/remove_users/")
fun removeUsers(
suspend fun removeUsers(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): String
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/update_title/")
fun updateTitle(
suspend fun updateTitle(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): DirectThreadDetailsChangeResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/add_admins/")
fun addAdmins(
suspend fun addAdmins(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): String
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/remove_admins/")
fun removeAdmins(
suspend fun removeAdmins(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): 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<String, String>,
): Call<String?>
): String
@GET("/api/v1/direct_v2/ranked_recipients/")
fun rankedRecipients(@QueryMap queryMap: Map<String, String>): Call<RankedRecipientsResponse?>
suspend fun rankedRecipients(@QueryMap queryMap: Map<String, String>): RankedRecipientsResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/broadcast/forward/")
fun forward(@FieldMap form: Map<String, String>): Call<DirectThreadBroadcastResponse?>
suspend fun forward(@FieldMap form: Map<String, String>): DirectThreadBroadcastResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/create_group_thread/")
fun createThread(@FieldMap signedForm: Map<String, String>): Call<DirectThread?>
suspend fun createThread(@FieldMap signedForm: Map<String, String>): DirectThread
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/mute/")
fun mute(
suspend fun mute(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): String
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/unmute/")
fun unmute(
suspend fun unmute(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): String
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/mute_mentions/")
fun muteMentions(
suspend fun muteMentions(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String?>,
): Call<String?>
): String
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/unmute_mentions/")
fun unmuteMentions(
suspend fun unmuteMentions(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): 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?>
): DirectThreadParticipantRequestsResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/approve_participant_requests/")
fun approveParticipantRequests(
suspend fun approveParticipantRequests(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): DirectThreadDetailsChangeResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/deny_participant_requests/")
fun declineParticipantRequests(
suspend fun declineParticipantRequests(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): 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<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): 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<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): DirectThreadDetailsChangeResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/leave/")
fun leave(
suspend fun leave(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): DirectThreadDetailsChangeResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/remove_all_users/")
fun end(
suspend fun end(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<DirectThreadDetailsChangeResponse?>
): DirectThreadDetailsChangeResponse
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/approve/")
fun approveRequest(
suspend fun approveRequest(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): String
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/decline/")
fun declineRequest(
suspend fun declineRequest(
@Path("threadId") threadId: String,
@FieldMap form: Map<String, String>,
): Call<String?>
): 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<String, String>,
): Call<DirectItemSeenResponse?>
): DirectItemSeenResponse
}

View File

@ -83,23 +83,23 @@ class DirectSettingsViewModel(
fun isViewerAdmin(): LiveData<Boolean> = threadManager.isViewerAdmin
fun updateTitle(newTitle: String): LiveData<Resource<Any?>> = threadManager.updateTitle(newTitle)
fun updateTitle(newTitle: String): LiveData<Resource<Any?>> = threadManager.updateTitle(newTitle, viewModelScope)
fun addMembers(users: Set<User>): LiveData<Resource<Any?>> = threadManager.addMembers(users)
fun addMembers(users: Set<User>): LiveData<Resource<Any?>> = threadManager.addMembers(users, viewModelScope)
fun removeMember(user: User): LiveData<Resource<Any?>> = threadManager.removeMember(user)
fun removeMember(user: User): LiveData<Resource<Any?>> = threadManager.removeMember(user, viewModelScope)
private fun makeAdmin(user: User): LiveData<Resource<Any?>> = threadManager.makeAdmin(user)
private fun makeAdmin(user: User): LiveData<Resource<Any?>> = threadManager.makeAdmin(user, viewModelScope)
private fun removeAdmin(user: User): LiveData<Resource<Any?>> = threadManager.removeAdmin(user)
private fun removeAdmin(user: User): LiveData<Resource<Any?>> = threadManager.removeAdmin(user, viewModelScope)
fun mute(): LiveData<Resource<Any?>> = threadManager.mute()
fun mute(): LiveData<Resource<Any?>> = threadManager.mute(viewModelScope)
fun unmute(): LiveData<Resource<Any?>> = threadManager.unmute()
fun unmute(): LiveData<Resource<Any?>> = threadManager.unmute(viewModelScope)
fun muteMentions(): LiveData<Resource<Any?>> = threadManager.muteMentions()
fun muteMentions(): LiveData<Resource<Any?>> = threadManager.muteMentions(viewModelScope)
fun unmuteMentions(): LiveData<Resource<Any?>> = threadManager.unmuteMentions()
fun unmuteMentions(): LiveData<Resource<Any?>> = threadManager.unmuteMentions(viewModelScope)
private fun blockUser(user: User): LiveData<Resource<Any?>> = threadManager.blockUser(user, viewModelScope)
@ -109,17 +109,17 @@ class DirectSettingsViewModel(
private fun unRestrictUser(user: User): LiveData<Resource<Any?>> = threadManager.unRestrictUser(user, viewModelScope)
fun approveUsers(users: List<User>): LiveData<Resource<Any?>> = threadManager.approveUsers(users)
fun approveUsers(users: List<User>): LiveData<Resource<Any?>> = threadManager.approveUsers(users, viewModelScope)
fun denyUsers(users: List<User>): LiveData<Resource<Any?>> = threadManager.denyUsers(users)
fun denyUsers(users: List<User>): LiveData<Resource<Any?>> = threadManager.denyUsers(users, viewModelScope)
fun approvalRequired(): LiveData<Resource<Any?>> = threadManager.approvalRequired()
fun approvalRequired(): LiveData<Resource<Any?>> = threadManager.approvalRequired(viewModelScope)
fun approvalNotRequired(): LiveData<Resource<Any?>> = threadManager.approvalNotRequired()
fun approvalNotRequired(): LiveData<Resource<Any?>> = threadManager.approvalNotRequired(viewModelScope)
fun leave(): LiveData<Resource<Any?>> = threadManager.leave()
fun leave(): LiveData<Resource<Any?>> = threadManager.leave(viewModelScope)
fun end(): LiveData<Resource<Any?>> = threadManager.end()
fun end(): LiveData<Resource<Any?>> = threadManager.end(viewModelScope)
fun createUserOptions(user: User?): ArrayList<Option<String>> {
val options: ArrayList<Option<String>> = ArrayList()

View File

@ -144,7 +144,7 @@ class DirectThreadViewModel(
}
fun unsend(item: DirectItem): LiveData<Resource<Any?>> {
return threadManager.unsend(item)
return threadManager.unsend(item, viewModelScope)
}
fun sendAnimatedMedia(giphyGif: GiphyGif): LiveData<Resource<Any?>> {
@ -161,11 +161,11 @@ class DirectThreadViewModel(
}
fun forward(recipients: Set<RankedRecipient>, 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<Resource<Any?>> {
return threadManager.acceptRequest()
return threadManager.acceptRequest(viewModelScope)
}
fun declineRequest(): LiveData<Resource<Any?>> {
return threadManager.declineRequest()
return threadManager.declineRequest(viewModelScope)
}
fun markAsSeen(): LiveData<Resource<Any?>> {
@ -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)
}
}

View File

@ -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<RankedRecipientsResponse> request = directMessagesService.rankedRecipients(null, null, null);
request.enqueue(new Callback<RankedRecipientsResponse>() {
@Override
public void onResponse(@NonNull final Call<RankedRecipientsResponse> call, @NonNull final Response<RankedRecipientsResponse> 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<RankedRecipientsResponse> 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<RankedRecipientsResponse>) searchRequest);
}
private void handleRankedRecipientRequest(@NonNull final Call<RankedRecipientsResponse> request) {
request.enqueue(new Callback<RankedRecipientsResponse>() {
@Override
public void onResponse(@NonNull final Call<RankedRecipientsResponse> call, @NonNull final Response<RankedRecipientsResponse> 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<RankedRecipient> list = rankedRecipientsResponse.getRankedRecipients();
recipients.postValue(Resource.success(mergeResponseWithCache(list)));
searchRequest = null;
}
@Override
public void onFailure(@NonNull final Call<RankedRecipientsResponse> 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<RankedRecipient> list = response.getRankedRecipients();
if (list != null) {
recipients.postValue(Resource.success(mergeResponseWithCache(list)));
}
}, Dispatchers.getIO())
);
}
private void handleRequest(@NonNull final Call<UserSearchResponse> request) {

View File

@ -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<Long>,
): Call<DirectThreadDetailsChangeResponse?> {
): 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<Long>,
): Call<String?> {
): 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?> {
): 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<Long>,
): Call<String?> {
): 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<Long>,
): Call<String?> {
): 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?> {
): 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?> {
): 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?> {
): 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<Long>,
threadTitle: String?,
): Call<DirectThread?> {
): DirectThread {
val userIdStringList = userIds.map { it.toString() }
val form = mutableMapOf<String, Any>(
"_csrftoken" to csrfToken,
@ -310,7 +309,7 @@ class DirectMessagesService private constructor(
return repository.createThread(signedForm)
}
fun mute(threadId: String): Call<String?> {
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<String?> {
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<String?> {
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<String?> {
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<DirectThreadParticipantRequestsResponse?> {
cursor: String? = null,
): DirectThreadParticipantRequestsResponse {
return repository.participantRequests(threadId, pageSize, cursor)
}
fun approveParticipantRequests(
suspend fun approveParticipantRequests(
threadId: String,
userIds: List<Long>,
): Call<DirectThreadDetailsChangeResponse?> {
): 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<Long>,
): Call<DirectThreadDetailsChangeResponse?> {
): 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<DirectThreadDetailsChangeResponse?> {
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<DirectThreadDetailsChangeResponse?> {
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<DirectThreadDetailsChangeResponse?> {
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<DirectThreadDetailsChangeResponse?> {
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<String?> {
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<String?> {
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?>? {
): DirectItemSeenResponse? {
val itemId = directItem.itemId ?: return null
val form = mapOf(
"_csrftoken" to csrfToken,