From ffedc54c8b0c2c0816f7816235dd8b0921c6274b Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 3 Oct 2025 16:40:26 +0100 Subject: [PATCH 1/2] Use the new notification sound. --- ElementX/Resources/Sounds/message.caf | Bin 30140 -> 24428 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ElementX/Resources/Sounds/message.caf b/ElementX/Resources/Sounds/message.caf index 4c241b58666d398fbff465087574eebe6b28ec28..55de8775137b31ef471af7bff24c39afd4d88fbe 100644 GIT binary patch literal 24428 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 From 627282e7837891b6653e3e1e672bebdfe402c29e Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 3 Oct 2025 17:59:05 +0100 Subject: [PATCH 2/2] Add a banner informing the user of the new notification sound. --- .../Sources/GeneratedAccessibilityTests.swift | 4 ++ ElementX.xcodeproj/project.pbxproj | 4 ++ .../en.lproj/Localizable.strings | 2 + .../Sources/Application/AppCoordinator.swift | 5 ++ .../Application/Settings/AppSettings.swift | 5 ++ ElementX/Sources/Generated/Strings.swift | 4 ++ .../TestablePreviewsDictionary.swift | 1 + .../Screens/HomeScreen/HomeScreenModels.swift | 6 ++ .../HomeScreen/HomeScreenViewModel.swift | 8 +++ .../HomeScreen/View/HomeScreenContent.swift | 6 +- .../View/HomeScreenNewSoundBanner.swift | 59 +++++++++++++++++++ .../Sources/GeneratedPreviewTests.swift | 6 ++ .../homeScreenNewSoundBanner.iPad-en-GB-0.png | 3 + ...homeScreenNewSoundBanner.iPad-pseudo-0.png | 3 + ...ScreenNewSoundBanner.iPhone-16-en-GB-0.png | 3 + ...creenNewSoundBanner.iPhone-16-pseudo-0.png | 3 + .../Sources/HomeScreenViewModelTests.swift | 13 ++++ 17 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 ElementX/Sources/Screens/HomeScreen/View/HomeScreenNewSoundBanner.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-en-GB-0.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-pseudo-0.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-en-GB-0.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-pseudo-0.png diff --git a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift index 81851cf451..aedd933673 100644 --- a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift +++ b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift @@ -183,6 +183,10 @@ extension AccessibilityTests { try await performAccessibilityAudit(named: "HomeScreenKnockedCell_Previews") } + func testHomeScreenNewSoundBanner() async throws { + try await performAccessibilityAudit(named: "HomeScreenNewSoundBanner_Previews") + } + func testHomeScreenRecoveryKeyConfirmationBanner() async throws { try await performAccessibilityAudit(named: "HomeScreenRecoveryKeyConfirmationBanner_Previews") } diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 6632f1af05..963a8bcb52 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -137,6 +137,7 @@ 1621BF6316FFFEF5AE067C77 /* Avatars.swift in Sources */ = {isa = PBXBuildFile; fileRef = C142248014E08E885E323E56 /* Avatars.swift */; }; 1653275750CE11F5CE94DDFD /* ReadReceiptsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8063E65441E771200108C558 /* ReadReceiptsSummaryView.swift */; }; 167D00CAA13FAFB822298021 /* MediaProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */; }; + 167D5024DB9D44197AEA0507 /* HomeScreenNewSoundBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD5480F03306234FC086E93B /* HomeScreenNewSoundBanner.swift */; }; 16A1F6C703305FCAF4E14EC6 /* TimelineProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A8AA0DFA06012A9DAB951E /* TimelineProxyMock.swift */; }; 16A5D1749A32B91203495EF7 /* FrequentlyUsedEmoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2074C0449B83D5858BD2D7 /* FrequentlyUsedEmoji.swift */; }; 16CBD087038DE3815CDA512C /* PollMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D38391154120264910D19528 /* PollMock.swift */; }; @@ -2474,6 +2475,7 @@ BC51BF90469412ABDE658CDD /* portrait_test_image.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = portrait_test_image.jpg; sourceTree = ""; }; BC8AA23D4F37CC26564F63C5 /* LayoutMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutMocks.swift; sourceTree = ""; }; BCDA016D05107DED3B9495CB /* TimelineItemDebugView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemDebugView.swift; sourceTree = ""; }; + BD5480F03306234FC086E93B /* HomeScreenNewSoundBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenNewSoundBanner.swift; sourceTree = ""; }; BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationContent.swift; sourceTree = ""; }; BE89A8BD65CCE3FCC925CA14 /* TimelineItemReplyDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemReplyDetails.swift; sourceTree = ""; }; BE98688578F8B0541D853695 /* test_pdf.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = test_pdf.pdf; sourceTree = ""; }; @@ -4125,6 +4127,7 @@ C0FEA560929DD73FFEF8C3DF /* HomeScreenEmptyStateView.swift */, D8FC33C3F6BF597E095CE9FA /* HomeScreenInviteCell.swift */, A103580EBA06155B1343EF16 /* HomeScreenKnockedCell.swift */, + BD5480F03306234FC086E93B /* HomeScreenNewSoundBanner.swift */, 05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */, ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */, C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */, @@ -7766,6 +7769,7 @@ 22C5483D01EEB290B8339817 /* HomeScreenInviteCell.swift in Sources */, 86DFA58FBBEB0AF671D2A1E1 /* HomeScreenKnockedCell.swift in Sources */, 8810A2A30A68252EBB54EE05 /* HomeScreenModels.swift in Sources */, + 167D5024DB9D44197AEA0507 /* HomeScreenNewSoundBanner.swift in Sources */, B04E9EB589CE99C3929E817A /* HomeScreenRecoveryKeyConfirmationBanner.swift in Sources */, 0AE0AB1952F186EB86719B4F /* HomeScreenRoomCell.swift in Sources */, A10D6CCDE2010C09EEA1A593 /* HomeScreenRoomList.swift in Sources */, diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 088ea2319e..0a25d0f795 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -159,6 +159,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; +"banner_new_sound_message" = "Your notification ping has been updated—clearer, quicker, and less disruptive."; +"banner_new_sound_title" = "We’ve refreshed your sounds"; "banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; "banner_set_up_recovery_title" = "Set up recovery to protect your account"; "call_invalid_audio_device_bluetooth_devices_disabled" = "Element Call does not support using Bluetooth audio devices in this Android version. Please select a different audio device."; diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 69bc3161b1..7fd8b58e2c 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -437,6 +437,11 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg await userSession.clientProxy.expireSyncSessions() } + if oldVersion < Version(25, 10, 0) { + MXLog.info("Migrating to version 25.10.0, showing new sound banner to existing user.") + appSettings.hasSeenNewSoundBanner = false + } + userSessionMigrationsOldVersion = nil } diff --git a/ElementX/Sources/Application/Settings/AppSettings.swift b/ElementX/Sources/Application/Settings/AppSettings.swift index a27522f962..5323f3c2a4 100644 --- a/ElementX/Sources/Application/Settings/AppSettings.swift +++ b/ElementX/Sources/Application/Settings/AppSettings.swift @@ -29,6 +29,7 @@ final class AppSettings { private enum UserDefaultsKeys: String { case lastVersionLaunched case seenInvites + case hasSeenNewSoundBanner case appLockNumberOfPINAttempts case appLockNumberOfBiometricAttempts case timelineStyle @@ -161,6 +162,10 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.seenInvites, defaultValue: [], storageType: .userDefaults(store)) var seenInvites: Set + /// Defaults to `true` for new users, and we use a migration to set it to `false` for existing users. + @UserPreference(key: UserDefaultsKeys.hasSeenNewSoundBanner, defaultValue: true, storageType: .userDefaults(store)) + var hasSeenNewSoundBanner + /// The initial set of account providers shown to the user in the authentication flow. /// /// Account provider is the friendly term for the server name. It should not contain an `https` prefix and should diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 8066a59956..812efc3dcc 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -356,6 +356,10 @@ internal enum L10n { internal static var bannerMigrateToNativeSlidingSyncForceLogoutTitle: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_force_logout_title") } /// Upgrade available internal static var bannerMigrateToNativeSlidingSyncTitle: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_title") } + /// Your notification ping has been updated—clearer, quicker, and less disruptive. + internal static var bannerNewSoundMessage: String { return L10n.tr("Localizable", "banner_new_sound_message") } + /// We’ve refreshed your sounds + internal static var bannerNewSoundTitle: String { return L10n.tr("Localizable", "banner_new_sound_title") } /// Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices. internal static var bannerSetUpRecoveryContent: String { return L10n.tr("Localizable", "banner_set_up_recovery_content") } /// Set up recovery diff --git a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift index 53ff42a4bc..0e4275df56 100644 --- a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift +++ b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift @@ -53,6 +53,7 @@ enum TestablePreviewsDictionary { "HomeScreenEmptyStateView_Previews" : HomeScreenEmptyStateView_Previews.self, "HomeScreenInviteCell_Previews" : HomeScreenInviteCell_Previews.self, "HomeScreenKnockedCell_Previews" : HomeScreenKnockedCell_Previews.self, + "HomeScreenNewSoundBanner_Previews" : HomeScreenNewSoundBanner_Previews.self, "HomeScreenRecoveryKeyConfirmationBanner_Previews" : HomeScreenRecoveryKeyConfirmationBanner_Previews.self, "HomeScreenRoomCell_Previews" : HomeScreenRoomCell_Previews.self, "HomeScreen_Previews" : HomeScreen_Previews.self, diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 7b1b6dafdf..a9eab56c98 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -39,6 +39,7 @@ enum HomeScreenViewAction { case confirmRecoveryKey case resetEncryption case skipRecoveryKeyConfirmation + case dismissNewSoundBanner case updateVisibleItemRange(Range) case globalSearch case markRoomAsUnread(roomIdentifier: String) @@ -92,6 +93,7 @@ struct HomeScreenViewState: BindableState { var userAvatarURL: URL? var securityBannerMode = HomeScreenSecurityBannerMode.none + var shouldShowNewSoundBanner = false var requiresExtraAccountSetup = false @@ -134,6 +136,10 @@ struct HomeScreenViewState: BindableState { var shouldShowFilters: Bool { !bindings.isSearchFieldFocused && roomListMode == .rooms } + + var shouldShowBanner: Bool { + securityBannerMode.isShown || shouldShowNewSoundBanner + } } struct HomeScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 177bcc693b..46990d80b9 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -105,6 +105,12 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol } .store(in: &cancellables) + appSettings.$hasSeenNewSoundBanner + .sink { [weak self] hasSeenNewSoundBanner in + self?.state.shouldShowNewSoundBanner = !hasSeenNewSoundBanner + } + .store(in: &cancellables) + userSession.clientProxy.hideInviteAvatarsPublisher .removeDuplicates() .receive(on: DispatchQueue.main) @@ -160,6 +166,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol actionsSubject.send(.presentEncryptionResetScreen) case .skipRecoveryKeyConfirmation: state.securityBannerMode = .dismissed + case .dismissNewSoundBanner: + appSettings.hasSeenNewSoundBanner = true case .updateVisibleItemRange(let range): roomSummaryProvider?.updateVisibleRange(range) case .startChat: diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift index 7e5dba6bcb..9b4c34fb37 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift @@ -122,14 +122,16 @@ struct HomeScreenContent: View { @ViewBuilder private var topSection: some View { // An empty VStack causes glitches within the room list - if context.viewState.shouldShowFilters || context.viewState.securityBannerMode.isShown { + if context.viewState.shouldShowFilters || context.viewState.shouldShowBanner { VStack(spacing: 0) { if context.viewState.shouldShowFilters { RoomListFiltersView(state: $context.filtersState) } - + if case let .show(state) = context.viewState.securityBannerMode { HomeScreenRecoveryKeyConfirmationBanner(state: state, context: context) + } else if context.viewState.shouldShowNewSoundBanner { + HomeScreenNewSoundBanner { context.send(viewAction: .dismissNewSoundBanner) } } } .background(Color.compound.bgCanvasDefault) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenNewSoundBanner.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenNewSoundBanner.swift new file mode 100644 index 0000000000..be62dc9e58 --- /dev/null +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenNewSoundBanner.swift @@ -0,0 +1,59 @@ +// +// Copyright 2025 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Compound +import SwiftUI + +struct HomeScreenNewSoundBanner: View { + let dismissAction: () -> Void + + var body: some View { + VStack(spacing: 16) { + content + buttons + } + .padding(16) + .background(Color.compound.bgSubtleSecondary) + .cornerRadius(14) + .padding(.horizontal, 16) + } + + var content: some View { + VStack(alignment: .leading, spacing: 4) { + HStack(alignment: .firstTextBaseline, spacing: 16) { + Text(L10n.bannerNewSoundTitle) + .font(.compound.bodyLGSemibold) + .foregroundColor(.compound.textPrimary) + .frame(maxWidth: .infinity, alignment: .leading) + + Button(action: dismissAction) { + Image(systemName: "xmark") + .foregroundColor(.compound.iconSecondary) + .frame(width: 12, height: 12) + } + } + + Text(L10n.bannerNewSoundMessage) + .font(.compound.bodyMD) + .foregroundColor(.compound.textSecondary) + } + } + + var buttons: some View { + Button(action: dismissAction) { + Text(L10n.actionOk) + .frame(maxWidth: .infinity) + } + .buttonStyle(.compound(.primary, size: .medium)) + } +} + +struct HomeScreenNewSoundBanner_Previews: PreviewProvider, TestablePreview { + static var previews: some View { + HomeScreenNewSoundBanner { } + } +} diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index a1b795053b..bab870d266 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -275,6 +275,12 @@ extension PreviewTests { } } + func testHomeScreenNewSoundBanner() async throws { + for (index, preview) in HomeScreenNewSoundBanner_Previews._allPreviews.enumerated() { + try await assertSnapshots(matching: preview, step: index) + } + } + func testHomeScreenRecoveryKeyConfirmationBanner() async throws { for (index, preview) in HomeScreenRecoveryKeyConfirmationBanner_Previews._allPreviews.enumerated() { try await assertSnapshots(matching: preview, step: index) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-en-GB-0.png new file mode 100644 index 0000000000..67b9151c0b --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-en-GB-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50db0fb40d18d13f4d2935d5a3bf94267665e39d4920640157b83440b2e43f24 +size 93124 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-pseudo-0.png new file mode 100644 index 0000000000..6ccb8b78ea --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPad-pseudo-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24b5a53e8064a09d2c419a861ce2877c8680ecafe8531469530bfff7c60e965a +size 103633 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-en-GB-0.png new file mode 100644 index 0000000000..8015e6c36b --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-en-GB-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f068c7428744de0bf0bfae78edda3ebe5d62f7e8f0c6bd820ea0872feed2423e +size 52313 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-pseudo-0.png new file mode 100644 index 0000000000..415225d9ee --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/homeScreenNewSoundBanner.iPhone-16-pseudo-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7863d39543d71a142c8d58c42688953e7a8892f4b81d78a9b9f60717624abce3 +size 72122 diff --git a/UnitTests/Sources/HomeScreenViewModelTests.swift b/UnitTests/Sources/HomeScreenViewModelTests.swift index 5d1aa0c2d5..1dd73d61e3 100644 --- a/UnitTests/Sources/HomeScreenViewModelTests.swift +++ b/UnitTests/Sources/HomeScreenViewModelTests.swift @@ -362,6 +362,19 @@ class HomeScreenViewModelTests: XCTestCase { try await deferredAction.fulfill() } + func testNewSoundBanner() { + appSettings.hasSeenNewSoundBanner = false + + setupViewModel() + XCTAssertTrue(context.viewState.shouldShowBanner) + XCTAssertTrue(context.viewState.shouldShowNewSoundBanner) + + context.send(viewAction: .dismissNewSoundBanner) + XCTAssertFalse(context.viewState.shouldShowBanner) + XCTAssertFalse(context.viewState.shouldShowNewSoundBanner) + XCTAssertTrue(appSettings.hasSeenNewSoundBanner) + } + // MARK: - Helpers enum InviteType { case rooms, spaces }