From e80f463bd12aa530d8cc0a62e3847a200a87f47e Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 16 Sep 2024 09:03:17 +1000 Subject: [PATCH 1/7] Listening to changes in community write access In order to allow the showing and hiding of the input bar dynamically --- .../conversation/v2/ConversationActivityV2.kt | 3 +- .../conversation/v2/ConversationViewModel.kt | 29 +++++++++++++++++-- .../securesms/groups/OpenGroupManager.kt | 13 +++++++++ .../v2/ConversationViewModelTest.kt | 4 +-- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index d21574ec96..0ec98824b3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -710,7 +710,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // called from onCreate private fun setUpInputBar() { - binding.inputBar.isGone = viewModel.hidesInputBar() binding.inputBar.delegate = this binding.inputBarRecordingView.delegate = this // GIF button @@ -854,6 +853,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // Conversation should be deleted now, just go back finish() } + + binding.inputBar.isGone = uiState.hideInputBar } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index b0a541a9e8..06515f40ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -9,8 +9,12 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.session.libsession.database.MessageDataProvider @@ -29,6 +33,7 @@ import org.thoughtcrime.securesms.audio.AudioSlidePlayer import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord +import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.repository.ConversationRepository import java.util.UUID @@ -65,6 +70,8 @@ class ConversationViewModel( } } + private var communityWriteAccessJob: Job? = null + private var _openGroup: RetrieveOnce = RetrieveOnce { storage.getOpenGroup(threadId) } @@ -105,6 +112,23 @@ class ConversationViewModel( } } } + + // listen to community write access updates from this point + communityWriteAccessJob?.cancel() + communityWriteAccessJob = viewModelScope.launch { + OpenGroupManager.getCommunitiesWriteAccessFlow() + .map { it[openGroup?.server] } + .filterNotNull() + .collect{ + // update our community object + _openGroup.updateTo(openGroup?.copy(canWrite = it)) + // when we get an update on the write access of a community + // we need to update the input text accordingly + _uiState.update { + it.copy(hideInputBar = shouldHideInputBar()) + } + } + } } override fun onCleared() { @@ -267,7 +291,7 @@ class ConversationViewModel( * - We are dealing with a contact from a community (blinded recipient) that does not allow * requests form community members */ - fun hidesInputBar(): Boolean = openGroup?.canWrite == false || + private fun shouldHideInputBar(): Boolean = openGroup?.canWrite == false || blindedRecipient?.blocksCommunityMessageRequests == true fun legacyBannerRecipient(context: Context): Recipient? = recipient?.run { @@ -311,7 +335,8 @@ data class UiMessage(val id: Long, val message: String) data class ConversationUiState( val uiMessages: List = emptyList(), val isMessageRequestAccepted: Boolean? = null, - val conversationExists: Boolean + val conversationExists: Boolean, + val hideInputBar: Boolean = false ) data class RetrieveOnce(val retrieval: () -> T?) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt index b8f3ba8012..5fae172dde 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt @@ -4,6 +4,9 @@ import android.content.Context import android.widget.Toast import androidx.annotation.WorkerThread import com.squareup.phrase.Phrase +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import java.util.concurrent.Executors import network.loki.messenger.R import okhttp3.HttpUrl.Companion.toHttpUrlOrNull @@ -39,6 +42,9 @@ object OpenGroupManager { return true } + // flow holding information on write access for our current communities + private val _communityWriteAccess: MutableStateFlow> = MutableStateFlow(emptyMap()) + fun startPolling() { if (isPolling) { return } isPolling = true @@ -66,6 +72,8 @@ object OpenGroupManager { } } + fun getCommunitiesWriteAccessFlow() = _communityWriteAccess.asStateFlow() + @WorkerThread fun add(server: String, room: String, publicKey: String, context: Context): Pair { val openGroupID = "$server.$room" @@ -167,6 +175,11 @@ object OpenGroupManager { val openGroupID = "${openGroup.server}.${openGroup.room}" val threadID = GroupManager.getOpenGroupThreadID(openGroupID, context) threadDB.setOpenGroupChat(openGroup, threadID) + + // update write access for this community + val writeAccesses = _communityWriteAccess.value.toMutableMap() + writeAccesses[openGroup.server] = openGroup.canWrite + _communityWriteAccess.value = writeAccesses } fun isUserModerator(context: Context, groupId: String, standardPublicKey: String, blindedPublicKey: String? = null): Boolean { diff --git a/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt index 1bd6a63c7f..936416d922 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt @@ -203,7 +203,7 @@ class ConversationViewModelTest: BaseViewModelTest() { @Test fun `local recipient should have input and no blinded recipient`() { whenever(recipient.isLocalNumber).thenReturn(true) - assertThat(viewModel.hidesInputBar(), equalTo(false)) + assertThat(viewModel.uiState.value.hideInputBar, equalTo(false)) assertThat(viewModel.blindedRecipient, nullValue()) } @@ -215,7 +215,7 @@ class ConversationViewModelTest: BaseViewModelTest() { } whenever(repository.maybeGetBlindedRecipient(recipient)).thenReturn(blinded) assertThat(viewModel.blindedRecipient, notNullValue()) - assertThat(viewModel.hidesInputBar(), equalTo(true)) + assertThat(viewModel.uiState.value.hideInputBar, equalTo(true)) } } \ No newline at end of file From cc63fa3ecaf0c951aa5144ee613232d3c8e42b9e Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 16 Sep 2024 09:15:23 +1000 Subject: [PATCH 2/7] Removing uneeded line in the "Report a bug" help category in settings --- .../securesms/conversation/v2/ConversationActivityV2.kt | 2 +- app/src/main/res/xml/preferences_help.xml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 0ec98824b3..089461c706 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -853,7 +853,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // Conversation should be deleted now, just go back finish() } - + binding.inputBar.isGone = uiState.hideInputBar } } diff --git a/app/src/main/res/xml/preferences_help.xml b/app/src/main/res/xml/preferences_help.xml index e5ff8578e5..a05855c476 100644 --- a/app/src/main/res/xml/preferences_help.xml +++ b/app/src/main/res/xml/preferences_help.xml @@ -7,9 +7,6 @@ android:title="@string/helpReportABug" android:summary="@string/helpReportABugExportLogsDescription" android:widgetLayout="@layout/export_logs_widget" /> - - - From 0d158b62e44908bbf80b72a430ae2a3160771b7b Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 16 Sep 2024 09:29:19 +1000 Subject: [PATCH 3/7] Cleaning up modals for platform consistency --- .../conversation/v2/ConversationActivityV2.kt | 17 +++++++++++++---- .../messagerequests/MessageRequestsActivity.kt | 7 ++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 089461c706..c7d1304266 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -949,11 +949,20 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe block(deleteThread = true) } binding.declineMessageRequestButton.setOnClickListener { - viewModel.declineMessageRequest() - lifecycleScope.launch(Dispatchers.IO) { - ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@ConversationActivityV2) + fun doDecline() { + viewModel.declineMessageRequest() + lifecycleScope.launch(Dispatchers.IO) { + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@ConversationActivityV2) + } + finish() + } + + showSessionDialog { + title(R.string.delete) + text(resources.getString(R.string.messageRequestsDelete)) + dangerButton(R.string.delete) { doDecline() } + button(R.string.cancel) } - finish() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt index 93f79d2b16..4d280a47ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt @@ -108,7 +108,7 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat showSessionDialog { title(R.string.delete) text(resources.getString(R.string.messageRequestsDelete)) - button(R.string.delete) { doDecline() } + dangerButton(R.string.delete) { doDecline() } button(R.string.cancel) } } @@ -129,9 +129,10 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat } showSessionDialog { + title(resources.getString(R.string.clearAll)) text(resources.getString(R.string.messageRequestsClearAllExplanation)) - button(R.string.yes) { doDeleteAllAndBlock() } - button(R.string.no) + dangerButton(R.string.clear) { doDeleteAllAndBlock() } + button(R.string.cancel) } } } From 915c6173646bde3902da28fe7ae771e508170182 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 16 Sep 2024 09:30:55 +1000 Subject: [PATCH 4/7] More dialog platform consistency --- .../securesms/conversation/v2/dialogs/LinkPreviewDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt index 416a796ea6..d9e6e22a4a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt @@ -18,7 +18,7 @@ class LinkPreviewDialog(private val onEnabled: () -> Unit) : DialogFragment() { title(R.string.linkPreviewsEnable) val txt = context.getSubbedCharSequence(R.string.linkPreviewsFirstDescription, APP_NAME_KEY to APP_NAME) text(txt) - button(R.string.enable) { enable() } + dangerButton(R.string.enable) { enable() } cancelButton { dismiss() } } From 1f6a1b13b2282d2e188487d6e48543807126c9f2 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 16 Sep 2024 09:35:19 +1000 Subject: [PATCH 5/7] Using groupId as the key instead of the server alone --- .../securesms/conversation/v2/ConversationViewModel.kt | 10 +++++++--- .../thoughtcrime/securesms/groups/OpenGroupManager.kt | 5 ++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index 06515f40ca..1e995bab01 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -117,15 +117,19 @@ class ConversationViewModel( communityWriteAccessJob?.cancel() communityWriteAccessJob = viewModelScope.launch { OpenGroupManager.getCommunitiesWriteAccessFlow() - .map { it[openGroup?.server] } + .map { + if(openGroup?.groupId != null) + it[openGroup?.groupId] + else null + } .filterNotNull() .collect{ // update our community object _openGroup.updateTo(openGroup?.copy(canWrite = it)) // when we get an update on the write access of a community // we need to update the input text accordingly - _uiState.update { - it.copy(hideInputBar = shouldHideInputBar()) + _uiState.update { state -> + state.copy(hideInputBar = shouldHideInputBar()) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt index 5fae172dde..4b6f73bd2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt @@ -172,13 +172,12 @@ object OpenGroupManager { fun updateOpenGroup(openGroup: OpenGroup, context: Context) { val threadDB = DatabaseComponent.get(context).lokiThreadDatabase() - val openGroupID = "${openGroup.server}.${openGroup.room}" - val threadID = GroupManager.getOpenGroupThreadID(openGroupID, context) + val threadID = GroupManager.getOpenGroupThreadID(openGroup.groupId, context) threadDB.setOpenGroupChat(openGroup, threadID) // update write access for this community val writeAccesses = _communityWriteAccess.value.toMutableMap() - writeAccesses[openGroup.server] = openGroup.canWrite + writeAccesses[openGroup.groupId] = openGroup.canWrite _communityWriteAccess.value = writeAccesses } From 7b7c053d181e32896ebabd4602c19daa9d32450d Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 16 Sep 2024 09:45:31 +1000 Subject: [PATCH 6/7] Using the right button for recovery banner --- .../java/org/thoughtcrime/securesms/home/SeedReminder.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt b/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt index 617f98a9cd..a413d09d86 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/SeedReminder.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import network.loki.messenger.R import org.thoughtcrime.securesms.ui.SessionShieldIcon +import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.SlimPrimaryOutlineButton import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.theme.LocalColors @@ -60,8 +61,8 @@ internal fun SeedReminder(startRecoveryPasswordActivity: () -> Unit) { style = LocalType.current.small ) } - Spacer(Modifier.width(LocalDimensions.current.xsSpacing)) - SlimPrimaryOutlineButton( + Spacer(Modifier.width(LocalDimensions.current.smallSpacing)) + PrimaryOutlineButton( text = stringResource(R.string.theContinue), modifier = Modifier .align(Alignment.CenterVertically) From c3cf2b8d035dc529eb6c7ead74c4407240ef365a Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 16 Sep 2024 09:59:34 +1000 Subject: [PATCH 7/7] Updated tests --- .../securesms/conversation/v2/ConversationViewModel.kt | 2 +- .../securesms/conversation/v2/ConversationViewModelTest.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index 1e995bab01..514dc24ea6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -295,7 +295,7 @@ class ConversationViewModel( * - We are dealing with a contact from a community (blinded recipient) that does not allow * requests form community members */ - private fun shouldHideInputBar(): Boolean = openGroup?.canWrite == false || + fun shouldHideInputBar(): Boolean = openGroup?.canWrite == false || blindedRecipient?.blocksCommunityMessageRequests == true fun legacyBannerRecipient(context: Context): Recipient? = recipient?.run { diff --git a/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt index 936416d922..03b9138249 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt @@ -203,7 +203,7 @@ class ConversationViewModelTest: BaseViewModelTest() { @Test fun `local recipient should have input and no blinded recipient`() { whenever(recipient.isLocalNumber).thenReturn(true) - assertThat(viewModel.uiState.value.hideInputBar, equalTo(false)) + assertThat(viewModel.shouldHideInputBar(), equalTo(false)) assertThat(viewModel.blindedRecipient, nullValue()) } @@ -215,7 +215,7 @@ class ConversationViewModelTest: BaseViewModelTest() { } whenever(repository.maybeGetBlindedRecipient(recipient)).thenReturn(blinded) assertThat(viewModel.blindedRecipient, notNullValue()) - assertThat(viewModel.uiState.value.hideInputBar, equalTo(true)) + assertThat(viewModel.shouldHideInputBar(), equalTo(true)) } } \ No newline at end of file