Merge branch 'develop' of github.com:element-hq/element-x-android into align-cta-button-on-login-flow
# Conflicts: # features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_0,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_1,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_2,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_3,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Day-0_1_null_4,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_0,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_1,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_2,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_3,NEXUS_5,1.0,en].png # tests/uitests/src/test/snapshots/images/ui_S_t[f.onboarding.impl_OnBoardingScreen_null_OnBoardingScreen-Night-0_2_null_4,NEXUS_5,1.0,en].png
This commit is contained in:
commit
f98cd5b99b
694 changed files with 6806 additions and 1630 deletions
11
.github/workflows/build.yml
vendored
11
.github/workflows/build.yml
vendored
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
- name: Configure gradle
|
||||
uses: gradle/gradle-build-action@v2.11.1
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Assemble debug APK
|
||||
|
|
@ -47,14 +47,15 @@ jobs:
|
|||
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
|
||||
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
|
||||
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
|
||||
run: ./gradlew :app:assembleDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
|
||||
run: ./gradlew :app:assembleGplayDebug :app:assembleFDroidDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload APK APKs
|
||||
if: ${{ matrix.variant == 'debug' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: elementx-debug
|
||||
path: |
|
||||
app/build/outputs/apk/debug/*.apk
|
||||
app/build/outputs/apk/gplay/debug/*.apk
|
||||
app/build/outputs/apk/fdroid/debug/*.apk
|
||||
- uses: rnkdsh/action-upload-diawi@v1.5.4
|
||||
id: diawi
|
||||
# Do not fail the whole build if Diawi upload fails
|
||||
|
|
@ -64,7 +65,7 @@ jobs:
|
|||
if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && env.token != '' }}
|
||||
with:
|
||||
token: ${{ env.token }}
|
||||
file: app/build/outputs/apk/debug/app-arm64-v8a-debug.apk
|
||||
file: app/build/outputs/apk/gplay/debug/app-gplay-arm64-v8a-debug.apk
|
||||
- name: Add or update PR comment with QR Code to download APK.
|
||||
if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && steps.diawi.conclusion == 'success' }}
|
||||
uses: NejcZdovc/comment-pr@v2
|
||||
|
|
@ -82,7 +83,7 @@ jobs:
|
|||
run: ./gradlew compileReleaseSources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Compile nightly sources
|
||||
if: ${{ matrix.variant == 'nightly' }}
|
||||
run: ./gradlew compileNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
|
||||
run: ./gradlew compileGplayNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Compile samples minimal
|
||||
if: ${{ matrix.variant == 'samples' }}
|
||||
run: ./gradlew :samples:minimal:assemble $CI_GRADLE_ARG_PROPERTIES
|
||||
|
|
|
|||
3
.github/workflows/fork-pr-notice.yml
vendored
3
.github/workflows/fork-pr-notice.yml
vendored
|
|
@ -11,7 +11,8 @@ jobs:
|
|||
welcome:
|
||||
runs-on: ubuntu-latest
|
||||
name: Welcome comment
|
||||
if: github.event.pull_request.fork != null
|
||||
# Only display it if base repo (upstream) is different from HEAD repo (possibly a fork)
|
||||
if: github.event.pull_request.base.repo.full_name != github.event.pull_request.head.repo.full_name
|
||||
steps:
|
||||
- name: Add auto-generated commit warning
|
||||
uses: actions/github-script@v7
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@ jobs:
|
|||
# No concurrency required, this is a prerequisite to other actions and should run every time.
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
|
|
|
|||
2
.github/workflows/maestro.yml
vendored
2
.github/workflows/maestro.yml
vendored
|
|
@ -53,7 +53,7 @@ jobs:
|
|||
api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }}
|
||||
# Doc says (https://github.com/mobile-dev-inc/action-maestro-cloud#android):
|
||||
# app-file should point to an x86 compatible APK file, so upload the x86_64 one (much smaller than the universal APK).
|
||||
app-file: app/build/outputs/apk/debug/app-x86_64-debug.apk
|
||||
app-file: app/build/outputs/apk/gplay/debug/app-gplay-x86_64-debug.apk
|
||||
env: |
|
||||
USERNAME=maestroelement
|
||||
PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }}
|
||||
|
|
|
|||
4
.github/workflows/nightly.yml
vendored
4
.github/workflows/nightly.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
yes n | towncrier build --version nightly
|
||||
- name: Build and upload Nightly application
|
||||
run: |
|
||||
./gradlew assembleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES
|
||||
./gradlew assembleGplayNightly appDistributionUploadGplayNightly $CI_GRADLE_ARG_PROPERTIES
|
||||
env:
|
||||
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
|
||||
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
|
||||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
- name: Additionally upload Nightly APK to browserstack for testing
|
||||
continue-on-error: true # don't block anything by this upload failing (for now)
|
||||
run: |
|
||||
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly"
|
||||
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/gplay/nightly/app-gplay-universal-nightly.apk" -F "custom_id=element-x-android-nightly"
|
||||
env:
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }}
|
||||
BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }}
|
||||
|
|
|
|||
2
.github/workflows/nightlyReports.yml
vendored
2
.github/workflows/nightlyReports.yml
vendored
|
|
@ -62,7 +62,7 @@ jobs:
|
|||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
- name: Configure gradle
|
||||
uses: gradle/gradle-build-action@v2.11.1
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Dependency analysis
|
||||
|
|
|
|||
2
.github/workflows/quality.yml
vendored
2
.github/workflows/quality.yml
vendored
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
- name: Configure gradle
|
||||
uses: gradle/gradle-build-action@v2.11.1
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Run code quality check suite
|
||||
|
|
|
|||
2
.github/workflows/recordScreenshots.yml
vendored
2
.github/workflows/recordScreenshots.yml
vendored
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
java-version: '17'
|
||||
# Add gradle cache, this should speed up the process
|
||||
- name: Configure gradle
|
||||
uses: gradle/gradle-build-action@v2.11.1
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Record screenshots
|
||||
|
|
|
|||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
|
|
@ -25,16 +25,16 @@ jobs:
|
|||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
- name: Configure gradle
|
||||
uses: gradle/gradle-build-action@v2.11.1
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
- name: Create app bundle
|
||||
env:
|
||||
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
|
||||
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
|
||||
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
|
||||
run: ./gradlew bundleRelease $CI_GRADLE_ARG_PROPERTIES
|
||||
run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload bundle as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: elementx-app-bundle-unsigned
|
||||
name: elementx-app-gplay-bundle-unsigned
|
||||
path: |
|
||||
app/build/outputs/bundle/release/app-release.aab
|
||||
app/build/outputs/bundle/gplayRelease/app-gplay-release.aab
|
||||
|
|
|
|||
2
.github/workflows/sonar.yml
vendored
2
.github/workflows/sonar.yml
vendored
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
- name: Configure gradle
|
||||
uses: gradle/gradle-build-action@v2.11.1
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: 🔊 Publish results to Sonar
|
||||
|
|
|
|||
2
.github/workflows/sync-localazy.yml
vendored
2
.github/workflows/sync-localazy.yml
vendored
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
- name: Run Localazy script
|
||||
run: ./tools/localazy/downloadStrings.sh --all
|
||||
- name: Create Pull Request for Strings
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
|
||||
commit-message: Sync Strings from Localazy
|
||||
|
|
|
|||
2
.github/workflows/sync-sas-strings.yml
vendored
2
.github/workflows/sync-sas-strings.yml
vendored
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
- name: Run SAS String script
|
||||
run: ./tools/sas/import_sas_strings.py
|
||||
- name: Create Pull Request for SAS Strings
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
commit-message: Sync SAS Strings
|
||||
title: Sync SAS Strings
|
||||
|
|
|
|||
10
.github/workflows/tests.yml
vendored
10
.github/workflows/tests.yml
vendored
|
|
@ -44,7 +44,7 @@ jobs:
|
|||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '17'
|
||||
- name: Configure gradle
|
||||
uses: gradle/gradle-build-action@v2.11.1
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
|
||||
|
|
@ -81,6 +81,8 @@ jobs:
|
|||
# https://github.com/codecov/codecov-action
|
||||
- name: ☂️ Upload coverage reports to codecov
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v3
|
||||
# with:
|
||||
# files: build/reports/kover/xml/report.xml
|
||||
uses: codecov/codecov-action@v4
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
# with:
|
||||
# files: build/reports/kover/xml/report.xml
|
||||
|
|
|
|||
1
.idea/dictionaries/shared.xml
generated
1
.idea/dictionaries/shared.xml
generated
|
|
@ -16,6 +16,7 @@
|
|||
<w>snackbar</w>
|
||||
<w>swipeable</w>
|
||||
<w>textfields</w>
|
||||
<w>tombstoned</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
|
|
|
|||
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.21" />
|
||||
<option name="version" value="1.9.22" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -9,5 +9,5 @@ appId: ${APP_ID}
|
|||
index: 1
|
||||
- takeScreenshot: build/maestro/330-createAndDeleteDM
|
||||
- tapOn: "maestroelement2"
|
||||
- tapOn: "Leave room"
|
||||
- tapOn: "Leave conversation"
|
||||
- tapOn: "Leave"
|
||||
|
|
|
|||
70
CHANGES.md
70
CHANGES.md
|
|
@ -1,3 +1,73 @@
|
|||
Changes in Element X v0.4.2 (2024-01-31)
|
||||
========================================
|
||||
|
||||
Matrix SDK 🦀 v0.1.95
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Add 'send private read receipts' option in advanced settings ([#2204](https://github.com/element-hq/element-x-android/issues/2204))
|
||||
- Send typing notification ([#2240](https://github.com/element-hq/element-x-android/issues/2240)). Disabling the sending of typing notification and rendering typing notification will come soon.
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Make the room settings screen update automatically when new room info (name, avatar, topic) is available. ([#921](https://github.com/element-hq/element-x-android/issues/921))
|
||||
- Update timeline items' read receipts when the room members info is loaded. ([#2176](https://github.com/element-hq/element-x-android/issues/2176))
|
||||
- Edited text message bubbles should resize when edited ([#2260](https://github.com/element-hq/element-x-android/issues/2260))
|
||||
- Ensure login and password exclude `\n` ([#2263](https://github.com/element-hq/element-x-android/issues/2263))
|
||||
- Room list Ensure the indicators stay grey if the global setting is set to mention only and a regular message is received. ([#2282](https://github.com/element-hq/element-x-android/issues/2282))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- Add a special logging configuration for nightlies so we can get more detailed info for existing issues. ([#+add-special-tracing-configuration-for-nightlies](https://github.com/element-hq/element-x-android/issues/+add-special-tracing-configuration-for-nightlies))
|
||||
- Try mitigating unexpected logouts by making getting/storing session data use a Mutex for synchronization.
|
||||
Also added some more logs so we can understand exactly where it's failing. ([#+try-mitigating-unexpected-logouts](https://github.com/element-hq/element-x-android/issues/+try-mitigating-unexpected-logouts))
|
||||
- Upgrade Material3 Compose to `1.2.0-beta02`.
|
||||
There is also a constraint on a transitive Compose Foundation dependency version (1.6.0-beta02) that fixes the timeline scrolling issue. ([#0-beta02](https://github.com/element-hq/element-x-android/issues/0-beta02))
|
||||
- Disambiguate display name in the timeline. ([#2215](https://github.com/element-hq/element-x-android/issues/2215))
|
||||
- Disambiguate display name in notifications ([#2224](https://github.com/element-hq/element-x-android/issues/2224))
|
||||
- Remove room creation, self-join of room creator and 'this is the beginning of X' timeline items for DMs. ([#2217](https://github.com/element-hq/element-x-android/issues/2217))
|
||||
- Encrypt databases used by the Rust SDK on Nightly and Debug builds. ([#2219](https://github.com/element-hq/element-x-android/issues/2219))
|
||||
- Fallback to UnifiedPush (if available) if the PlayServices are not installed on the device. ([#2248](https://github.com/element-hq/element-x-android/issues/2248))
|
||||
- Add "Report a problem" button to the onboarding screen ([#2275](https://github.com/element-hq/element-x-android/issues/2275))
|
||||
- Add in app logs viewer to the "Report a problem" screen. ([#2276](https://github.com/element-hq/element-x-android/issues/2276))
|
||||
|
||||
|
||||
Changes in Element X v0.4.1 (2024-01-17)
|
||||
========================================
|
||||
|
||||
Features ✨
|
||||
----------
|
||||
- Render m.sticker events ([#1949](https://github.com/element-hq/element-x-android/issues/1949))
|
||||
- Add support for sending images from the keyboard ([#1977](https://github.com/element-hq/element-x-android/issues/1977))
|
||||
- Added support for MSC4027 (render custom images in reactions) ([#2159](https://github.com/element-hq/element-x-android/issues/2159))
|
||||
|
||||
Bugfixes 🐛
|
||||
----------
|
||||
- Fix crash sending image with latest Posthog because of an usage of an internal Android method. ([#+crash-sending-image-with-latest-posthog](https://github.com/element-hq/element-x-android/issues/+crash-sending-image-with-latest-posthog))
|
||||
- Make sure the media viewer tries the main url first (if not empty) then the thumbnail url and then not open if both are missing instead of failing with an error dialog ([#1949](https://github.com/element-hq/element-x-android/issues/1949))
|
||||
- Fix room transition animation happens twice. ([#2084](https://github.com/element-hq/element-x-android/issues/2084))
|
||||
- Disable ability to send reaction if the user does not have the permission to. ([#2093](https://github.com/element-hq/element-x-android/issues/2093))
|
||||
- Trim whitespace at the end of messages to ensure we render the right content. ([#2099](https://github.com/element-hq/element-x-android/issues/2099))
|
||||
- Fix crashes in room list when the last message for a room was an extremely long one (several thousands of characters) with no line breaks. ([#2105](https://github.com/element-hq/element-x-android/issues/2105))
|
||||
- Disable rasterisation of Vector XMLs, which was causing crashes on API 23. ([#2124](https://github.com/element-hq/element-x-android/issues/2124))
|
||||
- Use `SubomposeLayout` for `ContentAvoidingLayout` to prevent wrong measurements in the layout process, leading to cut-off text messages in the timeline. ([#2155](https://github.com/element-hq/element-x-android/issues/2155))
|
||||
- Improve rendering of voice messages in the timeline in large displays ([#2156](https://github.com/element-hq/element-x-android/issues/2156))
|
||||
- Fix no indication that user list is loading when inviting to room. ([#2172](https://github.com/element-hq/element-x-android/issues/2172))
|
||||
- Hide keyboard when tapping on a message in the timeline. ([#2182](https://github.com/element-hq/element-x-android/issues/2182))
|
||||
- Mention selector gets stuck when quickly deleting the prompt. ([#2192](https://github.com/element-hq/element-x-android/issues/2192))
|
||||
- Hide verbose state events from the timeline ([#2216](https://github.com/element-hq/element-x-android/issues/2216))
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- Only apply `com.autonomousapps.dependency-analysis` plugin in those modules that need it. ([#+only-apply-dependency-analysis-plugin-where-needed](https://github.com/element-hq/element-x-android/issues/+only-apply-dependency-analysis-plugin-where-needed))
|
||||
- Migrate to Kover 0.7.X ([#1782](https://github.com/element-hq/element-x-android/issues/1782))
|
||||
- Remove extra logout screen. ([#2072](https://github.com/element-hq/element-x-android/issues/2072))
|
||||
- Handle `MembershipChange.NONE` rendering in the timeline. ([#2102](https://github.com/element-hq/element-x-android/issues/2102))
|
||||
- Remove extra previews for timestamp view with 'document' case ([#2127](https://github.com/element-hq/element-x-android/issues/2127))
|
||||
- Bump AGP version to 8.2.0 ([#2142](https://github.com/element-hq/element-x-android/issues/2142))
|
||||
- Replace 'leave room' text with 'leave conversation' for DMs. ([#2218](https://github.com/element-hq/element-x-android/issues/2218))
|
||||
|
||||
|
||||
Changes in Element X v0.4.0 (2023-12-22)
|
||||
========================================
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ import com.android.build.api.variant.FilterConfiguration.FilterType.ABI
|
|||
import extension.allFeaturesImpl
|
||||
import extension.allLibrariesImpl
|
||||
import extension.allServicesImpl
|
||||
import extension.gitBranchName
|
||||
import extension.gitRevision
|
||||
import extension.koverDependencies
|
||||
import extension.setupKover
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
|
||||
plugins {
|
||||
|
|
@ -36,6 +40,8 @@ plugins {
|
|||
// id("com.google.gms.google-services")
|
||||
}
|
||||
|
||||
setupKover()
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.x"
|
||||
|
||||
|
|
@ -50,6 +56,9 @@ android {
|
|||
abiFilters += listOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
|
||||
}
|
||||
|
||||
buildConfigField("String", "GIT_REVISION", "\"${gitRevision()}\"")
|
||||
buildConfigField("String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\"")
|
||||
|
||||
// Ref: https://developer.android.com/studio/build/configure-apk-splits.html#configure-abi-split
|
||||
splits {
|
||||
// Configures multiple APKs based on ABI.
|
||||
|
|
@ -69,7 +78,7 @@ android {
|
|||
}
|
||||
|
||||
signingConfigs {
|
||||
named("debug") {
|
||||
getByName("debug") {
|
||||
keyAlias = "androiddebugkey"
|
||||
keyPassword = "android"
|
||||
storeFile = file("./signature/debug.keystore")
|
||||
|
|
@ -87,13 +96,13 @@ android {
|
|||
}
|
||||
|
||||
buildTypes {
|
||||
named("debug") {
|
||||
getByName("debug") {
|
||||
resValue("string", "app_name", "Element X dbg")
|
||||
applicationIdSuffix = ".debug"
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
|
||||
named("release") {
|
||||
getByName("release") {
|
||||
resValue("string", "app_name", "Element X")
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
|
||||
|
|
@ -124,7 +133,7 @@ android {
|
|||
// We upload the universal APK to fix this error:
|
||||
// "App Distribution found more than 1 output file for this variant.
|
||||
// Please contact firebase-support@google.com for help using APK splits with App Distribution."
|
||||
artifactPath = "$rootDir/app/build/outputs/apk/nightly/app-universal-nightly.apk"
|
||||
artifactPath = "$rootDir/app/build/outputs/apk/gplay/nightly/app-gplay-universal-nightly.apk"
|
||||
// artifactType = "AAB"
|
||||
// artifactPath = "$rootDir/app/build/outputs/bundle/nightly/app-nightly.aab"
|
||||
// This file will be generated by the GitHub action
|
||||
|
|
@ -143,6 +152,20 @@ android {
|
|||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
flavorDimensions += "store"
|
||||
productFlavors {
|
||||
create("gplay") {
|
||||
dimension = "store"
|
||||
isDefault = true
|
||||
buildConfigField("String", "SHORT_FLAVOR_DESCRIPTION", "\"G\"")
|
||||
buildConfigField("String", "FLAVOR_DESCRIPTION", "\"GooglePlay\"")
|
||||
}
|
||||
create("fdroid") {
|
||||
dimension = "store"
|
||||
buildConfigField("String", "SHORT_FLAVOR_DESCRIPTION", "\"F\"")
|
||||
buildConfigField("String", "FLAVOR_DESCRIPTION", "\"FDroid\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
|
|
@ -222,6 +245,11 @@ dependencies {
|
|||
implementation(projects.appconfig)
|
||||
anvil(projects.anvilcodegen)
|
||||
|
||||
// Comment to not include firebase in the project
|
||||
"gplayImplementation"(projects.libraries.pushproviders.firebase)
|
||||
// Comment to not include unified push in the project
|
||||
implementation(projects.libraries.pushproviders.unifiedpush)
|
||||
|
||||
implementation(libs.appyx.core)
|
||||
implementation(libs.androidx.splash)
|
||||
implementation(libs.androidx.core)
|
||||
|
|
@ -251,4 +279,5 @@ dependencies {
|
|||
testImplementation(projects.libraries.matrix.test)
|
||||
|
||||
ksp(libs.showkase.processor)
|
||||
koverDependencies()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package io.element.android.x.di
|
|||
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import io.element.android.features.lockscreen.api.LockScreenService
|
||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||
import io.element.android.features.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.features.rageshake.api.reporter.BugReporter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.di.AppScope
|
||||
|
|
@ -34,5 +34,5 @@ interface AppBindings {
|
|||
|
||||
fun lockScreenService(): LockScreenService
|
||||
|
||||
fun preferencesStore(): PreferencesStore
|
||||
fun preferencesStore(): AppPreferencesStore
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,16 +85,10 @@ object AppModule {
|
|||
lowPrivacyLoggingEnabled = false,
|
||||
versionName = BuildConfig.VERSION_NAME,
|
||||
versionCode = BuildConfig.VERSION_CODE,
|
||||
// BuildConfig.GIT_REVISION,
|
||||
gitRevision = "TODO",
|
||||
// BuildConfig.GIT_REVISION_DATE,
|
||||
gitRevisionDate = "TODO",
|
||||
// BuildConfig.GIT_BRANCH_NAME,
|
||||
gitBranchName = "TODO",
|
||||
// BuildConfig.FLAVOR_DESCRIPTION,
|
||||
flavorDescription = "TODO",
|
||||
// BuildConfig.SHORT_FLAVOR_DESCRIPTION,
|
||||
flavorShortDescription = "TODO",
|
||||
gitRevision = BuildConfig.GIT_REVISION,
|
||||
gitBranchName = BuildConfig.GIT_BRANCH_NAME,
|
||||
flavorDescription = BuildConfig.FLAVOR_DESCRIPTION,
|
||||
flavorShortDescription = BuildConfig.SHORT_FLAVOR_DESCRIPTION,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ interface SessionComponent : NodeFactoriesBindings {
|
|||
interface Builder {
|
||||
@BindsInstance
|
||||
fun client(matrixClient: MatrixClient): Builder
|
||||
|
||||
fun build(): SessionComponent
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ fun logApplicationInfo() {
|
|||
append(BuildConfig.VERSION_CODE)
|
||||
append(") - ")
|
||||
append(BuildConfig.BUILD_TYPE)
|
||||
append(" / ")
|
||||
append(BuildConfig.FLAVOR)
|
||||
}
|
||||
// TODO Get SDK version somehow
|
||||
val sdkVersion = "SDK VERSION (TODO)"
|
||||
|
|
@ -37,6 +39,7 @@ fun logApplicationInfo() {
|
|||
Timber.d("----------------------------------------------------------------")
|
||||
Timber.d("----------------------------------------------------------------")
|
||||
Timber.d(" Application version: $appVersion")
|
||||
Timber.d(" Git SHA: ${BuildConfig.GIT_REVISION}")
|
||||
Timber.d(" SDK version: $sdkVersion")
|
||||
Timber.d(" Local time: $date")
|
||||
Timber.d("----------------------------------------------------------------")
|
||||
|
|
|
|||
|
|
@ -46,8 +46,13 @@ class TracingInitializer : Initializer<Unit> {
|
|||
writesToFilesConfiguration = WriteToFilesConfiguration.Disabled
|
||||
)
|
||||
} else {
|
||||
val config = if (BuildConfig.BUILD_TYPE == "nightly") {
|
||||
TracingFilterConfigurations.nightly
|
||||
} else {
|
||||
TracingFilterConfigurations.release
|
||||
}
|
||||
TracingConfiguration(
|
||||
filterConfiguration = TracingFilterConfigurations.release,
|
||||
filterConfiguration = config,
|
||||
writesToLogcat = false,
|
||||
writesToFilesConfiguration = WriteToFilesConfiguration.Enabled(
|
||||
directory = bugReporter.logDirectory().absolutePath,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ dependencies {
|
|||
implementation(libs.coil)
|
||||
|
||||
implementation(projects.features.ftue.api)
|
||||
implementation(projects.features.viewfolder.api)
|
||||
|
||||
implementation(projects.services.apperror.impl)
|
||||
implementation(projects.services.appnavstate.api)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.bumble.appyx.core.lifecycle.subscribe
|
|||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.plugin.Plugin
|
||||
import com.bumble.appyx.core.plugin.plugins
|
||||
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||
import com.bumble.appyx.navmodel.backstack.operation.push
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -54,6 +55,10 @@ class NotLoggedInFlowNode @AssistedInject constructor(
|
|||
buildContext = buildContext,
|
||||
plugins = plugins,
|
||||
) {
|
||||
interface Callback : Plugin {
|
||||
fun onOpenBugReport()
|
||||
}
|
||||
|
||||
override fun onBuilt() {
|
||||
super.onBuilt()
|
||||
lifecycle.subscribe(
|
||||
|
|
@ -91,6 +96,10 @@ class NotLoggedInFlowNode @AssistedInject constructor(
|
|||
override fun onOpenDeveloperSettings() {
|
||||
backstack.push(NavTarget.ConfigureTracing)
|
||||
}
|
||||
|
||||
override fun onReportProblem() {
|
||||
plugins<Callback>().forEach { it.onOpenBugReport() }
|
||||
}
|
||||
}
|
||||
onBoardingEntryPoint
|
||||
.nodeBuilder(this, buildContext)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import io.element.android.features.login.api.oidc.OidcAction
|
|||
import io.element.android.features.login.api.oidc.OidcActionFlow
|
||||
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
||||
import io.element.android.features.signedout.api.SignedOutEntryPoint
|
||||
import io.element.android.features.viewfolder.api.ViewFolderEntryPoint
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
import io.element.android.libraries.architecture.BaseFlowNode
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
|
|
@ -70,6 +71,7 @@ class RootFlowNode @AssistedInject constructor(
|
|||
private val matrixClientsHolder: MatrixClientsHolder,
|
||||
private val presenter: RootPresenter,
|
||||
private val bugReportEntryPoint: BugReportEntryPoint,
|
||||
private val viewFolderEntryPoint: ViewFolderEntryPoint,
|
||||
private val signedOutEntryPoint: SignedOutEntryPoint,
|
||||
private val intentResolver: IntentResolver,
|
||||
private val oidcActionFlow: OidcActionFlow,
|
||||
|
|
@ -141,7 +143,7 @@ class RootFlowNode @AssistedInject constructor(
|
|||
onSuccess(sessionId)
|
||||
}
|
||||
.onFailure {
|
||||
Timber.v("Failed to restore session $sessionId")
|
||||
Timber.e(it, "Failed to restore session $sessionId")
|
||||
onFailure()
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +196,11 @@ class RootFlowNode @AssistedInject constructor(
|
|||
|
||||
@Parcelize
|
||||
data object BugReport : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class ViewLogs(
|
||||
val rootPath: String,
|
||||
) : NavTarget
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
|
|
@ -210,7 +217,14 @@ class RootFlowNode @AssistedInject constructor(
|
|||
}
|
||||
createNode<LoggedInAppScopeFlowNode>(buildContext, plugins = listOf(inputs, callback))
|
||||
}
|
||||
NavTarget.NotLoggedInFlow -> createNode<NotLoggedInFlowNode>(buildContext)
|
||||
NavTarget.NotLoggedInFlow -> {
|
||||
val callback = object : NotLoggedInFlowNode.Callback {
|
||||
override fun onOpenBugReport() {
|
||||
backstack.push(NavTarget.BugReport)
|
||||
}
|
||||
}
|
||||
createNode<NotLoggedInFlowNode>(buildContext, plugins = listOf(callback))
|
||||
}
|
||||
is NavTarget.SignedOutFlow -> {
|
||||
signedOutEntryPoint.nodeBuilder(this, buildContext)
|
||||
.params(
|
||||
|
|
@ -226,12 +240,31 @@ class RootFlowNode @AssistedInject constructor(
|
|||
override fun onBugReportSent() {
|
||||
backstack.pop()
|
||||
}
|
||||
|
||||
override fun onViewLogs(basePath: String) {
|
||||
backstack.push(NavTarget.ViewLogs(rootPath = basePath))
|
||||
}
|
||||
}
|
||||
bugReportEntryPoint
|
||||
.nodeBuilder(this, buildContext)
|
||||
.callback(callback)
|
||||
.build()
|
||||
}
|
||||
is NavTarget.ViewLogs -> {
|
||||
val callback = object : ViewFolderEntryPoint.Callback {
|
||||
override fun onDone() {
|
||||
backstack.pop()
|
||||
}
|
||||
}
|
||||
val params = ViewFolderEntryPoint.Params(
|
||||
rootPath = navTarget.rootPath,
|
||||
)
|
||||
viewFolderEntryPoint
|
||||
.nodeBuilder(this, buildContext)
|
||||
.params(params)
|
||||
.callback(callback)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,14 +118,7 @@ class RoomLoadedFlowNode @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun fetchRoomMembers() = lifecycleScope.launch {
|
||||
val room = inputs.room
|
||||
room.updateMembers()
|
||||
.onFailure {
|
||||
Timber.e(it, "Fail to fetch members for room ${room.roomId}")
|
||||
}
|
||||
.onSuccess {
|
||||
Timber.v("Success fetching members for room ${room.roomId}")
|
||||
}
|
||||
inputs.room.updateMembers()
|
||||
}
|
||||
|
||||
private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ allprojects {
|
|||
config.from(files("$rootDir/tools/detekt/detekt.yml"))
|
||||
}
|
||||
dependencies {
|
||||
detektPlugins("io.nlopez.compose.rules:detekt:0.3.9")
|
||||
detektPlugins("io.nlopez.compose.rules:detekt:0.3.11")
|
||||
}
|
||||
|
||||
// KtLint
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Fix crash sending image with latest Posthog because of an usage of an internal Android method.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Only apply `com.autonomousapps.dependency-analysis` plugin in those modules that need it.
|
||||
1
changelog.d/+remove-compose-foundation-workaround.misc
Normal file
1
changelog.d/+remove-compose-foundation-workaround.misc
Normal file
|
|
@ -0,0 +1 @@
|
|||
Remove Compose Foundation version pinning workaround. This was done to avoid a bug introduced in the default foundation version used by the material3 library, but that has already been fixed.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Migrate to Kover 0.7.X
|
||||
|
|
@ -1 +0,0 @@
|
|||
Make sure the media viewer tries the main url first (if not empty) then the thumbnail url and then not open if both are missing instead of failing with an error dialog
|
||||
|
|
@ -1 +0,0 @@
|
|||
Render m.sticker events
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add support for sending images from the keyboard
|
||||
|
|
@ -1 +0,0 @@
|
|||
Remove extra logout screen.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix room transition animation happens twice.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Disable ability to send reaction if the user does not have the permission to.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Trim whitespace at the end of messages to ensure we render the right content.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Handle `MembershipChange.NONE` rendering in the timeline.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix crashes in room list when the last message for a room was an extremely long one (several thousands of characters) with no line breaks.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Disable rasterisation of Vector XMLs, which was causing crashes on API 23.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Remove extra previews for timestamp view with 'document' case
|
||||
|
|
@ -1 +0,0 @@
|
|||
Bump AGP version to 8.2.0
|
||||
|
|
@ -1 +0,0 @@
|
|||
Use `SubomposeLayout` for `ContentAvoidingLayout` to prevent wrong measurements in the layout process, leading to cut-off text messages in the timeline.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Improve rendering of voice messages in the timeline in large displays
|
||||
|
|
@ -1 +0,0 @@
|
|||
Added support for MSC4027 (render custom images in reactions)
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix no indication that user list is loading when inviting to room.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Hide keyboard when tapping on a message in the timeline.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Mention selector gets stuck when quickly deleting the prompt.
|
||||
1
changelog.d/2304.bugfix
Normal file
1
changelog.d/2304.bugfix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix crash after unregistering UnifiedPush distributor
|
||||
1
changelog.d/2316.bugfix
Normal file
1
changelog.d/2316.bugfix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add missing device id to settings screen.
|
||||
1
changelog.d/2333.feature
Normal file
1
changelog.d/2333.feature
Normal file
|
|
@ -0,0 +1 @@
|
|||
Allow joining unencrypted video calls in non encrypted rooms.
|
||||
|
|
@ -4,13 +4,43 @@ This document explains how to install Element X Android from a Github Release.
|
|||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Requirements](#requirements)
|
||||
* [Steps](#steps)
|
||||
* [I already have the application on my phone](#i-already-have-the-application-on-my-phone)
|
||||
* [Installing the universal APK](#installing-the-universal-apk)
|
||||
* [Instructions](#instructions)
|
||||
* [Steps](#steps)
|
||||
* [I already have the application on my phone](#i-already-have-the-application-on-my-phone)
|
||||
* [Installing from the App Bundle](#installing-from-the-app-bundle)
|
||||
* [Requirements](#requirements)
|
||||
* [Steps](#steps)
|
||||
* [I already have the application on my phone](#i-already-have-the-application-on-my-phone)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
## Requirements
|
||||
## Installing the universal APK
|
||||
|
||||
### Instructions
|
||||
|
||||
The easiest way to install the application from a GitHub release is to use the universal APK which is attached to the release. This APK is compatible with all Android devices, but it is not optimized for any of them. So it may not be as fast as it could be on your device, and it may not be as small as it could be.
|
||||
|
||||
Alternatively, you can generate an APK that is optimized for your device. This is explained in the next section.
|
||||
|
||||
### Steps
|
||||
|
||||
- Open the GitHub release that you want to install from using the Web browser of your phone.
|
||||
- Download the APK
|
||||
- Open the APK file from the download notification, or from the file manager
|
||||
- Follow the steps to install the application
|
||||
|
||||
### I already have the application on my phone
|
||||
|
||||
If the application was already installed on your phone, there are several cases:
|
||||
|
||||
- it was installed from the PlayStore, you can install the universal APK as long as the version is more recent. The existing data should not be lost.
|
||||
- it was installed from a previous GitHub release, this is like an application upgrade.
|
||||
- it was installed from a more recent GitHub release, or from the PlayStore with a later version, you will have to uninstall it first.
|
||||
|
||||
## Installing from the App Bundle
|
||||
|
||||
### Requirements
|
||||
|
||||
The Github release will contain an Android App Bundle (with `aab` extension) file, unlike in the Element Android project where releases directly provide the APKs. So there are some steps to perform to generate and sign App Bundle APKs. An APK suitable for the targeted device will then be generated.
|
||||
|
||||
|
|
@ -31,7 +61,7 @@ You will also need to install [bundletool](https://developer.android.com/studio/
|
|||
brew install bundletool
|
||||
```
|
||||
|
||||
## Steps
|
||||
### Steps
|
||||
|
||||
1. Open the GitHub release that you want to install from https://github.com/element-hq/element-x-android/releases
|
||||
2. Download the asset `app-release-signed.aab`
|
||||
|
|
@ -55,7 +85,7 @@ bundletool install-apks --apks=./tmp/elementx.apks
|
|||
|
||||
That's it, the application should be installed on your device, you can start it from the launcher icon.
|
||||
|
||||
## I already have the application on my phone
|
||||
### I already have the application on my phone
|
||||
|
||||
If the application was already installed on your phone, there are several cases:
|
||||
|
||||
|
|
|
|||
2
fastlane/metadata/android/en-US/changelogs/40004010.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40004010.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: Mainly bug fixes.
|
||||
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||
2
fastlane/metadata/android/en-US/changelogs/40004020.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/40004020.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: be able to send a problem from the first screen, and add an internal log viewer. Be able to send private read receipt, send typing notification, improve performance.
|
||||
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_settings_share_data">"Condividi dati statistici"</string>
|
||||
<string name="screen_analytics_settings_help_us_improve">"Condividi dati di utilizzo anonimi per aiutarci a identificare problemi."</string>
|
||||
<string name="screen_analytics_settings_read_terms">"Puoi leggere tutti i nostri termini %1$s."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"qui"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_prompt_data_usage">"Non registreremo né profileremo alcun dato personale"</string>
|
||||
<string name="screen_analytics_prompt_help_us_improve">"Condividi dati di utilizzo anonimi per aiutarci a identificare problemi."</string>
|
||||
<string name="screen_analytics_prompt_read_terms">"Puoi leggere tutti i nostri termini %1$s."</string>
|
||||
<string name="screen_analytics_prompt_read_terms_content_link">"qui"</string>
|
||||
<string name="screen_analytics_prompt_settings">"Puoi disattivarlo in qualsiasi momento"</string>
|
||||
<string name="screen_analytics_prompt_third_party_sharing">"Non condivideremo i tuoi dati con terze parti"</string>
|
||||
<string name="screen_analytics_prompt_title">"Aiutaci a migliorare %1$s"</string>
|
||||
</resources>
|
||||
|
|
@ -45,7 +45,7 @@ import io.element.android.features.call.CallForegroundService
|
|||
import io.element.android.features.call.CallType
|
||||
import io.element.android.features.call.di.CallBindings
|
||||
import io.element.android.features.call.utils.CallIntentDataParser
|
||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||
import io.element.android.features.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator {
|
|||
|
||||
@Inject lateinit var callIntentDataParser: CallIntentDataParser
|
||||
@Inject lateinit var presenterFactory: CallScreenPresenter.Factory
|
||||
@Inject lateinit var preferencesStore: PreferencesStore
|
||||
@Inject lateinit var appPreferencesStore: AppPreferencesStore
|
||||
|
||||
private lateinit var presenter: CallScreenPresenter
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator {
|
|||
|
||||
setContent {
|
||||
val theme by remember {
|
||||
preferencesStore.getThemeFlow().mapToTheme()
|
||||
appPreferencesStore.getThemeFlow().mapToTheme()
|
||||
}
|
||||
.collectAsState(initial = Theme.System)
|
||||
val state = presenter.present()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.call.utils
|
|||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.appconfig.ElementCallConfig
|
||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||
import io.element.android.features.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -31,7 +31,7 @@ import javax.inject.Inject
|
|||
@ContributesBinding(AppScope::class)
|
||||
class DefaultCallWidgetProvider @Inject constructor(
|
||||
private val matrixClientsProvider: MatrixClientProvider,
|
||||
private val preferencesStore: PreferencesStore,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val callWidgetSettingsProvider: CallWidgetSettingsProvider,
|
||||
) : CallWidgetProvider {
|
||||
override suspend fun getWidget(
|
||||
|
|
@ -42,8 +42,8 @@ class DefaultCallWidgetProvider @Inject constructor(
|
|||
theme: String?,
|
||||
): Result<Pair<MatrixWidgetDriver, String>> = runCatching {
|
||||
val room = matrixClientsProvider.getOrRestore(sessionId).getOrThrow().getRoom(roomId) ?: error("Room not found")
|
||||
val baseUrl = preferencesStore.getCustomElementCallBaseUrlFlow().firstOrNull() ?: ElementCallConfig.DEFAULT_BASE_URL
|
||||
val widgetSettings = callWidgetSettingsProvider.provide(baseUrl)
|
||||
val baseUrl = appPreferencesStore.getCustomElementCallBaseUrlFlow().firstOrNull() ?: ElementCallConfig.DEFAULT_BASE_URL
|
||||
val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = room.isEncrypted)
|
||||
val callUrl = room.generateWidgetWebViewUrl(widgetSettings, clientId, languageTag, theme).getOrThrow()
|
||||
room.getWidgetDriver(widgetSettings).getOrThrow() to callUrl
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="call_foreground_service_channel_title_android">"Folyamatban lévő hívás"</string>
|
||||
<string name="call_foreground_service_message_android">"Koppintson a híváshoz való visszatéréshez"</string>
|
||||
<string name="call_foreground_service_message_android">"Koppints a híváshoz való visszatéréshez"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Hívás folyamatban"</string>
|
||||
</resources>
|
||||
|
|
|
|||
6
features/call/src/main/res/values-it/translations.xml
Normal file
6
features/call/src/main/res/values-it/translations.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="call_foreground_service_channel_title_android">"Chiamata in corso"</string>
|
||||
<string name="call_foreground_service_message_android">"Tocca per tornare alla chiamata"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Chiamata in corso"</string>
|
||||
</resources>
|
||||
|
|
@ -2,5 +2,5 @@
|
|||
<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_message_android">"Коснитесь, чтобы вернуться к вызову"</string>
|
||||
<string name="call_foreground_service_title_android">"Идёт вызов"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Идёт вызов"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
package io.element.android.features.call.utils
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||
import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore
|
||||
import io.element.android.features.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.featureflag.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.libraries.matrix.api.MatrixClientProvider
|
||||
import io.element.android.libraries.matrix.api.widget.CallWidgetSettingsProvider
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
|
|
@ -94,14 +94,14 @@ class DefaultCallWidgetProviderTest {
|
|||
val client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
val preferencesStore = InMemoryPreferencesStore().apply {
|
||||
val preferencesStore = InMemoryAppPreferencesStore().apply {
|
||||
setCustomElementCallBaseUrl("https://custom.element.io")
|
||||
}
|
||||
val settingsProvider = FakeCallWidgetSettingsProvider()
|
||||
val provider = createProvider(
|
||||
matrixClientProvider = FakeMatrixClientProvider { Result.success(client) },
|
||||
callWidgetSettingsProvider = settingsProvider,
|
||||
preferencesStore = preferencesStore,
|
||||
appPreferencesStore = preferencesStore,
|
||||
)
|
||||
provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme")
|
||||
|
||||
|
|
@ -110,11 +110,11 @@ class DefaultCallWidgetProviderTest {
|
|||
|
||||
private fun createProvider(
|
||||
matrixClientProvider: MatrixClientProvider = FakeMatrixClientProvider(),
|
||||
preferencesStore: PreferencesStore = InMemoryPreferencesStore(),
|
||||
appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
callWidgetSettingsProvider: CallWidgetSettingsProvider = FakeCallWidgetSettingsProvider()
|
||||
) = DefaultCallWidgetProvider(
|
||||
matrixClientProvider,
|
||||
preferencesStore,
|
||||
appPreferencesStore,
|
||||
callWidgetSettingsProvider,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
import io.element.android.libraries.matrix.ui.components.CheckableMatrixUserRow
|
||||
import io.element.android.libraries.matrix.ui.components.CheckableUnresolvedUserRow
|
||||
import io.element.android.libraries.matrix.ui.components.CheckableUserRow
|
||||
import io.element.android.libraries.matrix.ui.components.CheckableUserRowData
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
import io.element.android.libraries.usersearch.api.UserSearchResult
|
||||
|
||||
@Composable
|
||||
|
|
@ -36,23 +37,24 @@ fun SearchMultipleUsersResultItem(
|
|||
onCheckedChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (searchResult.isUnresolved) {
|
||||
CheckableUnresolvedUserRow(
|
||||
checked = isUserSelected,
|
||||
modifier = modifier,
|
||||
val data = if (searchResult.isUnresolved) {
|
||||
CheckableUserRowData.Unresolved(
|
||||
avatarData = searchResult.matrixUser.getAvatarData(AvatarSize.UserListItem),
|
||||
id = searchResult.matrixUser.userId.value,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
} else {
|
||||
CheckableMatrixUserRow(
|
||||
checked = isUserSelected,
|
||||
modifier = modifier,
|
||||
matrixUser = searchResult.matrixUser,
|
||||
avatarSize = AvatarSize.UserListItem,
|
||||
onCheckedChange = onCheckedChange,
|
||||
CheckableUserRowData.Resolved(
|
||||
name = searchResult.matrixUser.getBestName(),
|
||||
subtext = if (searchResult.matrixUser.displayName.isNullOrEmpty()) null else searchResult.matrixUser.userId.value,
|
||||
avatarData = searchResult.matrixUser.getAvatarData(AvatarSize.UserListItem),
|
||||
)
|
||||
}
|
||||
CheckableUserRow(
|
||||
checked = isUserSelected,
|
||||
modifier = modifier,
|
||||
data = data,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_create_room_action_create_room">"Nuova stanza"</string>
|
||||
<string name="screen_create_room_action_invite_people">"Invita persone"</string>
|
||||
<string name="screen_create_room_add_people_title">"Aggiungi persone"</string>
|
||||
<string name="screen_create_room_action_invite_people">"Invita persone su Element"</string>
|
||||
<string name="screen_create_room_add_people_title">"Invita persone"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Si è verificato un errore durante la creazione della stanza"</string>
|
||||
<string name="screen_create_room_private_option_description">"I messaggi in questa stanza sono cifrati. La crittografia non può essere disattivata in seguito."</string>
|
||||
<string name="screen_create_room_private_option_title">"Stanza privata (solo su invito)"</string>
|
||||
<string name="screen_create_room_public_option_description">"I messaggi non sono cifrati e chiunque può leggerli. Puoi attivare la crittografia in un secondo momento."</string>
|
||||
<string name="screen_create_room_public_option_title">"Stanza pubblica (chiunque)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Nome stanza"</string>
|
||||
<string name="screen_create_room_topic_label">"Argomento (facoltativo)"</string>
|
||||
<string name="screen_start_chat_error_starting_chat">"Si è verificato un errore durante il tentativo di avviare una chat"</string>
|
||||
<string name="screen_create_room_title">"Crea una stanza"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
<string name="screen_create_room_action_invite_people">"Пригласите друзей в Element"</string>
|
||||
<string name="screen_create_room_add_people_title">"Пригласить людей"</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_public_option_description">"Сообщения не зашифрованы, и каждый может их прочитать. Вы можете включить шифрование позже."</string>
|
||||
<string name="screen_create_room_public_option_description">"Сообщения не зашифрованы, каждый может их прочитать. Вы можете включить шифрование позже."</string>
|
||||
<string name="screen_create_room_public_option_title">"Публичная комната (любой)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Название комнаты"</string>
|
||||
<string name="screen_create_room_topic_label">"Тема (необязательно)"</string>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package io.element.android.features.ftue.impl.migration
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.features.ftue.impl.R
|
||||
|
|
@ -32,8 +34,9 @@ fun MigrationScreenView(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (migrationState.isMigrating.not()) {
|
||||
val latestOnMigrationFinished by rememberUpdatedState(onMigrationFinished)
|
||||
LaunchedEffect(Unit) {
|
||||
onMigrationFinished()
|
||||
latestOnMigrationFinished()
|
||||
}
|
||||
}
|
||||
SunsetPage(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_migration_message">"Dies ist ein einmaliger Vorgang, danke fürs Warten."</string>
|
||||
<string name="screen_migration_title">"Richte dein Konto ein."</string>
|
||||
<string name="screen_migration_title">"Dein Konto wird eingerichtet."</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_welcome_bullet_1">"Anrufe, Umfragen, Suchfunktionen und mehr werden im Laufe des Jahres hinzugefügt."</string>
|
||||
|
|
|
|||
13
features/ftue/impl/src/main/res/values-it/translations.xml
Normal file
13
features/ftue/impl/src/main/res/values-it/translations.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_migration_message">"Si tratta di una procedura che si effettua una sola volta, grazie per l\'attesa."</string>
|
||||
<string name="screen_migration_title">"Configurazione del tuo account."</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_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_3">"Ci piacerebbe sentire il tuo parere, facci sapere cosa ne pensi tramite la pagina delle impostazioni."</string>
|
||||
<string name="screen_welcome_button">"Andiamo!"</string>
|
||||
<string name="screen_welcome_subtitle">"Ecco cosa c\'è da sapere:"</string>
|
||||
<string name="screen_welcome_title">"Benvenuti in %1$s!"</string>
|
||||
</resources>
|
||||
|
|
@ -169,7 +169,7 @@ class InviteListPresenter @Inject constructor(
|
|||
AvatarData(
|
||||
id = roomId.value,
|
||||
name = name,
|
||||
url = avatarURLString,
|
||||
url = avatarUrl,
|
||||
size = AvatarSize.RoomInviteItem,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -57,8 +59,9 @@ fun InviteListView(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (state.acceptedAction is AsyncData.Success) {
|
||||
val latestOnInviteAccepted by rememberUpdatedState(onInviteAccepted)
|
||||
LaunchedEffect(state.acceptedAction) {
|
||||
onInviteAccepted(state.acceptedAction.data)
|
||||
latestOnInviteAccepted(state.acceptedAction.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invites_decline_chat_message">"Vuoi davvero rifiutare l\'invito ad entrare in %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Rifiuta l\'invito"</string>
|
||||
<string name="screen_invites_decline_direct_chat_message">"Vuoi davvero rifiutare questa chat privata con %1$s?"</string>
|
||||
<string name="screen_invites_decline_direct_chat_title">"Rifiuta la chat"</string>
|
||||
<string name="screen_invites_empty_list">"Nessun invito"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) ti ha invitato"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_invites_decline_chat_message">"Вы уверены, что хотите отклонить приглашение в %1$s?"</string>
|
||||
<string name="screen_invites_decline_chat_title">"Отклонить приглашение"</string>
|
||||
<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_empty_list">"Нет приглашений"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил вас"</string>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||
|
|
@ -39,6 +38,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
|||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
||||
import io.element.android.libraries.push.test.notifications.FakeNotificationDrawerManager
|
||||
|
|
@ -425,14 +425,12 @@ class InviteListPresenterTests {
|
|||
postInviteRooms(
|
||||
listOf(
|
||||
RoomSummary.Filled(
|
||||
RoomSummaryDetails(
|
||||
aRoomSummaryDetails(
|
||||
roomId = A_ROOM_ID,
|
||||
name = A_ROOM_NAME,
|
||||
avatarURLString = null,
|
||||
avatarUrl = null,
|
||||
isDirect = false,
|
||||
lastMessage = null,
|
||||
lastMessageTimestamp = null,
|
||||
unreadNotificationCount = 0,
|
||||
inviter = RoomMember(
|
||||
userId = A_USER_ID,
|
||||
displayName = A_USER_NAME,
|
||||
|
|
@ -454,14 +452,12 @@ class InviteListPresenterTests {
|
|||
postInviteRooms(
|
||||
listOf(
|
||||
RoomSummary.Filled(
|
||||
RoomSummaryDetails(
|
||||
aRoomSummaryDetails(
|
||||
roomId = A_ROOM_ID,
|
||||
name = A_ROOM_NAME,
|
||||
avatarURLString = null,
|
||||
avatarUrl = null,
|
||||
isDirect = true,
|
||||
lastMessage = null,
|
||||
lastMessageTimestamp = null,
|
||||
unreadNotificationCount = 0,
|
||||
inviter = RoomMember(
|
||||
userId = A_USER_ID,
|
||||
displayName = A_USER_NAME,
|
||||
|
|
@ -480,14 +476,12 @@ class InviteListPresenterTests {
|
|||
}
|
||||
|
||||
private fun aRoomSummary(id: RoomId = A_ROOM_ID) = RoomSummary.Filled(
|
||||
RoomSummaryDetails(
|
||||
aRoomSummaryDetails(
|
||||
roomId = id,
|
||||
name = A_ROOM_NAME,
|
||||
avatarURLString = null,
|
||||
avatarUrl = null,
|
||||
isDirect = false,
|
||||
lastMessage = null,
|
||||
lastMessageTimestamp = null,
|
||||
unreadNotificationCount = 0,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ data class LeaveRoomState(
|
|||
) {
|
||||
sealed interface Confirmation {
|
||||
data object Hidden : Confirmation
|
||||
data class Dm(val roomId: RoomId) : Confirmation
|
||||
data class Generic(val roomId: RoomId) : Confirmation
|
||||
data class PrivateRoom(val roomId: RoomId) : Confirmation
|
||||
data class LastUserInRoom(val roomId: RoomId) : Confirmation
|
||||
|
|
|
|||
|
|
@ -28,17 +28,17 @@ class LeaveRoomStateProvider : PreviewParameterProvider<LeaveRoomState> {
|
|||
error = LeaveRoomState.Error.Hidden,
|
||||
),
|
||||
aLeaveRoomState(
|
||||
confirmation = LeaveRoomState.Confirmation.Generic(A_ROOM_ID),
|
||||
confirmation = LeaveRoomState.Confirmation.Generic(roomId = A_ROOM_ID),
|
||||
progress = LeaveRoomState.Progress.Hidden,
|
||||
error = LeaveRoomState.Error.Hidden,
|
||||
),
|
||||
aLeaveRoomState(
|
||||
confirmation = LeaveRoomState.Confirmation.PrivateRoom(A_ROOM_ID),
|
||||
confirmation = LeaveRoomState.Confirmation.PrivateRoom(roomId = A_ROOM_ID),
|
||||
progress = LeaveRoomState.Progress.Hidden,
|
||||
error = LeaveRoomState.Error.Hidden,
|
||||
),
|
||||
aLeaveRoomState(
|
||||
confirmation = LeaveRoomState.Confirmation.LastUserInRoom(A_ROOM_ID),
|
||||
confirmation = LeaveRoomState.Confirmation.LastUserInRoom(roomId = A_ROOM_ID),
|
||||
progress = LeaveRoomState.Progress.Hidden,
|
||||
error = LeaveRoomState.Error.Hidden,
|
||||
),
|
||||
|
|
@ -52,6 +52,11 @@ class LeaveRoomStateProvider : PreviewParameterProvider<LeaveRoomState> {
|
|||
progress = LeaveRoomState.Progress.Hidden,
|
||||
error = LeaveRoomState.Error.Shown,
|
||||
),
|
||||
aLeaveRoomState(
|
||||
confirmation = LeaveRoomState.Confirmation.Dm(roomId = A_ROOM_ID),
|
||||
progress = LeaveRoomState.Progress.Hidden,
|
||||
error = LeaveRoomState.Error.Hidden,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,21 +47,32 @@ private fun LeaveRoomConfirmationDialog(
|
|||
) {
|
||||
when (state.confirmation) {
|
||||
is LeaveRoomState.Confirmation.Hidden -> {}
|
||||
|
||||
is LeaveRoomState.Confirmation.Dm -> LeaveRoomConfirmationDialog(
|
||||
text = R.string.leave_conversation_alert_subtitle,
|
||||
roomId = state.confirmation.roomId,
|
||||
isDm = true,
|
||||
eventSink = state.eventSink,
|
||||
)
|
||||
|
||||
is LeaveRoomState.Confirmation.PrivateRoom -> LeaveRoomConfirmationDialog(
|
||||
text = R.string.leave_room_alert_private_subtitle,
|
||||
roomId = state.confirmation.roomId,
|
||||
isDm = false,
|
||||
eventSink = state.eventSink,
|
||||
)
|
||||
|
||||
is LeaveRoomState.Confirmation.LastUserInRoom -> LeaveRoomConfirmationDialog(
|
||||
text = R.string.leave_room_alert_empty_subtitle,
|
||||
roomId = state.confirmation.roomId,
|
||||
isDm = false,
|
||||
eventSink = state.eventSink,
|
||||
)
|
||||
|
||||
is LeaveRoomState.Confirmation.Generic -> LeaveRoomConfirmationDialog(
|
||||
text = R.string.leave_room_alert_subtitle,
|
||||
roomId = state.confirmation.roomId,
|
||||
isDm = false,
|
||||
eventSink = state.eventSink,
|
||||
)
|
||||
}
|
||||
|
|
@ -71,10 +82,11 @@ private fun LeaveRoomConfirmationDialog(
|
|||
private fun LeaveRoomConfirmationDialog(
|
||||
@StringRes text: Int,
|
||||
roomId: RoomId,
|
||||
isDm: Boolean,
|
||||
eventSink: (LeaveRoomEvent) -> Unit,
|
||||
) {
|
||||
ConfirmationDialog(
|
||||
title = stringResource(CommonStrings.action_leave_room),
|
||||
title = stringResource(if (isDm) CommonStrings.action_leave_conversation else CommonStrings.action_leave_room),
|
||||
content = stringResource(text),
|
||||
submitText = stringResource(CommonStrings.action_leave),
|
||||
onSubmitClicked = { eventSink(LeaveRoomEvent.LeaveRoom(roomId)) },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Opravdu chcete opustit tuto konverzaci? Tato konverzace není veřejná a bez pozvánky se k ní nebudete moci znovu připojit."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Opravdu chcete opustit tuto místnost? Jste tu jediná osoba. Pokud odejdete, nikdo se v budoucnu nebude moci připojit, včetně vás."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Opravdu chcete opustit tuto místnost? Tato místnost není veřejná a bez pozvánky se nebudete moci znovu připojit."</string>
|
||||
<string name="leave_room_alert_subtitle">"Opravdu chcete opustit místnost?"</string>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Êtes-vous sûr de vouloir quitter cette discussion? Vous ne pourrez pas la rejoindre à nouveau sans y être invité."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Êtes-vous sûr de vouloir quitter ce salon ? Vous êtes la seule personne ici. Si vous partez, personne ne pourra rejoindre le salon à l’avenir, y compris vous."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Êtes-vous sûr de vouloir quitter ce salon ? Ce salon n’est pas public et vous ne pourrez pas le rejoindre sans invitation."</string>
|
||||
<string name="leave_room_alert_subtitle">"Êtes-vous sûr de vouloir quitter le salon ?"</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_room_alert_empty_subtitle">"Biztos, hogy elhagyja ezt a szobát? Ön az egyedüli ember itt. Ha kilép, akkor senki sem fog tudni csatlakozni a jövőben, Önt is beleértve."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Biztos, hogy elhagyja ezt a szobát? Ez a szoba nem nyilvános, és meghívó nélkül nem fog tudni újra belépni."</string>
|
||||
<string name="leave_room_alert_subtitle">"Biztos, hogy elhagyja a szobát?"</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Biztos, hogy elhagyod ezt a szobát? Ez a szoba nem nyilvános, és meghívó nélkül nem fogsz tudni újra belépni."</string>
|
||||
<string name="leave_room_alert_subtitle">"Biztos, hogy elhagyod a szobát?"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Vuoi davvero abbandonare questa conversazione? La conversazione non è pubblica e non potrai rientrare senza un invito."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Sei sicuro di voler lasciare questa stanza? Sei l\'unica persona presente. Se esci, nessuno potrà unirsi in futuro, te compreso."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Sei sicuro di voler lasciare questa stanza? Questa stanza non è pubblica e non potrai rientrare senza un invito."</string>
|
||||
<string name="leave_room_alert_subtitle">"Sei sicuro di voler lasciare la stanza?"</string>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Вы уверены, что хотите покинуть беседу?"</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Вы уверены, что хотите покинуть эту комнату? Вы здесь единственный человек. Если вы уйдете, никто не сможет присоединиться в будущем, включая вас."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Вы уверены, что хотите покинуть эту комнату? Эта комната не является публичной, и Вы не сможете присоединиться к ней без приглашения."</string>
|
||||
<string name="leave_room_alert_subtitle">"Вы уверены, что хотите покинуть комнату?"</string>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Ste si istí, že chcete opustiť konverzáciu?"</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Ste si istí, že chcete opustiť túto miestnosť? Ste tu jediná osoba. Ak odídete, nikto sa do nej nebude môcť v budúcnosti pripojiť, vrátane vás."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Ste si istí, že chcete opustiť túto miestnosť? Táto miestnosť nie je verejná a bez pozvania sa do nej nebudete môcť vrátiť."</string>
|
||||
<string name="leave_room_alert_subtitle">"Ste si istí, že chcete opustiť miestnosť?"</string>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="leave_conversation_alert_subtitle">"Are you sure that you want to leave this conversation? This conversation is not public and you won\'t be able to rejoin without an invite."</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Are you sure that you want to leave this room? You\'re the only person here. If you leave, no one will be able to join in the future, including you."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Are you sure that you want to leave this room? This room is not public and you won\'t be able to rejoin without an invite."</string>
|
||||
<string name="leave_room_alert_subtitle">"Are you sure that you want to leave the room?"</string>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState.Confirmation.Dm
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState.Confirmation.Generic
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState.Confirmation.LastUserInRoom
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomState.Confirmation.PrivateRoom
|
||||
|
|
@ -85,6 +86,7 @@ private suspend fun showLeaveRoomAlert(
|
|||
) {
|
||||
matrixClient.getRoom(roomId)?.use { room ->
|
||||
confirmation.value = when {
|
||||
room.isDm -> Dm(roomId)
|
||||
!room.isPublic -> PrivateRoom(roomId)
|
||||
room.joinedMemberCount == 1L -> LastUserInRoom(roomId)
|
||||
else -> Generic(roomId)
|
||||
|
|
|
|||
|
|
@ -114,6 +114,26 @@ class LeaveRoomPresenterImplTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - show DM confirmation`() = runTest {
|
||||
val presenter = createLeaveRoomPresenter(
|
||||
client = FakeMatrixClient().apply {
|
||||
givenGetRoomResult(
|
||||
roomId = A_ROOM_ID,
|
||||
result = FakeMatrixRoom(activeMemberCount = 2, isDirect = true, isOneToOne = true),
|
||||
)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(LeaveRoomEvent.ShowConfirmation(A_ROOM_ID))
|
||||
val confirmationState = awaitItem()
|
||||
assertThat(confirmationState.confirmation).isEqualTo(LeaveRoomState.Confirmation.Dm(A_ROOM_ID))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - leaving a room leaves the room`() = runTest {
|
||||
val roomMembershipObserver = RoomMembershipObserver()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package io.element.android.features.lockscreen.impl.unlock
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import io.element.android.features.lockscreen.impl.biometric.BiometricUnlockManager
|
||||
import io.element.android.features.lockscreen.impl.biometric.DefaultBiometricUnlockCallback
|
||||
import io.element.android.features.lockscreen.impl.pin.DefaultPinCodeManagerCallback
|
||||
|
|
@ -30,15 +32,16 @@ class PinUnlockHelper @Inject constructor(
|
|||
) {
|
||||
@Composable
|
||||
fun OnUnlockEffect(onUnlock: () -> Unit) {
|
||||
val latestOnUnlock by rememberUpdatedState(onUnlock)
|
||||
DisposableEffect(Unit) {
|
||||
val biometricUnlockCallback = object : DefaultBiometricUnlockCallback() {
|
||||
override fun onBiometricUnlockSuccess() {
|
||||
onUnlock()
|
||||
latestOnUnlock()
|
||||
}
|
||||
}
|
||||
val pinCodeVerifiedCallback = object : DefaultPinCodeManagerCallback() {
|
||||
override fun onPinCodeVerified() {
|
||||
onUnlock()
|
||||
latestOnUnlock()
|
||||
}
|
||||
}
|
||||
biometricUnlockManager.addCallback(biometricUnlockCallback)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="screen_app_lock_subtitle">
|
||||
<item quantity="one">"Hai %1$d tentativo di sblocco"</item>
|
||||
<item quantity="other">"Hai %1$d tentativi di sblocco"</item>
|
||||
</plurals>
|
||||
<plurals name="screen_app_lock_subtitle_wrong_pin">
|
||||
<item quantity="one">"PIN sbagliato. Hai %1$d altro tentativo"</item>
|
||||
<item quantity="other">"PIN sbagliato. Hai altri %1$d tentativi"</item>
|
||||
</plurals>
|
||||
<string name="screen_app_lock_biometric_authentication">"autenticazione biometrica"</string>
|
||||
<string name="screen_app_lock_biometric_unlock">"sblocco biometrico"</string>
|
||||
<string name="screen_app_lock_biometric_unlock_title_android">"Sblocca con la biometria"</string>
|
||||
<string name="screen_app_lock_forgot_pin">"PIN dimenticato?"</string>
|
||||
<string name="screen_app_lock_settings_change_pin">"Modifica il codice PIN"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Consenti lo sblocco biometrico"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin">"Rimuovi PIN"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"Vuoi davvero rimuovere il PIN?"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"Rimuovere il PIN?"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_allow_title">"Consenti %1$s"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_skip">"Preferisco usare il PIN"</string>
|
||||
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Risparmia un po\' di tempo e usa %1$s per sbloccare l\'app ogni volta"</string>
|
||||
<string name="screen_app_lock_setup_choose_pin">"Scegli il PIN"</string>
|
||||
<string name="screen_app_lock_setup_confirm_pin">"Conferma il PIN"</string>
|
||||
<string name="screen_app_lock_setup_pin_blacklisted_dialog_content">"Non puoi scegliere questo codice PIN per motivi di sicurezza"</string>
|
||||
<string name="screen_app_lock_setup_pin_blacklisted_dialog_title">"Scegli un PIN diverso"</string>
|
||||
<string name="screen_app_lock_setup_pin_context">"Blocca %1$s per aggiungere ulteriore sicurezza alle tue chat.
|
||||
|
||||
Scegli qualcosa che puoi ricordare. Se dimentichi questo PIN, verrai disconnesso dall\'app."</string>
|
||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Inserisci lo stesso PIN due volte"</string>
|
||||
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"I PIN non corrispondono"</string>
|
||||
<string name="screen_app_lock_signout_alert_message">"Dovrai effettuare nuovamente l\'accesso e creare un nuovo PIN per procedere"</string>
|
||||
<string name="screen_app_lock_signout_alert_title">"Stai per essere disconnesso"</string>
|
||||
<string name="screen_app_lock_use_biometric_android">"Usa la biometria"</string>
|
||||
<string name="screen_app_lock_use_pin_android">"Usa il PIN"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Uscita in corso…"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
<item quantity="many">"Неверный PIN-код. У вас остался %1$d шанса"</item>
|
||||
</plurals>
|
||||
<string name="screen_app_lock_biometric_authentication">"биометрическая идентификация"</string>
|
||||
<string name="screen_app_lock_biometric_unlock">"биометрическая разблокировать"</string>
|
||||
<string name="screen_app_lock_biometric_unlock">"биометрическая разблокировка"</string>
|
||||
<string name="screen_app_lock_biometric_unlock_title_android">"Разблокировать с помощью биометрии"</string>
|
||||
<string name="screen_app_lock_forgot_pin">"Забыли PIN-код?"</string>
|
||||
<string name="screen_app_lock_settings_change_pin">"Измените PIN-код"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Разрешить биометрическую разблокировать"</string>
|
||||
<string name="screen_app_lock_settings_enable_biometric_unlock">"Разрешить биометрическую разблокировку"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin">"Удалить PIN-код"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_message">"Вы действительно хотите удалить PIN-код?"</string>
|
||||
<string name="screen_app_lock_settings_remove_pin_alert_title">"Удалить PIN-код?"</string>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package io.element.android.features.login.impl.changeserver
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
|
||||
|
|
@ -63,8 +65,11 @@ fun ChangeServerView(
|
|||
}
|
||||
}
|
||||
is AsyncData.Loading -> ProgressDialog()
|
||||
is AsyncData.Success -> LaunchedEffect(state.changeServerAction) {
|
||||
onDone()
|
||||
is AsyncData.Success -> {
|
||||
val latestOnDone by rememberUpdatedState(onDone)
|
||||
LaunchedEffect(state.changeServerAction) {
|
||||
latestOnDone()
|
||||
}
|
||||
}
|
||||
AsyncData.Uninitialized -> Unit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,15 +203,17 @@ private fun LoginForm(
|
|||
.onTabOrEnterKeyFocusNext(focusManager)
|
||||
.testTag(TestTags.loginEmailUsername)
|
||||
.autofill(autofillTypes = listOf(AutofillType.Username), onFill = {
|
||||
loginFieldState = it
|
||||
eventSink(LoginPasswordEvents.SetLogin(it))
|
||||
val sanitized = it.sanitize()
|
||||
loginFieldState = sanitized
|
||||
eventSink(LoginPasswordEvents.SetLogin(sanitized))
|
||||
}),
|
||||
placeholder = {
|
||||
Text(text = stringResource(CommonStrings.common_username))
|
||||
},
|
||||
onValueChange = {
|
||||
loginFieldState = it
|
||||
eventSink(LoginPasswordEvents.SetLogin(it))
|
||||
val sanitized = it.sanitize()
|
||||
loginFieldState = sanitized
|
||||
eventSink(LoginPasswordEvents.SetLogin(sanitized))
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Email,
|
||||
|
|
@ -233,7 +235,6 @@ private fun LoginForm(
|
|||
null
|
||||
},
|
||||
)
|
||||
|
||||
var passwordVisible by remember { mutableStateOf(false) }
|
||||
if (state.loginAction is AsyncData.Loading) {
|
||||
// Ensure password is hidden when user submits the form
|
||||
|
|
@ -248,12 +249,14 @@ private fun LoginForm(
|
|||
.onTabOrEnterKeyFocusNext(focusManager)
|
||||
.testTag(TestTags.loginPassword)
|
||||
.autofill(autofillTypes = listOf(AutofillType.Password), onFill = {
|
||||
passwordFieldState = it
|
||||
eventSink(LoginPasswordEvents.SetPassword(it))
|
||||
val sanitized = it.sanitize()
|
||||
passwordFieldState = sanitized
|
||||
eventSink(LoginPasswordEvents.SetPassword(sanitized))
|
||||
}),
|
||||
onValueChange = {
|
||||
passwordFieldState = it
|
||||
eventSink(LoginPasswordEvents.SetPassword(it))
|
||||
val sanitized = it.sanitize()
|
||||
passwordFieldState = sanitized
|
||||
eventSink(LoginPasswordEvents.SetPassword(sanitized))
|
||||
},
|
||||
placeholder = {
|
||||
Text(text = stringResource(CommonStrings.common_password))
|
||||
|
|
@ -281,6 +284,13 @@ private fun LoginForm(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the string does not contain any new line characters, which can happen when pasting values.
|
||||
*/
|
||||
private fun String.sanitize(): String {
|
||||
return replace("\n", "")
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoginErrorDialog(error: Throwable, onDismiss: () -> Unit) {
|
||||
ErrorDialog(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<string name="screen_account_provider_form_title">"Fiókszolgáltató keresése"</string>
|
||||
<string name="screen_account_provider_signin_subtitle">"Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."</string>
|
||||
<string name="screen_account_provider_signin_title">"Hamarosan bejelentkezik ide: %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Itt lesznek a beszélgetéseid – ahogyan egy e-mail-szolgáltatást is használnál a leveleid kezeléséhez."</string>
|
||||
<string name="screen_account_provider_signup_title">"Hamarosan létrehoz egy fiókot itt: %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"A Matrix.org egy nagy, ingyenes kiszolgáló a nyilvános Matrix-hálózaton, a biztonságos, decentralizált kommunikáció érdekében, amelyet a Matrix.org Alapítvány üzemeltet."</string>
|
||||
<string name="screen_change_account_provider_other">"Egyéb"</string>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,42 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_account_provider_change">"Cambia fornitore dell\'account"</string>
|
||||
<string name="screen_account_provider_form_hint">"Indirizzo dell\'homeserver"</string>
|
||||
<string name="screen_account_provider_form_notice">"Inserisci un termine di ricerca o un indirizzo di dominio."</string>
|
||||
<string name="screen_account_provider_form_subtitle">"Cerca un\' azienda, una comunità o un server privato."</string>
|
||||
<string name="screen_account_provider_form_title">"Trova un fornitore di account"</string>
|
||||
<string name="screen_account_provider_signin_subtitle">"Qui è dove vivranno le tue conversazioni - proprio come useresti un fornitore di posta elettronica per conservare le tue email."</string>
|
||||
<string name="screen_account_provider_signin_title">"Stai per accedere a %s"</string>
|
||||
<string name="screen_account_provider_signup_subtitle">"Qui è dove vivranno le tue conversazioni - proprio come useresti un fornitore di posta elettronica per conservare le tue email."</string>
|
||||
<string name="screen_account_provider_signup_title">"Stai per creare un account su %s"</string>
|
||||
<string name="screen_change_account_provider_matrix_org_subtitle">"Matrix.org è un grande server gratuito nella rete pubblica Matrix per una comunicazione sicura e decentralizzata, gestito dalla Fondazione Matrix.org."</string>
|
||||
<string name="screen_change_account_provider_other">"Altro"</string>
|
||||
<string name="screen_change_account_provider_subtitle">"Utilizza un provider di account diverso, ad esempio il tuo server privato o un account di lavoro."</string>
|
||||
<string name="screen_change_account_provider_title">"Cambia fornitore dell\'account"</string>
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Non siamo riusciti a raggiungere questo homserver. Verifica di aver inserito correttamente l\'URL del server domestico. Se l\'URL è corretto, contatta l\'amministratore del tuo server domestico per ulteriore assistenza."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Questo server attualmente non supporta la sincronizzazione scorrevole."</string>
|
||||
<string name="screen_change_server_form_header">"URL dell\'homeserver"</string>
|
||||
<string name="screen_change_server_form_notice">"Puoi connetterti solo a un server esistente che supporta la sincronizzazione scorrevole. L\'amministratore del tuo server domestico dovrà configurarlo. %1$s"</string>
|
||||
<string name="screen_change_server_subtitle">"Qual è l\'indirizzo del tuo server?"</string>
|
||||
<string name="screen_change_server_title">"Seleziona il tuo server"</string>
|
||||
<string name="screen_login_error_deactivated_account">"Questo profilo è stato disattivato."</string>
|
||||
<string name="screen_login_error_invalid_credentials">"Nome utente e/o password errati"</string>
|
||||
<string name="screen_login_error_invalid_user_id">"Questo non è un identificatore utente valido. Formato previsto: \'@user:homeserver.org\'"</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"L\'homeserver selezionato non supporta la password o l\'accesso OIDC. Contatta il tuo amministratore o scegli un altro homeserver."</string>
|
||||
<string name="screen_login_form_header">"Inserisci i tuoi dati"</string>
|
||||
<string name="screen_login_title">"Bentornato!"</string>
|
||||
<string name="screen_login_title_with_homeserver">"Accedi a %1$s"</string>
|
||||
<string name="screen_server_confirmation_change_server">"Cambia fornitore dell\'account"</string>
|
||||
<string name="screen_server_confirmation_message_login_element_dot_io">"Un server privato per i dipendenti di Element."</string>
|
||||
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix è una rete aperta per comunicazioni sicure e decentralizzate."</string>
|
||||
<string name="screen_server_confirmation_message_register">"Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email."</string>
|
||||
<string name="screen_server_confirmation_title_login">"Stai per accedere a %1$s"</string>
|
||||
<string name="screen_server_confirmation_title_register">"Stai per creare un account su %1$s"</string>
|
||||
<string name="screen_waitlist_message">"Al momento c\'è una grande richiesta per %1$s su %2$s. Torna a visitare l\'app tra qualche giorno e riprova.
|
||||
|
||||
Grazie per la pazienza!"</string>
|
||||
<string name="screen_waitlist_title">"Ci sei quasi."</string>
|
||||
<string name="screen_waitlist_title_success">"Sei dentro."</string>
|
||||
<string name="screen_login_subtitle">"Matrix è una rete aperta per comunicazioni sicure e decentralizzate."</string>
|
||||
<string name="screen_waitlist_message_success">"Benvenuti in %1$s!"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package io.element.android.features.logout.impl.ui
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.features.logout.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
|
@ -52,9 +54,11 @@ fun LogoutActionDialog(
|
|||
onRetry = onForceLogoutClicked,
|
||||
onDismiss = onDismissError,
|
||||
)
|
||||
is AsyncAction.Success ->
|
||||
is AsyncAction.Success -> {
|
||||
val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout)
|
||||
LaunchedEffect(state) {
|
||||
onSuccessLogout(state.data)
|
||||
latestOnSuccessLogout(state.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,4 +2,17 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_signout_confirmation_dialog_content">"Sei sicuro di voler uscire?"</string>
|
||||
<string name="screen_signout_in_progress_dialog_content">"Uscita in corso…"</string>
|
||||
<string name="screen_signout_key_backup_disabled_subtitle">"Stai per disconnettere la tua ultima sessione. Se esci ora, perderai l\'accesso ai tuoi messaggi cifrati."</string>
|
||||
<string name="screen_signout_key_backup_disabled_title">"Hai disattivato il backup"</string>
|
||||
<string name="screen_signout_key_backup_offline_subtitle">"Il backup delle chiavi era ancora in corso quando sei andato offline. Riconnettiti per eseguire il backup delle chiavi prima di uscire."</string>
|
||||
<string name="screen_signout_key_backup_offline_title">"Il backup delle chiavi è ancora in corso"</string>
|
||||
<string name="screen_signout_key_backup_ongoing_subtitle">"Attendi il completamento dell\'operazione prima di uscire."</string>
|
||||
<string name="screen_signout_key_backup_ongoing_title">"Il backup delle chiavi è ancora in corso"</string>
|
||||
<string name="screen_signout_recovery_disabled_subtitle">"Stai per disconnettere la tua ultima sessione. Se esci ora, perderai l\'accesso ai tuoi messaggi cifrati."</string>
|
||||
<string name="screen_signout_recovery_disabled_title">"Recupero non impostato"</string>
|
||||
<string name="screen_signout_save_recovery_key_subtitle">"Stai per disconnettere la tua ultima sessione. Se esci ora, potresti perdere l\'accesso ai tuoi messaggi cifrati."</string>
|
||||
<string name="screen_signout_save_recovery_key_title">"Hai salvato la chiave di recupero?"</string>
|
||||
<string name="screen_signout_confirmation_dialog_submit">"Disconnetti"</string>
|
||||
<string name="screen_signout_confirmation_dialog_title">"Disconnetti"</string>
|
||||
<string name="screen_signout_preference_item">"Disconnetti"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ import io.element.android.features.messages.impl.utils.messagesummary.MessageSum
|
|||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.preferences.api.store.PreferencesStore
|
||||
import io.element.android.features.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
|
|
@ -81,10 +81,10 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||
import io.element.android.libraries.matrix.ui.room.canRedactAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canRedactOtherAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canRedactOwnAsState
|
||||
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
|
||||
import io.element.android.libraries.textcomposer.model.MessageComposerMode
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -107,12 +107,11 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
private val messageSummaryFormatter: MessageSummaryFormatter,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val clipboardHelper: ClipboardHelper,
|
||||
private val preferencesStore: PreferencesStore,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val featureFlagsService: FeatureFlagService,
|
||||
private val htmlConverterProvider: HtmlConverterProvider,
|
||||
@Assisted private val navigator: MessagesNavigator,
|
||||
private val buildMeta: BuildMeta,
|
||||
private val currentSessionIdHolder: CurrentSessionIdHolder,
|
||||
) : Presenter<MessagesState> {
|
||||
private val timelinePresenter = timelinePresenterFactory.create(navigator = navigator)
|
||||
|
||||
|
|
@ -123,7 +122,7 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun present(): MessagesState {
|
||||
htmlConverterProvider.Update(currentUserId = currentSessionIdHolder.current)
|
||||
htmlConverterProvider.Update(currentUserId = room.sessionId)
|
||||
|
||||
val roomInfo by room.roomInfoFlow.collectAsState(null)
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
|
|
@ -138,7 +137,8 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
|
||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
|
||||
val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value)
|
||||
val userHasPermissionToRedactOwn by room.canRedactOwnAsState(updateKey = syncUpdateFlow.value)
|
||||
val userHasPermissionToRedactOther by room.canRedactOtherAsState(updateKey = syncUpdateFlow.value)
|
||||
val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION_SENT, updateKey = syncUpdateFlow.value)
|
||||
val roomName: AsyncData<String> by remember {
|
||||
derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
|
||||
|
|
@ -155,15 +155,15 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
LaunchedEffect(currentSessionIdHolder.current) {
|
||||
LaunchedEffect(syncUpdateFlow.value) {
|
||||
withContext(dispatchers.io) {
|
||||
canJoinCall = room.canUserJoinCall(userId = currentSessionIdHolder.current).getOrDefault(false)
|
||||
canJoinCall = room.canUserJoinCall(room.sessionId).getOrDefault(false)
|
||||
}
|
||||
}
|
||||
|
||||
val inviteProgress = remember { mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized) }
|
||||
var showReinvitePrompt by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow) {
|
||||
LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow.value) {
|
||||
withContext(dispatchers.io) {
|
||||
showReinvitePrompt = !hasDismissedInviteDialog && composerState.hasFocus && room.isDirect && room.activeMemberCount == 1L
|
||||
}
|
||||
|
|
@ -176,7 +176,7 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
timelineState.eventSink(TimelineEvents.SetHighlightedEvent(composerState.mode.relatedEventId))
|
||||
}
|
||||
|
||||
val enableTextFormatting by preferencesStore.isRichTextEditorEnabledFlow().collectAsState(initial = true)
|
||||
val enableTextFormatting by appPreferencesStore.isRichTextEditorEnabledFlow().collectAsState(initial = true)
|
||||
|
||||
var enableVoiceMessages by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(featureFlagsService) {
|
||||
|
|
@ -219,7 +219,8 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
roomName = roomName,
|
||||
roomAvatar = roomAvatar,
|
||||
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
||||
userHasPermissionToRedact = userHasPermissionToRedact,
|
||||
userHasPermissionToRedactOwn = userHasPermissionToRedactOwn,
|
||||
userHasPermissionToRedactOther = userHasPermissionToRedactOther,
|
||||
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
|
||||
composerState = composerState,
|
||||
voiceMessageComposerState = voiceMessageComposerState,
|
||||
|
|
@ -312,7 +313,7 @@ class MessagesPresenter @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun handleActionEdit(
|
||||
private fun handleActionEdit(
|
||||
targetEvent: TimelineItem.Event,
|
||||
composerState: MessageComposerState,
|
||||
enableTextFormatting: Boolean,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ data class MessagesState(
|
|||
val roomName: AsyncData<String>,
|
||||
val roomAvatar: AsyncData<AvatarData>,
|
||||
val userHasPermissionToSendMessage: Boolean,
|
||||
val userHasPermissionToRedact: Boolean,
|
||||
val userHasPermissionToRedactOwn: Boolean,
|
||||
val userHasPermissionToRedactOther: Boolean,
|
||||
val userHasPermissionToSendReaction: Boolean,
|
||||
val composerState: MessageComposerState,
|
||||
val voiceMessageComposerState: VoiceMessageComposerState,
|
||||
|
|
|
|||
|
|
@ -86,7 +86,8 @@ fun aMessagesState() = MessagesState(
|
|||
roomName = AsyncData.Success("Room name"),
|
||||
roomAvatar = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
|
||||
userHasPermissionToSendMessage = true,
|
||||
userHasPermissionToRedact = false,
|
||||
userHasPermissionToRedactOwn = false,
|
||||
userHasPermissionToRedactOther = false,
|
||||
userHasPermissionToSendReaction = true,
|
||||
composerState = aMessageComposerState().copy(
|
||||
richTextEditorState = RichTextEditorState("Hello", initialFocus = true),
|
||||
|
|
|
|||
|
|
@ -41,8 +41,10 @@ import androidx.compose.material3.MaterialTheme
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
|
|
@ -160,7 +162,8 @@ fun MessagesView(
|
|||
state.actionListState.eventSink(
|
||||
ActionListEvents.ComputeForMessage(
|
||||
event = event,
|
||||
canRedact = state.userHasPermissionToRedact,
|
||||
canRedactOwn = state.userHasPermissionToRedactOwn,
|
||||
canRedactOther = state.userHasPermissionToRedactOther,
|
||||
canSendMessage = state.userHasPermissionToSendMessage,
|
||||
canSendReaction = state.userHasPermissionToSendReaction,
|
||||
)
|
||||
|
|
@ -293,8 +296,11 @@ private fun AttachmentStateView(
|
|||
) {
|
||||
when (state) {
|
||||
AttachmentsState.None -> Unit
|
||||
is AttachmentsState.Previewing -> LaunchedEffect(state) {
|
||||
onPreviewAttachments(state.attachments)
|
||||
is AttachmentsState.Previewing -> {
|
||||
val latestOnPreviewAttachments by rememberUpdatedState(onPreviewAttachments)
|
||||
LaunchedEffect(state) {
|
||||
latestOnPreviewAttachments(state.attachments)
|
||||
}
|
||||
}
|
||||
is AttachmentsState.Sending -> {
|
||||
ProgressDialog(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue