Skip to content

Commit

Permalink
Merge pull request #1669 from oxen-io/feature/dynamic-community-rights
Browse files Browse the repository at this point in the history
Listening to changes in community write access
  • Loading branch information
ThomasSession authored Sep 16, 2024
2 parents f270572 + c3cf2b8 commit d044f12
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -854,6 +853,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// Conversation should be deleted now, just go back
finish()
}

binding.inputBar.isGone = uiState.hideInputBar
}
}
}
Expand Down Expand Up @@ -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()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -65,6 +70,8 @@ class ConversationViewModel(
}
}

private var communityWriteAccessJob: Job? = null

private var _openGroup: RetrieveOnce<OpenGroup> = RetrieveOnce {
storage.getOpenGroup(threadId)
}
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -311,7 +339,8 @@ data class UiMessage(val id: Long, val message: String)
data class ConversationUiState(
val uiMessages: List<UiMessage> = emptyList(),
val isMessageRequestAccepted: Boolean? = null,
val conversationExists: Boolean
val conversationExists: Boolean,
val hideInputBar: Boolean = false
)

data class RetrieveOnce<T>(val retrieval: () -> T?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -39,6 +42,9 @@ object OpenGroupManager {
return true
}

// flow holding information on write access for our current communities
private val _communityWriteAccess: MutableStateFlow<Map<String, Boolean>> = MutableStateFlow(emptyMap())

fun startPolling() {
if (isPolling) { return }
isPolling = true
Expand Down Expand Up @@ -66,6 +72,8 @@ object OpenGroupManager {
}
}

fun getCommunitiesWriteAccessFlow() = _communityWriteAccess.asStateFlow()

@WorkerThread
fun add(server: String, room: String, publicKey: String, context: Context): Pair<Long,OpenGroupApi.RoomInfo?> {
val openGroupID = "$server.$room"
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand All @@ -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)
}
}
}
3 changes: 0 additions & 3 deletions app/src/main/res/xml/preferences_help.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
android:title="@string/helpReportABug"
android:summary="@string/helpReportABugExportLogsDescription"
android:widgetLayout="@layout/export_logs_widget" />

<!-- Note: Having this as `android:layout` rather than `android:layoutWidget` allows it to fit the screen width -->
<Preference android:layout="@layout/preference_widget_progress" />
</PreferenceCategory>

<PreferenceCategory>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

Expand All @@ -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))
}

}

0 comments on commit d044f12

Please sign in to comment.