Merge branch 'develop' into feature/fga/space_settings_iteration
This commit is contained in:
2
.github/workflows/sync-localazy.yml
vendored
2
.github/workflows/sync-localazy.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
./tools/localazy/importSupportedLocalesFromLocalazy.py
|
||||
./tools/test/generateAllScreenshots.py
|
||||
- name: Create Pull Request for Strings
|
||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
|
||||
with:
|
||||
token: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
commit-message: Sync Strings from Localazy
|
||||
|
||||
2
.github/workflows/sync-sas-strings.yml
vendored
2
.github/workflows/sync-sas-strings.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Run SAS String script
|
||||
run: ./tools/sas/import_sas_strings.py
|
||||
- name: Create Pull Request for SAS Strings
|
||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
|
||||
with:
|
||||
commit-message: Sync SAS Strings
|
||||
title: Sync SAS Strings
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
|
||||
# https://github.com/codecov/codecov-action
|
||||
- name: ☂️ Upload coverage reports to codecov
|
||||
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -199,6 +199,10 @@ android {
|
||||
resources.pickFirsts += setOf(
|
||||
"META-INF/versions/9/OSGI-INF/MANIFEST.MF",
|
||||
)
|
||||
|
||||
jniLibs {
|
||||
useLegacyPackaging = project.findProperty("useLegacyPackaging")?.toString()?.toBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
@@ -69,4 +69,4 @@
|
||||
-keep class org.matrix.rustcomponents.sdk.** { *;}
|
||||
-keep class uniffi.** { *;}
|
||||
-keep class io.element.android.x.di.** { *; }
|
||||
-keepnames class io.element.android.x.**
|
||||
-keepclasseswithmembernames,allowoptimization,allowshrinking class io.element.android.** { *; }
|
||||
|
||||
@@ -13,4 +13,5 @@ object LearnMoreConfig {
|
||||
const val DEVICE_VERIFICATION_URL: String = "https://element.io/help#encryption-device-verification"
|
||||
const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5"
|
||||
const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18"
|
||||
const val HISTORY_VISIBLE_URL: String = "https://element.io/en/help#e2ee-history-sharing"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
package io.element.android.appnav.room.joined
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -96,6 +98,9 @@ class JoinedRoomLoadedFlowNode(
|
||||
private val callback: Callback = callback()
|
||||
override val graph = roomGraphFactory.create(inputs.room)
|
||||
|
||||
// This is an ugly hack to check activity recreation
|
||||
private var currentActivity: Activity? = null
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
@@ -115,8 +120,12 @@ class JoinedRoomLoadedFlowNode(
|
||||
},
|
||||
onDestroy = {
|
||||
Timber.v("OnDestroy")
|
||||
activeRoomsHolder.removeRoom(inputs.room.sessionId, inputs.room.roomId)
|
||||
inputs.room.destroy()
|
||||
// If we're just going through an activity recreation there's no need to destroy the Room object
|
||||
// Destroying it would actually cause an issue where its methods can no longer be called
|
||||
if (currentActivity?.isChangingConfigurations != true) {
|
||||
activeRoomsHolder.removeRoom(inputs.room.sessionId, inputs.room.roomId)
|
||||
inputs.room.destroy()
|
||||
}
|
||||
appNavigationStateService.onLeavingRoom(id)
|
||||
}
|
||||
)
|
||||
@@ -289,6 +298,8 @@ class JoinedRoomLoadedFlowNode(
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
currentActivity = LocalActivity.current
|
||||
|
||||
BackstackView()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ allprojects {
|
||||
config.from(files("$rootDir/tools/detekt/detekt.yml"))
|
||||
}
|
||||
dependencies {
|
||||
detektPlugins("io.nlopez.compose.rules:detekt:0.4.28")
|
||||
detektPlugins("io.nlopez.compose.rules:detekt:0.5.1")
|
||||
detektPlugins(project(":tests:detekt-rules"))
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[versions]
|
||||
# Project
|
||||
android_gradle_plugin = "8.13.1"
|
||||
android_gradle_plugin = "8.13.2"
|
||||
# When updateing this, please also update the version in the file ./idea/kotlinc.xml
|
||||
kotlin = "2.2.20"
|
||||
kotlinpoet = "2.2.0"
|
||||
@@ -18,7 +18,7 @@ constraintlayout_compose = "1.1.1"
|
||||
lifecycle = "2.9.2"
|
||||
activity = "1.11.0"
|
||||
media3 = "1.8.0"
|
||||
camera = "1.5.1"
|
||||
camera = "1.5.2"
|
||||
work = "2.11.0"
|
||||
|
||||
# Compose
|
||||
@@ -41,7 +41,7 @@ serialization_json = "1.9.0"
|
||||
#other
|
||||
coil = "3.3.0"
|
||||
# Rollback to 1.0.4, 1.0.5 has this issue: https://github.com/airbnb/Showkase/issues/420
|
||||
showkase = "1.0.4"
|
||||
showkase = "1.0.5"
|
||||
appyx = "1.7.1"
|
||||
sqldelight = "2.2.1"
|
||||
wysiwyg = "2.40.0"
|
||||
@@ -52,7 +52,7 @@ haze = "1.6.10"
|
||||
dependencyAnalysis = "3.5.1"
|
||||
|
||||
# DI
|
||||
metro = "0.8.1"
|
||||
metro = "0.8.2"
|
||||
|
||||
# Auto service
|
||||
autoservice = "1.1.1"
|
||||
@@ -160,7 +160,7 @@ test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" }
|
||||
test_arch_core = "androidx.arch.core:core-testing:2.2.0"
|
||||
test_junit = "junit:junit:4.13.2"
|
||||
test_runner = "androidx.test:runner:1.7.0"
|
||||
test_mockk = "io.mockk:mockk:1.14.6"
|
||||
test_mockk = "io.mockk:mockk:1.14.7"
|
||||
test_konsist = "com.lemonappdev:konsist:0.17.3"
|
||||
test_turbine = "app.cash.turbine:turbine:1.2.1"
|
||||
test_truth = "com.google.truth:truth:1.4.5"
|
||||
@@ -177,7 +177,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version
|
||||
# https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt
|
||||
# All new features should not be implemented in the pull request that upgrades the version, developers should
|
||||
# only fix API breaks and may add some TODOs.
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.12.2"
|
||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.12.10"
|
||||
|
||||
# Others
|
||||
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
||||
@@ -199,14 +199,14 @@ matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose",
|
||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||
sqldelight-driver-jvm = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqldelight" }
|
||||
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
|
||||
sqlcipher = "net.zetetic:sqlcipher-android:4.11.0"
|
||||
sqlcipher = "net.zetetic:sqlcipher-android:4.12.0"
|
||||
sqlite = "androidx.sqlite:sqlite-ktx:2.6.2"
|
||||
unifiedpush = "org.unifiedpush.android:connector:3.1.2"
|
||||
vanniktech_blurhash = "com.vanniktech:blurhash:0.3.0"
|
||||
telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" }
|
||||
telephoto_flick = { module = "me.saket.telephoto:flick-android", version.ref = "telephoto" }
|
||||
statemachine = "com.freeletics.flowredux:compose:1.2.2"
|
||||
maplibre = "org.maplibre.gl:android-sdk:12.2.1"
|
||||
maplibre = "org.maplibre.gl:android-sdk:12.2.2"
|
||||
maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:3.0.2"
|
||||
maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:3.0.2"
|
||||
opusencoder = "io.element.android:opusencoder:1.2.0"
|
||||
@@ -217,7 +217,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0"
|
||||
|
||||
# Analytics
|
||||
posthog = "com.posthog:posthog-android:3.26.0"
|
||||
sentry = "io.sentry:sentry-android:8.27.1"
|
||||
sentry = "io.sentry:sentry-android:8.28.0"
|
||||
# main branch can be tested replacing the version with main-SNAPSHOT
|
||||
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.29.2"
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ fun Activity.openUrlInChromeCustomTab(
|
||||
})
|
||||
}
|
||||
.launchUrl(this, url.toUri())
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
openUrlInExternalApp(url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ fun String.hash() = try {
|
||||
digest.digest()
|
||||
.joinToString("") { String.format(Locale.ROOT, "%02X", it) }
|
||||
.lowercase(Locale.ROOT)
|
||||
} catch (exc: Exception) {
|
||||
} catch (_: Exception) {
|
||||
// Should not happen, but just in case
|
||||
hashCode().toString()
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ fun Context.getApplicationLabel(packageName: String): String {
|
||||
return try {
|
||||
val ai = packageManager.getApplicationInfoCompat(packageName, 0)
|
||||
packageManager.getApplicationLabel(ai).toString()
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
} catch (_: PackageManager.NameNotFoundException) {
|
||||
packageName
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ fun Context.startNotificationSettingsIntent(
|
||||
} else {
|
||||
startActivity(intent)
|
||||
}
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
toast(noActivityFoundMessage)
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ fun Context.openAppSettingsPage(
|
||||
data = Uri.fromParts("package", packageName, null)
|
||||
}
|
||||
)
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
toast(noActivityFoundMessage)
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ fun Context.startInstallFromSourceIntent(
|
||||
.setData("package:$packageName".toUri())
|
||||
try {
|
||||
activityResultLauncher.launch(intent)
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
toast(noActivityFoundMessage)
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ fun Context.startSharePlainTextIntent(
|
||||
} else {
|
||||
startActivity(intent)
|
||||
}
|
||||
} catch (activityNotFoundException: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
toast(noActivityFoundMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.libraries.androidutils.text
|
||||
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.Charset
|
||||
|
||||
fun String.urlEncoded(charset: Charset = Charsets.UTF_8): String = URLEncoder.encode(this, charset.name())
|
||||
fun String.urlDecoded(charset: Charset = Charsets.UTF_8): String = URLDecoder.decode(this, charset.name())
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* !!! WARNING !!!
|
||||
*
|
||||
@@ -14,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
@@ -185,6 +181,9 @@ object CompoundIcons {
|
||||
@Composable fun ErrorSolid(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_error_solid)
|
||||
}
|
||||
@Composable fun ExitFullScreen(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_exit_full_screen)
|
||||
}
|
||||
@Composable fun Expand(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_expand)
|
||||
}
|
||||
@@ -218,6 +217,9 @@ object CompoundIcons {
|
||||
@Composable fun Forward(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_forward)
|
||||
}
|
||||
@Composable fun FullScreen(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_full_screen)
|
||||
}
|
||||
@Composable fun Grid(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_grid)
|
||||
}
|
||||
@@ -296,6 +298,9 @@ object CompoundIcons {
|
||||
@Composable fun Leave(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_leave)
|
||||
}
|
||||
@Composable fun LeftPanelClose(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_left_panel_close)
|
||||
}
|
||||
@Composable fun Link(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_link)
|
||||
}
|
||||
@@ -503,6 +508,12 @@ object CompoundIcons {
|
||||
@Composable fun SignOut(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_sign_out)
|
||||
}
|
||||
@Composable fun Space(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_space)
|
||||
}
|
||||
@Composable fun SpaceSolid(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_space_solid)
|
||||
}
|
||||
@Composable fun Spinner(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_spinner)
|
||||
}
|
||||
@@ -620,12 +631,6 @@ object CompoundIcons {
|
||||
@Composable fun Windows(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_windows)
|
||||
}
|
||||
@Composable fun Workspace(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_workspace)
|
||||
}
|
||||
@Composable fun WorkspaceSolid(): ImageVector {
|
||||
return ImageVector.vectorResource(R.drawable.ic_compound_workspace_solid)
|
||||
}
|
||||
|
||||
val all @Composable get() = persistentListOf<ImageVector>(
|
||||
Admin(),
|
||||
@@ -681,6 +686,7 @@ object CompoundIcons {
|
||||
EndCall(),
|
||||
Error(),
|
||||
ErrorSolid(),
|
||||
ExitFullScreen(),
|
||||
Expand(),
|
||||
Explore(),
|
||||
ExportArchive(),
|
||||
@@ -692,6 +698,7 @@ object CompoundIcons {
|
||||
Files(),
|
||||
Filter(),
|
||||
Forward(),
|
||||
FullScreen(),
|
||||
Grid(),
|
||||
Group(),
|
||||
Guest(),
|
||||
@@ -718,6 +725,7 @@ object CompoundIcons {
|
||||
Keyboard(),
|
||||
Labs(),
|
||||
Leave(),
|
||||
LeftPanelClose(),
|
||||
Link(),
|
||||
Linux(),
|
||||
ListBulleted(),
|
||||
@@ -787,6 +795,8 @@ object CompoundIcons {
|
||||
Shield(),
|
||||
Sidebar(),
|
||||
SignOut(),
|
||||
Space(),
|
||||
SpaceSolid(),
|
||||
Spinner(),
|
||||
Spotlight(),
|
||||
SpotlightView(),
|
||||
@@ -826,8 +836,6 @@ object CompoundIcons {
|
||||
Warning(),
|
||||
WebBrowser(),
|
||||
Windows(),
|
||||
Workspace(),
|
||||
WorkspaceSolid(),
|
||||
)
|
||||
|
||||
val allResIds get() = persistentListOf(
|
||||
@@ -884,6 +892,7 @@ object CompoundIcons {
|
||||
R.drawable.ic_compound_end_call,
|
||||
R.drawable.ic_compound_error,
|
||||
R.drawable.ic_compound_error_solid,
|
||||
R.drawable.ic_compound_exit_full_screen,
|
||||
R.drawable.ic_compound_expand,
|
||||
R.drawable.ic_compound_explore,
|
||||
R.drawable.ic_compound_export_archive,
|
||||
@@ -895,6 +904,7 @@ object CompoundIcons {
|
||||
R.drawable.ic_compound_files,
|
||||
R.drawable.ic_compound_filter,
|
||||
R.drawable.ic_compound_forward,
|
||||
R.drawable.ic_compound_full_screen,
|
||||
R.drawable.ic_compound_grid,
|
||||
R.drawable.ic_compound_group,
|
||||
R.drawable.ic_compound_guest,
|
||||
@@ -921,6 +931,7 @@ object CompoundIcons {
|
||||
R.drawable.ic_compound_keyboard,
|
||||
R.drawable.ic_compound_labs,
|
||||
R.drawable.ic_compound_leave,
|
||||
R.drawable.ic_compound_left_panel_close,
|
||||
R.drawable.ic_compound_link,
|
||||
R.drawable.ic_compound_linux,
|
||||
R.drawable.ic_compound_list_bulleted,
|
||||
@@ -990,6 +1001,8 @@ object CompoundIcons {
|
||||
R.drawable.ic_compound_shield,
|
||||
R.drawable.ic_compound_sidebar,
|
||||
R.drawable.ic_compound_sign_out,
|
||||
R.drawable.ic_compound_space,
|
||||
R.drawable.ic_compound_space_solid,
|
||||
R.drawable.ic_compound_spinner,
|
||||
R.drawable.ic_compound_spotlight,
|
||||
R.drawable.ic_compound_spotlight_view,
|
||||
@@ -1029,7 +1042,5 @@ object CompoundIcons {
|
||||
R.drawable.ic_compound_warning,
|
||||
R.drawable.ic_compound_web_browser,
|
||||
R.drawable.ic_compound_windows,
|
||||
R.drawable.ic_compound_workspace,
|
||||
R.drawable.ic_compound_workspace_solid,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
|
||||
/**
|
||||
* !!! WARNING !!!
|
||||
*
|
||||
@@ -21,12 +12,14 @@ import androidx.compose.ui.graphics.Color
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
/**
|
||||
* This class holds all the semantic tokens of the Compound theme.
|
||||
*/
|
||||
* This class holds all the semantic tokens of the Compound theme.
|
||||
*/
|
||||
data class SemanticColors(
|
||||
/** Background colour for accent or brand actions. State: Hover */
|
||||
val bgAccentHovered: Color,
|
||||
@@ -50,17 +43,21 @@ data class SemanticColors(
|
||||
val bgActionSecondaryPressed: Color,
|
||||
/** Background colour for secondary actions. State: Rest. */
|
||||
val bgActionSecondaryRest: Color,
|
||||
/** Background colour for tertiary actions. State: Hover */
|
||||
val bgActionTertiaryHovered: Color,
|
||||
/** Background colour for tertiary actions. State: Rest */
|
||||
val bgActionTertiaryRest: Color,
|
||||
/** Background colour for tertiary actions. State: Selected */
|
||||
val bgActionTertiarySelected: Color,
|
||||
/** Badge accent background colour */
|
||||
val bgBadgeAccent: Color,
|
||||
/** Badge default background colour */
|
||||
val bgBadgeDefault: Color,
|
||||
/** Badge info background colour */
|
||||
val bgBadgeInfo: Color,
|
||||
/** Default global background for the user interface.
|
||||
Elevation: Default (Level 0) */
|
||||
/** Default global background for the user interface. Elevation: Default (Level 0) */
|
||||
val bgCanvasDefault: Color,
|
||||
/** Default global background for the user interface.
|
||||
Elevation: Level 1. */
|
||||
/** Default global background for the user interface. Elevation: Level 1. */
|
||||
val bgCanvasDefaultLevel1: Color,
|
||||
/** Default background for disabled elements. There's no minimum contrast requirement. */
|
||||
val bgCanvasDisabled: Color,
|
||||
@@ -86,14 +83,11 @@ Elevation: Level 1. */
|
||||
val bgDecorative6: Color,
|
||||
/** Subtle background colour for informational elements. State: Rest. */
|
||||
val bgInfoSubtle: Color,
|
||||
/** Medium contrast surfaces.
|
||||
Elevation: Default (Level 2). */
|
||||
/** Medium contrast surfaces. Elevation: Default (Level 2). */
|
||||
val bgSubtlePrimary: Color,
|
||||
/** Low contrast surfaces.
|
||||
Elevation: Default (Level 1). */
|
||||
/** Low contrast surfaces. Elevation: Default (Level 1). */
|
||||
val bgSubtleSecondary: Color,
|
||||
/** Lower contrast surfaces.
|
||||
Elevation: Level 0. */
|
||||
/** Lower contrast surfaces. Elevation: Level 0. */
|
||||
val bgSubtleSecondaryLevel0: Color,
|
||||
/** Subtle background colour for success state elements. State: Rest. */
|
||||
val bgSuccessSubtle: Color,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
@@ -37,9 +34,12 @@ val compoundColorsDark = SemanticColors(
|
||||
bgActionSecondaryHovered = DarkColorTokens.colorAlphaGray200,
|
||||
bgActionSecondaryPressed = DarkColorTokens.colorAlphaGray300,
|
||||
bgActionSecondaryRest = DarkColorTokens.colorThemeBg,
|
||||
bgBadgeAccent = DarkColorTokens.colorAlphaGreen300,
|
||||
bgBadgeDefault = DarkColorTokens.colorAlphaGray300,
|
||||
bgBadgeInfo = DarkColorTokens.colorAlphaBlue300,
|
||||
bgActionTertiaryHovered = DarkColorTokens.colorGray300,
|
||||
bgActionTertiaryRest = DarkColorTokens.colorThemeBg,
|
||||
bgActionTertiarySelected = DarkColorTokens.colorGray400,
|
||||
bgBadgeAccent = DarkColorTokens.colorAlphaGreen500,
|
||||
bgBadgeDefault = DarkColorTokens.colorAlphaGray500,
|
||||
bgBadgeInfo = DarkColorTokens.colorAlphaBlue500,
|
||||
bgCanvasDefault = DarkColorTokens.colorThemeBg,
|
||||
bgCanvasDefaultLevel1 = DarkColorTokens.colorGray300,
|
||||
bgCanvasDisabled = DarkColorTokens.colorGray200,
|
||||
@@ -89,7 +89,7 @@ val compoundColorsDark = SemanticColors(
|
||||
iconAccentTertiary = DarkColorTokens.colorGreen800,
|
||||
iconCriticalPrimary = DarkColorTokens.colorRed900,
|
||||
iconDisabled = DarkColorTokens.colorGray700,
|
||||
iconInfoPrimary = DarkColorTokens.colorBlue900,
|
||||
iconInfoPrimary = DarkColorTokens.colorBlue1100,
|
||||
iconOnSolidPrimary = DarkColorTokens.colorThemeBg,
|
||||
iconPrimary = DarkColorTokens.colorGray1400,
|
||||
iconPrimaryAlpha = DarkColorTokens.colorAlphaGray1400,
|
||||
@@ -112,8 +112,8 @@ val compoundColorsDark = SemanticColors(
|
||||
textDecorative5 = DarkColorTokens.colorPink1100,
|
||||
textDecorative6 = DarkColorTokens.colorOrange1100,
|
||||
textDisabled = DarkColorTokens.colorGray800,
|
||||
textInfoPrimary = DarkColorTokens.colorBlue900,
|
||||
textLinkExternal = DarkColorTokens.colorBlue900,
|
||||
textInfoPrimary = DarkColorTokens.colorBlue1100,
|
||||
textLinkExternal = DarkColorTokens.colorBlue1100,
|
||||
textOnSolidPrimary = DarkColorTokens.colorThemeBg,
|
||||
textPrimary = DarkColorTokens.colorGray1400,
|
||||
textSecondary = DarkColorTokens.colorGray900,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
@@ -37,9 +34,12 @@ val compoundColorsHcDark = SemanticColors(
|
||||
bgActionSecondaryHovered = DarkHcColorTokens.colorAlphaGray200,
|
||||
bgActionSecondaryPressed = DarkHcColorTokens.colorAlphaGray300,
|
||||
bgActionSecondaryRest = DarkHcColorTokens.colorThemeBg,
|
||||
bgBadgeAccent = DarkHcColorTokens.colorAlphaGreen300,
|
||||
bgBadgeDefault = DarkHcColorTokens.colorAlphaGray300,
|
||||
bgBadgeInfo = DarkHcColorTokens.colorAlphaBlue300,
|
||||
bgActionTertiaryHovered = DarkHcColorTokens.colorGray300,
|
||||
bgActionTertiaryRest = DarkHcColorTokens.colorThemeBg,
|
||||
bgActionTertiarySelected = DarkHcColorTokens.colorGray400,
|
||||
bgBadgeAccent = DarkHcColorTokens.colorAlphaGreen500,
|
||||
bgBadgeDefault = DarkHcColorTokens.colorAlphaGray500,
|
||||
bgBadgeInfo = DarkHcColorTokens.colorAlphaBlue500,
|
||||
bgCanvasDefault = DarkHcColorTokens.colorThemeBg,
|
||||
bgCanvasDefaultLevel1 = DarkHcColorTokens.colorGray300,
|
||||
bgCanvasDisabled = DarkHcColorTokens.colorGray200,
|
||||
@@ -89,7 +89,7 @@ val compoundColorsHcDark = SemanticColors(
|
||||
iconAccentTertiary = DarkHcColorTokens.colorGreen800,
|
||||
iconCriticalPrimary = DarkHcColorTokens.colorRed900,
|
||||
iconDisabled = DarkHcColorTokens.colorGray700,
|
||||
iconInfoPrimary = DarkHcColorTokens.colorBlue900,
|
||||
iconInfoPrimary = DarkHcColorTokens.colorBlue1100,
|
||||
iconOnSolidPrimary = DarkHcColorTokens.colorThemeBg,
|
||||
iconPrimary = DarkHcColorTokens.colorGray1400,
|
||||
iconPrimaryAlpha = DarkHcColorTokens.colorAlphaGray1400,
|
||||
@@ -112,8 +112,8 @@ val compoundColorsHcDark = SemanticColors(
|
||||
textDecorative5 = DarkHcColorTokens.colorPink1100,
|
||||
textDecorative6 = DarkHcColorTokens.colorOrange1100,
|
||||
textDisabled = DarkHcColorTokens.colorGray800,
|
||||
textInfoPrimary = DarkHcColorTokens.colorBlue900,
|
||||
textLinkExternal = DarkHcColorTokens.colorBlue900,
|
||||
textInfoPrimary = DarkHcColorTokens.colorBlue1100,
|
||||
textLinkExternal = DarkHcColorTokens.colorBlue1100,
|
||||
textOnSolidPrimary = DarkHcColorTokens.colorThemeBg,
|
||||
textPrimary = DarkHcColorTokens.colorGray1400,
|
||||
textSecondary = DarkHcColorTokens.colorGray900,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
@@ -37,9 +34,12 @@ val compoundColorsLight = SemanticColors(
|
||||
bgActionSecondaryHovered = LightColorTokens.colorAlphaGray200,
|
||||
bgActionSecondaryPressed = LightColorTokens.colorAlphaGray300,
|
||||
bgActionSecondaryRest = LightColorTokens.colorThemeBg,
|
||||
bgBadgeAccent = LightColorTokens.colorAlphaGreen300,
|
||||
bgBadgeDefault = LightColorTokens.colorAlphaGray300,
|
||||
bgBadgeInfo = LightColorTokens.colorAlphaBlue300,
|
||||
bgActionTertiaryHovered = LightColorTokens.colorGray300,
|
||||
bgActionTertiaryRest = LightColorTokens.colorThemeBg,
|
||||
bgActionTertiarySelected = LightColorTokens.colorGray400,
|
||||
bgBadgeAccent = LightColorTokens.colorAlphaGreen400,
|
||||
bgBadgeDefault = LightColorTokens.colorAlphaGray400,
|
||||
bgBadgeInfo = LightColorTokens.colorAlphaBlue400,
|
||||
bgCanvasDefault = LightColorTokens.colorThemeBg,
|
||||
bgCanvasDefaultLevel1 = LightColorTokens.colorThemeBg,
|
||||
bgCanvasDisabled = LightColorTokens.colorGray200,
|
||||
@@ -89,7 +89,7 @@ val compoundColorsLight = SemanticColors(
|
||||
iconAccentTertiary = LightColorTokens.colorGreen800,
|
||||
iconCriticalPrimary = LightColorTokens.colorRed900,
|
||||
iconDisabled = LightColorTokens.colorGray700,
|
||||
iconInfoPrimary = LightColorTokens.colorBlue900,
|
||||
iconInfoPrimary = LightColorTokens.colorBlue1100,
|
||||
iconOnSolidPrimary = LightColorTokens.colorThemeBg,
|
||||
iconPrimary = LightColorTokens.colorGray1400,
|
||||
iconPrimaryAlpha = LightColorTokens.colorAlphaGray1400,
|
||||
@@ -112,8 +112,8 @@ val compoundColorsLight = SemanticColors(
|
||||
textDecorative5 = LightColorTokens.colorPink1100,
|
||||
textDecorative6 = LightColorTokens.colorOrange1100,
|
||||
textDisabled = LightColorTokens.colorGray800,
|
||||
textInfoPrimary = LightColorTokens.colorBlue900,
|
||||
textLinkExternal = LightColorTokens.colorBlue900,
|
||||
textInfoPrimary = LightColorTokens.colorBlue1100,
|
||||
textLinkExternal = LightColorTokens.colorBlue1100,
|
||||
textOnSolidPrimary = LightColorTokens.colorThemeBg,
|
||||
textPrimary = LightColorTokens.colorGray1400,
|
||||
textSecondary = LightColorTokens.colorGray900,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
@@ -37,9 +34,12 @@ val compoundColorsHcLight = SemanticColors(
|
||||
bgActionSecondaryHovered = LightHcColorTokens.colorAlphaGray200,
|
||||
bgActionSecondaryPressed = LightHcColorTokens.colorAlphaGray300,
|
||||
bgActionSecondaryRest = LightHcColorTokens.colorThemeBg,
|
||||
bgBadgeAccent = LightHcColorTokens.colorAlphaGreen300,
|
||||
bgBadgeDefault = LightHcColorTokens.colorAlphaGray300,
|
||||
bgBadgeInfo = LightHcColorTokens.colorAlphaBlue300,
|
||||
bgActionTertiaryHovered = LightHcColorTokens.colorGray300,
|
||||
bgActionTertiaryRest = LightHcColorTokens.colorThemeBg,
|
||||
bgActionTertiarySelected = LightHcColorTokens.colorGray400,
|
||||
bgBadgeAccent = LightHcColorTokens.colorAlphaGreen400,
|
||||
bgBadgeDefault = LightHcColorTokens.colorAlphaGray400,
|
||||
bgBadgeInfo = LightHcColorTokens.colorAlphaBlue400,
|
||||
bgCanvasDefault = LightHcColorTokens.colorThemeBg,
|
||||
bgCanvasDefaultLevel1 = LightHcColorTokens.colorThemeBg,
|
||||
bgCanvasDisabled = LightHcColorTokens.colorGray200,
|
||||
@@ -89,7 +89,7 @@ val compoundColorsHcLight = SemanticColors(
|
||||
iconAccentTertiary = LightHcColorTokens.colorGreen800,
|
||||
iconCriticalPrimary = LightHcColorTokens.colorRed900,
|
||||
iconDisabled = LightHcColorTokens.colorGray700,
|
||||
iconInfoPrimary = LightHcColorTokens.colorBlue900,
|
||||
iconInfoPrimary = LightHcColorTokens.colorBlue1100,
|
||||
iconOnSolidPrimary = LightHcColorTokens.colorThemeBg,
|
||||
iconPrimary = LightHcColorTokens.colorGray1400,
|
||||
iconPrimaryAlpha = LightHcColorTokens.colorAlphaGray1400,
|
||||
@@ -112,8 +112,8 @@ val compoundColorsHcLight = SemanticColors(
|
||||
textDecorative5 = LightHcColorTokens.colorPink1100,
|
||||
textDecorative6 = LightHcColorTokens.colorOrange1100,
|
||||
textDisabled = LightHcColorTokens.colorGray800,
|
||||
textInfoPrimary = LightHcColorTokens.colorBlue900,
|
||||
textLinkExternal = LightHcColorTokens.colorBlue900,
|
||||
textInfoPrimary = LightHcColorTokens.colorBlue1100,
|
||||
textLinkExternal = LightHcColorTokens.colorBlue1100,
|
||||
textOnSolidPrimary = LightHcColorTokens.colorThemeBg,
|
||||
textPrimary = LightHcColorTokens.colorGray1400,
|
||||
textSecondary = LightHcColorTokens.colorGray900,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* !!! WARNING !!!
|
||||
*
|
||||
@@ -14,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated.internal
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated.internal
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated.internal
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
@@ -13,8 +12,6 @@
|
||||
* DO NOT EDIT MANUALLY.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@file:Suppress("all")
|
||||
package io.element.android.compound.tokens.generated.internal
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M10,20a1,1 0,1 1,-2 0v-4L4,16a1,1 0,1 1,0 -2h6zM20,14a1,1 0,1 1,0 2h-4v4a1,1 0,1 1,-2 0v-6zM9,3a1,1 0,0 1,1 1v6L4,10a1,1 0,0 1,0 -2h4L8,4a1,1 0,0 1,1 -1m6,0a1,1 0,0 1,1 1v4h4a1,1 0,1 1,0 2h-6L14,4a1,1 0,0 1,1 -1"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M4,14a1,1 0,0 1,1 1v4h4a1,1 0,1 1,0 2L3,21v-6a1,1 0,0 1,1 -1m16,0a1,1 0,0 1,1 1v6h-6a1,1 0,1 1,0 -2h4v-4a1,1 0,0 1,1 -1M9,3a1,1 0,0 1,0 2L5,5v4a1,1 0,0 1,-2 0L3,3zM21,9a1,1 0,1 1,-2 0L19,5h-4a1,1 0,1 1,0 -2h6z"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M16.5,14.8L16.5,9.2q0,-0.35 -0.3,-0.475t-0.55,0.125L13.2,11.3q-0.3,0.3 -0.3,0.7t0.3,0.7l2.45,2.45q0.25,0.25 0.55,0.125t0.3,-0.475M5,19q-0.824,0 -1.412,-0.587A1.93,1.93 0,0 1,3 17L3,7q0,-0.824 0.587,-1.412A1.93,1.93 0,0 1,5 5h14q0.824,0 1.413,0.588Q21,6.175 21,7v10q0,0.824 -0.587,1.413A1.93,1.93 0,0 1,19 19zM8,17L8,7L5,7v10zM10,17h9L19,7h-9z"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,21q-1.65,0 -2.825,-1.175T2,17t1.175,-2.825T6,13t2.825,1.175T10,17t-1.175,2.825T6,21m12,0q-1.65,0 -2.825,-1.175T14,17t1.175,-2.825T18,13t2.825,1.175T22,17t-1.175,2.825T18,21M6,19q0.824,0 1.412,-0.587Q8,17.825 8,17t-0.588,-1.412A1.93,1.93 0,0 0,6 15q-0.824,0 -1.412,0.588A1.93,1.93 0,0 0,4 17q0,0.824 0.588,1.413Q5.175,19 6,19m12,0q0.824,0 1.413,-0.587Q20,17.825 20,17t-0.587,-1.412A1.93,1.93 0,0 0,18 15q-0.824,0 -1.413,0.588A1.93,1.93 0,0 0,16 17q0,0.824 0.587,1.413Q17.176,19 18,19m-6,-8q-1.65,0 -2.825,-1.175T8,7t1.175,-2.825T12,3t2.825,1.175T16,7t-1.175,2.825T12,11m0,-2q0.825,0 1.412,-0.588Q14,7.826 14,7q0,-0.824 -0.588,-1.412A1.93,1.93 0,0 0,12 5q-0.825,0 -1.412,0.588A1.93,1.93 0,0 0,10 7q0,0.824 0.588,1.412Q11.175,9 12,9"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M3.175,19.825Q4.35,21 6,21t2.825,-1.175T10,17t-1.175,-2.825T6,13t-2.825,1.175T2,17t1.175,2.825m12,0Q16.35,21 18,21t2.825,-1.175T22,17t-1.175,-2.825T18,13t-2.825,1.175T14,17t1.175,2.825m-6,-10Q10.35,11 12,11t2.825,-1.175T16,7t-1.175,-2.825T12,3 9.175,4.175 8,7t1.175,2.825"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
@@ -4,10 +4,10 @@
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7 10a0.97 0.97 0 0 1-0.71-0.29A0.97 0.97 0 0 1 6 9q0-0.42 0.29-0.71A0.97 0.97 0 0 1 7 8h10q0.42 0 0.71 0.29T18 9t-0.29 0.71A0.97 0.97 0 0 1 17 10z m0 4a0.97 0.97 0 0 1-0.71-0.29A0.97 0.97 0 0 1 6 13q0-0.42 0.29-0.71A0.97 0.97 0 0 1 7 12h6q0.42 0 0.71 0.29T14 13q0 0.42-0.29 0.71A0.97 0.97 0 0 1 13 14z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3.7 21.3C3.09 21.91 2 21.47 2 20.58V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6zM6 17h14V5H4v13.17l0.59-0.58A2 2 0 0 1 6 17"/>
|
||||
<path
|
||||
android:pathData="M7,10a0.97,0.97 0,0 1,-0.713 -0.287A0.97,0.97 0,0 1,6 9q0,-0.424 0.287,-0.713A0.97,0.97 0,0 1,7 8h10q0.424,0 0.712,0.287Q18,8.576 18,9t-0.288,0.713A0.97,0.97 0,0 1,17 10zM7,14a0.97,0.97 0,0 1,-0.713 -0.287A0.97,0.97 0,0 1,6 13q0,-0.424 0.287,-0.713A0.97,0.97 0,0 1,7 12h6q0.424,0 0.713,0.287 0.287,0.288 0.287,0.713 0,0.424 -0.287,0.713A0.97,0.97 0,0 1,13 14z"
|
||||
android:fillColor="#FF000000"/>
|
||||
<path
|
||||
android:pathData="M3.707,21.293c-0.63,0.63 -1.707,0.184 -1.707,-0.707V5a2,2 0,0 1,2 -2h16a2,2 0,0 1,2 2v12a2,2 0,0 1,-2 2H6zM6,17h14V5H4v13.172l0.586,-0.586A2,2 0,0 1,6 17"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.3 2.3C3.09 21.91 2 21.47 2 20.58V5a2 2 0 0 1 2-2m3 7h10q0.42 0 0.71-0.29A0.97 0.97 0 0 0 18 9a0.97 0.97 0 0 0-0.29-0.71A0.97 0.97 0 0 0 17 8H7a0.97 0.97 0 0 0-0.71 0.29A0.97 0.97 0 0 0 6 9q0 0.42 0.29 0.71T7 10m0 4h6q0.42 0 0.71-0.29A0.97 0.97 0 0 0 14 13a0.97 0.97 0 0 0-0.29-0.71A0.97 0.97 0 0 0 13 12H7a0.97 0.97 0 0 0-0.71 0.29A0.97 0.97 0 0 0 6 13q0 0.42 0.29 0.71T7 14"/>
|
||||
<path
|
||||
android:pathData="M4,3h16a2,2 0,0 1,2 2v12a2,2 0,0 1,-2 2H6l-2.293,2.293c-0.63,0.63 -1.707,0.184 -1.707,-0.707V5a2,2 0,0 1,2 -2m3,7h10q0.424,0 0.712,-0.287A0.97,0.97 0,0 0,18 9a0.97,0.97 0,0 0,-0.288 -0.713A0.97,0.97 0,0 0,17 8H7a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,6 9q0,0.424 0.287,0.713Q6.576,10 7,10m0,4h6q0.424,0 0.713,-0.287A0.97,0.97 0,0 0,14 13a0.97,0.97 0,0 0,-0.287 -0.713A0.97,0.97 0,0 0,13 12H7a0.97,0.97 0,0 0,-0.713 0.287A0.97,0.97 0,0 0,6 13q0,0.424 0.287,0.713Q6.576,14 7,14"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,21q-1.65,0 -2.825,-1.175T2,17t1.175,-2.825T6,13t2.825,1.175T10,17t-1.175,2.825T6,21m12,0q-1.65,0 -2.825,-1.175T14,17t1.175,-2.825T18,13t2.825,1.175T22,17t-1.175,2.825T18,21M6,19q0.824,0 1.412,-0.587Q8,17.825 8,17t-0.588,-1.412A1.93,1.93 0,0 0,6 15q-0.824,0 -1.412,0.588A1.93,1.93 0,0 0,4 17q0,0.824 0.588,1.413Q5.175,19 6,19m12,0q0.824,0 1.413,-0.587Q20,17.825 20,17t-0.587,-1.412A1.93,1.93 0,0 0,18 15q-0.824,0 -1.413,0.588A1.93,1.93 0,0 0,16 17q0,0.824 0.587,1.413Q17.176,19 18,19m-6,-8q-1.65,0 -2.825,-1.175T8,7t1.175,-2.825T12,3t2.825,1.175T16,7t-1.175,2.825T12,11m0,-2q0.825,0 1.412,-0.588Q14,7.826 14,7q0,-0.824 -0.588,-1.412A1.93,1.93 0,0 0,12 5q-0.825,0 -1.412,0.588A1.93,1.93 0,0 0,10 7q0,0.824 0.588,1.412Q11.175,9 12,9"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M3.175,19.825Q4.35,21 6,21t2.825,-1.175T10,17t-1.175,-2.825T6,13t-2.825,1.175T2,17t1.175,2.825m12,0Q16.35,21 18,21t2.825,-1.175T22,17t-1.175,-2.825T18,13t-2.825,1.175T14,17t1.175,2.825m-6,-10Q10.35,11 12,11t2.825,-1.175T16,7t-1.175,-2.825T12,3 9.175,4.175 8,7t1.175,2.825"
|
||||
android:fillColor="#FF000000"/>
|
||||
</vector>
|
||||
@@ -21,7 +21,7 @@ fun String.md5() = try {
|
||||
digest.digest()
|
||||
.joinToString("") { String.format(locale, "%02X", it) }
|
||||
.lowercase(locale)
|
||||
} catch (exc: Exception) {
|
||||
} catch (_: Exception) {
|
||||
// Should not happen, but just in case
|
||||
hashCode().toString()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ fun String.isValidUrl(): Boolean {
|
||||
return try {
|
||||
URI(this).toURL()
|
||||
true
|
||||
} catch (t: Throwable) {
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ package io.element.android.libraries.deeplink.impl
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.text.urlEncoded
|
||||
import io.element.android.libraries.deeplink.api.DeepLinkCreator
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
@@ -21,13 +22,13 @@ class DefaultDeepLinkCreator : DeepLinkCreator {
|
||||
override fun create(sessionId: SessionId, roomId: RoomId?, threadId: ThreadId?, eventId: EventId?): String {
|
||||
return buildString {
|
||||
append("$SCHEME://$HOST/")
|
||||
append(sessionId.value)
|
||||
append(sessionId.value.urlEncoded())
|
||||
append("/")
|
||||
append(roomId?.value.orEmpty())
|
||||
append(roomId?.value?.urlEncoded().orEmpty())
|
||||
append("/")
|
||||
append(threadId?.value.orEmpty())
|
||||
append(threadId?.value?.urlEncoded().orEmpty())
|
||||
append("/")
|
||||
append(eventId?.value.orEmpty())
|
||||
append(eventId?.value?.urlEncoded().orEmpty())
|
||||
}
|
||||
// Remove all possible trailing '/' characters:
|
||||
// No event id
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.text.urlDecoded
|
||||
import io.element.android.libraries.deeplink.api.DeeplinkData
|
||||
import io.element.android.libraries.deeplink.api.DeeplinkParser
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
@@ -31,7 +32,7 @@ class DefaultDeeplinkParser : DeeplinkParser {
|
||||
private fun Uri.toDeeplinkData(): DeeplinkData? {
|
||||
if (scheme != SCHEME) return null
|
||||
if (host != HOST) return null
|
||||
val pathBits = path.orEmpty().split("/").drop(1)
|
||||
val pathBits = encodedPath.orEmpty().split("/").drop(1).map { it.urlDecoded() }
|
||||
val sessionId = pathBits.elementAtOrNull(0)?.let(::SessionId) ?: return null
|
||||
|
||||
return when (val screenPathComponent = pathBits.elementAtOrNull(1)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user