refactor: testing and building out new edit closed group capabilities
This commit is contained in:
parent
e67946146c
commit
d8bc838754
|
@ -18,6 +18,7 @@ plugins {
|
||||||
id 'kotlin-kapt'
|
id 'kotlin-kapt'
|
||||||
id 'com.google.dagger.hilt.android'
|
id 'com.google.dagger.hilt.android'
|
||||||
id 'com.google.devtools.ksp' version "$kspVersion"
|
id 'com.google.devtools.ksp' version "$kspVersion"
|
||||||
|
id 'app.cash.molecule'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
|
@ -152,6 +152,10 @@
|
||||||
android:theme="@style/Theme.Session.DayNight.FlatActionBar"
|
android:theme="@style/Theme.Session.DayNight.FlatActionBar"
|
||||||
android:label="@string/blocked_contacts_title"
|
android:label="@string/blocked_contacts_title"
|
||||||
/>
|
/>
|
||||||
|
<activity
|
||||||
|
android:name="org.thoughtcrime.securesms.groups.EditLegacyClosedGroupActivity"
|
||||||
|
android:label="@string/activity_edit_closed_group_title"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
android:name="org.thoughtcrime.securesms.groups.EditClosedGroupActivity"
|
||||||
android:label="@string/activity_edit_closed_group_title"
|
android:label="@string/activity_edit_closed_group_title"
|
||||||
|
|
|
@ -14,8 +14,7 @@ import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity
|
import org.thoughtcrime.securesms.groups.EditLegacyClosedGroupActivity
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
|
||||||
import org.thoughtcrime.securesms.showSessionDialog
|
import org.thoughtcrime.securesms.showSessionDialog
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -164,9 +163,9 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
|
||||||
v === binding.editGroup -> {
|
v === binding.editGroup -> {
|
||||||
val recipient = viewModel.recipient ?: return
|
val recipient = viewModel.recipient ?: return
|
||||||
if (!recipient.isClosedGroupRecipient || !recipient.isLegacyClosedGroupRecipient) return
|
if (!recipient.isClosedGroupRecipient || !recipient.isLegacyClosedGroupRecipient) return
|
||||||
val intent = Intent(this, EditClosedGroupActivity::class.java)
|
val intent = Intent(this, EditLegacyClosedGroupActivity::class.java)
|
||||||
val groupID: String = recipient.address.toGroupString()
|
val groupID: String = recipient.address.toGroupString()
|
||||||
intent.putExtra(EditClosedGroupActivity.groupIDKey, groupID)
|
intent.putExtra(EditLegacyClosedGroupActivity.groupIDKey, groupID)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,8 @@ import org.thoughtcrime.securesms.contacts.SelectContactsActivity
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils
|
import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity
|
import org.thoughtcrime.securesms.groups.EditLegacyClosedGroupActivity
|
||||||
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity.Companion.groupIDKey
|
import org.thoughtcrime.securesms.groups.EditLegacyClosedGroupActivity.Companion.groupIDKey
|
||||||
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
|
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
|
||||||
import org.thoughtcrime.securesms.service.WebRtcCallService
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||||
import org.thoughtcrime.securesms.showSessionDialog
|
import org.thoughtcrime.securesms.showSessionDialog
|
||||||
|
@ -281,7 +281,7 @@ object ConversationMenuHelper {
|
||||||
|
|
||||||
private fun editClosedGroup(context: Context, thread: Recipient) {
|
private fun editClosedGroup(context: Context, thread: Recipient) {
|
||||||
if (!thread.isLegacyClosedGroupRecipient) { return }
|
if (!thread.isLegacyClosedGroupRecipient) { return }
|
||||||
val intent = Intent(context, EditClosedGroupActivity::class.java)
|
val intent = Intent(context, EditLegacyClosedGroupActivity::class.java)
|
||||||
val groupID: String = thread.address.toGroupString()
|
val groupID: String = thread.address.toGroupString()
|
||||||
intent.putExtra(groupIDKey, groupID)
|
intent.putExtra(groupIDKey, groupID)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
|
|
|
@ -1251,6 +1251,9 @@ open class Storage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getLibSessionClosedGroup(groupSessionId: String) =
|
||||||
|
configFactory.userGroups?.getClosedGroup(groupSessionId)
|
||||||
|
|
||||||
override fun setServerCapabilities(server: String, capabilities: List<String>) {
|
override fun setServerCapabilities(server: String, capabilities: List<String>) {
|
||||||
return DatabaseComponent.get(context).lokiAPIDatabase().setServerCapabilities(server, capabilities)
|
return DatabaseComponent.get(context).lokiAPIDatabase().setServerCapabilities(server, capabilities)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,342 +1,30 @@
|
||||||
package org.thoughtcrime.securesms.groups
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import androidx.activity.compose.setContent
|
||||||
import android.view.MenuItem
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import android.view.View
|
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||||
import android.view.inputmethod.EditorInfo
|
import com.ramcosta.composedestinations.navigation.dependency
|
||||||
import android.view.inputmethod.InputMethodManager
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.loader.app.LoaderManager
|
|
||||||
import androidx.loader.content.Loader
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import network.loki.messenger.R
|
|
||||||
import nl.komponents.kovenant.Promise
|
|
||||||
import nl.komponents.kovenant.task
|
|
||||||
import nl.komponents.kovenant.ui.failUi
|
|
||||||
import nl.komponents.kovenant.ui.successUi
|
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
|
||||||
import org.session.libsession.messaging.sending_receiving.groupSizeLimit
|
|
||||||
import org.session.libsession.utilities.Address
|
|
||||||
import org.session.libsession.utilities.GroupUtil
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
|
||||||
import org.session.libsession.utilities.ThemeUtil
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import org.session.libsignal.utilities.toHexString
|
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.contacts.SelectContactsActivity
|
import org.thoughtcrime.securesms.groups.compose.EditGroupViewModel
|
||||||
import org.thoughtcrime.securesms.database.Storage
|
import org.thoughtcrime.securesms.ui.AppTheme
|
||||||
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
|
||||||
import org.thoughtcrime.securesms.groups.ClosedGroupManager.updateLegacyGroup
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideApp
|
|
||||||
import org.thoughtcrime.securesms.util.fadeIn
|
|
||||||
import org.thoughtcrime.securesms.util.fadeOut
|
|
||||||
import java.io.IOException
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
class EditClosedGroupActivity: PassphraseRequiredActionBarActivity() {
|
||||||
|
|
||||||
@Inject
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||||
lateinit var groupConfigFactory: ConfigFactory
|
setContent {
|
||||||
@Inject
|
AppTheme {
|
||||||
lateinit var storage: Storage
|
DestinationsNavHost(
|
||||||
|
navGraph = NavGraphs.editGroup,
|
||||||
private val originalMembers = HashSet<String>()
|
dependenciesContainerBuilder = {
|
||||||
private val zombies = HashSet<String>()
|
dependency(NavGraphs.editGroup) {
|
||||||
private val members = HashSet<String>()
|
EditGroupViewModel.Factory()
|
||||||
private val allMembers: Set<String>
|
}
|
||||||
get() {
|
|
||||||
return members + zombies
|
|
||||||
}
|
|
||||||
private var hasNameChanged = false
|
|
||||||
private var isSelfAdmin = false
|
|
||||||
private var isLoading = false
|
|
||||||
set(newValue) { field = newValue; invalidateOptionsMenu() }
|
|
||||||
|
|
||||||
private lateinit var groupID: String
|
|
||||||
private lateinit var originalName: String
|
|
||||||
private lateinit var name: String
|
|
||||||
|
|
||||||
private var isEditingName = false
|
|
||||||
set(value) {
|
|
||||||
if (field == value) return
|
|
||||||
field = value
|
|
||||||
handleIsEditingNameChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val memberListAdapter by lazy {
|
|
||||||
if (isSelfAdmin)
|
|
||||||
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin, this::onMemberClick)
|
|
||||||
else
|
|
||||||
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var mainContentContainer: LinearLayout
|
|
||||||
private lateinit var cntGroupNameEdit: LinearLayout
|
|
||||||
private lateinit var cntGroupNameDisplay: LinearLayout
|
|
||||||
private lateinit var edtGroupName: EditText
|
|
||||||
private lateinit var emptyStateContainer: LinearLayout
|
|
||||||
private lateinit var lblGroupNameDisplay: TextView
|
|
||||||
private lateinit var loaderContainer: View
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic val groupIDKey = "groupIDKey"
|
|
||||||
private val loaderID = 0
|
|
||||||
val addUsersRequestCode = 124
|
|
||||||
val legacyGroupSizeLimit = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Lifecycle
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
|
||||||
super.onCreate(savedInstanceState, isReady)
|
|
||||||
setContentView(R.layout.activity_edit_closed_group)
|
|
||||||
|
|
||||||
supportActionBar!!.setHomeAsUpIndicator(
|
|
||||||
ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable))
|
|
||||||
|
|
||||||
groupID = intent.getStringExtra(groupIDKey)!!
|
|
||||||
val groupInfo = DatabaseComponent.get(this).groupDatabase().getGroup(groupID).get()
|
|
||||||
originalName = groupInfo.title
|
|
||||||
isSelfAdmin = groupInfo.admins.any{ it.serialize() == TextSecurePreferences.getLocalNumber(this) }
|
|
||||||
|
|
||||||
name = originalName
|
|
||||||
|
|
||||||
mainContentContainer = findViewById(R.id.mainContentContainer)
|
|
||||||
cntGroupNameEdit = findViewById(R.id.cntGroupNameEdit)
|
|
||||||
cntGroupNameDisplay = findViewById(R.id.cntGroupNameDisplay)
|
|
||||||
edtGroupName = findViewById(R.id.edtGroupName)
|
|
||||||
emptyStateContainer = findViewById(R.id.emptyStateContainer)
|
|
||||||
lblGroupNameDisplay = findViewById(R.id.lblGroupNameDisplay)
|
|
||||||
loaderContainer = findViewById(R.id.loaderContainer)
|
|
||||||
|
|
||||||
findViewById<View>(R.id.addMembersClosedGroupButton).setOnClickListener {
|
|
||||||
onAddMembersClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
findViewById<RecyclerView>(R.id.rvUserList).apply {
|
|
||||||
adapter = memberListAdapter
|
|
||||||
layoutManager = LinearLayoutManager(this@EditClosedGroupActivity)
|
|
||||||
}
|
|
||||||
|
|
||||||
lblGroupNameDisplay.text = originalName
|
|
||||||
cntGroupNameDisplay.setOnClickListener { isEditingName = true }
|
|
||||||
findViewById<View>(R.id.btnCancelGroupNameEdit).setOnClickListener { isEditingName = false }
|
|
||||||
findViewById<View>(R.id.btnSaveGroupNameEdit).setOnClickListener { saveName() }
|
|
||||||
edtGroupName.setImeActionLabel(getString(R.string.save), EditorInfo.IME_ACTION_DONE)
|
|
||||||
edtGroupName.setOnEditorActionListener { _, actionId, _ ->
|
|
||||||
when (actionId) {
|
|
||||||
EditorInfo.IME_ACTION_DONE -> {
|
|
||||||
saveName()
|
|
||||||
return@setOnEditorActionListener true
|
|
||||||
}
|
|
||||||
else -> return@setOnEditorActionListener false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks<GroupMembers> {
|
|
||||||
|
|
||||||
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<GroupMembers> {
|
|
||||||
return EditClosedGroupLoader(this@EditClosedGroupActivity, groupID)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<GroupMembers>, groupMembers: GroupMembers) {
|
|
||||||
// We no longer need any subsequent loading events
|
|
||||||
// (they will occur on every activity resume).
|
|
||||||
LoaderManager.getInstance(this@EditClosedGroupActivity).destroyLoader(loaderID)
|
|
||||||
|
|
||||||
members.clear()
|
|
||||||
members.addAll(groupMembers.members.toHashSet())
|
|
||||||
zombies.clear()
|
|
||||||
zombies.addAll(groupMembers.zombieMembers.toHashSet())
|
|
||||||
originalMembers.clear()
|
|
||||||
originalMembers.addAll(members + zombies)
|
|
||||||
updateMembers()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLoaderReset(loader: Loader<GroupMembers>) {
|
|
||||||
updateMembers()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
||||||
menuInflater.inflate(R.menu.menu_edit_closed_group, menu)
|
|
||||||
return allMembers.isNotEmpty() && !isLoading
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Updating
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
when (requestCode) {
|
|
||||||
addUsersRequestCode -> {
|
|
||||||
if (resultCode != RESULT_OK) return
|
|
||||||
if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return
|
|
||||||
|
|
||||||
val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet()
|
|
||||||
members.addAll(selectedContacts)
|
|
||||||
updateMembers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleIsEditingNameChanged() {
|
|
||||||
cntGroupNameEdit.visibility = if (isEditingName) View.VISIBLE else View.INVISIBLE
|
|
||||||
cntGroupNameDisplay.visibility = if (isEditingName) View.INVISIBLE else View.VISIBLE
|
|
||||||
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
||||||
if (isEditingName) {
|
|
||||||
edtGroupName.setText(name)
|
|
||||||
edtGroupName.selectAll()
|
|
||||||
edtGroupName.requestFocus()
|
|
||||||
inputMethodManager.showSoftInput(edtGroupName, 0)
|
|
||||||
} else {
|
|
||||||
inputMethodManager.hideSoftInputFromWindow(edtGroupName.windowToken, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateMembers() {
|
|
||||||
memberListAdapter.setMembers(allMembers)
|
|
||||||
memberListAdapter.setZombieMembers(zombies)
|
|
||||||
|
|
||||||
mainContentContainer.visibility = if (allMembers.isEmpty()) View.GONE else View.VISIBLE
|
|
||||||
emptyStateContainer.visibility = if (allMembers.isEmpty()) View.VISIBLE else View.GONE
|
|
||||||
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Interaction
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
when (item.itemId) {
|
|
||||||
R.id.action_apply -> if (!isLoading) { commitChanges() }
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onMemberClick(member: String) {
|
|
||||||
val bottomSheet = ClosedGroupEditingOptionsBottomSheet()
|
|
||||||
bottomSheet.onRemoveTapped = {
|
|
||||||
if (zombies.contains(member)) zombies.remove(member)
|
|
||||||
else members.remove(member)
|
|
||||||
updateMembers()
|
|
||||||
bottomSheet.dismiss()
|
|
||||||
}
|
|
||||||
bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onAddMembersClick() {
|
|
||||||
val intent = Intent(this@EditClosedGroupActivity, SelectContactsActivity::class.java)
|
|
||||||
intent.putExtra(SelectContactsActivity.usersToExcludeKey, allMembers.toTypedArray())
|
|
||||||
intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add")
|
|
||||||
startActivityForResult(intent, addUsersRequestCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun saveName() {
|
|
||||||
val name = edtGroupName.text.toString().trim()
|
|
||||||
if (name.isEmpty()) {
|
|
||||||
return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_missing_error, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
if (name.length >= 64) {
|
|
||||||
return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_too_long_error, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
this.name = name
|
|
||||||
lblGroupNameDisplay.text = name
|
|
||||||
hasNameChanged = true
|
|
||||||
isEditingName = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun commitChanges() {
|
|
||||||
val hasMemberListChanges = (allMembers != originalMembers)
|
|
||||||
|
|
||||||
if (!hasNameChanged && !hasMemberListChanges) {
|
|
||||||
return finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
val name = if (hasNameChanged) this.name else originalName
|
|
||||||
|
|
||||||
val members = this.allMembers.map {
|
|
||||||
Recipient.from(this, Address.fromSerialized(it), false)
|
|
||||||
}.toSet()
|
|
||||||
val originalMembers = this.originalMembers.map {
|
|
||||||
Recipient.from(this, Address.fromSerialized(it), false)
|
|
||||||
}.toSet()
|
|
||||||
|
|
||||||
var isClosedGroup: Boolean
|
|
||||||
var groupPublicKey: String?
|
|
||||||
try {
|
|
||||||
groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
|
|
||||||
isClosedGroup = DatabaseComponent.get(this).lokiAPIDatabase().isClosedGroup(groupPublicKey)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
groupPublicKey = null
|
|
||||||
isClosedGroup = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (members.isEmpty()) {
|
|
||||||
return Toast.makeText(this, R.string.activity_edit_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
val maxGroupMembers = if (isClosedGroup) groupSizeLimit else legacyGroupSizeLimit
|
|
||||||
if (members.size >= maxGroupMembers) {
|
|
||||||
return Toast.makeText(this, R.string.activity_create_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(this)!!
|
|
||||||
val userAsRecipient = Recipient.from(this, Address.fromSerialized(userPublicKey), false)
|
|
||||||
|
|
||||||
if (!members.contains(userAsRecipient) && !members.map { it.address.toString() }.containsAll(originalMembers.minus(userPublicKey))) {
|
|
||||||
val message = "Can't leave while adding or removing other members."
|
|
||||||
return Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isClosedGroup) {
|
|
||||||
isLoading = true
|
|
||||||
loaderContainer.fadeIn()
|
|
||||||
val promise: Promise<Any, Exception> = if (!members.contains(Recipient.from(this, Address.fromSerialized(userPublicKey), false))) {
|
|
||||||
MessageSender.explicitLeave(groupPublicKey!!, false)
|
|
||||||
} else {
|
|
||||||
task {
|
|
||||||
if (hasNameChanged) {
|
|
||||||
MessageSender.explicitNameChange(groupPublicKey!!, name)
|
|
||||||
}
|
}
|
||||||
members.filterNot { it in originalMembers }.let { adds ->
|
)
|
||||||
if (adds.isNotEmpty()) MessageSender.explicitAddMembers(groupPublicKey!!, adds.map { it.address.serialize() })
|
|
||||||
}
|
|
||||||
originalMembers.filterNot { it in members }.let { removes ->
|
|
||||||
if (removes.isNotEmpty()) MessageSender.explicitRemoveMembers(groupPublicKey!!, removes.map { it.address.serialize() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
promise.successUi {
|
|
||||||
loaderContainer.fadeOut()
|
|
||||||
isLoading = false
|
|
||||||
updateGroupConfig()
|
|
||||||
finish()
|
|
||||||
}.failUi { exception ->
|
|
||||||
val message = if (exception is MessageSender.Error) exception.description else "An error occurred"
|
|
||||||
Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show()
|
|
||||||
loaderContainer.fadeOut()
|
|
||||||
isLoading = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateGroupConfig() {
|
|
||||||
val latestRecipient = storage.getRecipientSettings(Address.fromSerialized(groupID))
|
|
||||||
?: return Log.w("Loki", "No recipient settings when trying to update group config")
|
|
||||||
val latestGroup = storage.getGroup(groupID)
|
|
||||||
?: return Log.w("Loki", "No group record when trying to update group config")
|
|
||||||
groupConfigFactory.updateLegacyGroup(latestRecipient, latestGroup)
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupMembers(val members: List<String>, val zombieMembers: List<String>)
|
|
||||||
}
|
}
|
|
@ -4,13 +4,13 @@ import android.content.Context
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.util.AsyncLoader
|
import org.thoughtcrime.securesms.util.AsyncLoader
|
||||||
|
|
||||||
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<EditClosedGroupActivity.GroupMembers>(context) {
|
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<EditLegacyClosedGroupActivity.GroupMembers>(context) {
|
||||||
|
|
||||||
override fun loadInBackground(): EditClosedGroupActivity.GroupMembers {
|
override fun loadInBackground(): EditLegacyClosedGroupActivity.GroupMembers {
|
||||||
val groupDatabase = DatabaseComponent.get(context).groupDatabase()
|
val groupDatabase = DatabaseComponent.get(context).groupDatabase()
|
||||||
val members = groupDatabase.getGroupMembers(groupID, true)
|
val members = groupDatabase.getGroupMembers(groupID, true)
|
||||||
val zombieMembers = groupDatabase.getGroupZombieMembers(groupID)
|
val zombieMembers = groupDatabase.getGroupZombieMembers(groupID)
|
||||||
return EditClosedGroupActivity.GroupMembers(
|
return EditLegacyClosedGroupActivity.GroupMembers(
|
||||||
members.map {
|
members.map {
|
||||||
it.address.toString()
|
it.address.toString()
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
package org.thoughtcrime.securesms.groups
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.loader.app.LoaderManager
|
||||||
|
import androidx.loader.content.Loader
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import nl.komponents.kovenant.Promise
|
||||||
|
import nl.komponents.kovenant.task
|
||||||
|
import nl.komponents.kovenant.ui.failUi
|
||||||
|
import nl.komponents.kovenant.ui.successUi
|
||||||
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
|
import org.session.libsession.messaging.sending_receiving.groupSizeLimit
|
||||||
|
import org.session.libsession.utilities.Address
|
||||||
|
import org.session.libsession.utilities.GroupUtil
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.ThemeUtil
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.session.libsignal.utilities.Log
|
||||||
|
import org.session.libsignal.utilities.toHexString
|
||||||
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
|
import org.thoughtcrime.securesms.contacts.SelectContactsActivity
|
||||||
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ConfigFactory
|
||||||
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
|
import org.thoughtcrime.securesms.groups.ClosedGroupManager.updateLegacyGroup
|
||||||
|
import org.thoughtcrime.securesms.mms.GlideApp
|
||||||
|
import org.thoughtcrime.securesms.util.fadeIn
|
||||||
|
import org.thoughtcrime.securesms.util.fadeOut
|
||||||
|
import java.io.IOException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class EditLegacyClosedGroupActivity : PassphraseRequiredActionBarActivity() {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var groupConfigFactory: ConfigFactory
|
||||||
|
@Inject
|
||||||
|
lateinit var storage: Storage
|
||||||
|
|
||||||
|
private val originalMembers = HashSet<String>()
|
||||||
|
private val zombies = HashSet<String>()
|
||||||
|
private val members = HashSet<String>()
|
||||||
|
private val allMembers: Set<String>
|
||||||
|
get() {
|
||||||
|
return members + zombies
|
||||||
|
}
|
||||||
|
private var hasNameChanged = false
|
||||||
|
private var isSelfAdmin = false
|
||||||
|
private var isLoading = false
|
||||||
|
set(newValue) { field = newValue; invalidateOptionsMenu() }
|
||||||
|
|
||||||
|
private lateinit var groupID: String
|
||||||
|
private lateinit var originalName: String
|
||||||
|
private lateinit var name: String
|
||||||
|
|
||||||
|
private var isEditingName = false
|
||||||
|
set(value) {
|
||||||
|
if (field == value) return
|
||||||
|
field = value
|
||||||
|
handleIsEditingNameChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val memberListAdapter by lazy {
|
||||||
|
if (isSelfAdmin)
|
||||||
|
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin, this::onMemberClick)
|
||||||
|
else
|
||||||
|
EditClosedGroupMembersAdapter(this, GlideApp.with(this), isSelfAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var mainContentContainer: LinearLayout
|
||||||
|
private lateinit var cntGroupNameEdit: LinearLayout
|
||||||
|
private lateinit var cntGroupNameDisplay: LinearLayout
|
||||||
|
private lateinit var edtGroupName: EditText
|
||||||
|
private lateinit var emptyStateContainer: LinearLayout
|
||||||
|
private lateinit var lblGroupNameDisplay: TextView
|
||||||
|
private lateinit var loaderContainer: View
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic val groupIDKey = "groupIDKey"
|
||||||
|
private val loaderID = 0
|
||||||
|
val addUsersRequestCode = 124
|
||||||
|
val legacyGroupSizeLimit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Lifecycle
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
||||||
|
super.onCreate(savedInstanceState, isReady)
|
||||||
|
setContentView(R.layout.activity_edit_closed_group)
|
||||||
|
|
||||||
|
supportActionBar!!.setHomeAsUpIndicator(
|
||||||
|
ThemeUtil.getThemedDrawableResId(this, R.attr.actionModeCloseDrawable))
|
||||||
|
|
||||||
|
groupID = intent.getStringExtra(groupIDKey)!!
|
||||||
|
val groupInfo = DatabaseComponent.get(this).groupDatabase().getGroup(groupID).get()
|
||||||
|
originalName = groupInfo.title
|
||||||
|
isSelfAdmin = groupInfo.admins.any{ it.serialize() == TextSecurePreferences.getLocalNumber(this) }
|
||||||
|
|
||||||
|
name = originalName
|
||||||
|
|
||||||
|
mainContentContainer = findViewById(R.id.mainContentContainer)
|
||||||
|
cntGroupNameEdit = findViewById(R.id.cntGroupNameEdit)
|
||||||
|
cntGroupNameDisplay = findViewById(R.id.cntGroupNameDisplay)
|
||||||
|
edtGroupName = findViewById(R.id.edtGroupName)
|
||||||
|
emptyStateContainer = findViewById(R.id.emptyStateContainer)
|
||||||
|
lblGroupNameDisplay = findViewById(R.id.lblGroupNameDisplay)
|
||||||
|
loaderContainer = findViewById(R.id.loaderContainer)
|
||||||
|
|
||||||
|
findViewById<View>(R.id.addMembersClosedGroupButton).setOnClickListener {
|
||||||
|
onAddMembersClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
findViewById<RecyclerView>(R.id.rvUserList).apply {
|
||||||
|
adapter = memberListAdapter
|
||||||
|
layoutManager = LinearLayoutManager(this@EditLegacyClosedGroupActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
lblGroupNameDisplay.text = originalName
|
||||||
|
cntGroupNameDisplay.setOnClickListener { isEditingName = true }
|
||||||
|
findViewById<View>(R.id.btnCancelGroupNameEdit).setOnClickListener { isEditingName = false }
|
||||||
|
findViewById<View>(R.id.btnSaveGroupNameEdit).setOnClickListener { saveName() }
|
||||||
|
edtGroupName.setImeActionLabel(getString(R.string.save), EditorInfo.IME_ACTION_DONE)
|
||||||
|
edtGroupName.setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
when (actionId) {
|
||||||
|
EditorInfo.IME_ACTION_DONE -> {
|
||||||
|
saveName()
|
||||||
|
return@setOnEditorActionListener true
|
||||||
|
}
|
||||||
|
else -> return@setOnEditorActionListener false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LoaderManager.getInstance(this).initLoader(loaderID, null, object : LoaderManager.LoaderCallbacks<GroupMembers> {
|
||||||
|
|
||||||
|
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<GroupMembers> {
|
||||||
|
return EditClosedGroupLoader(this@EditLegacyClosedGroupActivity, groupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLoadFinished(loader: Loader<GroupMembers>, groupMembers: GroupMembers) {
|
||||||
|
// We no longer need any subsequent loading events
|
||||||
|
// (they will occur on every activity resume).
|
||||||
|
LoaderManager.getInstance(this@EditLegacyClosedGroupActivity).destroyLoader(loaderID)
|
||||||
|
|
||||||
|
members.clear()
|
||||||
|
members.addAll(groupMembers.members.toHashSet())
|
||||||
|
zombies.clear()
|
||||||
|
zombies.addAll(groupMembers.zombieMembers.toHashSet())
|
||||||
|
originalMembers.clear()
|
||||||
|
originalMembers.addAll(members + zombies)
|
||||||
|
updateMembers()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLoaderReset(loader: Loader<GroupMembers>) {
|
||||||
|
updateMembers()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.menu_edit_closed_group, menu)
|
||||||
|
return allMembers.isNotEmpty() && !isLoading
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Updating
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
when (requestCode) {
|
||||||
|
addUsersRequestCode -> {
|
||||||
|
if (resultCode != RESULT_OK) return
|
||||||
|
if (data == null || data.extras == null || !data.hasExtra(SelectContactsActivity.selectedContactsKey)) return
|
||||||
|
|
||||||
|
val selectedContacts = data.extras!!.getStringArray(SelectContactsActivity.selectedContactsKey)!!.toSet()
|
||||||
|
members.addAll(selectedContacts)
|
||||||
|
updateMembers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIsEditingNameChanged() {
|
||||||
|
cntGroupNameEdit.visibility = if (isEditingName) View.VISIBLE else View.INVISIBLE
|
||||||
|
cntGroupNameDisplay.visibility = if (isEditingName) View.INVISIBLE else View.VISIBLE
|
||||||
|
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
if (isEditingName) {
|
||||||
|
edtGroupName.setText(name)
|
||||||
|
edtGroupName.selectAll()
|
||||||
|
edtGroupName.requestFocus()
|
||||||
|
inputMethodManager.showSoftInput(edtGroupName, 0)
|
||||||
|
} else {
|
||||||
|
inputMethodManager.hideSoftInputFromWindow(edtGroupName.windowToken, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMembers() {
|
||||||
|
memberListAdapter.setMembers(allMembers)
|
||||||
|
memberListAdapter.setZombieMembers(zombies)
|
||||||
|
|
||||||
|
mainContentContainer.visibility = if (allMembers.isEmpty()) View.GONE else View.VISIBLE
|
||||||
|
emptyStateContainer.visibility = if (allMembers.isEmpty()) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Interaction
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.action_apply -> if (!isLoading) { commitChanges() }
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onMemberClick(member: String) {
|
||||||
|
val bottomSheet = ClosedGroupEditingOptionsBottomSheet()
|
||||||
|
bottomSheet.onRemoveTapped = {
|
||||||
|
if (zombies.contains(member)) zombies.remove(member)
|
||||||
|
else members.remove(member)
|
||||||
|
updateMembers()
|
||||||
|
bottomSheet.dismiss()
|
||||||
|
}
|
||||||
|
bottomSheet.show(supportFragmentManager, "GroupEditingOptionsBottomSheet")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onAddMembersClick() {
|
||||||
|
val intent = Intent(this@EditLegacyClosedGroupActivity, SelectContactsActivity::class.java)
|
||||||
|
intent.putExtra(SelectContactsActivity.usersToExcludeKey, allMembers.toTypedArray())
|
||||||
|
intent.putExtra(SelectContactsActivity.emptyStateTextKey, "No contacts to add")
|
||||||
|
startActivityForResult(intent, addUsersRequestCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveName() {
|
||||||
|
val name = edtGroupName.text.toString().trim()
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_missing_error, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
if (name.length >= 64) {
|
||||||
|
return Toast.makeText(this, R.string.activity_edit_closed_group_group_name_too_long_error, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
this.name = name
|
||||||
|
lblGroupNameDisplay.text = name
|
||||||
|
hasNameChanged = true
|
||||||
|
isEditingName = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun commitChanges() {
|
||||||
|
val hasMemberListChanges = (allMembers != originalMembers)
|
||||||
|
|
||||||
|
if (!hasNameChanged && !hasMemberListChanges) {
|
||||||
|
return finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = if (hasNameChanged) this.name else originalName
|
||||||
|
|
||||||
|
val members = this.allMembers.map {
|
||||||
|
Recipient.from(this, Address.fromSerialized(it), false)
|
||||||
|
}.toSet()
|
||||||
|
val originalMembers = this.originalMembers.map {
|
||||||
|
Recipient.from(this, Address.fromSerialized(it), false)
|
||||||
|
}.toSet()
|
||||||
|
|
||||||
|
var isClosedGroup: Boolean
|
||||||
|
var groupPublicKey: String?
|
||||||
|
try {
|
||||||
|
groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
|
||||||
|
isClosedGroup = DatabaseComponent.get(this).lokiAPIDatabase().isClosedGroup(groupPublicKey)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
groupPublicKey = null
|
||||||
|
isClosedGroup = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (members.isEmpty()) {
|
||||||
|
return Toast.makeText(this, R.string.activity_edit_closed_group_not_enough_group_members_error, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
val maxGroupMembers = if (isClosedGroup) groupSizeLimit else legacyGroupSizeLimit
|
||||||
|
if (members.size >= maxGroupMembers) {
|
||||||
|
return Toast.makeText(this, R.string.activity_create_closed_group_too_many_group_members_error, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
val userPublicKey = TextSecurePreferences.getLocalNumber(this)!!
|
||||||
|
val userAsRecipient = Recipient.from(this, Address.fromSerialized(userPublicKey), false)
|
||||||
|
|
||||||
|
if (!members.contains(userAsRecipient) && !members.map { it.address.toString() }.containsAll(originalMembers.minus(userPublicKey))) {
|
||||||
|
val message = "Can't leave while adding or removing other members."
|
||||||
|
return Toast.makeText(this@EditLegacyClosedGroupActivity, message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isClosedGroup) {
|
||||||
|
isLoading = true
|
||||||
|
loaderContainer.fadeIn()
|
||||||
|
val promise: Promise<Any, Exception> = if (!members.contains(Recipient.from(this, Address.fromSerialized(userPublicKey), false))) {
|
||||||
|
MessageSender.explicitLeave(groupPublicKey!!, false)
|
||||||
|
} else {
|
||||||
|
task {
|
||||||
|
if (hasNameChanged) {
|
||||||
|
MessageSender.explicitNameChange(groupPublicKey!!, name)
|
||||||
|
}
|
||||||
|
members.filterNot { it in originalMembers }.let { adds ->
|
||||||
|
if (adds.isNotEmpty()) MessageSender.explicitAddMembers(groupPublicKey!!, adds.map { it.address.serialize() })
|
||||||
|
}
|
||||||
|
originalMembers.filterNot { it in members }.let { removes ->
|
||||||
|
if (removes.isNotEmpty()) MessageSender.explicitRemoveMembers(groupPublicKey!!, removes.map { it.address.serialize() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
promise.successUi {
|
||||||
|
loaderContainer.fadeOut()
|
||||||
|
isLoading = false
|
||||||
|
updateGroupConfig()
|
||||||
|
finish()
|
||||||
|
}.failUi { exception ->
|
||||||
|
val message = if (exception is MessageSender.Error) exception.description else "An error occurred"
|
||||||
|
Toast.makeText(this@EditLegacyClosedGroupActivity, message, Toast.LENGTH_LONG).show()
|
||||||
|
loaderContainer.fadeOut()
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateGroupConfig() {
|
||||||
|
val latestRecipient = storage.getRecipientSettings(Address.fromSerialized(groupID))
|
||||||
|
?: return Log.w("Loki", "No recipient settings when trying to update group config")
|
||||||
|
val latestGroup = storage.getGroup(groupID)
|
||||||
|
?: return Log.w("Loki", "No group record when trying to update group config")
|
||||||
|
groupConfigFactory.updateLegacyGroup(latestRecipient, latestGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
class GroupMembers(val members: List<String>, val zombieMembers: List<String>)
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package org.thoughtcrime.securesms.groups.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.cash.molecule.RecompositionMode
|
||||||
|
import app.cash.molecule.RecompositionMode.Immediate
|
||||||
|
import app.cash.molecule.launchMolecule
|
||||||
|
import com.google.android.gms.auth.api.signin.internal.Storage
|
||||||
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
|
import org.session.libsession.database.StorageProtocol
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@EditGroupNavGraph(start = true)
|
||||||
|
@Composable
|
||||||
|
@Destination
|
||||||
|
fun EditClosedGroupScreen(
|
||||||
|
navigator: DestinationsNavigator,
|
||||||
|
viewModel: EditGroupViewModel
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class EditGroupViewModel @Inject constructor(private val groupSessionId: String,
|
||||||
|
private val storage: StorageProtocol): ViewModel() {
|
||||||
|
|
||||||
|
val viewState = viewModelScope.launchMolecule(Immediate) {
|
||||||
|
|
||||||
|
val closedGroup = remember {
|
||||||
|
// storage.getLibSessionClosedGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class EditGroupState(
|
||||||
|
val viewState: EditGroupViewState,
|
||||||
|
val eventSink: (Unit)->Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class EditGroupViewState {
|
||||||
|
data object NoOp: EditGroupViewState()
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.thoughtcrime.securesms.groups.compose
|
||||||
|
|
||||||
|
import com.ramcosta.composedestinations.annotation.NavGraph
|
||||||
|
|
||||||
|
@NavGraph
|
||||||
|
annotation class EditGroupNavGraph(
|
||||||
|
val start: Boolean = false
|
||||||
|
)
|
|
@ -4,7 +4,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
tools:context="org.thoughtcrime.securesms.groups.EditClosedGroupActivity">
|
tools:context="org.thoughtcrime.securesms.groups.EditLegacyClosedGroupActivity">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/mainContentContainer"
|
android:id="@+id/mainContentContainer"
|
||||||
|
|
|
@ -17,6 +17,7 @@ buildscript {
|
||||||
classpath "com.google.gms:google-services:$googleServicesVersion"
|
classpath "com.google.gms:google-services:$googleServicesVersion"
|
||||||
classpath "com.squareup:javapoet:1.13.0"
|
classpath "com.squareup:javapoet:1.13.0"
|
||||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$daggerVersion"
|
classpath "com.google.dagger:hilt-android-gradle-plugin:$daggerVersion"
|
||||||
|
classpath 'app.cash.molecule:molecule-gradle-plugin:1.2.1'
|
||||||
if (project.hasProperty('huawei')) classpath 'com.huawei.agconnect:agcp:1.9.1.300'
|
if (project.hasProperty('huawei')) classpath 'com.huawei.agconnect:agcp:1.9.1.300'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.session.libsession.database
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import network.loki.messenger.libsession_util.Config
|
import network.loki.messenger.libsession_util.Config
|
||||||
|
import network.loki.messenger.libsession_util.util.Conversation
|
||||||
|
import network.loki.messenger.libsession_util.util.GroupInfo
|
||||||
import org.session.libsession.messaging.BlindedIdMapping
|
import org.session.libsession.messaging.BlindedIdMapping
|
||||||
import org.session.libsession.messaging.calls.CallMessageType
|
import org.session.libsession.messaging.calls.CallMessageType
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
import org.session.libsession.messaging.contacts.Contact
|
||||||
|
@ -32,6 +34,7 @@ import org.session.libsession.utilities.recipients.Recipient.RecipientSettings
|
||||||
import org.session.libsignal.crypto.ecc.ECKeyPair
|
import org.session.libsignal.crypto.ecc.ECKeyPair
|
||||||
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
||||||
import org.session.libsignal.messages.SignalServiceGroup
|
import org.session.libsignal.messages.SignalServiceGroup
|
||||||
|
import org.session.libsignal.protos.SignalServiceProtos.ConfigurationMessage.ClosedGroup
|
||||||
import org.session.libsignal.utilities.SessionId
|
import org.session.libsignal.utilities.SessionId
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
import network.loki.messenger.libsession_util.util.Contact as LibSessionContact
|
import network.loki.messenger.libsession_util.util.Contact as LibSessionContact
|
||||||
|
@ -161,6 +164,7 @@ interface StorageProtocol {
|
||||||
fun getMembers(groupPublicKey: String): List<LibSessionGroupMember>
|
fun getMembers(groupPublicKey: String): List<LibSessionGroupMember>
|
||||||
fun acceptClosedGroupInvite(groupId: SessionId, name: String, authData: ByteArray, invitingAdmin: SessionId)
|
fun acceptClosedGroupInvite(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?
|
||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
fun getAllGroups(includeInactive: Boolean): List<GroupRecord>
|
fun getAllGroups(includeInactive: Boolean): List<GroupRecord>
|
||||||
|
|
Loading…
Reference in New Issue