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() {
|
private fun setUpMessageRequestsBar() {
|
||||||
|
val recipient = viewModel.recipient ?: return
|
||||||
binding?.inputBar?.showMediaControls = !isOutgoingMessageRequestThread()
|
binding?.inputBar?.showMediaControls = !isOutgoingMessageRequestThread()
|
||||||
binding?.messageRequestBar?.isVisible = isIncomingMessageRequestThread()
|
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 {
|
binding?.acceptMessageRequestButton?.setOnClickListener {
|
||||||
acceptMessageRequest()
|
acceptMessageRequest()
|
||||||
}
|
}
|
||||||
|
@ -866,11 +871,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||||
|
|
||||||
private fun isIncomingMessageRequestThread(): Boolean {
|
private fun isIncomingMessageRequestThread(): Boolean {
|
||||||
val recipient = viewModel.recipient ?: return false
|
val recipient = viewModel.recipient ?: return false
|
||||||
return !recipient.isGroupRecipient &&
|
return !recipient.isLegacyClosedGroupRecipient &&
|
||||||
|
!recipient.isOpenGroupRecipient &&
|
||||||
!recipient.isApproved &&
|
!recipient.isApproved &&
|
||||||
!recipient.isLocalNumber &&
|
!recipient.isLocalNumber &&
|
||||||
!threadDb.getLastSeenAndHasSent(viewModel.threadId).second() &&
|
!threadDb.getLastSeenAndHasSent(viewModel.threadId).second() &&
|
||||||
threadDb.getMessageCount(viewModel.threadId) > 0
|
(threadDb.getMessageCount(viewModel.threadId) > 0 || recipient.isClosedGroupRecipient)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inputBarEditTextContentChanged(newContent: CharSequence) {
|
override fun inputBarEditTextContentChanged(newContent: CharSequence) {
|
||||||
|
|
|
@ -79,7 +79,7 @@ class ConversationViewModel(
|
||||||
val isMessageRequestThread : Boolean
|
val isMessageRequestThread : Boolean
|
||||||
get() {
|
get() {
|
||||||
val recipient = recipient ?: return false
|
val recipient = recipient ?: return false
|
||||||
return !recipient.isLocalNumber && !recipient.isGroupRecipient && !recipient.isApproved
|
return !recipient.isLocalNumber && !recipient.isLegacyClosedGroupRecipient && !recipient.isOpenGroupRecipient && !recipient.isApproved
|
||||||
}
|
}
|
||||||
|
|
||||||
val canReactToMessages: Boolean
|
val canReactToMessages: Boolean
|
||||||
|
|
|
@ -649,10 +649,12 @@ open class Storage(
|
||||||
for (closedGroup in newClosedGroups) {
|
for (closedGroup in newClosedGroups) {
|
||||||
val recipient = Recipient.from(context, Address.fromSerialized(closedGroup.groupSessionId.hexString()), false)
|
val recipient = Recipient.from(context, Address.fromSerialized(closedGroup.groupSessionId.hexString()), false)
|
||||||
setRecipientApprovedMe(recipient, true)
|
setRecipientApprovedMe(recipient, true)
|
||||||
setRecipientApproved(recipient, true)
|
setRecipientApproved(recipient, !closedGroup.invited)
|
||||||
val threadId = getOrCreateThreadIdFor(recipient.address)
|
val threadId = getOrCreateThreadIdFor(recipient.address)
|
||||||
setPinned(threadId, closedGroup.priority == PRIORITY_PINNED)
|
setPinned(threadId, closedGroup.priority == PRIORITY_PINNED)
|
||||||
pollerFactory.pollerFor(closedGroup.groupSessionId)?.start()
|
if (!closedGroup.invited) {
|
||||||
|
pollerFactory.pollerFor(closedGroup.groupSessionId)?.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (group in lgc) {
|
for (group in lgc) {
|
||||||
|
@ -1219,6 +1221,24 @@ open class Storage(
|
||||||
override fun getMembers(groupPublicKey: String): List<LibSessionGroupMember> =
|
override fun getMembers(groupPublicKey: String): List<LibSessionGroupMember> =
|
||||||
configFactory.getGroupMemberConfig(SessionId.from(groupPublicKey))?.use { it.all() }?.toList() ?: emptyList()
|
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(
|
override fun addClosedGroupInvite(
|
||||||
groupId: SessionId,
|
groupId: SessionId,
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -1228,15 +1248,19 @@ open class Storage(
|
||||||
val recipient = Recipient.from(context, fromSerialized(groupId.hexString()), false)
|
val recipient = Recipient.from(context, fromSerialized(groupId.hexString()), false)
|
||||||
val profileManager = SSKEnvironment.shared.profileManager
|
val profileManager = SSKEnvironment.shared.profileManager
|
||||||
val groups = configFactory.userGroups ?: return
|
val groups = configFactory.userGroups ?: return
|
||||||
|
val shouldAutoApprove = false //TESTING// getRecipientApproved(fromSerialized(invitingAdmin.hexString()))
|
||||||
val closedGroupInfo = GroupInfo.ClosedGroupInfo(
|
val closedGroupInfo = GroupInfo.ClosedGroupInfo(
|
||||||
groupId, byteArrayOf(), authData, PRIORITY_VISIBLE
|
groupId,
|
||||||
|
byteArrayOf(),
|
||||||
|
authData,
|
||||||
|
PRIORITY_VISIBLE,
|
||||||
|
!shouldAutoApprove,
|
||||||
)
|
)
|
||||||
groups.set(closedGroupInfo)
|
groups.set(closedGroupInfo)
|
||||||
configFactory.persist(groups, SnodeAPI.nowWithOffset)
|
configFactory.persist(groups, SnodeAPI.nowWithOffset)
|
||||||
profileManager.setName(context, recipient, name)
|
profileManager.setName(context, recipient, name)
|
||||||
getOrCreateThreadIdFor(recipient.address)
|
getOrCreateThreadIdFor(recipient.address)
|
||||||
setRecipientApprovedMe(recipient, true)
|
setRecipientApprovedMe(recipient, true)
|
||||||
val shouldAutoApprove = false //TESTING// getRecipientApproved(fromSerialized(invitingAdmin.hexString()))
|
|
||||||
setRecipientApproved(recipient, shouldAutoApprove)
|
setRecipientApproved(recipient, shouldAutoApprove)
|
||||||
if (shouldAutoApprove) {
|
if (shouldAutoApprove) {
|
||||||
pollerFactory.pollerFor(groupId)?.start()
|
pollerFactory.pollerFor(groupId)?.start()
|
||||||
|
|
|
@ -439,32 +439,6 @@ public class ThreadDatabase extends Database {
|
||||||
return db.rawQuery(query, null);
|
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() {
|
public long getLatestUnapprovedConversationTimestamp() {
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
@ -510,7 +484,8 @@ public class ThreadDatabase extends Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getUnapprovedConversationList() {
|
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.APPROVED + " = 0 AND " +
|
||||||
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
|
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
|
||||||
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL";
|
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL";
|
||||||
|
|
|
@ -4,6 +4,7 @@ import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.plus
|
import kotlinx.coroutines.plus
|
||||||
|
import network.loki.messenger.libsession_util.util.GroupInfo
|
||||||
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller
|
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPoller
|
||||||
import org.session.libsignal.utilities.SessionId
|
import org.session.libsignal.utilities.SessionId
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
@ -24,7 +25,7 @@ class PollerFactory(private val scope: CoroutineScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startAll() {
|
fun startAll() {
|
||||||
configFactory.userGroups?.allClosedGroupInfo()?.forEach {
|
configFactory.userGroups?.allClosedGroupInfo()?.filterNot(GroupInfo.ClosedGroupInfo::invited)?.forEach {
|
||||||
pollerFor(it.groupSessionId)?.start()
|
pollerFor(it.groupSessionId)?.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +37,7 @@ class PollerFactory(private val scope: CoroutineScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePollers() {
|
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 } }
|
val toRemove = pollers.filter { (id, _) -> id !in currentGroups.map { it.groupSessionId } }
|
||||||
toRemove.forEach { (id, _) ->
|
toRemove.forEach { (id, _) ->
|
||||||
pollers.remove(id)?.stop()
|
pollers.remove(id)?.stop()
|
||||||
|
|
|
@ -334,9 +334,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupMessageRequestsBanner() {
|
private fun setupMessageRequestsBanner() {
|
||||||
val messageRequestCount = threadDb.unapprovedConversationCount
|
val messageRequestCount = threadDb.unapprovedConversationList.use { it.count }
|
||||||
// Set up message requests
|
// Set up message requests
|
||||||
if (messageRequestCount > 0 && !textSecurePreferences.hasHiddenMessageRequests()) {
|
if (messageRequestCount > 0 && !textSecurePreferences.hasHiddenMessageRequests() && messageRequestCount != homeAdapter.requestCount) {
|
||||||
with(ViewMessageRequestBannerBinding.inflate(layoutInflater)) {
|
with(ViewMessageRequestBannerBinding.inflate(layoutInflater)) {
|
||||||
unreadCountTextView.text = messageRequestCount.toString()
|
unreadCountTextView.text = messageRequestCount.toString()
|
||||||
timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(
|
timestampTextView.text = DateUtils.getDisplayFormattedTimeSpanString(
|
||||||
|
@ -352,13 +352,14 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||||
if (hadHeader) homeAdapter.notifyItemChanged(0)
|
if (hadHeader) homeAdapter.notifyItemChanged(0)
|
||||||
else homeAdapter.notifyItemInserted(0)
|
else homeAdapter.notifyItemInserted(0)
|
||||||
}
|
}
|
||||||
} else {
|
} else if (messageRequestCount == 0) {
|
||||||
val hadHeader = homeAdapter.hasHeaderView()
|
val hadHeader = homeAdapter.hasHeaderView()
|
||||||
homeAdapter.header = null
|
homeAdapter.header = null
|
||||||
if (hadHeader) {
|
if (hadHeader) {
|
||||||
homeAdapter.notifyItemRemoved(0)
|
homeAdapter.notifyItemRemoved(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
homeAdapter.requestCount = messageRequestCount
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateLegacyConfigView() {
|
private fun updateLegacyConfigView() {
|
||||||
|
|
|
@ -38,6 +38,7 @@ class HomeAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasHeaderView(): Boolean = header != null
|
fun hasHeaderView(): Boolean = header != null
|
||||||
|
var requestCount = 0
|
||||||
|
|
||||||
private val headerCount: Int
|
private val headerCount: Int
|
||||||
get() = if (header == null) 0 else 1
|
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 ->
|
override suspend fun acceptMessageRequest(threadId: Long, recipient: Recipient): ResultOf<Unit> = suspendCoroutine { continuation ->
|
||||||
storage.setRecipientApproved(recipient, true)
|
storage.setRecipientApproved(recipient, true)
|
||||||
val message = MessageRequestResponse(true)
|
if (recipient.isClosedGroupRecipient) {
|
||||||
MessageSender.send(message, Destination.from(recipient.address), isSyncMessage = recipient.isLocalNumber)
|
storage.respondToClosedGroupInvitation(recipient, true)
|
||||||
.success {
|
} else {
|
||||||
threadDb.setHasSent(threadId, true)
|
val message = MessageRequestResponse(true)
|
||||||
continuation.resume(ResultOf.Success(Unit))
|
MessageSender.send(message, Destination.from(recipient.address), isSyncMessage = recipient.isLocalNumber)
|
||||||
}.fail { error ->
|
.success {
|
||||||
continuation.resumeWithException(error)
|
threadDb.setHasSent(threadId, true)
|
||||||
}
|
continuation.resume(ResultOf.Success(Unit))
|
||||||
|
}.fail { error ->
|
||||||
|
continuation.resumeWithException(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun declineMessageRequest(threadId: Long) {
|
override fun declineMessageRequest(threadId: Long) {
|
||||||
|
|
|
@ -934,6 +934,7 @@
|
||||||
<string name="global_search_messages">Messages</string>
|
<string name="global_search_messages">Messages</string>
|
||||||
<string name="activity_message_requests_title">Message Requests</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_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="accept">Accept</string>
|
||||||
<string name="decline">Decline</string>
|
<string name="decline">Decline</string>
|
||||||
<string name="message_requests_clear_all">Clear All</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);
|
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");
|
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,
|
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;
|
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 secret_field = env->GetFieldID(closed_group_class, "adminKey", "[B");
|
||||||
jfieldID auth_field = env->GetFieldID(closed_group_class, "authData", "[B");
|
jfieldID auth_field = env->GetFieldID(closed_group_class, "authData", "[B");
|
||||||
jfieldID priority_field = env->GetFieldID(closed_group_class, "priority", "J");
|
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);
|
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.auth_data = auth_bytes;
|
||||||
group_info.secretkey = secret_bytes;
|
group_info.secretkey = secret_bytes;
|
||||||
group_info.priority = env->GetLongField(info_serialized, priority_field);
|
group_info.priority = env->GetLongField(info_serialized, priority_field);
|
||||||
|
group_info.invited = env->GetBooleanField(info_serialized, invited_field);
|
||||||
|
|
||||||
return group_info;
|
return group_info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ sealed class GroupInfo {
|
||||||
val adminKey: ByteArray,
|
val adminKey: ByteArray,
|
||||||
val authData: ByteArray,
|
val authData: ByteArray,
|
||||||
val priority: Long,
|
val priority: Long,
|
||||||
|
val invited: Boolean,
|
||||||
): GroupInfo() {
|
): GroupInfo() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -163,6 +163,7 @@ interface StorageProtocol {
|
||||||
// Closed Groups
|
// Closed Groups
|
||||||
fun createNewGroup(groupName: String, groupDescription: String, members: Set<Contact>): Optional<Recipient>
|
fun createNewGroup(groupName: String, groupDescription: String, members: Set<Contact>): Optional<Recipient>
|
||||||
fun getMembers(groupPublicKey: String): List<LibSessionGroupMember>
|
fun getMembers(groupPublicKey: String): List<LibSessionGroupMember>
|
||||||
|
fun respondToClosedGroupInvitation(groupRecipient: Recipient, approved: Boolean)
|
||||||
fun addClosedGroupInvite(groupId: SessionId, name: String, authData: ByteArray, invitingAdmin: SessionId)
|
fun addClosedGroupInvite(groupId: SessionId, name: String, authData: ByteArray, invitingAdmin: SessionId)
|
||||||
fun setGroupInviteCompleteIfNeeded(approved: Boolean, invitee: String, closedGroup: SessionId)
|
fun setGroupInviteCompleteIfNeeded(approved: Boolean, invitee: String, closedGroup: SessionId)
|
||||||
fun getLibSessionClosedGroup(groupSessionId: String): GroupInfo.ClosedGroupInfo?
|
fun getLibSessionClosedGroup(groupSessionId: String): GroupInfo.ClosedGroupInfo?
|
||||||
|
|
Loading…
Reference in New Issue