Merge branch 'develop' into renovate/app.cash.paparazzi-1.x

This commit is contained in:
ganfra 2024-06-04 16:51:59 +02:00 committed by GitHub
commit a72536057e
1163 changed files with 12570 additions and 7532 deletions

View file

@ -9,8 +9,8 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC -Dkotlin.daemon.jvm.options="-Xmx3g"
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 -Dsonar.gradle.skipCompile=true
jobs: jobs:
debug: debug:

View file

@ -11,7 +11,7 @@ jobs:
- run: | - run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger - name: Danger
uses: danger/danger-js@12.2.0 uses: danger/danger-js@12.3.0
with: with:
args: "--dangerfile ./tools/danger/dangerfile.js" args: "--dangerfile ./tools/danger/dangerfile.js"
env: env:

View file

@ -8,8 +8,8 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC -Dkotlin.daemon.jvm.options="-Xmx3g"
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon -Dsonar.gradle.skipCompile=true
jobs: jobs:
build-apk: build-apk:
@ -38,6 +38,10 @@ jobs:
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17' java-version: '17'
- name: Configure gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble debug APK - name: Assemble debug APK
run: ./gradlew :app:assembleGplayDebug $CI_GRADLE_ARG_PROPERTIES run: ./gradlew :app:assembleGplayDebug $CI_GRADLE_ARG_PROPERTIES
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch' if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'

View file

@ -7,8 +7,8 @@ on:
- cron: "0 4 * * *" - cron: "0 4 * * *"
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon -Dsonar.gradle.skipCompile=true
jobs: jobs:
nightly: nightly:

View file

@ -8,8 +8,8 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx3g" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx3g" -Dkotlin.incremental=false -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 -Dsonar.gradle.skipCompile=true
jobs: jobs:
nightlyReports: nightlyReports:
@ -26,6 +26,11 @@ jobs:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17' java-version: '17'
- name: Configure gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: false
- name: ⚙️ Run unit tests, debug and release - name: ⚙️ Run unit tests, debug and release
run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES run: ./gradlew test $CI_GRADLE_ARG_PROPERTIES
@ -48,7 +53,7 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }} ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }}
if: ${{ always() && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }} if: ${{ always() && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }}
run: ./gradlew sonar $CI_GRADLE_ARG_PROPERTIES run: ./gradlew assembleDebug createFullJarDebugTestFixtures :app:createFullJarGplayDebugTestFixtures $CI_GRADLE_ARG_PROPERTIES
# Gradle dependency analysis using https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin # Gradle dependency analysis using https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin
dependency-analysis: dependency-analysis:

View file

@ -9,8 +9,8 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon -Dsonar.gradle.skipCompile=true
jobs: jobs:
checkScript: checkScript:
@ -207,7 +207,7 @@ jobs:
yarn add danger-plugin-lint-report --dev yarn add danger-plugin-lint-report --dev
- name: Danger lint - name: Danger lint
if: always() if: always()
uses: danger/danger-js@12.2.0 uses: danger/danger-js@12.3.0
with: with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js" args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env: env:

View file

@ -7,7 +7,7 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx5g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx5g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC -Dsonar.gradle.skipCompile=true
jobs: jobs:
record: record:

View file

@ -1,4 +1,4 @@
name: Create release App Bundle name: Create release App Bundle and APKs
on: on:
workflow_dispatch: workflow_dispatch:
@ -7,8 +7,8 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon -Dsonar.gradle.skipCompile=true
jobs: jobs:
gplay: gplay:

View file

@ -9,8 +9,8 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -XX:MaxMetaspaceSize=512m -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -XX:MaxMetaspaceSize=512m -Dkotlin.incremental=false -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --no-daemon --warn CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --warn -Dsonar.gradle.skipCompile=true
jobs: jobs:
sonar: sonar:
@ -35,6 +35,8 @@ jobs:
uses: gradle/actions/setup-gradle@v3 uses: gradle/actions/setup-gradle@v3
with: with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }} cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Build projects
run: ./gradlew assembleDebug createFullJarDebugTestFixtures :app:createFullJarGplayDebugTestFixtures $CI_GRADLE_ARG_PROPERTIES
- name: 🔊 Publish results to Sonar - name: 🔊 Publish results to Sonar
env: env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

View file

@ -9,8 +9,8 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx8g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseParallelGC GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.incremental=false -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --warn CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 8 --warn -Dsonar.gradle.skipCompile=true
jobs: jobs:
tests: tests:
@ -80,7 +80,6 @@ jobs:
# https://github.com/codecov/codecov-action # https://github.com/codecov/codecov-action
- name: ☂️ Upload coverage reports to codecov - name: ☂️ Upload coverage reports to codecov
if: always()
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v4
with: with:
fail_ci_if_error: true fail_ci_if_error: true

View file

@ -3,7 +3,9 @@
<words> <words>
<w>backstack</w> <w>backstack</w>
<w>blurhash</w> <w>blurhash</w>
<w>fdroid</w>
<w>ftue</w> <w>ftue</w>
<w>gplay</w>
<w>homeserver</w> <w>homeserver</w>
<w>konsist</w> <w>konsist</w>
<w>kover</w> <w>kover</w>

View file

@ -1,6 +1,6 @@
appId: ${MAESTRO_APP_ID} appId: ${MAESTRO_APP_ID}
--- ---
- tapOn: "Continue" - tapOn: "Sign in manually"
- runFlow: ../assertions/assertLoginDisplayed.yaml - runFlow: ../assertions/assertLoginDisplayed.yaml
- takeScreenshot: build/maestro/100-SignIn - takeScreenshot: build/maestro/100-SignIn
- runFlow: changeServer.yaml - runFlow: changeServer.yaml

View file

@ -9,5 +9,5 @@ appId: ${MAESTRO_APP_ID}
- tapOn: "Continue" - tapOn: "Continue"
- extendedWaitUntil: - extendedWaitUntil:
visible: "Device verified" visible: "Device verified"
timeout: 10000 timeout: 30000
- tapOn: "Continue" - tapOn: "Continue"

View file

@ -36,6 +36,6 @@ class ElementXApplication : Application(), DaggerComponentOwner {
initializeComponent(TracingInitializer::class.java) initializeComponent(TracingInitializer::class.java)
initializeComponent(CacheCleanerInitializer::class.java) initializeComponent(CacheCleanerInitializer::class.java)
} }
logApplicationInfo() logApplicationInfo(this)
} }
} }

View file

@ -26,6 +26,7 @@ import dagger.Provides
import io.element.android.appconfig.ApplicationConfig import io.element.android.appconfig.ApplicationConfig
import io.element.android.features.messages.impl.timeline.components.customreaction.DefaultEmojibaseProvider import io.element.android.features.messages.impl.timeline.components.customreaction.DefaultEmojibaseProvider
import io.element.android.features.messages.impl.timeline.components.customreaction.EmojibaseProvider import io.element.android.features.messages.impl.timeline.components.customreaction.EmojibaseProvider
import io.element.android.libraries.androidutils.system.getVersionCodeFromManifest
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.meta.BuildType
@ -33,7 +34,6 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.DefaultPreferences
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.x.BuildConfig import io.element.android.x.BuildConfig
import io.element.android.x.R import io.element.android.x.R
@ -87,7 +87,7 @@ object AppModule {
// TODO EAx Config.LOW_PRIVACY_LOG_ENABLE, // TODO EAx Config.LOW_PRIVACY_LOG_ENABLE,
lowPrivacyLoggingEnabled = false, lowPrivacyLoggingEnabled = false,
versionName = BuildConfig.VERSION_NAME, versionName = BuildConfig.VERSION_NAME,
versionCode = BuildConfig.VERSION_CODE, versionCode = context.getVersionCodeFromManifest(),
gitRevision = BuildConfig.GIT_REVISION, gitRevision = BuildConfig.GIT_REVISION,
gitBranchName = BuildConfig.GIT_BRANCH_NAME, gitBranchName = BuildConfig.GIT_BRANCH_NAME,
flavorDescription = BuildConfig.FLAVOR_DESCRIPTION, flavorDescription = BuildConfig.FLAVOR_DESCRIPTION,
@ -96,8 +96,7 @@ object AppModule {
@Provides @Provides
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
@DefaultPreferences fun providesSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
fun providesDefaultSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context) return PreferenceManager.getDefaultSharedPreferences(context)
} }

View file

@ -16,17 +16,19 @@
package io.element.android.x.info package io.element.android.x.info
import android.content.Context
import io.element.android.libraries.androidutils.system.getVersionCodeFromManifest
import io.element.android.x.BuildConfig import io.element.android.x.BuildConfig
import timber.log.Timber import timber.log.Timber
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
fun logApplicationInfo() { fun logApplicationInfo(context: Context) {
val appVersion = buildString { val appVersion = buildString {
append(BuildConfig.VERSION_NAME) append(BuildConfig.VERSION_NAME)
append(" (") append(" (")
append(BuildConfig.VERSION_CODE) append(context.getVersionCodeFromManifest())
append(") - ") append(") - ")
append(BuildConfig.BUILD_TYPE) append(BuildConfig.BUILD_TYPE)
append(" / ") append(" / ")

View file

@ -20,7 +20,7 @@ import android.content.Context
import android.system.Os import android.system.Os
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.startup.Initializer import androidx.startup.Initializer
import io.element.android.features.preferences.impl.developer.tracing.SharedPrefTracingConfigurationStore import io.element.android.features.preferences.impl.developer.tracing.SharedPreferencesTracingConfigurationStore
import io.element.android.features.preferences.impl.developer.tracing.TargetLogLevelMapBuilder import io.element.android.features.preferences.impl.developer.tracing.TargetLogLevelMapBuilder
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
@ -38,7 +38,7 @@ class TracingInitializer : Initializer<Unit> {
Timber.plant(tracingService.createTimberTree()) Timber.plant(tracingService.createTimberTree())
val tracingConfiguration = if (BuildConfig.DEBUG) { val tracingConfiguration = if (BuildConfig.DEBUG) {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
val store = SharedPrefTracingConfigurationStore(prefs) val store = SharedPreferencesTracingConfigurationStore(prefs)
val builder = TargetLogLevelMapBuilder(store) val builder = TargetLogLevelMapBuilder(store)
TracingConfiguration( TracingConfiguration(
filterConfiguration = TracingFilterConfigurations.custom(builder.getCurrentMap()), filterConfiguration = TracingFilterConfigurations.custom(builder.getCurrentMap()),

View file

@ -31,7 +31,7 @@ import io.element.android.x.MainActivity
import javax.inject.Inject import javax.inject.Inject
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class IntentProviderImpl @Inject constructor( class DefaultIntentProvider @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val deepLinkCreator: DeepLinkCreator, private val deepLinkCreator: DeepLinkCreator,
) : IntentProvider { ) : IntentProvider {

View file

@ -30,10 +30,10 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment import org.robolectric.RuntimeEnvironment
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
class IntentProviderImplTest { class DefaultIntentProviderTest {
@Test @Test
fun `test getViewRoomIntent with Session`() { fun `test getViewRoomIntent with Session`() {
val sut = createIntentProviderImpl() val sut = createDefaultIntentProvider()
val result = sut.getViewRoomIntent( val result = sut.getViewRoomIntent(
sessionId = A_SESSION_ID, sessionId = A_SESSION_ID,
roomId = null, roomId = null,
@ -45,7 +45,7 @@ class IntentProviderImplTest {
@Test @Test
fun `test getViewRoomIntent with Session and Room`() { fun `test getViewRoomIntent with Session and Room`() {
val sut = createIntentProviderImpl() val sut = createDefaultIntentProvider()
val result = sut.getViewRoomIntent( val result = sut.getViewRoomIntent(
sessionId = A_SESSION_ID, sessionId = A_SESSION_ID,
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
@ -57,7 +57,7 @@ class IntentProviderImplTest {
@Test @Test
fun `test getViewRoomIntent with Session, Room and Thread`() { fun `test getViewRoomIntent with Session, Room and Thread`() {
val sut = createIntentProviderImpl() val sut = createDefaultIntentProvider()
val result = sut.getViewRoomIntent( val result = sut.getViewRoomIntent(
sessionId = A_SESSION_ID, sessionId = A_SESSION_ID,
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
@ -67,8 +67,8 @@ class IntentProviderImplTest {
assertThat(result.data.toString()).isEqualTo("elementx://open/@alice:server.org/!aRoomId:domain/\$aThreadId") assertThat(result.data.toString()).isEqualTo("elementx://open/@alice:server.org/!aRoomId:domain/\$aThreadId")
} }
private fun createIntentProviderImpl(): IntentProviderImpl { private fun createDefaultIntentProvider(): DefaultIntentProvider {
return IntentProviderImpl( return DefaultIntentProvider(
context = RuntimeEnvironment.getApplication() as Context, context = RuntimeEnvironment.getApplication() as Context,
deepLinkCreator = DeepLinkCreator(), deepLinkCreator = DeepLinkCreator(),
) )

View file

@ -20,6 +20,9 @@ object NotificationConfig {
// TODO EAx Implement and set to true at some point // TODO EAx Implement and set to true at some point
const val SUPPORT_MARK_AS_READ_ACTION = false const val SUPPORT_MARK_AS_READ_ACTION = false
// TODO EAx Implement and set to true at some point
const val SUPPORT_JOIN_DECLINE_INVITE = false
// TODO EAx Implement and set to true at some point // TODO EAx Implement and set to true at some point
const val SUPPORT_QUICK_REPLY_ACTION = false const val SUPPORT_QUICK_REPLY_ACTION = false
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2023 New Vector Ltd * Copyright (c) 2024 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,9 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.features.onboarding.impl package io.element.android.appconfig
object OnBoardingConfig { object OnBoardingConfig {
/** Whether the user can use QR code login. */
const val CAN_LOGIN_WITH_QR_CODE = false const val CAN_LOGIN_WITH_QR_CODE = false
/** Whether the user can create an account using the app. */
const val CAN_CREATE_ACCOUNT = false const val CAN_CREATE_ACCOUNT = false
} }

View file

@ -229,31 +229,31 @@ class LoggedInFlowNode @AssistedInject constructor(
} }
NavTarget.RoomList -> { NavTarget.RoomList -> {
val callback = object : RoomListEntryPoint.Callback { val callback = object : RoomListEntryPoint.Callback {
override fun onRoomClicked(roomId: RoomId) { override fun onRoomClick(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
} }
override fun onSettingsClicked() { override fun onSettingsClick() {
backstack.push(NavTarget.Settings()) backstack.push(NavTarget.Settings())
} }
override fun onCreateRoomClicked() { override fun onCreateRoomClick() {
backstack.push(NavTarget.CreateRoom) backstack.push(NavTarget.CreateRoom)
} }
override fun onSessionConfirmRecoveryKeyClicked() { override fun onSessionConfirmRecoveryKeyClick() {
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey)) backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
} }
override fun onRoomSettingsClicked(roomId: RoomId) { override fun onRoomSettingsClick(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details)) backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details))
} }
override fun onReportBugClicked() { override fun onReportBugClick() {
plugins<Callback>().forEach { it.onOpenBugReport() } plugins<Callback>().forEach { it.onOpenBugReport() }
} }
override fun onRoomDirectorySearchClicked() { override fun onRoomDirectorySearchClick() {
backstack.push(NavTarget.RoomDirectorySearch) backstack.push(NavTarget.RoomDirectorySearch)
} }
} }
@ -272,7 +272,7 @@ class LoggedInFlowNode @AssistedInject constructor(
coroutineScope.launch { attachRoom(roomId.toRoomIdOrAlias()) } coroutineScope.launch { attachRoom(roomId.toRoomIdOrAlias()) }
} }
override fun onPermalinkClicked(data: PermalinkData) { override fun onPermalinkClick(data: PermalinkData) {
when (data) { when (data) {
is PermalinkData.UserLink -> { is PermalinkData.UserLink -> {
// Should not happen (handled by MessagesNode) // Should not happen (handled by MessagesNode)
@ -325,7 +325,7 @@ class LoggedInFlowNode @AssistedInject constructor(
plugins<Callback>().forEach { it.onOpenBugReport() } plugins<Callback>().forEach { it.onOpenBugReport() }
} }
override fun onSecureBackupClicked() { override fun onSecureBackupClick() {
backstack.push(NavTarget.SecureBackup()) backstack.push(NavTarget.SecureBackup())
} }
@ -363,7 +363,7 @@ class LoggedInFlowNode @AssistedInject constructor(
NavTarget.RoomDirectorySearch -> { NavTarget.RoomDirectorySearch -> {
roomDirectoryEntryPoint.nodeBuilder(this, buildContext) roomDirectoryEntryPoint.nodeBuilder(this, buildContext)
.callback(object : RoomDirectoryEntryPoint.Callback { .callback(object : RoomDirectoryEntryPoint.Callback {
override fun onResultClicked(roomDescription: RoomDescription) { override fun onResultClick(roomDescription: RoomDescription) {
backstack.push( backstack.push(
NavTarget.Room( NavTarget.Room(
roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(), roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(),

View file

@ -31,10 +31,13 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.login.api.LoginEntryPoint
import io.element.android.features.login.api.LoginFlowType
import io.element.android.features.onboarding.api.OnBoardingEntryPoint import io.element.android.features.onboarding.api.OnBoardingEntryPoint
import io.element.android.features.preferences.api.ConfigureTracingEntryPoint import io.element.android.features.preferences.api.ConfigureTracingEntryPoint
import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices
import io.element.android.libraries.designsystem.utils.ScreenOrientation
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactory import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactory
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -73,9 +76,7 @@ class NotLoggedInFlowNode @AssistedInject constructor(
data object OnBoarding : NavTarget data object OnBoarding : NavTarget
@Parcelize @Parcelize
data class LoginFlow( data class LoginFlow(val type: LoginFlowType) : NavTarget
val isAccountCreation: Boolean,
) : NavTarget
@Parcelize @Parcelize
data object ConfigureTracing : NavTarget data object ConfigureTracing : NavTarget
@ -86,11 +87,15 @@ class NotLoggedInFlowNode @AssistedInject constructor(
NavTarget.OnBoarding -> { NavTarget.OnBoarding -> {
val callback = object : OnBoardingEntryPoint.Callback { val callback = object : OnBoardingEntryPoint.Callback {
override fun onSignUp() { override fun onSignUp() {
backstack.push(NavTarget.LoginFlow(isAccountCreation = true)) backstack.push(NavTarget.LoginFlow(type = LoginFlowType.SIGN_UP))
} }
override fun onSignIn() { override fun onSignIn() {
backstack.push(NavTarget.LoginFlow(isAccountCreation = false)) backstack.push(NavTarget.LoginFlow(type = LoginFlowType.SIGN_IN_MANUAL))
}
override fun onSignInWithQrCode() {
backstack.push(NavTarget.LoginFlow(type = LoginFlowType.SIGN_IN_QR_CODE))
} }
override fun onOpenDeveloperSettings() { override fun onOpenDeveloperSettings() {
@ -108,7 +113,7 @@ class NotLoggedInFlowNode @AssistedInject constructor(
} }
is NavTarget.LoginFlow -> { is NavTarget.LoginFlow -> {
loginEntryPoint.nodeBuilder(this, buildContext) loginEntryPoint.nodeBuilder(this, buildContext)
.params(LoginEntryPoint.Params(isAccountCreation = navTarget.isAccountCreation)) .params(LoginEntryPoint.Params(flowType = navTarget.type))
.build() .build()
} }
NavTarget.ConfigureTracing -> { NavTarget.ConfigureTracing -> {
@ -119,6 +124,9 @@ class NotLoggedInFlowNode @AssistedInject constructor(
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
// The login flow doesn't support landscape mode on mobile devices yet
ForceOrientationInMobileDevices(orientation = ScreenOrientation.PORTRAIT)
BackstackView() BackstackView()
} }
} }

View file

@ -217,7 +217,7 @@ class RoomFlowNode @AssistedInject constructor(
LoadingRoomNodeView( LoadingRoomNodeView(
state = LoadingRoomState.Loading, state = LoadingRoomState.Loading,
hasNetworkConnection = networkStatus == NetworkStatus.Online, hasNetworkConnection = networkStatus == NetworkStatus.Online,
onBackClicked = { navigateUp() }, onBackClick = { navigateUp() },
modifier = modifier, modifier = modifier,
) )
} }

View file

@ -121,14 +121,14 @@ class JoinedRoomFlowNode @AssistedInject constructor(
} }
} }
private fun loadingNode(buildContext: BuildContext, onBackClicked: () -> Unit) = node(buildContext) { modifier -> private fun loadingNode(buildContext: BuildContext, onBackClick: () -> Unit) = node(buildContext) { modifier ->
val loadingRoomState by loadingRoomStateStateFlow.collectAsState() val loadingRoomState by loadingRoomStateStateFlow.collectAsState()
val networkStatus by networkMonitor.connectivity.collectAsState() val networkStatus by networkMonitor.connectivity.collectAsState()
LoadingRoomNodeView( LoadingRoomNodeView(
state = loadingRoomState, state = loadingRoomState,
hasNetworkConnection = networkStatus == NetworkStatus.Online, hasNetworkConnection = networkStatus == NetworkStatus.Online,
modifier = modifier, modifier = modifier,
onBackClicked = onBackClicked onBackClick = onBackClick
) )
} }

View file

@ -77,7 +77,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
), DaggerComponentOwner { ), DaggerComponentOwner {
interface Callback : Plugin { interface Callback : Plugin {
fun onOpenRoom(roomId: RoomId) fun onOpenRoom(roomId: RoomId)
fun onPermalinkClicked(data: PermalinkData) fun onPermalinkClick(data: PermalinkData)
fun onForwardedToSingleRoom(roomId: RoomId) fun onForwardedToSingleRoom(roomId: RoomId)
fun onOpenGlobalNotificationSettings() fun onOpenGlobalNotificationSettings()
} }
@ -144,16 +144,16 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
return when (navTarget) { return when (navTarget) {
is NavTarget.Messages -> { is NavTarget.Messages -> {
val callback = object : MessagesEntryPoint.Callback { val callback = object : MessagesEntryPoint.Callback {
override fun onRoomDetailsClicked() { override fun onRoomDetailsClick() {
backstack.push(NavTarget.RoomDetails) backstack.push(NavTarget.RoomDetails)
} }
override fun onUserDataClicked(userId: UserId) { override fun onUserDataClick(userId: UserId) {
backstack.push(NavTarget.RoomMemberDetails(userId)) backstack.push(NavTarget.RoomMemberDetails(userId))
} }
override fun onPermalinkClicked(data: PermalinkData) { override fun onPermalinkClick(data: PermalinkData) {
callbacks.forEach { it.onPermalinkClicked(data) } callbacks.forEach { it.onPermalinkClick(data) }
} }
override fun onForwardedToSingleRoom(roomId: RoomId) { override fun onForwardedToSingleRoom(roomId: RoomId) {

View file

@ -46,7 +46,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
fun LoadingRoomNodeView( fun LoadingRoomNodeView(
state: LoadingRoomState, state: LoadingRoomState,
hasNetworkConnection: Boolean, hasNetworkConnection: Boolean,
onBackClicked: () -> Unit, onBackClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Scaffold( Scaffold(
@ -54,7 +54,7 @@ fun LoadingRoomNodeView(
topBar = { topBar = {
Column { Column {
ConnectivityIndicatorView(isOnline = hasNetworkConnection) ConnectivityIndicatorView(isOnline = hasNetworkConnection)
LoadingRoomTopBar(onBackClicked) LoadingRoomTopBar(onBackClick)
} }
}, },
content = { padding -> content = { padding ->
@ -83,11 +83,11 @@ fun LoadingRoomNodeView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun LoadingRoomTopBar( private fun LoadingRoomTopBar(
onBackClicked: () -> Unit, onBackClick: () -> Unit,
) { ) {
TopAppBar( TopAppBar(
navigationIcon = { navigationIcon = {
BackButton(onClick = onBackClicked) BackButton(onClick = onBackClick)
}, },
title = { title = {
IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp) IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp)
@ -101,7 +101,7 @@ private fun LoadingRoomTopBar(
internal fun LoadingRoomNodeViewPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreview { internal fun LoadingRoomNodeViewPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreview {
LoadingRoomNodeView( LoadingRoomNodeView(
state = state, state = state,
onBackClicked = {}, onBackClick = {},
hasNetworkConnection = false hasNetworkConnection = false
) )
} }

View file

@ -16,6 +16,7 @@
package io.element.android.appnav.room.joined package io.element.android.appnav.room.joined
import androidx.compose.runtime.Immutable
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
@ -31,6 +32,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject import javax.inject.Inject
@Immutable
sealed interface LoadingRoomState { sealed interface LoadingRoomState {
data object Loading : LoadingRoomState data object Loading : LoadingRoomState
data object Error : LoadingRoomState data object Error : LoadingRoomState

View file

@ -17,11 +17,15 @@
package io.element.android.appnav.root package io.element.android.appnav.root
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import im.vector.app.features.analytics.plan.SuperProperties
import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter
import io.element.android.features.rageshake.api.detection.RageshakeDetectionPresenter import io.element.android.features.rageshake.api.detection.RageshakeDetectionPresenter
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.SdkMetadata
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.apperror.api.AppErrorStateService import io.element.android.services.apperror.api.AppErrorStateService
import javax.inject.Inject import javax.inject.Inject
@ -29,6 +33,8 @@ class RootPresenter @Inject constructor(
private val crashDetectionPresenter: CrashDetectionPresenter, private val crashDetectionPresenter: CrashDetectionPresenter,
private val rageshakeDetectionPresenter: RageshakeDetectionPresenter, private val rageshakeDetectionPresenter: RageshakeDetectionPresenter,
private val appErrorStateService: AppErrorStateService, private val appErrorStateService: AppErrorStateService,
private val analyticsService: AnalyticsService,
private val sdkMetadata: SdkMetadata,
) : Presenter<RootState> { ) : Presenter<RootState> {
@Composable @Composable
override fun present(): RootState { override fun present(): RootState {
@ -36,6 +42,16 @@ class RootPresenter @Inject constructor(
val crashDetectionState = crashDetectionPresenter.present() val crashDetectionState = crashDetectionPresenter.present()
val appErrorState by appErrorStateService.appErrorStateFlow.collectAsState() val appErrorState by appErrorStateService.appErrorStateFlow.collectAsState()
LaunchedEffect(Unit) {
analyticsService.updateSuperProperties(
SuperProperties(
cryptoSDK = SuperProperties.CryptoSDK.Rust,
appPlatform = SuperProperties.AppPlatform.EXA,
cryptoSDKVersion = sdkMetadata.sdkGitSha,
)
)
}
return RootState( return RootState(
rageshakeDetectionState = rageshakeDetectionState, rageshakeDetectionState = rageshakeDetectionState,
crashDetectionState = crashDetectionState, crashDetectionState = crashDetectionState,

View file

@ -156,7 +156,7 @@ class JoinRoomLoadedFlowNodeTest {
) )
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper() val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
// WHEN // WHEN
fakeMessagesEntryPoint.callback?.onRoomDetailsClicked() fakeMessagesEntryPoint.callback?.onRoomDetailsClick()
// THEN // THEN
roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED) roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED)
val roomDetailsNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails)!! val roomDetailsNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails)!!

View file

@ -28,7 +28,9 @@ import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
import io.element.android.features.rageshake.test.rageshake.FakeRageShake import io.element.android.features.rageshake.test.rageshake.FakeRageShake
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
import io.element.android.libraries.matrix.test.FakeSdkMetadata
import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.services.apperror.api.AppErrorState import io.element.android.services.apperror.api.AppErrorState
import io.element.android.services.apperror.api.AppErrorStateService import io.element.android.services.apperror.api.AppErrorStateService
import io.element.android.services.apperror.impl.DefaultAppErrorStateService import io.element.android.services.apperror.impl.DefaultAppErrorStateService
@ -99,6 +101,8 @@ class RootPresenterTest {
crashDetectionPresenter = crashDetectionPresenter, crashDetectionPresenter = crashDetectionPresenter,
rageshakeDetectionPresenter = rageshakeDetectionPresenter, rageshakeDetectionPresenter = rageshakeDetectionPresenter,
appErrorStateService = appErrorService, appErrorStateService = appErrorService,
analyticsService = FakeAnalyticsService(),
sdkMetadata = FakeSdkMetadata("sha")
) )
} }
} }

View file

@ -20,21 +20,21 @@ import com.bumble.appyx.core.state.MutableSavedStateMapImpl
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.A_SESSION_ID 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.FakeMatrixClient
import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
class MatrixClientsHolderTest { class MatrixClientsHolderTest {
@Test @Test
fun `test getOrNull`() { fun `test getOrNull`() {
val fakeAuthenticationService = FakeAuthenticationService() val fakeAuthenticationService = FakeMatrixAuthenticationService()
val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService) val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull() assertThat(matrixClientsHolder.getOrNull(A_SESSION_ID)).isNull()
} }
@Test @Test
fun `test getOrRestore`() = runTest { fun `test getOrRestore`() = runTest {
val fakeAuthenticationService = FakeAuthenticationService() val fakeAuthenticationService = FakeMatrixAuthenticationService()
val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService) val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
val fakeMatrixClient = FakeMatrixClient() val fakeMatrixClient = FakeMatrixClient()
fakeAuthenticationService.givenMatrixClient(fakeMatrixClient) fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)
@ -47,7 +47,7 @@ class MatrixClientsHolderTest {
@Test @Test
fun `test remove`() = runTest { fun `test remove`() = runTest {
val fakeAuthenticationService = FakeAuthenticationService() val fakeAuthenticationService = FakeMatrixAuthenticationService()
val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService) val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
val fakeMatrixClient = FakeMatrixClient() val fakeMatrixClient = FakeMatrixClient()
fakeAuthenticationService.givenMatrixClient(fakeMatrixClient) fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)
@ -60,7 +60,7 @@ class MatrixClientsHolderTest {
@Test @Test
fun `test remove all`() = runTest { fun `test remove all`() = runTest {
val fakeAuthenticationService = FakeAuthenticationService() val fakeAuthenticationService = FakeMatrixAuthenticationService()
val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService) val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
val fakeMatrixClient = FakeMatrixClient() val fakeMatrixClient = FakeMatrixClient()
fakeAuthenticationService.givenMatrixClient(fakeMatrixClient) fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)
@ -73,7 +73,7 @@ class MatrixClientsHolderTest {
@Test @Test
fun `test save and restore`() = runTest { fun `test save and restore`() = runTest {
val fakeAuthenticationService = FakeAuthenticationService() val fakeAuthenticationService = FakeMatrixAuthenticationService()
val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService) val matrixClientsHolder = MatrixClientsHolder(fakeAuthenticationService)
val fakeMatrixClient = FakeMatrixClient() val fakeMatrixClient = FakeMatrixClient()
fakeAuthenticationService.givenMatrixClient(fakeMatrixClient) fakeAuthenticationService.givenMatrixClient(fakeMatrixClient)

View file

@ -26,7 +26,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
class AnalyticsVerificationStateMappingTests { class AnalyticsVerificationStateMappingTest {
@get:Rule @get:Rule
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()

View file

@ -60,7 +60,7 @@ allprojects {
config.from(files("$rootDir/tools/detekt/detekt.yml")) config.from(files("$rootDir/tools/detekt/detekt.yml"))
} }
dependencies { dependencies {
detektPlugins("io.nlopez.compose.rules:detekt:0.3.12") detektPlugins("io.nlopez.compose.rules:detekt:0.4.3")
} }
// KtLint // KtLint

1
changelog.d/2809.bugfix Normal file
View file

@ -0,0 +1 @@
Render selected/deselected room list filters on top

1
changelog.d/2893.misc Normal file
View file

@ -0,0 +1 @@
BugReporting | Add public device keys to rageshakes

1
changelog.d/2912.misc Normal file
View file

@ -0,0 +1 @@
Move push provider setting to the "Notifications" screen and display it only when several push provider are available.

1
changelog.d/2917.bugfix Normal file
View file

@ -0,0 +1 @@
Fixed sending rich content from android keyboards on the markdown text input

3
changelog.d/2924.misc Normal file
View file

@ -0,0 +1,3 @@
Simplify notifications by removing the custom persistence layer.
Bump minSdk to 24 (Android 7).

1
changelog.d/2930.misc Normal file
View file

@ -0,0 +1 @@
Add a feature flag ShowBlockedUsersDetails, disabled by default to render display name and avatar of blocked users in the blocked users list.

1
changelog.d/2932.misc Normal file
View file

@ -0,0 +1 @@
Be more specific with the widget permissions

1
changelog.d/2953.misc Normal file
View file

@ -0,0 +1 @@
Analytics | Add support for SuperProperties

1
changelog.d/2959.bugfix Normal file
View file

@ -0,0 +1 @@
Fix crash when restoring the selection values in the plain text editor.

View file

@ -63,15 +63,15 @@ fun AnalyticsOptInView(
) { ) {
val eventSink = state.eventSink val eventSink = state.eventSink
fun onTermsAccepted() { fun onAcceptTerms() {
eventSink(AnalyticsOptInEvents.EnableAnalytics(true)) eventSink(AnalyticsOptInEvents.EnableAnalytics(true))
} }
fun onTermsDeclined() { fun onDeclineTerms() {
eventSink(AnalyticsOptInEvents.EnableAnalytics(false)) eventSink(AnalyticsOptInEvents.EnableAnalytics(false))
} }
BackHandler(onBack = ::onTermsDeclined) BackHandler(onBack = ::onDeclineTerms)
HeaderFooterPage( HeaderFooterPage(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
@ -82,8 +82,8 @@ fun AnalyticsOptInView(
content = { AnalyticsOptInContent() }, content = { AnalyticsOptInContent() },
footer = { footer = {
AnalyticsOptInFooter( AnalyticsOptInFooter(
onTermsAccepted = ::onTermsAccepted, onAcceptTerms = ::onAcceptTerms,
onTermsDeclined = ::onTermsDeclined, onDeclineTerms = ::onDeclineTerms,
) )
} }
) )
@ -165,19 +165,19 @@ private fun AnalyticsOptInContent() {
@Composable @Composable
private fun AnalyticsOptInFooter( private fun AnalyticsOptInFooter(
onTermsAccepted: () -> Unit, onAcceptTerms: () -> Unit,
onTermsDeclined: () -> Unit, onDeclineTerms: () -> Unit,
) { ) {
ButtonColumnMolecule { ButtonColumnMolecule {
Button( Button(
text = stringResource(id = CommonStrings.action_ok), text = stringResource(id = CommonStrings.action_ok),
onClick = onTermsAccepted, onClick = onAcceptTerms,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )
TextButton( TextButton(
text = stringResource(id = CommonStrings.action_not_now), text = stringResource(id = CommonStrings.action_not_now),
size = ButtonSize.Medium, size = ButtonSize.Medium,
onClick = onTermsDeclined, onClick = onDeclineTerms,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )
} }

View file

@ -35,7 +35,6 @@ anvil {
} }
dependencies { dependencies {
implementation(projects.appnav)
implementation(projects.appconfig) implementation(projects.appconfig)
implementation(projects.anvilannotations) implementation(projects.anvilannotations)
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)

View file

@ -72,15 +72,10 @@ class CallForegroundService : Service() {
startForeground(1, notification) startForeground(1, notification)
} }
@Suppress("DEPRECATION")
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { stopForeground(STOP_FOREGROUND_REMOVE)
stopForeground(STOP_FOREGROUND_REMOVE)
} else {
stopForeground(true)
}
} }
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? {

View file

@ -81,12 +81,12 @@ internal fun CallScreenView(
.fillMaxSize(), .fillMaxSize(),
url = state.urlState, url = state.urlState,
userAgent = state.userAgent, userAgent = state.userAgent,
onPermissionsRequested = { request -> onPermissionsRequest = { request ->
val androidPermissions = mapWebkitPermissions(request.resources) val androidPermissions = mapWebkitPermissions(request.resources)
val callback: RequestPermissionCallback = { request.grant(it) } val callback: RequestPermissionCallback = { request.grant(it) }
requestPermissions(androidPermissions.toTypedArray(), callback) requestPermissions(androidPermissions.toTypedArray(), callback)
}, },
onWebViewCreated = { webView -> onWebViewCreate = { webView ->
val interceptor = WebViewWidgetMessageInterceptor(webView) val interceptor = WebViewWidgetMessageInterceptor(webView)
state.eventSink(CallScreenEvents.SetupMessageChannels(interceptor)) state.eventSink(CallScreenEvents.SetupMessageChannels(interceptor))
} }
@ -98,8 +98,8 @@ internal fun CallScreenView(
private fun CallWebView( private fun CallWebView(
url: AsyncData<String>, url: AsyncData<String>,
userAgent: String, userAgent: String,
onPermissionsRequested: (PermissionRequest) -> Unit, onPermissionsRequest: (PermissionRequest) -> Unit,
onWebViewCreated: (WebView) -> Unit, onWebViewCreate: (WebView) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
if (LocalInspectionMode.current) { if (LocalInspectionMode.current) {
@ -111,8 +111,8 @@ private fun CallWebView(
modifier = modifier, modifier = modifier,
factory = { context -> factory = { context ->
WebView(context).apply { WebView(context).apply {
onWebViewCreated(this) onWebViewCreate(this)
setup(userAgent, onPermissionsRequested) setup(userAgent, onPermissionsRequest)
} }
}, },
update = { webView -> update = { webView ->

View file

@ -31,12 +31,12 @@ import android.webkit.PermissionRequest
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.core.content.IntentCompat import androidx.core.content.IntentCompat
import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.theme.Theme import io.element.android.compound.theme.Theme
import io.element.android.compound.theme.isDark import io.element.android.compound.theme.isDark
@ -49,7 +49,7 @@ import io.element.android.features.preferences.api.store.AppPreferencesStore
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import javax.inject.Inject import javax.inject.Inject
class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator { class ElementCallActivity : AppCompatActivity(), CallScreenNavigator {
companion object { companion object {
private const val EXTRA_CALL_WIDGET_SETTINGS = "EXTRA_CALL_WIDGET_SETTINGS" private const val EXTRA_CALL_WIDGET_SETTINGS = "EXTRA_CALL_WIDGET_SETTINGS"
@ -121,13 +121,11 @@ class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator {
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
updateUiMode(newConfig) updateUiMode(newConfig)
} }
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
setCallType(intent) setCallType(intent)
} }

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="call_foreground_service_channel_title_android">"Бягучы выклік"</string> <string name="call_foreground_service_channel_title_android">"Бягучы званок"</string>
<string name="call_foreground_service_message_android">"Націсніце, каб вярнуцца да выкліка"</string> <string name="call_foreground_service_message_android">"Націсніце, каб вярнуцца да званку"</string>
<string name="call_foreground_service_title_android">"☎️ Выконваецца выклік"</string> <string name="call_foreground_service_title_android">"☎️ Ідзе званок"</string>
</resources> </resources>

View file

@ -30,7 +30,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID 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.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.widget.FakeWidgetDriver import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver
import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
@ -68,7 +68,7 @@ class CallScreenPresenterTest {
@Test @Test
fun `present - with CallType RoomCall loads URL and runs WidgetDriver`() = runTest { fun `present - with CallType RoomCall loads URL and runs WidgetDriver`() = runTest {
val widgetDriver = FakeWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val widgetProvider = FakeCallWidgetProvider(widgetDriver) val widgetProvider = FakeCallWidgetProvider(widgetDriver)
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
@ -91,7 +91,7 @@ class CallScreenPresenterTest {
@Test @Test
fun `present - set message interceptor, send and receive messages`() = runTest { fun `present - set message interceptor, send and receive messages`() = runTest {
val widgetDriver = FakeWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
@ -119,7 +119,7 @@ class CallScreenPresenterTest {
@Test @Test
fun `present - hang up event closes the screen and stops the widget driver`() = runTest(UnconfinedTestDispatcher()) { fun `present - hang up event closes the screen and stops the widget driver`() = runTest(UnconfinedTestDispatcher()) {
val navigator = FakeCallScreenNavigator() val navigator = FakeCallScreenNavigator()
val widgetDriver = FakeWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
@ -149,7 +149,7 @@ class CallScreenPresenterTest {
@Test @Test
fun `present - a received hang up message closes the screen and stops the widget driver`() = runTest(UnconfinedTestDispatcher()) { fun `present - a received hang up message closes the screen and stops the widget driver`() = runTest(UnconfinedTestDispatcher()) {
val navigator = FakeCallScreenNavigator() val navigator = FakeCallScreenNavigator()
val widgetDriver = FakeWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
@ -178,7 +178,7 @@ class CallScreenPresenterTest {
@Test @Test
fun `present - automatically starts the Matrix client sync when on RoomCall`() = runTest { fun `present - automatically starts the Matrix client sync when on RoomCall`() = runTest {
val navigator = FakeCallScreenNavigator() val navigator = FakeCallScreenNavigator()
val widgetDriver = FakeWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val matrixClient = FakeMatrixClient() val matrixClient = FakeMatrixClient()
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
@ -201,7 +201,7 @@ class CallScreenPresenterTest {
@Test @Test
fun `present - automatically stops the Matrix client sync on dispose`() = runTest { fun `present - automatically stops the Matrix client sync on dispose`() = runTest {
val navigator = FakeCallScreenNavigator() val navigator = FakeCallScreenNavigator()
val widgetDriver = FakeWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val matrixClient = FakeMatrixClient() val matrixClient = FakeMatrixClient()
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
@ -229,7 +229,7 @@ class CallScreenPresenterTest {
private fun TestScope.createCallScreenPresenter( private fun TestScope.createCallScreenPresenter(
callType: CallType, callType: CallType,
navigator: CallScreenNavigator = FakeCallScreenNavigator(), navigator: CallScreenNavigator = FakeCallScreenNavigator(),
widgetDriver: FakeWidgetDriver = FakeWidgetDriver(), widgetDriver: FakeMatrixWidgetDriver = FakeMatrixWidgetDriver(),
widgetProvider: FakeCallWidgetProvider = FakeCallWidgetProvider(widgetDriver), widgetProvider: FakeCallWidgetProvider = FakeCallWidgetProvider(widgetDriver),
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(), dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
matrixClientsProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(), matrixClientsProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),

View file

@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.widget.FakeCallWidgetSettingsProvider import io.element.android.libraries.matrix.test.widget.FakeCallWidgetSettingsProvider
import io.element.android.libraries.matrix.test.widget.FakeWidgetDriver import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@ -76,7 +76,7 @@ class DefaultCallWidgetProviderTest {
fun `getWidget - returns a widget driver when all steps are successful`() = runTest { fun `getWidget - returns a widget driver when all steps are successful`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom().apply {
givenGenerateWidgetWebViewUrlResult(Result.success("url")) givenGenerateWidgetWebViewUrlResult(Result.success("url"))
givenGetWidgetDriverResult(Result.success(FakeWidgetDriver())) givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
} }
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)
@ -89,7 +89,7 @@ class DefaultCallWidgetProviderTest {
fun `getWidget - will use a custom base url if it exists`() = runTest { fun `getWidget - will use a custom base url if it exists`() = runTest {
val room = FakeMatrixRoom().apply { val room = FakeMatrixRoom().apply {
givenGenerateWidgetWebViewUrlResult(Result.success("url")) givenGenerateWidgetWebViewUrlResult(Result.success("url"))
givenGetWidgetDriverResult(Result.success(FakeWidgetDriver())) givenGetWidgetDriverResult(Result.success(FakeMatrixWidgetDriver()))
} }
val client = FakeMatrixClient().apply { val client = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room) givenGetRoomResult(A_ROOM_ID, room)

View file

@ -19,10 +19,10 @@ package io.element.android.features.call.utils
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.matrix.test.widget.FakeWidgetDriver import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver
class FakeCallWidgetProvider( class FakeCallWidgetProvider(
private val widgetDriver: FakeWidgetDriver = FakeWidgetDriver(), private val widgetDriver: FakeMatrixWidgetDriver = FakeMatrixWidgetDriver(),
private val url: String = "https://call.element.io", private val url: String = "https://call.element.io",
) : CallWidgetProvider { ) : CallWidgetProvider {
var getWidgetCalled = false var getWidgetCalled = false

View file

@ -47,8 +47,8 @@ class AddPeopleNode @AssistedInject constructor(
AddPeopleView( AddPeopleView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackPressed = this::navigateUp, onBackClick = this::navigateUp,
onNextPressed = this::onContinue, onNextClick = this::onContinue,
) )
} }
} }

View file

@ -42,8 +42,8 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable @Composable
fun AddPeopleView( fun AddPeopleView(
state: UserListState, state: UserListState,
onBackPressed: () -> Unit, onBackClick: () -> Unit,
onNextPressed: () -> Unit, onNextClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Scaffold( Scaffold(
@ -51,14 +51,14 @@ fun AddPeopleView(
topBar = { topBar = {
AddPeopleViewTopBar( AddPeopleViewTopBar(
hasSelectedUsers = state.selectedUsers.isNotEmpty(), hasSelectedUsers = state.selectedUsers.isNotEmpty(),
onBackPressed = { onBackClick = {
if (state.isSearchActive) { if (state.isSearchActive) {
state.eventSink(UserListEvents.OnSearchActiveChanged(false)) state.eventSink(UserListEvents.OnSearchActiveChanged(false))
} else { } else {
onBackPressed() onBackClick()
} }
}, },
onNextPressed = onNextPressed, onNextClick = onNextClick,
) )
} }
) { padding -> ) { padding ->
@ -69,8 +69,8 @@ fun AddPeopleView(
.consumeWindowInsets(padding), .consumeWindowInsets(padding),
state = state, state = state,
showBackButton = false, showBackButton = false,
onUserSelected = {}, onSelectUser = {},
onUserDeselected = {}, onDeselectUser = {},
) )
} }
} }
@ -79,8 +79,8 @@ fun AddPeopleView(
@Composable @Composable
private fun AddPeopleViewTopBar( private fun AddPeopleViewTopBar(
hasSelectedUsers: Boolean, hasSelectedUsers: Boolean,
onBackPressed: () -> Unit, onBackClick: () -> Unit,
onNextPressed: () -> Unit, onNextClick: () -> Unit,
) { ) {
TopAppBar( TopAppBar(
title = { title = {
@ -89,12 +89,12 @@ private fun AddPeopleViewTopBar(
style = ElementTheme.typography.aliasScreenTitle style = ElementTheme.typography.aliasScreenTitle
) )
}, },
navigationIcon = { BackButton(onClick = onBackPressed) }, navigationIcon = { BackButton(onClick = onBackClick) },
actions = { actions = {
val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip
TextButton( TextButton(
text = stringResource(id = textActionResId), text = stringResource(id = textActionResId),
onClick = onNextPressed, onClick = onNextClick,
) )
} }
) )
@ -105,7 +105,7 @@ private fun AddPeopleViewTopBar(
internal fun AddPeopleViewPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreview { internal fun AddPeopleViewPreview(@PreviewParameter(AddPeopleUserListStateProvider::class) state: UserListState) = ElementPreview {
AddPeopleView( AddPeopleView(
state = state, state = state,
onBackPressed = {}, onBackClick = {},
onNextPressed = {}, onNextClick = {},
) )
} }

View file

@ -41,7 +41,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
@Composable @Composable
fun RoomPrivacyOption( fun RoomPrivacyOption(
roomPrivacyItem: RoomPrivacyItem, roomPrivacyItem: RoomPrivacyItem,
onOptionSelected: (RoomPrivacyItem) -> Unit, onOptionClick: (RoomPrivacyItem) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isSelected: Boolean = false, isSelected: Boolean = false,
) { ) {
@ -50,7 +50,7 @@ fun RoomPrivacyOption(
.fillMaxWidth() .fillMaxWidth()
.selectable( .selectable(
selected = isSelected, selected = isSelected,
onClick = { onOptionSelected(roomPrivacyItem) }, onClick = { onOptionClick(roomPrivacyItem) },
role = Role.RadioButton, role = Role.RadioButton,
) )
.padding(8.dp), .padding(8.dp),
@ -98,12 +98,12 @@ internal fun RoomPrivacyOptionPreview() = ElementPreview {
Column { Column {
RoomPrivacyOption( RoomPrivacyOption(
roomPrivacyItem = aRoomPrivacyItem, roomPrivacyItem = aRoomPrivacyItem,
onOptionSelected = {}, onOptionClick = {},
isSelected = true, isSelected = true,
) )
RoomPrivacyOption( RoomPrivacyOption(
roomPrivacyItem = aRoomPrivacyItem, roomPrivacyItem = aRoomPrivacyItem,
onOptionSelected = {}, onOptionClick = {},
isSelected = false, isSelected = false,
) )
} }

View file

@ -53,11 +53,11 @@ fun SearchUserBar(
showLoader: Boolean, showLoader: Boolean,
selectedUsers: ImmutableList<MatrixUser>, selectedUsers: ImmutableList<MatrixUser>,
active: Boolean, active: Boolean,
isMultiSelectionEnabled: Boolean, isMultiSelectionEnable: Boolean,
onActiveChanged: (Boolean) -> Unit, onActiveChange: (Boolean) -> Unit,
onTextChanged: (String) -> Unit, onTextChange: (String) -> Unit,
onUserSelected: (MatrixUser) -> Unit, onUserSelect: (MatrixUser) -> Unit,
onUserDeselected: (MatrixUser) -> Unit, onUserDeselect: (MatrixUser) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
showBackButton: Boolean = true, showBackButton: Boolean = true,
placeHolderTitle: String = stringResource(CommonStrings.common_search_for_someone), placeHolderTitle: String = stringResource(CommonStrings.common_search_for_someone),
@ -66,14 +66,14 @@ fun SearchUserBar(
SearchBar( SearchBar(
query = query, query = query,
onQueryChange = onTextChanged, onQueryChange = onTextChange,
active = active, active = active,
onActiveChange = onActiveChanged, onActiveChange = onActiveChange,
modifier = modifier, modifier = modifier,
placeHolderTitle = placeHolderTitle, placeHolderTitle = placeHolderTitle,
showBackButton = showBackButton, showBackButton = showBackButton,
contentPrefix = { contentPrefix = {
if (isMultiSelectionEnabled && active && selectedUsers.isNotEmpty()) { if (isMultiSelectionEnable && active && selectedUsers.isNotEmpty()) {
// We want the selected users to behave a bit like a top bar - when the list below is scrolled, the colour // We want the selected users to behave a bit like a top bar - when the list below is scrolled, the colour
// should change to indicate elevation. // should change to indicate elevation.
@ -96,7 +96,7 @@ fun SearchUserBar(
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(16.dp),
selectedUsers = selectedUsers, selectedUsers = selectedUsers,
autoScroll = true, autoScroll = true,
onUserRemoved = onUserDeselected, onUserRemove = onUserDeselect,
modifier = Modifier.background(appBarContainerColor) modifier = Modifier.background(appBarContainerColor)
) )
} }
@ -109,7 +109,7 @@ fun SearchUserBar(
resultState = state, resultState = state,
resultHandler = { users -> resultHandler = { users ->
LazyColumn(state = columnState) { LazyColumn(state = columnState) {
if (isMultiSelectionEnabled) { if (isMultiSelectionEnable) {
itemsIndexed(users) { index, searchResult -> itemsIndexed(users) { index, searchResult ->
SearchMultipleUsersResultItem( SearchMultipleUsersResultItem(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -117,9 +117,9 @@ fun SearchUserBar(
isUserSelected = selectedUsers.contains(searchResult.matrixUser), isUserSelected = selectedUsers.contains(searchResult.matrixUser),
onCheckedChange = { checked -> onCheckedChange = { checked ->
if (checked) { if (checked) {
onUserSelected(searchResult.matrixUser) onUserSelect(searchResult.matrixUser)
} else { } else {
onUserDeselected(searchResult.matrixUser) onUserDeselect(searchResult.matrixUser)
} }
} }
) )
@ -132,7 +132,7 @@ fun SearchUserBar(
SearchSingleUserResultItem( SearchSingleUserResultItem(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
searchResult = searchResult, searchResult = searchResult,
onClick = { onUserSelected(searchResult.matrixUser) } onClick = { onUserSelect(searchResult.matrixUser) }
) )
if (index < users.lastIndex) { if (index < users.lastIndex) {
HorizontalDivider() HorizontalDivider()

View file

@ -44,8 +44,8 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable @Composable
fun UserListView( fun UserListView(
state: UserListState, state: UserListState,
onUserSelected: (MatrixUser) -> Unit, onSelectUser: (MatrixUser) -> Unit,
onUserDeselected: (MatrixUser) -> Unit, onDeselectUser: (MatrixUser) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
showBackButton: Boolean = true, showBackButton: Boolean = true,
) { ) {
@ -59,17 +59,17 @@ fun UserListView(
selectedUsers = state.selectedUsers, selectedUsers = state.selectedUsers,
active = state.isSearchActive, active = state.isSearchActive,
showLoader = state.showSearchLoader, showLoader = state.showSearchLoader,
isMultiSelectionEnabled = state.isMultiSelectionEnabled, isMultiSelectionEnable = state.isMultiSelectionEnabled,
showBackButton = showBackButton, showBackButton = showBackButton,
onActiveChanged = { state.eventSink(UserListEvents.OnSearchActiveChanged(it)) }, onActiveChange = { state.eventSink(UserListEvents.OnSearchActiveChanged(it)) },
onTextChanged = { state.eventSink(UserListEvents.UpdateSearchQuery(it)) }, onTextChange = { state.eventSink(UserListEvents.UpdateSearchQuery(it)) },
onUserSelected = { onUserSelect = {
state.eventSink(UserListEvents.AddToSelection(it)) state.eventSink(UserListEvents.AddToSelection(it))
onUserSelected(it) onSelectUser(it)
}, },
onUserDeselected = { onUserDeselect = {
state.eventSink(UserListEvents.RemoveFromSelection(it)) state.eventSink(UserListEvents.RemoveFromSelection(it))
onUserDeselected(it) onDeselectUser(it)
}, },
) )
@ -78,9 +78,9 @@ fun UserListView(
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(16.dp),
selectedUsers = state.selectedUsers, selectedUsers = state.selectedUsers,
autoScroll = true, autoScroll = true,
onUserRemoved = { onUserRemove = {
state.eventSink(UserListEvents.RemoveFromSelection(it)) state.eventSink(UserListEvents.RemoveFromSelection(it))
onUserDeselected(it) onDeselectUser(it)
}, },
) )
} }
@ -102,10 +102,10 @@ fun UserListView(
onCheckedChange = { onCheckedChange = {
if (isSelected) { if (isSelected) {
state.eventSink(UserListEvents.RemoveFromSelection(recentDirectRoom.matrixUser)) state.eventSink(UserListEvents.RemoveFromSelection(recentDirectRoom.matrixUser))
onUserDeselected(recentDirectRoom.matrixUser) onDeselectUser(recentDirectRoom.matrixUser)
} else { } else {
state.eventSink(UserListEvents.AddToSelection(recentDirectRoom.matrixUser)) state.eventSink(UserListEvents.AddToSelection(recentDirectRoom.matrixUser))
onUserSelected(recentDirectRoom.matrixUser) onSelectUser(recentDirectRoom.matrixUser)
} }
}, },
data = CheckableUserRowData.Resolved( data = CheckableUserRowData.Resolved(
@ -129,7 +129,7 @@ fun UserListView(
internal fun UserListViewPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = ElementPreview { internal fun UserListViewPreview(@PreviewParameter(UserListStateProvider::class) state: UserListState) = ElementPreview {
UserListView( UserListView(
state = state, state = state,
onUserSelected = {}, onSelectUser = {},
onUserDeselected = {}, onDeselectUser = {},
) )
} }

View file

@ -50,7 +50,7 @@ class ConfigureRoomNode @AssistedInject constructor(
fun onCreateRoomSuccess(roomId: RoomId) fun onCreateRoomSuccess(roomId: RoomId)
} }
private fun onRoomCreated(roomId: RoomId) { private fun onCreateRoomSuccess(roomId: RoomId) {
plugins<Callback>().forEach { it.onCreateRoomSuccess(roomId) } plugins<Callback>().forEach { it.onCreateRoomSuccess(roomId) }
} }
@ -60,8 +60,8 @@ class ConfigureRoomNode @AssistedInject constructor(
ConfigureRoomView( ConfigureRoomView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onBackPressed = this::navigateUp, onBackClick = this::navigateUp,
onRoomCreated = this::onRoomCreated, onCreateRoomSuccess = this::onCreateRoomSuccess,
) )
} }
} }

View file

@ -65,14 +65,14 @@ import io.element.android.libraries.ui.strings.CommonStrings
@Composable @Composable
fun ConfigureRoomView( fun ConfigureRoomView(
state: ConfigureRoomState, state: ConfigureRoomState,
onBackPressed: () -> Unit, onBackClick: () -> Unit,
onRoomCreated: (RoomId) -> Unit, onCreateRoomSuccess: (RoomId) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
val isAvatarActionsSheetVisible = remember { mutableStateOf(false) } val isAvatarActionsSheetVisible = remember { mutableStateOf(false) }
fun onAvatarClicked() { fun onAvatarClick() {
focusManager.clearFocus() focusManager.clearFocus()
isAvatarActionsSheetVisible.value = true isAvatarActionsSheetVisible.value = true
} }
@ -82,8 +82,8 @@ fun ConfigureRoomView(
topBar = { topBar = {
ConfigureRoomToolbar( ConfigureRoomToolbar(
isNextActionEnabled = state.isCreateButtonEnabled, isNextActionEnabled = state.isCreateButtonEnabled,
onBackPressed = onBackPressed, onBackClick = onBackClick,
onNextPressed = { onNextClick = {
focusManager.clearFocus() focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.CreateRoom(state.config)) state.eventSink(ConfigureRoomEvents.CreateRoom(state.config))
}, },
@ -102,20 +102,20 @@ fun ConfigureRoomView(
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
avatarUri = state.config.avatarUri, avatarUri = state.config.avatarUri,
roomName = state.config.roomName.orEmpty(), roomName = state.config.roomName.orEmpty(),
onAvatarClick = ::onAvatarClicked, onAvatarClick = ::onAvatarClick,
onRoomNameChanged = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) }, onChangeRoomName = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) },
) )
RoomTopic( RoomTopic(
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
topic = state.config.topic.orEmpty(), topic = state.config.topic.orEmpty(),
onTopicChanged = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) }, onTopicChange = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) },
) )
if (state.config.invites.isNotEmpty()) { if (state.config.invites.isNotEmpty()) {
SelectedUsersRowList( SelectedUsersRowList(
modifier = Modifier.padding(bottom = 16.dp), modifier = Modifier.padding(bottom = 16.dp),
contentPadding = PaddingValues(horizontal = 24.dp), contentPadding = PaddingValues(horizontal = 24.dp),
selectedUsers = state.config.invites, selectedUsers = state.config.invites,
onUserRemoved = { onUserRemove = {
focusManager.clearFocus() focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it)) state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it))
}, },
@ -124,7 +124,7 @@ fun ConfigureRoomView(
RoomPrivacyOptions( RoomPrivacyOptions(
modifier = Modifier.padding(bottom = 40.dp), modifier = Modifier.padding(bottom = 40.dp),
selected = state.config.privacy, selected = state.config.privacy,
onOptionSelected = { onOptionClick = {
focusManager.clearFocus() focusManager.clearFocus()
state.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(it.privacy)) state.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(it.privacy))
}, },
@ -136,7 +136,7 @@ fun ConfigureRoomView(
actions = state.avatarActions, actions = state.avatarActions,
isVisible = isAvatarActionsSheetVisible.value, isVisible = isAvatarActionsSheetVisible.value,
onDismiss = { isAvatarActionsSheetVisible.value = false }, onDismiss = { isAvatarActionsSheetVisible.value = false },
onActionSelected = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) } onSelectAction = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) }
) )
AsyncActionView( AsyncActionView(
@ -146,7 +146,7 @@ fun ConfigureRoomView(
progressText = stringResource(CommonStrings.common_creating_room), progressText = stringResource(CommonStrings.common_creating_room),
) )
}, },
onSuccess = { onRoomCreated(it) }, onSuccess = { onCreateRoomSuccess(it) },
errorMessage = { stringResource(R.string.screen_create_room_error_creating_room) }, errorMessage = { stringResource(R.string.screen_create_room_error_creating_room) },
onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom(state.config)) }, onRetry = { state.eventSink(ConfigureRoomEvents.CreateRoom(state.config)) },
onErrorDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) }, onErrorDismiss = { state.eventSink(ConfigureRoomEvents.CancelCreateRoom) },
@ -161,8 +161,8 @@ fun ConfigureRoomView(
@Composable @Composable
private fun ConfigureRoomToolbar( private fun ConfigureRoomToolbar(
isNextActionEnabled: Boolean, isNextActionEnabled: Boolean,
onBackPressed: () -> Unit, onBackClick: () -> Unit,
onNextPressed: () -> Unit, onNextClick: () -> Unit,
) { ) {
TopAppBar( TopAppBar(
title = { title = {
@ -171,12 +171,12 @@ private fun ConfigureRoomToolbar(
style = ElementTheme.typography.aliasScreenTitle, style = ElementTheme.typography.aliasScreenTitle,
) )
}, },
navigationIcon = { BackButton(onClick = onBackPressed) }, navigationIcon = { BackButton(onClick = onBackClick) },
actions = { actions = {
TextButton( TextButton(
text = stringResource(CommonStrings.action_create), text = stringResource(CommonStrings.action_create),
enabled = isNextActionEnabled, enabled = isNextActionEnabled,
onClick = onNextPressed, onClick = onNextClick,
) )
} }
) )
@ -187,7 +187,7 @@ private fun RoomNameWithAvatar(
avatarUri: Uri?, avatarUri: Uri?,
roomName: String, roomName: String,
onAvatarClick: () -> Unit, onAvatarClick: () -> Unit,
onRoomNameChanged: (String) -> Unit, onChangeRoomName: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Row( Row(
@ -205,7 +205,7 @@ private fun RoomNameWithAvatar(
value = roomName, value = roomName,
placeholder = stringResource(CommonStrings.common_room_name_placeholder), placeholder = stringResource(CommonStrings.common_room_name_placeholder),
singleLine = true, singleLine = true,
onValueChange = onRoomNameChanged, onValueChange = onChangeRoomName,
) )
} }
} }
@ -213,7 +213,7 @@ private fun RoomNameWithAvatar(
@Composable @Composable
private fun RoomTopic( private fun RoomTopic(
topic: String, topic: String,
onTopicChanged: (String) -> Unit, onTopicChange: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
LabelledTextField( LabelledTextField(
@ -221,7 +221,7 @@ private fun RoomTopic(
label = stringResource(R.string.screen_create_room_topic_label), label = stringResource(R.string.screen_create_room_topic_label),
value = topic, value = topic,
placeholder = stringResource(CommonStrings.common_topic_placeholder), placeholder = stringResource(CommonStrings.common_topic_placeholder),
onValueChange = onTopicChanged, onValueChange = onTopicChange,
maxLines = 3, maxLines = 3,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, capitalization = KeyboardCapitalization.Sentences,
@ -232,7 +232,7 @@ private fun RoomTopic(
@Composable @Composable
private fun RoomPrivacyOptions( private fun RoomPrivacyOptions(
selected: RoomPrivacy?, selected: RoomPrivacy?,
onOptionSelected: (RoomPrivacyItem) -> Unit, onOptionClick: (RoomPrivacyItem) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val items = roomPrivacyItems() val items = roomPrivacyItems()
@ -241,7 +241,7 @@ private fun RoomPrivacyOptions(
RoomPrivacyOption( RoomPrivacyOption(
roomPrivacyItem = item, roomPrivacyItem = item,
isSelected = selected == item.privacy, isSelected = selected == item.privacy,
onOptionSelected = onOptionSelected, onOptionClick = onOptionClick,
) )
} }
} }
@ -252,7 +252,7 @@ private fun RoomPrivacyOptions(
internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview { internal fun ConfigureRoomViewPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreview {
ConfigureRoomView( ConfigureRoomView(
state = state, state = state,
onBackPressed = {}, onBackClick = {},
onRoomCreated = {}, onCreateRoomSuccess = {},
) )
} }

View file

@ -68,10 +68,10 @@ class CreateRoomRootNode @AssistedInject constructor(
CreateRoomRootView( CreateRoomRootView(
state = state, state = state,
modifier = modifier, modifier = modifier,
onClosePressed = this::navigateUp, onCloseClick = this::navigateUp,
onNewRoomClicked = ::onCreateNewRoom, onNewRoomClick = ::onCreateNewRoom,
onOpenDM = ::onStartChatSuccess, onOpenDM = ::onStartChatSuccess,
onInviteFriendsClicked = { invitePeople(activity) } onInviteFriendsClick = { invitePeople(activity) }
) )
} }

View file

@ -59,17 +59,17 @@ import kotlinx.collections.immutable.persistentListOf
@Composable @Composable
fun CreateRoomRootView( fun CreateRoomRootView(
state: CreateRoomRootState, state: CreateRoomRootState,
onClosePressed: () -> Unit, onCloseClick: () -> Unit,
onNewRoomClicked: () -> Unit, onNewRoomClick: () -> Unit,
onOpenDM: (RoomId) -> Unit, onOpenDM: (RoomId) -> Unit,
onInviteFriendsClicked: () -> Unit, onInviteFriendsClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Scaffold( Scaffold(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
topBar = { topBar = {
if (!state.userListState.isSearchActive) { if (!state.userListState.isSearchActive) {
CreateRoomRootViewTopBar(onClosePressed = onClosePressed) CreateRoomRootViewTopBar(onCloseClick = onCloseClick)
} }
} }
) { paddingValues -> ) { paddingValues ->
@ -86,18 +86,18 @@ fun CreateRoomRootView(
state = state.userListState.copy( state = state.userListState.copy(
recentDirectRooms = persistentListOf(), recentDirectRooms = persistentListOf(),
), ),
onUserSelected = { onSelectUser = {
state.eventSink(CreateRoomRootEvents.StartDM(it)) state.eventSink(CreateRoomRootEvents.StartDM(it))
}, },
onUserDeselected = { }, onDeselectUser = { },
) )
if (!state.userListState.isSearchActive) { if (!state.userListState.isSearchActive) {
CreateRoomActionButtonsList( CreateRoomActionButtonsList(
state = state, state = state,
onNewRoomClicked = onNewRoomClicked, onNewRoomClick = onNewRoomClick,
onInvitePeopleClicked = onInviteFriendsClicked, onInvitePeopleClick = onInviteFriendsClick,
onDmClicked = onOpenDM, onDmClick = onOpenDM,
) )
} }
} }
@ -125,7 +125,7 @@ fun CreateRoomRootView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun CreateRoomRootViewTopBar( private fun CreateRoomRootViewTopBar(
onClosePressed: () -> Unit, onCloseClick: () -> Unit,
) { ) {
TopAppBar( TopAppBar(
title = { title = {
@ -137,7 +137,7 @@ private fun CreateRoomRootViewTopBar(
navigationIcon = { navigationIcon = {
BackButton( BackButton(
imageVector = CompoundIcons.Close(), imageVector = CompoundIcons.Close(),
onClick = onClosePressed, onClick = onCloseClick,
) )
} }
) )
@ -146,23 +146,23 @@ private fun CreateRoomRootViewTopBar(
@Composable @Composable
private fun CreateRoomActionButtonsList( private fun CreateRoomActionButtonsList(
state: CreateRoomRootState, state: CreateRoomRootState,
onNewRoomClicked: () -> Unit, onNewRoomClick: () -> Unit,
onInvitePeopleClicked: () -> Unit, onInvitePeopleClick: () -> Unit,
onDmClicked: (RoomId) -> Unit, onDmClick: (RoomId) -> Unit,
) { ) {
LazyColumn { LazyColumn {
item { item {
CreateRoomActionButton( CreateRoomActionButton(
iconRes = CompoundDrawables.ic_compound_plus, iconRes = CompoundDrawables.ic_compound_plus,
text = stringResource(id = R.string.screen_create_room_action_create_room), text = stringResource(id = R.string.screen_create_room_action_create_room),
onClick = onNewRoomClicked, onClick = onNewRoomClick,
) )
} }
item { item {
CreateRoomActionButton( CreateRoomActionButton(
iconRes = CompoundDrawables.ic_compound_share_android, iconRes = CompoundDrawables.ic_compound_share_android,
text = stringResource(id = CommonStrings.action_invite_friends_to_app, state.applicationName), text = stringResource(id = CommonStrings.action_invite_friends_to_app, state.applicationName),
onClick = onInvitePeopleClicked, onClick = onInvitePeopleClick,
) )
} }
if (state.userListState.recentDirectRooms.isNotEmpty()) { if (state.userListState.recentDirectRooms.isNotEmpty()) {
@ -177,7 +177,7 @@ private fun CreateRoomActionButtonsList(
MatrixUserRow( MatrixUserRow(
modifier = Modifier.clickable( modifier = Modifier.clickable(
onClick = { onClick = {
onDmClicked(recentDirectRoom.roomId) onDmClick(recentDirectRoom.roomId)
} }
), ),
matrixUser = recentDirectRoom.matrixUser, matrixUser = recentDirectRoom.matrixUser,
@ -222,9 +222,9 @@ internal fun CreateRoomRootViewPreview(@PreviewParameter(CreateRoomRootStateProv
ElementPreview { ElementPreview {
CreateRoomRootView( CreateRoomRootView(
state = state, state = state,
onClosePressed = {}, onCloseClick = {},
onNewRoomClicked = {}, onNewRoomClick = {},
onOpenDM = {}, onOpenDM = {},
onInviteFriendsClicked = {}, onInviteFriendsClick = {},
) )
} }

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_action_create_room">"Новы пакой"</string> <string name="screen_create_room_action_create_room">"Новы пакой"</string>
<string name="screen_create_room_add_people_title">"Запрасіць карыстальникаў"</string> <string name="screen_create_room_add_people_title">"Запрасіць карыстальнікаў"</string>
<string name="screen_create_room_error_creating_room">"Пры стварэнні пакоя адбылася памылка"</string> <string name="screen_create_room_error_creating_room">"Пры стварэнні пакоя адбылася памылка"</string>
<string name="screen_create_room_private_option_description">"Паведамленні ў гэтым пакоі зашыфраваны. Гэта шыфраванне нельга адключыць."</string> <string name="screen_create_room_private_option_description">"Паведамленні ў гэтым пакоі зашыфраваны. Гэта шыфраванне нельга адключыць."</string>
<string name="screen_create_room_private_option_title">"Прыватны пакой (толькі па запрашэнні)"</string> <string name="screen_create_room_private_option_title">"Прыватны пакой (толькі па запрашэнні)"</string>

View file

@ -31,7 +31,7 @@ import io.element.android.services.analytics.test.FakeAnalyticsService
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
class DefaultStartDMActionTests { class DefaultStartDMActionTest {
@Test @Test
fun `when dm is found, assert state is updated with given room id`() = runTest { fun `when dm is found, assert state is updated with given room id`() = runTest {
val matrixClient = FakeMatrixClient().apply { val matrixClient = FakeMatrixClient().apply {

View file

@ -30,7 +30,7 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
class AddPeoplePresenterTests { class AddPeoplePresenterTest {
@get:Rule @get:Rule
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()

View file

@ -47,7 +47,7 @@ class AddPeopleViewTest {
aUserListState( aUserListState(
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
onBackPressed = it onBackClick = it
) )
rule.pressBack() rule.pressBack()
} }
@ -75,7 +75,7 @@ class AddPeopleViewTest {
aUserListState( aUserListState(
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
onNextPressed = it onNextClick = it
) )
rule.clickOn(CommonStrings.action_skip) rule.clickOn(CommonStrings.action_skip)
} }
@ -85,14 +85,14 @@ class AddPeopleViewTest {
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAddPeopleView( private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setAddPeopleView(
state: UserListState, state: UserListState,
onBackPressed: () -> Unit = EnsureNeverCalled(), onBackClick: () -> Unit = EnsureNeverCalled(),
onNextPressed: () -> Unit = EnsureNeverCalled(), onNextClick: () -> Unit = EnsureNeverCalled(),
) { ) {
setContent { setContent {
AddPeopleView( AddPeopleView(
state = state, state = state,
onBackPressed = onBackPressed, onBackClick = onBackClick,
onNextPressed = onNextPressed, onNextClick = onNextClick,
) )
} }
} }

View file

@ -61,7 +61,7 @@ private const val AN_URI_FROM_CAMERA_2 = "content://uri_from_camera_2"
private const val AN_URI_FROM_GALLERY = "content://uri_from_gallery" private const val AN_URI_FROM_GALLERY = "content://uri_from_gallery"
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
class ConfigureRoomPresenterTests { class ConfigureRoomPresenterTest {
@get:Rule @get:Rule
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()

View file

@ -37,7 +37,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
class CreateRoomRootPresenterTests { class CreateRoomRootPresenterTest {
@get:Rule @get:Rule
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()

View file

@ -54,7 +54,7 @@ class CreateRoomRootViewTest {
aCreateRoomRootState( aCreateRoomRootState(
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
onClosePressed = it onCloseClick = it
) )
rule.pressBack() rule.pressBack()
} }
@ -68,7 +68,7 @@ class CreateRoomRootViewTest {
aCreateRoomRootState( aCreateRoomRootState(
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
onNewRoomClicked = it onNewRoomClick = it
) )
rule.clickOn(R.string.screen_create_room_action_create_room) rule.clickOn(R.string.screen_create_room_action_create_room)
} }
@ -84,7 +84,7 @@ class CreateRoomRootViewTest {
applicationName = "test", applicationName = "test",
eventSink = eventsRecorder, eventSink = eventsRecorder,
), ),
onInviteFriendsClicked = it onInviteFriendsClick = it
) )
val text = rule.activity.getString(CommonStrings.action_invite_friends_to_app, "test") val text = rule.activity.getString(CommonStrings.action_invite_friends_to_app, "test")
rule.onNodeWithText(text).performClick() rule.onNodeWithText(text).performClick()
@ -114,18 +114,18 @@ class CreateRoomRootViewTest {
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setCreateRoomRootView( private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setCreateRoomRootView(
state: CreateRoomRootState, state: CreateRoomRootState,
onClosePressed: () -> Unit = EnsureNeverCalled(), onCloseClick: () -> Unit = EnsureNeverCalled(),
onNewRoomClicked: () -> Unit = EnsureNeverCalled(), onNewRoomClick: () -> Unit = EnsureNeverCalled(),
onOpenDM: (RoomId) -> Unit = EnsureNeverCalledWithParam(), onOpenDM: (RoomId) -> Unit = EnsureNeverCalledWithParam(),
onInviteFriendsClicked: () -> Unit = EnsureNeverCalled(), onInviteFriendsClick: () -> Unit = EnsureNeverCalled(),
) { ) {
setContent { setContent {
CreateRoomRootView( CreateRoomRootView(
state = state, state = state,
onClosePressed = onClosePressed, onCloseClick = onCloseClick,
onNewRoomClicked = onNewRoomClicked, onNewRoomClick = onNewRoomClick,
onOpenDM = onOpenDM, onOpenDM = onOpenDM,
onInviteFriendsClicked = onInviteFriendsClicked, onInviteFriendsClick = onInviteFriendsClick,
) )
} }
} }

View file

@ -33,7 +33,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
class DefaultUserListPresenterTests { class DefaultUserListPresenterTest {
@get:Rule @get:Rule
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()

View file

@ -34,18 +34,18 @@ class WelcomeNode @AssistedInject constructor(
private val buildMeta: BuildMeta, private val buildMeta: BuildMeta,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin { interface Callback : Plugin {
fun onContinueClicked() fun onContinueClick()
} }
private fun onContinueClicked() { private fun onContinueClick() {
plugins.filterIsInstance<Callback>().forEach { it.onContinueClicked() } plugins.filterIsInstance<Callback>().forEach { it.onContinueClick() }
} }
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {
WelcomeView( WelcomeView(
applicationName = buildMeta.applicationName, applicationName = buildMeta.applicationName,
onContinueClicked = ::onContinueClicked, onContinueClick = ::onContinueClick,
modifier = modifier modifier = modifier
) )
} }

View file

@ -52,9 +52,9 @@ import kotlinx.collections.immutable.persistentListOf
fun WelcomeView( fun WelcomeView(
applicationName: String, applicationName: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onContinueClicked: () -> Unit, onContinueClick: () -> Unit,
) { ) {
BackHandler(onBack = onContinueClicked) BackHandler(onBack = onContinueClick)
OnBoardingPage( OnBoardingPage(
modifier = modifier modifier = modifier
.systemBarsPadding() .systemBarsPadding()
@ -90,7 +90,7 @@ fun WelcomeView(
Button( Button(
text = stringResource(CommonStrings.action_continue), text = stringResource(CommonStrings.action_continue),
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = onContinueClicked onClick = onContinueClick
) )
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
} }
@ -113,6 +113,6 @@ private fun listItems() = persistentListOf(
@Composable @Composable
internal fun WelcomeViewPreview() { internal fun WelcomeViewPreview() {
ElementPreview { ElementPreview {
WelcomeView(applicationName = "Element X", onContinueClicked = {}) WelcomeView(applicationName = "Element X", onContinueClick = {})
} }
} }

View file

@ -20,15 +20,14 @@ import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.DefaultPreferences
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import javax.inject.Inject import javax.inject.Inject
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
class AndroidWelcomeScreenState @Inject constructor( class SharedPreferencesWelcomeScreenState @Inject constructor(
@DefaultPreferences private val sharedPreferences: SharedPreferences, private val sharedPreferences: SharedPreferences,
) : WelcomeScreenState { ) : WelcomeScreenStore {
companion object { companion object {
private const val IS_WELCOME_SCREEN_SHOWN = "is_welcome_screen_shown" private const val IS_WELCOME_SCREEN_SHOWN = "is_welcome_screen_shown"
} }

View file

@ -16,7 +16,7 @@
package io.element.android.features.ftue.impl.welcome.state package io.element.android.features.ftue.impl.welcome.state
interface WelcomeScreenState { interface WelcomeScreenStore {
fun isWelcomeScreenNeeded(): Boolean fun isWelcomeScreenNeeded(): Boolean
fun setWelcomeScreenShown() fun setWelcomeScreenShown()
fun reset() fun reset()

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Вы можаце змяніць налады пазней."</string> <string name="screen_notification_optin_subtitle">"Вы можаце змяніць налады пазней."</string>
<string name="screen_notification_optin_title">"Дазвольце апавяшчэнні і ніколі не прапускайце іх"</string> <string name="screen_notification_optin_title">"Дазвольце апавяшчэнні і ніколі не прапускайце іх"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Ўсталяванне бяспечнага злучэння"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Не атрымалася ўсталяваць бяспечнае злучэнне з новай прыладай. Існуючыя прылады па-ранейшаму ў бяспецы, і вам не трэба турбавацца пра іх."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Што зараз?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Паспрабуйце зноў увайсці ў сістэму з дапамогай QR-кода, калі гэта была сеткавая праблема"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Калі вы сутыкнуліся з той жа праблемай, паспрабуйце іншую сетку Wi-Fi або скарыстайцеся мабільнымі дадзенымі замест Wi-Fi."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Калі гэта не дапамагло, увайдзіце ўручную"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Злучэнне небяспечнае"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Вам будзе прапанавана ўвесці дзве лічбы, паказаныя на гэтай прыладзе."</string>
<string name="screen_qr_code_login_device_code_title">"Увядзіце наступны нумар на іншай прыладзе."</string>
<string name="screen_qr_code_login_initial_state_button_title">"Гатовы да сканавання"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Адкрыйце %1$s на настольнай прыладзе"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Націсніце на свой аватар"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Выберыце %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Звязаць новую прыладу”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Адсканіруйце QR-код з дапамогай гэтай прылады"</string>
<string name="screen_qr_code_login_initial_state_title">"Адкрыйце %1$s на іншай прыладзе, каб атрымаць QR-код"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Выкарыстоўвайце QR-код, паказаны на іншай прыладзе."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Паўтарыць спробу"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Няправільны QR-код"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Перайсці ў налады камеры"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Каб працягнуць, вам неабходна дазволіць %1$s выкарыстоўваць камеру вашай прылады."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Дазвольце доступ да камеры для сканавання QR-кода"</string>
<string name="screen_qr_code_login_scanning_state_title">"Сканаваць QR-код"</string>
<string name="screen_qr_code_login_start_over_button">"Пачаць спачатку"</string>
<string name="screen_qr_code_login_unknown_error_description">"Адбылася нечаканая памылка. Калі ласка, паспрабуйце яшчэ раз."</string>
<string name="screen_qr_code_login_verify_code_loading">"У чаканні іншай прылады"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Ваш правайдэр уліковага запісу можа запытаць наступны код для праверкі ўваходу."</string>
<string name="screen_qr_code_login_verify_code_title">"Ваш код спраўджання"</string>
<string name="screen_welcome_bullet_1">"Званкі, апытанні, пошук і многае іншае будзе дададзена пазней у гэтым годзе."</string> <string name="screen_welcome_bullet_1">"Званкі, апытанні, пошук і многае іншае будзе дададзена пазней у гэтым годзе."</string>
<string name="screen_welcome_bullet_2">"Гісторыя паведамленняў для зашыфраваных пакояў пакуль недаступна."</string> <string name="screen_welcome_bullet_2">"Гісторыя паведамленняў для зашыфраваных пакояў пакуль недаступна."</string>
<string name="screen_welcome_bullet_3">"Мы будзем рады пачуць вашае меркаванне, паведаміце нам аб гэтым праз старонку налад."</string> <string name="screen_welcome_bullet_3">"Мы будзем рады пачуць вашае меркаванне, паведаміце нам аб гэтым праз старонку налад."</string>

View file

@ -2,7 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Можете да промените настройките си по-късно."</string> <string name="screen_notification_optin_subtitle">"Можете да промените настройките си по-късно."</string>
<string name="screen_notification_optin_title">"Разрешете известията и никога не пропускайте съобщение"</string> <string name="screen_notification_optin_title">"Разрешете известията и никога не пропускайте съобщение"</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Повторен опит"</string>
<string name="screen_welcome_bullet_2">"Хронологията на съобщенията за шифровани стаи все още не е налична."</string> <string name="screen_welcome_bullet_2">"Хронологията на съобщенията за шифровани стаи все още не е налична."</string>
<string name="screen_welcome_title">"Добре дошли в %1$s!"</string> <string name="screen_welcome_title">"Добре дошли в %1$s!"</string>
</resources> </resources>

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Nastavení můžete později změnit."</string> <string name="screen_notification_optin_subtitle">"Nastavení můžete později změnit."</string>
<string name="screen_notification_optin_title">"Povolte oznámení a nezmeškejte žádnou zprávu"</string> <string name="screen_notification_optin_title">"Povolte oznámení a nezmeškejte žádnou zprávu"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Navazování zabezpečeného spojení"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"K novému zařízení se nepodařilo navázat bezpečné připojení. Vaše stávající zařízení jsou stále v bezpečí a nemusíte se o ně obávat."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Co teď?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Zkuste se znovu přihlásit pomocí QR kódu v případě, že se jednalo o problém se sítí"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Pokud narazíte na stejný problém, zkuste jinou síť wifi nebo použijte mobilní data místo wifi"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Pokud to nefunguje, přihlaste se ručně"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Připojení není zabezpečené"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Budete požádáni o zadání dvou níže uvedených číslic."</string>
<string name="screen_qr_code_login_device_code_title">"Zadejte níže uvedené číslo na svém dalším zařízení"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Připraveno ke skenování"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Otevřete %1$s na stolním počítači"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Klikněte na svůj avatar"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Vybrat %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"\"Připojit nové zařízení\""</string>
<string name="screen_qr_code_login_initial_state_item_4">"Naskenujte QR kód pomocí tohoto zařízení"</string>
<string name="screen_qr_code_login_initial_state_title">"Otevřete %1$s na jiném zařízení pro získání QR kódu"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Použijte QR kód zobrazený na druhém zařízení."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Zkusit znovu"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Špatný QR kód"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Přejděte na nastavení fotoaparátu"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Abyste mohli pokračovat, musíte aplikaci %1$s udělit povolení k použití kamery vašeho zařízení."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Povolte přístup k fotoaparátu a naskenujte QR kód"</string>
<string name="screen_qr_code_login_scanning_state_title">"Naskenujte QR kód"</string>
<string name="screen_qr_code_login_start_over_button">"Začít znovu"</string>
<string name="screen_qr_code_login_unknown_error_description">"Vyskytla se neočekávaná chyba. Prosím zkuste to znovu."</string>
<string name="screen_qr_code_login_verify_code_loading">"Čekání na vaše další zařízení"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Váš poskytovatel účtu může požádat o následující kód pro ověření přihlášení."</string>
<string name="screen_qr_code_login_verify_code_title">"Váš ověřovací kód"</string>
<string name="screen_welcome_bullet_1">"Hovory, hlasování, vyhledávání a další budou přidány koncem tohoto roku."</string> <string name="screen_welcome_bullet_1">"Hovory, hlasování, vyhledávání a další budou přidány koncem tohoto roku."</string>
<string name="screen_welcome_bullet_2">"Historie zpráv šifrovaných místností nebude v této aktualizaci k dispozici."</string> <string name="screen_welcome_bullet_2">"Historie zpráv šifrovaných místností nebude v této aktualizaci k dispozici."</string>
<string name="screen_welcome_bullet_3">"Rádi bychom se od vás dozvěděli, co si o tom myslíte, dejte nám vědět prostřednictvím stránky s nastavením."</string> <string name="screen_welcome_bullet_3">"Rádi bychom se od vás dozvěděli, co si o tom myslíte, dejte nám vědět prostřednictvím stránky s nastavením."</string>

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Du kannst deine Einstellungen später ändern."</string> <string name="screen_notification_optin_subtitle">"Du kannst deine Einstellungen später ändern."</string>
<string name="screen_notification_optin_title">"Erlaube Benachrichtigungen und verpasse keine Nachricht"</string> <string name="screen_notification_optin_title">"Erlaube Benachrichtigungen und verpasse keine Nachricht"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Sichere Verbindung aufbauen"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Es konnte keine sichere Verbindung zu dem neuen Gerät hergestellt werden."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Und jetzt?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Versuche, dich erneut mit einem QR-Code anzumelden, falls dies ein Netzwerkproblem war."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Wenn das Problem bestehen bleibt, versuche es mit einem anderen WLAN-Netzwerk oder verwende deine mobilen Daten statt WLAN."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Wenn das nicht funktioniert, melde dich manuell an"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Die Verbindung ist nicht sicher"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Du wirst aufgefordert, die beiden unten abgebildeten Ziffern einzugeben."</string>
<string name="screen_qr_code_login_device_code_title">"Trage die unten angezeigte Zahl auf einem anderen Device ein"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Bereit zum Scannen"</string>
<string name="screen_qr_code_login_initial_state_item_1">"%1$s auf einem Desktop-Gerät öffnen"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Klick auf deinen Avatar"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Wähle %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"\"Neues Gerät verknüpfen\""</string>
<string name="screen_qr_code_login_initial_state_item_4">"Scanne den QR-Code mit diesem Gerät"</string>
<string name="screen_qr_code_login_initial_state_title">"Öffne %1$s auf einem anderen Gerät, um den QR-Code zu erhalten"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Verwende den QR-Code, der auf dem anderen Gerät angezeigt wird."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Erneut versuchen"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Falscher QR-Code"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Gehe zu den Kameraeinstellungen"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Du musst %1$s die Erlaubnis erteilen, die Kamera deines Geräts zu verwenden, um fortzufahren."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Erlaube Zugriff auf die Kamera zum Scannen des QR-Codes"</string>
<string name="screen_qr_code_login_scanning_state_title">"QR-Code scannen"</string>
<string name="screen_qr_code_login_start_over_button">"Neu beginnen"</string>
<string name="screen_qr_code_login_unknown_error_description">"Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut."</string>
<string name="screen_qr_code_login_verify_code_loading">"Warten auf dein anderes Gerät"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Dein Account-Provider kann nach dem folgenden Code fragen, um die Anmeldung zu bestätigen."</string>
<string name="screen_qr_code_login_verify_code_title">"Dein Verifizierungscode"</string>
<string name="screen_welcome_bullet_1">"Anrufe, Umfragen, Suchfunktionen und mehr werden im Laufe des Jahres hinzugefügt."</string> <string name="screen_welcome_bullet_1">"Anrufe, Umfragen, Suchfunktionen und mehr werden im Laufe des Jahres hinzugefügt."</string>
<string name="screen_welcome_bullet_2">"Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein."</string> <string name="screen_welcome_bullet_2">"Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein."</string>
<string name="screen_welcome_bullet_3">"Wir würden uns freuen, von dir zu hören. Teile uns deine Meinung über die Einstellungsseite mit."</string> <string name="screen_welcome_bullet_3">"Wir würden uns freuen, von dir zu hören. Teile uns deine Meinung über die Einstellungsseite mit."</string>

View file

@ -2,7 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Puedes cambiar la configuración más tarde."</string> <string name="screen_notification_optin_subtitle">"Puedes cambiar la configuración más tarde."</string>
<string name="screen_notification_optin_title">"Activa las notificaciones y nunca te pierdas un mensaje"</string> <string name="screen_notification_optin_title">"Activa las notificaciones y nunca te pierdas un mensaje"</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Inténtalo de nuevo"</string>
<string name="screen_welcome_bullet_1">"Las llamadas, las encuestas, la búsqueda y más se agregarán más adelante este año."</string> <string name="screen_welcome_bullet_1">"Las llamadas, las encuestas, la búsqueda y más se agregarán más adelante este año."</string>
<string name="screen_welcome_bullet_2">"El historial de mensajes de las salas cifradas aún no está disponible."</string> <string name="screen_welcome_bullet_2">"El historial de mensajes de las salas cifradas aún no está disponible."</string>
<string name="screen_welcome_bullet_3">"Nos encantaría saber de ti, haznos saber lo que piensas a través de la página de configuración."</string> <string name="screen_welcome_bullet_3">"Nos encantaría saber de ti, haznos saber lo que piensas a través de la página de configuración."</string>

View file

@ -2,33 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Vous pourrez modifier vos paramètres ultérieurement."</string> <string name="screen_notification_optin_subtitle">"Vous pourrez modifier vos paramètres ultérieurement."</string>
<string name="screen_notification_optin_title">"Autorisez les notifications et ne manquez aucun message"</string> <string name="screen_notification_optin_title">"Autorisez les notifications et ne manquez aucun message"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Établissement dune connexion sécurisée"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Aucune connexion sécurisée na pu être établie avec la nouvelle session. Vos sessions existantes sont toujours en sécurité et vous navez pas à vous en soucier."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Et maintenant ?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Essayez de vous connecter à nouveau à laide du code QR au cas où il sagirait dun problème réseau"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Si vous rencontrez le même problème, essayez un autre réseau wifi ou utilisez vos données mobiles au lieu du wifi"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Si cela ne fonctionne pas, connectez-vous manuellement"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"La connexion nest pas sécurisée"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Il vous sera demandé de saisir les deux chiffres affichés sur cet appareil."</string>
<string name="screen_qr_code_login_device_code_title">"Saisissez le nombre ci-dessous sur votre autre appareil"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Ouvrez %1$s sur un ordinateur"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Cliquez sur votre image de profil"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Choisissez %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Associer une nouvelle session”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Suivez les instructions affichées"</string>
<string name="screen_qr_code_login_initial_state_title">"Ouvrez %1$s sur un autre appareil pour obtenir le QR code"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Scannez le QR code affiché sur lautre appareil."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Essayer à nouveau"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"QR code erroné"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Accéder aux paramètres de lappareil photo"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Vous devez autoriser %1$s à utiliser la camera de votre appareil pour continuer."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Autoriser lusage de la caméra pour scanner le code QR"</string>
<string name="screen_qr_code_login_scanning_state_title">"Scannez le QR code"</string>
<string name="screen_qr_code_login_start_over_button">"Recommencer"</string>
<string name="screen_qr_code_login_unknown_error_description">"Une erreur inattendue sest produite. Veuillez réessayer."</string>
<string name="screen_qr_code_login_verify_code_loading">"En attente de votre autre session"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Votre fournisseur de compte peut vous demander le code suivant pour vérifier la connexion."</string>
<string name="screen_qr_code_login_verify_code_title">"Votre code de vérification"</string>
<string name="screen_welcome_bullet_1">"Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année."</string> <string name="screen_welcome_bullet_1">"Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année."</string>
<string name="screen_welcome_bullet_2">"Lhistorique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour."</string> <string name="screen_welcome_bullet_2">"Lhistorique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour."</string>
<string name="screen_welcome_bullet_3">"Nhésitez pas à nous faire part de vos commentaires via lécran des paramètres."</string> <string name="screen_welcome_bullet_3">"Nhésitez pas à nous faire part de vos commentaires via lécran des paramètres."</string>

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"A beállításokat később is módosíthatja."</string> <string name="screen_notification_optin_subtitle">"A beállításokat később is módosíthatja."</string>
<string name="screen_notification_optin_title">"Értesítések engedélyezése, hogy soha ne maradjon le egyetlen üzenetről sem"</string> <string name="screen_notification_optin_title">"Értesítések engedélyezése, hogy soha ne maradjon le egyetlen üzenetről sem"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Biztonságos kapcsolat létesítése"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Nem sikerült biztonságos kapcsolatot létesíteni az új eszközzel. A meglévő eszközei továbbra is biztonságban vannak, és nem kell aggódnia miattuk."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Most mi lesz?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Próbáljon meg újra bejelentkezni egy QR-kóddal, ha ez hálózati probléma volt."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Ha ugyanezzel a problémával találkozik, próbálkozzon másik Wi-Fi-hálózattal, vagy a Wi-Fi helyett használja a mobil-adatkapcsolatát"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Ha ez nem működik, jelentkezzen be kézileg"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"A kapcsolat nem biztonságos"</string>
<string name="screen_qr_code_login_device_code_subtitle">"A rendszer kérni fogja, hogy adja meg az alábbi két számjegyet az eszközén."</string>
<string name="screen_qr_code_login_device_code_title">"Adja meg az alábbi számot a másik eszközén"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Készen áll a beolvasásra"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Nyissa meg az %1$set egy asztali eszközön"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Kattintson a profilképére"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Válassza ezt: %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"„Új eszköz összekapcsolása”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Olvassa be a QR-kódot ezzel az eszközzel"</string>
<string name="screen_qr_code_login_initial_state_title">"Nyissa meg az %1$set egy másik eszközön a QR-kód lekéréséhez."</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Használja a másik eszközön látható QR-kódot."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Próbálja újra"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Hibás QR-kód"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Ugrás a kamerabeállításokhoz"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"A folytatáshoz engedélyeznie kell, hogy az %1$s használhassa az eszköz kameráját."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Engedélyezze a kamera elérését a QR-kód beolvasásához"</string>
<string name="screen_qr_code_login_scanning_state_title">"Olvassa be a QR-kódot"</string>
<string name="screen_qr_code_login_start_over_button">"Újrakezdés"</string>
<string name="screen_qr_code_login_unknown_error_description">"Váratlan hiba történt. Próbálja meg újra."</string>
<string name="screen_qr_code_login_verify_code_loading">"Várakozás a másik eszközre"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"A fiókszolgáltatója kérheti a következő kódot a bejelentkezése ellenőrzéséhez."</string>
<string name="screen_qr_code_login_verify_code_title">"Az Ön ellenőrzőkódja"</string>
<string name="screen_welcome_bullet_1">"A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra."</string> <string name="screen_welcome_bullet_1">"A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra."</string>
<string name="screen_welcome_bullet_2">"A titkosított szobák üzenetelőzményei nem lesznek elérhetők ebben a frissítésben."</string> <string name="screen_welcome_bullet_2">"A titkosított szobák üzenetelőzményei nem lesznek elérhetők ebben a frissítésben."</string>
<string name="screen_welcome_bullet_3">"Szeretnénk hallani a véleményét, ossza meg velünk a beállítások oldalon."</string> <string name="screen_welcome_bullet_3">"Szeretnénk hallani a véleményét, ossza meg velünk a beállítások oldalon."</string>

View file

@ -2,32 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Anda dapat mengubah pengaturan Anda nanti."</string> <string name="screen_notification_optin_subtitle">"Anda dapat mengubah pengaturan Anda nanti."</string>
<string name="screen_notification_optin_title">"Izinkan pemberitahuan dan jangan pernah melewatkan pesan"</string> <string name="screen_notification_optin_title">"Izinkan pemberitahuan dan jangan pernah melewatkan pesan"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Membuat koneksi"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Koneksi aman tidak dapat dibuat ke perangkat baru. Perangkat Anda yang ada masih aman dan Anda tidak perlu khawatir tentang mereka."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Apa sekarang?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Coba masuk lagi dengan kode QR jika ini adalah masalah jaringan"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Jika Anda mengalami masalah yang sama, coba jaringan Wi-Fi yang berbeda atau gunakan data seluler Anda daripada Wi-Fi"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Jika tidak berhasil, masuk secara manual"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Koneksi tidak aman"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Anda akan diminta untuk memasukkan dua digit yang ditunjukkan di bawah ini."</string>
<string name="screen_qr_code_login_device_code_title">"Masukkan nomor di perangkat Anda"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Buka %1$s di perangkat desktop"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Klik pada avatar Anda"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Pilih %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Tautkan perangkat baru”"</string>
<string name="screen_qr_code_login_initial_state_title">"Buka %1$s di perangkat lain untuk mendapatkan kode QR"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Gunakan kode QR yang ditampilkan di perangkat lain."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Coba lagi"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Kode QR salah"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Pergi ke pengaturan kamera"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Anda perlu memberikan izin ke %1$s untuk menggunakan kamera perangkat Anda untuk melanjutkan."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Izinkan akses kamera untuk memindai kode QR"</string>
<string name="screen_qr_code_login_scanning_state_title">"Pindai kode QR"</string>
<string name="screen_qr_code_login_start_over_button">"Mulai dari awal"</string>
<string name="screen_qr_code_login_unknown_error_description">"Terjadi kesalahan tak terduga. Silakan coba lagi."</string>
<string name="screen_qr_code_login_verify_code_loading">"Menunggu perangkat Anda yang lain"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Penyedia akun Anda mungkin meminta kode berikut untuk memverifikasi proses masuk."</string>
<string name="screen_qr_code_login_verify_code_title">"Kode verifikasi Anda"</string>
<string name="screen_welcome_bullet_1">"Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini."</string> <string name="screen_welcome_bullet_1">"Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini."</string>
<string name="screen_welcome_bullet_2">"Riwayat pesan untuk ruangan terenkripsi tidak akan tersedia dalam pembaruan ini."</string> <string name="screen_welcome_bullet_2">"Riwayat pesan untuk ruangan terenkripsi tidak akan tersedia dalam pembaruan ini."</string>
<string name="screen_welcome_bullet_3">"Kami ingin mendengar dari Anda, beri tahu kami pendapat Anda melalui halaman pengaturan."</string> <string name="screen_welcome_bullet_3">"Kami ingin mendengar dari Anda, beri tahu kami pendapat Anda melalui halaman pengaturan."</string>

View file

@ -2,33 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Potrai modificare le tue impostazioni in seguito."</string> <string name="screen_notification_optin_subtitle">"Potrai modificare le tue impostazioni in seguito."</string>
<string name="screen_notification_optin_title">"Consenti le notifiche e non perdere mai un messaggio"</string> <string name="screen_notification_optin_title">"Consenti le notifiche e non perdere mai un messaggio"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Stabilendo la connessione"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Non è stato possibile stabilire una connessione sicura con il nuovo dispositivo. I tuoi dispositivi esistenti sono ancora al sicuro e non devi preoccuparti di loro."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"E adesso?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Prova ad accedere di nuovo con un codice QR nel caso si sia verificato un problema di rete."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Se riscontri lo stesso problema, prova con un altra rete wifi o usa i dati mobili al posto del wifi."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Se il problema persiste, accedi manualmente"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"La connessione non è sicura"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Ti verrà chiesto di inserire le due cifre mostrate su questo dispositivo."</string>
<string name="screen_qr_code_login_device_code_title">"Inserisci il numero qui sotto sull\'altro dispositivo"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Apri %1$s su un dispositivo desktop"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Clicca sul tuo avatar"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Seleziona %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"\"Collega un nuovo dispositivo\""</string>
<string name="screen_qr_code_login_initial_state_item_4">"Segui le istruzioni mostrate"</string>
<string name="screen_qr_code_login_initial_state_title">"Apri %1$s su un altro dispositivo per ottenere il codice QR"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Usa il codice QR mostrato sull\'altro dispositivo."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Riprova"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Codice QR sbagliato"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Vai alle impostazioni della fotocamera"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Per continuare, è necessario fornire l\'autorizzazione a %1$s per utilizzare la fotocamera del dispositivo."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Consenti l\'accesso alla fotocamera per la scansione del codice QR"</string>
<string name="screen_qr_code_login_scanning_state_title">"Scansiona il codice QR"</string>
<string name="screen_qr_code_login_start_over_button">"Ricomincia"</string>
<string name="screen_qr_code_login_unknown_error_description">"Si è verificato un errore inatteso. Riprova."</string>
<string name="screen_qr_code_login_verify_code_loading">"In attesa dell\'altro dispositivo"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Il fornitore dell\'account potrebbe richiedere il seguente codice per verificare l\'accesso."</string>
<string name="screen_qr_code_login_verify_code_title">"Il tuo codice di verifica"</string>
<string name="screen_welcome_bullet_1">"Chiamate, sondaggi, ricerche e altro ancora saranno aggiunti nel corso dell\'anno."</string> <string name="screen_welcome_bullet_1">"Chiamate, sondaggi, ricerche e altro ancora saranno aggiunti nel corso dell\'anno."</string>
<string name="screen_welcome_bullet_2">"La cronologia dei messaggi per le stanze crittografate non è ancora disponibile."</string> <string name="screen_welcome_bullet_2">"La cronologia dei messaggi per le stanze crittografate non è ancora disponibile."</string>
<string name="screen_welcome_bullet_3">"Ci piacerebbe sentire il tuo parere, facci sapere cosa ne pensi tramite la pagina delle impostazioni."</string> <string name="screen_welcome_bullet_3">"Ci piacerebbe sentire il tuo parere, facci sapere cosa ne pensi tramite la pagina delle impostazioni."</string>

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Podes alterar as tuas definições mais tarde."</string> <string name="screen_notification_optin_subtitle">"Podes alterar as tuas definições mais tarde."</string>
<string name="screen_notification_optin_title">"Permite as notificações e nunca percas uma mensagem"</string> <string name="screen_notification_optin_title">"Permite as notificações e nunca percas uma mensagem"</string>
<string name="screen_qr_code_login_connecting_subtitle">"A estabelecer uma ligação segura"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Não foi possível estabelecer uma ligação segura com o novo dispositivo. Os teus outros dispositivos continuam seguros, não precisas de te preocupar com eles."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"E agora?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Tenta iniciar sessão novamente com um código QR, caso se trate de um problema de rede"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Se tiveres o mesmo problema, experimenta uma rede Wi-Fi diferente ou utiliza os teus dados móveis."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Se isso não funcionar, inicia sessão manualmente"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Ligação insegura"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Ser-te-á pedido que insiras os dois dígitos indicados neste dispositivo."</string>
<string name="screen_qr_code_login_device_code_title">"Insere o número abaixo no teu dispositivo"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Pronto para ler"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Abre a %1$s num computador"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Carrega no teu avatar"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Seleciona %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Ligar novo dispositivo”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Lê o código QR com este dispositivo"</string>
<string name="screen_qr_code_login_initial_state_title">"Abre a %1$s noutro dispositivo para obteres o código QR"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Lê o código QR apresentado no outro dispositivo."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Tentar novamente"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Código QR inválido"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Ir para as configurações da câmara"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Para continuar, tens que dar permissão à %1$s para aceder à câmara do teu dispositivo."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Permitir o acesso à câmara para ler o código QR"</string>
<string name="screen_qr_code_login_scanning_state_title">"Ler o código QR"</string>
<string name="screen_qr_code_login_start_over_button">"Começar de novo"</string>
<string name="screen_qr_code_login_unknown_error_description">"Ocorreu um erro inesperado. Tenta novamente."</string>
<string name="screen_qr_code_login_verify_code_loading">"À espera do teu outro dispositivo"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"O teu fornecedor de conta pode pedir o seguinte código para verificar o início de sessão."</string>
<string name="screen_qr_code_login_verify_code_title">"O teu código de verificação"</string>
<string name="screen_welcome_bullet_1">"Chamadas, sondagens, pesquisa e mais funcionalidades vão ser adicionadas ao longo do ano."</string> <string name="screen_welcome_bullet_1">"Chamadas, sondagens, pesquisa e mais funcionalidades vão ser adicionadas ao longo do ano."</string>
<string name="screen_welcome_bullet_2">"O histórico de mensagens em salas cifradas ainda não está disponível."</string> <string name="screen_welcome_bullet_2">"O histórico de mensagens em salas cifradas ainda não está disponível."</string>
<string name="screen_welcome_bullet_3">"Gostaríamos de ouvir a tua opinião, diz-nos o que pensas através da página de configurações."</string> <string name="screen_welcome_bullet_3">"Gostaríamos de ouvir a tua opinião, diz-nos o que pensas através da página de configurações."</string>

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Puteți modifica setările mai târziu."</string> <string name="screen_notification_optin_subtitle">"Puteți modifica setările mai târziu."</string>
<string name="screen_notification_optin_title">"Permiteți notificările și nu pierdeți niciodată un mesaj"</string> <string name="screen_notification_optin_title">"Permiteți notificările și nu pierdeți niciodată un mesaj"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Se stabilește o conexiune securizată"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Nu a putut fi făcută o conexiune sigură la noul dispozitiv. Dispozitivele existente sunt încă în siguranță și nu trebuie să vă faceți griji cu privire la ele."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Și acum?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Încercați să vă conectați din nou cu un cod QR în cazul în care a fost o problemă de rețea."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Dacă întâmpinați aceeași problemă, încercați o altă rețea Wi-Fi sau utilizați datele mobile în loc de Wi-Fi."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Dacă nu funcționează, conectați-vă manual"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Conexiunea nu este sigură"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Vi se va cere să introduceți cele două cifre afișate pe acest dispozitiv."</string>
<string name="screen_qr_code_login_device_code_title">"Introduceți numărul de mai jos pe celălalt dispozitiv"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Gata de scanare"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Deschideți %1$s pe un dispozitiv desktop"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Faceți clic pe avatarul dumneavoastră"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Selectați %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"„Conectați un dispozitiv nou”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Scanați codul QR cu acest dispozitiv"</string>
<string name="screen_qr_code_login_initial_state_title">"Deschideți %1$s pe un alt dispozitiv pentru a obține codul QR"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Utilizați codul QR afișat pe celălalt dispozitiv."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Încercați din nou"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Cod QR greșit"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Mergeți la setările camerei"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Trebuie să acordați permisiunea ca %1$s să folosească camera dispozitivului pentru a continua."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Permiteți accesul la cameră pentru a scana codul QR"</string>
<string name="screen_qr_code_login_scanning_state_title">"Scanați codul QR"</string>
<string name="screen_qr_code_login_start_over_button">"Începeți din nou"</string>
<string name="screen_qr_code_login_unknown_error_description">"A apărut o eroare neașteptată. Vă rugăm să încercați din nou."</string>
<string name="screen_qr_code_login_verify_code_loading">"În așteptarea celuilalt dispozitiv"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Furnizorul dumneavoastră de cont poate solicita următorul cod pentru a verifica conectarea."</string>
<string name="screen_qr_code_login_verify_code_title">"Codul dumneavoastră de verificare"</string>
<string name="screen_welcome_bullet_1">"Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an."</string> <string name="screen_welcome_bullet_1">"Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an."</string>
<string name="screen_welcome_bullet_2">"Istoricul mesajelor pentru camerele criptate nu va fi disponibil în această actualizare."</string> <string name="screen_welcome_bullet_2">"Istoricul mesajelor pentru camerele criptate nu va fi disponibil în această actualizare."</string>
<string name="screen_welcome_bullet_3">"Ne-ar plăcea să auzim de la dumneavoastră, spuneți-ne ce părere aveți prin intermediul paginii de setări."</string> <string name="screen_welcome_bullet_3">"Ne-ar plăcea să auzim de la dumneavoastră, spuneți-ne ce părere aveți prin intermediul paginii de setări."</string>

View file

@ -2,33 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Вы можете изменить настройки позже."</string> <string name="screen_notification_optin_subtitle">"Вы можете изменить настройки позже."</string>
<string name="screen_notification_optin_title">"Разрешите уведомления и никогда не пропустите сообщение"</string> <string name="screen_notification_optin_title">"Разрешите уведомления и никогда не пропустите сообщение"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Установление безопасного соединения"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Не удалось установить безопасное соединение с новым устройством. Существующие устройства по-прежнему в безопасности, и вам не нужно беспокоиться о них."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Что теперь?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Попробуйте снова войти в систему с помощью QR-кода, если это была сетевая проблема"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Если вы столкнулись с той же проблемой, попробуйте сменить точку доступа Wi-Fi или используйте мобильные данные"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Если это не помогло, войдите вручную"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Соединение не защищено"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Вам нужно будет ввести две цифры, показанные на этом устройстве."</string>
<string name="screen_qr_code_login_device_code_title">"Введите показанный номер на своем другом устройстве"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Откройте %1$s на настольном устройстве"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Нажмите на свое изображение"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Выбрать %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"\"Привязать новое устройство\""</string>
<string name="screen_qr_code_login_initial_state_item_4">"Соблюдайте показанную инструкцию"</string>
<string name="screen_qr_code_login_initial_state_title">"Откройте %1$s на другом устройстве, чтобы получить QR-код"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Используйте QR-код, показанный на другом устройстве."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Повторить попытку"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Неверный QR-код"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Перейдите в настройки камеры"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Чтобы продолжить, вам необходимо разрешить %1$s использовать камеру вашего устройства."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Разрешите доступ к камере для сканирования QR-кода"</string>
<string name="screen_qr_code_login_scanning_state_title">"Сканировать QR-код"</string>
<string name="screen_qr_code_login_start_over_button">"Начать заново"</string>
<string name="screen_qr_code_login_unknown_error_description">"Произошла непредвиденная ошибка. Пожалуйста, попробуйте еще раз."</string>
<string name="screen_qr_code_login_verify_code_loading">"В ожидании другого устройства"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Поставщик учетной записи может запросить следующий код для подтверждения входа."</string>
<string name="screen_qr_code_login_verify_code_title">"Ваш код подтверждения"</string>
<string name="screen_welcome_bullet_1">"Звонки, опросы, поиск и многое другое будут добавлены позже в этом году."</string> <string name="screen_welcome_bullet_1">"Звонки, опросы, поиск и многое другое будут добавлены позже в этом году."</string>
<string name="screen_welcome_bullet_2">"История сообщений для зашифрованных комнат в этом обновлении будет недоступна."</string> <string name="screen_welcome_bullet_2">"История сообщений для зашифрованных комнат в этом обновлении будет недоступна."</string>
<string name="screen_welcome_bullet_3">"Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек."</string> <string name="screen_welcome_bullet_3">"Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек."</string>

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Svoje nastavenia môžete neskôr zmeniť."</string> <string name="screen_notification_optin_subtitle">"Svoje nastavenia môžete neskôr zmeniť."</string>
<string name="screen_notification_optin_title">"Povoľte oznámenia a nikdy nezmeškajte žiadnu správu"</string> <string name="screen_notification_optin_title">"Povoľte oznámenia a nikdy nezmeškajte žiadnu správu"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Nadväzovanie bezpečného spojenia"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"K novému zariadeniu sa nepodarilo vytvoriť bezpečné pripojenie. Vaše existujúce zariadenia sú stále v bezpečí a nemusíte sa o ne obávať."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Čo teraz?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Skúste sa znova prihlásiť pomocou QR kódu v prípade, že ide o problém so sieťou"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"Ak narazíte na rovnaký problém, vyskúšajte inú sieť Wi-Fi alebo namiesto siete Wi-Fi použite mobilné dáta"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"Ak to nefunguje, prihláste sa manuálne"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Pripojenie nie je bezpečené"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Budete požiadaní o zadanie dvoch číslic zobrazených na tomto zariadení."</string>
<string name="screen_qr_code_login_device_code_title">"Zadajte nižšie uvedené číslo na vašom druhom zariadení"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Pripravené na skenovanie"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Otvorte %1$s na stolnom zariadení"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Kliknite na svoj obrázok"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Vyberte %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"„Prepojiť nové zariadenie“"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Naskenujte QR kód pomocou tohto zariadenia"</string>
<string name="screen_qr_code_login_initial_state_title">"Ak chcete získať QR kód, otvorte %1$s na inom zariadení"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Použite QR kód zobrazený na druhom zariadení."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Skúste to znova"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Nesprávny QR kód"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Prejsť na nastavenia fotoaparátu"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Ak chcete pokračovať, musíte udeliť povolenie aplikácii %1$s používať fotoaparát vášho zariadenia."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Povoľte prístup k fotoaparátu na naskenovanie QR kódu"</string>
<string name="screen_qr_code_login_scanning_state_title">"Naskenovať QR kód"</string>
<string name="screen_qr_code_login_start_over_button">"Začať odznova"</string>
<string name="screen_qr_code_login_unknown_error_description">"Vyskytla sa neočakávaná chyba. Prosím, skúste to znova."</string>
<string name="screen_qr_code_login_verify_code_loading">"Čaká sa na vaše druhé zariadenie"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Váš poskytovateľ účtu môže požiadať o nasledujúci kód na overenie prihlásenia."</string>
<string name="screen_qr_code_login_verify_code_title">"Váš overovací kód"</string>
<string name="screen_welcome_bullet_1">"Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku."</string> <string name="screen_welcome_bullet_1">"Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku."</string>
<string name="screen_welcome_bullet_2">"História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii."</string> <string name="screen_welcome_bullet_2">"História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii."</string>
<string name="screen_welcome_bullet_3">"Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení."</string> <string name="screen_welcome_bullet_3">"Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení."</string>

View file

@ -2,7 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Du kan ändra dina inställningar senare."</string> <string name="screen_notification_optin_subtitle">"Du kan ändra dina inställningar senare."</string>
<string name="screen_notification_optin_title">"Tillåt aviseringar och missa aldrig ett meddelande"</string> <string name="screen_notification_optin_title">"Tillåt aviseringar och missa aldrig ett meddelande"</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Försök igen"</string>
<string name="screen_welcome_bullet_1">"Samtal, omröstningar, sökning och mer kommer att läggas till senare i år."</string> <string name="screen_welcome_bullet_1">"Samtal, omröstningar, sökning och mer kommer att läggas till senare i år."</string>
<string name="screen_welcome_bullet_2">"Meddelandehistorik för krypterade rum är inte tillgänglig än."</string> <string name="screen_welcome_bullet_2">"Meddelandehistorik för krypterade rum är inte tillgänglig än."</string>
<string name="screen_welcome_bullet_3">"Vi vill gärna höra från dig, låt oss veta vad du tycker via inställningssidan."</string> <string name="screen_welcome_bullet_3">"Vi vill gärna höra från dig, låt oss veta vad du tycker via inställningssidan."</string>

View file

@ -2,7 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"Ви можете змінити свої налаштування пізніше."</string> <string name="screen_notification_optin_subtitle">"Ви можете змінити свої налаштування пізніше."</string>
<string name="screen_notification_optin_title">"Дозволити сповіщення і ніколи не пропускати повідомлення"</string> <string name="screen_notification_optin_title">"Дозволити сповіщення і ніколи не пропускати повідомлення"</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Спробуйте ще раз"</string>
<string name="screen_welcome_bullet_1">"Дзвінки, опитування, пошук тощо будуть додані пізніше цього року."</string> <string name="screen_welcome_bullet_1">"Дзвінки, опитування, пошук тощо будуть додані пізніше цього року."</string>
<string name="screen_welcome_bullet_2">"Історія повідомлень для зашифрованих кімнат ще недоступна."</string> <string name="screen_welcome_bullet_2">"Історія повідомлень для зашифрованих кімнат ще недоступна."</string>
<string name="screen_welcome_bullet_3">"Ми хотіли б почути вас, розкажіть нам ваші враження та ідеї щодо застосунку на сторінці налаштувань."</string> <string name="screen_welcome_bullet_3">"Ми хотіли б почути вас, розкажіть нам ваші враження та ідеї щодо застосунку на сторінці налаштувань."</string>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"再試一次"</string>
<string name="screen_welcome_bullet_1">"通話、投票、搜尋等更多功能將在今年登場。"</string> <string name="screen_welcome_bullet_1">"通話、投票、搜尋等更多功能將在今年登場。"</string>
<string name="screen_welcome_bullet_2">"在這次的更新,您無法查看聊天室內被加密的歷史訊息。"</string> <string name="screen_welcome_bullet_2">"在這次的更新,您無法查看聊天室內被加密的歷史訊息。"</string>
<string name="screen_welcome_bullet_3">"我們很樂意聽取您的意見,請到設定頁面告訴我們您的想法。"</string> <string name="screen_welcome_bullet_3">"我們很樂意聽取您的意見,請到設定頁面告訴我們您的想法。"</string>

View file

@ -2,33 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"您可以稍后更改设置。"</string> <string name="screen_notification_optin_subtitle">"您可以稍后更改设置。"</string>
<string name="screen_notification_optin_title">"允许通知,绝不错过任何消息"</string> <string name="screen_notification_optin_title">"允许通知,绝不错过任何消息"</string>
<string name="screen_qr_code_login_connecting_subtitle">"建立安全连接"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"无法与新设备建立安全连接。您现有的设备仍然安全,无需担心。"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"现在怎么办?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"如果这是网络问题,请尝试使用二维码再次登录"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"如果你遇到同样的问题,请尝试使用不同的 WiFi 网络或使用你的移动数据代替 WiFi"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"如果不起作用,请手动登录"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"连接不安全"</string>
<string name="screen_qr_code_login_device_code_subtitle">"您会被要求输入此设备上显示的两位数。"</string>
<string name="screen_qr_code_login_device_code_title">"在您的其他设备上输入下面的数字"</string>
<string name="screen_qr_code_login_initial_state_item_1">"在桌面设备上打开 %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_2">"点击你的头像"</string>
<string name="screen_qr_code_login_initial_state_item_3">"选择 %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"「连接新设备」"</string>
<string name="screen_qr_code_login_initial_state_item_4">"按照说明进行操作"</string>
<string name="screen_qr_code_login_initial_state_title">"在另一台设备上打开 %1$s 以获取二维码"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"使用其他设备上显示的二维码。"</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"再试一次"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"二维码错误"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"转到摄像头设置"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"您需要授予 %1$s 使用设备摄像头的权限才能继续。"</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"允许摄像头权限以扫描 QR 码"</string>
<string name="screen_qr_code_login_scanning_state_title">"扫描二维码"</string>
<string name="screen_qr_code_login_start_over_button">"重新开始"</string>
<string name="screen_qr_code_login_unknown_error_description">"发生了意外错误。请再试一次。"</string>
<string name="screen_qr_code_login_verify_code_loading">"等着您的其他设备"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"您的账户提供商可能会要求您提供以下代码来验证登录。"</string>
<string name="screen_qr_code_login_verify_code_title">"您的验证码"</string>
<string name="screen_welcome_bullet_1">"今年晚些时候将增加通话、投票、搜索等功能。"</string> <string name="screen_welcome_bullet_1">"今年晚些时候将增加通话、投票、搜索等功能。"</string>
<string name="screen_welcome_bullet_2">"加密房间的消息历史记录尚不可用。"</string> <string name="screen_welcome_bullet_2">"加密房间的消息历史记录尚不可用。"</string>
<string name="screen_welcome_bullet_3">"我们很乐意听取您的意见,请通过设置页面告诉我们您的想法。"</string> <string name="screen_welcome_bullet_3">"我们很乐意听取您的意见,请通过设置页面告诉我们您的想法。"</string>

View file

@ -2,34 +2,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_notification_optin_subtitle">"You can change your settings later."</string> <string name="screen_notification_optin_subtitle">"You can change your settings later."</string>
<string name="screen_notification_optin_title">"Allow notifications and never miss a message"</string> <string name="screen_notification_optin_title">"Allow notifications and never miss a message"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Establishing a secure connection"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"A secure connection could not be made to the new device. Your existing devices are still safe and you don\'t need to worry about them."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"What now?"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_1">"Try signing in again with a QR code in case this was a network problem"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_2">"If you encounter the same problem, try a different wifi network or use your mobile data instead of wifi"</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_item_3">"If that doesnt work, sign in manually"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Connection not secure"</string>
<string name="screen_qr_code_login_device_code_subtitle">"Youll be asked to enter the two digits shown on this device."</string>
<string name="screen_qr_code_login_device_code_title">"Enter the number below on your other device"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Ready to scan"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Open %1$s on a desktop device"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Click on your avatar"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Select %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"“Link new device”"</string>
<string name="screen_qr_code_login_initial_state_item_4">"Scan the QR code with this device"</string>
<string name="screen_qr_code_login_initial_state_title">"Open %1$s on another device to get the QR code"</string>
<string name="screen_qr_code_login_invalid_scan_state_description">"Use the QR code shown on the other device."</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Try again"</string>
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Wrong QR code"</string>
<string name="screen_qr_code_login_no_camera_permission_button">"Go to camera settings"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"You need to give permission for %1$s to use your devices camera in order to continue."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Allow camera access to scan the QR code"</string>
<string name="screen_qr_code_login_scanning_state_title">"Scan the QR code"</string>
<string name="screen_qr_code_login_start_over_button">"Start over"</string>
<string name="screen_qr_code_login_unknown_error_description">"An unexpected error occurred. Please try again."</string>
<string name="screen_qr_code_login_verify_code_loading">"Waiting for your other device"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Your account provider may ask for the following code to verify the sign in."</string>
<string name="screen_qr_code_login_verify_code_title">"Your verification code"</string>
<string name="screen_welcome_bullet_1">"Calls, polls, search and more will be added later this year."</string> <string name="screen_welcome_bullet_1">"Calls, polls, search and more will be added later this year."</string>
<string name="screen_welcome_bullet_2">"Message history for encrypted rooms isnt available yet."</string> <string name="screen_welcome_bullet_2">"Message history for encrypted rooms isnt available yet."</string>
<string name="screen_welcome_bullet_3">"Wed love to hear from you, let us know what you think via the settings page."</string> <string name="screen_welcome_bullet_3">"Wed love to hear from you, let us know what you think via the settings page."</string>

View file

@ -37,7 +37,7 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
class DefaultFtueServiceTests { class DefaultFtueServiceTest {
@Test @Test
fun `given any check being false and session verification state being loaded, FtueState is Incomplete`() = runTest { fun `given any check being false and session verification state being loaded, FtueState is Incomplete`() = runTest {
val sessionVerificationService = FakeSessionVerificationService().apply { val sessionVerificationService = FakeSessionVerificationService().apply {

View file

@ -38,7 +38,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
class NotificationsOptInPresenterTests { class NotificationsOptInPresenterTest {
@get:Rule @get:Rule
val warmUpRule = WarmUpRule() val warmUpRule = WarmUpRule()

View file

@ -16,7 +16,7 @@
package io.element.android.features.ftue.impl.welcome.state package io.element.android.features.ftue.impl.welcome.state
class FakeWelcomeState : WelcomeScreenState { class InMemoryWelcomeScreenState : WelcomeScreenStore {
private var isWelcomeScreenNeeded = true private var isWelcomeScreenNeeded = true
override fun isWelcomeScreenNeeded(): Boolean { override fun isWelcomeScreenNeeded(): Boolean {

View file

@ -24,8 +24,8 @@ interface AcceptDeclineInviteView {
@Composable @Composable
fun Render( fun Render(
state: AcceptDeclineInviteState, state: AcceptDeclineInviteState,
onInviteAccepted: (RoomId) -> Unit, onAcceptInvite: (RoomId) -> Unit,
onInviteDeclined: (RoomId) -> Unit, onDeclineInvite: (RoomId) -> Unit,
modifier: Modifier, modifier: Modifier,
) )
} }

View file

@ -112,7 +112,7 @@ class AcceptDeclineInvitePresenter @Inject constructor(
trigger = JoinedRoom.Trigger.Invite, trigger = JoinedRoom.Trigger.Invite,
) )
.onSuccess { .onSuccess {
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true) notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId)
} }
.map { roomId } .map { roomId }
} }
@ -122,7 +122,7 @@ class AcceptDeclineInvitePresenter @Inject constructor(
suspend { suspend {
client.getRoom(roomId)?.use { client.getRoom(roomId)?.use {
it.leave().getOrThrow() it.leave().getOrThrow()
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true) notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId)
} }
roomId roomId
}.runCatchingUpdatingState(declinedAction) }.runCatchingUpdatingState(declinedAction)

View file

@ -36,21 +36,21 @@ import kotlin.jvm.optionals.getOrNull
@Composable @Composable
fun AcceptDeclineInviteView( fun AcceptDeclineInviteView(
state: AcceptDeclineInviteState, state: AcceptDeclineInviteState,
onInviteAccepted: (RoomId) -> Unit, onAcceptInvite: (RoomId) -> Unit,
onInviteDeclined: (RoomId) -> Unit, onDeclineInvite: (RoomId) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Box(modifier = modifier) { Box(modifier = modifier) {
AsyncActionView( AsyncActionView(
async = state.acceptAction, async = state.acceptAction,
onSuccess = onInviteAccepted, onSuccess = onAcceptInvite,
onErrorDismiss = { onErrorDismiss = {
state.eventSink(InternalAcceptDeclineInviteEvents.DismissAcceptError) state.eventSink(InternalAcceptDeclineInviteEvents.DismissAcceptError)
}, },
) )
AsyncActionView( AsyncActionView(
async = state.declineAction, async = state.declineAction,
onSuccess = onInviteDeclined, onSuccess = onDeclineInvite,
onErrorDismiss = { onErrorDismiss = {
state.eventSink(InternalAcceptDeclineInviteEvents.DismissDeclineError) state.eventSink(InternalAcceptDeclineInviteEvents.DismissDeclineError)
}, },
@ -59,10 +59,10 @@ fun AcceptDeclineInviteView(
if (invite != null) { if (invite != null) {
DeclineConfirmationDialog( DeclineConfirmationDialog(
invite = invite, invite = invite,
onConfirmClicked = { onConfirmClick = {
state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite) state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite)
}, },
onDismissClicked = { onDismissClick = {
state.eventSink(InternalAcceptDeclineInviteEvents.CancelDeclineInvite) state.eventSink(InternalAcceptDeclineInviteEvents.CancelDeclineInvite)
} }
) )
@ -75,8 +75,8 @@ fun AcceptDeclineInviteView(
@Composable @Composable
private fun DeclineConfirmationDialog( private fun DeclineConfirmationDialog(
invite: InviteData, invite: InviteData,
onConfirmClicked: () -> Unit, onConfirmClick: () -> Unit,
onDismissClicked: () -> Unit, onDismissClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val contentResource = if (invite.isDirect) { val contentResource = if (invite.isDirect) {
@ -97,8 +97,8 @@ private fun DeclineConfirmationDialog(
title = stringResource(titleResource), title = stringResource(titleResource),
submitText = stringResource(CommonStrings.action_decline), submitText = stringResource(CommonStrings.action_decline),
cancelText = stringResource(CommonStrings.action_cancel), cancelText = stringResource(CommonStrings.action_cancel),
onSubmitClicked = onConfirmClicked, onSubmitClick = onConfirmClick,
onDismiss = onDismissClicked, onDismiss = onDismissClick,
) )
} }
@ -108,7 +108,7 @@ internal fun AcceptDeclineInviteViewPreview(@PreviewParameter(AcceptDeclineInvit
ElementPreview { ElementPreview {
AcceptDeclineInviteView( AcceptDeclineInviteView(
state = state, state = state,
onInviteAccepted = {}, onAcceptInvite = {},
onInviteDeclined = {}, onDeclineInvite = {},
) )
} }

View file

@ -26,18 +26,18 @@ import io.element.android.libraries.matrix.api.core.RoomId
import javax.inject.Inject import javax.inject.Inject
@ContributesBinding(SessionScope::class) @ContributesBinding(SessionScope::class)
class AcceptDeclineInviteViewWrapper @Inject constructor() : AcceptDeclineInviteView { class DefaultAcceptDeclineInviteView @Inject constructor() : AcceptDeclineInviteView {
@Composable @Composable
override fun Render( override fun Render(
state: AcceptDeclineInviteState, state: AcceptDeclineInviteState,
onInviteAccepted: (RoomId) -> Unit, onAcceptInvite: (RoomId) -> Unit,
onInviteDeclined: (RoomId) -> Unit, onDeclineInvite: (RoomId) -> Unit,
modifier: Modifier, modifier: Modifier,
) { ) {
AcceptDeclineInviteView( AcceptDeclineInviteView(
state = state, state = state,
onInviteAccepted = onInviteAccepted, onAcceptInvite = onAcceptInvite,
onInviteDeclined = onInviteDeclined, onDeclineInvite = onDeclineInvite,
modifier = modifier modifier = modifier
) )
} }

View file

@ -5,5 +5,5 @@
<string name="screen_invites_decline_direct_chat_message">"Вы ўпэўненыя, што хочаце адмовіцца ад прыватных зносін з %1$s?"</string> <string name="screen_invites_decline_direct_chat_message">"Вы ўпэўненыя, што хочаце адмовіцца ад прыватных зносін з %1$s?"</string>
<string name="screen_invites_decline_direct_chat_title">"Адхіліць чат"</string> <string name="screen_invites_decline_direct_chat_title">"Адхіліць чат"</string>
<string name="screen_invites_empty_list">"Няма запрашэнняў"</string> <string name="screen_invites_empty_list">"Няма запрашэнняў"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) запрасіў вас"</string> <string name="screen_invites_invited_you">"%1$s (%2$s) запрасіў(-ла) вас"</string>
</resources> </resources>

View file

@ -50,15 +50,15 @@ class JoinRoomNode @AssistedInject constructor(
val state = presenter.present() val state = presenter.present()
JoinRoomView( JoinRoomView(
state = state, state = state,
onBackPressed = ::navigateUp, onBackClick = ::navigateUp,
onJoinSuccess = ::navigateUp, onJoinSuccess = ::navigateUp,
onKnockSuccess = ::navigateUp, onKnockSuccess = ::navigateUp,
modifier = modifier modifier = modifier
) )
acceptDeclineInviteView.Render( acceptDeclineInviteView.Render(
state = state.acceptDeclineInviteState, state = state.acceptDeclineInviteState,
onInviteAccepted = {}, onAcceptInvite = {},
onInviteDeclined = { navigateUp() }, onDeclineInvite = { navigateUp() },
modifier = Modifier modifier = Modifier
) )
} }

Some files were not shown because too many files have changed in this diff Show more