feat: add in group invited tracking and group message requests in a basic way
This commit is contained in:
parent
60f4b17b57
commit
4caa7681f8
|
@ -831,8 +831,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||
}
|
||||
|
||||
private fun setUpMessageRequestsBar() {
|
||||
val recipient = viewModel.recipient ?: return
|
||||
binding?.inputBar?.showMediaControls = !isOutgoingMessageRequestThread()
|
||||
binding?.messageRequestBar?.isVisible = isIncomingMessageRequestThread()
|
||||
binding?.sendAcceptsTextView?.setText(
|
||||
if (recipient.isClosedGroupRecipient) R.string.message_requests_send_group_notice
|
||||
else R.string.message_requests_send_notice
|
||||
)
|
||||
binding?.acceptMessageRequestButton?.setOnClickListener {
|
||||
acceptMessageRequest()
|
||||
}
|
||||
|
@ -866,11 +871,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||
|
||||
private fun isIncomingMessageRequestThread(): Boolean {
|
||||
val recipient = viewModel.recipient ?: return false
|
||||
return !recipient.isGroupRecipient &&
|
||||
return !recipient.isLegacyClosedGroupRecipient &&
|
||||
!recipient.isOpenGroupRecipient &&
|
||||
!recipient.isApproved &&
|
||||
!recipient.isLocalNumber &&
|
||||
!threadDb.getLastSeenAndHasSent(viewModel.threadId).second() &&
|
||||
threadDb.getMessageCount(viewModel.threadId) > 0
|
||||
(threadDb.getMessageCount(viewModel.threadId) > 0 || recipient.isClosedGroupRecipient)
|
||||
}
|
||||
|
||||
override fun inputBarEditTextContentChanged(newContent: CharSequence) {
|
||||
|
|
|
@ -79,7 +79,7 @@ class ConversationViewModel(
|
|||
val isMessageRequestThread : Boolean
|
||||
get() {
|
||||
val recipient = recipient ?: return false
|
||||
return !recipient.isLocalNumber && !recipient.isGroupRecipient && !recipient.isApproved
|
||||
return !recipient.isLocalNumber && !recipient.isLegacyClosedGroupRecipient && !recipient.isOpenGroupRecipient && !recipient.isApproved
|
||||
}
|
||||
|
||||
val canReactToMessages: Boolean
|
||||
|
|
|
@ -649,10 +649,12 @@ open class Storage(
|
|||
for (closedGroup in newClosedGroups) {
|
||||
val recipient = Recipient.from(context, Address.fromSerialized(closedGroup.groupSessionId.hexString()), false)
|
||||
setRecipientApprovedMe(recipient, true)
|
||||
setRecipientApproved(recipient, true)
|
||||
setRecipientApproved(recipient, !closedGroup.invited)
|
||||
val threadId = getOrCreateThreadIdFor(recipient.address)
|
||||
setPinned(threadId, closedGroup.priority == PRIORITY_PINNED)
|
||||
pollerFactory.pollerFor(closedGroup.groupSessionId)?.start()
|
||||
if (!closedGroup.invited) {
|
||||
pollerFactory.pollerFor(closedGroup.groupSessionId)?.start()
|
||||
}
|
||||
}
|
||||
|
||||
for (group in lgc) {
|
||||
|
@ -1219,6 +1221,24 @@ open class Storage(
|
|||
override fun getMembers(groupPublicKey: String): List<LibSessionGroupMember> =
|
||||
configFactory.getGroupMemberConfig(SessionId.from(groupPublicKey))?.use { it.all() }?.toList() ?: emptyList()
|
||||
|
||||
override fun respondToClosedGroupInvitation(groupRecipient: Recipient, approved: Boolean) {
|
||||
val groups = configFactory.userGroups ?: return
|
||||
val groupSessionId = SessionId.from(groupRecipient.address.serialize())
|
||||
val closedGroupInfo = groups.getClosedGroup(groupSessionId.hexString())?.copy(
|
||||
invited = false
|
||||
) ?: return
|
||||
groups.set(closedGroupInfo)
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
|
||||
pollerFactory.pollerFor(groupSessionId)?.start()
|
||||
val inviteResponse = GroupUpdateInviteResponseMessage.newBuilder()
|
||||
.setIsApproved(true)
|
||||
val responseData = GroupUpdateMessage.newBuilder()
|
||||
.setInviteResponse(inviteResponse)
|
||||
val responseMessage = GroupUpdated(responseData.build())
|
||||
// this will fail the first couple of times :)
|
||||
MessageSender.send(responseMessage, fromSerialized(groupSessionId.hexString()))
|
||||
}
|
||||
|
||||
override fun addClosedGroupInvite(
|
||||
groupId: SessionId,
|
||||
name: String,
|
||||
|
@ -1228,15 +1248,19 @@ open class Storage(
|
|||
val recipient = Recipient.from(context, fromSerialized(groupId.hexString()), false)
|
||||
val profileManager = SSKEnvironment.shared.profileManager
|
||||
val groups = configFactory.userGroups ?: return
|
||||
val shouldAutoApprove = false //TESTING// getRecipientApproved(fromSerialized(invitingAdmin.hexString()))
|
||||
val closedGroupInfo = GroupInfo.ClosedGroupInfo(
|
||||
groupId, byteArrayOf(), authData, PRIORITY_VISIBLE
|
||||
groupId,
|
||||
byteArrayOf(),
|
||||
authData,
|
||||
PRIORITY_VISIBLE,
|
||||
!shouldAutoApprove,
|
||||
)
|
||||
groups.set(closedGroupInfo)
|
||||
configFactory.persist(groups, SnodeAPI.nowWithOffset)
|
||||
profileManager.setName(context, recipient, name)
|
||||
getOrCreateThreadIdFor(recipient.address)
|
||||
setRecipientApprovedMe(recipient, true)
|
||||
val shouldAutoApprove = false //TESTING// getRecipientApproved(fromSerialized(invitingAdmin.hexString()))
|
||||
setRecipientApproved(recipient, shouldAutoApprove)
|
||||
if (shouldAutoApprove) {
|
||||
pollerFactory.pollerFor(groupId)?.start()
|
||||
|
|
|
@ -439,32 +439,6 @@ public class ThreadDatabase extends Database {
|
|||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public int getUnapprovedConversationCount() {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
String query = "SELECT COUNT (*) FROM " + TABLE_NAME +
|
||||
" LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME +
|
||||
" ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS +
|
||||
" LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME +
|
||||
" ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GROUP_ID +
|
||||
" WHERE " + MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " +
|
||||
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0 AND " +
|
||||
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
|
||||
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL";
|
||||
cursor = db.rawQuery(query, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst())
|
||||
return cursor.getInt(0);
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getLatestUnapprovedConversationTimestamp() {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = null;
|
||||
|
@ -510,7 +484,8 @@ public class ThreadDatabase extends Database {
|
|||
}
|
||||
|
||||
public Cursor getUnapprovedConversationList() {
|
||||
String where = MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " +
|
||||
String where = "("+MESSAGE_COUNT + " != 0 OR "+ThreadDatabase.TABLE_NAME+"."+ThreadDatabase.ADDRESS+" LIKE '"+IdPrefix.GROUP.getValue()+"%')" +
|
||||
" AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " +
|
||||
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0 AND " +
|
||||
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
|
||||
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL";
|
||||
|
|
|
@ -4,6 +4,7 @@ import kotlinx.coroutines.CoroutineDispatcher
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.plus
|
||||
import network.loki.messenger.libsession_util.util.GroupInfo
|
||||
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller
|
||||
import org.session.libsignal.utilities.SessionId
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
@ -24,7 +25,7 @@ class PollerFactory(private val scope: CoroutineScope,
|
|||
}
|
||||
|
||||
fun startAll() {
|
||||
configFactory.userGroups?.allClosedGroupInfo()?.forEach {
|
||||
configFactory.userGroups?.allClosedGroupInfo()?.filterNot(GroupInfo.ClosedGroupInfo::invited)?.forEach {
|
||||
pollerFor(it.groupSessionId)?.start()
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +37,7 @@ class PollerFactory(private val scope: CoroutineScope,
|
|||
}
|
||||
|
||||
fun updatePollers() {
|
||||
val currentGroups = configFactory.userGroups?.allClosedGroupInfo() ?: return
|
||||
val currentGroups = configFactory.userGroups?.allClosedGroupInfo()?.filterNot(GroupInfo.ClosedGroupInfo::invited) ?: return
|
||||
val toRemove = pollers.filter { (id, _) -> id !in currentGroups.map { it.groupSessionId } }
|
||||
toRemove.forEach { (id, _) ->
|
||||
pollers.remove(id)?.stop()
|
||||
|
|
|
@ -334,9 +334,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||
}
|
||||
|
||||
private fun setupMessageRequestsBanner() {
|
||||
val messageRequestCount = threadDb.unapprovedConversationCount
|
||||
val messageRequestCount = threadDb.unapprovedConversationList.use { it.count }
|
||||
// Set up message requests
|
||||
if (messageRequestCount > 0 && !textSecurePreferences.hasHiddenMessageRequests()) {
|
||||
if (messageRequestCount > 0 && !textSecurePreferences.hasHiddenMessageRequests() && messageRequestCount != homeAdapter.requestCount) {
|
||||
with(ViewMessageRequestBannerBinding.inflate(layoutInflater)) {
|
||||
unreadCountTextView.text = messageRequestCount.toString()
|
||||
timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(
|
||||
|
@ -352,13 +352,14 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||
if (hadHeader) homeAdapter.notifyItemChanged(0)
|
||||
else homeAdapter.notifyItemInserted(0)
|
||||
}
|
||||
} else {
|
||||
} else if (messageRequestCount == 0) {
|
||||
val hadHeader = homeAdapter.hasHeaderView()
|
||||
homeAdapter.header = null
|
||||
if (hadHeader) {
|
||||
homeAdapter.notifyItemRemoved(0)
|
||||
}
|
||||
}
|
||||
homeAdapter.requestCount = messageRequestCount
|
||||
}
|
||||
|
||||
private fun updateLegacyConfigView() {
|
||||
|
|
|
@ -38,6 +38,7 @@ class HomeAdapter(
|
|||
}
|
||||
|
||||
fun hasHeaderView(): Boolean = header != null
|
||||
var requestCount = 0
|
||||
|
||||
private val headerCount: Int
|
||||
get() = if (header == null) 0 else 1
|
||||
|
|
|
@ -306,14 +306,18 @@ class DefaultConversationRepository @Inject constructor(
|
|||
|
||||
override suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient): ResultOf<Unit> = suspendCoroutine { continuation ->
|
||||
storage.setRecipientApproved(recipient, true)
|
||||
val message = MessageRequestResponse(true)
|
||||
MessageSender.send(message, Destination.from(recipient.address), isSyncMessage = recipient.isLocalNumber)
|
||||
.success {
|
||||
threadDb.setHasSent(threadId, true)
|
||||
continuation.resume(ResultOf.Success(Unit))
|
||||
}.fail { error ->
|
||||
continuation.resumeWithException(error)
|
||||
}
|
||||
if (recipient.isClosedGroupRecipient) {
|
||||
storage.respondToClosedGroupInvitation(recipient, true)
|
||||
} else {
|
||||
val message = MessageRequestResponse(true)
|
||||
MessageSender.send(message, Destination.from(recipient.address), isSyncMessage = recipient.isLocalNumber)
|
||||
.success {
|
||||
threadDb.setHasSent(threadId, true)
|
||||
continuation.resume(ResultOf.Success(Unit))
|
||||
}.fail { error ->
|
||||
continuation.resumeWithException(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun declineMessageRequest(threadId: Long) {
|
||||
|
|
|
@ -934,6 +934,7 @@
|
|||
<string name="global_search_messages">Messages</string>
|
||||
<string name="activity_message_requests_title">Message Requests</string>
|
||||
<string name="message_requests_send_notice">Sending a message to this user will automatically accept their message request and reveal your Session ID.</string>
|
||||
<string name="message_requests_send_group_notice">Sending a message to this group will automatically accept the group invite.</string>
|
||||
<string name="accept">Accept</string>
|
||||
<string name="decline">Decline</string>
|
||||
<string name="message_requests_clear_all">Clear All</string>
|
||||
|
|
|
@ -132,9 +132,10 @@ inline jobject serialize_closed_group_info(JNIEnv* env, session::config::group_i
|
|||
jbyteArray auth_bytes = util::bytes_from_ustring(env, info.auth_data);
|
||||
|
||||
jclass group_info_class = env->FindClass("network/loki/messenger/libsession_util/util/GroupInfo$ClosedGroupInfo");
|
||||
jmethodID constructor = env->GetMethodID(group_info_class, "<init>", "(Lorg/session/libsignal/utilities/SessionId;[B[BJ)V");
|
||||
jmethodID constructor = env->GetMethodID(group_info_class, "<init>",
|
||||
"(Lorg/session/libsignal/utilities/SessionId;[B[BJZ)V");
|
||||
jobject return_object = env->NewObject(group_info_class,constructor,
|
||||
session_id, admin_bytes, auth_bytes, (jlong)info.priority);
|
||||
session_id, admin_bytes, auth_bytes, (jlong)info.priority, info.invited);
|
||||
return return_object;
|
||||
}
|
||||
|
||||
|
@ -144,6 +145,7 @@ inline session::config::group_info deserialize_closed_group_info(JNIEnv* env, jo
|
|||
jfieldID secret_field = env->GetFieldID(closed_group_class, "adminKey", "[B");
|
||||
jfieldID auth_field = env->GetFieldID(closed_group_class, "authData", "[B");
|
||||
jfieldID priority_field = env->GetFieldID(closed_group_class, "priority", "J");
|
||||
jfieldID invited_field = env->GetFieldID(closed_group_class, "invited", "Z");
|
||||
|
||||
|
||||
jobject id_jobject = env->GetObjectField(info_serialized, id_field);
|
||||
|
@ -158,6 +160,7 @@ inline session::config::group_info deserialize_closed_group_info(JNIEnv* env, jo
|
|||
group_info.auth_data = auth_bytes;
|
||||
group_info.secretkey = secret_bytes;
|
||||
group_info.priority = env->GetLongField(info_serialized, priority_field);
|
||||
group_info.invited = env->GetBooleanField(info_serialized, invited_field);
|
||||
|
||||
return group_info;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ sealed class GroupInfo {
|
|||
val adminKey: ByteArray,
|
||||
val authData: ByteArray,
|
||||
val priority: Long,
|
||||
val invited: Boolean,
|
||||
): GroupInfo() {
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -163,6 +163,7 @@ interface StorageProtocol {
|
|||
// Closed Groups
|
||||
fun createNewGroup(groupName: String, groupDescription: String, members: Set<Contact>): Optional<Recipient>
|
||||
fun getMembers(groupPublicKey: String): List<LibSessionGroupMember>
|
||||
fun respondToClosedGroupInvitation(groupRecipient: Recipient, approved: Boolean)
|
||||
fun addClosedGroupInvite(groupId: SessionId, name: String, authData: ByteArray, invitingAdmin: SessionId)
|
||||
fun setGroupInviteCompleteIfNeeded(approved: Boolean, invitee: String, closedGroup: SessionId)
|
||||
fun getLibSessionClosedGroup(groupSessionId: String): GroupInfo.ClosedGroupInfo?
|
||||
|
|
Loading…
Reference in New Issue