From f0700f9904ef4e27b3492462a2a9424997a9a133 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 26 Feb 2024 16:24:22 +0100 Subject: [PATCH] Show blocked users list (#2437) * Show blocked users list. Also allow to unblock them from this list. * Add non-blocking `AsyncIndicatorHost` component * Use `StateFlow` for getting ignored users. --------- Co-authored-by: ElementBot --- .../android/appnav/loggedin/LoggedInView.kt | 6 +- .../android/appnav/loggedin/SyncStateView.kt | 47 +-- .../preferences/impl/PreferencesFlowNode.kt | 11 + .../impl/blockedusers/BlockedUsersEvents.kt | 25 ++ .../impl/blockedusers/BlockedUsersNode.kt | 44 +++ .../blockedusers/BlockedUsersPresenter.kt | 82 +++++ .../impl/blockedusers/BlockedUsersState.kt | 27 ++ .../BlockedUsersStatePreviewProvider.kt | 48 +++ .../impl/blockedusers/BlockedUsersView.kt | 137 +++++++++ .../impl/root/PreferencesRootNode.kt | 6 + .../impl/root/PreferencesRootView.kt | 7 + .../src/main/res/values-be/translations.xml | 3 + .../src/main/res/values-bg/translations.xml | 2 + .../src/main/res/values-cs/translations.xml | 3 + .../src/main/res/values-de/translations.xml | 4 + .../src/main/res/values-es/translations.xml | 3 + .../src/main/res/values-fr/translations.xml | 5 + .../src/main/res/values-hu/translations.xml | 3 + .../src/main/res/values-in/translations.xml | 3 + .../src/main/res/values-it/translations.xml | 3 + .../src/main/res/values-ro/translations.xml | 3 + .../src/main/res/values-ru/translations.xml | 4 + .../src/main/res/values-sk/translations.xml | 3 + .../src/main/res/values-sv/translations.xml | 3 + .../impl/src/main/res/values/localazy.xml | 4 + .../BlockedUsersPresenterTests.kt | 161 ++++++++++ .../components/async/AsyncIndicator.kt | 117 ++++++++ .../components/async/AsyncIndicatorHost.kt | 152 ++++++++++ .../components/async/AsyncIndicatorView.kt | 90 ++++++ .../component/async/AsyncIndicatorTests.kt | 280 ++++++++++++++++++ .../libraries/matrix/api/MatrixClient.kt | 3 + .../libraries/matrix/impl/RustMatrixClient.kt | 19 ++ .../libraries/matrix/test/FakeMatrixClient.kt | 5 + .../src/main/res/values-be/translations.xml | 3 - .../src/main/res/values-bg/translations.xml | 2 - .../src/main/res/values-cs/translations.xml | 3 - .../src/main/res/values-de/translations.xml | 4 - .../src/main/res/values-es/translations.xml | 3 - .../src/main/res/values-fr/translations.xml | 3 - .../src/main/res/values-hu/translations.xml | 3 - .../src/main/res/values-in/translations.xml | 3 - .../src/main/res/values-it/translations.xml | 3 - .../src/main/res/values-ro/translations.xml | 3 - .../src/main/res/values-ru/translations.xml | 7 +- .../src/main/res/values-sk/translations.xml | 3 - .../src/main/res/values-sv/translations.xml | 3 - .../main/res/values-uk-rUA/translations.xml | 3 - .../main/res/values-zh-rTW/translations.xml | 2 - .../src/main/res/values/localazy.xml | 4 - ...StateView-Day-1_1_null,NEXUS_5,1.0,en].png | 4 +- ...ateView-Night-1_2_null,NEXUS_5,1.0,en].png | 4 +- ...ersView-Day-3_4_null_0,NEXUS_5,1.0,en].png | 3 + ...ersView-Day-3_4_null_1,NEXUS_5,1.0,en].png | 3 + ...ersView-Day-3_4_null_2,NEXUS_5,1.0,en].png | 3 + ...ersView-Day-3_4_null_3,NEXUS_5,1.0,en].png | 3 + ...ersView-Day-3_4_null_4,NEXUS_5,1.0,en].png | 3 + ...ersView-Day-3_4_null_5,NEXUS_5,1.0,en].png | 3 + ...sView-Night-3_5_null_0,NEXUS_5,1.0,en].png | 3 + ...sView-Night-3_5_null_1,NEXUS_5,1.0,en].png | 3 + ...sView-Night-3_5_null_2,NEXUS_5,1.0,en].png | 3 + ...sView-Night-3_5_null_3,NEXUS_5,1.0,en].png | 3 + ...sView-Night-3_5_null_4,NEXUS_5,1.0,en].png | 3 + ...sView-Night-3_5_null_5,NEXUS_5,1.0,en].png | 3 + ...ngView-Day-5_6_null_0,NEXUS_5,1.0,en].png} | 0 ...View-Night-5_7_null_0,NEXUS_5,1.0,en].png} | 0 ...gsView-Day-4_5_null_0,NEXUS_5,1.0,en].png} | 0 ...gsView-Day-4_5_null_1,NEXUS_5,1.0,en].png} | 0 ...gsView-Day-4_5_null_2,NEXUS_5,1.0,en].png} | 0 ...View-Night-4_6_null_0,NEXUS_5,1.0,en].png} | 0 ...View-Night-4_6_null_1,NEXUS_5,1.0,en].png} | 0 ...View-Night-4_6_null_2,NEXUS_5,1.0,en].png} | 0 ...ngOption-Day-8_9_null,NEXUS_5,1.0,en].png} | 0 ...ption-Night-8_10_null,NEXUS_5,1.0,en].png} | 0 ...gView-Day-9_10_null_0,NEXUS_5,1.0,en].png} | 0 ...gView-Day-9_10_null_1,NEXUS_5,1.0,en].png} | 0 ...gView-Day-9_10_null_2,NEXUS_5,1.0,en].png} | 0 ...gView-Day-9_10_null_3,NEXUS_5,1.0,en].png} | 0 ...gView-Day-9_10_null_4,NEXUS_5,1.0,en].png} | 0 ...iew-Night-9_11_null_0,NEXUS_5,1.0,en].png} | 0 ...iew-Night-9_11_null_1,NEXUS_5,1.0,en].png} | 0 ...iew-Night-9_11_null_2,NEXUS_5,1.0,en].png} | 0 ...iew-Night-9_11_null_3,NEXUS_5,1.0,en].png} | 0 ...iew-Night-9_11_null_4,NEXUS_5,1.0,en].png} | 0 ...ingsView-Day-7_8_null,NEXUS_5,1.0,en].png} | 0 ...gsView-Night-7_9_null,NEXUS_5,1.0,en].png} | 0 ...gsView-Day-6_7_null_0,NEXUS_5,1.0,en].png} | 0 ...gsView-Day-6_7_null_1,NEXUS_5,1.0,en].png} | 0 ...gsView-Day-6_7_null_2,NEXUS_5,1.0,en].png} | 0 ...View-Night-6_8_null_0,NEXUS_5,1.0,en].png} | 0 ...View-Night-6_8_null_1,NEXUS_5,1.0,en].png} | 0 ...View-Night-6_8_null_2,NEXUS_5,1.0,en].png} | 0 ...otViewDark--1_1_null_0,NEXUS_5,1.0,en].png | 4 +- ...otViewDark--1_1_null_1,NEXUS_5,1.0,en].png | 4 +- ...tViewLight--0_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...tViewLight--0_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...View-Day-11_12_null_0,NEXUS_5,1.0,en].png} | 0 ...ew-Night-11_13_null_0,NEXUS_5,1.0,en].png} | 0 ...nces-Day-10_11_null_0,NEXUS_5,1.0,en].png} | 0 ...nces-Day-10_11_null_1,NEXUS_5,1.0,en].png} | 0 ...nces-Day-10_11_null_2,NEXUS_5,1.0,en].png} | 0 ...es-Night-10_12_null_0,NEXUS_5,1.0,en].png} | 0 ...es-Night-10_12_null_1,NEXUS_5,1.0,en].png} | 0 ...es-Night-10_12_null_2,NEXUS_5,1.0,en].png} | 0 ...iew_Failed_-Day_0_null,NEXUS_5,1.0,en].png | 3 + ...w_Failed_-Night_1_null,NEXUS_5,1.0,en].png | 3 + ...ew_Loading_-Day_0_null,NEXUS_5,1.0,en].png | 3 + ..._Loading_-Night_1_null,NEXUS_5,1.0,en].png | 3 + tools/localazy/config.json | 3 +- 108 files changed, 1334 insertions(+), 106 deletions(-) create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvents.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt create mode 100644 features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt create mode 100644 features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTests.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicator.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorHost.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorView.kt create mode 100644 libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncIndicatorTests.kt create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_5,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-4_5_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-5_6_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-4_6_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-5_7_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-7_8_null,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-8_9_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-7_9_null,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-8_10_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Day-6_7_null,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Day-7_8_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Night-6_8_null,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Night-7_9_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_11_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-11_12_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_12_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-11_13_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_2,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Day_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Night_1_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Day_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Night_1_null,NEXUS_5,1.0,en].png diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index be9550d990..74c71d387c 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -18,13 +18,11 @@ package io.element.android.appnav.loggedin import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -39,9 +37,7 @@ fun LoggedInView( .systemBarsPadding() ) { SyncStateView( - modifier = Modifier - .padding(top = 8.dp) - .align(Alignment.TopCenter), + modifier = Modifier.align(Alignment.TopCenter), isVisible = state.showSyncSpinner, ) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt index 9522d8835f..b8066ec100 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt @@ -20,25 +20,15 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.spring import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.progressSemantics -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.components.async.AsyncIndicator import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator -import io.element.android.libraries.designsystem.theme.components.Surface -import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -46,38 +36,15 @@ fun SyncStateView( isVisible: Boolean, modifier: Modifier = Modifier ) { - val animationSpec = spring(stiffness = 500F) AnimatedVisibility( - modifier = modifier, visible = isVisible, - enter = fadeIn(animationSpec = animationSpec), - exit = fadeOut(animationSpec = animationSpec), + modifier = modifier, + enter = fadeIn(spring(stiffness = 500F)), + exit = fadeOut(spring(stiffness = 500F)), ) { - Surface( - shape = RoundedCornerShape(24.dp), - shadowElevation = 8.dp, - ) { - Row( - modifier = Modifier - .background(color = ElementTheme.colors.bgSubtleSecondary) - .padding(horizontal = 24.dp, vertical = 10.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) { - CircularProgressIndicator( - modifier = Modifier - .progressSemantics() - .size(12.dp), - color = ElementTheme.colors.textPrimary, - strokeWidth = 1.5.dp, - ) - Text( - text = stringResource(id = CommonStrings.common_syncing), - color = ElementTheme.colors.textPrimary, - style = ElementTheme.typography.fontBodyMdMedium - ) - } - } + AsyncIndicator.Loading( + text = stringResource(id = CommonStrings.common_syncing), + ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index ab665acc17..c27a5ec14e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -34,6 +34,7 @@ import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.preferences.impl.about.AboutNode import io.element.android.features.preferences.impl.advanced.AdvancedSettingsNode import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode +import io.element.android.features.preferences.impl.blockedusers.BlockedUsersNode import io.element.android.features.preferences.impl.developer.DeveloperSettingsNode import io.element.android.features.preferences.impl.developer.tracing.ConfigureTracingNode import io.element.android.features.preferences.impl.notifications.NotificationSettingsNode @@ -93,6 +94,9 @@ class PreferencesFlowNode @AssistedInject constructor( @Parcelize data class UserProfile(val matrixUser: MatrixUser) : NavTarget + @Parcelize + data object BlockedUsers : NavTarget + @Parcelize data object SignOut : NavTarget } @@ -141,6 +145,10 @@ class PreferencesFlowNode @AssistedInject constructor( backstack.push(NavTarget.UserProfile(matrixUser)) } + override fun onOpenBlockedUsers() { + backstack.push(NavTarget.BlockedUsers) + } + override fun onSignOutClicked() { backstack.push(NavTarget.SignOut) } @@ -193,6 +201,9 @@ class PreferencesFlowNode @AssistedInject constructor( .target(LockScreenEntryPoint.Target.Settings) .build() } + NavTarget.BlockedUsers -> { + createNode(buildContext) + } NavTarget.SignOut -> { val callBack: LogoutEntryPoint.Callback = object : LogoutEntryPoint.Callback { override fun onChangeRecoveryKeyClicked() { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvents.kt new file mode 100644 index 0000000000..fcdf3caffa --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersEvents.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.blockedusers + +import io.element.android.libraries.matrix.api.core.UserId + +sealed interface BlockedUsersEvents { + data class Unblock(val userId: UserId) : BlockedUsersEvents + data object ConfirmUnblock : BlockedUsersEvents + data object Cancel : BlockedUsersEvents +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt new file mode 100644 index 0000000000..e277ecc9b2 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersNode.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.blockedusers + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.di.SessionScope + +@ContributesNode(SessionScope::class) +class BlockedUsersNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: BlockedUsersPresenter, +) : Node(buildContext = buildContext, plugins = plugins) { + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + BlockedUsersView( + state = state, + onBackPressed = ::navigateUp, + modifier = modifier, + ) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt new file mode 100644 index 0000000000..5424fd737d --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.blockedusers + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runUpdatingState +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.UserId +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import javax.inject.Inject + +class BlockedUsersPresenter @Inject constructor( + private val matrixClient: MatrixClient, +) : Presenter { + @Composable + override fun present(): BlockedUsersState { + val coroutineScope = rememberCoroutineScope() + + var pendingUserToUnblock by remember { + mutableStateOf(null) + } + val unblockUserAction: MutableState> = remember { + mutableStateOf(AsyncAction.Uninitialized) + } + + val ignoredUserIds by matrixClient.ignoredUsersFlow.collectAsState() + + fun handleEvents(event: BlockedUsersEvents) { + when (event) { + is BlockedUsersEvents.Unblock -> { + pendingUserToUnblock = event.userId + unblockUserAction.value = AsyncAction.Confirming + } + BlockedUsersEvents.ConfirmUnblock -> { + pendingUserToUnblock?.let { + coroutineScope.unblockUser(it, unblockUserAction) + pendingUserToUnblock = null + } + } + BlockedUsersEvents.Cancel -> { + pendingUserToUnblock = null + unblockUserAction.value = AsyncAction.Uninitialized + } + } + } + return BlockedUsersState( + blockedUsers = ignoredUserIds, + unblockUserAction = unblockUserAction.value, + eventSink = ::handleEvents + ) + } + + private fun CoroutineScope.unblockUser(userId: UserId, asyncAction: MutableState>) = launch { + runUpdatingState(asyncAction) { + matrixClient.unignoreUser(userId) + } + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt new file mode 100644 index 0000000000..8b5209a0cd --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersState.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.blockedusers + +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.UserId +import kotlinx.collections.immutable.ImmutableList + +data class BlockedUsersState( + val blockedUsers: ImmutableList, + val unblockUserAction: AsyncAction, + val eventSink: (BlockedUsersEvents) -> Unit, +) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt new file mode 100644 index 0000000000..0b5466ed04 --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersStatePreviewProvider.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.blockedusers + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import kotlinx.collections.immutable.toPersistentList + +class BlockedUsersStatePreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aBlockedUsersState(), + aBlockedUsersState(blockedUsers = emptyList()), + aBlockedUsersState(unblockUserAction = AsyncAction.Confirming), + // Sadly there's no good way to preview Loading or Failure states since they're presented with an animation + // All these 3 screen states will be displayed as the Uninitialized one + aBlockedUsersState(unblockUserAction = AsyncAction.Loading), + aBlockedUsersState(unblockUserAction = AsyncAction.Failure(Throwable("Failed to unblock user"))), + aBlockedUsersState(unblockUserAction = AsyncAction.Success(Unit)), + ) +} + +internal fun aBlockedUsersState( + blockedUsers: List = aMatrixUserList().map { it.userId }, + unblockUserAction: AsyncAction = AsyncAction.Uninitialized, +): BlockedUsersState { + return BlockedUsersState( + blockedUsers = blockedUsers.toPersistentList(), + unblockUserAction = unblockUserAction, + eventSink = {}, + ) +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt new file mode 100644 index 0000000000..6e7726cfab --- /dev/null +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersView.kt @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.blockedusers + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.compound.theme.ElementTheme +import io.element.android.features.preferences.impl.R +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.designsystem.components.async.AsyncIndicator +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorHost +import io.element.android.libraries.designsystem.components.async.rememberAsyncIndicatorState +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.components.MatrixUserRow +import io.element.android.libraries.ui.strings.CommonStrings + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BlockedUsersView( + state: BlockedUsersState, + onBackPressed: () -> Unit, + modifier: Modifier = Modifier, +) { + Box(modifier = modifier) { + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + text = stringResource(CommonStrings.common_blocked_users), + style = ElementTheme.typography.aliasScreenTitle, + ) + }, + navigationIcon = { + BackButton(onClick = onBackPressed) + } + ) + } + ) { padding -> + LazyColumn( + modifier = Modifier.padding(padding) + ) { + items(state.blockedUsers) { userId -> + BlockedUserItem( + userId = userId, + onClick = { state.eventSink(BlockedUsersEvents.Unblock(it)) } + ) + } + } + } + + val asyncIndicatorState = rememberAsyncIndicatorState() + AsyncIndicatorHost(modifier = Modifier.statusBarsPadding(), state = asyncIndicatorState) + + when (state.unblockUserAction) { + is AsyncAction.Loading -> { + LaunchedEffect(state.unblockUserAction) { + asyncIndicatorState.enqueue { + AsyncIndicator.Loading(text = stringResource(R.string.screen_blocked_users_unblocking)) + } + } + } + is AsyncAction.Failure -> { + LaunchedEffect(state.unblockUserAction) { + asyncIndicatorState.enqueue(durationMs = AsyncIndicator.DURATION_SHORT) { + AsyncIndicator.Failure(text = stringResource(CommonStrings.common_failed)) + } + } + } + is AsyncAction.Confirming -> { + ConfirmationDialog( + title = stringResource(R.string.screen_blocked_users_unblock_alert_title), + content = stringResource(R.string.screen_blocked_users_unblock_alert_description), + submitText = stringResource(R.string.screen_blocked_users_unblock_alert_action), + onSubmitClicked = { state.eventSink(BlockedUsersEvents.ConfirmUnblock) }, + onDismiss = { state.eventSink(BlockedUsersEvents.Cancel) } + ) + } + else -> Unit + } + } +} + +@Composable +private fun BlockedUserItem( + userId: UserId, + onClick: (UserId) -> Unit, +) { + MatrixUserRow( + modifier = Modifier.clickable { onClick(userId) }, + matrixUser = MatrixUser(userId), + ) +} + +@PreviewsDayNight +@Composable +internal fun BlockedUsersViewPreview(@PreviewParameter(BlockedUsersStatePreviewProvider::class) state: BlockedUsersState) { + ElementPreview { + BlockedUsersView( + state = state, + onBackPressed = {} + ) + } +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index cbdf2418ce..3bd762794d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -53,6 +53,7 @@ class PreferencesRootNode @AssistedInject constructor( fun onOpenLockScreenSettings() fun onOpenAdvancedSettings() fun onOpenUserProfile(matrixUser: MatrixUser) + fun onOpenBlockedUsers() fun onSignOutClicked() } @@ -117,6 +118,10 @@ class PreferencesRootNode @AssistedInject constructor( plugins().forEach { it.onOpenUserProfile(matrixUser) } } + private fun onOpenBlockedUsers() { + plugins().forEach { it.onOpenBlockedUsers() } + } + private fun onSignOutClicked() { plugins().forEach { it.onSignOutClicked() } } @@ -141,6 +146,7 @@ class PreferencesRootNode @AssistedInject constructor( onOpenNotificationSettings = this::onOpenNotificationSettings, onOpenLockScreenSettings = this::onOpenLockScreenSettings, onOpenUserProfile = this::onOpenUserProfile, + onOpenBlockedUsers = this::onOpenBlockedUsers, onSignOutClicked = { if (state.directLogoutState.canDoDirectSignOut) { state.directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 5539bed900..be247b8b28 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -62,6 +62,7 @@ fun PreferencesRootView( onOpenAdvancedSettings: () -> Unit, onOpenNotificationSettings: () -> Unit, onOpenUserProfile: (MatrixUser) -> Unit, + onOpenBlockedUsers: () -> Unit, onSignOutClicked: () -> Unit, modifier: Modifier = Modifier, ) { @@ -121,6 +122,11 @@ fun PreferencesRootView( onClick = onOpenNotificationSettings, ) } + ListItem( + headlineContent = { Text(stringResource(id = CommonStrings.common_blocked_users)) }, + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())), + onClick = onOpenBlockedUsers, + ) ListItem( headlineContent = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ChatProblem())), @@ -230,6 +236,7 @@ private fun ContentToPreview(matrixUser: MatrixUser) { onOpenNotificationSettings = {}, onOpenLockScreenSettings = {}, onOpenUserProfile = {}, + onOpenBlockedUsers = {}, onSignOutClicked = {}, ) } diff --git a/features/preferences/impl/src/main/res/values-be/translations.xml b/features/preferences/impl/src/main/res/values-be/translations.xml index d887078719..1f69842028 100644 --- a/features/preferences/impl/src/main/res/values-be/translations.xml +++ b/features/preferences/impl/src/main/res/values-be/translations.xml @@ -7,6 +7,9 @@ "Адрас пазначаны няправільна, пераканайцеся, што вы ўказалі пратакол (http/https) і правільны адрас." "Адключыць рэдактар фарматаванага тэксту і ўключыць Markdown." "Уключыце опцыю для прагляду крыніцы паведамлення на часовай шкале." + "Разблакіраваць" + "Вы зноў зможаце ўбачыць усе паведамленні." + "Разблакіраваць карыстальніка" "Бачнае імя" "Ваша бачнае імя" "Узнікла невядомая памылка, і інфармацыю не ўдалося змяніць." diff --git a/features/preferences/impl/src/main/res/values-bg/translations.xml b/features/preferences/impl/src/main/res/values-bg/translations.xml index ab56ee51a4..f2bbbe2482 100644 --- a/features/preferences/impl/src/main/res/values-bg/translations.xml +++ b/features/preferences/impl/src/main/res/values-bg/translations.xml @@ -1,6 +1,8 @@ "Потвърждения за прочитане" + "Отблокиране" + "Отблокиране на потребителя" "Име" "Вашето Име" "Възникна неизвестна грешка и информацията не можа да бъде променена." diff --git a/features/preferences/impl/src/main/res/values-cs/translations.xml b/features/preferences/impl/src/main/res/values-cs/translations.xml index 2e199c8bec..d66f1a2370 100644 --- a/features/preferences/impl/src/main/res/values-cs/translations.xml +++ b/features/preferences/impl/src/main/res/values-cs/translations.xml @@ -11,6 +11,9 @@ "Sdílejte přítomnost" "Pokud je tato funkce vypnutá, nebudete moci odesílat ani přijímat potvrzení o přečtení ani upozornění na psaní" "Povolit možnost zobrazení zdroje zprávy na časové ose." + "Odblokovat" + "Znovu uvidíte všechny zprávy od nich." + "Odblokovat uživatele" "Zobrazované jméno" "Vaše zobrazované jméno" "Došlo k neznámé chybě a informace nelze změnit." diff --git a/features/preferences/impl/src/main/res/values-de/translations.xml b/features/preferences/impl/src/main/res/values-de/translations.xml index 8d467dc0c7..1c9b837460 100644 --- a/features/preferences/impl/src/main/res/values-de/translations.xml +++ b/features/preferences/impl/src/main/res/values-de/translations.xml @@ -11,6 +11,10 @@ "Präsenz teilen" "Wenn diese Option deaktiviert ist, kannst du keine Lesebestätigungen oder Tipp-Benachrichtigungen senden oder empfangen." "Option aktiveren, um Nachrichtenquelle in der Zeitleiste anzuzeigen." + "Blockierung aufheben" + "Du kannst dann wieder alle Nachrichten von ihnen sehen." + "Blockierung aufheben" + "Blockierung wird aufgehoben…" "Anzeigename" "Dein Anzeigename" "Ein unbekannter Fehler ist aufgetreten und die Informationen konnten nicht geändert werden." diff --git a/features/preferences/impl/src/main/res/values-es/translations.xml b/features/preferences/impl/src/main/res/values-es/translations.xml index ee62e11c44..2a6de80c05 100644 --- a/features/preferences/impl/src/main/res/values-es/translations.xml +++ b/features/preferences/impl/src/main/res/values-es/translations.xml @@ -7,6 +7,9 @@ "URL no válida, asegúrate de incluir el protocolo (http/https) y la dirección correcta." "Desactiva el editor de texto enriquecido para escribir Markdown manualmente." "Habilita la opción para ver el contenido en bruto del mensaje en la cronología." + "Desbloquear" + "Podrás ver todos sus mensajes de nuevo." + "Desbloquear usuario" "Nombre público" "Tu nombre visible" "Se encontró un error desconocido y no se pudo cambiar la información." diff --git a/features/preferences/impl/src/main/res/values-fr/translations.xml b/features/preferences/impl/src/main/res/values-fr/translations.xml index 92cea7a048..a2d6d49feb 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -8,7 +8,12 @@ "Désactivez l’éditeur de texte enrichi pour saisir manuellement du Markdown." "Accusés de lecture" "En cas de désactivation, vos accusés de lecture ne seront pas envoyés aux autres membres. Vous verrez toujours les accusés des autres membres." + "Partager la présence" + "Si cette option est désactivée, vous ne pourrez ni envoyer ni recevoir de confirmations de lecture ni de notifications de saisie" "Activer cette option pour pouvoir voir la source des messages dans la discussion." + "Débloquer" + "Vous pourrez à nouveau voir tous ses messages." + "Débloquer l’utilisateur" "Pseudonyme" "Votre pseudonyme" "Une erreur inconnue s’est produite et les informations n’ont pas pu être modifiées." diff --git a/features/preferences/impl/src/main/res/values-hu/translations.xml b/features/preferences/impl/src/main/res/values-hu/translations.xml index f453107959..fc6cda5046 100644 --- a/features/preferences/impl/src/main/res/values-hu/translations.xml +++ b/features/preferences/impl/src/main/res/values-hu/translations.xml @@ -11,6 +11,9 @@ "Jelenlét megosztása" "Ha ki van kapcsolva, nem tud olvasási visszaigazolást vagy írási értesítést küldeni és fogadni" "Engedélyezd a beállítást az üzenet forrásának megjelenítéséhez az idővonalon." + "Letiltás feloldása" + "Újra láthatja az összes üzenetét." + "Felhasználó kitiltásának feloldása" "Megjelenítendő név" "Saját megjelenítendő név" "Ismeretlen hiba történt, és az információ módosítása nem sikerült." diff --git a/features/preferences/impl/src/main/res/values-in/translations.xml b/features/preferences/impl/src/main/res/values-in/translations.xml index 520cce2ddb..a0bfd15462 100644 --- a/features/preferences/impl/src/main/res/values-in/translations.xml +++ b/features/preferences/impl/src/main/res/values-in/translations.xml @@ -7,6 +7,9 @@ "URL tidak valid, pastikan Anda menyertakan protokol (http/https) dan alamat yang benar." "Nonaktifkan penyunting teks kaya untuk mengetik Markdown secara manual." "Aktifkan opsi untuk melihat sumber pesan dalam lini masa." + "Buka blokir" + "Anda akan dapat melihat semua pesan dari mereka lagi." + "Buka blokir pengguna" "Nama tampilan" "Nama tampilan Anda" "Terjadi kesalahan yang tidak diketahui dan informasi tidak dapat diubah." diff --git a/features/preferences/impl/src/main/res/values-it/translations.xml b/features/preferences/impl/src/main/res/values-it/translations.xml index e2a135af31..76fc34e954 100644 --- a/features/preferences/impl/src/main/res/values-it/translations.xml +++ b/features/preferences/impl/src/main/res/values-it/translations.xml @@ -11,6 +11,9 @@ "Condividi presenza online" "Se disattivato, non potrai inviare o ricevere ricevute di lettura o notifiche di digitazione." "Attiva l\'opzione per visualizzare il sorgente del messaggio nella linea temporale." + "Sblocca" + "Potrai vedere di nuovo tutti i suoi messaggi." + "Sblocca utente" "Nome da mostrare" "Il tuo nome da mostrare" "Si è verificato un errore sconosciuto e non è stato possibile modificare le informazioni." diff --git a/features/preferences/impl/src/main/res/values-ro/translations.xml b/features/preferences/impl/src/main/res/values-ro/translations.xml index df306a923d..75c0411aa8 100644 --- a/features/preferences/impl/src/main/res/values-ro/translations.xml +++ b/features/preferences/impl/src/main/res/values-ro/translations.xml @@ -11,6 +11,9 @@ "Împărtășiți prezența" "Dacă dezactivată, nu veți putea trimite sau primi chitanțe de citire sau notificări de tastare." "Activați opțiunea pentru a vizualiza sursa mesajelor." + "Deblocați" + "La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta." + "Deblocați utilizatorul" "Nume" "Numele dumneavoastra" "A fost întâlnită o eroare necunoscută și informațiile nu au putut fi modificate." diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index ad1102fa0a..383bfdae15 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -11,6 +11,10 @@ "Поделиться присутствием" "Если выключено, вы не сможете отправлять, получать уведомления о прочтении и наборе текста" "Включить опцию просмотра источника сообщения в ленте." + "Разблокировать" + "Вы снова сможете увидеть все сообщения." + "Разблокировать пользователя" + "Разблокировка…" "Отображаемое имя" "Ваше отображаемое имя" "Произошла неизвестная ошибка, изменить информацию не удалось." diff --git a/features/preferences/impl/src/main/res/values-sk/translations.xml b/features/preferences/impl/src/main/res/values-sk/translations.xml index b11eb8c277..68f3be9784 100644 --- a/features/preferences/impl/src/main/res/values-sk/translations.xml +++ b/features/preferences/impl/src/main/res/values-sk/translations.xml @@ -11,6 +11,9 @@ "Zdieľať prítomnosť" "Ak je vypnuté, nebudete môcť odosielať ani prijímať potvrdenia o prečítaní alebo písať upozornenia" "Povoliť možnosť zobrazenia zdroja správy na časovej osi." + "Odblokovať" + "Všetky správy od nich budete môcť opäť vidieť." + "Odblokovať používateľa" "Zobrazované meno" "Vaše zobrazované meno" "Vyskytla sa neznáma chyba a informácie nebolo možné zmeniť." diff --git a/features/preferences/impl/src/main/res/values-sv/translations.xml b/features/preferences/impl/src/main/res/values-sv/translations.xml index 9d64053bba..33ac072dea 100644 --- a/features/preferences/impl/src/main/res/values-sv/translations.xml +++ b/features/preferences/impl/src/main/res/values-sv/translations.xml @@ -1,5 +1,8 @@ + "Avblockera" + "Du kommer att kunna se alla meddelanden från dem igen." + "Avblockera användare" "Visningsnamn" "Ditt visningsnamn" "Ett okänt fel påträffades och informationen kunde inte ändras." diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 951cfb317a..5168d088b8 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -11,6 +11,10 @@ "Share presence" "If turned off, you won’t be able to send or receive read receipts or typing notifications" "Enable option to view message source in the timeline." + "Unblock" + "You\'ll be able to see all messages from them again." + "Unblock user" + "Unblocking…" "Display name" "Your display name" "An unknown error was encountered and the information couldn\'t be changed." diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTests.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTests.kt new file mode 100644 index 0000000000..3bcd730fed --- /dev/null +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenterTests.kt @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.preferences.impl.blockedusers + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncAction +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_ID_2 +import io.element.android.libraries.matrix.test.FakeMatrixClient +import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class BlockedUsersPresenterTests { + @Test + fun `present - initial state with no blocked users`() = runTest { + val presenter = aBlockedUsersPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + with(awaitItem()) { + assertThat(blockedUsers).isEmpty() + assertThat(unblockUserAction).isEqualTo(AsyncAction.Uninitialized) + } + } + } + + @Test + fun `present - initial state with blocked users`() = runTest { + val matrixClient = FakeMatrixClient().apply { + ignoredUsersFlow.value = persistentListOf(A_USER_ID) + } + val presenter = aBlockedUsersPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + with(awaitItem()) { + assertThat(blockedUsers).isEqualTo(persistentListOf(A_USER_ID)) + assertThat(unblockUserAction).isEqualTo(AsyncAction.Uninitialized) + } + } + } + + @Test + fun `present - blocked users list updates with new emissions`() = runTest { + val matrixClient = FakeMatrixClient().apply { + ignoredUsersFlow.value = persistentListOf(A_USER_ID) + } + val presenter = aBlockedUsersPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + with(awaitItem()) { + assertThat(blockedUsers).containsAtLeastElementsIn(persistentListOf(A_USER_ID)) + assertThat(unblockUserAction).isEqualTo(AsyncAction.Uninitialized) + } + + matrixClient.ignoredUsersFlow.value = persistentListOf(A_USER_ID, A_USER_ID_2) + with(awaitItem()) { + assertThat(blockedUsers).isEqualTo(persistentListOf(A_USER_ID, A_USER_ID_2)) + assertThat(unblockUserAction).isEqualTo(AsyncAction.Uninitialized) + } + } + } + + @Test + fun `present - unblock user`() = runTest { + val matrixClient = FakeMatrixClient().apply { + ignoredUsersFlow.value = persistentListOf(A_USER_ID) + } + val presenter = aBlockedUsersPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(BlockedUsersEvents.Unblock(A_USER_ID)) + + assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Confirming::class.java) + initialState.eventSink(BlockedUsersEvents.ConfirmUnblock) + + assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Loading::class.java) + assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Success::class.java) + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - unblock user handles failure`() = runTest { + val matrixClient = FakeMatrixClient().apply { + ignoredUsersFlow.value = persistentListOf(A_USER_ID) + givenUnignoreUserResult(Result.failure(IllegalStateException("User not banned"))) + } + val presenter = aBlockedUsersPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(BlockedUsersEvents.Unblock(A_USER_ID)) + + assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Confirming::class.java) + initialState.eventSink(BlockedUsersEvents.ConfirmUnblock) + + assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Loading::class.java) + assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Failure::class.java) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - unblock user then cancel`() = runTest { + val matrixClient = FakeMatrixClient().apply { + ignoredUsersFlow.value = persistentListOf(A_USER_ID) + givenUnignoreUserResult(Result.failure(IllegalStateException("User not banned"))) + } + val presenter = aBlockedUsersPresenter(matrixClient = matrixClient) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(BlockedUsersEvents.Unblock(A_USER_ID)) + + assertThat(awaitItem().unblockUserAction).isInstanceOf(AsyncAction.Confirming::class.java) + initialState.eventSink(BlockedUsersEvents.Cancel) + + assertThat(awaitItem().unblockUserAction).isEqualTo(AsyncAction.Uninitialized) + } + } + + @Test + fun `present - confirm unblock without a pending blocked user does nothing`() = runTest { + val presenter = aBlockedUsersPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().eventSink(BlockedUsersEvents.ConfirmUnblock) + ensureAllEventsConsumed() + } + } + + private fun aBlockedUsersPresenter( + matrixClient: FakeMatrixClient = FakeMatrixClient(), + ) = BlockedUsersPresenter(matrixClient) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicator.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicator.kt new file mode 100644 index 0000000000..b15c7d2ddc --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicator.kt @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.components.async + +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.progressSemantics +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.Icon + +/** + * A helper to create [AsyncIndicatorView] with some defaults. + */ +@Stable +object AsyncIndicator { + /** + * A loading async indicator. + * @param text The text to display. + * @param modifier The modifier to apply to the indicator. + */ + @Composable + fun Loading( + text: String, + modifier: Modifier = Modifier, + ) { + AsyncIndicatorView( + modifier = modifier, + text = text, + spacing = 10.dp, + ) { + CircularProgressIndicator( + modifier = Modifier + .progressSemantics() + .size(12.dp), + color = ElementTheme.colors.textPrimary, + strokeWidth = 1.5.dp, + ) + } + } + + /** + * A failure async indicator. + * @param text The text to display. + * @param modifier The modifier to apply to the indicator. + */ + @Composable + fun Failure( + text: String, + modifier: Modifier = Modifier, + ) { + AsyncIndicatorView( + modifier = modifier, + text = text, + spacing = defaultSpacing + ) { + Icon( + modifier = Modifier.size(18.dp), + imageVector = CompoundIcons.Close(), + contentDescription = null, + ) + } + } + + /** + * A custom async indicator. + * @param text The text to display. + * @param modifier The modifier to apply to the indicator. + * @param spacing The spacing between the leading content and the text. + * @param leadingContent The leading content to display. + */ + @Composable + fun Custom( + text: String, + modifier: Modifier = Modifier, + spacing: Dp = defaultSpacing, + leadingContent: @Composable (() -> Unit)? = null, + ) { + AsyncIndicatorView( + modifier = modifier, + text = text, + spacing = spacing, + leadingContent = leadingContent, + ) + } + + /** + * A short duration to display indicators. + */ + const val DURATION_SHORT = 3000L + + /** + * A long duration to display indicators. + */ + const val DURATION_LONG = 5000L + + private val defaultSpacing = 4.dp +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorHost.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorHost.kt new file mode 100644 index 0000000000..52296b0a2c --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorHost.kt @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.components.async + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.MutableTransitionState +import androidx.compose.animation.core.spring +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.Stable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalInspectionMode +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +@Stable +class AsyncIndicatorState { + private val queue = SnapshotStateList() + val currentItem = mutableStateOf(null) + val currentAnimationState = MutableTransitionState(false) + + /** + * Enqueue a new indicator to be displayed. + * @param durationMs The duration to display the indicator, if `null` (the default value) it will be displayed indefinitely, until the next indicator is + * displayed or the current one is manually cleared. + * @param composable The composable to display. + */ + fun enqueue(durationMs: Long? = null, composable: @Composable () -> Unit) { + queue.add(AsyncIndicatorItem(composable, durationMs)) + if (currentItem.value == null || currentItem.value?.durationMs == null) { + nextState() + } + } + + internal fun nextState() { + if (!currentAnimationState.isIdle) return + + if (currentItem.value != null && currentAnimationState.currentState && currentAnimationState.isIdle) { + // Is visible and not animating, start the exit animation + currentAnimationState.targetState = false + } else if (currentItem.value == null || !currentAnimationState.currentState && currentAnimationState.isIdle) { + // Not visible or present, start the enter animation for the next item + val newItem = queue.removeFirstOrNull() + if (newItem != null) { + currentItem.value = null + currentAnimationState.targetState = true + } + currentItem.value = newItem + } + } + + /** + * Clear the current indicator using its exit animation. + */ + fun clear() { + currentAnimationState.targetState = false + } +} + +/** + * An item to be displayed in the [AsyncIndicatorHost]. + */ +data class AsyncIndicatorItem( + val composable: @Composable () -> Unit, + val durationMs: Long? = null, +) + +/** + * Remember an [AsyncIndicatorState] instance. + */ +@Composable +fun rememberAsyncIndicatorState(): AsyncIndicatorState { + return remember { AsyncIndicatorState() } +} + +/** + * A host for displaying async indicators. + * @param modifier The modifier to apply. + * @param state The [AsyncIndicatorState] which values this component will display. + * @param enterTransition The enter transition to use for the displayed indicators. + * @param exitTransition The exit transition to use for the hiding indicators. + */ +@Composable +fun AsyncIndicatorHost( + modifier: Modifier = Modifier, + state: AsyncIndicatorState = rememberAsyncIndicatorState(), + enterTransition: EnterTransition = fadeIn(spring(stiffness = 500F)) + slideInVertically(), + exitTransition: ExitTransition = fadeOut(spring(stiffness = 500F)) + slideOutVertically(), +) { + val coroutineScope = rememberCoroutineScope() + Box( + modifier = modifier.fillMaxWidth(), + contentAlignment = Alignment.TopCenter, + ) { + if (LocalInspectionMode.current) { + state.currentItem.value?.composable?.invoke() + } else { + state.currentItem.value?.let { item -> + AnimatedVisibility( + visibleState = state.currentAnimationState, + enter = enterTransition, + exit = exitTransition, + ) { + item.composable() + } + + if (state.currentAnimationState.hasEntered() && item.durationMs != null) { + SideEffect { + coroutineScope.launch { + delay(item.durationMs) + state.nextState() + } + } + } else if (state.currentAnimationState.hasExited()) { + SideEffect { + state.nextState() + } + } + } + } + } +} + +internal fun MutableTransitionState.hasEntered() = currentState && isIdle +internal fun MutableTransitionState.hasExited() = !currentState && isIdle diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorView.kt new file mode 100644 index 0000000000..897d9ffc9a --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncIndicatorView.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.components.async + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Surface +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +internal fun AsyncIndicatorView( + text: String, + spacing: Dp, + modifier: Modifier = Modifier, + elevation: Dp = 8.dp, + leadingContent: @Composable (() -> Unit)?, +) { + Box( + modifier = modifier + .padding(horizontal = 32.dp) + .padding(elevation) + ) { + Surface( + shape = RoundedCornerShape(24.dp), + shadowElevation = elevation, + ) { + Row( + modifier = Modifier + .background(color = ElementTheme.colors.bgSubtleSecondary) + .padding(horizontal = 24.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(spacing) + ) { + leadingContent?.let { view -> + view() + } + Text( + text = text, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = ElementTheme.colors.textPrimary, + style = ElementTheme.typography.fontBodyMdMedium + ) + } + } + } +} + +@PreviewsDayNight +@Composable +internal fun AsyncIndicatorView_Loading_Preview() { + ElementPreview { + AsyncIndicator.Loading(text = "Loading") + } +} + +@PreviewsDayNight +@Composable +internal fun AsyncIndicatorView_Failed_Preview() { + ElementPreview { + AsyncIndicator.Failure(text = "Failed") + } +} diff --git a/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncIndicatorTests.kt b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncIndicatorTests.kt new file mode 100644 index 0000000000..fb6f70040b --- /dev/null +++ b/libraries/designsystem/src/test/kotlin/io/element/android/libraries/designsystem/component/async/AsyncIndicatorTests.kt @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.component.async + +import androidx.compose.animation.core.Transition +import androidx.compose.animation.core.updateTransition +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.rememberCoroutineScope +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorItem +import io.element.android.libraries.designsystem.components.async.AsyncIndicatorState +import io.element.android.libraries.designsystem.components.async.hasEntered +import io.element.android.libraries.designsystem.components.async.hasExited +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class AsyncIndicatorTests { + @Test + fun `initial state`() = runTest { + val state = AsyncIndicatorState() + moleculeFlow(RecompositionMode.Immediate) { + val transitionState = fakeAsyncIndicatorHost(state = state) + val item = state.currentItem.value + Snapshot( + currentItem = item, + currentAnimationState = TransitionStateSnapshot(transitionState), + ) + }.test { + with(awaitItem()) { + assertThat(currentItem).isNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isFalse() + } + } + } + + @Test + fun `add item with timeout`() = runTest(StandardTestDispatcher()) { + val state = AsyncIndicatorState() + moleculeFlow(RecompositionMode.Immediate) { + val transitionState = fakeAsyncIndicatorHost(state = state) + val item = state.currentItem.value + Snapshot( + currentItem = item, + currentAnimationState = TransitionStateSnapshot(transitionState), + ) + }.test { + skipItems(1) + state.enqueue(durationMs = 1000, composable = {}) + // Give it some time to pre-load the events + advanceTimeBy(1000) + runCurrent() + // First, item is invisible but the target state is visible (will start animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isTrue() + } + // Then, item is visible and the target state is visible (stopped animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isTrue() + } + // Then, item is visible and the target state is not visible (will start animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isFalse() + } + // Then, item is not visible and the target state is not visible (stopped animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isFalse() + } + // Finally, the current item is removed + with(awaitItem()) { + assertThat(currentItem).isNull() + } + } + } + + @Test + fun `add item without timeout`() = runTest(StandardTestDispatcher()) { + val state = AsyncIndicatorState() + moleculeFlow(RecompositionMode.Immediate) { + val transitionState = fakeAsyncIndicatorHost(state = state) + val item = state.currentItem.value + Snapshot( + currentItem = item, + currentAnimationState = TransitionStateSnapshot(transitionState), + ) + }.test { + skipItems(1) + state.enqueue(composable = {}) + // First, item is invisible but the target state is visible (will start animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isTrue() + } + // Then, item is visible and the target state is visible (stopped animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isTrue() + } + // That's all, the current item will be displayed indefinitely + ensureAllEventsConsumed() + } + } + + @Test + fun `add item without timeout then clear`() = runTest(StandardTestDispatcher()) { + val state = AsyncIndicatorState() + moleculeFlow(RecompositionMode.Immediate) { + val transitionState = fakeAsyncIndicatorHost(state = state) + val item = state.currentItem.value + Snapshot( + currentItem = item, + currentAnimationState = TransitionStateSnapshot(transitionState), + ) + }.test { + skipItems(1) + state.enqueue(composable = {}) + // First, item is invisible but the target state is visible (will start animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isTrue() + } + // Then, item is visible and the target state is visible (stopped animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isTrue() + } + // Clear the current item + state.clear() + // Animating the exit animation + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isFalse() + } + // Current item is no longer visible + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isFalse() + } + // Finally, the current item is removed + with(awaitItem()) { + assertThat(currentItem).isNull() + } + } + } + + @Test + fun `add item without timeout, then another one`() = runTest(StandardTestDispatcher()) { + val state = AsyncIndicatorState() + moleculeFlow(RecompositionMode.Immediate) { + val transitionState = fakeAsyncIndicatorHost(state = state) + val item = state.currentItem.value + Snapshot( + currentItem = item, + currentAnimationState = TransitionStateSnapshot(transitionState), + ) + }.test { + var firstItem: Any? = null + skipItems(1) + state.enqueue(composable = {}) + state.enqueue(composable = {}) + // First, item is invisible but the target state is visible (will start animating) + with(awaitItem()) { + firstItem = currentItem + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isTrue() + } + // Then, item is visible and the target state is visible (stopped animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isTrue() + } + // Then, item is visible and the target state is not visible (will start animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isFalse() + } + // Then, item is not visible and the target state is not visible (stopped animating) + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isFalse() + } + // Then a new item will be not visible and its target animation visible + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(firstItem).isNotEqualTo(currentItem) + assertThat(currentAnimationState.currentState).isFalse() + assertThat(currentAnimationState.targetState).isTrue() + } + // Finally, the second item is visible and not animating + with(awaitItem()) { + assertThat(currentItem).isNotNull() + assertThat(firstItem).isNotEqualTo(currentItem) + assertThat(currentAnimationState.currentState).isTrue() + assertThat(currentAnimationState.targetState).isTrue() + } + // That's all, the current item will be displayed indefinitely + ensureAllEventsConsumed() + } + } + + @Composable + private fun fakeAsyncIndicatorHost(state: AsyncIndicatorState): Transition? { + val coroutineScope = rememberCoroutineScope() + val transition = state.currentItem.value?.let { + // If there is an item, update its transition state to simulate an animation + updateTransition(state.currentAnimationState, label = "") + } + if (state.currentAnimationState.hasEntered() && state.currentItem.value?.durationMs != null) { + SideEffect { + coroutineScope.launch { + delay(state.currentItem.value!!.durationMs!!) + state.nextState() + } + } + } else if (state.currentItem.value != null && state.currentAnimationState.hasExited()) { + SideEffect { + state.nextState() + } + } + return transition + } + + private data class Snapshot( + val currentItem: AsyncIndicatorItem?, + val currentAnimationState: TransitionStateSnapshot, + ) + + private data class TransitionStateSnapshot( + val currentState: Boolean, + val targetState: Boolean, + ) { + constructor(transition: Transition?) : this( + currentState = transition?.currentState ?: false, + targetState = transition?.targetState ?: false, + ) + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 5c5de97a9c..49e30e5158 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -34,7 +34,9 @@ import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.StateFlow import java.io.Closeable interface MatrixClient : Closeable { @@ -43,6 +45,7 @@ interface MatrixClient : Closeable { val roomListService: RoomListService val mediaLoader: MatrixMediaLoader val sessionCoroutineScope: CoroutineScope + val ignoredUsersFlow: StateFlow> suspend fun getRoom(roomId: RoomId): MatrixRoom? suspend fun findDM(userId: UserId): RoomId? suspend fun ignoreUser(userId: UserId): Result diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 521d26f062..c27b75361b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -62,16 +62,24 @@ import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper import io.element.android.libraries.matrix.impl.util.SessionDirectoryNameProvider import io.element.android.libraries.matrix.impl.util.cancelAndDestroy +import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout @@ -79,6 +87,7 @@ import org.matrix.rustcomponents.sdk.BackupState import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientDelegate import org.matrix.rustcomponents.sdk.FilterTimelineEventType +import org.matrix.rustcomponents.sdk.IgnoredUsersListener import org.matrix.rustcomponents.sdk.NotificationProcessSetup import org.matrix.rustcomponents.sdk.PowerLevels import org.matrix.rustcomponents.sdk.Room @@ -240,6 +249,16 @@ class RustMatrixClient( private val clientDelegateTaskHandle: TaskHandle? = client.setDelegate(clientDelegate) + override val ignoredUsersFlow = mxCallbackFlow> { + client.subscribeToIgnoredUsers(object : IgnoredUsersListener { + override fun call(ignoredUserIds: List) { + channel.trySend(ignoredUserIds.map(::UserId).toPersistentList()) + } + }) + } + .buffer(Channel.UNLIMITED) + .stateIn(sessionCoroutineScope, started = SharingStarted.Eagerly, initialValue = persistentListOf()) + init { roomListService.state.onEach { state -> if (state == RoomListService.State.Running) { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 1684a1715d..7b3e09e3e3 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -43,8 +43,11 @@ import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.tests.testutils.simulateLongTask +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope class FakeMatrixClient( @@ -70,6 +73,8 @@ class FakeMatrixClient( var removeAvatarCalled: Boolean = false private set + override val ignoredUsersFlow: MutableStateFlow> = MutableStateFlow(persistentListOf()) + private var ignoreUserResult: Result = Result.success(Unit) private var unignoreUserResult: Result = Result.success(Unit) private var createRoomResult: Result = Result.success(A_ROOM_ID) diff --git a/libraries/ui-strings/src/main/res/values-be/translations.xml b/libraries/ui-strings/src/main/res/values-be/translations.xml index cc8d23592c..91948a272d 100644 --- a/libraries/ui-strings/src/main/res/values-be/translations.xml +++ b/libraries/ui-strings/src/main/res/values-be/translations.xml @@ -236,9 +236,6 @@ "Гэй, пагавары са мной у %1$s: %2$s" "%1$s Android" "Rageshake паведаміць пра памылку" - "Разблакіраваць" - "Вы зноў зможаце ўбачыць усе паведамленні." - "Разблакіраваць карыстальніка" "Не ўдалося выбраць носьбіт, паўтарыце спробу." "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз." "Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз." diff --git a/libraries/ui-strings/src/main/res/values-bg/translations.xml b/libraries/ui-strings/src/main/res/values-bg/translations.xml index b4635d552a..b60563eaa7 100644 --- a/libraries/ui-strings/src/main/res/values-bg/translations.xml +++ b/libraries/ui-strings/src/main/res/values-bg/translations.xml @@ -194,8 +194,6 @@ "🔐️ Присъединете се към мен в %1$s" "Хей, говорете с мен в %1$s: %2$s" "%1$s Android" - "Отблокиране" - "Отблокиране на потребителя" "Споделяне на местоположение" "Споделяне на моето местоположение" "Отваряне в Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index b5c844b77a..c3f368bf9f 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -237,9 +237,6 @@ "Ahoj, ozvi se mi na %1$s: %2$s" "%1$s Android" "Zatřeste zařízením pro nahlášení chyby" - "Odblokovat" - "Znovu uvidíte všechny zprávy od nich." - "Odblokovat uživatele" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 6855e1bfae..1905cdec8c 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -237,10 +237,6 @@ "Hey, sprich mit mir auf %1$s: %2$s" "%1$s Android" "Schüttel heftig zum Melden von Fehlern" - "Blockierung aufheben" - "Du kannst dann wieder alle Nachrichten von ihnen sehen." - "Blockierung aufheben" - "Blockierung wird aufgehoben…" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut." diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index 4482304cb7..7377ed9ec7 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -231,9 +231,6 @@ "Hola, puedes hablar conmigo en %1$s: %2$s" "%1$s Android" "Agitar con fuerza para informar de un error" - "Desbloquear" - "Podrás ver todos sus mensajes de nuevo." - "Desbloquear usuario" "Error al seleccionar archivos multimedia, por favor inténtalo de nuevo." "Error al procesar el contenido multimedia, por favor inténtalo de nuevo." "Error al subir el contenido multimedia, por favor inténtalo de nuevo." diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index f8fe8b89b8..787aa8396f 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -234,9 +234,6 @@ "Salut, parle-moi sur %1$s : %2$s" "%1$s Android" "Rageshake pour signaler un problème" - "Débloquer" - "Vous pourrez à nouveau voir tous ses messages." - "Débloquer l’utilisateur" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." diff --git a/libraries/ui-strings/src/main/res/values-hu/translations.xml b/libraries/ui-strings/src/main/res/values-hu/translations.xml index c77d757bc4..f3261f6b6a 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -233,9 +233,6 @@ "Beszélgessünk a(z) %1$s: %2$s -n" "%1$s Android" "Az eszköz rázása a hibajelentéshez" - "Letiltás feloldása" - "Újra láthatja az összes üzenetét." - "Felhasználó kitiltásának feloldása" "Nem sikerült kiválasztani a médiát, próbálja újra." "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Nem sikerült a média feltöltése, próbálja újra." diff --git a/libraries/ui-strings/src/main/res/values-in/translations.xml b/libraries/ui-strings/src/main/res/values-in/translations.xml index a15b808fd3..63ad840fed 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -227,9 +227,6 @@ "Hai, bicaralah dengan saya di %1$s: %2$s" "%1$s Android" "Rageshake untuk melaporkan kutu" - "Buka blokir" - "Anda akan dapat melihat semua pesan dari mereka lagi." - "Buka blokir pengguna" "Gagal memilih media, silakan coba lagi." "Gagal memproses media untuk diunggah, silakan coba lagi." "Gagal mengunggah media, silakan coba lagi." diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 7dfc9f8a47..752f126dd2 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -233,9 +233,6 @@ "Ehi, parlami su %1$s: %2$s" "%1$s Android" "Scuoti per segnalare un problema" - "Sblocca" - "Potrai vedere di nuovo tutti i suoi messaggi." - "Sblocca utente" "Selezione del file multimediale fallita, riprova." "Elaborazione del file multimediale da caricare fallita, riprova." "Caricamento del file multimediale fallito, riprova." diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 16d1b8165a..838b6d15cc 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -238,9 +238,6 @@ "Hei, vorbește cu mine pe %1$s: %2$s" "%1$s Android" "Rageshake pentru a raporta erori" - "Deblocați" - "La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta." - "Deblocați utilizatorul" "Selectarea fișierelor media a eșuat, încercați din nou." "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Încărcarea fișierelor media a eșuat, încercați din nou." diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 2296b3c393..b06128a659 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -113,6 +113,7 @@ "Аналитика" "Внешний вид" "Аудио" + "Заблокированные пользователи" "Пузыри" "Резервная копия чатов" "Авторское право" @@ -129,7 +130,9 @@ "Введите свой PIN-код" "Ошибка" "Для всех" + "Ошибка" "Избранное" + "Избранное" "Файл" "Файл сохранен в «Загрузки»" "Переслать сообщение" @@ -155,6 +158,7 @@ "Без звука" "Ничего не найдено" "Не в сети" + "или" "Пароль" "Люди" "Постоянная ссылка" @@ -237,9 +241,6 @@ "Привет, поговори со мной по %1$s: %2$s" "%1$s Android" "Встряхните устройство, чтобы сообщить об ошибке" - "Разблокировать" - "Вы снова сможете увидеть все сообщения." - "Разблокировать пользователя" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 0a58e2c9e8..09468c6656 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -237,9 +237,6 @@ "Ahoj, porozprávajte sa so mnou na %1$s: %2$s" "%1$s Android" "Zúrivo potriasť pre nahlásenie chyby" - "Odblokovať" - "Všetky správy od nich budete môcť opäť vidieť." - "Odblokovať používateľa" "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa nahrať médiá, skúste to prosím znova." diff --git a/libraries/ui-strings/src/main/res/values-sv/translations.xml b/libraries/ui-strings/src/main/res/values-sv/translations.xml index 1196187694..8ee3e87a29 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -173,9 +173,6 @@ "Hallå, prata med mig på %1$s: %2$s" "%1$s Android" "Raseriskaka för att rapportera bugg" - "Avblockera" - "Du kommer att kunna se alla meddelanden från dem igen." - "Avblockera användare" "Misslyckades att välja media, vänligen pröva igen." "Misslyckades att bearbeta media för uppladdning, vänligen pröva igen." "Misslyckades att ladda upp media, vänligen pröva igen." diff --git a/libraries/ui-strings/src/main/res/values-uk-rUA/translations.xml b/libraries/ui-strings/src/main/res/values-uk-rUA/translations.xml index cc4a7457b1..0c70602535 100644 --- a/libraries/ui-strings/src/main/res/values-uk-rUA/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk-rUA/translations.xml @@ -238,9 +238,6 @@ "Привіт, пишіть мені за адресою %1$s: %2$s" "%1$s Android" "Повідомити про ваду за допомогою Rageshake" - "Розблокувати" - "Ви знову зможете бачити всі повідомлення від них." - "Розблокувати користувача" "Не вдалося вибрати медіафайл, спробуйте ще раз." "Не вдалося обробити медіафайл для завантаження, спробуйте ще раз." "Не вдалося завантажити медіафайл, спробуйте ще раз." diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 2469703ea6..845902212d 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -213,8 +213,6 @@ "有些訊息尚未傳送" "嘿,來 %1$s 和我聊天:%2$s" "%1$s Android" - "解除封鎖" - "解除封鎖使用者" "無法上傳媒體檔案,請稍後再試。" "分享位置" "分享我的位置" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 6d6a499f4e..ebbcfcc8d5 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -237,10 +237,6 @@ "Hey, talk to me on %1$s: %2$s" "%1$s Android" "Rageshake to report bug" - "Unblock" - "You\'ll be able to see all messages from them again." - "Unblock user" - "Unblocking…" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Day-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Day-1_1_null,NEXUS_5,1.0,en].png index dc368efc72..1210af066e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Day-1_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Day-1_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f12f65bd54debdd46849ee2de58727cc5525df402e6467e641218184aa017c7 -size 9628 +oid sha256:3a5d28daefa0d088c24136406fad2c0457585784cfbf7aefc8e720d203ed5d1f +size 9744 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Night-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Night-1_2_null,NEXUS_5,1.0,en].png index 6f1e00083a..4160c1c63b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Night-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[appnav.loggedin_SyncStateView_null_SyncStateView-Night-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a5a759ada36389158202ec331ca097da81cf487dcfab639dc15018a5f5fc03c -size 8229 +oid sha256:4a9bbf2aa592c0b6d4b7b09a09b5ee94f1b8a26427852c43e49278b6e7fb4266 +size 8399 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d901b548ee --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0cb8cd165599d1bae48bc9b6334e519cdba89287756b65caae86cd268667f40 +size 62939 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0823188ced --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c9e8374a634bf28b84e9945596a0c144971f550f8c1cd57abc0f4db30556fe5 +size 8959 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b21bd70471 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:709a470ac8764fee25eca83978daf039c089f2e8073b22afc1aa08fcf0d69998 +size 60072 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3c2b0f964a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a515efeb80f1380d661f8531f0c85bb46abd71bf535e050444fbb9f3e72f7011 +size 65964 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b1ee32bb22 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a47cd564da066d38db3e26f7b80b2a6e9a63ab276a2af688bee10bae462e775 +size 65580 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d901b548ee --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Day-3_4_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0cb8cd165599d1bae48bc9b6334e519cdba89287756b65caae86cd268667f40 +size 62939 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..35d8c664af --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:faa8c0cfc37478b8396d40c793ee3388fadfea8463c5f7cc4db1f5cc2cf10144 +size 58891 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5317ea009a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc12e1a6ebd2340718bfb70a5ceb265c526cef8c1fac285e1e6ec924a7c12324 +size 8393 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..821ae7cb21 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc5c62caa2da7e51d035ffc7a00d9d3abaeee06cbd48c3d0a6e7e3226c04e4e4 +size 55857 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8a94e17cbb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6974b5d06b3d8b64bf26c3b0b48cd57a2c2e38cee6aa8b06bb85d64392e1b685 +size 60855 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..eb756b484e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96053ba2bb1dbbb69cd0ddb7e218ad68401ee1344844508766c5724a08e171fb +size 60491 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..35d8c664af --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.blockedusers_BlockedUsersView_null_BlockedUsersView-Night-3_5_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:faa8c0cfc37478b8396d40c793ee3388fadfea8463c5f7cc4db1f5cc2cf10144 +size 58891 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-5_6_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-4_5_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Day-5_6_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-4_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-5_7_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-4_6_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer.tracing_ConfigureTracingView_null_ConfigureTracingView-Night-5_7_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_4_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-4_5_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_5_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-4_6_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-8_9_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-7_8_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Day-8_9_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-8_10_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-7_9_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_DefaultNotificationSettingOption_null_DefaultNotificationSettingOption-Night-8_10_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-8_9_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Day-9_10_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-8_10_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_null_EditDefaultNotificationSettingView-Night-9_11_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Day-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Day-7_8_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Day-6_7_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Day-7_8_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Night-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Night-7_9_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Night-6_8_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_InvalidNotificationSettingsView_null_InvalidNotificationSettingsView-Night-7_9_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-5_6_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Day-6_7_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-5_7_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.notifications_NotificationSettingsView_null_NotificationSettingsView-Night-6_8_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png index 6d7a1cf94e..d4df69f743 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a508cc1bef69d9b5b2ccd96b8090892263781b7188c17158267c7a6347fb9ec3 -size 36900 +oid sha256:be4d708775d03680be450dfd5eebfeda24f1841eedced3e7de2ba036669ac479 +size 39086 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png index 245cc5021a..9d950f5916 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:faf22bc041d24f7c9cc36324237f4c2da81f655b42c3381cfe72ea1ae1037f0e -size 36539 +oid sha256:53660f4354d2a750ab539370151c9ee4ac49f9d06712f7646fdd487bf133753a +size 38766 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png index c95c1559d5..d8b913deea 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54e5b726e9aba9fddbba19e658314ad8375ec717d6e3ae086d0c3afd7f9af00a -size 38814 +oid sha256:8ae1828957e00adcbdbb772a0ebdc1fb6604afbe644cc5d8662da9fcd3973129 +size 41162 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png index 23981dd746..7f24f64da5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:078df60d22b49832f6c5cafe2c9ab656541f44c853e28f02ee350f64c5bae761 -size 38767 +oid sha256:58e567815b4dc36abf8422b17f9902d8581dbec1a1ed216df2f120c6198b6fe5 +size 41124 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-11_12_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_11_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-11_12_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-11_13_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_12_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-11_13_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-9_10_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Day-10_11_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-9_11_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user_UserPreferences_null_UserPreferences-Night-10_12_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Day_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e571a7d2d0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Day_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cffbcd958bcbf3a55971014af6ad0d1e07e21ee02c63cb599bd6c737f524e628 +size 8869 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Night_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..85f8ca2142 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Failed__null_AsyncIndicatorView_Failed_-Night_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d22c25a8af6da4b87e96f452ebccb95a242fa0d91f652040d43d3eda0ba8675d +size 7555 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Day_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..02d1fcd138 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Day_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1092852ff43cb3d9a04412ce24b29860e8f83c53999a67e469614a2fe846132 +size 9369 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Night_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2043108353 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.async_AsyncIndicatorView_Loading__null_AsyncIndicatorView_Loading_-Night_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f12481ac4baa8866b845199e727f297557e4eeea963a8bcee86bfecbe9b999f6 +size 8010 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 2416876fa4..f87f071ba4 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -180,7 +180,8 @@ "screen_advanced_settings_.*", "screen\\.advanced_settings\\..*", "screen_edit_profile_.*", - "screen_notification_settings_.*" + "screen_notification_settings_.*", + "screen_blocked_users_.*" ] }, {