Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage external users #25

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ object LearnMoreConfig {
const val ENCRYPTION_URL: String = "https://element.io/help#encryption"
const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5"
const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18"
const val FAQ_URL: String = "https://aide.tchap.beta.gouv.fr"
}
2 changes: 2 additions & 0 deletions features/createroom/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ android {
setupAnvil(componentMergingStrategy = ComponentMergingStrategy.KSP)

dependencies {
implementation(projects.appconfig)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api)
Expand All @@ -38,6 +39,7 @@ dependencies {
implementation(projects.libraries.mediaupload.api)
implementation(projects.libraries.permissions.api)
implementation(projects.libraries.usersearch.impl)
implementation(projects.libraries.tchaputils)
implementation(projects.services.analytics.api)
implementation(libs.coil.compose)
implementation(projects.libraries.featureflag.api)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import fr.gouv.tchap.libraries.tchaputils.TchapPatterns.isExternalTchapUser
import im.vector.app.features.analytics.plan.CreatedRoom
import io.element.android.features.createroom.impl.CreateRoomConfig
import io.element.android.features.createroom.impl.CreateRoomDataStore
Expand Down Expand Up @@ -184,10 +185,17 @@ class ConfigureRoomPresenter @Inject constructor(
createRoomAction: MutableState<AsyncAction<RoomId>>
) = launch {
suspend {
val accessRules =
if (config.roomVisibility is RoomVisibilityState.Public || !config.invites.any { it.userId.toString().isExternalTchapUser() }) {
"restricted"
} else {
"unrestricted"
}
val avatarUrl = config.avatarUri?.let { uploadAvatar(it) }
val params = when (config.roomVisibility) {
is RoomVisibilityState.Public -> {
CreateRoomParameters(
accessRules = accessRules,
name = config.roomName,
topic = config.topic,
isEncrypted = false,
Expand All @@ -202,6 +210,7 @@ class ConfigureRoomPresenter @Inject constructor(
}
is RoomVisibilityState.Private -> {
CreateRoomParameters(
accessRules = accessRules,
name = config.roomName,
topic = config.topic,
isEncrypted = true,
Expand All @@ -214,6 +223,7 @@ class ConfigureRoomPresenter @Inject constructor(
}
is RoomVisibilityState.PrivateNotEncrypted -> { // TCHAP room type
CreateRoomParameters(
accessRules = accessRules,
name = config.roomName,
topic = config.topic,
isEncrypted = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,27 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import fr.gouv.tchap.libraries.tchaputils.TchapPatterns.isExternalTchapUser
import io.element.android.appconfig.LearnMoreConfig
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.createroom.impl.R
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
import io.element.android.libraries.designsystem.components.ClickableLinkText
import io.element.android.libraries.designsystem.components.LINK_TAG
import io.element.android.libraries.designsystem.components.async.AsyncActionView
import io.element.android.libraries.designsystem.components.async.AsyncActionViewDefaults
import io.element.android.libraries.designsystem.components.button.BackButton
Expand All @@ -48,6 +59,8 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
import io.element.android.libraries.designsystem.theme.badgeExternalContentColor
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
Expand Down Expand Up @@ -119,6 +132,14 @@ fun ConfigureRoomView(
},
)
}
// TCHAP external user
val hasExternalUsers = state.config.invites.any { it.userId.toString().isExternalTchapUser() }
if (hasExternalUsers) {
RoomExternalGuestsWarning()
if (state.config.roomVisibility is RoomVisibilityState.Public) {
state.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(RoomVisibilityItem.Private))
}
}
RoomVisibilityOptions(
selected = when (state.config.roomVisibility) {
is RoomVisibilityState.Private -> RoomVisibilityItem.Private
Expand All @@ -129,6 +150,7 @@ fun ConfigureRoomView(
focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.RoomVisibilityChanged(it))
},
hasExternalUsers = hasExternalUsers,
)
if (state.config.roomVisibility is RoomVisibilityState.Public && state.isKnockFeatureEnabled) {
RoomAccessOptions(
Expand Down Expand Up @@ -269,8 +291,65 @@ private fun ConfigureRoomOptions(
}
}

@Composable
private fun RoomExternalGuestsWarning() {
val externalGuestText = buildAnnotatedString {
val learnMoreStr = stringResource(CommonStrings.action_learn_more)
val externalUsersStr = stringResource(R.string.tchap_screen_create_room_external_guests_content)
val fullText = stringResource(
id = R.string.tchap_screen_create_room_external_guests_warning_title,
externalUsersStr,
learnMoreStr,
)
append(fullText)
val externalUsersStartIndex = fullText.indexOf(externalUsersStr)
addStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold,
),
start = externalUsersStartIndex,
end = externalUsersStartIndex + externalUsersStr.length,
)
val learnMoreStartIndex = fullText.lastIndexOf(learnMoreStr)
addStyle(
style = SpanStyle(
textDecoration = TextDecoration.Underline,
),
start = learnMoreStartIndex,
end = learnMoreStartIndex + learnMoreStr.length,
)
addStringAnnotation(
tag = LINK_TAG,
annotation = "${LearnMoreConfig.FAQ_URL}/fr/article/comment-inviter-un-partenaire-externe-sur-tchap-android-j29jmh/",
start = learnMoreStartIndex,
end = learnMoreStartIndex + learnMoreStr.length
)
}

Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = CompoundIcons.InfoSolid(),
contentDescription = null,
tint = ElementTheme.colors.badgeExternalContentColor,
modifier = Modifier.padding(end = 8.dp)
)
ClickableLinkText(
annotatedString = externalGuestText,
style = ElementTheme.typography.fontBodyMdRegular
.copy(
textAlign = TextAlign.Center,
)
)
}
}

@Composable
private fun RoomVisibilityOptions(
hasExternalUsers: Boolean,
selected: RoomVisibilityItem,
onOptionClick: (RoomVisibilityItem) -> Unit,
modifier: Modifier = Modifier,
Expand All @@ -281,6 +360,7 @@ private fun RoomVisibilityOptions(
) {
RoomVisibilityItem.entries.forEach { item ->
val isSelected = item == selected
val enabled = !(hasExternalUsers && item == RoomVisibilityItem.Public)
ListItem(
leadingContent = ListItemContent.Custom {
RoundedIconAtom(
Expand All @@ -290,9 +370,26 @@ private fun RoomVisibilityOptions(
)
},
headlineContent = { Text(text = stringResource(item.title)) },
supportingContent = { Text(text = stringResource(item.description)) },
supportingContent = {
val content = stringResource(id = item.description)
if (enabled) {
Text(text = content)
} else {
val disabledContent = buildAnnotatedString {
append(content)
val externalDescriptionIndex = content.lastIndexOf('•')
addStyle(
style = SpanStyle(color = ElementTheme.colors.badgeExternalContentColor),
start = externalDescriptionIndex,
end = content.length,
)
}
Text(text = disabledContent)
}
},
trailingContent = ListItemContent.RadioButton(selected = isSelected),
onClick = { onOptionClick(item) },
enabled = enabled
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ enum class RoomVisibilityItem(
) {
// TCHAP room type
Private(
icon = CompoundDrawables.ic_compound_lock,
icon = CompoundDrawables.ic_compound_lock_solid,
title = R.string.tchap_screen_create_room_private_encrypted_option_title,
description = R.string.tchap_screen_create_room_private_encrypted_option_description,
),
Expand Down
19 changes: 10 additions & 9 deletions features/createroom/impl/src/main/res/values-fr/strings_tchap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@

<resources>
<string name="tchap_screen_create_room_room_access_encryption_section_title">"Sécurité et accès du salon"</string>
<string name="tchap_screen_create_room_private_encrypted_option_description">"• Messages chiffrés
• Accessible sur invitation
Limité à 500 membres."</string>
<string name="tchap_screen_create_room_private_encrypted_option_description">"• Chiffré de bout en bout
• Accessible uniquement sur invitation
Conseillé jusqu’à 500 membres"</string>
<string name="tchap_screen_create_room_private_encrypted_option_title">"Salon privé sécurisé"</string>
<string name="tchap_screen_create_room_private_not_encrypted_option_description">"• Messages non‑chiffrés
• Accessible sur invitation
• Nombre de membres illimité."</string>
<string name="tchap_screen_create_room_public_option_description">"• Messages non‑chiffrés
• Accessible à tous les agents depuis la liste des salons publics
• Nombre de membres illimité."</string>
<string name="tchap_screen_create_room_private_not_encrypted_option_description">"• Non‑chiffré
• Accessible uniquement sur invitation"</string>
<string name="tchap_screen_create_room_public_option_description">"• Non‑chiffré
• Accessible depuis la liste des salons
• Non accessible aux invités externes"</string>
<string name="tchap_screen_create_room_external_guests_warning_title">"Ce salon compte des %1$s. %2$s."</string>
<string name="tchap_screen_create_room_external_guests_content">"invités externes"</string>
</resources>
17 changes: 9 additions & 8 deletions features/createroom/impl/src/main/res/values/strings_tchap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@

<resources>
<string name="tchap_screen_create_room_room_access_encryption_section_title">"Room Access and Security"</string>
<string name="tchap_screen_create_room_private_encrypted_option_description">"• Encrypted messages
• Access by invite
Limited to 500 members"</string>
<string name="tchap_screen_create_room_private_encrypted_option_description">"• End to end Encrypted
• Access by invite only
Recommended for up to 500 members"</string>
<string name="tchap_screen_create_room_private_encrypted_option_title">"Secure private room"</string>
<string name="tchap_screen_create_room_private_not_encrypted_option_description">"• Not encrypted messages
• Access by invite
• Unlimited members"</string>
<string name="tchap_screen_create_room_private_not_encrypted_option_description">"• Not encrypted
• Access by invite only</string>
<string name="tchap_screen_create_room_public_option_description">"• Not encrypted messages
• Accessible to all agents from public rooms directory
• Unlimited members"</string>
• Accessible from rooms directory
• Not accessible to external guests"</string>
<string name="tchap_screen_create_room_external_guests_warning_title">"This room contains some %1$s. %2$s."</string>
<string name="tchap_screen_create_room_external_guests_content">"external guests"</string>
</resources>
1 change: 1 addition & 0 deletions features/messages/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation(projects.features.location.api)
implementation(projects.features.poll.api)
implementation(projects.features.roomcall.api)
implementation(projects.features.roomdetails.impl)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ class MessagesPresenter @AssistedInject constructor(

return MessagesState(
isDebugBuild = buildMeta.isDebuggable,
isEncrypted = room.isEncrypted,
isPublic = room.isPublic,
isExternal = true, // TCHAP TODO get value from room
roomId = room.roomId,
roomName = roomName,
roomAvatar = roomAvatar,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ import io.element.android.features.messages.impl.timeline.components.receipt.bot
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
import io.element.android.features.roomcall.api.RoomCallState
import io.element.android.features.roomdetails.impl.RoomBadge
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList

@Immutable
data class MessagesState(
val isDebugBuild: Boolean,
val isEncrypted: Boolean,
val isPublic: Boolean,
val isExternal: Boolean,
val roomId: RoomId,
val roomName: AsyncData<String>,
val roomAvatar: AsyncData<AvatarData>,
Expand All @@ -52,4 +57,20 @@ data class MessagesState(
val appName: String,
val pinnedMessagesBannerState: PinnedMessagesBannerState,
val eventSink: (MessagesEvents) -> Unit
)
) {
val roomBadges = buildList {
if (isEncrypted || isPublic) {
if (isEncrypted) {
add(RoomBadge.ENCRYPTED)
} else {
add(RoomBadge.NOT_ENCRYPTED)
}
}
if (isPublic) {
add(RoomBadge.PUBLIC)
}
if (isExternal) {
add(RoomBadge.EXTERNAL)
}
}.toPersistentList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ fun aMessagesState(
eventSink: (MessagesEvents) -> Unit = {},
) = MessagesState(
isDebugBuild = false,
isEncrypted = true,
isPublic = true,
isExternal = true,
roomId = RoomId("!id:domain"),
roomName = roomName,
roomAvatar = roomAvatar,
Expand Down
Loading
Loading