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..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 @@ -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 } } } @@ -948,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/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index b0a541a9e8..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 @@ -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,27 @@ class ConversationViewModel( } } } + + // listen to community write access updates from this point + communityWriteAccessJob?.cancel() + communityWriteAccessJob = viewModelScope.launch { + OpenGroupManager.getCommunitiesWriteAccessFlow() + .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 { state -> + state.copy(hideInputBar = shouldHideInputBar()) + } + } + } } override fun onCleared() { @@ -267,7 +295,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 || + fun shouldHideInputBar(): Boolean = openGroup?.canWrite == false || blindedRecipient?.blocksCommunityMessageRequests == true fun legacyBannerRecipient(context: Context): Recipient? = recipient?.run { @@ -311,7 +339,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/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() } } 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..4b6f73bd2a 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" @@ -164,9 +172,13 @@ 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.groupId] = openGroup.canWrite + _communityWriteAccess.value = writeAccesses } fun isUserModerator(context: Context, groupId: String, standardPublicKey: String, blindedPublicKey: String? = null): Boolean { 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) 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) } } } 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" /> - - - 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..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.hidesInputBar(), 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.hidesInputBar(), equalTo(true)) + assertThat(viewModel.shouldHideInputBar(), equalTo(true)) } } \ No newline at end of file