Merge branch 'develop' into feature/valere/support_verification_violation_banner
This commit is contained in:
commit
36d5528904
625 changed files with 6274 additions and 2292 deletions
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_settings_help_us_improve">"Sorunları tanımlamamıza yardımcı olmak için anonim kullanım verilerini paylaşın."</string>
|
||||
<string name="screen_analytics_settings_read_terms">"Tüm şartlarımızı okuyabilirsiniz %1$s."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"burada"</string>
|
||||
<string name="screen_analytics_settings_share_data">"Analitik verileri paylaşın"</string>
|
||||
</resources>
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
package io.element.android.features.analytics.impl
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
|
|
@ -34,7 +34,7 @@ class AnalyticsOptInNode @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val activity = LocalContext.current as Activity
|
||||
val activity = requireNotNull(LocalActivity.current)
|
||||
val isDark = ElementTheme.isLightTheme.not()
|
||||
val state = presenter.present()
|
||||
AnalyticsOptInView(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.BiasAlignment
|
||||
|
|
@ -111,7 +110,7 @@ private fun AnalyticsOptInHeader(
|
|||
.padding(8.dp),
|
||||
style = ElementTheme.typography.fontBodyMdRegular
|
||||
.copy(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_prompt_data_usage">"Hiçbir kişisel veriyi kaydetmeyeceğiz veya profillemeyeceğiz"</string>
|
||||
<string name="screen_analytics_prompt_help_us_improve">"Sorunları tanımlamamıza yardımcı olmak için anonim kullanım verilerini paylaşın."</string>
|
||||
<string name="screen_analytics_prompt_read_terms">"Tüm şartlarımızı okuyabilirsiniz %1$s."</string>
|
||||
<string name="screen_analytics_prompt_read_terms_content_link">"burada"</string>
|
||||
<string name="screen_analytics_prompt_settings">"Bu özelliği istediğiniz zaman kapatabilirsiniz"</string>
|
||||
<string name="screen_analytics_prompt_third_party_sharing">"Verilerinizi üçüncü taraflarla paylaşmayacağız"</string>
|
||||
<string name="screen_analytics_prompt_title">"%1$s geliştirilmesine yardımcı olun"</string>
|
||||
</resources>
|
||||
|
|
@ -29,6 +29,7 @@ setupAnvil()
|
|||
|
||||
dependencies {
|
||||
implementation(projects.appconfig)
|
||||
implementation(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.designsystem)
|
||||
|
|
@ -40,6 +41,7 @@ dependencies {
|
|||
implementation(projects.libraries.push.api)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.services.analytics.api)
|
||||
implementation(projects.services.appnavstate.api)
|
||||
implementation(projects.services.toolbox.api)
|
||||
implementation(libs.androidx.webkit)
|
||||
implementation(libs.coil.compose)
|
||||
|
|
@ -59,6 +61,7 @@ dependencies {
|
|||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.push.test)
|
||||
testImplementation(projects.services.analytics.test)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(libs.androidx.compose.ui.test.junit)
|
||||
testReleaseImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import io.element.android.libraries.matrix.api.sync.SyncState
|
|||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
||||
import io.element.android.services.analytics.api.ScreenTracker
|
||||
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
|
@ -58,9 +59,9 @@ class CallScreenPresenter @AssistedInject constructor(
|
|||
private val dispatchers: CoroutineDispatchers,
|
||||
private val matrixClientsProvider: MatrixClientProvider,
|
||||
private val screenTracker: ScreenTracker,
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
private val activeCallManager: ActiveCallManager,
|
||||
private val languageTagProvider: LanguageTagProvider,
|
||||
private val appForegroundStateService: AppForegroundStateService,
|
||||
) : Presenter<CallScreenState> {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
|
|
@ -226,19 +227,13 @@ class CallScreenPresenter @AssistedInject constructor(
|
|||
if (state == SyncState.Running) {
|
||||
client.notifyCallStartIfNeeded(callType.roomId)
|
||||
} else {
|
||||
client.syncService().startSync()
|
||||
appForegroundStateService.updateIsInCallState(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
onDispose {
|
||||
// We can't use the local coroutine scope here because it will be disposed before this effect
|
||||
appCoroutineScope.launch {
|
||||
client.syncService().run {
|
||||
if (syncState.value == SyncState.Running) {
|
||||
stopSync()
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure we mark the call as ended in the app state
|
||||
appForegroundStateService.updateIsInCallState(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import io.element.android.features.call.impl.pip.PictureInPictureState
|
|||
import io.element.android.features.call.impl.pip.PipView
|
||||
import io.element.android.features.call.impl.services.CallForegroundService
|
||||
import io.element.android.features.call.impl.utils.CallIntentDataParser
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
|
|
@ -61,6 +62,7 @@ class ElementCallActivity :
|
|||
@Inject lateinit var callIntentDataParser: CallIntentDataParser
|
||||
@Inject lateinit var presenterFactory: CallScreenPresenter.Factory
|
||||
@Inject lateinit var appPreferencesStore: AppPreferencesStore
|
||||
@Inject lateinit var enterpriseService: EnterpriseService
|
||||
@Inject lateinit var pictureInPicturePresenter: PictureInPicturePresenter
|
||||
|
||||
private lateinit var presenter: Presenter<CallScreenState>
|
||||
|
|
@ -109,7 +111,10 @@ class ElementCallActivity :
|
|||
setContent {
|
||||
val pipState = pictureInPicturePresenter.present()
|
||||
ListenToAndroidEvents(pipState)
|
||||
ElementThemeApp(appPreferencesStore) {
|
||||
ElementThemeApp(
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
enterpriseService = enterpriseService,
|
||||
) {
|
||||
val state = presenter.present()
|
||||
eventSink = state.eventSink
|
||||
LaunchedEffect(state.isCallActive, state.isInWidgetMode) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import io.element.android.features.call.impl.di.CallBindings
|
|||
import io.element.android.features.call.impl.notifications.CallNotificationData
|
||||
import io.element.android.features.call.impl.utils.ActiveCallManager
|
||||
import io.element.android.features.call.impl.utils.CallState
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.designsystem.theme.ElementThemeApp
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
|
|
@ -47,6 +48,9 @@ class IncomingCallActivity : AppCompatActivity() {
|
|||
@Inject
|
||||
lateinit var appPreferencesStore: AppPreferencesStore
|
||||
|
||||
@Inject
|
||||
lateinit var enterpriseService: EnterpriseService
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
|
@ -64,7 +68,10 @@ class IncomingCallActivity : AppCompatActivity() {
|
|||
val notificationData = intent?.let { IntentCompat.getParcelableExtra(it, EXTRA_NOTIFICATION_DATA, CallNotificationData::class.java) }
|
||||
if (notificationData != null) {
|
||||
setContent {
|
||||
ElementThemeApp(appPreferencesStore) {
|
||||
ElementThemeApp(
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
enterpriseService = enterpriseService,
|
||||
) {
|
||||
IncomingCallScreen(
|
||||
notificationData = notificationData,
|
||||
onAnswer = ::onAnswer,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="call_foreground_service_channel_title_android">"Devam eden çağrı"</string>
|
||||
<string name="call_foreground_service_message_android">"Aramaya geri dönmek için dokunun"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Çağrı devam ediyor"</string>
|
||||
<string name="screen_incoming_call_subtitle_android">"Gelen Element Call"</string>
|
||||
</resources>
|
||||
|
|
@ -32,10 +32,9 @@ import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver
|
|||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
||||
import io.element.android.services.analytics.api.ScreenTracker
|
||||
import io.element.android.services.analytics.test.FakeScreenTracker
|
||||
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.consumeItemsUntilTimeout
|
||||
import io.element.android.tests.testutils.lambda.assert
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
|
|
@ -243,7 +242,7 @@ class CallScreenPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - automatically starts the Matrix client sync when on RoomCall`() = runTest {
|
||||
fun `present - automatically sets the isInCall state when starting the call and disposing the screen`() = runTest {
|
||||
val navigator = FakeCallScreenNavigator()
|
||||
val widgetDriver = FakeMatrixWidgetDriver()
|
||||
val startSyncLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||
|
|
@ -251,6 +250,7 @@ class CallScreenPresenterTest {
|
|||
this.startSyncLambda = startSyncLambda
|
||||
}
|
||||
val matrixClient = FakeMatrixClient(syncService = syncService)
|
||||
val appForegroundStateService = FakeAppForegroundStateService()
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
widgetDriver = widgetDriver,
|
||||
|
|
@ -258,34 +258,7 @@ class CallScreenPresenterTest {
|
|||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }),
|
||||
screenTracker = FakeScreenTracker {},
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
consumeItemsUntilTimeout()
|
||||
|
||||
assert(startSyncLambda).isCalledOnce()
|
||||
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - automatically stops the Matrix client sync on dispose`() = runTest {
|
||||
val navigator = FakeCallScreenNavigator()
|
||||
val widgetDriver = FakeMatrixWidgetDriver()
|
||||
val stopSyncLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||
val syncService = FakeSyncService(SyncState.Running).apply {
|
||||
this.stopSyncLambda = stopSyncLambda
|
||||
}
|
||||
val matrixClient = FakeMatrixClient(syncService = syncService)
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
widgetDriver = widgetDriver,
|
||||
navigator = navigator,
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }),
|
||||
screenTracker = FakeScreenTracker {},
|
||||
appForegroundStateService = appForegroundStateService,
|
||||
)
|
||||
val hasRun = Mutex(true)
|
||||
val job = launch {
|
||||
|
|
@ -296,11 +269,25 @@ class CallScreenPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
hasRun.lock()
|
||||
appForegroundStateService.isInCall.test {
|
||||
// The initial isInCall state will always be false
|
||||
assertThat(awaitItem()).isFalse()
|
||||
|
||||
job.cancelAndJoin()
|
||||
// Wait until the call starts
|
||||
hasRun.lock()
|
||||
|
||||
assert(stopSyncLambda).isCalledOnce()
|
||||
// Then it'll be true once the call is active
|
||||
assertThat(awaitItem()).isTrue()
|
||||
|
||||
// If we dispose the screen
|
||||
job.cancelAndJoin()
|
||||
|
||||
// The isInCall state is now false
|
||||
assertThat(awaitItem()).isFalse()
|
||||
|
||||
// And there are no more events
|
||||
ensureAllEventsConsumed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -354,6 +341,7 @@ class CallScreenPresenterTest {
|
|||
matrixClientsProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
|
||||
activeCallManager: FakeActiveCallManager = FakeActiveCallManager(),
|
||||
screenTracker: ScreenTracker = FakeScreenTracker(),
|
||||
appForegroundStateService: FakeAppForegroundStateService = FakeAppForegroundStateService(),
|
||||
): CallScreenPresenter {
|
||||
val userAgentProvider = object : UserAgentProvider {
|
||||
override fun provide(): String {
|
||||
|
|
@ -369,10 +357,10 @@ class CallScreenPresenterTest {
|
|||
clock = clock,
|
||||
dispatchers = dispatchers,
|
||||
matrixClientsProvider = matrixClientsProvider,
|
||||
appCoroutineScope = this,
|
||||
activeCallManager = activeCallManager,
|
||||
screenTracker = screenTracker,
|
||||
languageTagProvider = FakeLanguageTagProvider("en-US"),
|
||||
appForegroundStateService = appForegroundStateService,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
package io.element.android.features.createroom.impl.root
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
|
|
@ -55,7 +55,7 @@ class CreateRoomRootNode @AssistedInject constructor(
|
|||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
val activity = LocalContext.current as Activity
|
||||
val activity = requireNotNull(LocalActivity.current)
|
||||
CreateRoomRootView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -211,7 +210,7 @@ private fun CreateRoomActionButton(
|
|||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
tint = ElementTheme.colors.iconSecondary,
|
||||
resourceId = iconRes,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<string name="screen_create_room_private_option_description">"Паведамленні ў гэтым пакоі зашыфраваны. Гэта шыфраванне нельга адключыць."</string>
|
||||
<string name="screen_create_room_private_option_title">"Прыватны пакой (толькі па запрашэнні)"</string>
|
||||
<string name="screen_create_room_public_option_description">"Паведамленні не зашыфраваны, і кожны можа іх прачытаць. Вы можаце ўключыць шыфраванне пазней."</string>
|
||||
<string name="screen_create_room_public_option_title">"Публічны пакой (для ўсіх)"</string>
|
||||
<string name="screen_create_room_public_option_title">"Публічны пакой"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_title">"Хто заўгодна"</string>
|
||||
<string name="screen_create_room_room_access_section_header">"Доступ у пакой"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Папрасіце далучыцца"</string>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
<string name="screen_create_room_private_option_description">"Съобщенията в тази стая са шифровани. Шифроването не може да бъде изключено впоследствие."</string>
|
||||
<string name="screen_create_room_private_option_title">"Частна стая (само с покана)"</string>
|
||||
<string name="screen_create_room_public_option_description">"Съобщенията не са шифровани и всеки може да ги прочете. Можете да активирате шифроването на по-късна дата."</string>
|
||||
<string name="screen_create_room_public_option_title">"Публична стая (всеки)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Име на стаята"</string>
|
||||
<string name="screen_create_room_title">"Създаване на стая"</string>
|
||||
<string name="screen_create_room_topic_label">"Тема за разговор (незадължително)"</string>
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@
|
|||
<string name="screen_create_room_private_option_title">"Privater Chatroom"</string>
|
||||
<string name="screen_create_room_public_option_description">"Jeder kann diesen Chatroom finden.
|
||||
Sie können dies aber jederzeit in den Chatroomeinstellungen ändern."</string>
|
||||
<string name="screen_create_room_public_option_title">"Öffentlicher Chatroom"</string>
|
||||
<string name="screen_create_room_public_option_title">"Öffentlicher Raum"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_description">"Jeder kann diesem Chatroom beitreten"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_title">"Jemand"</string>
|
||||
<string name="screen_create_room_room_access_section_header">"Chatroom Zugang"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Jeder kann darum bitten, dem Chatroom beizutreten, aber ein Administrator oder ein Moderator muss die Anfrage akzeptieren."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Beitritt beantragen"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Damit dieser Chatroom im öffentlichen Chatroomverzeichnis sichtbar ist, benötigen Sie eine Chatroomadresse."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Chatroom Adresse"</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Chatroomadresse"</string>
|
||||
<string name="screen_create_room_room_name_label">"Raumname"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">" Sichtbarkeit des Chatrooms"</string>
|
||||
<string name="screen_create_room_title">"Raum erstellen"</string>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
<string name="screen_create_room_room_access_section_knocking_option_description">"Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Αίτημα συμμετοχής"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Για να είναι ορατό αυτό το δωμάτιο στον κατάλογο των δημόσιων δωματίων, θα χρειαστείς μια διεύθυνση δωματίου."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Διεύθυνση δωματίου"</string>
|
||||
<string name="screen_create_room_room_name_label">"Όνομα δωματίου"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Ορατότητα δωματίου"</string>
|
||||
<string name="screen_create_room_title">"Δημιούργησε ένα δωμάτιο"</string>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
<string name="screen_create_room_private_option_description">"Los mensajes de esta sala están cifrados. La encriptación no se puede desactivar después."</string>
|
||||
<string name="screen_create_room_private_option_title">"Sala privada (sólo con invitación)"</string>
|
||||
<string name="screen_create_room_public_option_description">"Los mensajes no están cifrados y cualquiera puede leerlos. Puedes activar la encriptación más adelante."</string>
|
||||
<string name="screen_create_room_public_option_title">"Sala pública (cualquiera)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Nombre de la sala"</string>
|
||||
<string name="screen_create_room_title">"Crear una sala"</string>
|
||||
<string name="screen_create_room_topic_label">"Tema (opcional)"</string>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<string name="screen_create_room_private_option_description">"پیامهای این اتاق رمز شدهاند. رمزنگاری نمیتواند از این پس تغییر کند."</string>
|
||||
<string name="screen_create_room_private_option_title">"اتاق خصوصی (فقط دعوت)"</string>
|
||||
<string name="screen_create_room_public_option_description">"پیامها رمزنگاری نشده و هرکسی میتواند بخواندشان. میتوانید بعداً رمزنگاری را به کار بیندازید."</string>
|
||||
<string name="screen_create_room_public_option_title">"اتاق عمومی (هرکسی)"</string>
|
||||
<string name="screen_create_room_public_option_title">"اتاق عمومی"</string>
|
||||
<string name="screen_create_room_room_name_label">"نام اتاق"</string>
|
||||
<string name="screen_create_room_title">"ایجاد اتاق"</string>
|
||||
<string name="screen_create_room_topic_label">"موضوع (اختیاری)"</string>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Ezt bármikor módosíthatja a szobabeállításokban."</string>
|
|||
<string name="screen_create_room_room_access_section_knocking_option_description">"Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Csatlakozás kérése"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Ahhoz, hogy ez a szoba látható legyen a nyilvános szobák címtárában, meg kell adnia a szoba címét."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Szoba címe"</string>
|
||||
<string name="screen_create_room_room_address_section_title">"A szoba címe"</string>
|
||||
<string name="screen_create_room_room_name_label">"Szoba neve"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Szoba láthatósága"</string>
|
||||
<string name="screen_create_room_title">"Szoba létrehozása"</string>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ Anda dapat mengubah ini kapan pun dalam pengaturan ruangan."</string>
|
|||
<string name="screen_create_room_room_access_section_knocking_option_description">"Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Minta untuk bergabung"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Alamat ruangan"</string>
|
||||
<string name="screen_create_room_room_name_label">"Nama ruangan"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Keterlihatan ruangan"</string>
|
||||
<string name="screen_create_room_title">"Buat ruangan"</string>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<string name="screen_create_room_private_option_title">"კერძო ოთახი"</string>
|
||||
<string name="screen_create_room_public_option_description">"ყველას ამ ოთახის მოძებნა შეუძლია.
|
||||
თქვენ ნებისმიერ დროს შეგიძლიათ ამის შეცვლა ოთახის პარამეტრებში."</string>
|
||||
<string name="screen_create_room_public_option_title">"საჯარო ოთახი"</string>
|
||||
<string name="screen_create_room_room_name_label">"ოთახის სახელი"</string>
|
||||
<string name="screen_create_room_title">"ოთახის შექმნა"</string>
|
||||
<string name="screen_create_room_topic_label">"თემა (სურვილისამებრ)"</string>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<string name="screen_create_room_private_option_title">"Sala privativa (somente por convite)"</string>
|
||||
<string name="screen_create_room_public_option_description">"Qualquer um pode encontrar esta sala.
|
||||
Você pode mudar isso a qualquer momento nas configurações da sala."</string>
|
||||
<string name="screen_create_room_public_option_title">"Sala pública (qualquer pessoa)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Nome da sala"</string>
|
||||
<string name="screen_create_room_title">"Criar uma sala"</string>
|
||||
<string name="screen_create_room_topic_label">"Tópico (opcional)"</string>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<string name="screen_create_room_action_create_room">"Nova sala"</string>
|
||||
<string name="screen_create_room_add_people_title">"Convidar pessoas"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Ocorreu um erro ao criar a sala"</string>
|
||||
<string name="screen_create_room_private_option_description">"Apenas as pessoas convidadas podem aceder a esta sala. Todas as mensagens são encriptadas ponta a ponta."</string>
|
||||
<string name="screen_create_room_private_option_description">"Apenas as pessoas convidadas podem aceder a esta sala. Todas as mensagens são cifradas ponta-a-ponta."</string>
|
||||
<string name="screen_create_room_private_option_title">"Sala privada"</string>
|
||||
<string name="screen_create_room_public_option_description">"Qualquer um pode encontrar esta sala.
|
||||
Pode alterar esta opção nas definições da sala."</string>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ Puteți modifica acest lucru oricând în setări."</string>
|
|||
<string name="screen_create_room_room_access_section_knocking_option_description">"Oricine poate cere să se alăture camerei, dar un administrator sau un moderator va trebui să accepte solicitarea"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Cereți să vă alăturați"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Pentru ca această cameră să fie vizibilă în directorul de camere publice, veți avea nevoie de o adresă de cameră."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Adresa camerei"</string>
|
||||
<string name="screen_create_room_room_name_label">"Numele camerei"</string>
|
||||
<string name="screen_create_room_title">"Creați o cameră"</string>
|
||||
<string name="screen_create_room_topic_label">"Subiect (opțional)"</string>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ Du kan ändra detta när som helst i rumsinställningarna."</string>
|
|||
<string name="screen_create_room_room_access_section_header">"Rumsåtkomst"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Vem som helst kan be om att gå med i rummet men en administratör eller en moderator måste acceptera begäran"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Be om att gå med"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"För att detta rum ska vara synligt i den allmänna rumskatalogen behöver du en rumsadress."</string>
|
||||
<string name="screen_create_room_room_name_label">"Rumsnamn"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Rumssynlighet"</string>
|
||||
<string name="screen_create_room_title">"Skapa ett rum"</string>
|
||||
<string name="screen_create_room_topic_label">"Ämne (valfritt)"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Ett fel uppstod när du försökte starta en chatt"</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_create_room_action_create_room">"Yeni oda"</string>
|
||||
<string name="screen_create_room_add_people_title">"İnsanları davet et"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Oda oluşturulurken bir hata oluştu"</string>
|
||||
<string name="screen_create_room_private_option_description">"Bu odaya yalnızca davet edilen kişiler erişebilir. Tüm mesajlar uçtan uca şifrelenir."</string>
|
||||
<string name="screen_create_room_private_option_title">"Özel oda"</string>
|
||||
<string name="screen_create_room_public_option_description">"Bu odayı herkes bulabilir.
|
||||
Bunu istediğiniz zaman oda ayarlarından değiştirebilirsiniz."</string>
|
||||
<string name="screen_create_room_public_option_title">"Herkese açık oda"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_description">"Bu odaya herkes katılabilir"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_title">"Herkes"</string>
|
||||
<string name="screen_create_room_room_access_section_header">"Oda Erişimi"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Herkes odaya katılmayı isteyebilir ancak bir yönetici veya moderatörün isteği kabul etmesi gerekecektir"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Katılmak için sor"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Bu odanın genel oda dizininde görünür olması için bir oda adresine ihtiyacınız olacaktır."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Oda adresi"</string>
|
||||
<string name="screen_create_room_room_name_label">"Oda adı"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Oda görünürlüğü"</string>
|
||||
<string name="screen_create_room_title">"Bir oda oluştur"</string>
|
||||
<string name="screen_create_room_topic_label">"Konu (isteğe bağlı)"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Sohbet başlatmaya çalışırken bir hata oluştu"</string>
|
||||
</resources>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="screen_create_room_private_option_title">"Приватна кімната (тільки за запрошенням)"</string>
|
||||
<string name="screen_create_room_public_option_description">"Будь-хто може знайти цю кімнату.
|
||||
Ви можете змінити це в будь-який час у налаштуваннях кімнати."</string>
|
||||
<string name="screen_create_room_public_option_title">"Публічна кімната"</string>
|
||||
<string name="screen_create_room_public_option_title">"Загальнодоступна кімната"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_description">"Будь-хто може приєднатися до цієї кімнати"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_title">"Кожний"</string>
|
||||
<string name="screen_create_room_room_access_section_header">"Доступ до кімнати"</string>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
<string name="screen_create_room_private_option_description">"Bu xonadagi xabarlar shifrlangan. Keyinchalik shifrlashni o‘chirib bo‘lmaydi."</string>
|
||||
<string name="screen_create_room_private_option_title">"Shaxsiy xona (faqat taklif)"</string>
|
||||
<string name="screen_create_room_public_option_description">"Xabarlar shifrlanmagan va har kim ularni o\'qiy oladi. Keyinchalik shifrlashni yoqishingiz mumkin."</string>
|
||||
<string name="screen_create_room_public_option_title">"Jamoat xonasi (har kim)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Xona nomi"</string>
|
||||
<string name="screen_create_room_title">"Xonani yaratish"</string>
|
||||
<string name="screen_create_room_topic_label">"Mavzu (ixtiyoriy)"</string>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="screen_create_room_private_option_title">"私密聊天室"</string>
|
||||
<string name="screen_create_room_public_option_description">"任何人都可以找到此聊天室。
|
||||
您隨時都可以在聊天室設定中變更此設定。"</string>
|
||||
<string name="screen_create_room_public_option_title">"公開聊天室"</string>
|
||||
<string name="screen_create_room_public_option_title">"公開的聊天室"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_description">"任何人都可以加入此聊天室"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_title">"任何人"</string>
|
||||
<string name="screen_create_room_room_access_section_header">"聊天室存取權"</string>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="screen_create_room_private_option_title">"私有聊天室"</string>
|
||||
<string name="screen_create_room_public_option_description">"任何人都能找到此聊天室。
|
||||
你可以随时在聊天室设置中更改。"</string>
|
||||
<string name="screen_create_room_public_option_title">"公开聊天室"</string>
|
||||
<string name="screen_create_room_public_option_title">"公共聊天室"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_description">"任何人都可以加入此房间"</string>
|
||||
<string name="screen_create_room_room_access_section_anyone_option_title">"任何人"</string>
|
||||
<string name="screen_create_room_room_access_section_header">"房间访问权限"</string>
|
||||
|
|
|
|||
|
|
@ -10,5 +10,5 @@
|
|||
<string name="screen_deactivate_account_list_item_2">"Vous retirer de tous les salons et toutes les discussions."</string>
|
||||
<string name="screen_deactivate_account_list_item_3">"Supprimer les informations de votre compte du serveur d’identité."</string>
|
||||
<string name="screen_deactivate_account_list_item_4">"Rendre vos messages invisibles aux futurs membres des salons si vous choisissez de les supprimer. Vos messages seront toujours visibles pour les utilisateurs qui les ont déjà récupérés."</string>
|
||||
<string name="screen_deactivate_account_title">"Désactiver votre compte"</string>
|
||||
<string name="screen_deactivate_account_title">"Désactiver le compte"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -10,5 +10,5 @@
|
|||
<string name="screen_deactivate_account_list_item_2">"Ti rimuove da tutte le stanze di chat."</string>
|
||||
<string name="screen_deactivate_account_list_item_3">"Elimina le informazioni del tuo account dal nostro server di identità."</string>
|
||||
<string name="screen_deactivate_account_list_item_4">"I tuoi messaggi saranno ancora visibili agli utenti registrati, ma non saranno disponibili per gli utenti nuovi o non registrati se decidi di eliminarli."</string>
|
||||
<string name="screen_deactivate_account_title">"Disattivazione dell\'account"</string>
|
||||
<string name="screen_deactivate_account_title">"Disattiva account"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_deactivate_account_confirmation_dialog_content">"Lütfen hesabınızı devre dışı bırakmak istediğinizi onaylayın. Bu işlem geri alınamaz."</string>
|
||||
<string name="screen_deactivate_account_delete_all_messages">"Tüm mesajlarımı sil"</string>
|
||||
<string name="screen_deactivate_account_delete_all_messages_notice">"Uyarı: Gelecekteki kullanıcılar eksik konuşmalar görebilir."</string>
|
||||
<string name="screen_deactivate_account_description">"Hesabınızı devre dışı bırakmak %1$s, şunları yapacaktır:"</string>
|
||||
<string name="screen_deactivate_account_description_bold_part">"geri alınamaz"</string>
|
||||
<string name="screen_deactivate_account_list_item_1">"%1$s (tekrar giriş yapamazsınız ve kimliğiniz yeniden kullanılamaz)."</string>
|
||||
<string name="screen_deactivate_account_list_item_1_bold_part">"Kalıcı olarak devre dışı bırak"</string>
|
||||
<string name="screen_deactivate_account_list_item_2">"Sizi tüm sohbet odalarından çıkarmak."</string>
|
||||
<string name="screen_deactivate_account_list_item_3">"Hesap bilgileriniz kimlik sunucumuzdan silinecek."</string>
|
||||
<string name="screen_deactivate_account_list_item_4">"Mesajlarınız kayıtlı kullanıcılar tarafından görülmeye devam eder, ancak silmeyi seçerseniz yeni veya kayıtlı olmayan kullanıcılar tarafından görüntülenemeyecek."</string>
|
||||
<string name="screen_deactivate_account_title">"Hesabı devre dışı bırak"</string>
|
||||
</resources>
|
||||
|
|
@ -10,5 +10,5 @@
|
|||
<string name="screen_deactivate_account_list_item_2">"Видалити вас з усіх чатів."</string>
|
||||
<string name="screen_deactivate_account_list_item_3">"Видаліть інформацію свого облікового запису з нашого сервера ідентифікації."</string>
|
||||
<string name="screen_deactivate_account_list_item_4">"Ваші повідомлення залишатимуться видимими для зареєстрованих користувачів, але недоступними для нових або незареєстрованих користувачів, якщо ви вирішите їх видалити."</string>
|
||||
<string name="screen_deactivate_account_title">"Відключити обліковий запис"</string>
|
||||
<string name="screen_deactivate_account_title">"Деактивувати обліковий запис"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.compound)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,13 @@
|
|||
|
||||
package io.element.android.features.enterprise.api
|
||||
|
||||
import io.element.android.compound.tokens.generated.SemanticColors
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
interface EnterpriseService {
|
||||
val isEnterpriseBuild: Boolean
|
||||
suspend fun isEnterpriseUser(sessionId: SessionId): Boolean
|
||||
|
||||
fun semanticColorsLight(): SemanticColors
|
||||
fun semanticColorsDark(): SemanticColors
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ android {
|
|||
setupAnvil()
|
||||
|
||||
dependencies {
|
||||
implementation(libs.compound)
|
||||
implementation(projects.anvilannotations)
|
||||
api(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
package io.element.android.features.enterprise.impl
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.compound.tokens.generated.SemanticColors
|
||||
import io.element.android.compound.tokens.generated.compoundColorsDark
|
||||
import io.element.android.compound.tokens.generated.compoundColorsLight
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
|
@ -18,4 +21,8 @@ class DefaultEnterpriseService @Inject constructor() : EnterpriseService {
|
|||
override val isEnterpriseBuild = false
|
||||
|
||||
override suspend fun isEnterpriseUser(sessionId: SessionId) = false
|
||||
|
||||
override fun semanticColorsLight(): SemanticColors = compoundColorsLight
|
||||
|
||||
override fun semanticColorsDark(): SemanticColors = compoundColorsDark
|
||||
}
|
||||
|
|
|
|||
11
features/ftue/impl/src/main/res/values-tr/translations.xml
Normal file
11
features/ftue/impl/src/main/res/values-tr/translations.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_notification_optin_subtitle">"Ayarlarınızı daha sonra değiştirebilirsiniz."</string>
|
||||
<string name="screen_notification_optin_title">"Bildirimlere izin verin ve hiçbir mesajı kaçırmayın"</string>
|
||||
<string name="screen_welcome_bullet_1">"Çağrılar, anketler, arama ve daha fazlası bu yıl içinde eklenecek."</string>
|
||||
<string name="screen_welcome_bullet_2">"Şifrelenmiş odalar için mesaj geçmişi henüz mevcut değil."</string>
|
||||
<string name="screen_welcome_bullet_3">"Düşüncelerinizi bizimle paylaşmanızı çok isteriz. Ayarlar sayfasından düşüncelerinizi bize iletin."</string>
|
||||
<string name="screen_welcome_button">"Hadi başlayalım!"</string>
|
||||
<string name="screen_welcome_subtitle">"İşte bilmeniz gerekenler:"</string>
|
||||
<string name="screen_welcome_title">"%1$s hoş geldiniz!"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invites_decline_chat_message">"%1$s katılma davetini reddetmek istediğinizden emin misiniz?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Daveti reddet"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"%1$s ile bu özel sohbeti reddetmek istediğinizden emin misiniz?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Sohbeti reddet"</string>
|
||||
<string name="screen_invites_empty_list">"Davet Yok"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) sizi davet etti"</string>
|
||||
</resources>
|
||||
|
|
@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
|
|||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakePendingRoom
|
||||
import io.element.android.libraries.matrix.test.room.FakeRoomPreview
|
||||
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
|
||||
import io.element.android.libraries.push.api.notifications.NotificationCleaner
|
||||
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
|
||||
|
|
@ -77,9 +77,11 @@ class AcceptDeclineInvitePresenterTest {
|
|||
val declineInviteFailure = lambdaRecorder { ->
|
||||
Result.failure<Unit>(RuntimeException("Failed to leave room"))
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
getPendingRoomResults[A_ROOM_ID] = FakePendingRoom(declineInviteResult = declineInviteFailure)
|
||||
}
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(FakeRoomPreview(declineInviteResult = declineInviteFailure))
|
||||
}
|
||||
)
|
||||
val presenter = createAcceptDeclineInvitePresenter(client = client)
|
||||
presenter.test {
|
||||
val inviteData = anInviteData()
|
||||
|
|
@ -120,9 +122,11 @@ class AcceptDeclineInvitePresenterTest {
|
|||
val declineInviteSuccess = lambdaRecorder { ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val client = FakeMatrixClient().apply {
|
||||
getPendingRoomResults[A_ROOM_ID] = FakePendingRoom(declineInviteResult = declineInviteSuccess)
|
||||
}
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(FakeRoomPreview(declineInviteResult = declineInviteSuccess))
|
||||
}
|
||||
)
|
||||
val presenter = createAcceptDeclineInvitePresenter(
|
||||
client = client,
|
||||
notificationCleaner = fakeNotificationCleaner,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ package io.element.android.features.joinroom.impl
|
|||
|
||||
sealed interface JoinRoomEvents {
|
||||
data object RetryFetchingContent : JoinRoomEvents
|
||||
data object DismissErrorAndHideContent : JoinRoomEvents
|
||||
data object JoinRoom : JoinRoomEvents
|
||||
data object KnockRoom : JoinRoomEvents
|
||||
data object ForgetRoom : JoinRoomEvents
|
||||
data class CancelKnock(val requiresConfirmation: Boolean) : JoinRoomEvents
|
||||
data class UpdateKnockMessage(val message: String) : JoinRoomEvents
|
||||
data object ClearActionStates : JoinRoomEvents
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ class JoinRoomNode @AssistedInject constructor(
|
|||
state = state,
|
||||
onBackClick = ::navigateUp,
|
||||
onJoinSuccess = ::navigateUp,
|
||||
onForgetSuccess = ::navigateUp,
|
||||
onCancelKnockSuccess = {},
|
||||
onKnockSuccess = {},
|
||||
modifier = modifier
|
||||
|
|
|
|||
|
|
@ -26,22 +26,28 @@ import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
|||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.InviteData
|
||||
import io.element.android.features.joinroom.impl.di.CancelKnockRoom
|
||||
import io.element.android.features.joinroom.impl.di.ForgetRoom
|
||||
import io.element.android.features.joinroom.impl.di.KnockRoom
|
||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runUpdatingState
|
||||
import io.element.android.libraries.core.extensions.mapFailure
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.exception.ErrorKind
|
||||
import io.element.android.libraries.matrix.api.getRoomInfoFlow
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRoom
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -58,6 +64,7 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
private val joinRoom: JoinRoom,
|
||||
private val knockRoom: KnockRoom,
|
||||
private val cancelKnockRoom: CancelKnockRoom,
|
||||
private val forgetRoom: ForgetRoom,
|
||||
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||
private val buildMeta: BuildMeta,
|
||||
) : Presenter<JoinRoomState> {
|
||||
|
|
@ -79,31 +86,59 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
val joinAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val knockAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val cancelKnockAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
val forgetRoomAction: MutableState<AsyncAction<Unit>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||
var knockMessage by rememberSaveable { mutableStateOf("") }
|
||||
var isDismissingContent by remember { mutableStateOf(false) }
|
||||
val contentState by produceState<ContentState>(
|
||||
initialValue = ContentState.Loading(roomIdOrAlias),
|
||||
initialValue = ContentState.Loading,
|
||||
key1 = roomInfo,
|
||||
key2 = retryCount,
|
||||
key3 = isDismissingContent,
|
||||
) {
|
||||
when {
|
||||
isDismissingContent -> value = ContentState.Dismissing
|
||||
roomInfo.isPresent -> {
|
||||
value = roomInfo.get().toContentState()
|
||||
val (sender, reason) = when (roomInfo.get().currentUserMembership) {
|
||||
CurrentUserMembership.BANNED -> {
|
||||
// Workaround to get info about the sender for banned rooms
|
||||
// TODO re-do this once we have a better API in the SDK
|
||||
val preview = matrixClient.getRoomPreview(roomIdOrAlias, serverNames)
|
||||
val membershipDetalis = preview.getOrNull()?.membershipDetails()?.getOrNull()
|
||||
membershipDetalis?.senderMember to membershipDetalis?.currentUserMember?.membershipChangeReason
|
||||
}
|
||||
CurrentUserMembership.INVITED -> {
|
||||
roomInfo.get().inviter to null
|
||||
}
|
||||
else -> null to null
|
||||
}
|
||||
value = roomInfo.get().toContentState(sender, reason)
|
||||
}
|
||||
roomDescription.isPresent -> {
|
||||
value = roomDescription.get().toContentState()
|
||||
}
|
||||
else -> {
|
||||
value = ContentState.Loading(roomIdOrAlias)
|
||||
val result = matrixClient.getRoomPreviewInfo(roomIdOrAlias, serverNames)
|
||||
value = ContentState.Loading
|
||||
val result = matrixClient.getRoomPreview(roomIdOrAlias, serverNames)
|
||||
value = result.fold(
|
||||
onSuccess = { previewInfo ->
|
||||
previewInfo.toContentState()
|
||||
onSuccess = { preview ->
|
||||
val membershipInfo = when (preview.info.membership) {
|
||||
CurrentUserMembership.INVITED,
|
||||
CurrentUserMembership.BANNED,
|
||||
CurrentUserMembership.KNOCKED -> {
|
||||
preview.membershipDetails().getOrNull()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
preview.info.toContentState(
|
||||
senderMember = membershipInfo?.senderMember,
|
||||
reason = membershipInfo?.currentUserMember?.membershipChangeReason,
|
||||
)
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
if (throwable.message?.contains("403") == true) {
|
||||
ContentState.UnknownRoom(roomIdOrAlias)
|
||||
if (throwable is ClientException.MatrixApi && (throwable.kind == ErrorKind.NotFound || throwable.kind == ErrorKind.Forbidden)) {
|
||||
ContentState.UnknownRoom
|
||||
} else {
|
||||
ContentState.Failure(roomIdOrAlias, throwable)
|
||||
ContentState.Failure(throwable)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -136,18 +171,25 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
knockAction.value = AsyncAction.Uninitialized
|
||||
joinAction.value = AsyncAction.Uninitialized
|
||||
cancelKnockAction.value = AsyncAction.Uninitialized
|
||||
forgetRoomAction.value = AsyncAction.Uninitialized
|
||||
}
|
||||
is JoinRoomEvents.UpdateKnockMessage -> {
|
||||
knockMessage = event.message.take(MAX_KNOCK_MESSAGE_LENGTH)
|
||||
}
|
||||
JoinRoomEvents.DismissErrorAndHideContent -> {
|
||||
isDismissingContent = true
|
||||
}
|
||||
JoinRoomEvents.ForgetRoom -> coroutineScope.forgetRoom(forgetRoomAction)
|
||||
}
|
||||
}
|
||||
|
||||
return JoinRoomState(
|
||||
roomIdOrAlias = roomIdOrAlias,
|
||||
contentState = contentState,
|
||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||
joinAction = joinAction.value,
|
||||
knockAction = knockAction.value,
|
||||
forgetAction = forgetRoomAction.value,
|
||||
cancelKnockAction = cancelKnockAction.value,
|
||||
applicationName = buildMeta.applicationName,
|
||||
knockMessage = knockMessage,
|
||||
|
|
@ -161,7 +203,13 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
roomIdOrAlias = roomIdOrAlias,
|
||||
serverNames = serverNames,
|
||||
trigger = trigger
|
||||
)
|
||||
).mapFailure {
|
||||
if (it is ClientException.MatrixApi && it.kind == ErrorKind.Forbidden) {
|
||||
JoinRoomFailures.UnauthorizedJoin
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,9 +228,15 @@ class JoinRoomPresenter @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.forgetRoom(forgetAction: MutableState<AsyncAction<Unit>>) = launch {
|
||||
forgetAction.runUpdatingState {
|
||||
forgetRoom.invoke(roomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun RoomPreviewInfo.toContentState(): ContentState {
|
||||
private fun RoomPreviewInfo.toContentState(senderMember: RoomMember?, reason: String?): ContentState {
|
||||
return ContentState.Loaded(
|
||||
roomId = roomId,
|
||||
name = name,
|
||||
|
|
@ -192,12 +246,11 @@ private fun RoomPreviewInfo.toContentState(): ContentState {
|
|||
isDm = false,
|
||||
roomType = roomType,
|
||||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = when {
|
||||
// Note when isInvited, roomInfo will be used, so if this happen, it will be temporary.
|
||||
isInvited -> JoinAuthorisationStatus.IsInvited(null)
|
||||
canKnock -> JoinAuthorisationStatus.CanKnock
|
||||
isPublic -> JoinAuthorisationStatus.CanJoin
|
||||
else -> JoinAuthorisationStatus.Unknown
|
||||
joinAuthorisationStatus = when (membership) {
|
||||
CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(senderMember?.toInviteSender())
|
||||
CurrentUserMembership.BANNED -> JoinAuthorisationStatus.IsBanned(senderMember?.toInviteSender(), reason)
|
||||
CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked
|
||||
else -> joinRule.toJoinAuthorisationStatus()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -222,27 +275,42 @@ internal fun RoomDescription.toContentState(): ContentState {
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun MatrixRoomInfo.toContentState(): ContentState {
|
||||
internal fun MatrixRoomInfo.toContentState(membershipSender: RoomMember?, reason: String?): ContentState {
|
||||
return ContentState.Loaded(
|
||||
roomId = id,
|
||||
name = name,
|
||||
topic = topic,
|
||||
alias = canonicalAlias,
|
||||
numberOfMembers = activeMembersCount,
|
||||
numberOfMembers = joinedMembersCount,
|
||||
isDm = isDm,
|
||||
roomType = if (isSpace) RoomType.Space else RoomType.Room,
|
||||
roomAvatarUrl = avatarUrl,
|
||||
joinAuthorisationStatus = when {
|
||||
currentUserMembership == CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(
|
||||
inviteSender = inviter?.toInviteSender()
|
||||
joinAuthorisationStatus = when (currentUserMembership) {
|
||||
CurrentUserMembership.INVITED -> JoinAuthorisationStatus.IsInvited(
|
||||
inviteSender = membershipSender?.toInviteSender()
|
||||
)
|
||||
currentUserMembership == CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked
|
||||
isPublic -> JoinAuthorisationStatus.CanJoin
|
||||
else -> JoinAuthorisationStatus.Unknown
|
||||
CurrentUserMembership.BANNED -> JoinAuthorisationStatus.IsBanned(
|
||||
banSender = membershipSender?.toInviteSender(),
|
||||
reason = reason,
|
||||
)
|
||||
CurrentUserMembership.KNOCKED -> JoinAuthorisationStatus.IsKnocked
|
||||
else -> joinRule.toJoinAuthorisationStatus()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun JoinRule?.toJoinAuthorisationStatus(): JoinAuthorisationStatus {
|
||||
return when (this) {
|
||||
JoinRule.Knock,
|
||||
is JoinRule.KnockRestricted -> JoinAuthorisationStatus.CanKnock
|
||||
JoinRule.Invite,
|
||||
JoinRule.Private -> JoinAuthorisationStatus.NeedInvite
|
||||
is JoinRule.Restricted -> JoinAuthorisationStatus.Restricted
|
||||
JoinRule.Public -> JoinAuthorisationStatus.CanJoin
|
||||
else -> JoinAuthorisationStatus.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun ContentState.toInviteData(): InviteData? {
|
||||
return when (this) {
|
||||
|
|
|
|||
|
|
@ -22,30 +22,49 @@ internal const val MAX_KNOCK_MESSAGE_LENGTH = 500
|
|||
|
||||
@Immutable
|
||||
data class JoinRoomState(
|
||||
val roomIdOrAlias: RoomIdOrAlias,
|
||||
val contentState: ContentState,
|
||||
val acceptDeclineInviteState: AcceptDeclineInviteState,
|
||||
val joinAction: AsyncAction<Unit>,
|
||||
val knockAction: AsyncAction<Unit>,
|
||||
val forgetAction: AsyncAction<Unit>,
|
||||
val cancelKnockAction: AsyncAction<Unit>,
|
||||
val applicationName: String,
|
||||
private val applicationName: String,
|
||||
val knockMessage: String,
|
||||
val eventSink: (JoinRoomEvents) -> Unit
|
||||
) {
|
||||
val isJoinActionUnauthorized = joinAction is AsyncAction.Failure && joinAction.error is JoinRoomFailures.UnauthorizedJoin
|
||||
val joinAuthorisationStatus = when (contentState) {
|
||||
// Use the join authorisation status from the loaded content state
|
||||
is ContentState.Loaded -> contentState.joinAuthorisationStatus
|
||||
// Assume that if the room is unknown, the user can join it
|
||||
is ContentState.UnknownRoom -> JoinAuthorisationStatus.CanJoin
|
||||
// Otherwise assume that the user can't join the room
|
||||
else -> JoinAuthorisationStatus.Unknown
|
||||
is ContentState.Loaded -> {
|
||||
when {
|
||||
contentState.roomType == RoomType.Space -> {
|
||||
JoinAuthorisationStatus.IsSpace(applicationName)
|
||||
}
|
||||
isJoinActionUnauthorized -> {
|
||||
JoinAuthorisationStatus.Unauthorized
|
||||
}
|
||||
else -> {
|
||||
contentState.joinAuthorisationStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
is ContentState.UnknownRoom -> {
|
||||
if (isJoinActionUnauthorized) {
|
||||
JoinAuthorisationStatus.Unauthorized
|
||||
} else {
|
||||
JoinAuthorisationStatus.Unknown
|
||||
}
|
||||
}
|
||||
else -> JoinAuthorisationStatus.None
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed interface ContentState {
|
||||
data class Loading(val roomIdOrAlias: RoomIdOrAlias) : ContentState
|
||||
data class Failure(val roomIdOrAlias: RoomIdOrAlias, val error: Throwable) : ContentState
|
||||
data class UnknownRoom(val roomIdOrAlias: RoomIdOrAlias) : ContentState
|
||||
data object Dismissing : ContentState
|
||||
data object Loading : ContentState
|
||||
data class Failure(val error: Throwable) : ContentState
|
||||
data object UnknownRoom : ContentState
|
||||
data class Loaded(
|
||||
val roomId: RoomId,
|
||||
val name: String?,
|
||||
|
|
@ -71,9 +90,19 @@ sealed interface ContentState {
|
|||
}
|
||||
|
||||
sealed interface JoinAuthorisationStatus {
|
||||
data object None : JoinAuthorisationStatus
|
||||
data class IsSpace(val applicationName: String) : JoinAuthorisationStatus
|
||||
data class IsInvited(val inviteSender: InviteSender?) : JoinAuthorisationStatus
|
||||
data class IsBanned(val banSender: InviteSender?, val reason: String?) : JoinAuthorisationStatus
|
||||
data object IsKnocked : JoinAuthorisationStatus
|
||||
data object CanKnock : JoinAuthorisationStatus
|
||||
data object CanJoin : JoinAuthorisationStatus
|
||||
data object NeedInvite : JoinAuthorisationStatus
|
||||
data object Restricted : JoinAuthorisationStatus
|
||||
data object Unknown : JoinAuthorisationStatus
|
||||
data object Unauthorized : JoinAuthorisationStatus
|
||||
}
|
||||
|
||||
sealed class JoinRoomFailures : Exception() {
|
||||
data object UnauthorizedJoin : JoinRoomFailures()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.ui.model.InviteSender
|
||||
|
||||
|
|
@ -25,10 +26,10 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
|||
override val values: Sequence<JoinRoomState>
|
||||
get() = sequenceOf(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadingContentState()
|
||||
contentState = ContentState.Loading
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = anUnknownContentState()
|
||||
contentState = ContentState.UnknownRoom
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(
|
||||
|
|
@ -40,6 +41,14 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
|||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin)
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin),
|
||||
joinAction = AsyncAction.Failure(JoinRoomFailures.UnauthorizedJoin)
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin),
|
||||
joinAction = AsyncAction.Failure(ClientException.Generic("Something went wrong"))
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null))
|
||||
),
|
||||
|
|
@ -52,9 +61,6 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
|||
aJoinRoomState(
|
||||
contentState = aFailureContentState()
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aFailureContentState(roomIdOrAlias = A_ROOM_ALIAS.toRoomIdOrAlias())
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(
|
||||
roomId = RoomId("!aSpaceId:domain"),
|
||||
|
|
@ -98,23 +104,42 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
|
|||
name = "A knocked Room",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsKnocked
|
||||
)
|
||||
)
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(
|
||||
name = "A private room",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.NeedInvite
|
||||
)
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(
|
||||
name = "A banned room",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned(
|
||||
banSender = InviteSender(
|
||||
userId = UserId("@alice:domain"),
|
||||
displayName = "Alice",
|
||||
avatarData = AvatarData("alice", "Alice", size = AvatarSize.InviteSender),
|
||||
membershipChangeReason = "spamming"
|
||||
),
|
||||
reason = "spamming",
|
||||
),
|
||||
)
|
||||
),
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(
|
||||
name = "A restricted room",
|
||||
joinAuthorisationStatus = JoinAuthorisationStatus.Restricted,
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aFailureContentState(
|
||||
roomIdOrAlias: RoomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias()
|
||||
): ContentState {
|
||||
fun aFailureContentState(): ContentState {
|
||||
return ContentState.Failure(
|
||||
roomIdOrAlias = roomIdOrAlias,
|
||||
error = Exception("Error"),
|
||||
)
|
||||
}
|
||||
|
||||
fun anUnknownContentState(roomId: RoomId = A_ROOM_ID) = ContentState.UnknownRoom(roomId.toRoomIdOrAlias())
|
||||
|
||||
fun aLoadingContentState(roomId: RoomId = A_ROOM_ID) = ContentState.Loading(roomId.toRoomIdOrAlias())
|
||||
|
||||
fun aLoadedContentState(
|
||||
roomId: RoomId = A_ROOM_ID,
|
||||
name: String? = "Element X android",
|
||||
|
|
@ -138,19 +163,23 @@ fun aLoadedContentState(
|
|||
)
|
||||
|
||||
fun aJoinRoomState(
|
||||
roomIdOrAlias: RoomIdOrAlias = A_ROOM_ALIAS.toRoomIdOrAlias(),
|
||||
contentState: ContentState = aLoadedContentState(),
|
||||
acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(),
|
||||
joinAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
knockAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
forgetAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
cancelKnockAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
knockMessage: String = "",
|
||||
eventSink: (JoinRoomEvents) -> Unit = {}
|
||||
) = JoinRoomState(
|
||||
roomIdOrAlias = roomIdOrAlias,
|
||||
contentState = contentState,
|
||||
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||
joinAction = joinAction,
|
||||
knockAction = knockAction,
|
||||
cancelKnockAction = cancelKnockAction,
|
||||
forgetAction = forgetAction,
|
||||
applicationName = "AppName",
|
||||
knockMessage = knockMessage,
|
||||
eventSink = eventSink
|
||||
|
|
@ -160,10 +189,12 @@ internal fun anInviteSender(
|
|||
userId: UserId = UserId("@bob:domain"),
|
||||
displayName: String = "Bob",
|
||||
avatarData: AvatarData = AvatarData(userId.value, displayName, size = AvatarSize.InviteSender),
|
||||
membershipChangeReason: String? = null,
|
||||
) = InviteSender(
|
||||
userId = userId,
|
||||
displayName = displayName,
|
||||
avatarData = avatarData,
|
||||
membershipChangeReason = membershipChangeReason,
|
||||
)
|
||||
|
||||
private val A_ROOM_ID = RoomId("!exa:matrix.org")
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -32,7 +32,6 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -47,7 +46,8 @@ import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubti
|
|||
import io.element.android.libraries.designsystem.atomic.molecules.RoomPreviewMembersCountMolecule
|
||||
import io.element.android.libraries.designsystem.atomic.organisms.RoomPreviewOrganism
|
||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||
import io.element.android.libraries.designsystem.background.LightGradientBackground
|
||||
import io.element.android.libraries.designsystem.components.Announcement
|
||||
import io.element.android.libraries.designsystem.components.AnnouncementType
|
||||
import io.element.android.libraries.designsystem.components.BigIcon
|
||||
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
|
|
@ -55,16 +55,17 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
|||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.components.button.SuperButton
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||
import io.element.android.libraries.designsystem.components.dialogs.RetryDialog
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextField
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.ui.components.InviteSenderView
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
|
|
@ -74,30 +75,33 @@ fun JoinRoomView(
|
|||
onBackClick: () -> Unit,
|
||||
onJoinSuccess: () -> Unit,
|
||||
onKnockSuccess: () -> Unit,
|
||||
onForgetSuccess: () -> Unit,
|
||||
onCancelKnockSuccess: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
LightGradientBackground()
|
||||
HeaderFooterPage(
|
||||
containerColor = Color.Transparent,
|
||||
paddingValues = PaddingValues(16.dp),
|
||||
paddingValues = PaddingValues(
|
||||
horizontal = 16.dp,
|
||||
vertical = 32.dp
|
||||
),
|
||||
topBar = {
|
||||
JoinRoomTopBar(contentState = state.contentState, onBackClick = onBackClick)
|
||||
},
|
||||
content = {
|
||||
JoinRoomContent(
|
||||
roomIdOrAlias = state.roomIdOrAlias,
|
||||
contentState = state.contentState,
|
||||
applicationName = state.applicationName,
|
||||
knockMessage = state.knockMessage,
|
||||
onKnockMessageUpdate = { state.eventSink(JoinRoomEvents.UpdateKnockMessage(it)) },
|
||||
)
|
||||
},
|
||||
footer = {
|
||||
JoinRoomFooter(
|
||||
state = state,
|
||||
joinAuthorisationStatus = state.joinAuthorisationStatus,
|
||||
onAcceptInvite = {
|
||||
state.eventSink(JoinRoomEvents.AcceptInvite)
|
||||
},
|
||||
|
|
@ -113,31 +117,55 @@ fun JoinRoomView(
|
|||
onCancelKnock = {
|
||||
state.eventSink(JoinRoomEvents.CancelKnock(requiresConfirmation = true))
|
||||
},
|
||||
onRetry = {
|
||||
state.eventSink(JoinRoomEvents.RetryFetchingContent)
|
||||
onForgetRoom = {
|
||||
state.eventSink(JoinRoomEvents.ForgetRoom)
|
||||
},
|
||||
onGoBack = onBackClick,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (state.contentState is ContentState.Failure) {
|
||||
RetryDialog(
|
||||
title = stringResource(R.string.screen_join_room_loading_alert_title),
|
||||
content = stringResource(CommonStrings.error_network_or_server_issue),
|
||||
onRetry = { state.eventSink(JoinRoomEvents.RetryFetchingContent) },
|
||||
onDismiss = {
|
||||
state.eventSink(JoinRoomEvents.DismissErrorAndHideContent)
|
||||
onBackClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
// This particular error is shown directly in the footer
|
||||
if (!state.isJoinActionUnauthorized) {
|
||||
AsyncActionView(
|
||||
async = state.joinAction,
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
onSuccess = { onJoinSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
)
|
||||
}
|
||||
AsyncActionView(
|
||||
async = state.joinAction,
|
||||
onSuccess = { onJoinSuccess() },
|
||||
async = state.knockAction,
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
onSuccess = { onKnockSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
)
|
||||
AsyncActionView(
|
||||
async = state.knockAction,
|
||||
onSuccess = { onKnockSuccess() },
|
||||
async = state.forgetAction,
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
onSuccess = { onForgetSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
)
|
||||
AsyncActionView(
|
||||
async = state.cancelKnockAction,
|
||||
onSuccess = { onCancelKnockSuccess() },
|
||||
onErrorDismiss = { state.eventSink(JoinRoomEvents.ClearActionStates) },
|
||||
errorMessage = {
|
||||
stringResource(CommonStrings.error_unknown)
|
||||
},
|
||||
errorTitle = { stringResource(CommonStrings.common_something_went_wrong) },
|
||||
errorMessage = { stringResource(CommonStrings.error_network_or_server_issue) },
|
||||
confirmationDialog = {
|
||||
ConfirmationDialog(
|
||||
content = stringResource(R.string.screen_join_room_cancel_knock_alert_description),
|
||||
|
|
@ -153,13 +181,13 @@ fun JoinRoomView(
|
|||
|
||||
@Composable
|
||||
private fun JoinRoomFooter(
|
||||
state: JoinRoomState,
|
||||
joinAuthorisationStatus: JoinAuthorisationStatus,
|
||||
onAcceptInvite: () -> Unit,
|
||||
onDeclineInvite: () -> Unit,
|
||||
onJoinRoom: () -> Unit,
|
||||
onKnockRoom: () -> Unit,
|
||||
onCancelKnock: () -> Unit,
|
||||
onRetry: () -> Unit,
|
||||
onForgetRoom: () -> Unit,
|
||||
onGoBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -168,79 +196,170 @@ private fun JoinRoomFooter(
|
|||
.fillMaxWidth()
|
||||
.padding(top = 8.dp)
|
||||
) {
|
||||
if (state.contentState is ContentState.Failure) {
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_retry),
|
||||
onClick = onRetry,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
} else if (state.contentState is ContentState.Loaded && state.contentState.roomType == RoomType.Space) {
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_go_back),
|
||||
onClick = onGoBack,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
} else {
|
||||
val joinAuthorisationStatus = state.joinAuthorisationStatus
|
||||
when (joinAuthorisationStatus) {
|
||||
is JoinAuthorisationStatus.IsInvited -> {
|
||||
ButtonRowMolecule(horizontalArrangement = Arrangement.spacedBy(20.dp)) {
|
||||
OutlinedButton(
|
||||
text = stringResource(CommonStrings.action_decline),
|
||||
onClick = onDeclineInvite,
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
)
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_accept),
|
||||
onClick = onAcceptInvite,
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
)
|
||||
}
|
||||
}
|
||||
JoinAuthorisationStatus.CanJoin -> {
|
||||
SuperButton(
|
||||
onClick = onJoinRoom,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
buttonSize = ButtonSize.Large,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_join_room_join_action),
|
||||
)
|
||||
}
|
||||
}
|
||||
JoinAuthorisationStatus.CanKnock -> {
|
||||
SuperButton(
|
||||
onClick = onKnockRoom,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
buttonSize = ButtonSize.Large,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_join_room_knock_action),
|
||||
)
|
||||
}
|
||||
}
|
||||
JoinAuthorisationStatus.IsKnocked -> {
|
||||
when (joinAuthorisationStatus) {
|
||||
is JoinAuthorisationStatus.IsInvited -> {
|
||||
ButtonRowMolecule(horizontalArrangement = Arrangement.spacedBy(20.dp)) {
|
||||
OutlinedButton(
|
||||
text = stringResource(R.string.screen_join_room_cancel_knock_action),
|
||||
onClick = onCancelKnock,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
text = stringResource(CommonStrings.action_decline),
|
||||
onClick = onDeclineInvite,
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
)
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_accept),
|
||||
onClick = onAcceptInvite,
|
||||
modifier = Modifier.weight(1f),
|
||||
size = ButtonSize.LargeLowPadding,
|
||||
)
|
||||
}
|
||||
JoinAuthorisationStatus.Unknown -> Unit
|
||||
}
|
||||
JoinAuthorisationStatus.CanJoin -> {
|
||||
SuperButton(
|
||||
onClick = onJoinRoom,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
buttonSize = ButtonSize.Large,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_join_room_join_action),
|
||||
)
|
||||
}
|
||||
}
|
||||
JoinAuthorisationStatus.CanKnock -> {
|
||||
SuperButton(
|
||||
onClick = onKnockRoom,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
buttonSize = ButtonSize.Large,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_join_room_knock_action),
|
||||
)
|
||||
}
|
||||
}
|
||||
JoinAuthorisationStatus.IsKnocked -> {
|
||||
OutlinedButton(
|
||||
text = stringResource(R.string.screen_join_room_cancel_knock_action),
|
||||
onClick = onCancelKnock,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
JoinAuthorisationStatus.NeedInvite -> {
|
||||
Announcement(
|
||||
title = stringResource(R.string.screen_join_room_invite_required_message),
|
||||
description = null,
|
||||
type = AnnouncementType.Informative(isCritical = false),
|
||||
)
|
||||
}
|
||||
is JoinAuthorisationStatus.IsBanned -> JoinBannedFooter(joinAuthorisationStatus, onForgetRoom)
|
||||
JoinAuthorisationStatus.Unknown -> JoinRestrictedFooter(onJoinRoom)
|
||||
JoinAuthorisationStatus.Restricted -> JoinRestrictedFooter(onJoinRoom)
|
||||
JoinAuthorisationStatus.Unauthorized -> JoinUnauthorizedFooter(onGoBack)
|
||||
is JoinAuthorisationStatus.IsSpace -> UnsupportedSpaceFooter(joinAuthorisationStatus.applicationName, onGoBack)
|
||||
JoinAuthorisationStatus.None -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun JoinRoomContent(
|
||||
contentState: ContentState,
|
||||
private fun JoinUnauthorizedFooter(
|
||||
onOkClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Announcement(
|
||||
title = stringResource(R.string.screen_join_room_fail_message),
|
||||
description = stringResource(R.string.screen_join_room_fail_reason),
|
||||
type = AnnouncementType.Informative(isCritical = true),
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_ok),
|
||||
onClick = onOkClick,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun JoinBannedFooter(
|
||||
status: JoinAuthorisationStatus.IsBanned,
|
||||
onForgetRoom: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
val banReason = status.reason?.let {
|
||||
stringResource(R.string.screen_join_room_ban_reason, it.removeSuffix("."))
|
||||
}
|
||||
val title = if (status.banSender != null) {
|
||||
stringResource(R.string.screen_join_room_ban_by_message, status.banSender.displayName)
|
||||
} else {
|
||||
stringResource(R.string.screen_join_room_ban_message)
|
||||
}
|
||||
Announcement(
|
||||
title = title,
|
||||
description = banReason,
|
||||
type = AnnouncementType.Informative(isCritical = true),
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Button(
|
||||
text = stringResource(R.string.screen_join_room_forget_action),
|
||||
onClick = onForgetRoom,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun JoinRestrictedFooter(
|
||||
onJoinRoom: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Announcement(
|
||||
title = stringResource(R.string.screen_join_room_join_restricted_message),
|
||||
description = null,
|
||||
type = AnnouncementType.Informative(),
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
SuperButton(
|
||||
onClick = onJoinRoom,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
buttonSize = ButtonSize.Large,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.screen_join_room_join_action),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UnsupportedSpaceFooter(
|
||||
applicationName: String,
|
||||
onGoBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Announcement(
|
||||
title = stringResource(R.string.screen_join_room_space_not_supported_title),
|
||||
description = stringResource(R.string.screen_join_room_space_not_supported_description, applicationName),
|
||||
type = AnnouncementType.Informative(),
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Button(
|
||||
text = stringResource(CommonStrings.action_ok),
|
||||
onClick = onGoBack,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun JoinRoomContent(
|
||||
roomIdOrAlias: RoomIdOrAlias,
|
||||
contentState: ContentState,
|
||||
knockMessage: String,
|
||||
onKnockMessageUpdate: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -256,67 +375,67 @@ private fun JoinRoomContent(
|
|||
DefaultLoadedContent(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
contentState = contentState,
|
||||
applicationName = applicationName,
|
||||
knockMessage = knockMessage,
|
||||
onKnockMessageUpdate = onKnockMessageUpdate
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is ContentState.UnknownRoom -> {
|
||||
RoomPreviewOrganism(
|
||||
avatar = {
|
||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
||||
},
|
||||
title = {
|
||||
RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview))
|
||||
},
|
||||
subtitle = {
|
||||
RoomPreviewSubtitleAtom(stringResource(R.string.screen_join_room_subtitle_no_preview))
|
||||
},
|
||||
)
|
||||
}
|
||||
is ContentState.Loading -> {
|
||||
RoomPreviewOrganism(
|
||||
avatar = {
|
||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
||||
},
|
||||
title = {
|
||||
PlaceholderAtom(width = 200.dp, height = 22.dp)
|
||||
},
|
||||
subtitle = {
|
||||
PlaceholderAtom(width = 140.dp, height = 20.dp)
|
||||
},
|
||||
)
|
||||
}
|
||||
is ContentState.Failure -> {
|
||||
RoomPreviewOrganism(
|
||||
avatar = {
|
||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
||||
},
|
||||
title = {
|
||||
when (contentState.roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> {
|
||||
RoomPreviewTitleAtom(contentState.roomIdOrAlias.identifier)
|
||||
}
|
||||
is RoomIdOrAlias.Id -> {
|
||||
PlaceholderAtom(width = 200.dp, height = 22.dp)
|
||||
}
|
||||
}
|
||||
},
|
||||
subtitle = {
|
||||
Text(
|
||||
text = stringResource(id = CommonStrings.error_unknown),
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
is ContentState.UnknownRoom -> UnknownRoomContent()
|
||||
is ContentState.Loading -> IncompleteContent(roomIdOrAlias, isLoading = true)
|
||||
is ContentState.Dismissing -> IncompleteContent(roomIdOrAlias, isLoading = false)
|
||||
is ContentState.Failure -> IncompleteContent(roomIdOrAlias, isLoading = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UnknownRoomContent(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
RoomPreviewOrganism(
|
||||
modifier = modifier,
|
||||
avatar = {
|
||||
Spacer(modifier = Modifier.size(AvatarSize.RoomHeader.dp))
|
||||
},
|
||||
title = {
|
||||
RoomPreviewTitleAtom(stringResource(R.string.screen_join_room_title_no_preview))
|
||||
},
|
||||
subtitle = {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IncompleteContent(
|
||||
roomIdOrAlias: RoomIdOrAlias,
|
||||
isLoading: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
RoomPreviewOrganism(
|
||||
modifier = modifier,
|
||||
avatar = {
|
||||
PlaceholderAtom(width = AvatarSize.RoomHeader.dp, height = AvatarSize.RoomHeader.dp)
|
||||
},
|
||||
title = {
|
||||
when (roomIdOrAlias) {
|
||||
is RoomIdOrAlias.Alias -> {
|
||||
RoomPreviewSubtitleAtom(roomIdOrAlias.identifier)
|
||||
}
|
||||
is RoomIdOrAlias.Id -> {
|
||||
PlaceholderAtom(width = 200.dp, height = 22.dp)
|
||||
}
|
||||
}
|
||||
},
|
||||
subtitle = {
|
||||
if (isLoading) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IsKnockedLoadedContent(modifier: Modifier = Modifier) {
|
||||
BoxWithConstraints(
|
||||
|
|
@ -337,7 +456,6 @@ private fun IsKnockedLoadedContent(modifier: Modifier = Modifier) {
|
|||
@Composable
|
||||
private fun DefaultLoadedContent(
|
||||
contentState: ContentState.Loaded,
|
||||
applicationName: String,
|
||||
knockMessage: String,
|
||||
onKnockMessageUpdate: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -374,21 +492,7 @@ private fun DefaultLoadedContent(
|
|||
InviteSenderView(inviteSender = inviteSender)
|
||||
}
|
||||
RoomPreviewDescriptionAtom(contentState.topic ?: "")
|
||||
if (contentState.roomType == RoomType.Space) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.screen_join_room_space_not_supported_title),
|
||||
textAlign = TextAlign.Center,
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.screen_join_room_space_not_supported_description, applicationName),
|
||||
textAlign = TextAlign.Center,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
} else if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanKnock) {
|
||||
if (contentState.joinAuthorisationStatus is JoinAuthorisationStatus.CanKnock) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
val supportingText = if (knockMessage.isNotEmpty()) {
|
||||
"${knockMessage.length}/$MAX_KNOCK_MESSAGE_LENGTH"
|
||||
|
|
@ -462,6 +566,7 @@ internal fun JoinRoomViewPreview(@PreviewParameter(JoinRoomStateProvider::class)
|
|||
onBackClick = { },
|
||||
onJoinSuccess = { },
|
||||
onKnockSuccess = { },
|
||||
onForgetSuccess = { },
|
||||
onCancelKnockSuccess = { },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package io.element.android.features.joinroom.impl.di
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import javax.inject.Inject
|
||||
|
||||
interface ForgetRoom {
|
||||
suspend operator fun invoke(roomId: RoomId): Result<Unit>
|
||||
}
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultForgetRoom @Inject constructor(private val client: MatrixClient) : ForgetRoom {
|
||||
override suspend fun invoke(roomId: RoomId): Result<Unit> {
|
||||
return client
|
||||
.getPendingRoom(roomId)
|
||||
?.forget()
|
||||
?: Result.failure(IllegalStateException("No pending room found"))
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ object JoinRoomModule {
|
|||
joinRoom: JoinRoom,
|
||||
knockRoom: KnockRoom,
|
||||
cancelKnockRoom: CancelKnockRoom,
|
||||
forgetRoom: ForgetRoom,
|
||||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
|
||||
buildMeta: BuildMeta,
|
||||
): JoinRoomPresenter.Factory {
|
||||
|
|
@ -52,6 +53,7 @@ object JoinRoomModule {
|
|||
matrixClient = client,
|
||||
joinRoom = joinRoom,
|
||||
knockRoom = knockRoom,
|
||||
forgetRoom = forgetRoom,
|
||||
cancelKnockRoom = cancelKnockRoom,
|
||||
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
|
||||
buildMeta = buildMeta,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Ano, zrušit"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Opravdu chcete zrušit svou žádost o vstup do této místnosti?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Zrušit žádost o vstup"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ano, odmítnout a zablokovat"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Opravdu chcete odmítnout pozvánku do této místnosti? Tím také zabráníte tomu, aby vás %1$s kontaktoval(a) nebo pozval(a) do místností."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Odmítnout pozvání a zablokovat"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Odmítnout a zablokovat"</string>
|
||||
<string name="screen_join_room_fail_message">"Vstup do místnosti se nezdařil."</string>
|
||||
<string name="screen_join_room_fail_reason">"Tato místnost je buď určena pouze pro zvané, nebo do ní může být omezen přístup na úrovni prostoru."</string>
|
||||
<string name="screen_join_room_forget_action">"Zapomenout na tuto místnost"</string>
|
||||
|
|
@ -17,6 +21,8 @@
|
|||
<string name="screen_join_room_knock_message_description">"Zpráva (nepovinné)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Pokud bude váš požadavek přijat, obdržíte pozvánku na vstup do místnosti."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Žádost o vstup odeslána"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Náhled místnosti se nám nepodařilo zobrazit. To může být způsobeno problémy se sítí nebo serverem."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Náhled této místnosti jsme nemohli zobrazit"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s zatím nepodporuje prostory. Prostory můžete používat na webu."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Prostory zatím nejsou podporovány"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Klikněte na tlačítko níže a správce místnosti bude informován. Po schválení se budete moci připojit ke konverzaci."</string>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Ja, abbrechen"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Möchten Sie Ihre Beitrittsanfrage für diesen Chatroom wirklich stornieren?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Beitrittsanfrage stornieren"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ja, ablehnen und blockieren"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Sind Sie sicher, dass Sie die Einladung zu diesem Raum ablehnen möchten? Dadurch wird auch verhindert, dass %1$s Sie kontaktiert oder in Räume einlädt."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Einladung ablehnen und blockieren"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Ablehnen und blockieren"</string>
|
||||
<string name="screen_join_room_fail_message">"Der Beitritt zum Chatroom schlug fehl."</string>
|
||||
<string name="screen_join_room_fail_reason">"Dieser Chatroom ist entweder nur auf Einladung zugänglich oder es kann zu Zugangsbeschränkungen auf Spaceebene kommen."</string>
|
||||
<string name="screen_join_room_forget_action">"Vergessen Sie diesen Raum"</string>
|
||||
|
|
@ -17,6 +21,8 @@
|
|||
<string name="screen_join_room_knock_message_description">"Nachricht (optional)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Falls Ihre Anfrage, dem Raum beizutreten, akzeptiert wird, werden Sie eine Einladung erhalten."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Beitrittsanfrage geschickt"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Wir konnten die Chatroomvorschau nicht anzeigen. Dies kann an Netzwerk- oder Serverproblemen liegen."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Wir konnten diese Chatroomvorschau nicht anzeigen"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s unterstützt noch keine Spaces. Du kannst auf Spaces im Web zugreifen."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Spaces werden noch nicht unterstützt"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Klopfe an um einen Administrator zu benachrichtigen. Nach der Freigabe kannst du dich an der Unterhaltung beteiligen."</string>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Jah, tühista"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Kas sa oled kindel, et soovid tühistada oma palve jututoaga liitumiseks?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Tühista liitumispalve"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Jah, keeldu ja blokeeri"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Kas sa oled kindel, et soovid keelduda kutsest sellesse jututuppa? Samaga kaob kasutajal %1$s võimalus sinuga suhelda ja saata sulle jututubade kutseid."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Keeldu kutsest ja blokeeri"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Keeldu ja blokeeri"</string>
|
||||
<string name="screen_join_room_fail_message">"Jututoaga liitumine ei õnnestunud."</string>
|
||||
<string name="screen_join_room_fail_reason">"Ligipääs siia jututuppa on võimalik vaid kutse alusel või kehtivad siin kogukonnakohased piirangud."</string>
|
||||
<string name="screen_join_room_forget_action">"Unusta see jututuba"</string>
|
||||
|
|
@ -17,6 +21,8 @@
|
|||
<string name="screen_join_room_knock_message_description">"Selgitus (kui soovid lisada)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Kui sinu liitumispalvega ollakse nõus, siis saad kutse jututoaga liitumiseks."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Liitumispalve on saadetud"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Me ei saanud jututoa eelvaadet näidata. See võib olla põhjustatud võrguühenduse või serveri vigadest."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Meil ei õnnestunud selle jututoa eelvaadet kuvada"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s veel ei toeta kogukondadega liitumise ja kasutamise võimalust. Vajadusel saad seda teha veebiliidese vahendusel."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Kogukonnad pole veel toetatud"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Klõpsi allolevat nuppu ja jututoa haldaja saab asjakohase teate. Sa saad liituda, kui haldaja sinu soovi heaks kiidab."</string>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Oui, annuler"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Êtes-vous sûr de vouloir annuler votre demande d’accès à ce salon ?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Annuler la demande d’adhésion"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Oui, refuser et bloquer"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Êtes-vous sûr de vouloir refuser l’invitation à rejoindre ce salon ? Cela empêchera également %1$s de vous contacter ou de vous inviter dans les salons."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Refuser l’invitation et bloquer"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Refuser et bloquer"</string>
|
||||
<string name="screen_join_room_fail_message">"Rejoindre le salon a échoué."</string>
|
||||
<string name="screen_join_room_fail_reason">"Ce salon est accessible uniquement sur invitation ou il peut y avoir des restrictions d’accès au niveau du Space."</string>
|
||||
<string name="screen_join_room_forget_action">"Oublier ce salon"</string>
|
||||
|
|
@ -17,6 +21,8 @@
|
|||
<string name="screen_join_room_knock_message_description">"Message (facultatif)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Vous recevrez une invitation à rejoindre le salon si votre demande est acceptée."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Demande de rejoindre le salon envoyée"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Impossible d’afficher l’aperçu du salon. Cela peut être dû à des problèmes de réseau ou de serveur."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Impossible d’afficher l’aperçu de ce salon"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"Les Spaces ne sont pas encore pris en charge par %1$s . Vous pouvez voir les Spaces sur le Web."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Les Spaces ne sont pas encore pris en charge"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Cliquez ci-dessous et un administrateur sera prévenu. Une fois votre demande approuvée, pour pourrez rejoindre la discussion."</string>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Igen, visszavonás"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Biztos, hogy visszavonja a szobához való csatlakozási kérését?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Csatlakozási kérés visszavonása"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Igen, elutasítás és blokkolás"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Biztos, hogy elutasítja a meghívást, hogy csatlakozzon ehhez a szobához? Ez azt is megakadályozza, hogy %1$s kapcsolatba lépjen Önnel, vagy szobákba hívja."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Meghívó elutasítása és blokkolás"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Elutasítás és blokkolás"</string>
|
||||
<string name="screen_join_room_fail_message">"A szobához való csatlakozás sikertelen."</string>
|
||||
<string name="screen_join_room_fail_reason">"Ebbe a szobába csak meghívóval vagy tértagsággal lehet belépni."</string>
|
||||
<string name="screen_join_room_forget_action">"Szoba elfelejtése"</string>
|
||||
|
|
@ -17,6 +21,8 @@
|
|||
<string name="screen_join_room_knock_message_description">"Üzenet (nem kötelező)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Ha a kérését elfogadják, meghívót kap a szobához való csatlakozáshoz."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Csatlakozási kérés elküldve"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Nem tudtuk megjeleníteni a szoba előnézetét. Ennek az oka hálózati vagy kiszolgálóprobléma is lehet."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Nem tudtuk megjeleníteni a szoba előnézetét"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"Az %1$s még nem támogatja a tereket. A tereket a weben érheti el."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"A terek még nem támogatottak"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Kattintson az alábbi gombra, és a szoba adminisztrátora értesítést kap. A jóváhagyást követően csatlakozhat a beszélgetéshez."</string>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_join_room_ban_by_message">"Zostałeś zbanowany z tego pokoju przez %1$s."</string>
|
||||
<string name="screen_join_room_ban_message">"Zostałeś zbanowany z tego pokoju"</string>
|
||||
<string name="screen_join_room_ban_reason">"Powód: %1$s."</string>
|
||||
<string name="screen_join_room_cancel_knock_action">"Anuluj prośbę"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Tak, anuluj"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Czy na pewno chcesz anulować prośbę o dołączenie do tego pokoju?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Anuluj prośbę o dołączenie"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Tak, odrzuć i zablokuj"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Czy na pewno chcesz odrzucić zaproszenie dołączenia do tego pokoju? %1$s nie będzie mógł się również z Tobą skontaktować, ani zaprosić Cię do pokoju."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Odrzuć zaproszenie i zablokuj"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Odrzuć i zablokuj"</string>
|
||||
<string name="screen_join_room_fail_message">"Nie udało się dołączyć do pokoju."</string>
|
||||
<string name="screen_join_room_fail_reason">"Ten pokój wymaga zaproszenia lub jest ograniczony z poziomu przestrzeni."</string>
|
||||
<string name="screen_join_room_forget_action">"Zapomnij o tym pokoju"</string>
|
||||
<string name="screen_join_room_invite_required_message">"Potrzebujesz zaproszenia, aby dołączyć do tego pokoju"</string>
|
||||
<string name="screen_join_room_join_action">"Dołącz do pokoju"</string>
|
||||
<string name="screen_join_room_join_restricted_message">"Aby dołączyć, musisz uzyskać zaproszenie lub być członkiem danej przestrzeni."</string>
|
||||
<string name="screen_join_room_knock_action">"Wyślij prośbę o dołączenie"</string>
|
||||
<string name="screen_join_room_knock_message_description">"Wiadomość (opcjonalne)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Otrzymasz zaproszenie dołączenia do pokoju, jeśli prośba zostanie zaakceptowana."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Wysłano prośbę o dołączenie"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Nie udało się wyświetlić podglądu pokoju. Może to być spowodowane problemami z siecią lub serwerem."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Nie udało nam się wyświetlić podglądu tego pokoju"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s jeszcze nie obsługuje przestrzeni. Uzyskaj dostęp do przestrzeni w wersji web."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Przestrzenie nie są jeszcze obsługiwane"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Kliknij przycisk poniżej, aby powiadomić administratora pokoju. Po zatwierdzeniu będziesz mógł dołączyć do rozmowy."</string>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_join_room_ban_by_message">"Foste banido desta sala por %1$s."</string>
|
||||
<string name="screen_join_room_ban_message">"Foste banido desta sala."</string>
|
||||
<string name="screen_join_room_ban_reason">"Razão: %1$s."</string>
|
||||
<string name="screen_join_room_cancel_knock_action">"Cancelar pedido"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Sim, cancelar"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Tens a certeza de que queres cancelar o teu pedido de entrada nesta sala?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Cancela o pedido de adesão"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Sim, recusar & bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Tens a certeza de que queres recusar o convite para entrar nesta sala? Isto também evitará que %1$s te contacte ou te convide para salas."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Recusar convite & bloquear"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Recusar e bloquear"</string>
|
||||
<string name="screen_join_room_fail_message">"Falha ao entrar na sala."</string>
|
||||
<string name="screen_join_room_fail_reason">"A entrada nesta sala ou está limitada a convites ou a alguma configuração de espaço."</string>
|
||||
<string name="screen_join_room_forget_action">"Esquecer esta sala"</string>
|
||||
<string name="screen_join_room_invite_required_message">"Precisas de um convite para entrares nesta sala"</string>
|
||||
<string name="screen_join_room_join_action">"Entrar na sala"</string>
|
||||
<string name="screen_join_room_join_restricted_message">"Podes ter que ser convidado ou pertenceres a um espaço para poderes entrar."</string>
|
||||
<string name="screen_join_room_knock_action">"Bater à porta"</string>
|
||||
<string name="screen_join_room_knock_message_description">"Mensagem (opcional)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Irá receber um convite para participar na sala se seu pedido for aceite."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Pedido de adesão enviado"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Não conseguimos exibir a pré-visualização da sala. Isso pode ser devido a problemas de rede ou servidor."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Não foi possível exibir a pré-visualização desta sala"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"A %1$s ainda não funciona com espaços. Podes usá-los na aplicação web."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Os espaços ainda não estão implementados"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Carrega no botão abaixo para notificar um administrador da sala. Poderás entrar quando te aprovarem."</string>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
<string name="screen_join_room_knock_message_description">"Сообщение (опционально)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Вы получите приглашение присоединиться к комнате, как только ваш запрос будет принят."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Запрос на присоединение отправлен"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Не удалось отобразить предварительный просмотр комнаты. Это может быть связано с проблемами сети или сервера."</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s еще не поддерживает пространства. Вы можете получить к ним доступ в веб-версии."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Пространства пока не поддерживаются"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Нажмите кнопку ниже и администратор комнаты получит уведомление. После одобрения вы сможете присоединиться к обсуждению."</string>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Áno, zrušiť"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Ste si istí, že chcete zrušiť svoju žiadosť o vstup do tejto miestnosti?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Zrušiť žiadosť o pripojenie"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Áno, odmietnuť a zablokovať"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Ste si istí, že chcete odmietnuť pozvanie na vstup do tejto miestnosti? To tiež zabráni tomu, aby vás %1$s kontaktoval/a alebo vás pozval/a do miestností."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Odmietnuť pozvánku a zablokovať"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Odmietnuť a zablokovať"</string>
|
||||
<string name="screen_join_room_fail_message">"Pripojenie do miestnosti zlyhalo."</string>
|
||||
<string name="screen_join_room_fail_reason">"Táto miestnosť je buď len pre pozvaných, alebo môžu existovať obmedzenia na prístup na úrovni priestoru."</string>
|
||||
<string name="screen_join_room_forget_action">"Zabudnúť túto miestnosť"</string>
|
||||
|
|
@ -17,6 +21,8 @@
|
|||
<string name="screen_join_room_knock_message_description">"Správa (voliteľné)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Ak bude vaša žiadosť prijatá, dostanete pozvánku na vstup do miestnosti."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Žiadosť o pripojenie bola odoslaná"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Nepodarilo sa zobraziť ukážku miestnosti. Môže to byť spôsobené problémami so sieťou alebo serverom."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Ukážku tejto miestnosti sa nepodarilo zobraziť"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s zatiaľ nepodporuje priestory. K priestorom môžete pristupovať na webe."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Priestory zatiaľ nie sú podporované"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Kliknite na tlačidlo nižšie a správca miestnosti bude informovaný. Po schválení sa budete môcť pripojiť ku konverzácii."</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_join_room_cancel_knock_action">"Avbryt begäran"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Ja, avbryt"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Är du säker på att du vill avbryta din begäran om att gå med i det här rummet?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Avbryt begäran om att gå med"</string>
|
||||
<string name="screen_join_room_join_action">"Gå med i rummet"</string>
|
||||
<string name="screen_join_room_knock_action">"Knacka för att gå med"</string>
|
||||
<string name="screen_join_room_knock_message_description">"Meddelande (valfritt)"</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_join_room_ban_by_message">"%1$s tarafından bu odadan yasaklandınız."</string>
|
||||
<string name="screen_join_room_ban_message">"Bu odadan yasaklandın"</string>
|
||||
<string name="screen_join_room_ban_reason">"Neden: %1$s."</string>
|
||||
<string name="screen_join_room_cancel_knock_action">"İsteği iptal et"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Evet, iptal et"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Bu odaya katılma isteğinizi iptal etmek istediğinizden emin misiniz?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Katılma isteğini iptal et"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Evet, reddet ve engelle"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Bu odaya katılma davetini reddetmek istediğinizden emin misiniz? Bu aynı zamanda %1$s sizinle iletişim kurmasını veya sizi odalara davet etmesini de engeller."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Daveti reddet ve engelle"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Reddet ve engelle"</string>
|
||||
<string name="screen_join_room_fail_message">"Odaya katılım başarısız oldu."</string>
|
||||
<string name="screen_join_room_fail_reason">"Bu odaya yalnızca davetle girilebilir veya alan düzeyinde erişim kısıtlamaları olabilir."</string>
|
||||
<string name="screen_join_room_forget_action">"Bu odayı unut"</string>
|
||||
<string name="screen_join_room_invite_required_message">"Bu odaya katılmak için bir davete ihtiyacınız var"</string>
|
||||
<string name="screen_join_room_join_action">"Odaya katıl"</string>
|
||||
<string name="screen_join_room_join_restricted_message">"Katılmak için davet edilmeniz veya bir alana üye olmanız gerekebilir."</string>
|
||||
<string name="screen_join_room_knock_action">"Katılma isteği gönder"</string>
|
||||
<string name="screen_join_room_knock_message_description">"Mesaj (isteğe bağlı)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Talebiniz kabul edilirse odaya katılmanız için bir davet alacaksınız."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Katılma isteği gönderildi"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Oda önizlemesini görüntüleyemedik. Bunun nedeni ağ veya sunucu sorunları olabilir."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Bu oda önizlemesini görüntüleyemedik"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s henüz alanları desteklemiyor. Alanlara web üzerinden erişebilirsiniz."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Alanlar henüz desteklenmiyor"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Aşağıdaki düğmeyi tıkladığınızda bir oda yöneticisi bilgilendirilecektir. Onaylandıktan sonra görüşmeye katılabilirsiniz."</string>
|
||||
<string name="screen_join_room_subtitle_no_preview">"Mesaj geçmişini görüntülemek için bu odaya üye olmanız gerekmektedir."</string>
|
||||
<string name="screen_join_room_title_knock">"Bu odaya katılmak ister misiniz?"</string>
|
||||
<string name="screen_join_room_title_no_preview">"Önizleme mevcut değil"</string>
|
||||
</resources>
|
||||
|
|
@ -1,14 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_join_room_ban_by_message">"%1$s забороняє вам відвідувати цю кімнату."</string>
|
||||
<string name="screen_join_room_ban_message">"Вам заборонили відвідувати цю кімнату"</string>
|
||||
<string name="screen_join_room_ban_reason">"Причина: %1$s."</string>
|
||||
<string name="screen_join_room_cancel_knock_action">"Скасувати запит"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Так, скасувати"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Ви впевнені, що бажаєте скасувати свій запит на приєднання до цієї кімнати?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Скасувати запит на приєднання"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Так, відхилити та заблокувати"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Ви впевнені, що хочете відхилити запрошення приєднатися до цієї кімнати? Це також завадить %1$s зв\'язатися з вами або запрошувати вас в кімнати."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Відхилити запрошення та заблокувати"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Відхилити та заблокувати"</string>
|
||||
<string name="screen_join_room_fail_message">"Не вдалося приєднатися до кімнати."</string>
|
||||
<string name="screen_join_room_fail_reason">"Ця кімната доступна лише за запрошенням або на рівні простору можуть бути обмеження доступу."</string>
|
||||
<string name="screen_join_room_forget_action">"Забути цю кімнату"</string>
|
||||
<string name="screen_join_room_invite_required_message">"Вам потрібне запрошення, щоб приєднатися до цієї кімнати"</string>
|
||||
<string name="screen_join_room_join_action">"Приєднатися до кімнати"</string>
|
||||
<string name="screen_join_room_join_restricted_message">"Можливо, вам знадобиться отримати запрошення або стати учасником простору, щоб приєднатися."</string>
|
||||
<string name="screen_join_room_knock_action">"Постукати, щоб приєднатися"</string>
|
||||
<string name="screen_join_room_knock_message_description">"Повідомлення (необов\'язково)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"Ви отримаєте запрошення приєднатися до кімнати, якщо ваш запит буде прийнятий."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Запит на приєднання надіслано"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"Ми не змогли показати попередній перегляд кімнати. Це може бути пов\'язано з проблемами мережі або сервера."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"Ми не можемо показати попередній перегляд цієї кімнати"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s ще не підтримує простори. Ви можете отримати доступ до них у вебверсії."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Простори поки що не підтримуються"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Натисніть кнопку нижче, і адміністратор кімнати отримає сповіщення. Ви зможете приєднатися до розмови після схвалення."</string>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@
|
|||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Yes, cancel"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_description">"Are you sure that you want to cancel your request to join this room?"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_title">"Cancel request to join"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_confirmation">"Yes, decline & block"</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_message">"Are you sure you want to decline the invite to join this room? This will also prevent %1$s from contacting you or inviting you to rooms."</string>
|
||||
<string name="screen_join_room_decline_and_block_alert_title">"Decline invite & block"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Decline and block"</string>
|
||||
<string name="screen_join_room_fail_message">"Joining the room failed."</string>
|
||||
<string name="screen_join_room_fail_reason">"This room is either invite-only or there might be restrictions to access at space level."</string>
|
||||
<string name="screen_join_room_forget_action">"Forget this room"</string>
|
||||
|
|
@ -17,6 +21,8 @@
|
|||
<string name="screen_join_room_knock_message_description">"Message (optional)"</string>
|
||||
<string name="screen_join_room_knock_sent_description">"You will receive an invite to join the room if your request is accepted."</string>
|
||||
<string name="screen_join_room_knock_sent_title">"Request to join sent"</string>
|
||||
<string name="screen_join_room_loading_alert_message">"We could not display the room preview. This may be due to network or server issues."</string>
|
||||
<string name="screen_join_room_loading_alert_title">"We couldn’t display this room preview"</string>
|
||||
<string name="screen_join_room_space_not_supported_description">"%1$s does not support spaces yet. You can access spaces on web."</string>
|
||||
<string name="screen_join_room_space_not_supported_title">"Spaces are not supported yet"</string>
|
||||
<string name="screen_join_room_subtitle_knock">"Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package io.element.android.features.joinroom.impl
|
||||
|
||||
import io.element.android.features.joinroom.impl.di.ForgetRoom
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
|
||||
class FakeForgetRoom(
|
||||
var lambda: (RoomId) -> Result<Unit> = { Result.success(Unit) }
|
||||
) : ForgetRoom {
|
||||
override suspend fun invoke(roomId: RoomId) = simulateLongTask {
|
||||
lambda(roomId)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents
|
|||
import io.element.android.features.invite.api.response.AcceptDeclineInviteState
|
||||
import io.element.android.features.invite.api.response.anAcceptDeclineInviteState
|
||||
import io.element.android.features.joinroom.impl.di.CancelKnockRoom
|
||||
import io.element.android.features.joinroom.impl.di.ForgetRoom
|
||||
import io.element.android.features.joinroom.impl.di.KnockRoom
|
||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
|
@ -24,16 +25,23 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.exception.ErrorKind
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
|
||||
import io.element.android.libraries.matrix.api.room.RoomType
|
||||
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_SERVER_LIST
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.core.aBuildMeta
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.test.room.aRoomPreview
|
||||
import io.element.android.libraries.matrix.test.room.aRoomPreviewInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
|
||||
import io.element.android.libraries.matrix.ui.model.toInviteSender
|
||||
|
|
@ -43,12 +51,14 @@ import io.element.android.tests.testutils.lambda.assert
|
|||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.util.Optional
|
||||
|
||||
@Suppress("LargeClass")
|
||||
class JoinRoomPresenterTest {
|
||||
@get:Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
|
@ -58,12 +68,10 @@ class JoinRoomPresenterTest {
|
|||
val presenter = createJoinRoomPresenter()
|
||||
presenter.test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias()))
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown)
|
||||
assertThat(state.contentState).isEqualTo(ContentState.Loading)
|
||||
assertThat(state.acceptDeclineInviteState).isEqualTo(anAcceptDeclineInviteState())
|
||||
assertThat(state.cancelKnockAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(state.knockAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(state.applicationName).isEqualTo("AppName")
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +96,7 @@ class JoinRoomPresenterTest {
|
|||
assertThat(contentState.name).isEqualTo(roomSummary.info.name)
|
||||
assertThat(contentState.topic).isEqualTo(roomSummary.info.topic)
|
||||
assertThat(contentState.alias).isEqualTo(roomSummary.info.canonicalAlias)
|
||||
assertThat(contentState.numberOfMembers).isEqualTo(roomSummary.info.activeMembersCount)
|
||||
assertThat(contentState.numberOfMembers).isEqualTo(roomSummary.info.joinedMembersCount)
|
||||
assertThat(contentState.isDm).isEqualTo(roomSummary.info.isDirect)
|
||||
assertThat(contentState.roomAvatarUrl).isEqualTo(roomSummary.info.avatarUrl)
|
||||
}
|
||||
|
|
@ -226,9 +234,79 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is joined with unauthorized error, then the authorisation status is unauthorized`() = runTest {
|
||||
val roomDescription = aRoomDescription()
|
||||
val presenter = createJoinRoomPresenter(
|
||||
roomDescription = Optional.of(roomDescription),
|
||||
joinRoomLambda = { _, _, _ ->
|
||||
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden"))
|
||||
},
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.JoinRoom)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAction).isEqualTo(AsyncAction.Loading)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAction).isEqualTo(AsyncAction.Failure(JoinRoomFailures.UnauthorizedJoin))
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `present - when room is banned, then join authorization is equal to IsBanned`() = runTest {
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.BANNED, joinRule = JoinRule.Public)
|
||||
val matrixClient = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = CurrentUserMembership.BANNED,
|
||||
),
|
||||
roomMembershipDetails = {
|
||||
Result.success(
|
||||
RoomMembershipDetails(
|
||||
currentUserMember = aRoomMember(userId = A_USER_ID, displayName = "Alice"),
|
||||
senderMember = aRoomMember(userId = A_USER_ID_2, displayName = "Bob"),
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
).apply {
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
}
|
||||
}
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = matrixClient
|
||||
)
|
||||
presenter.test {
|
||||
// Skip initial state
|
||||
skipItems(1)
|
||||
|
||||
// Advance until the room info is loaded and the presenter recomposes. The room preview info still needs to be loaded async.
|
||||
skipItems(1)
|
||||
|
||||
// Now we should have the room info
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isInstanceOf(JoinAuthorisationStatus.IsBanned::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is left and public then join authorization is equal to canJoin`() = runTest {
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, isPublic = true)
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, joinRule = JoinRule.Public)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
|
|
@ -246,8 +324,8 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is left and not public then join authorization is equal to unknown`() = runTest {
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, isPublic = false)
|
||||
fun `present - when room is left and join rule null then join authorization is equal to Unknown`() = runTest {
|
||||
val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, joinRule = null)
|
||||
val matrixClient = FakeMatrixClient().apply {
|
||||
getRoomSummaryFlowLambda = { _ ->
|
||||
flowOf(Optional.of(roomSummary))
|
||||
|
|
@ -327,6 +405,20 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room preview join rule is Private then join authorization is equal to NeedInvite`() = runTest {
|
||||
val roomDescription = aRoomDescription(joinRule = RoomDescription.JoinRule.UNKNOWN)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
roomDescription = Optional.of(roomDescription)
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - emit knock room event`() = runTest {
|
||||
val knockMessage = "Knock message"
|
||||
|
|
@ -405,24 +497,60 @@ class JoinRoomPresenterTest {
|
|||
.with(value(A_ROOM_ID))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - emit forget room event`() = runTest {
|
||||
val forgetRoomSuccess = lambdaRecorder { _: RoomId ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val forgetRoomFailure = lambdaRecorder { _: RoomId ->
|
||||
Result.failure<Unit>(RuntimeException("Failed to forget room"))
|
||||
}
|
||||
val fakeForgetRoom = FakeForgetRoom(forgetRoomSuccess)
|
||||
val presenter = createJoinRoomPresenter(forgetRoom = fakeForgetRoom)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
state.eventSink(JoinRoomEvents.ForgetRoom)
|
||||
}
|
||||
|
||||
assertThat(awaitItem().forgetAction).isEqualTo(AsyncAction.Loading)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.forgetAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
fakeForgetRoom.lambda = forgetRoomFailure
|
||||
state.eventSink(JoinRoomEvents.ForgetRoom)
|
||||
}
|
||||
|
||||
assertThat(awaitItem().forgetAction).isEqualTo(AsyncAction.Loading)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.forgetAction).isInstanceOf(AsyncAction.Failure::class.java)
|
||||
}
|
||||
}
|
||||
assert(forgetRoomFailure)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID))
|
||||
assert(forgetRoomSuccess)
|
||||
.isCalledOnce()
|
||||
.with(value(A_ROOM_ID))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
RoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = RoomAlias("#alias:matrix.org"),
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 2,
|
||||
roomType = RoomType.Room,
|
||||
isHistoryWorldReadable = false,
|
||||
isJoined = false,
|
||||
isInvited = false,
|
||||
isPublic = true,
|
||||
canKnock = false,
|
||||
aRoomPreview(
|
||||
info = aRoomPreviewInfo(
|
||||
roomId = A_ROOM_ID,
|
||||
canonicalAlias = RoomAlias("#alias:matrix.org"),
|
||||
name = "Room name",
|
||||
topic = "Room topic",
|
||||
avatarUrl = "avatarUrl",
|
||||
numberOfJoinedMembers = 2,
|
||||
isSpace = false,
|
||||
isHistoryWorldReadable = false,
|
||||
joinRule = JoinRule.Public,
|
||||
currentUserMembership = null,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -450,10 +578,130 @@ class JoinRoomPresenterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Private`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Private))
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.NeedInvite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Custom`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Custom("custom")))
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Invite`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Invite))
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.NeedInvite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as KnockRestricted`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.KnockRestricted(emptyList())))
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.CanKnock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Restricted`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(joinRule = JoinRule.Restricted(emptyList())))
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.Restricted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded as Space`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.success(
|
||||
aRoomPreview(info = aRoomPreviewInfo(isSpace = true))
|
||||
)
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
matrixClient = client
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.IsSpace("AppName"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded with error`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.failure(AN_EXCEPTION)
|
||||
}
|
||||
)
|
||||
|
|
@ -464,35 +712,27 @@ class JoinRoomPresenterTest {
|
|||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.Failure(
|
||||
roomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias(),
|
||||
error = AN_EXCEPTION
|
||||
)
|
||||
ContentState.Failure(error = AN_EXCEPTION)
|
||||
)
|
||||
state.eventSink(JoinRoomEvents.RetryFetchingContent)
|
||||
}
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.Loading(A_ROOM_ID.toRoomIdOrAlias())
|
||||
)
|
||||
assertThat(state.contentState).isEqualTo(ContentState.Loading)
|
||||
}
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.Failure(
|
||||
roomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias(),
|
||||
error = AN_EXCEPTION
|
||||
)
|
||||
ContentState.Failure(error = AN_EXCEPTION)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - when room is not known RoomPreview is loaded with error 403`() = runTest {
|
||||
fun `present - when room is not known RoomPreview is loaded with error Forbidden`() = runTest {
|
||||
val client = FakeMatrixClient(
|
||||
getRoomPreviewInfoResult = { _, _ ->
|
||||
Result.failure(Exception("403"))
|
||||
getRoomPreviewResult = { _, _ ->
|
||||
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden"))
|
||||
}
|
||||
)
|
||||
val presenter = createJoinRoomPresenter(
|
||||
|
|
@ -501,11 +741,7 @@ class JoinRoomPresenterTest {
|
|||
presenter.test {
|
||||
skipItems(1)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state.contentState).isEqualTo(
|
||||
ContentState.UnknownRoom(
|
||||
roomIdOrAlias = A_ROOM_ID.toRoomIdOrAlias(),
|
||||
)
|
||||
)
|
||||
assertThat(state.contentState).isEqualTo(ContentState.UnknownRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -521,6 +757,7 @@ class JoinRoomPresenterTest {
|
|||
},
|
||||
knockRoom: KnockRoom = FakeKnockRoom(),
|
||||
cancelKnockRoom: CancelKnockRoom = FakeCancelKnockRoom(),
|
||||
forgetRoom: ForgetRoom = FakeForgetRoom(),
|
||||
buildMeta: BuildMeta = aBuildMeta(applicationName = "AppName"),
|
||||
acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState> = Presenter { anAcceptDeclineInviteState() }
|
||||
): JoinRoomPresenter {
|
||||
|
|
@ -534,6 +771,7 @@ class JoinRoomPresenterTest {
|
|||
joinRoom = FakeJoinRoom(joinRoomLambda),
|
||||
knockRoom = knockRoom,
|
||||
cancelKnockRoom = cancelKnockRoom,
|
||||
forgetRoom = forgetRoom,
|
||||
buildMeta = buildMeta,
|
||||
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter
|
||||
)
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ class JoinRoomViewTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on Go back when a space is displayed invokes the expected callback`() {
|
||||
fun `clicking on ok when a space is displayed invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
rule.setJoinRoomView(
|
||||
|
|
@ -188,9 +188,38 @@ class JoinRoomViewTest {
|
|||
),
|
||||
onBackClick = it
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_go_back)
|
||||
rule.clickOn(CommonStrings.action_ok)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on ok when user is unauthorized the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>(expectEvents = false)
|
||||
ensureCalledOnce {
|
||||
rule.setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(),
|
||||
joinAction = AsyncAction.Failure(JoinRoomFailures.UnauthorizedJoin),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onBackClick = it
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_ok)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on forget when user is banned invokes the expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<JoinRoomEvents>()
|
||||
rule.setJoinRoomView(
|
||||
aJoinRoomState(
|
||||
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsBanned(null, null)),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
rule.clickOn(R.string.screen_join_room_forget_action)
|
||||
eventsRecorder.assertSingle(JoinRoomEvents.ForgetRoom)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinRoomView(
|
||||
|
|
@ -199,6 +228,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinR
|
|||
onJoinSuccess: () -> Unit = EnsureNeverCalled(),
|
||||
onKnockSuccess: () -> Unit = EnsureNeverCalled(),
|
||||
onCancelKnockSuccess: () -> Unit = EnsureNeverCalled(),
|
||||
onForgetSuccess: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
JoinRoomView(
|
||||
|
|
@ -206,7 +236,8 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setJoinR
|
|||
onBackClick = onBackClick,
|
||||
onJoinSuccess = onJoinSuccess,
|
||||
onKnockSuccess = onKnockSuccess,
|
||||
onCancelKnockSuccess = onCancelKnockSuccess
|
||||
onForgetSuccess = onForgetSuccess,
|
||||
onCancelKnockSuccess = onCancelKnockSuccess,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,14 +128,14 @@ private fun KnockRequestsBannerContent(
|
|||
Text(
|
||||
text = state.formattedTitle(),
|
||||
style = ElementTheme.typography.fontBodyMdMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
textAlign = TextAlign.Start,
|
||||
)
|
||||
if (state.subtitle != null) {
|
||||
Text(
|
||||
text = state.subtitle,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
textAlign = TextAlign.Start,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.width
|
|||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
|
|
@ -331,7 +330,7 @@ private fun KnockRequestItem(
|
|||
text = knockRequest.getBestName(),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
)
|
||||
val formattedDate = knockRequest.formattedDate
|
||||
|
|
@ -339,7 +338,7 @@ private fun KnockRequestItem(
|
|||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = formattedDate,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
style = ElementTheme.typography.fontBodySmRegular,
|
||||
)
|
||||
}
|
||||
|
|
@ -348,7 +347,7 @@ private fun KnockRequestItem(
|
|||
if (!knockRequest.displayName.isNullOrEmpty()) {
|
||||
Text(
|
||||
text = knockRequest.userId.value,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Прыняць"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Приемане"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Aceptar"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"پذیرش"</string>
|
||||
</resources>
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<string name="screen_knock_requests_list_empty_state_description">"Ha valaki csatlakozni kíván a szobához, itt láthatja a kérését."</string>
|
||||
<string name="screen_knock_requests_list_empty_state_title">"Nincs függőben lévő csatlakozási kérelem"</string>
|
||||
<string name="screen_knock_requests_list_initial_loading_title">"Csatlakozási kérések betöltése…"</string>
|
||||
<string name="screen_knock_requests_list_title">"Csatlakozási kérelmek"</string>
|
||||
<string name="screen_knock_requests_list_title">"Csatlakozási kérelem"</string>
|
||||
<plurals name="screen_room_multiple_knock_requests_title">
|
||||
<item quantity="one">"%1$s és még %2$d felhasználó szeretne csatlakozni ehhez a szobához"</item>
|
||||
<item quantity="other">"%1$s és még %2$d felhasználó szeretne csatlakozni ehhez a szobához"</item>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"მიღება"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Godta"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Accepteren"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Aceitar"</string>
|
||||
</resources>
|
||||
|
|
@ -4,15 +4,26 @@
|
|||
<string name="screen_knock_requests_list_accept_all_alert_description">"Tens a certeza de que queres aceitar todos os pedidos de entrada?"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_alert_title">"Aceitar todos os pedidos"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_button_title">"Aceitar todos"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_failed_alert_description">"Não foi possível aceitar todos os pedidos. Queres tentar novamente?"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_failed_alert_title">"Falha ao aceitar todos os pedidos"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_loading_title">"A aceitar todos os pedidos de entrada"</string>
|
||||
<string name="screen_knock_requests_list_accept_failed_alert_description">"Não foi possível aceitar este pedido. Queres tentar novamente?"</string>
|
||||
<string name="screen_knock_requests_list_accept_failed_alert_title">"Falha ao aceitar pedido"</string>
|
||||
<string name="screen_knock_requests_list_accept_loading_title">"A aceitar pedido de entrada"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_confirm_button_title">"Sim, recusar e proibir"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_description">"Tens a certeza de que queres recusar e banir %1$s? Este utilizador não poderá voltar a pedir para entrar nesta sala."</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_title">"Recusar e banir"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_confirm_button_title">"Sim, recusar"</string>
|
||||
<string name="screen_knock_requests_list_ban_loading_title">"A rejeitar pedido e a banir o utilizador"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_confirm_button_title">"Sim, rejeitar"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_description">"Tens a certeza que queres recusar o pedido de entrada de %1$s?"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_title">"Recusar entrada"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_title">"Rejeitar entrada"</string>
|
||||
<string name="screen_knock_requests_list_decline_and_ban_action_title">"Recusar e banir"</string>
|
||||
<string name="screen_knock_requests_list_decline_failed_alert_description">"Não foi possível rejeitar este pedido. Queres tentar novamente?"</string>
|
||||
<string name="screen_knock_requests_list_decline_failed_alert_title">"Falha ao rejeitar pedido"</string>
|
||||
<string name="screen_knock_requests_list_decline_loading_title">"A rejeitar pedido de entrada"</string>
|
||||
<string name="screen_knock_requests_list_empty_state_description">"Quando alguém pedir para entrar na sala, irás poder rever o pedido aqui."</string>
|
||||
<string name="screen_knock_requests_list_empty_state_title">"Sem pedidos de entrada"</string>
|
||||
<string name="screen_knock_requests_list_initial_loading_title">"A carregar pedidos de entrada…"</string>
|
||||
<string name="screen_knock_requests_list_title">"Pedidos de entrada"</string>
|
||||
<plurals name="screen_room_multiple_knock_requests_title">
|
||||
<item quantity="one">"%1$s +%2$d outro querem entrar nesta sala"</item>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Acceptați"</string>
|
||||
</resources>
|
||||
|
|
@ -24,14 +24,14 @@
|
|||
<string name="screen_knock_requests_list_empty_state_description">"Вы сможете увидеть запрос, когда кто-то попросит присоединиться к комнате."</string>
|
||||
<string name="screen_knock_requests_list_empty_state_title">"Нет ожидающих запросов на присоединение"</string>
|
||||
<string name="screen_knock_requests_list_initial_loading_title">"Загрузка запросов на присоединение…"</string>
|
||||
<string name="screen_knock_requests_list_title">"Запросы на присоединение"</string>
|
||||
<string name="screen_knock_requests_list_title">"Запросы на вступление"</string>
|
||||
<plurals name="screen_room_multiple_knock_requests_title">
|
||||
<item quantity="one">"%1$s +%2$d хочет присоединиться к этой комнате"</item>
|
||||
<item quantity="few">"%1$s +%2$d хотят присоединиться к этой комнате"</item>
|
||||
<item quantity="many">"%1$s +%2$d хотят присоединиться к этой комнате"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Показать все"</string>
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Принять"</string>
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Разрешить"</string>
|
||||
<string name="screen_room_single_knock_request_title">"%1$s хочет присоединиться к этой комнате"</string>
|
||||
<string name="screen_room_single_knock_request_view_button_title">"Просмотр"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<string name="screen_knock_requests_list_empty_state_description">"Keď niekto požiada, aby sa pripojil k miestnosti, jeho žiadosť si môžete pozrieť tu."</string>
|
||||
<string name="screen_knock_requests_list_empty_state_title">"Žiadna čakajúca žiadosť o pripojenie"</string>
|
||||
<string name="screen_knock_requests_list_initial_loading_title">"Načítavajú sa žiadosti o pripojenie…"</string>
|
||||
<string name="screen_knock_requests_list_title">"Žiadosti o pripojenie"</string>
|
||||
<string name="screen_knock_requests_list_title">"Žiadosti o vstup"</string>
|
||||
<plurals name="screen_room_multiple_knock_requests_title">
|
||||
<item quantity="one">"%1$s +%2$d ďalší chcú vstúpiť do tejto miestnosti"</item>
|
||||
<item quantity="few">"%1$s +%2$d ďalší chcú vstúpiť do tejto miestnosti"</item>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_knock_requests_list_accept_all_alert_confirm_button_title">"Ja, acceptera alla"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_alert_description">"Är du säker på att du vill acceptera alla förfrågningar om att gå med?"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_alert_title">"Acceptera alla förfrågningar"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_button_title">"Acceptera alla"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_failed_alert_title">"Misslyckades att acceptera alla förfrågningar"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_loading_title">"Accepterar alla förfrågningar om att gå med"</string>
|
||||
<string name="screen_knock_requests_list_accept_loading_title">"Accepterar begäran om att gå med"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_confirm_button_title">"Ja, avslå och förbjud"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_description">"Är du säker på att du vill avvisa och förbjuda%1$s? Den här användaren kommer inte att kunna begära åtkomst för att gå med i det här rummet igen."</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_title">"Avvisa och förbjud åtkomst"</string>
|
||||
<string name="screen_knock_requests_list_ban_loading_title">"Avvisar och bannar åtkomst"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_confirm_button_title">"Ja, avböj"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_description">"Är du säker på att du vill avslå %1$s begäran om att gå med i det här rummet?"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_title">"Avvisa åtkomst"</string>
|
||||
<string name="screen_knock_requests_list_decline_and_ban_action_title">"Avvisa och förbjud"</string>
|
||||
<string name="screen_knock_requests_list_decline_loading_title">"Avvisa begäran om att gå med"</string>
|
||||
<string name="screen_knock_requests_list_empty_state_description">"När någon begär om att gå med i rummet, kan du se deras förfrågan här."</string>
|
||||
<string name="screen_knock_requests_list_empty_state_title">"Ingen väntande begäran om att gå med"</string>
|
||||
<string name="screen_knock_requests_list_initial_loading_title">"Laddar förfrågningar om att gå med …"</string>
|
||||
<string name="screen_knock_requests_list_title">"Begäran om att gå med"</string>
|
||||
<plurals name="screen_room_multiple_knock_requests_title">
|
||||
<item quantity="one">"%1$s+ %2$d annan vill gå med i detta rum"</item>
|
||||
<item quantity="other">"%1$s+ %2$d andra vill gå med i detta rum"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Visa alla"</string>
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Godkänn"</string>
|
||||
<string name="screen_room_single_knock_request_title">"%1$s vill gå med i det här rummet"</string>
|
||||
<string name="screen_room_single_knock_request_view_button_title">"Visa"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_knock_requests_list_accept_all_alert_confirm_button_title">"Evet, tümünü kabul et"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_alert_description">"Tüm katılma isteklerini kabul etmek istediğinizden emin misiniz?"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_alert_title">"Tüm istekleri kabul et"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_button_title">"Tümünü kabul et"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_failed_alert_description">"Tüm istekleri kabul edemedik. Tekrar denemek ister misiniz?"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_failed_alert_title">"Tüm istekler kabul edilemedi"</string>
|
||||
<string name="screen_knock_requests_list_accept_all_loading_title">"Tüm katılım istekleri kabul ediliyor"</string>
|
||||
<string name="screen_knock_requests_list_accept_failed_alert_description">"Bu isteği kabul edemedik. Tekrar denemek ister misiniz?"</string>
|
||||
<string name="screen_knock_requests_list_accept_failed_alert_title">"İstek kabul edilemedi"</string>
|
||||
<string name="screen_knock_requests_list_accept_loading_title">"Katılma isteği kabul ediliyor"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_confirm_button_title">"Evet, reddet ve yasakla"</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_description">"%1$s reddetmek ve yasaklamak istediğinizden emin misiniz? Bu kullanıcı, bu odaya tekrar katılmak için erişim isteğinde bulunamaz."</string>
|
||||
<string name="screen_knock_requests_list_ban_alert_title">"Reddet ve erişimi yasakla"</string>
|
||||
<string name="screen_knock_requests_list_ban_loading_title">"Reddediliyor ve erişim yasaklanıyor"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_confirm_button_title">"Evet, reddet"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_description">"Bu odaya katılma isteğini reddetmek istediğinizden emin misiniz %1$s?"</string>
|
||||
<string name="screen_knock_requests_list_decline_alert_title">"Erişimi reddet"</string>
|
||||
<string name="screen_knock_requests_list_decline_and_ban_action_title">"Reddet ve yasakla"</string>
|
||||
<string name="screen_knock_requests_list_decline_failed_alert_description">"Bu isteği reddedemedik. Tekrar denemek ister misiniz?"</string>
|
||||
<string name="screen_knock_requests_list_decline_failed_alert_title">"İsteği reddetme başarısız oldu"</string>
|
||||
<string name="screen_knock_requests_list_decline_loading_title">"Katılma isteği reddediliyor"</string>
|
||||
<string name="screen_knock_requests_list_empty_state_description">"Birisi odaya katılmak istediğinde, isteklerini burada görebileceksiniz."</string>
|
||||
<string name="screen_knock_requests_list_empty_state_title">"Bekleyen katılım isteği yok"</string>
|
||||
<string name="screen_knock_requests_list_initial_loading_title">"Katılma istekleri yükleniyor…"</string>
|
||||
<string name="screen_knock_requests_list_title">"Katılma istekleri"</string>
|
||||
<plurals name="screen_room_multiple_knock_requests_title">
|
||||
<item quantity="one">"%1$s +%2$d kişi daha bu odaya katılmak istiyor"</item>
|
||||
<item quantity="other">"%1$s +%2$d kişi daha bu odaya katılmak istiyor"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Tümünü görüntüle"</string>
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Kabul et"</string>
|
||||
<string name="screen_room_single_knock_request_title">"%1$s bu odaya katılmak istiyor"</string>
|
||||
<string name="screen_room_single_knock_request_view_button_title">"Görüntüle"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_single_knock_request_accept_button_title">"Qabul qiling"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Bu sohbetten ayrılmak istediğinizden emin misiniz? Bu sohbet herkese açık değil ve davet olmadan tekrar katılamayacaksınız."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Bu odadan ayrılmak istediğinizden emin misiniz? Burada tek kişi sizsiniz. Ayrılırsanız, siz de dahil olmak üzere gelecekte kimse katılamayacak."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Bu odadan ayrılmak istediğinizden emin misiniz? Bu oda herkese açık değildir ve davet olmadan tekrar katılamazsınız."</string>
|
||||
<string name="leave_room_alert_subtitle">"Odadan çıkmak istediğinden emin misin?"</string>
|
||||
</resources>
|
||||
|
|
@ -21,6 +21,7 @@ setupAnvil()
|
|||
dependencies {
|
||||
api(projects.features.lockscreen.api)
|
||||
implementation(projects.appconfig)
|
||||
implementation(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class DefaultLockScreenService @Inject constructor(
|
|||
*/
|
||||
private fun observeAppForegroundState() {
|
||||
coroutineScope.launch {
|
||||
appForegroundStateService.start()
|
||||
appForegroundStateService.startObservingForeground()
|
||||
appForegroundStateService.isInForeground.collect { isInForeground ->
|
||||
if (isInForeground) {
|
||||
lockJob?.cancel()
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@
|
|||
|
||||
package io.element.android.features.lockscreen.impl.unlock
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
|
|
@ -42,7 +41,7 @@ class PinUnlockNode @AssistedInject constructor(
|
|||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
val activity = LocalContext.current as Activity
|
||||
val activity = requireNotNull(LocalActivity.current)
|
||||
val isDark = ElementTheme.isLightTheme.not()
|
||||
LaunchedEffect(state.isUnlocked) {
|
||||
if (state.isUnlocked) {
|
||||
|
|
|
|||
|
|
@ -23,17 +23,19 @@ open class PinUnlockStateProvider : PreviewParameterProvider<PinUnlockState> {
|
|||
aPinUnlockState(showWrongPinTitle = true),
|
||||
aPinUnlockState(showSignOutPrompt = true),
|
||||
aPinUnlockState(showBiometricUnlock = false),
|
||||
aPinUnlockState(showSignOutPrompt = true, remainingAttempts = 0),
|
||||
aPinUnlockState(showSignOutPrompt = true, remainingAttempts = AsyncData.Success(0)),
|
||||
aPinUnlockState(signOutAction = AsyncAction.Loading),
|
||||
aPinUnlockState(biometricUnlockResult = BiometricAuthenticator.AuthenticationResult.Failure(
|
||||
BiometricUnlockError(BiometricPrompt.ERROR_LOCKOUT, "Biometric auth disabled")
|
||||
)),
|
||||
aPinUnlockState(
|
||||
biometricUnlockResult = BiometricAuthenticator.AuthenticationResult.Failure(
|
||||
BiometricUnlockError(BiometricPrompt.ERROR_LOCKOUT, "Biometric auth disabled")
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aPinUnlockState(
|
||||
pinEntry: PinEntry = PinEntry.createEmpty(4),
|
||||
remainingAttempts: Int = 3,
|
||||
remainingAttempts: AsyncData<Int> = AsyncData.Success(3),
|
||||
showWrongPinTitle: Boolean = false,
|
||||
showSignOutPrompt: Boolean = false,
|
||||
showBiometricUnlock: Boolean = true,
|
||||
|
|
@ -43,7 +45,7 @@ fun aPinUnlockState(
|
|||
) = PinUnlockState(
|
||||
pinEntry = AsyncData.Success(pinEntry),
|
||||
showWrongPinTitle = showWrongPinTitle,
|
||||
remainingAttempts = AsyncData.Success(remainingAttempts),
|
||||
remainingAttempts = remainingAttempts,
|
||||
showSignOutPrompt = showSignOutPrompt,
|
||||
showBiometricUnlock = showBiometricUnlock,
|
||||
signOutAction = signOutAction,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.systemBarsPadding
|
|||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Lock
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -316,7 +315,7 @@ private fun PinUnlockHeader(
|
|||
.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
style = ElementTheme.typography.fontHeadingMdBold,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
val remainingAttempts = state.remainingAttempts.dataOrNull()
|
||||
|
|
@ -330,9 +329,9 @@ private fun PinUnlockHeader(
|
|||
""
|
||||
}
|
||||
val subtitleColor = if (state.showWrongPinTitle) {
|
||||
MaterialTheme.colorScheme.error
|
||||
ElementTheme.colors.textCriticalPrimary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.secondary
|
||||
ElementTheme.colors.textSecondary
|
||||
}
|
||||
Text(
|
||||
text = subtitle,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import androidx.activity.enableEdgeToEdge
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.features.lockscreen.api.LockScreenLockState
|
||||
import io.element.android.features.lockscreen.api.LockScreenService
|
||||
import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter
|
||||
|
|
@ -38,13 +39,17 @@ class PinUnlockActivity : AppCompatActivity() {
|
|||
@Inject lateinit var presenter: PinUnlockPresenter
|
||||
@Inject lateinit var lockScreenService: LockScreenService
|
||||
@Inject lateinit var appPreferencesStore: AppPreferencesStore
|
||||
@Inject lateinit var enterpriseService: EnterpriseService
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
bindings<PinUnlockBindings>().inject(this)
|
||||
setContent {
|
||||
ElementThemeApp(appPreferencesStore) {
|
||||
ElementThemeApp(
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
enterpriseService = enterpriseService,
|
||||
) {
|
||||
val state = presenter.present()
|
||||
val isDark = ElementTheme.isLightTheme.not()
|
||||
PinUnlockView(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<string name="screen_app_lock_biometric_authentication">"biometrisk autentisering"</string>
|
||||
<string name="screen_app_lock_biometric_unlock">"biometrisk upplåsning"</string>
|
||||
<string name="screen_app_lock_biometric_unlock_title_android">"Lås upp med biometri"</string>
|
||||
<string name="screen_app_lock_confirm_biometric_authentication_android">"Bekräfta biometriskt"</string>
|
||||
<string name="screen_app_lock_forgot_pin">"Glömt PIN-kod?"</string>
|
||||
<string name="screen_app_lock_settings_change_pin">"Byt PIN-kod"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Tillåt biometrisk upplåsning"</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_app_lock_biometric_authentication">"biyometrik kimlik doğrulama"</string>
|
||||
<string name="screen_app_lock_biometric_unlock">"biyometrik kilit açma"</string>
|
||||
<string name="screen_app_lock_biometric_unlock_title_android">"Biyometrik ile kilit aç"</string>
|
||||
<string name="screen_app_lock_confirm_biometric_authentication_android">"Biyometrik doğrulama"</string>
|
||||
<string name="screen_app_lock_forgot_pin">"PIN\'i unuttum"</string>
|
||||
<string name="screen_app_lock_settings_change_pin">"PIN kodunu değiştir"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Biyometrik kilit açmaya izin ver"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin">"PIN kodunu kaldır"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"PIN\'i kaldırmak istediğinizden emin misiniz?"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"PIN\'i kaldır?"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"İzin ver %1$s"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_skip">"PIN kullanmayı tercih ederim"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"%1$s kullanarak oturum açarken kendinize zaman kazandırın"</string>
|
||||
<string name="screen_app_lock_setup_choose_pin">"PIN Seç"</string>
|
||||
<string name="screen_app_lock_setup_confirm_pin">"PIN\'i onayla"</string>
|
||||
<string name="screen_app_lock_setup_pin_context">"Sohbetlerinize ekstra güvenlik eklemek için %1$s kilitleyin.
|
||||
|
||||
Hatırlanabilir bir şey seçin. Bu PIN\'i unutursanız, uygulamadan çıkış yaparsınız."</string>
|
||||
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Güvenlik nedeniyle bunu PIN kodunuz olarak seçemezsiniz"</string>
|
||||
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Farklı bir PIN seçin"</string>
|
||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Lütfen aynı PIN\'i iki kez girin"</string>
|
||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PIN\'ler eşleşmiyor"</string>
|
||||
<string name="screen_app_lock_signout_alert_message">"Devam etmek için yeniden oturum açmanız ve yeni bir PIN oluşturmanız gerekir"</string>
|
||||
<string name="screen_app_lock_signout_alert_title">"Oturumunuz kapatılıyor"</string>
|
||||
<plurals name="screen_app_lock_subtitle">
|
||||
<item quantity="one">"Kilidi açmak için %1$d deneme hakkınız var"</item>
|
||||
<item quantity="other">"Kilidi açmak için %1$d deneme hakkınız var"</item>
|
||||
</plurals>
|
||||
<plurals name="screen_app_lock_subtitle_wrong_pin">
|
||||
<item quantity="one">"Yanlış PIN. %1$d kere daha şansınız var"</item>
|
||||
<item quantity="other">"Yanlış PIN. %1$d kere daha şansınız var"</item>
|
||||
</plurals>
|
||||
<string name="screen_app_lock_use_biometric_android">"Biyometrik kullan"</string>
|
||||
<string name="screen_app_lock_use_pin_android">"PIN kullan"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Oturum kapatılıyor…"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package io.element.android.features.lockscreen.impl.unlock
|
||||
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.lockscreen.impl.biometric.BiometricAuthenticator
|
||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockError
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import org.junit.Test
|
||||
|
||||
class PinUnlockStateTest {
|
||||
@Test
|
||||
fun `isSignOutPromptCancellable should have expected values`() {
|
||||
assertThat(aPinUnlockState(remainingAttempts = AsyncData.Uninitialized).isSignOutPromptCancellable).isTrue()
|
||||
assertThat(aPinUnlockState(remainingAttempts = AsyncData.Success(1)).isSignOutPromptCancellable).isTrue()
|
||||
assertThat(aPinUnlockState(remainingAttempts = AsyncData.Success(0)).isSignOutPromptCancellable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `biometricUnlockErrorMessage and showBiometricUnlockError should have expected values`() {
|
||||
listOf(
|
||||
null,
|
||||
BiometricAuthenticator.AuthenticationResult.Failure(),
|
||||
BiometricAuthenticator.AuthenticationResult.Success,
|
||||
).forEach { biometricUnlockResult ->
|
||||
aPinUnlockState(
|
||||
biometricUnlockResult = biometricUnlockResult,
|
||||
).let {
|
||||
assertThat(it.biometricUnlockErrorMessage).isNull()
|
||||
assertThat(it.showBiometricUnlockError).isFalse()
|
||||
}
|
||||
}
|
||||
listOf(
|
||||
BiometricPrompt.ERROR_HW_UNAVAILABLE,
|
||||
BiometricPrompt.ERROR_UNABLE_TO_PROCESS,
|
||||
BiometricPrompt.ERROR_TIMEOUT,
|
||||
BiometricPrompt.ERROR_NO_SPACE,
|
||||
BiometricPrompt.ERROR_CANCELED,
|
||||
BiometricPrompt.ERROR_VENDOR,
|
||||
BiometricPrompt.ERROR_USER_CANCELED,
|
||||
BiometricPrompt.ERROR_NO_BIOMETRICS,
|
||||
BiometricPrompt.ERROR_HW_NOT_PRESENT,
|
||||
BiometricPrompt.ERROR_NEGATIVE_BUTTON,
|
||||
BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL,
|
||||
BiometricPrompt.ERROR_SECURITY_UPDATE_REQUIRED,
|
||||
).forEach { code ->
|
||||
aPinUnlockState(
|
||||
biometricUnlockResult = BiometricAuthenticator.AuthenticationResult.Failure(
|
||||
error = BiometricUnlockError(code, "Error message")
|
||||
),
|
||||
).let {
|
||||
assertThat(it.biometricUnlockErrorMessage).isNull()
|
||||
assertThat(it.showBiometricUnlockError).isFalse()
|
||||
}
|
||||
}
|
||||
listOf(
|
||||
BiometricPrompt.ERROR_LOCKOUT,
|
||||
BiometricPrompt.ERROR_LOCKOUT_PERMANENT,
|
||||
).forEach { code ->
|
||||
aPinUnlockState(
|
||||
biometricUnlockResult = BiometricAuthenticator.AuthenticationResult.Failure(
|
||||
error = BiometricUnlockError(code, "Error message")
|
||||
),
|
||||
).let {
|
||||
assertThat(it.biometricUnlockErrorMessage).isEqualTo("Error message")
|
||||
assertThat(it.showBiometricUnlockError).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,10 @@ package io.element.android.features.login.impl
|
|||
|
||||
import android.app.Activity
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
|
|
@ -199,7 +199,7 @@ class LoginFlowNode @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
activity = LocalContext.current as? Activity
|
||||
activity = requireNotNull(LocalActivity.current)
|
||||
darkTheme = !ElementTheme.isLightTheme
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -68,7 +67,7 @@ fun AccountProviderView(
|
|||
RoundedIconAtom(
|
||||
size = RoundedIconAtomSize.Medium,
|
||||
imageVector = CompoundIcons.Search(),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
tint = ElementTheme.colors.iconPrimary,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
|
|
@ -77,7 +76,7 @@ fun AccountProviderView(
|
|||
.weight(1f),
|
||||
text = item.title,
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
if (item.isPublic) {
|
||||
Icon(
|
||||
|
|
@ -96,7 +95,7 @@ fun AccountProviderView(
|
|||
.padding(start = 46.dp, bottom = 12.dp, end = 26.dp),
|
||||
text = item.subtitle,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
package io.element.android.features.login.impl.screens.createaccount
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
|
|
@ -41,7 +41,7 @@ class CreateAccountNode @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val activity = LocalContext.current as Activity
|
||||
val activity = requireNotNull(LocalActivity.current)
|
||||
val isDark = ElementTheme.isLightTheme.not()
|
||||
val state = presenter.present()
|
||||
CreateAccountView(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.progressSemantics
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -138,7 +137,7 @@ private fun ColumnScope.Buttons(
|
|||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Error(),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
|
@ -151,7 +150,7 @@ private fun ColumnScope.Buttons(
|
|||
else -> stringResource(R.string.screen_qr_code_login_invalid_scan_state_subtitle)
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
color = ElementTheme.colors.textCriticalPrimary,
|
||||
style = ElementTheme.typography.fontBodySmMedium,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<string name="screen_change_account_provider_subtitle">"Използвайте друг доставчик на акаунт, като например собствен частен сървър или работен акаунт."</string>
|
||||
<string name="screen_change_account_provider_title">"Промяна на доставчика на акаунт"</string>
|
||||
<string name="screen_change_server_subtitle">"Какъв е адресът на вашия сървър?"</string>
|
||||
<string name="screen_create_account_title">"Създаване на акаунт"</string>
|
||||
<string name="screen_login_error_deactivated_account">"Този акаунт бе деактивиран."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"Неправилно потребителско име и/или парола"</string>
|
||||
<string name="screen_login_form_header">"Въведете своите данни"</string>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
<string name="screen_change_server_form_notice">"Solo puedes conectarte a un servidor que soporte sliding sync. El administrador de tu servidor tendrá que configurarlo. %1$s"</string>
|
||||
<string name="screen_change_server_subtitle">"¿Cuál es la dirección de tu servidor?"</string>
|
||||
<string name="screen_change_server_title">"Selecciona tu servidor"</string>
|
||||
<string name="screen_create_account_title">"Crear cuenta"</string>
|
||||
<string name="screen_login_error_deactivated_account">"Esta cuenta ha sido desactivada."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"Usuario y/o contraseña incorrectos"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Este no es un id de usuario válido. Formato esperado: \'@user:homeserver.org\'"</string>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<string name="screen_change_server_form_notice">"თქვენ შეგიძლიათ დაუკავშირდეთ მხოლოდ იმ სერვერს, რომელიც მხარს უჭერს \"sliding sync\"-ს. თქვენი სახლის სერვერის ადმინისტრატორს დასჭირდება მისი კონფიგურაცია.%1$s"</string>
|
||||
<string name="screen_change_server_subtitle">"რა არის თქვენი სერვერის მისამართი?"</string>
|
||||
<string name="screen_change_server_title">"აირჩიეთ თქვენი სერვერი"</string>
|
||||
<string name="screen_create_account_title">"ანგარიშის შექმნა"</string>
|
||||
<string name="screen_login_error_deactivated_account">"ეს ანგარიში დეაქტივირებულია."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"არასწორი მომხმარებლის სახელი და/ან პაროლი"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"მოცემული მომხმარებლის იდენტიფიკატორი არასწორია. დასაშვები ფორმატი: ‘@user:homeserver.org’"</string>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue