Merge branch 'develop' into feature/fga/space_settings_iteration
This commit is contained in:
commit
ce079e84f5
600 changed files with 3591 additions and 2388 deletions
|
|
@ -81,7 +81,7 @@ private fun SpaceAnnouncementHeader(
|
|||
showBetaLabel = true,
|
||||
subTitle = stringResource(id = R.string.screen_space_announcement_subtitle),
|
||||
iconStyle = BigIcon.Style.Default(
|
||||
vectorIcon = CompoundIcons.WorkspaceSolid(),
|
||||
vectorIcon = CompoundIcons.SpaceSolid(),
|
||||
usePrimaryTint = true,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidityEf
|
|||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -132,7 +132,7 @@ class ConfigureRoomPresenter(
|
|||
cameraPhotoPicker.launch()
|
||||
} else {
|
||||
pendingPermissionRequest = true
|
||||
cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
cameraPermissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
AvatarAction.Remove -> dataStore.setAvatarUri(uri = null)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import dev.zacsweers.metro.AssistedInject
|
|||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.permissions.api.PermissionStateProvider
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.libraries.permissions.noop.NoopPermissionsPresenter
|
||||
import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
|
||||
|
|
@ -58,7 +58,7 @@ class NotificationsOptInPresenter(
|
|||
if (notificationsPermissionsState.permissionGranted) {
|
||||
callback.onNotificationsOptInFinished()
|
||||
} else {
|
||||
notificationsPermissionsState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
notificationsPermissionsState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
}
|
||||
NotificationsOptInEvents.NotNowClicked -> {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ enum class HomeNavigationBarItem(
|
|||
isSelected: Boolean,
|
||||
) = when (this) {
|
||||
Chats -> if (isSelected) CompoundIcons.ChatSolid() else CompoundIcons.Chat()
|
||||
Spaces -> if (isSelected) CompoundIcons.WorkspaceSolid() else CompoundIcons.Workspace()
|
||||
Spaces -> if (isSelected) CompoundIcons.SpaceSolid() else CompoundIcons.Space()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,6 @@ internal fun RoomSummaryRow(
|
|||
) {
|
||||
NameAndTimestampRow(
|
||||
name = room.name,
|
||||
latestEvent = room.latestEvent,
|
||||
timestamp = room.timestamp,
|
||||
isHighlighted = room.isHighlighted
|
||||
)
|
||||
|
|
@ -138,7 +137,6 @@ internal fun RoomSummaryRow(
|
|||
) {
|
||||
NameAndTimestampRow(
|
||||
name = room.name,
|
||||
latestEvent = room.latestEvent,
|
||||
timestamp = null,
|
||||
isHighlighted = room.isHighlighted
|
||||
)
|
||||
|
|
@ -214,7 +212,6 @@ private fun RoomSummaryScaffoldRow(
|
|||
@Composable
|
||||
private fun NameAndTimestampRow(
|
||||
name: String?,
|
||||
latestEvent: LatestEvent,
|
||||
timestamp: String?,
|
||||
isHighlighted: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
|
|
@ -236,28 +233,6 @@ private fun NameAndTimestampRow(
|
|||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
// Picto
|
||||
when (latestEvent) {
|
||||
is LatestEvent.Sending -> {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
imageVector = CompoundIcons.Time(),
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.iconTertiary,
|
||||
)
|
||||
}
|
||||
is LatestEvent.Error -> {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
imageVector = CompoundIcons.ErrorSolid(),
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
// Timestamp
|
||||
Text(
|
||||
|
|
@ -302,7 +277,6 @@ private fun MessagePreviewAndIndicatorRow(
|
|||
) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = spacedBy(28.dp)
|
||||
) {
|
||||
if (room.isTombstoned) {
|
||||
Text(
|
||||
|
|
@ -316,6 +290,16 @@ private fun MessagePreviewAndIndicatorRow(
|
|||
)
|
||||
} else {
|
||||
if (room.latestEvent is LatestEvent.Error) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(top = 2.dp)
|
||||
.size(16.dp),
|
||||
imageVector = CompoundIcons.ErrorSolid(),
|
||||
// The last message contains the error.
|
||||
contentDescription = null,
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResource(CommonStrings.common_message_failed_to_send),
|
||||
|
|
@ -326,6 +310,17 @@ private fun MessagePreviewAndIndicatorRow(
|
|||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
} else {
|
||||
if (room.latestEvent is LatestEvent.Sending) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(top = 2.dp)
|
||||
.size(16.dp),
|
||||
imageVector = CompoundIcons.Time(),
|
||||
contentDescription = stringResource(CommonStrings.common_sending),
|
||||
tint = ElementTheme.colors.iconTertiary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
}
|
||||
val messagePreview = room.latestEvent.content()
|
||||
val annotatedMessagePreview = messagePreview as? AnnotatedString ?: AnnotatedString(text = messagePreview.orEmpty().toString())
|
||||
Text(
|
||||
|
|
@ -339,7 +334,7 @@ private fun MessagePreviewAndIndicatorRow(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
// Call and unread
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class DefaultPinCodeManager(
|
|||
lockScreenStore.onWrongPin()
|
||||
}
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import androidx.compose.runtime.setValue
|
|||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
|
||||
@Inject
|
||||
|
|
@ -46,7 +46,7 @@ class QrCodeIntroPresenter(
|
|||
canContinue = true
|
||||
} else {
|
||||
pendingPermissionRequest = true
|
||||
cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
cameraPermissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ private fun Content(
|
|||
QrCodeCameraView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onScanQrCode = { state.eventSink.invoke(QrCodeScanEvents.QrCodeScanned(it)) },
|
||||
renderPreview = state.isScanning,
|
||||
isScanning = state.isScanning,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ dependencies {
|
|||
implementation(libs.jsoup)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.androidx.constraintlayout.compose)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(libs.androidx.media3.exoplayer)
|
||||
implementation(libs.androidx.media3.ui)
|
||||
implementation(libs.sigpwned.emoji4j)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import io.element.android.features.messages.api.timeline.HtmlConverterProvider
|
|||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleState
|
||||
import io.element.android.features.messages.impl.crypto.identity.IdentityChangeState
|
||||
import io.element.android.features.messages.impl.link.LinkState
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvent
|
||||
|
|
@ -101,6 +102,7 @@ class MessagesPresenter(
|
|||
@Assisted private val timelinePresenter: Presenter<TimelineState>,
|
||||
private val timelineProtectionPresenter: Presenter<TimelineProtectionState>,
|
||||
private val identityChangeStatePresenter: Presenter<IdentityChangeState>,
|
||||
private val historyVisibleStatePresenter: Presenter<HistoryVisibleState>,
|
||||
private val linkPresenter: Presenter<LinkState>,
|
||||
@Assisted private val actionListPresenter: Presenter<ActionListState>,
|
||||
private val customReactionPresenter: Presenter<CustomReactionState>,
|
||||
|
|
@ -152,6 +154,7 @@ class MessagesPresenter(
|
|||
val timelineState = timelinePresenter.present()
|
||||
val timelineProtectionState = timelineProtectionPresenter.present()
|
||||
val identityChangeState = identityChangeStatePresenter.present()
|
||||
val historyVisibleState = historyVisibleStatePresenter.present()
|
||||
val actionListState = actionListPresenter.present()
|
||||
val linkState = linkPresenter.present()
|
||||
val customReactionState = customReactionPresenter.present()
|
||||
|
|
@ -274,6 +277,7 @@ class MessagesPresenter(
|
|||
timelineState = timelineState,
|
||||
timelineProtectionState = timelineProtectionState,
|
||||
identityChangeState = identityChangeState,
|
||||
historyVisibleState = historyVisibleState,
|
||||
linkState = linkState,
|
||||
actionListState = actionListState,
|
||||
customReactionState = customReactionState,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.features.messages.impl
|
|||
|
||||
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerState
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleState
|
||||
import io.element.android.features.messages.impl.crypto.identity.IdentityChangeState
|
||||
import io.element.android.features.messages.impl.link.LinkState
|
||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
||||
|
|
@ -40,6 +41,7 @@ data class MessagesState(
|
|||
val timelineState: TimelineState,
|
||||
val timelineProtectionState: TimelineProtectionState,
|
||||
val identityChangeState: IdentityChangeState,
|
||||
val historyVisibleState: HistoryVisibleState,
|
||||
val linkState: LinkState,
|
||||
val actionListState: ActionListState,
|
||||
val customReactionState: CustomReactionState,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ import io.element.android.features.messages.api.timeline.voicemessages.composer.
|
|||
import io.element.android.features.messages.api.timeline.voicemessages.composer.aVoiceMessagePreviewState
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.anActionListState
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleState
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.aHistoryVisibleState
|
||||
import io.element.android.features.messages.impl.crypto.identity.IdentityChangeState
|
||||
import io.element.android.features.messages.impl.crypto.identity.aRoomMemberIdentityStateChange
|
||||
import io.element.android.features.messages.impl.crypto.identity.anIdentityChangeState
|
||||
import io.element.android.features.messages.impl.link.LinkState
|
||||
import io.element.android.features.messages.impl.link.aLinkState
|
||||
|
|
@ -38,6 +41,7 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr
|
|||
import io.element.android.features.roomcall.api.RoomCallState
|
||||
import io.element.android.features.roomcall.api.aStandByCallState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
|
|
@ -48,6 +52,7 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
|||
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
||||
import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown
|
||||
import io.element.android.libraries.textcomposer.model.aTextEditorStateRich
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -83,6 +88,19 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
|||
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||
)
|
||||
),
|
||||
aMessagesState(
|
||||
composerState = aMessageComposerState(textEditorState = aTextEditorStateMarkdown()),
|
||||
identityChangeState = anIdentityChangeState(listOf(aRoomMemberIdentityStateChange()))
|
||||
),
|
||||
aMessagesState(
|
||||
composerState = aMessageComposerState(textEditorState = aTextEditorStateMarkdown()),
|
||||
historyVisibleState = aHistoryVisibleState(showAlert = true)
|
||||
),
|
||||
aMessagesState(
|
||||
composerState = aMessageComposerState(textEditorState = aTextEditorStateMarkdown()),
|
||||
identityChangeState = anIdentityChangeState(listOf(aRoomMemberIdentityStateChange())),
|
||||
historyVisibleState = aHistoryVisibleState(showAlert = true)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +121,7 @@ fun aMessagesState(
|
|||
),
|
||||
timelineProtectionState: TimelineProtectionState = aTimelineProtectionState(),
|
||||
identityChangeState: IdentityChangeState = anIdentityChangeState(),
|
||||
historyVisibleState: HistoryVisibleState = aHistoryVisibleState(),
|
||||
linkState: LinkState = aLinkState(),
|
||||
readReceiptBottomSheetState: ReadReceiptBottomSheetState = aReadReceiptBottomSheetState(),
|
||||
actionListState: ActionListState = anActionListState(),
|
||||
|
|
@ -125,6 +144,7 @@ fun aMessagesState(
|
|||
voiceMessageComposerState = voiceMessageComposerState,
|
||||
timelineProtectionState = timelineProtectionState,
|
||||
identityChangeState = identityChangeState,
|
||||
historyVisibleState = historyVisibleState,
|
||||
linkState = linkState,
|
||||
timelineState = timelineState,
|
||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||
|
|
@ -145,11 +165,9 @@ fun aMessagesState(
|
|||
)
|
||||
|
||||
fun aRoomMemberModerationState(
|
||||
canKick: Boolean = false,
|
||||
canBan: Boolean = false,
|
||||
permissions: RoomMemberModerationPermissions = RoomMemberModerationPermissions.DEFAULT,
|
||||
) = object : RoomMemberModerationState {
|
||||
override val canKick: Boolean = canKick
|
||||
override val canBan: Boolean = canBan
|
||||
override val permissions: RoomMemberModerationPermissions = permissions
|
||||
override val eventSink: (RoomMemberModerationEvents) -> Unit = {}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import io.element.android.features.messages.api.timeline.voicemessages.composer.
|
|||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
||||
import io.element.android.features.messages.impl.actionlist.ActionListView
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleStateView
|
||||
import io.element.android.features.messages.impl.crypto.identity.IdentityChangeStateView
|
||||
import io.element.android.features.messages.impl.link.LinkEvents
|
||||
import io.element.android.features.messages.impl.link.LinkView
|
||||
|
|
@ -486,10 +487,17 @@ private fun MessagesViewComposerBottomSheetContents(
|
|||
// Do not show the identity change if user is composing a Rich message or is seeing suggestion(s).
|
||||
if (state.composerState.suggestions.isEmpty() &&
|
||||
state.composerState.textEditorState is TextEditorState.Markdown) {
|
||||
IdentityChangeStateView(
|
||||
state = state.identityChangeState,
|
||||
onLinkClick = onLinkClick,
|
||||
)
|
||||
if (state.identityChangeState.roomMemberIdentityStateChanges.isNotEmpty()) {
|
||||
IdentityChangeStateView(
|
||||
state = state.identityChangeState,
|
||||
onLinkClick = onLinkClick,
|
||||
)
|
||||
} else {
|
||||
HistoryVisibleStateView(
|
||||
state = state.historyVisibleState,
|
||||
onLinkClick = onLinkClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
val verificationViolation = state.identityChangeState.roomMemberIdentityStateChanges.firstOrNull {
|
||||
it.identityState == IdentityState.VerificationViolation
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.hash.hash
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.preferences.api.store.PreferenceDataStoreFactory
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
interface HistoryVisibleAcknowledgementRepository {
|
||||
fun hasAcknowledged(roomId: RoomId): Flow<Boolean>
|
||||
suspend fun setAcknowledged(roomId: RoomId, value: Boolean)
|
||||
}
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultHistoryVisibleAcknowledgementRepository(
|
||||
sessionId: SessionId,
|
||||
preferenceDataStoreFactory: PreferenceDataStoreFactory,
|
||||
) : HistoryVisibleAcknowledgementRepository {
|
||||
val store =
|
||||
sessionId.value.hash().take(16).let { hash ->
|
||||
preferenceDataStoreFactory.create("elementx_historyvisible_$hash")
|
||||
}
|
||||
|
||||
override fun hasAcknowledged(roomId: RoomId): Flow<Boolean> {
|
||||
return store.data.map { prefs ->
|
||||
val acknowledged = prefs[booleanPreferencesKey(roomId.value)] ?: false
|
||||
acknowledged
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setAcknowledged(roomId: RoomId, value: Boolean) {
|
||||
store.edit { prefs ->
|
||||
prefs[booleanPreferencesKey(roomId.value)] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
sealed interface HistoryVisibleEvent {
|
||||
data object Acknowledge : HistoryVisibleEvent
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
data class HistoryVisibleState(
|
||||
val showAlert: Boolean,
|
||||
val eventSink: (HistoryVisibleEvent) -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Inject
|
||||
class HistoryVisibleStatePresenter(
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val repository: HistoryVisibleAcknowledgementRepository,
|
||||
private val room: JoinedRoom,
|
||||
) : Presenter<HistoryVisibleState> {
|
||||
@Composable
|
||||
override fun present(): HistoryVisibleState {
|
||||
val isFeatureEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.EnableKeyShareOnInvite).collectAsState(initial = false)
|
||||
val roomInfo by room.roomInfoFlow.collectAsState()
|
||||
// Implicitly assume the alert is initially acknowledged to avoid flashes in UI.
|
||||
val acknowledged by repository.hasAcknowledged(room.roomId).collectAsState(initial = true)
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(roomInfo.historyVisibility, acknowledged) {
|
||||
if (roomInfo.historyVisibility == RoomHistoryVisibility.Joined && acknowledged) {
|
||||
repository.setAcknowledged(room.roomId, false)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEvent(event: HistoryVisibleEvent) {
|
||||
when (event) {
|
||||
is HistoryVisibleEvent.Acknowledge -> coroutineScope.setAcknowledged(room.roomId, true)
|
||||
}
|
||||
}
|
||||
|
||||
return HistoryVisibleState(
|
||||
showAlert = isFeatureEnabled && roomInfo.historyVisibility != RoomHistoryVisibility.Joined && roomInfo.isEncrypted == true && !acknowledged,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.setAcknowledged(roomId: RoomId, value: Boolean) = launch {
|
||||
repository.setAcknowledged(roomId, value)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
|
||||
class HistoryVisibleStateProvider : PreviewParameterProvider<HistoryVisibleState> {
|
||||
override val values: Sequence<HistoryVisibleState>
|
||||
get() = sequenceOf(
|
||||
aHistoryVisibleState(showAlert = true),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun aHistoryVisibleState(
|
||||
showAlert: Boolean = false,
|
||||
eventSink: (HistoryVisibleEvent) -> Unit = {},
|
||||
) = HistoryVisibleState(
|
||||
showAlert,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.appconfig.LearnMoreConfig
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertLevel
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun HistoryVisibleStateView(
|
||||
state: HistoryVisibleState,
|
||||
onLinkClick: (String, Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (!state.showAlert) {
|
||||
return
|
||||
}
|
||||
|
||||
ComposerAlertMolecule(
|
||||
modifier = modifier,
|
||||
avatar = null,
|
||||
showIcon = true,
|
||||
level = ComposerAlertLevel.Info,
|
||||
content = buildAnnotatedString {
|
||||
val learnMoreStr = stringResource(CommonStrings.action_learn_more)
|
||||
val fullText = stringResource(CommonStrings.crypto_history_visible, learnMoreStr)
|
||||
append(fullText)
|
||||
val learnMoreStartIndex = fullText.lastIndexOf(learnMoreStr)
|
||||
addStyle(
|
||||
style = SpanStyle(
|
||||
textDecoration = TextDecoration.Underline,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = ElementTheme.colors.textPrimary
|
||||
),
|
||||
start = learnMoreStartIndex,
|
||||
end = learnMoreStartIndex + learnMoreStr.length,
|
||||
)
|
||||
addLink(
|
||||
url = LinkAnnotation.Url(
|
||||
url = LearnMoreConfig.HISTORY_VISIBLE_URL,
|
||||
linkInteractionListener = {
|
||||
onLinkClick(LearnMoreConfig.HISTORY_VISIBLE_URL, true)
|
||||
}
|
||||
),
|
||||
start = learnMoreStartIndex,
|
||||
end = learnMoreStartIndex + learnMoreStr.length,
|
||||
)
|
||||
},
|
||||
submitText = stringResource(CommonStrings.action_dismiss),
|
||||
onSubmitClick = { state.eventSink(HistoryVisibleEvent.Acknowledge) },
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun HistoryVisibleStateViewPreview(
|
||||
@PreviewParameter(HistoryVisibleStateProvider::class) state: HistoryVisibleState,
|
||||
) = ElementPreview {
|
||||
HistoryVisibleStateView(
|
||||
state = state,
|
||||
onLinkClick = { _, _ -> },
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.features.messages.impl.MessagesView
|
||||
import io.element.android.features.messages.impl.aMessagesState
|
||||
import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun MessagesViewWithHistoryVisiblePreview() = ElementPreview {
|
||||
MessagesView(
|
||||
state = aMessagesState(
|
||||
composerState = aMessageComposerState(
|
||||
textEditorState = aTextEditorStateMarkdown(
|
||||
initialText = "",
|
||||
initialFocus = false,
|
||||
)
|
||||
),
|
||||
historyVisibleState = aHistoryVisibleState(showAlert = true),
|
||||
),
|
||||
onBackClick = {},
|
||||
onRoomDetailsClick = {},
|
||||
onEventContentClick = { _, _ -> false },
|
||||
onUserDataClick = {},
|
||||
onLinkClick = { _, _ -> },
|
||||
onSendLocationClick = {},
|
||||
onCreatePollClick = {},
|
||||
onJoinCallClick = {},
|
||||
onViewAllPinnedMessagesClick = {},
|
||||
knockRequestsBannerView = {}
|
||||
)
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ package io.element.android.features.messages.impl.di
|
|||
import dev.zacsweers.metro.BindingContainer
|
||||
import dev.zacsweers.metro.Binds
|
||||
import dev.zacsweers.metro.ContributesTo
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleState
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.HistoryVisibleStatePresenter
|
||||
import io.element.android.features.messages.impl.crypto.identity.IdentityChangeState
|
||||
import io.element.android.features.messages.impl.crypto.identity.IdentityChangeStatePresenter
|
||||
import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailurePresenter
|
||||
|
|
@ -61,4 +63,7 @@ interface MessagesBindsModule {
|
|||
|
||||
@Binds
|
||||
fun bindIdentityChangeStatePresenter(presenter: IdentityChangeStatePresenter): Presenter<IdentityChangeState>
|
||||
|
||||
@Binds
|
||||
fun bindHistoryVisibleStatePresenter(presenter: HistoryVisibleStatePresenter): Presenter<HistoryVisibleState>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ import io.element.android.libraries.mediapickers.api.PickerProvider
|
|||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaSenderFactory
|
||||
import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService
|
||||
|
|
@ -286,7 +286,7 @@ class MessageComposerPresenter(
|
|||
cameraPhotoPicker.launch()
|
||||
} else {
|
||||
pendingEvent = event
|
||||
cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
cameraPermissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
}
|
||||
MessageComposerEvent.PickAttachmentSource.VideoFromCamera -> localCoroutineScope.launch {
|
||||
|
|
@ -295,7 +295,7 @@ class MessageComposerPresenter(
|
|||
cameraVideoPicker.launch()
|
||||
} else {
|
||||
pendingEvent = event
|
||||
cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
cameraPermissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
}
|
||||
MessageComposerEvent.PickAttachmentSource.Location -> {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ fun EventDebugInfoView(
|
|||
private fun prettyJSON(maybeJSON: String): String {
|
||||
return try {
|
||||
JSONObject(maybeJSON).toString(2)
|
||||
} catch (e: JSONException) {
|
||||
} catch (_: JSONException) {
|
||||
// Prefer not pretty-printing over crashing if the data is not actually JSON
|
||||
maybeJSON
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import io.element.android.libraries.di.RoomScope
|
|||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.mediaupload.api.MediaSenderFactory
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent
|
||||
import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent
|
||||
|
|
@ -111,7 +111,7 @@ class DefaultVoiceMessageComposerPresenter(
|
|||
}
|
||||
else -> {
|
||||
Timber.i("Voice message permission needed")
|
||||
permissionState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
permissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -176,10 +176,10 @@ class DefaultVoiceMessageComposerPresenter(
|
|||
localCoroutineScope.deleteRecording()
|
||||
}
|
||||
VoiceMessageComposerEvent.DismissPermissionsRationale -> {
|
||||
permissionState.eventSink(PermissionsEvents.CloseDialog)
|
||||
permissionState.eventSink(PermissionsEvent.CloseDialog)
|
||||
}
|
||||
VoiceMessageComposerEvent.AcceptPermissionRationale -> {
|
||||
permissionState.eventSink(PermissionsEvents.OpenSystemSettingAndCloseDialog)
|
||||
permissionState.eventSink(PermissionsEvent.OpenSystemSettingAndCloseDialog)
|
||||
}
|
||||
is VoiceMessageComposerEvent.LifecycleEvent -> handleLifecycleEvent(event.event)
|
||||
VoiceMessageComposerEvent.DismissSendFailureDialog -> {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
|||
import io.element.android.features.messages.impl.actionlist.ActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.anActionListState
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.crypto.historyvisible.aHistoryVisibleState
|
||||
import io.element.android.features.messages.impl.crypto.identity.anIdentityChangeState
|
||||
import io.element.android.features.messages.impl.fixtures.aMessageEvent
|
||||
import io.element.android.features.messages.impl.link.aLinkState
|
||||
|
|
@ -1297,6 +1298,7 @@ class MessagesPresenterTest {
|
|||
timelinePresenter = { aTimelineState(eventSink = timelineEventSink) },
|
||||
timelineProtectionPresenter = { aTimelineProtectionState() },
|
||||
identityChangeStatePresenter = { anIdentityChangeState() },
|
||||
historyVisibleStatePresenter = { aHistoryVisibleState() },
|
||||
linkPresenter = { aLinkState() },
|
||||
actionListPresenter = { anActionListState(eventSink = actionListEventSink) },
|
||||
customReactionPresenter = { aCustomReactionState() },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class FakeHistoryVisibleAcknowledgementRepository(
|
||||
private val acknowledgements: MutableMap<RoomId, MutableStateFlow<Boolean>> = mutableMapOf()
|
||||
) : HistoryVisibleAcknowledgementRepository {
|
||||
override fun hasAcknowledged(roomId: RoomId): Flow<Boolean> {
|
||||
return acknowledgements.getOrPut(roomId) {
|
||||
MutableStateFlow(false)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setAcknowledged(roomId: RoomId, value: Boolean) {
|
||||
val flow = acknowledgements.getOrPut(roomId) {
|
||||
MutableStateFlow(value)
|
||||
}
|
||||
flow.emit(value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create the repository with a pre-existing entry.
|
||||
*/
|
||||
fun withRoom(roomId: RoomId, acknowledged: Boolean = false): FakeHistoryVisibleAcknowledgementRepository {
|
||||
return FakeHistoryVisibleAcknowledgementRepository(
|
||||
mutableMapOf(
|
||||
roomId to MutableStateFlow(acknowledged)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.messages.impl.crypto.historyvisible
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.awaitLastSequentialItem
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class HistoryVisibleStatePresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
@Test
|
||||
fun `present - not visible if feature disabled`() = runTest {
|
||||
val room = FakeJoinedRoom()
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Joined, isEncrypted = true))
|
||||
val presenter = createHistoryVisibleStatePresenter(room, enabled = false, acknowledged = false)
|
||||
presenter.test {
|
||||
assertThat(awaitLastSequentialItem().showAlert).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial with room shared, unencrypted`() = runTest {
|
||||
val room = FakeJoinedRoom()
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, isEncrypted = false))
|
||||
val presenter = createHistoryVisibleStatePresenter(room)
|
||||
presenter.test {
|
||||
assertThat(awaitLastSequentialItem().showAlert).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial with room joined, encrypted`() = runTest {
|
||||
val room = FakeJoinedRoom()
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Joined, isEncrypted = false))
|
||||
val presenter = createHistoryVisibleStatePresenter(room)
|
||||
presenter.test {
|
||||
assertThat(awaitLastSequentialItem().showAlert).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial with room shared, encrypted, unacknowledged`() = runTest {
|
||||
val room = FakeJoinedRoom()
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, isEncrypted = true))
|
||||
val presenter = createHistoryVisibleStatePresenter(room, acknowledged = false)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.showAlert).isFalse()
|
||||
val nextState = awaitItem()
|
||||
assertThat(nextState.showAlert).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial with room shared, encrypted, acknowledged`() = runTest {
|
||||
val room = FakeJoinedRoom()
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, isEncrypted = true))
|
||||
val presenter = createHistoryVisibleStatePresenter(room, acknowledged = true)
|
||||
presenter.test {
|
||||
assertThat(awaitLastSequentialItem().showAlert).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - transition from joined + unencrypted, to shared + encrypted`() = runTest {
|
||||
val room = FakeJoinedRoom()
|
||||
val featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.EnableKeyShareOnInvite.key to true))
|
||||
val repository = FakeHistoryVisibleAcknowledgementRepository()
|
||||
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Joined, isEncrypted = false))
|
||||
|
||||
val presenter = HistoryVisibleStatePresenter(
|
||||
featureFlagService,
|
||||
repository,
|
||||
room,
|
||||
)
|
||||
|
||||
presenter.test {
|
||||
// emitted by the feature flag service(?)
|
||||
assertThat(awaitItem().showAlert).isFalse()
|
||||
|
||||
// emitted state from room info assignment
|
||||
assertThat(awaitItem().showAlert).isFalse()
|
||||
|
||||
// room is marked as encrypted
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Joined, isEncrypted = true))
|
||||
assertThat(awaitItem().showAlert).isFalse()
|
||||
|
||||
// room history visibility is changed to shared
|
||||
room.givenRoomInfo(aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, isEncrypted = true))
|
||||
assertThat(awaitItem().showAlert).isTrue()
|
||||
|
||||
// alert is acknowledged
|
||||
repository.setAcknowledged(room.roomId, true)
|
||||
assertThat(awaitItem().showAlert).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createHistoryVisibleStatePresenter(
|
||||
room: JoinedRoom = FakeJoinedRoom(),
|
||||
enabled: Boolean = true,
|
||||
acknowledged: Boolean = false
|
||||
): HistoryVisibleStatePresenter {
|
||||
return HistoryVisibleStatePresenter(
|
||||
room = room,
|
||||
featureFlagService = FakeFeatureFlagService(mapOf("feature.enableKeyShareOnInvite" to enabled)),
|
||||
repository = FakeHistoryVisibleAcknowledgementRepository.withRoom(room.roomId, acknowledged)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -21,4 +21,5 @@ sealed interface DeveloperSettingsEvents {
|
|||
data class SetShowColorPicker(val show: Boolean) : DeveloperSettingsEvents
|
||||
data class ChangeBrandColor(val color: Color?) : DeveloperSettingsEvents
|
||||
data object ClearCache : DeveloperSettingsEvents
|
||||
data object VacuumStores : DeveloperSettingsEvents
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import io.element.android.features.preferences.impl.developer.tracing.toLogLevel
|
|||
import io.element.android.features.preferences.impl.model.EnabledFeature
|
||||
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.VacuumStoresUseCase
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
|
|
@ -61,6 +62,7 @@ class DeveloperSettingsPresenter(
|
|||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val buildMeta: BuildMeta,
|
||||
private val enterpriseService: EnterpriseService,
|
||||
private val vacuumStoresUseCase: VacuumStoresUseCase,
|
||||
) : Presenter<DeveloperSettingsState> {
|
||||
@Composable
|
||||
override fun present(): DeveloperSettingsState {
|
||||
|
|
@ -151,6 +153,9 @@ class DeveloperSettingsPresenter(
|
|||
is DeveloperSettingsEvents.SetShowColorPicker -> {
|
||||
showColorPicker = event.show
|
||||
}
|
||||
DeveloperSettingsEvents.VacuumStores -> coroutineScope.launch {
|
||||
vacuumStoresUseCase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,14 @@ fun DeveloperSettingsView(
|
|||
}
|
||||
val cache = state.cacheSize
|
||||
PreferenceCategory(title = "Cache") {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("Vacuum stores")
|
||||
},
|
||||
onClick = {
|
||||
state.eventSink(DeveloperSettingsEvents.VacuumStores)
|
||||
}
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text("Clear cache")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.preferences.impl.tasks
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import timber.log.Timber
|
||||
|
||||
fun interface VacuumStoresUseCase {
|
||||
suspend operator fun invoke()
|
||||
}
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultVacuumStoresUseCase(
|
||||
private val matrixClient: MatrixClient,
|
||||
) : VacuumStoresUseCase {
|
||||
override suspend fun invoke() {
|
||||
matrixClient.performDatabaseVacuum()
|
||||
.onFailure { Timber.e(it, "Failed to vacuum stores") }
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
|
|||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -127,7 +127,7 @@ class EditUserProfilePresenter(
|
|||
cameraPhotoPicker.launch()
|
||||
} else {
|
||||
pendingPermissionRequest = true
|
||||
cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
cameraPermissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
AvatarAction.Remove -> {
|
||||
temporaryUriDeleter.delete(userAvatarUri?.toUri())
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import io.element.android.features.enterprise.test.FakeEnterpriseService
|
|||
import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem
|
||||
import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase
|
||||
import io.element.android.features.preferences.impl.tasks.VacuumStoresUseCase
|
||||
import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
|
|
@ -212,6 +213,23 @@ class DeveloperSettingsPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - VacuumStores action invokes the VacuumStoresUseCase`() = runTest {
|
||||
var vacuumCalled = false
|
||||
val presenter = createDeveloperSettingsPresenter(
|
||||
vacuumStoresUseCase = VacuumStoresUseCase {
|
||||
vacuumCalled = true
|
||||
}
|
||||
)
|
||||
presenter.test {
|
||||
val state = awaitItem()
|
||||
assertThat(vacuumCalled).isFalse()
|
||||
state.eventSink(DeveloperSettingsEvents.VacuumStores)
|
||||
skipItems(1)
|
||||
assertThat(vacuumCalled).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDeveloperSettingsPresenter(
|
||||
sessionId: SessionId = A_SESSION_ID,
|
||||
featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(
|
||||
|
|
@ -230,6 +248,7 @@ class DeveloperSettingsPresenterTest {
|
|||
preferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
buildMeta: BuildMeta = aBuildMeta(),
|
||||
enterpriseService: EnterpriseService = FakeEnterpriseService(),
|
||||
vacuumStoresUseCase: VacuumStoresUseCase = VacuumStoresUseCase {},
|
||||
): DeveloperSettingsPresenter {
|
||||
return DeveloperSettingsPresenter(
|
||||
sessionId = sessionId,
|
||||
|
|
@ -240,6 +259,7 @@ class DeveloperSettingsPresenterTest {
|
|||
appPreferencesStore = preferencesStore,
|
||||
buildMeta = buildMeta,
|
||||
enterpriseService = enterpriseService,
|
||||
vacuumStoresUseCase = vacuumStoresUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,9 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_change_permissions_administrators">"Správca"</string>
|
||||
<string name="screen_room_change_permissions_ban_people">"Zakázať ľudí"</string>
|
||||
<string name="screen_room_change_permissions_change_settings">"Zmeniť nastavenia"</string>
|
||||
<string name="screen_room_change_permissions_delete_messages">"Odstrániť správy"</string>
|
||||
<string name="screen_room_change_permissions_everyone">"Člen"</string>
|
||||
<string name="screen_room_change_permissions_invite_people">"Pozvať ľudí"</string>
|
||||
<string name="screen_room_change_permissions_manage_space">"Spravovať priestor"</string>
|
||||
<string name="screen_room_change_permissions_manage_space_rooms">"Spravovať miestnosti"</string>
|
||||
<string name="screen_room_change_permissions_member_moderation">"Spravovať členov"</string>
|
||||
<string name="screen_room_change_permissions_messages_and_content">"Správy a obsah"</string>
|
||||
<string name="screen_room_change_permissions_moderators">"Moderátor"</string>
|
||||
|
|
@ -17,7 +14,6 @@
|
|||
<string name="screen_room_change_permissions_room_name">"Zmeniť názov miestnosti"</string>
|
||||
<string name="screen_room_change_permissions_room_topic">"Zmeniť tému miestnosti"</string>
|
||||
<string name="screen_room_change_permissions_send_messages">"Odoslať správy"</string>
|
||||
<string name="screen_room_change_permissions_title">"Povolenia"</string>
|
||||
<string name="screen_room_change_role_administrators_title">"Upraviť správcov"</string>
|
||||
<string name="screen_room_change_role_confirm_add_admin_description">"Túto akciu nebudete môcť vrátiť späť. Zvyšujete úroveň používateľa na rovnakú úroveň výkonu ako máte vy."</string>
|
||||
<string name="screen_room_change_role_confirm_add_admin_title">"Pridať správcu?"</string>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ data class RoomMemberListState(
|
|||
val moderationState: RoomMemberModerationState,
|
||||
val eventSink: (RoomMemberListEvents) -> Unit,
|
||||
) {
|
||||
val showBannedSection: Boolean = moderationState.canBan && roomMembers.dataOrNull()?.banned?.isNotEmpty() == true
|
||||
val showBannedSection: Boolean = moderationState.permissions.canBan && roomMembers.dataOrNull()?.banned?.isNotEmpty() == true
|
||||
}
|
||||
|
||||
enum class SelectedSection {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.features.roomdetails.impl.members
|
|||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.map
|
||||
|
|
@ -99,8 +100,10 @@ fun aRoomMemberModerationState(
|
|||
canKick: Boolean = false,
|
||||
): RoomMemberModerationState {
|
||||
return object : RoomMemberModerationState {
|
||||
override val canKick: Boolean = canKick
|
||||
override val canBan: Boolean = canBan
|
||||
override val permissions: RoomMemberModerationPermissions = RoomMemberModerationPermissions(
|
||||
canBan = canBan,
|
||||
canKick = canKick,
|
||||
)
|
||||
override val eventSink: (RoomMemberModerationEvents) -> Unit = {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@
|
|||
<string name="screen_room_member_list_manage_member_unban_title">"Eemalda suhtluskeeld jututoas"</string>
|
||||
<string name="screen_room_member_list_mode_banned">"Suhtluskeeluga kasutajad"</string>
|
||||
<string name="screen_room_member_list_mode_members">"Liikmed"</string>
|
||||
<plurals name="screen_room_member_list_pending_header_title">
|
||||
<item quantity="one">"%1$d saatis kutse"</item>
|
||||
<item quantity="other">"%1$d saatis kutse"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_member_list_pending_status">"Ootel"</string>
|
||||
<string name="screen_room_member_list_role_administrator">"Peakasutajad"</string>
|
||||
<string name="screen_room_member_list_role_moderator">"Moderaatorid"</string>
|
||||
|
|
@ -133,6 +137,7 @@ Me ei soovita krüptimise kasutamist selliste avalike jututubade puhul, millega
|
|||
<string name="screen_security_and_privacy_encryption_toggle_title">"Võta läbiv krüptimine kasutusele"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Kõik võivad jututoaga liituda"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Kõik"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Halda kogukondi"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Liituda saab vaid kutse olemasolul"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Vaid kutsega"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Ligipääs"</string>
|
||||
|
|
|
|||
|
|
@ -141,9 +141,13 @@ Nous ne recommandons pas d’activer le chiffrement pour les salons que tout le
|
|||
<string name="screen_security_and_privacy_encryption_toggle_title">"Activer le chiffrement de bout en bout"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Tout le monde peut rejoindre."</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Tout le monde"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer">"Choisissez les espaces dont les membres peuvent rejoindre ce salon sans invitation. %1$s"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Gérer les espaces"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Seules les personnes invitées peuvent rejoindre."</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Sur invitation uniquement"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Accès"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Toute personne se trouvant dans un espace autorisé peut joindre le salon."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Toute personne de l’espace %1$s peut joindre le salon."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Membres de l’espace"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Les Espaces ne sont pas encore supportés"</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_footer">"Vous aurez besoin d’une adresse pour le rendre visible dans l’annuaire public."</string>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_edit_room_address_room_address_section_footer">"Budete potrebovať adresu miestnosti, aby bola viditeľná v adresári."</string>
|
||||
<string name="screen_edit_room_address_title">"Adresa miestnosti"</string>
|
||||
<string name="screen_edit_room_address_room_address_section_footer">"Budete potrebovať adresu, aby sa zobrazovala vo verejnom adresári."</string>
|
||||
<string name="screen_edit_room_address_title">"Upraviť adresu"</string>
|
||||
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Pri aktualizácii nastavenia oznámenia došlo k chybe."</string>
|
||||
<string name="screen_notification_settings_mentions_only_disclaimer">"Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v niektorých miestnostiach nemusíte dostať upozornenie."</string>
|
||||
<string name="screen_polls_history_title">"Ankety"</string>
|
||||
<string name="screen_room_change_permissions_administrators">"Iba správcovia"</string>
|
||||
<string name="screen_room_change_permissions_administrators">"Správca"</string>
|
||||
<string name="screen_room_change_permissions_ban_people">"Zakázať ľudí"</string>
|
||||
<string name="screen_room_change_permissions_delete_messages">"Odstrániť správy"</string>
|
||||
<string name="screen_room_change_permissions_invite_people">"Pozvite ľudí a prijmite žiadosti o pripojenie"</string>
|
||||
<string name="screen_room_change_permissions_everyone">"Člen"</string>
|
||||
<string name="screen_room_change_permissions_invite_people">"Pozvať ľudí"</string>
|
||||
<string name="screen_room_change_permissions_member_moderation">"Spravovať členov"</string>
|
||||
<string name="screen_room_change_permissions_messages_and_content">"Správy a obsah"</string>
|
||||
<string name="screen_room_change_permissions_moderators">"Správcovia a moderátori"</string>
|
||||
<string name="screen_room_change_permissions_remove_people">"Odstrániť ľudí a odmietnuť žiadosti o pripojenie"</string>
|
||||
<string name="screen_room_change_permissions_moderators">"Moderátor"</string>
|
||||
<string name="screen_room_change_permissions_remove_people">"Odstrániť ľudí"</string>
|
||||
<string name="screen_room_change_permissions_room_avatar">"Zmeniť obrázok miestnosti"</string>
|
||||
<string name="screen_room_change_permissions_room_details">"Upraviť miestnosť"</string>
|
||||
<string name="screen_room_change_permissions_room_details">"Upraviť podrobnosti"</string>
|
||||
<string name="screen_room_change_permissions_room_name">"Zmeniť názov miestnosti"</string>
|
||||
<string name="screen_room_change_permissions_room_topic">"Zmeniť tému miestnosti"</string>
|
||||
<string name="screen_room_change_permissions_send_messages">"Odoslať správy"</string>
|
||||
|
|
@ -40,7 +42,7 @@
|
|||
<string name="screen_room_details_badge_encrypted">"Zašifrované"</string>
|
||||
<string name="screen_room_details_badge_not_encrypted">"Nešifrované"</string>
|
||||
<string name="screen_room_details_badge_public">"Verejná miestnosť"</string>
|
||||
<string name="screen_room_details_edit_room_title">"Upraviť miestnosť"</string>
|
||||
<string name="screen_room_details_edit_room_title">"Upraviť podrobnosti"</string>
|
||||
<string name="screen_room_details_edition_error">"Vyskytla sa neznáma chyba a informácie nebolo možné zmeniť."</string>
|
||||
<string name="screen_room_details_edition_error_title">"Nepodarilo sa aktualizovať miestnosť"</string>
|
||||
<string name="screen_room_details_encryption_enabled_subtitle">"Správy sú zabezpečené zámkami. Jedine vy a príjemcovia máte jedinečné kľúče na ich odomknutie."</string>
|
||||
|
|
@ -69,6 +71,13 @@
|
|||
<string name="screen_room_details_topic_title">"Téma"</string>
|
||||
<string name="screen_room_details_updating_room">"Aktualizácia miestnosti…"</string>
|
||||
<string name="screen_room_member_list_banned_empty">"Neexistujú žiadni zablokovaní používatelia."</string>
|
||||
<plurals name="screen_room_member_list_banned_header_title">
|
||||
<item quantity="one">"%1$d zakázaný"</item>
|
||||
<item quantity="few">"%1$d zakázaní"</item>
|
||||
<item quantity="other">"%1$d zakázaných"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_member_list_empty_search_subtitle">"Skontrolujte preklepy alebo skúste nové vyhľadávanie"</string>
|
||||
<string name="screen_room_member_list_empty_search_title">"Žiadne výsledky pre „%1$s“"</string>
|
||||
<plurals name="screen_room_member_list_header_title">
|
||||
<item quantity="one">"%1$d osoba"</item>
|
||||
<item quantity="few">"%1$d osoby"</item>
|
||||
|
|
@ -81,8 +90,14 @@
|
|||
<string name="screen_room_member_list_manage_member_unban_title">"Zrušiť zákaz prístupu do miestnosti"</string>
|
||||
<string name="screen_room_member_list_mode_banned">"Zakázaní"</string>
|
||||
<string name="screen_room_member_list_mode_members">"Členovia"</string>
|
||||
<string name="screen_room_member_list_role_administrator">"Iba správcovia"</string>
|
||||
<string name="screen_room_member_list_role_moderator">"Správcovia a moderátori"</string>
|
||||
<plurals name="screen_room_member_list_pending_header_title">
|
||||
<item quantity="one">"%1$d pozvaný"</item>
|
||||
<item quantity="few">"%1$d pozvaní"</item>
|
||||
<item quantity="other">"%1$d pozvaných"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_member_list_pending_status">"Čaká na schválenie"</string>
|
||||
<string name="screen_room_member_list_role_administrator">"Správca"</string>
|
||||
<string name="screen_room_member_list_role_moderator">"Moderátor"</string>
|
||||
<string name="screen_room_member_list_role_owner">"Vlastník"</string>
|
||||
<string name="screen_room_member_list_room_members_header_title">"Členovia miestnosti"</string>
|
||||
<string name="screen_room_member_list_unbanning_user">"Zrušenie zákazu %1$s"</string>
|
||||
|
|
@ -109,14 +124,15 @@
|
|||
<string name="screen_room_roles_and_permissions_messages_and_content">"Správy a obsah"</string>
|
||||
<string name="screen_room_roles_and_permissions_moderators">"Moderátori"</string>
|
||||
<string name="screen_room_roles_and_permissions_owners">"Vlastníci"</string>
|
||||
<string name="screen_room_roles_and_permissions_permissions_header">"Povolenia"</string>
|
||||
<string name="screen_room_roles_and_permissions_reset">"Obnoviť povolenia"</string>
|
||||
<string name="screen_room_roles_and_permissions_reset_confirm_description">"Po obnovení oprávnení prídete o aktuálne nastavenia."</string>
|
||||
<string name="screen_room_roles_and_permissions_reset_confirm_title">"Obnoviť oprávnenia?"</string>
|
||||
<string name="screen_room_roles_and_permissions_roles_header">"Roly"</string>
|
||||
<string name="screen_room_roles_and_permissions_room_details">"Podrobnosti o miestnosti"</string>
|
||||
<string name="screen_room_roles_and_permissions_title">"Roly a povolenia"</string>
|
||||
<string name="screen_security_and_privacy_add_room_address_action">"Pridať adresu miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_description">"Ktokoľvek môže požiadať o pripojenie do miestnosti, ale správca alebo moderátor bude musieť žiadosť prijať."</string>
|
||||
<string name="screen_security_and_privacy_add_room_address_action">"Pridať adresu"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_description">"Všetci musia požiadať o prístup."</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_title">"Požiadať o pripojenie"</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_confirm_button_title">"Áno, povoliť šifrovanie"</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_description">"Po aktivácii nie je možné zakázať šifrovanie pre miestnosť. História správ bude viditeľná len pre členov miestnosti, odkedy boli pozvaní alebo keď vstúpili do miestnosti.
|
||||
|
|
@ -126,17 +142,22 @@ To môže brániť správnemu fungovaniu robotov a premostení. Neodporúčame p
|
|||
<string name="screen_security_and_privacy_encryption_section_footer">"Po zapnutí už šifrovanie nie je možné vypnúť."</string>
|
||||
<string name="screen_security_and_privacy_encryption_section_header">"Šifrovanie"</string>
|
||||
<string name="screen_security_and_privacy_encryption_toggle_title">"Povoliť end-to-end šifrovanie"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Ktokoľvek môže nájsť a pripojiť sa"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Pripojiť sa môže ktokoľvek."</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Ktokoľvek"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Ľudia sa môžu pripojiť len vtedy, ak sú pozvaní"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer">"Vyberte, ktorých členovia priestorov sa môžu pripojiť k tejto miestnosti bez pozvánky. %1$s"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Spravovať priestory"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Pripojiť sa môžu iba pozvaní ľudia."</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Iba na pozvánku"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Prístup do miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Prístup"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Ktokoľvek v povolených priestoroch sa môže pripojiť."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Ktokoľvek v %1$s sa môže pripojiť."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Členovia priestoru"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Priestory momentálne nie sú podporované"</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_footer">"Budete potrebovať adresu miestnosti, aby bola viditeľná v adresári."</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_header">"Adresa miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_footer">"Budete potrebovať adresu, aby sa zobrazovala vo verejnom adresári."</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_header">"Adresa"</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Umožniť vyhľadanie tejto miestnosti v adresári verejných miestností %1$s"</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Viditeľné v adresári verejných miestností"</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_toggle_description">"Umožniť nájdenie vyhľadávaním vo verejnom adresári."</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Viditeľné vo verejnom adresári"</string>
|
||||
<string name="screen_security_and_privacy_room_history_anyone_option_title">"Ktokoľvek"</string>
|
||||
<string name="screen_security_and_privacy_room_history_section_header">"Kto môže čítať históriu"</string>
|
||||
<string name="screen_security_and_privacy_room_history_since_invite_option_title">"Len pre členov, odkedy boli pozvaní"</string>
|
||||
|
|
@ -144,6 +165,7 @@ To môže brániť správnemu fungovaniu robotov a premostení. Neodporúčame p
|
|||
<string name="screen_security_and_privacy_room_publishing_section_footer">"Adresy miestností predstavujú spôsoby, ako nájsť a získať prístup k miestnostiam. To tiež zaisťuje, že môžete jednoducho zdieľať svoju miestnosť s ostatnými.
|
||||
Môžete sa rozhodnúť zverejniť svoju miestnosť v adresári verejných miestností vášho domovského servera."</string>
|
||||
<string name="screen_security_and_privacy_room_publishing_section_header">"Zverejnenie miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_visibility_section_header">"Viditeľnosť miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_visibility_section_footer">"Adresy sú spôsob, ako nájsť a získať prístup do miestností a priestorov. To tiež zabezpečuje, že ich môžete jednoducho zdieľať s ostatnými."</string>
|
||||
<string name="screen_security_and_privacy_room_visibility_section_header">"Viditeľnosť"</string>
|
||||
<string name="screen_security_and_privacy_title">"Bezpečnosť a súkromie"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
<string name="screen_room_details_share_room_title">"Share room"</string>
|
||||
<string name="screen_room_details_title">"Room info"</string>
|
||||
<string name="screen_room_details_topic_title">"Topic"</string>
|
||||
<string name="screen_room_details_updating_room">"Updating room…"</string>
|
||||
<string name="screen_room_details_updating_room">"Updating details…"</string>
|
||||
<string name="screen_room_member_list_banned_empty">"There are no banned users."</string>
|
||||
<plurals name="screen_room_member_list_banned_header_title">
|
||||
<item quantity="one">"%1$d Banned"</item>
|
||||
|
|
@ -129,8 +129,10 @@
|
|||
<string name="screen_room_roles_and_permissions_room_details">"Room details"</string>
|
||||
<string name="screen_room_roles_and_permissions_title">"Roles & permissions"</string>
|
||||
<string name="screen_security_and_privacy_add_room_address_action">"Add address"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_multiple_spaces_members_option_description">"Anyone in authorized spaces can join, but everyone else must request access."</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_description">"Everyone must request access."</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_title">"Ask to join"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_single_space_members_option_description">"Anyone in %1$s can join, but everyone else must request access."</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_confirm_button_title">"Yes, enable encryption"</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_description">"Once enabled, encryption for a room cannot be disabled, Message history will only be visible for room members since they were invited or since they joined the room.
|
||||
No one besides the room members will be able to read messages. This may prevent bots and bridges to work correctly.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import io.element.android.libraries.matrix.ui.media.AvatarAction
|
|||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvent
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -151,7 +151,7 @@ class RoomDetailsEditPresenter(
|
|||
cameraPhotoPicker.launch()
|
||||
} else {
|
||||
pendingPermissionRequest = true
|
||||
cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions)
|
||||
cameraPermissionState.eventSink(PermissionsEvent.RequestPermissions)
|
||||
}
|
||||
AvatarAction.Remove -> {
|
||||
temporaryUriDeleter.delete(roomAvatarUriEdited?.toUri())
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
<string name="screen_room_details_edit_room_title">"Edit details"</string>
|
||||
<string name="screen_room_details_edition_error">"There was an unknown error and the information couldn\'t be changed."</string>
|
||||
<string name="screen_room_details_edition_error_title">"Unable to update room"</string>
|
||||
<string name="screen_room_details_updating_room">"Updating room…"</string>
|
||||
<string name="screen_room_details_updating_room">"Updating details…"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roommembermoderation.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions
|
||||
|
||||
data class RoomMemberModerationPermissions(
|
||||
val canKick: Boolean,
|
||||
val canBan: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
val DEFAULT = RoomMemberModerationPermissions(
|
||||
canKick = false,
|
||||
canBan = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun RoomPermissions.roomMemberModerationPermissions(): RoomMemberModerationPermissions {
|
||||
return RoomMemberModerationPermissions(
|
||||
canKick = canOwnUserKick(),
|
||||
canBan = canOwnUserBan(),
|
||||
)
|
||||
}
|
||||
|
|
@ -12,8 +12,7 @@ import androidx.compose.runtime.Immutable
|
|||
|
||||
@Immutable
|
||||
interface RoomMemberModerationState {
|
||||
val canKick: Boolean
|
||||
val canBan: Boolean
|
||||
val permissions: RoomMemberModerationPermissions
|
||||
val eventSink: (RoomMemberModerationEvents) -> Unit
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ package io.element.android.features.roommembermoderation.impl
|
|||
|
||||
import io.element.android.features.roommembermoderation.api.ModerationActionState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class InternalRoomMemberModerationState(
|
||||
override val canKick: Boolean,
|
||||
override val canBan: Boolean,
|
||||
override val permissions: RoomMemberModerationPermissions,
|
||||
val selectedUser: MatrixUser?,
|
||||
val actions: ImmutableList<ModerationActionState>,
|
||||
val kickUserAsyncAction: AsyncAction<Unit>,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import io.element.android.features.roommembermoderation.api.ModerationAction
|
||||
import io.element.android.features.roommembermoderation.api.ModerationActionState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
|
@ -83,8 +84,7 @@ fun anAlice() = MatrixUser(
|
|||
)
|
||||
|
||||
fun aRoomMembersModerationState(
|
||||
canKick: Boolean = false,
|
||||
canBan: Boolean = false,
|
||||
permissions: RoomMemberModerationPermissions = RoomMemberModerationPermissions.DEFAULT,
|
||||
selectedUser: MatrixUser? = null,
|
||||
actions: List<ModerationActionState> = emptyList(),
|
||||
kickUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
|
|
@ -92,8 +92,7 @@ fun aRoomMembersModerationState(
|
|||
unbanUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (RoomMemberModerationEvents) -> Unit = {},
|
||||
) = InternalRoomMemberModerationState(
|
||||
canKick = canKick,
|
||||
canBan = canBan,
|
||||
permissions = permissions,
|
||||
selectedUser = selectedUser,
|
||||
actions = actions.toImmutableList(),
|
||||
kickUserAsyncAction = kickUserAsyncAction,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ import im.vector.app.features.analytics.plan.RoomModeration
|
|||
import io.element.android.features.roommembermoderation.api.ModerationAction
|
||||
import io.element.android.features.roommembermoderation.api.ModerationActionState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.features.roommembermoderation.api.roomMemberModerationPermissions
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runUpdatingState
|
||||
|
|
@ -55,11 +57,8 @@ class RoomMemberModerationPresenter(
|
|||
override fun present(): RoomMemberModerationState {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val permissions by room.permissionsAsState(Permissions()) { perms ->
|
||||
Permissions(
|
||||
canKick = perms.canOwnUserKick(),
|
||||
canBan = perms.canOwnUserBan(),
|
||||
)
|
||||
val permissions by room.permissionsAsState(RoomMemberModerationPermissions.DEFAULT) { perms ->
|
||||
perms.roomMemberModerationPermissions()
|
||||
}
|
||||
val currentUserMemberPowerLevel = room.userPowerLevelAsState(syncUpdateFlow.value)
|
||||
|
||||
|
|
@ -136,8 +135,7 @@ class RoomMemberModerationPresenter(
|
|||
}
|
||||
|
||||
return InternalRoomMemberModerationState(
|
||||
canKick = permissions.canKick,
|
||||
canBan = permissions.canBan,
|
||||
permissions = permissions,
|
||||
selectedUser = selectedUser,
|
||||
actions = moderationActions.value,
|
||||
kickUserAsyncAction = kickUserAsyncAction.value,
|
||||
|
|
@ -149,7 +147,7 @@ class RoomMemberModerationPresenter(
|
|||
|
||||
private fun computeModerationActions(
|
||||
member: RoomMember?,
|
||||
permissions: Permissions,
|
||||
permissions: RoomMemberModerationPermissions,
|
||||
currentUserMemberPowerLevel: Long,
|
||||
): ImmutableList<ModerationActionState> {
|
||||
return buildList {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat
|
|||
import io.element.android.features.roommembermoderation.api.ModerationAction
|
||||
import io.element.android.features.roommembermoderation.api.ModerationActionState
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationPermissions
|
||||
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
|
|
@ -49,8 +50,7 @@ class RoomMemberModerationPresenterTest {
|
|||
val room = aJoinedRoom()
|
||||
createRoomMemberModerationPresenter(room = room).test {
|
||||
val initialState = awaitState()
|
||||
assertThat(initialState.canKick).isFalse()
|
||||
assertThat(initialState.canBan).isFalse()
|
||||
assertThat(initialState.permissions).isEqualTo(RoomMemberModerationPermissions.DEFAULT)
|
||||
assertThat(initialState.selectedUser).isNull()
|
||||
assertThat(initialState.banUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(initialState.kickUserAsyncAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ private fun RoomAccessSection(
|
|||
Text(text = stringResource(R.string.screen_security_and_privacy_room_access_space_members_option_unavailable_description))
|
||||
},
|
||||
trailingContent = ListItemContent.RadioButton(selected = edited == SecurityAndPrivacyRoomAccess.SpaceMember, enabled = false),
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Workspace())),
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Space())),
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ Me ei soovita krüptimise kasutamist selliste avalike jututubade puhul, millega
|
|||
<string name="screen_security_and_privacy_encryption_toggle_title">"Võta läbiv krüptimine kasutusele"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Kõik võivad jututoaga liituda"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Kõik"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Halda kogukondi"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Liituda saab vaid kutse olemasolul"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Vaid kutsega"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Ligipääs"</string>
|
||||
|
|
|
|||
|
|
@ -15,9 +15,13 @@ Nous ne recommandons pas d’activer le chiffrement pour les salons que tout le
|
|||
<string name="screen_security_and_privacy_encryption_toggle_title">"Activer le chiffrement de bout en bout"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Tout le monde peut rejoindre."</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Tout le monde"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer">"Choisissez les espaces dont les membres peuvent rejoindre ce salon sans invitation. %1$s"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Gérer les espaces"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Seules les personnes invitées peuvent rejoindre."</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Sur invitation uniquement"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Accès"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Toute personne se trouvant dans un espace autorisé peut joindre le salon."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Toute personne de l’espace %1$s peut joindre le salon."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Membres de l’espace"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Les Espaces ne sont pas encore supportés"</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_footer">"Vous aurez besoin d’une adresse pour le rendre visible dans l’annuaire public."</string>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_edit_room_address_room_address_section_footer">"Budete potrebovať adresu miestnosti, aby bola viditeľná v adresári."</string>
|
||||
<string name="screen_edit_room_address_title">"Adresa miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_add_room_address_action">"Pridať adresu miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_description">"Ktokoľvek môže požiadať o pripojenie do miestnosti, ale správca alebo moderátor bude musieť žiadosť prijať."</string>
|
||||
<string name="screen_edit_room_address_room_address_section_footer">"Budete potrebovať adresu, aby sa zobrazovala vo verejnom adresári."</string>
|
||||
<string name="screen_edit_room_address_title">"Upraviť adresu"</string>
|
||||
<string name="screen_security_and_privacy_add_room_address_action">"Pridať adresu"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_description">"Všetci musia požiadať o prístup."</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_title">"Požiadať o pripojenie"</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_confirm_button_title">"Áno, povoliť šifrovanie"</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_description">"Po aktivácii nie je možné zakázať šifrovanie pre miestnosť. História správ bude viditeľná len pre členov miestnosti, odkedy boli pozvaní alebo keď vstúpili do miestnosti.
|
||||
|
|
@ -13,17 +13,22 @@ To môže brániť správnemu fungovaniu robotov a premostení. Neodporúčame p
|
|||
<string name="screen_security_and_privacy_encryption_section_footer">"Po zapnutí už šifrovanie nie je možné vypnúť."</string>
|
||||
<string name="screen_security_and_privacy_encryption_section_header">"Šifrovanie"</string>
|
||||
<string name="screen_security_and_privacy_encryption_toggle_title">"Povoliť end-to-end šifrovanie"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Ktokoľvek môže nájsť a pripojiť sa"</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Pripojiť sa môže ktokoľvek."</string>
|
||||
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Ktokoľvek"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Ľudia sa môžu pripojiť len vtedy, ak sú pozvaní"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer">"Vyberte, ktorých členovia priestorov sa môžu pripojiť k tejto miestnosti bez pozvánky. %1$s"</string>
|
||||
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Spravovať priestory"</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Pripojiť sa môžu iba pozvaní ľudia."</string>
|
||||
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Iba na pozvánku"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Prístup do miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_access_section_header">"Prístup"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_multiple_parents_description">"Ktokoľvek v povolených priestoroch sa môže pripojiť."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_single_parent_description">"Ktokoľvek v %1$s sa môže pripojiť."</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_title">"Členovia priestoru"</string>
|
||||
<string name="screen_security_and_privacy_room_access_space_members_option_unavailable_description">"Priestory momentálne nie sú podporované"</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_footer">"Budete potrebovať adresu miestnosti, aby bola viditeľná v adresári."</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_header">"Adresa miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_footer">"Budete potrebovať adresu, aby sa zobrazovala vo verejnom adresári."</string>
|
||||
<string name="screen_security_and_privacy_room_address_section_header">"Adresa"</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_section_footer">"Umožniť vyhľadanie tejto miestnosti v adresári verejných miestností %1$s"</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Viditeľné v adresári verejných miestností"</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_toggle_description">"Umožniť nájdenie vyhľadávaním vo verejnom adresári."</string>
|
||||
<string name="screen_security_and_privacy_room_directory_visibility_toggle_title">"Viditeľné vo verejnom adresári"</string>
|
||||
<string name="screen_security_and_privacy_room_history_anyone_option_title">"Ktokoľvek"</string>
|
||||
<string name="screen_security_and_privacy_room_history_section_header">"Kto môže čítať históriu"</string>
|
||||
<string name="screen_security_and_privacy_room_history_since_invite_option_title">"Len pre členov, odkedy boli pozvaní"</string>
|
||||
|
|
@ -31,6 +36,7 @@ To môže brániť správnemu fungovaniu robotov a premostení. Neodporúčame p
|
|||
<string name="screen_security_and_privacy_room_publishing_section_footer">"Adresy miestností predstavujú spôsoby, ako nájsť a získať prístup k miestnostiam. To tiež zaisťuje, že môžete jednoducho zdieľať svoju miestnosť s ostatnými.
|
||||
Môžete sa rozhodnúť zverejniť svoju miestnosť v adresári verejných miestností vášho domovského servera."</string>
|
||||
<string name="screen_security_and_privacy_room_publishing_section_header">"Zverejnenie miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_visibility_section_header">"Viditeľnosť miestnosti"</string>
|
||||
<string name="screen_security_and_privacy_room_visibility_section_footer">"Adresy sú spôsob, ako nájsť a získať prístup do miestností a priestorov. To tiež zabezpečuje, že ich môžete jednoducho zdieľať s ostatnými."</string>
|
||||
<string name="screen_security_and_privacy_room_visibility_section_header">"Viditeľnosť"</string>
|
||||
<string name="screen_security_and_privacy_title">"Bezpečnosť a súkromie"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
<string name="screen_edit_room_address_room_address_section_footer">"You’ll need an address in order to make it visible in the public directory."</string>
|
||||
<string name="screen_edit_room_address_title">"Edit address"</string>
|
||||
<string name="screen_security_and_privacy_add_room_address_action">"Add address"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_multiple_spaces_members_option_description">"Anyone in authorized spaces can join, but everyone else must request access."</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_description">"Everyone must request access."</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_option_title">"Ask to join"</string>
|
||||
<string name="screen_security_and_privacy_ask_to_join_single_space_members_option_description">"Anyone in %1$s can join, but everyone else must request access."</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_confirm_button_title">"Yes, enable encryption"</string>
|
||||
<string name="screen_security_and_privacy_enable_encryption_alert_description">"Once enabled, encryption for a room cannot be disabled, Message history will only be visible for room members since they were invited or since they joined the room.
|
||||
No one besides the room members will be able to read messages. This may prevent bots and bridges to work correctly.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue