From f384c2703f74e7b4a767cd139a3afee47c8ef355 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sat, 19 Oct 2024 00:58:10 +0500 Subject: [PATCH 01/58] Display Fire window downloads per Fire Window (#3250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/1199230911884351/1207052331808912/f **Description**: - Separate downloads from Fire window and Regular windows **Steps to test this PR**: 1. Start a download in a regular window 2. Open a Fire Window, validate its downloads list is empty 3. Start a download in the Fire Window; Validate the download stays in the list after it‘s completed. 4. Close the Fire Window with active downloads; Validate the alert is displayed (with "this file" or "these files" for 1 or many files downloaded) 5. Close the Fire Window, validate the downloads aren‘t displayed in the Regular window **Definition of Done**: * [ ] Does this PR satisfy our [Definition of Done](https://app.asana.com/0/1202500774821704/1207634633537039/f)? --- [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --------- Co-authored-by: Juan Manuel Pereira --- DuckDuckGo/Localizable.xcstrings | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index 918c1bea94..d978b890cc 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -18019,7 +18019,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Möchtest du wirklich aufhören?\n\nDuckDuckGo lädt derzeit „%1$@“%2$@ herunter. Wenn du jetzt aufhörst, wird DuckDuckGo den Download %3$@ nicht beenden." + "value" : "Möchtest du wirklich beenden?\n\nDuckDuckGo lädt derzeit „%@“%@ herunter. Wenn du jetzt aufhörst, wird DuckDuckGo den Download %@ nicht beenden." } }, "en" : { @@ -18031,43 +18031,43 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Seguro que quieres salir?\n\nDuckDuckGo se está descargando actualmente \"%1$@\"%2$@. Si sales ahora, DuckDuckGo no terminará de descargar %3$@." + "value" : "¿Seguro que quieres salir?\n\nDuckDuckGo está descargando \"%@\"%@. Si sales ahora, DuckDuckGo no terminará de descargar %@." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Voulez-vous vraiment abandonner ?DuckDuckGo télécharge actuellement « %1$@”%2$@ ». Si vous abandonnez maintenant, DuckDuckGo ne terminera pas le téléchargement de %3$@." + "value" : "Voulez-vous vraiment abandonner ?\n\nDuckDuckGo télécharge actuellement “%@”%@. Si vous abandonnez maintenant, DuckDuckGo ne terminera pas le téléchargement de %@." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Uscire?DuckDuckGo sta scaricando \"%1$@\"%2$@. Se esci ora, DuckDuckGo non completerà il download di %3$@." + "value" : "Vuoi davvero uscire?\n\nDuckDuckGo sta attualmente scaricando “%@”%@. Se esci ora, DuckDuckGo non completerà il download di %@." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Weet je zeker dat je wilt afsluiten?\n\nDuckDuckGo downloadt momenteel '%1$@'%2$@. Als je nu stopt, wordt %3$@ niet helemaal gedownload." + "value" : "Weet je zeker dat je wilt stoppen?\n\nDuckDuckGo downloadt op dit moment \"%@\"%@. Als je nu stopt, wordt het downloaden van %@ niet voltooid." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Are you sure you want to quit?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you quit now, DuckDuckGo won‘t finish downloading %3$@." + "value" : "Czy na pewno chcesz zrezygnować?\n\nDuckDuckGo aktualnie pobiera „%@”%@. Jeśli teraz wyjdziesz, DuckDuckGo nie dokończy pobierania %@." } }, "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Tens a certeza de que pretendes sair?\n\nO DuckDuckGo está a transferir \"%1$@”%2$@. Se saíres agora, o DuckDuckGo não vai transferir %3$@ até ao fim." + "value" : "Tens a certeza de que pretendes sair?\n\nO DuckDuckGo está a transferir “%@”%@. Se saíres agora, o DuckDuckGo não vai transferir %@ até ao fim." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Are you sure you want to quit?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you quit now, DuckDuckGo won‘t finish downloading %3$@." + "value" : "Действительно закрыть приложение?\n\nDuckDuckGo сейчас загружает «%@»%@. Если закрыть приложение, DuckDuckGo не загрузит %@." } } } @@ -23007,7 +23007,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Möchtest du das Fire Window wirklich schließen?\n\nDuckDuckGo lädt derzeit „%1$@“%2$@ herunter. Wenn du das Fire Window schließt, löscht DuckDuckGo %3$@." + "value" : "Bist du sicher, dass du das Fire Window schließen willst?\n\nDuckDuckGo lädt gerade „%@“%@ herunter. Wenn du das Fire Window schließt, löscht DuckDuckGo %@." } }, "en" : { @@ -23019,43 +23019,43 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Seguro que quieres cerrar la Fire Window?\n\nDuckDuckGo se está descargando actualmente \"%1$@\"%2$@. Si cierras la Fire Window, DuckDuckGo eliminará %3$@." + "value" : "¿Seguro que quieres cerrar la Fire Window?\n\nDuckDuckGo está descargando \"%@\"%@. Si cierras la Fire Window, DuckDuckGo eliminará %@." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Voulez-vous vraiment fermer la Fire Window ?\n\nDuckDuckGo télécharge actuellement « %1$@”%2$@ ». Si vous fermez la Fire Window, DuckDuckGo supprimera %3$@." + "value" : "Voulez-vous vraiment fermer la Fire Window ?\n\nDuckDuckGo télécharge actuellement “%@”%@. Si vous fermez la Fire Window, DuckDuckGo supprimera %@." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Chiudere la Fire Window?\n\nDuckDuckGo sta scaricando \"%1$@\"%2$@. Se la chiudi, DuckDuckGo eliminerà %3$@." + "value" : "Vuoi davvero chiudere la Fire Window?\n\nDuckDuckGo sta attualmente scaricando “%@”%@. Se la chiudi, DuckDuckGo eliminerà %@." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Weet je zeker dat je het Fire Window wilt sluiten?\n\nDuckDuckGo downloadt momenteel '%1$@'%2$@. Als je het Fire Window sluit, wordt %3$@ verwijderd." + "value" : "Weet je zeker dat je het Fire Window wilt sluiten?\n\nDuckDuckGo downloadt op dit moment \"%@\"%@. Als je het Fire Window sluit, wordt %@ verwijderd." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Are you sure you want to close the Fire Window?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you close the Fire Window, DuckDuckGo will delete %3$@." + "value" : "Czy na pewno chcesz zamknąć okno Fire Window?\n\nDuckDuckGo aktualnie pobiera „%@”%@. Jeśli zamkniesz okno Fire Window, DuckDuckGo usunie %@." } }, "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Tens a certeza de que pretendes fechar a Fire Window?\n\nO DuckDuckGo está a transferir \"%1$@”%2$@. Se fechares a Fire Window, o DuckDuckGo vai eliminar %3$@." + "value" : "Tens a certeza de que pretendes fechar a Fire Window?\n\nO DuckDuckGo está a transferir “%@”%@. Se fechares a Fire Window, o DuckDuckGo vai eliminar %@." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Are you sure you want to close the Fire Window?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you close the Fire Window, DuckDuckGo will delete %3$@." + "value" : "Действительно закрыть окно Fire Window?\n\nDuckDuckGo сейчас загружает «%@»%@. В случае закрытия DuckDuckGo удалит %@." } } } @@ -32578,7 +32578,7 @@ "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Завершить DuckDuckGo" + "value" : "Закрыть DuckDuckGo" } } } From 3ae0e16c5f54250f317c911adca7340f2e6bbf73 Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:31:48 +0200 Subject: [PATCH 02/58] fix grey space and copy (#3432) Task/Issue URL: https://app.asana.com/0/1176956903599313/1208592739186184/f **Description**: Avoids grey space between DaxDialog and webView content, fix copy for surprise suggestion --- DuckDuckGo/Localizable.xcstrings | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index d978b890cc..ae5547319a 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -18019,7 +18019,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Möchtest du wirklich beenden?\n\nDuckDuckGo lädt derzeit „%@“%@ herunter. Wenn du jetzt aufhörst, wird DuckDuckGo den Download %@ nicht beenden." + "value" : "Möchtest du wirklich aufhören?\n\nDuckDuckGo lädt derzeit „%1$@“%2$@ herunter. Wenn du jetzt aufhörst, wird DuckDuckGo den Download %3$@ nicht beenden." } }, "en" : { @@ -18031,43 +18031,43 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Seguro que quieres salir?\n\nDuckDuckGo está descargando \"%@\"%@. Si sales ahora, DuckDuckGo no terminará de descargar %@." + "value" : "¿Seguro que quieres salir?\n\nDuckDuckGo se está descargando actualmente \"%1$@\"%2$@. Si sales ahora, DuckDuckGo no terminará de descargar %3$@." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Voulez-vous vraiment abandonner ?\n\nDuckDuckGo télécharge actuellement “%@”%@. Si vous abandonnez maintenant, DuckDuckGo ne terminera pas le téléchargement de %@." + "value" : "Voulez-vous vraiment abandonner ?DuckDuckGo télécharge actuellement « %1$@”%2$@ ». Si vous abandonnez maintenant, DuckDuckGo ne terminera pas le téléchargement de %3$@." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Vuoi davvero uscire?\n\nDuckDuckGo sta attualmente scaricando “%@”%@. Se esci ora, DuckDuckGo non completerà il download di %@." + "value" : "Uscire?DuckDuckGo sta scaricando \"%1$@\"%2$@. Se esci ora, DuckDuckGo non completerà il download di %3$@." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Weet je zeker dat je wilt stoppen?\n\nDuckDuckGo downloadt op dit moment \"%@\"%@. Als je nu stopt, wordt het downloaden van %@ niet voltooid." + "value" : "Weet je zeker dat je wilt afsluiten?\n\nDuckDuckGo downloadt momenteel '%1$@'%2$@. Als je nu stopt, wordt %3$@ niet helemaal gedownload." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Czy na pewno chcesz zrezygnować?\n\nDuckDuckGo aktualnie pobiera „%@”%@. Jeśli teraz wyjdziesz, DuckDuckGo nie dokończy pobierania %@." + "value" : "Are you sure you want to quit?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you quit now, DuckDuckGo won‘t finish downloading %3$@." } }, "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Tens a certeza de que pretendes sair?\n\nO DuckDuckGo está a transferir “%@”%@. Se saíres agora, o DuckDuckGo não vai transferir %@ até ao fim." + "value" : "Tens a certeza de que pretendes sair?\n\nO DuckDuckGo está a transferir \"%1$@”%2$@. Se saíres agora, o DuckDuckGo não vai transferir %3$@ até ao fim." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Действительно закрыть приложение?\n\nDuckDuckGo сейчас загружает «%@»%@. Если закрыть приложение, DuckDuckGo не загрузит %@." + "value" : "Are you sure you want to quit?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you quit now, DuckDuckGo won‘t finish downloading %3$@." } } } @@ -23007,7 +23007,7 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Bist du sicher, dass du das Fire Window schließen willst?\n\nDuckDuckGo lädt gerade „%@“%@ herunter. Wenn du das Fire Window schließt, löscht DuckDuckGo %@." + "value" : "Möchtest du das Fire Window wirklich schließen?\n\nDuckDuckGo lädt derzeit „%1$@“%2$@ herunter. Wenn du das Fire Window schließt, löscht DuckDuckGo %3$@." } }, "en" : { @@ -23019,43 +23019,43 @@ "es" : { "stringUnit" : { "state" : "translated", - "value" : "¿Seguro que quieres cerrar la Fire Window?\n\nDuckDuckGo está descargando \"%@\"%@. Si cierras la Fire Window, DuckDuckGo eliminará %@." + "value" : "¿Seguro que quieres cerrar la Fire Window?\n\nDuckDuckGo se está descargando actualmente \"%1$@\"%2$@. Si cierras la Fire Window, DuckDuckGo eliminará %3$@." } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Voulez-vous vraiment fermer la Fire Window ?\n\nDuckDuckGo télécharge actuellement “%@”%@. Si vous fermez la Fire Window, DuckDuckGo supprimera %@." + "value" : "Voulez-vous vraiment fermer la Fire Window ?\n\nDuckDuckGo télécharge actuellement « %1$@”%2$@ ». Si vous fermez la Fire Window, DuckDuckGo supprimera %3$@." } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Vuoi davvero chiudere la Fire Window?\n\nDuckDuckGo sta attualmente scaricando “%@”%@. Se la chiudi, DuckDuckGo eliminerà %@." + "value" : "Chiudere la Fire Window?\n\nDuckDuckGo sta scaricando \"%1$@\"%2$@. Se la chiudi, DuckDuckGo eliminerà %3$@." } }, "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Weet je zeker dat je het Fire Window wilt sluiten?\n\nDuckDuckGo downloadt op dit moment \"%@\"%@. Als je het Fire Window sluit, wordt %@ verwijderd." + "value" : "Weet je zeker dat je het Fire Window wilt sluiten?\n\nDuckDuckGo downloadt momenteel '%1$@'%2$@. Als je het Fire Window sluit, wordt %3$@ verwijderd." } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Czy na pewno chcesz zamknąć okno Fire Window?\n\nDuckDuckGo aktualnie pobiera „%@”%@. Jeśli zamkniesz okno Fire Window, DuckDuckGo usunie %@." + "value" : "Are you sure you want to close the Fire Window?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you close the Fire Window, DuckDuckGo will delete %3$@." } }, "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Tens a certeza de que pretendes fechar a Fire Window?\n\nO DuckDuckGo está a transferir “%@”%@. Se fechares a Fire Window, o DuckDuckGo vai eliminar %@." + "value" : "Tens a certeza de que pretendes fechar a Fire Window?\n\nO DuckDuckGo está a transferir \"%1$@”%2$@. Se fechares a Fire Window, o DuckDuckGo vai eliminar %3$@." } }, "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Действительно закрыть окно Fire Window?\n\nDuckDuckGo сейчас загружает «%@»%@. В случае закрытия DuckDuckGo удалит %@." + "value" : "Are you sure you want to close the Fire Window?\n\nDuckDuckGo is currently downloading “%1$@”%2$@. If you close the Fire Window, DuckDuckGo will delete %3$@." } } } @@ -32578,7 +32578,7 @@ "ru" : { "stringUnit" : { "state" : "translated", - "value" : "Закрыть DuckDuckGo" + "value" : "Завершить DuckDuckGo" } } } @@ -62878,4 +62878,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file From eb9eb440c2b7d0f9d4b8fea82b0efb69b8b83e49 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 3 Sep 2024 16:04:18 -0400 Subject: [PATCH 03/58] Revert "Resolving automatic update edge cases (#3142)" This reverts commit c165e7491003e237769645ada5fc6b68438aa533. # Conflicts: # DuckDuckGo/Updates/BinaryOwnershipChecker.swift # DuckDuckGo/Updates/UpdateController.swift --- DuckDuckGo.xcodeproj/project.pbxproj | 8 -- .../Updates/BinaryOwnershipChecker.swift | 66 --------- DuckDuckGo/Updates/UpdateController.swift | 129 +++++++----------- .../InputFilesChecker/InputFilesChecker.swift | 2 - .../Updates/BinaryOwnershipCheckerTests.swift | 87 ------------ 5 files changed, 50 insertions(+), 242 deletions(-) delete mode 100644 DuckDuckGo/Updates/BinaryOwnershipChecker.swift delete mode 100644 UnitTests/Updates/BinaryOwnershipCheckerTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index cff2085141..994ca20265 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -69,8 +69,6 @@ 1D220BF92B86192200F8BBC6 /* PreferencesEmailProtectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D220BF72B86192200F8BBC6 /* PreferencesEmailProtectionView.swift */; }; 1D220BFC2B87AACF00F8BBC6 /* PrivacyProtectionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D220BFB2B87AACF00F8BBC6 /* PrivacyProtectionStatus.swift */; }; 1D220BFD2B87AACF00F8BBC6 /* PrivacyProtectionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D220BFB2B87AACF00F8BBC6 /* PrivacyProtectionStatus.swift */; }; - 1D232E942C7860DA0043840D /* BinaryOwnershipChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D232E932C7860DA0043840D /* BinaryOwnershipChecker.swift */; }; - 1D232E992C7870D90043840D /* BinaryOwnershipCheckerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D232E962C786E7D0043840D /* BinaryOwnershipCheckerTests.swift */; }; 1D26EBAC2B74BECB0002A93F /* NSImageSendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D26EBAB2B74BECB0002A93F /* NSImageSendable.swift */; }; 1D26EBAD2B74BECB0002A93F /* NSImageSendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D26EBAB2B74BECB0002A93F /* NSImageSendable.swift */; }; 1D26EBB02B74DB600002A93F /* TabSnapshotCleanupService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D26EBAF2B74DB600002A93F /* TabSnapshotCleanupService.swift */; }; @@ -3304,8 +3302,6 @@ 1D1C36E529FB019C001FA40C /* HistoryTabExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryTabExtensionTests.swift; sourceTree = ""; }; 1D220BF72B86192200F8BBC6 /* PreferencesEmailProtectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesEmailProtectionView.swift; sourceTree = ""; }; 1D220BFB2B87AACF00F8BBC6 /* PrivacyProtectionStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyProtectionStatus.swift; sourceTree = ""; }; - 1D232E932C7860DA0043840D /* BinaryOwnershipChecker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryOwnershipChecker.swift; sourceTree = ""; }; - 1D232E962C786E7D0043840D /* BinaryOwnershipCheckerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinaryOwnershipCheckerTests.swift; sourceTree = ""; }; 1D26EBAB2B74BECB0002A93F /* NSImageSendable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImageSendable.swift; sourceTree = ""; }; 1D26EBAF2B74DB600002A93F /* TabSnapshotCleanupService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabSnapshotCleanupService.swift; sourceTree = ""; }; 1D36E657298AA3BA00AA485D /* InternalUserDeciderStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalUserDeciderStore.swift; sourceTree = ""; }; @@ -5230,7 +5226,6 @@ 1D72D59B2BFF61B200AEDE36 /* UpdateNotificationPresenter.swift */, 1D9297BE2C1B062900A38521 /* ApplicationUpdateDetector.swift */, 1D0DE93D2C3BA9840037ABC2 /* AppRestarter.swift */, - 1D232E932C7860DA0043840D /* BinaryOwnershipChecker.swift */, 1D39E5762C2BFD5700757339 /* ReleaseNotesTabExtension.swift */, 1D39E5792C2C0F3700757339 /* ReleaseNotesUserScript.swift */, 1D0DE9402C3BB9CC0037ABC2 /* ReleaseNotesParser.swift */, @@ -5271,7 +5266,6 @@ children = ( 1D838A312C44F0180078373F /* ReleaseNotesParserTests.swift */, 1D638D602C44F2BA00530DD5 /* ApplicationUpdateDetectorTests.swift */, - 1D232E962C786E7D0043840D /* BinaryOwnershipCheckerTests.swift */, ); path = Updates; sourceTree = ""; @@ -13156,7 +13150,6 @@ 37BF3F21286F0A7A00BD9014 /* PinnedTabsViewModel.swift in Sources */, EEC4A6692B2C87D300F7C0AA /* VPNLocationView.swift in Sources */, AAC5E4D225D6A709007F5990 /* BookmarkList.swift in Sources */, - 1D232E942C7860DA0043840D /* BinaryOwnershipChecker.swift in Sources */, B602E81D2A1E25B1006D261F /* NEOnDemandRuleExtension.swift in Sources */, 56A0543E2C215FB3007D8FAB /* OnboardingUserScript.swift in Sources */, C1372EF42BBC5BAD003F8793 /* SecureTextField.swift in Sources */, @@ -13504,7 +13497,6 @@ 567A23E12C89B1EE0010F66C /* BrowserTabViewControllerOnboardingTests.swift in Sources */, 986189E62A7CFB3E001B4519 /* LocalBookmarkStoreSavingTests.swift in Sources */, AA652CD325DDA6E9009059CC /* LocalBookmarkManagerTests.swift in Sources */, - 1D232E992C7870D90043840D /* BinaryOwnershipCheckerTests.swift in Sources */, CBDD5DE329A67F2700832877 /* MockConfigurationStore.swift in Sources */, 9F3910692B68D87B00CB5112 /* ProgressExtensionTests.swift in Sources */, 560C6ED02CCA5C6000D411E2 /* CapturingOnboardingNavigationDelegate.swift in Sources */, diff --git a/DuckDuckGo/Updates/BinaryOwnershipChecker.swift b/DuckDuckGo/Updates/BinaryOwnershipChecker.swift deleted file mode 100644 index e3825f92ae..0000000000 --- a/DuckDuckGo/Updates/BinaryOwnershipChecker.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// BinaryOwnershipChecker.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// 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. -// - -import Foundation -import Common -import os.log - -protocol BinaryOwnershipChecking { - func isCurrentUserOwner() -> Bool -} - -/// A class responsible for checking whether the current user owns the binary of the app. -/// The result is cached after the first check to avoid repeated file system access. -final class BinaryOwnershipChecker: BinaryOwnershipChecking { - - private let fileManager: FileManager - private var ownershipCache: Bool? - - init(fileManager: FileManager = .default) { - self.fileManager = fileManager - } - - /// Checks if the current user owns the binary of the currently running app. - /// The method caches the result after the first check to improve performance on subsequent calls. - /// - Returns: `true` if the current user is the owner, `false` otherwise. - func isCurrentUserOwner() -> Bool { - if let cachedResult = ownershipCache { - return cachedResult - } - - guard let binaryPath = Bundle.main.executablePath else { - Logger.updates.debug("Failed to get the binary path") - ownershipCache = false - return false - } - - do { - let attributes = try fileManager.attributesOfItem(atPath: binaryPath) - if let ownerID = attributes[FileAttributeKey.ownerAccountID] as? NSNumber { - let isOwner = ownerID.intValue == getuid() - ownershipCache = isOwner - return isOwner - } - } catch { - Logger.updates.error("Failed to get binary file attributes: \(error.localizedDescription, privacy: .public)") - } - - ownershipCache = false - return false - } -} diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 2d7b4ffec8..041bd77766 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -59,26 +59,27 @@ final class UpdateController: NSObject, UpdateControllerProtocol { lazy var notificationPresenter = UpdateNotificationPresenter() let willRelaunchAppPublisher: AnyPublisher - @Published private(set) var isUpdateBeingLoaded = false - var isUpdateBeingLoadedPublisher: Published.Publisher { $isUpdateBeingLoaded } + init(internalUserDecider: InternalUserDecider, + appRestarter: AppRestarting = AppRestarter()) { + willRelaunchAppPublisher = willRelaunchAppSubject.eraseToAnyPublisher() + self.internalUserDecider = internalUserDecider + self.appRestarter = appRestarter + super.init() - // Struct used to cache data until the updater finishes checking for updates - struct UpdateCheckResult { - let item: SUAppcastItem - let isInstalled: Bool + configureUpdater() } - private var updateCheckResult: UpdateCheckResult? + + @Published private(set) var isUpdateBeingLoaded = false + var isUpdateBeingLoadedPublisher: Published.Publisher { $isUpdateBeingLoaded } @Published private(set) var latestUpdate: Update? { didSet { if let latestUpdate, !latestUpdate.isInstalled { - if !shouldShowManualUpdateDialog { - switch latestUpdate.type { - case .critical: - notificationPresenter.showUpdateNotification(icon: NSImage.criticalUpdateNotificationInfo, text: UserText.criticalUpdateNotification, presentMultiline: true) - case .regular: - notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) - } + switch latestUpdate.type { + case .critical: + notificationPresenter.showUpdateNotification(icon: NSImage.criticalUpdateNotificationInfo, text: UserText.criticalUpdateNotification, presentMultiline: true) + case .regular: + notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) } isUpdateAvailableToInstall = !latestUpdate.isInstalled } else { @@ -112,34 +113,15 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } } - var automaticUpdateFlow: Bool { - // In case the current user is not the owner of the binary, we have to switch - // to manual update flow because the authentication is required. - return areAutomaticUpdatesEnabled && binaryOwnershipChecker.isCurrentUserOwner() - } - var shouldShowManualUpdateDialog = false private(set) var updater: SPUStandardUpdaterController! private var appRestarter: AppRestarting private let willRelaunchAppSubject = PassthroughSubject() private var internalUserDecider: InternalUserDecider - private let binaryOwnershipChecker: BinaryOwnershipChecking // MARK: - Public - init(internalUserDecider: InternalUserDecider, - appRestarter: AppRestarting = AppRestarter(), - binaryOwnershipChecker: BinaryOwnershipChecking = BinaryOwnershipChecker()) { - willRelaunchAppPublisher = willRelaunchAppSubject.eraseToAnyPublisher() - self.internalUserDecider = internalUserDecider - self.appRestarter = appRestarter - self.binaryOwnershipChecker = binaryOwnershipChecker - super.init() - - configureUpdater() - } - func checkNewApplicationVersion() { let updateStatus = ApplicationUpdateDetector.isApplicationUpdated() switch updateStatus { @@ -163,18 +145,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater.updater.checkForUpdatesInBackground() } - @objc func runUpdate() { - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidRunUpdate)) - - if automaticUpdateFlow { - appRestarter.restart() - } else { - updater.userDriver.activeUpdateAlert?.hideUnnecessaryUpdateButtons() - shouldShowManualUpdateDialog = true - checkForUpdate() - } - } - // MARK: - Private private func configureUpdater() { @@ -182,8 +152,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater = SPUStandardUpdaterController(updaterDelegate: self, userDriverDelegate: self) shouldShowManualUpdateDialog = false - if updater.updater.automaticallyDownloadsUpdates != automaticUpdateFlow { - updater.updater.automaticallyDownloadsUpdates = automaticUpdateFlow + if updater.updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { + updater.updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled } #if DEBUG @@ -194,12 +164,26 @@ final class UpdateController: NSObject, UpdateControllerProtocol { // Load the appcast to retrieve information about the latest update (required for displaying Release Notes) checkForUpdateInBackground() #endif + + checkForUpdateInBackground() } - @objc private func openUpdatesPage() { + @objc func openUpdatesPage() { notificationPresenter.openUpdatesPage() } + @objc func runUpdate() { + PixelKit.fire(DebugEvent(GeneralPixel.updaterDidRunUpdate)) + + if areAutomaticUpdatesEnabled { + appRestarter.restart() + } else { + updater.userDriver.activeUpdateAlert?.hideUnnecessaryUpdateButtons() + shouldShowManualUpdateDialog = true + checkForUpdate() + } + } + } extension UpdateController: SPUStandardUserDriverDelegate { @@ -221,7 +205,6 @@ extension UpdateController: SPUUpdaterDelegate { } private func onUpdateCheckStart() { - updateCheckResult = nil isUpdateBeingLoaded = true } @@ -255,20 +238,19 @@ extension UpdateController: SPUUpdaterDelegate { PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - if !automaticUpdateFlow { - // For manual updates, we can present the available update without waiting for the update cycle to finish. The Sparkle flow downloads the update later - updateCheckResult = UpdateCheckResult(item: item, isInstalled: false) - onUpdateCheckEnd() + guard !areAutomaticUpdatesEnabled else { + // If automatic updates are enabled, we are waiting until the update is downloaded + return } + // For manual updates, show the available update without downloading + onUpdateCheckEnd(item: item, isInstalled: false) } func updaterDidNotFindUpdate(_ updater: SPUUpdater, error: any Error) { let item = (error as NSError).userInfo["SULatestAppcastItemFound"] as? SUAppcastItem Logger.updates.debug("Updater did not find update: \(String(describing: item?.displayVersionString))(\(String(describing: item?.versionString)))") - if let item { - // User is running the latest version - updateCheckResult = UpdateCheckResult(item: item, isInstalled: true) - } + + onUpdateCheckEnd(item: item, isInstalled: true) PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) } @@ -276,38 +258,27 @@ extension UpdateController: SPUUpdaterDelegate { func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { Logger.updates.debug("Updater did download update: \(item.displayVersionString)(\(item.versionString))") - if automaticUpdateFlow { - // For automatic updates, the available item has to be downloaded - updateCheckResult = UpdateCheckResult(item: item, isInstalled: false) + guard areAutomaticUpdatesEnabled else { + // If manual are enabled, we don't download return } + // Automatic updates present the available update after it's downloaded + onUpdateCheckEnd(item: item, isInstalled: false) PixelKit.fire(DebugEvent(GeneralPixel.updaterDidDownloadUpdate)) } - func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { - Logger.updates.debug("Updater did finish update cycle") - - onUpdateCheckEnd() - } - - private func onUpdateCheckEnd() { - guard isUpdateBeingLoaded else { - // The update check end is already handled - return - } - - // If the update is available, present it - if let updateCheckResult = updateCheckResult { - latestUpdate = Update(appcastItem: updateCheckResult.item, - isInstalled: updateCheckResult.isInstalled) + private func onUpdateCheckEnd(item: SUAppcastItem?, isInstalled: Bool) { + if let item { + latestUpdate = Update(appcastItem: item, isInstalled: isInstalled) } else { latestUpdate = nil } - - // Clear cache isUpdateBeingLoaded = false - updateCheckResult = nil + } + + func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { + Logger.updates.debug("Updater did finish update cycle") } } diff --git a/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift b/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift index f9ef9def11..eebcb0f3d9 100644 --- a/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift +++ b/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift @@ -21,7 +21,6 @@ import PackagePlugin import XcodeProjectPlugin let nonSandboxedExtraInputFiles: Set = [ - .init("BinaryOwnershipChecker.swift", .source), .init("BWEncryption.m", .source), .init("BWEncryptionOutput.m", .source), .init("BWManager.swift", .source), @@ -50,7 +49,6 @@ let extraInputFiles: [TargetName: Set] = [ "DuckDuckGo Privacy Pro": nonSandboxedExtraInputFiles, "Unit Tests": [ - .init("BinaryOwnershipCheckerTests.swift", .source), .init("BWEncryptionTests.swift", .source), .init("WKWebViewPrivateMethodsAvailabilityTests.swift", .source) ], diff --git a/UnitTests/Updates/BinaryOwnershipCheckerTests.swift b/UnitTests/Updates/BinaryOwnershipCheckerTests.swift deleted file mode 100644 index 76a8029c2f..0000000000 --- a/UnitTests/Updates/BinaryOwnershipCheckerTests.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// BinaryOwnershipCheckerTests.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// 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. -// - -import BrowserServicesKit -import XCTest - -@testable import DuckDuckGo_Privacy_Browser - -class BinaryOwnershipCheckerTests: XCTestCase { - - func testWhenUserIsOwner_ThenIsCurrentUserOwnerReturnsTrue() { - let mockFileManager = MockFileManager() - mockFileManager.attributes = [ - .ownerAccountID: NSNumber(value: getuid()) - ] - let checker = BinaryOwnershipChecker(fileManager: mockFileManager) - let isOwner = checker.isCurrentUserOwner() - - XCTAssertTrue(isOwner, "Expected the current user to be identified as the owner.") - } - - func testWhenUserIsNotOwner_ThenIsCurrentUserOwnerReturnsFalse() { - let mockFileManager = MockFileManager() - mockFileManager.attributes = [ - .ownerAccountID: NSNumber(value: getuid() + 1) // Simulate a different user - ] - let checker = BinaryOwnershipChecker(fileManager: mockFileManager) - let isOwner = checker.isCurrentUserOwner() - - XCTAssertFalse(isOwner, "Expected the current user not to be identified as the owner.") - } - - func testWhenFileManagerThrowsError_ThenIsCurrentUserOwnerReturnsFalse() { - let mockFileManager = MockFileManager() - mockFileManager.shouldThrowError = true - let checker = BinaryOwnershipChecker(fileManager: mockFileManager) - let isOwner = checker.isCurrentUserOwner() - - XCTAssertFalse(isOwner, "Expected the ownership check to fail and return false when an error occurs.") - } - - func testWhenOwnershipIsCheckedMultipleTimes_ThenResultIsCached() { - let mockFileManager = MockFileManager() - mockFileManager.attributes = [ - .ownerAccountID: NSNumber(value: getuid()) - ] - let checker = BinaryOwnershipChecker(fileManager: mockFileManager) - let isOwnerFirstCheck = checker.isCurrentUserOwner() - - mockFileManager.attributes = [ - .ownerAccountID: NSNumber(value: getuid() + 1) - ] - let isOwnerSecondCheck = checker.isCurrentUserOwner() - - XCTAssertTrue(isOwnerFirstCheck, "Expected the current user to be identified as the owner on first check.") - XCTAssertTrue(isOwnerSecondCheck, "Expected the cached result to be used, so the second check should return the same result as the first.") - } -} - -// Mock FileManager to simulate different file attributes and errors -class MockFileManager: FileManager { - - var attributes: [FileAttributeKey: Any]? - var shouldThrowError = false - - override func attributesOfItem(atPath path: String) throws -> [FileAttributeKey: Any] { - if shouldThrowError { - throw NSError(domain: NSCocoaErrorDomain, code: NSFileReadNoSuchFileError, userInfo: nil) - } - return attributes ?? [:] - } -} From 16c89715d4b733248f58fa414c56709f6b25f490 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 3 Sep 2024 16:14:06 -0400 Subject: [PATCH 04/58] UpdateUserDriver draft --- DuckDuckGo.xcodeproj/project.pbxproj | 4 + .../Preferences/Model/AboutPreferences.swift | 2 +- DuckDuckGo/Updates/UpdateController.swift | 82 +++++------ DuckDuckGo/Updates/UpdateUserDriver.swift | 138 ++++++++++++++++++ .../InputFilesChecker/InputFilesChecker.swift | 1 + 5 files changed, 177 insertions(+), 50 deletions(-) create mode 100644 DuckDuckGo/Updates/UpdateUserDriver.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 994ca20265..76bf28c142 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -2760,6 +2760,7 @@ BD384ACA2BBC821A00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; BD384ACB2BBC821B00EF3735 /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */; }; BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = BD384AC72BBC821100EF3735 /* vpn-light-mode.json */; }; + BD6367252C877BE1009DE7A8 /* UpdateUserDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6367242C877BE1009DE7A8 /* UpdateUserDriver.swift */; }; BD7090CF2C5182FB009EED82 /* UnifiedFeedbackFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7090CE2C5182FB009EED82 /* UnifiedFeedbackFormView.swift */; }; BD7090D02C5182FB009EED82 /* UnifiedFeedbackFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7090CE2C5182FB009EED82 /* UnifiedFeedbackFormView.swift */; }; BD7090D22C52ECFE009EED82 /* UnifiedMetadataCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7090D12C52ECFE009EED82 /* UnifiedMetadataCollector.swift */; }; @@ -4653,6 +4654,7 @@ BBFF355C2C4AF26200DA3289 /* BookmarksSortModeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksSortModeTests.swift; sourceTree = ""; }; BD384AC72BBC821100EF3735 /* vpn-light-mode.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "vpn-light-mode.json"; sourceTree = ""; }; BD384AC82BBC821100EF3735 /* vpn-dark-mode.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "vpn-dark-mode.json"; sourceTree = ""; }; + BD6367242C877BE1009DE7A8 /* UpdateUserDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateUserDriver.swift; sourceTree = ""; }; BD7090CE2C5182FB009EED82 /* UnifiedFeedbackFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnifiedFeedbackFormView.swift; sourceTree = ""; }; BD7090D12C52ECFE009EED82 /* UnifiedMetadataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnifiedMetadataCollector.swift; sourceTree = ""; }; BD7090D52C540D5D009EED82 /* EmptyMetadataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyMetadataCollector.swift; sourceTree = ""; }; @@ -5230,6 +5232,7 @@ 1D39E5792C2C0F3700757339 /* ReleaseNotesUserScript.swift */, 1D0DE9402C3BB9CC0037ABC2 /* ReleaseNotesParser.swift */, 1D710F4A2C48F1F200C3975F /* UpdateDialogHelper.swift */, + BD6367242C877BE1009DE7A8 /* UpdateUserDriver.swift */, ); path = Updates; sourceTree = ""; @@ -13178,6 +13181,7 @@ 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, AA6820EB25503D6A005ED0D5 /* Fire.swift in Sources */, + BD6367252C877BE1009DE7A8 /* UpdateUserDriver.swift in Sources */, 3158B1492B0BF73000AF130C /* DBPHomeViewController.swift in Sources */, 9F56CFA92B82DC4300BB7F11 /* AddEditBookmarkFolderView.swift in Sources */, 37445F9C2A1569F00029F789 /* SyncBookmarksAdapter.swift in Sources */, diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index 67eaa2128b..fb152f19c7 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -90,7 +90,7 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { #if SPARKLE func checkForUpdate() { - updateController?.checkForUpdateInBackground() + updateController?.checkForUpdate() } func restartToUpdate() { diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 041bd77766..9e655caea4 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -94,15 +94,15 @@ final class UpdateController: NSObject, UpdateControllerProtocol { var isUpdateAvailableToInstallPublisher: Published.Publisher { $isUpdateAvailableToInstall } var lastUpdateCheckDate: Date? { - updater.updater.lastUpdateCheckDate + updater.lastUpdateCheckDate } @UserDefaultsWrapper(key: .automaticUpdates, defaultValue: true) var areAutomaticUpdatesEnabled: Bool { didSet { Logger.updates.debug("areAutomaticUpdatesEnabled: \(self.areAutomaticUpdatesEnabled)") - if updater.updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { - updater.updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled + if updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { + updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled // Reinitialize in order to reset the current loaded state if !areAutomaticUpdatesEnabled { @@ -115,7 +115,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { var shouldShowManualUpdateDialog = false - private(set) var updater: SPUStandardUpdaterController! + private(set) var updater: SPUUpdater! + private(set) var userDriver: UpdateUserDriver! private var appRestarter: AppRestarting private let willRelaunchAppSubject = PassthroughSubject() private var internalUserDecider: InternalUserDecider @@ -136,33 +137,34 @@ final class UpdateController: NSObject, UpdateControllerProtocol { func checkForUpdate() { Logger.updates.debug("Checking for updates") - updater.updater.checkForUpdates() + updater.checkForUpdates() } func checkForUpdateInBackground() { Logger.updates.debug("Checking for updates in background") - updater.updater.checkForUpdatesInBackground() + updater.checkForUpdatesInBackground() } // MARK: - Private private func configureUpdater() { // The default configuration of Sparkle updates is in Info.plist - updater = SPUStandardUpdaterController(updaterDelegate: self, userDriverDelegate: self) + userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, + automaticUpdateFlow: areAutomaticUpdatesEnabled, + delegate: self) + updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) + try? updater.start() shouldShowManualUpdateDialog = false - if updater.updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { - updater.updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled + if updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { + updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled } #if DEBUG - updater.updater.automaticallyChecksForUpdates = false - updater.updater.automaticallyDownloadsUpdates = false - updater.updater.updateCheckInterval = 0 -#else - // Load the appcast to retrieve information about the latest update (required for displaying Release Notes) - checkForUpdateInBackground() + updater.automaticallyChecksForUpdates = false + updater.automaticallyDownloadsUpdates = false + updater.updateCheckInterval = 0 #endif checkForUpdateInBackground() @@ -178,7 +180,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { if areAutomaticUpdatesEnabled { appRestarter.restart() } else { - updater.userDriver.activeUpdateAlert?.hideUnnecessaryUpdateButtons() +// updater.userDriver.activeUpdateAlert?.hideUnnecessaryUpdateButtons() shouldShowManualUpdateDialog = true checkForUpdate() } @@ -186,24 +188,28 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } -extension UpdateController: SPUStandardUserDriverDelegate { +//extension UpdateController: SPUStandardUserDriverDelegate { +// +// func standardUserDriverShouldHandleShowingScheduledUpdate(_ update: SUAppcastItem, andInImmediateFocus immediateFocus: Bool) -> Bool { +// return shouldShowManualUpdateDialog +// } +// +// func standardUserDriverWillHandleShowingUpdate(_ handleShowingUpdate: Bool, forUpdate update: SUAppcastItem, state: SPUUserUpdateState) {} +// +//} - func standardUserDriverShouldHandleShowingScheduledUpdate(_ update: SUAppcastItem, andInImmediateFocus immediateFocus: Bool) -> Bool { - return shouldShowManualUpdateDialog +extension UpdateController: UpdateUserDriverDelegate { + func userDriverUpdateCheckStart(_ userDriver: UpdateUserDriver) { + onUpdateCheckStart() } - func standardUserDriverWillHandleShowingUpdate(_ handleShowingUpdate: Bool, forUpdate update: SUAppcastItem, state: SPUUserUpdateState) {} - + func userDriverUpdateCheckEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) { + onUpdateCheckEnd(item: item, isInstalled: isInstalled) + } } extension UpdateController: SPUUpdaterDelegate { - func updater(_ updater: SPUUpdater, mayPerform updateCheck: SPUUpdateCheck) throws { - Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") - - onUpdateCheckStart() - } - private func onUpdateCheckStart() { isUpdateBeingLoaded = true } @@ -233,28 +239,6 @@ extension UpdateController: SPUUpdaterDelegate { PixelKit.fire(DebugEvent(GeneralPixel.updaterAborted, error: error)) } - func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) { - Logger.updates.debug("Updater did find valid update: \(item.displayVersionString)(\(item.versionString))") - - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - - guard !areAutomaticUpdatesEnabled else { - // If automatic updates are enabled, we are waiting until the update is downloaded - return - } - // For manual updates, show the available update without downloading - onUpdateCheckEnd(item: item, isInstalled: false) - } - - func updaterDidNotFindUpdate(_ updater: SPUUpdater, error: any Error) { - let item = (error as NSError).userInfo["SULatestAppcastItemFound"] as? SUAppcastItem - Logger.updates.debug("Updater did not find update: \(String(describing: item?.displayVersionString))(\(String(describing: item?.versionString)))") - - onUpdateCheckEnd(item: item, isInstalled: true) - - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) - } - func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { Logger.updates.debug("Updater did download update: \(item.displayVersionString)(\(item.versionString))") diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift new file mode 100644 index 0000000000..e1c8886f85 --- /dev/null +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -0,0 +1,138 @@ +// +// UpdateUserDriver.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// 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. +// + +import Foundation +import Sparkle +import PixelKit +import BrowserServicesKit +import os.log + +#if SPARKLE + +protocol UpdateUserDriverDelegate: AnyObject { + func userDriverUpdateCheckStart(_ userDriver: UpdateUserDriver) + func userDriverUpdateCheckEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) +} + +final class UpdateUserDriver: NSObject, SPUUserDriver { + private var internalUserDecider: InternalUserDecider + private var automaticUpdateFlow: Bool + private weak var delegate: UpdateUserDriverDelegate? + + init(internalUserDecider: InternalUserDecider, + automaticUpdateFlow: Bool, + delegate: UpdateUserDriverDelegate? = nil) { + self.internalUserDecider = internalUserDecider + self.automaticUpdateFlow = automaticUpdateFlow + self.delegate = delegate + } + + func show(_ request: SPUUpdatePermissionRequest) async -> SUUpdatePermissionResponse { +#if DEBUG + .init(automaticUpdateChecks: false, sendSystemProfile: false) +#else + .init(automaticUpdateChecks: true, sendSystemProfile: false) +#endif + } + + func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) { + Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") + delegate?.userDriverUpdateCheckStart(self) + } + + func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { + guard !appcastItem.isInformationOnlyUpdate else { + return .dismiss + } + + Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") + + PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) + + if !automaticUpdateFlow { + delegate?.userDriverUpdateCheckEnd(self, item: appcastItem, isInstalled: false) + + return .dismiss + } + + return .install + } + + func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { + } + + func showUpdateReleaseNotesFailedToDownloadWithError(_ error: any Error) { + } + + func showUpdateNotFoundWithError(_ error: any Error, acknowledgement: @escaping () -> Void) { + let item = (error as NSError).userInfo["SULatestAppcastItemFound"] as? SUAppcastItem + Logger.updates.debug("Updater did not find update: \(String(describing: item?.displayVersionString))(\(String(describing: item?.versionString)))") + if let item { + // User is running the latest version + delegate?.userDriverUpdateCheckEnd(self, item: item, isInstalled: true) + } + + PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) + + acknowledgement() + } + + func showUpdaterError(_ error: any Error, acknowledgement: @escaping () -> Void) { + } + + func showDownloadInitiated(cancellation: @escaping () -> Void) { + } + + func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) { + let megabytes = Double(expectedContentLength) / 1024 + print("[Update] Expected content length: \(String(format: "%.2f", megabytes)) MB") + } + + func showDownloadDidReceiveData(ofLength length: UInt64) { + let megabytes = Double(length) / 1024 + print("[Update] Did receive: \(String(format: "%.2f", megabytes)) MB") + } + + func showDownloadDidStartExtractingUpdate() { + print("[Update] Start extracting update") + } + + func showExtractionReceivedProgress(_ progress: Double) { + print("[Update] Extraction: \(String(format: "%.2f", progress / 100.0))%") + } + + func showReadyToInstallAndRelaunch() async -> SPUUserUpdateChoice { + .install + } + + func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { + retryTerminatingApplication() + } + + func showUpdateInstalledAndRelaunched(_ relaunched: Bool, acknowledgement: @escaping () -> Void) { + acknowledgement() + } + + func showUpdateInFocus() { + } + + func dismissUpdateInstallation() { + } +} + +#endif diff --git a/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift b/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift index eebcb0f3d9..8f91c6078c 100644 --- a/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift +++ b/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift @@ -25,6 +25,7 @@ let nonSandboxedExtraInputFiles: Set = [ .init("BWEncryptionOutput.m", .source), .init("BWManager.swift", .source), .init("UpdateController.swift", .source), + .init("UpdateUserDriver.swift", .source), .init("PFMoveApplication.m", .source), .init("DuckDuckGo VPN.app", .unknown), .init("DuckDuckGo Notifications.app", .unknown), From 0b90f6b2407b7fc7e4fdb023270720fc5e698048 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 3 Sep 2024 18:09:44 -0400 Subject: [PATCH 05/58] Refactor UpdateState to show progress --- DuckDuckGo/Localizable.xcstrings | 28 +++++++ .../Preferences/Model/AboutPreferences.swift | 26 ++++--- .../View/PreferencesAboutView.swift | 77 ++++++++++++++----- .../Updates/ReleaseNotesTabExtension.swift | 4 +- DuckDuckGo/Updates/UpdateController.swift | 48 ++++++++---- DuckDuckGo/Updates/UpdateUserDriver.swift | 65 ++++++++-------- 6 files changed, 168 insertions(+), 80 deletions(-) diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index ae5547319a..b3413d5a7d 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -12895,6 +12895,9 @@ } } } + }, + "Checking for update" : { + }, "clear" : { "comment" : "Clear button", @@ -17951,6 +17954,19 @@ } } } + }, + "Downloading %@ / %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Downloading %1$@ / %2$@" + } + } + } + }, + "Downloading update" : { + }, "downloads.active.alert.message.and.others" : { "comment" : "Alert text format element for “, and other files”", @@ -22466,6 +22482,12 @@ } } } + }, + "Extracting update" : { + + }, + "Extracting update (%lf%%)" : { + }, "Favorite This Page…" : { "comment" : "Main Menu History item", @@ -31530,6 +31552,9 @@ } } } + }, + "Installing" : { + }, "invite.dialog.get.started.button" : { "comment" : "Get Started button on an invite dialog", @@ -54629,6 +54654,9 @@ } } } + }, + "Ready to install" : { + }, "Recently Closed" : { "comment" : "Main Menu History item", diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index fb152f19c7..1603672a73 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -27,19 +27,21 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { #if SPARKLE enum UpdateState { - case loading case upToDate - case newVersionAvailable + case newVersionAvailable(UpdateControllerProgress) - init(from update: Update?, isLoading: Bool) { - if isLoading { - self = .loading + var isLoading: Bool { + switch self { + case .upToDate: return false + case .newVersionAvailable(let progress): return !progress.isIdle + } + } + + init(from update: Update?, progress: UpdateControllerProgress) { + if let update, !update.isInstalled { + self = .newVersionAvailable(progress) } else { - if let update, !update.isInstalled { - self = .newVersionAvailable - } else { - self = .upToDate - } + self = .upToDate } } } @@ -101,7 +103,7 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { guard let updateController, !subscribed else { return } cancellable = updateController.latestUpdatePublisher - .combineLatest(updateController.isUpdateBeingLoadedPublisher) + .combineLatest(updateController.updateProgressPublisher) .receive(on: DispatchQueue.main) .sink { [weak self] _ in self?.refreshUpdateState() @@ -114,7 +116,7 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { private func refreshUpdateState() { guard let updateController else { return } - updateState = UpdateState(from: updateController.latestUpdate, isLoading: updateController.isUpdateBeingLoaded) + updateState = UpdateState(from: updateController.latestUpdate, progress: updateController.updateProgress) } #endif diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index ecf966ac2d..b9ac47d4e9 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -150,35 +150,68 @@ extension Preferences { })) #if SPARKLE switch model.updateState { - case .loading: - Text(" — " + UserText.checkingForUpdate) case .upToDate: Text(" — " + UserText.upToDate) - case .newVersionAvailable: - Text(" — " + UserText.newerVersionAvailable) + case .newVersionAvailable(let progress): + if progress.isIdle { + Text(" — " + UserText.newerVersionAvailable) + } else { + text(for: progress) + } } #endif } } #if SPARKLE + private var formatter: ByteCountFormatter { + let formatter = ByteCountFormatter() + formatter.allowsNonnumericFormatting = false + formatter.allowedUnits = [.useKB, .useMB, .useGB] + return formatter + } + + @ViewBuilder + private func text(for progress: UpdateControllerProgress) -> some View { + switch progress { + case .checkDidStart: + Text("- Checking for update") + case .downloadDidStart: + Text("- Downloading update") + case .downloading(let bytesDownloaded, let bytesToDownload): + Text("- Downloading \(formatter.string(fromByteCount: Int64(bytesDownloaded))) / \(formatter.string(fromByteCount: Int64(bytesToDownload)))") + case .extractionDidStart: + Text("- Extracting update") + case .extracting(let percentage): + Text("- Extracting update (\(percentage * 100)%)") + case .readyToInstallAndRelaunch: + Text("- Ready to install") + case .installationDidStart, .installing: + Text("- Installing") + case .idle, .done: + Text("") + } + } + @ViewBuilder private var statusIcon: some View { switch model.updateState { - case .loading: - ProgressView() - .scaleEffect(0.6) case .upToDate: Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) - case .newVersionAvailable: - Image(systemName: "exclamationmark.circle.fill") - .foregroundColor(.red) + case .newVersionAvailable(let progress): + if progress.isIdle { + Image(systemName: "exclamationmark.circle.fill") + .foregroundColor(.red) + } else { + ProgressView() + .scaleEffect(0.6) + } } } private var lastCheckedText: some View { - let lastChecked = model.updateState != .loading ? "\(lastCheckedFormattedDate(model.lastUpdateCheckDate))" : "-" + let lastChecked = !model.updateState.isLoading ? "\(lastCheckedFormattedDate(model.lastUpdateCheckDate))" : "-" return Text("\(UserText.lastChecked): \(lastChecked)") .foregroundColor(.secondary) } @@ -200,22 +233,24 @@ extension Preferences { @ViewBuilder private var updateButton: some View { switch model.updateState { - case .loading: - Button(UserText.checkForUpdate) { - model.checkForUpdate() - } - .buttonStyle(UpdateButtonStyle(enabled: false)) - .disabled(true) case .upToDate: Button(UserText.checkForUpdate) { model.checkForUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) - case .newVersionAvailable: - Button(UserText.restartToUpdate) { - model.restartToUpdate() + case .newVersionAvailable(let progress): + if progress.isIdle { + Button(UserText.restartToUpdate) { + model.restartToUpdate() + } + .buttonStyle(UpdateButtonStyle(enabled: true)) + } else { + Button(UserText.checkForUpdate) { + model.checkForUpdate() + } + .buttonStyle(UpdateButtonStyle(enabled: false)) + .disabled(true) } - .buttonStyle(UpdateButtonStyle(enabled: true)) } } #endif diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index 9ad7e59e48..e098acd6c1 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -84,7 +84,7 @@ final class ReleaseNotesTabExtension: NavigationResponder { return } let updateController = Application.appDelegate.updateController! - Publishers.CombineLatest(updateController.isUpdateBeingLoadedPublisher, updateController.latestUpdatePublisher) + Publishers.CombineLatest(updateController.updateProgressPublisher, updateController.latestUpdatePublisher) .receive(on: DispatchQueue.main) .sink { [weak self] _ in guard let self else { return } @@ -125,7 +125,7 @@ extension ReleaseNotesValues { let status: String let latestVersion: String - guard let updateController, !updateController.isUpdateBeingLoaded else { + guard let updateController, !updateController.updateProgress.isIdle else { self.init(status: "loading", currentVersion: currentVersion, lastUpdate: lastUpdate) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 9e655caea4..b37778dc08 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -26,6 +26,28 @@ import PixelKit import SwiftUI import os.log +enum UpdateControllerProgress { + case idle + case checkDidStart + case downloadDidStart + case downloading(UInt64, UInt64) + case extractionDidStart + case extracting(Double) + case readyToInstallAndRelaunch + case installationDidStart + case installing + case done + + static var `default` = UpdateControllerProgress.idle + + var isIdle: Bool { + switch self { + case .idle, .done: return true + default: return false + } + } +} + protocol UpdateControllerProtocol: AnyObject { var latestUpdate: Update? { get } @@ -34,8 +56,8 @@ protocol UpdateControllerProtocol: AnyObject { var isUpdateAvailableToInstall: Bool { get } var isUpdateAvailableToInstallPublisher: Published.Publisher { get } - var isUpdateBeingLoaded: Bool { get } - var isUpdateBeingLoadedPublisher: Published.Publisher { get } + var updateProgress: UpdateControllerProgress { get } + var updateProgressPublisher: Published.Publisher { get } var lastUpdateCheckDate: Date? { get } @@ -69,8 +91,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { configureUpdater() } - @Published private(set) var isUpdateBeingLoaded = false - var isUpdateBeingLoadedPublisher: Published.Publisher { $isUpdateBeingLoaded } + @Published private(set) var updateProgress = UpdateControllerProgress.default + var updateProgressPublisher: Published.Publisher { $updateProgress } @Published private(set) var latestUpdate: Update? { didSet { @@ -151,7 +173,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { private func configureUpdater() { // The default configuration of Sparkle updates is in Info.plist userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, - automaticUpdateFlow: areAutomaticUpdatesEnabled, + deferInstallation: areAutomaticUpdatesEnabled, delegate: self) updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) try? updater.start() @@ -167,7 +189,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater.updateCheckInterval = 0 #endif - checkForUpdateInBackground() +// checkForUpdateInBackground() } @objc func openUpdatesPage() { @@ -199,21 +221,17 @@ final class UpdateController: NSObject, UpdateControllerProtocol { //} extension UpdateController: UpdateUserDriverDelegate { - func userDriverUpdateCheckStart(_ userDriver: UpdateUserDriver) { - onUpdateCheckStart() - } - func userDriverUpdateCheckEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) { onUpdateCheckEnd(item: item, isInstalled: isInstalled) } + + func userDriverUpdateCheckProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) { + updateProgress = progress + } } extension UpdateController: SPUUpdaterDelegate { - private func onUpdateCheckStart() { - isUpdateBeingLoaded = true - } - func allowedChannels(for updater: SPUUpdater) -> Set { if internalUserDecider.isInternalUser { return Set([Constants.internalChannelName]) @@ -258,11 +276,11 @@ extension UpdateController: SPUUpdaterDelegate { } else { latestUpdate = nil } - isUpdateBeingLoaded = false } func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { Logger.updates.debug("Updater did finish update cycle") + updateProgress = .done } } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index e1c8886f85..99fe1b043a 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -25,20 +25,23 @@ import os.log #if SPARKLE protocol UpdateUserDriverDelegate: AnyObject { - func userDriverUpdateCheckStart(_ userDriver: UpdateUserDriver) func userDriverUpdateCheckEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) + func userDriverUpdateCheckProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) } final class UpdateUserDriver: NSObject, SPUUserDriver { private var internalUserDecider: InternalUserDecider - private var automaticUpdateFlow: Bool + private var deferInstallation: Bool private weak var delegate: UpdateUserDriverDelegate? + private var bytesToDownload: UInt64 = 0 + private var bytesDownloaded: UInt64 = 0 + init(internalUserDecider: InternalUserDecider, - automaticUpdateFlow: Bool, - delegate: UpdateUserDriverDelegate? = nil) { + deferInstallation: Bool, + delegate: UpdateUserDriverDelegate?) { self.internalUserDecider = internalUserDecider - self.automaticUpdateFlow = automaticUpdateFlow + self.deferInstallation = deferInstallation self.delegate = delegate } @@ -52,79 +55,80 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) { Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") - delegate?.userDriverUpdateCheckStart(self) + delegate?.userDriverUpdateCheckProgress(self, progress: .checkDidStart) } func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { - guard !appcastItem.isInformationOnlyUpdate else { - return .dismiss - } - Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - if !automaticUpdateFlow { - delegate?.userDriverUpdateCheckEnd(self, item: appcastItem, isInstalled: false) + delegate?.userDriverUpdateCheckEnd(self, item: appcastItem, isInstalled: false) - return .dismiss - } - - return .install + return deferInstallation ? .install : .dismiss } func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { + // no-op } func showUpdateReleaseNotesFailedToDownloadWithError(_ error: any Error) { + // no-op } func showUpdateNotFoundWithError(_ error: any Error, acknowledgement: @escaping () -> Void) { - let item = (error as NSError).userInfo["SULatestAppcastItemFound"] as? SUAppcastItem - Logger.updates.debug("Updater did not find update: \(String(describing: item?.displayVersionString))(\(String(describing: item?.versionString)))") - if let item { - // User is running the latest version - delegate?.userDriverUpdateCheckEnd(self, item: item, isInstalled: true) + guard let item = (error as NSError).userInfo["SULatestAppcastItemFound"] as? SUAppcastItem else { + acknowledgement() + return } + Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) + // User is running the latest version + delegate?.userDriverUpdateCheckEnd(self, item: item, isInstalled: true) + acknowledgement() } func showUpdaterError(_ error: any Error, acknowledgement: @escaping () -> Void) { + // no-op } func showDownloadInitiated(cancellation: @escaping () -> Void) { + delegate?.userDriverUpdateCheckProgress(self, progress: .downloadDidStart) } func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) { - let megabytes = Double(expectedContentLength) / 1024 - print("[Update] Expected content length: \(String(format: "%.2f", megabytes)) MB") + bytesDownloaded = 0 + bytesToDownload = expectedContentLength } func showDownloadDidReceiveData(ofLength length: UInt64) { - let megabytes = Double(length) / 1024 - print("[Update] Did receive: \(String(format: "%.2f", megabytes)) MB") + bytesDownloaded += length + if bytesDownloaded > bytesToDownload { + bytesToDownload = bytesDownloaded + } + delegate?.userDriverUpdateCheckProgress(self, progress: .downloading(bytesDownloaded, bytesToDownload)) } func showDownloadDidStartExtractingUpdate() { - print("[Update] Start extracting update") + delegate?.userDriverUpdateCheckProgress(self, progress: .extractionDidStart) } func showExtractionReceivedProgress(_ progress: Double) { - print("[Update] Extraction: \(String(format: "%.2f", progress / 100.0))%") + delegate?.userDriverUpdateCheckProgress(self, progress: .extracting(progress)) } func showReadyToInstallAndRelaunch() async -> SPUUserUpdateChoice { - .install + deferInstallation ? .dismiss : .install } func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { - retryTerminatingApplication() + delegate?.userDriverUpdateCheckProgress(self, progress: .installationDidStart) } func showUpdateInstalledAndRelaunched(_ relaunched: Bool, acknowledgement: @escaping () -> Void) { + delegate?.userDriverUpdateCheckProgress(self, progress: .installing) acknowledgement() } @@ -132,6 +136,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func dismissUpdateInstallation() { + delegate?.userDriverUpdateCheckProgress(self, progress: .done) } } From ee244bed5f72621cce8a053c902be7397a2b3dea Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 3 Sep 2024 20:11:46 -0400 Subject: [PATCH 06/58] Fix wrong deferInstallation param --- DuckDuckGo/Localizable.xcstrings | 53 ++++++++++++----------- DuckDuckGo/Updates/UpdateController.swift | 2 +- DuckDuckGo/Updates/UpdateUserDriver.swift | 2 +- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index b3413d5a7d..2d4fc66c84 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -104,6 +104,34 @@ } } } + }, + "- Checking for update" : { + + }, + "- Downloading %@ / %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "- Downloading %1$@ / %2$@" + } + } + } + }, + "- Downloading update" : { + + }, + "- Extracting update" : { + + }, + "- Extracting update (%lf%%)" : { + + }, + "- Installing" : { + + }, + "- Ready to install" : { + }, "**%lld** tracking attempts blocked" : { "comment" : "The number of tracking attempts blocked in the last 7 days, shown on a new tab, translate as: Tracking attempts blocked: %@", @@ -17954,19 +17982,6 @@ } } } - }, - "Downloading %@ / %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Downloading %1$@ / %2$@" - } - } - } - }, - "Downloading update" : { - }, "downloads.active.alert.message.and.others" : { "comment" : "Alert text format element for “, and other files”", @@ -22482,12 +22497,6 @@ } } } - }, - "Extracting update" : { - - }, - "Extracting update (%lf%%)" : { - }, "Favorite This Page…" : { "comment" : "Main Menu History item", @@ -31552,9 +31561,6 @@ } } } - }, - "Installing" : { - }, "invite.dialog.get.started.button" : { "comment" : "Get Started button on an invite dialog", @@ -54654,9 +54660,6 @@ } } } - }, - "Ready to install" : { - }, "Recently Closed" : { "comment" : "Main Menu History item", diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index b37778dc08..62cbcffabb 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -173,7 +173,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { private func configureUpdater() { // The default configuration of Sparkle updates is in Info.plist userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, - deferInstallation: areAutomaticUpdatesEnabled, + deferInstallation: !areAutomaticUpdatesEnabled, delegate: self) updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) try? updater.start() diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 99fe1b043a..f85ca3c88c 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -64,7 +64,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { delegate?.userDriverUpdateCheckEnd(self, item: appcastItem, isInstalled: false) - return deferInstallation ? .install : .dismiss + return deferInstallation ? .dismiss : .install } func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { From 91cf329bdcc84d1c876ca7d4725ede3839ab9c71 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 11:44:45 -0400 Subject: [PATCH 07/58] Show update progress --- .../Preferences/Model/AboutPreferences.swift | 2 +- .../Preferences/View/PreferencesAboutView.swift | 6 +++--- DuckDuckGo/Updates/ReleaseNotesTabExtension.swift | 2 +- DuckDuckGo/Updates/UpdateController.swift | 13 +++---------- DuckDuckGo/Updates/UpdateUserDriver.swift | 14 ++++++++++++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index 1603672a73..a9ece7c323 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -33,7 +33,7 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { var isLoading: Bool { switch self { case .upToDate: return false - case .newVersionAvailable(let progress): return !progress.isIdle + case .newVersionAvailable(let progress): return !progress.isDone } } diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index b9ac47d4e9..426c654ea3 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -153,7 +153,7 @@ extension Preferences { case .upToDate: Text(" — " + UserText.upToDate) case .newVersionAvailable(let progress): - if progress.isIdle { + if progress.isDone { Text(" — " + UserText.newerVersionAvailable) } else { text(for: progress) @@ -200,7 +200,7 @@ extension Preferences { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) case .newVersionAvailable(let progress): - if progress.isIdle { + if progress.isDone { Image(systemName: "exclamationmark.circle.fill") .foregroundColor(.red) } else { @@ -239,7 +239,7 @@ extension Preferences { } .buttonStyle(UpdateButtonStyle(enabled: true)) case .newVersionAvailable(let progress): - if progress.isIdle { + if progress.isDone { Button(UserText.restartToUpdate) { model.restartToUpdate() } diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index e098acd6c1..cafbab141e 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -125,7 +125,7 @@ extension ReleaseNotesValues { let status: String let latestVersion: String - guard let updateController, !updateController.updateProgress.isIdle else { + guard let updateController, updateController.updateProgress.isDone else { self.init(status: "loading", currentVersion: currentVersion, lastUpdate: lastUpdate) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 62cbcffabb..30299d9dd0 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -40,9 +40,9 @@ enum UpdateControllerProgress { static var `default` = UpdateControllerProgress.idle - var isIdle: Bool { + var isDone: Bool { switch self { - case .idle, .done: return true + case .done: return true default: return false } } @@ -198,14 +198,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { @objc func runUpdate() { PixelKit.fire(DebugEvent(GeneralPixel.updaterDidRunUpdate)) - - if areAutomaticUpdatesEnabled { - appRestarter.restart() - } else { -// updater.userDriver.activeUpdateAlert?.hideUnnecessaryUpdateButtons() - shouldShowManualUpdateDialog = true - checkForUpdate() - } + userDriver.install() } } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index f85ca3c88c..0672913abe 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -37,6 +37,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { private var bytesToDownload: UInt64 = 0 private var bytesDownloaded: UInt64 = 0 + private var onManualInstall: () -> Void = {} + init(internalUserDecider: InternalUserDecider, deferInstallation: Bool, delegate: UpdateUserDriverDelegate?) { @@ -45,6 +47,10 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { self.delegate = delegate } + func install() { + onManualInstall() + } + func show(_ request: SPUUpdatePermissionRequest) async -> SUUpdatePermissionResponse { #if DEBUG .init(automaticUpdateChecks: false, sendSystemProfile: false) @@ -58,13 +64,17 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { delegate?.userDriverUpdateCheckProgress(self, progress: .checkDidStart) } - func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { + func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState, reply: @escaping (SPUUserUpdateChoice) -> Void) { Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) delegate?.userDriverUpdateCheckEnd(self, item: appcastItem, isInstalled: false) - return deferInstallation ? .dismiss : .install + if deferInstallation { + onManualInstall = { reply(.install) } + } else { + reply(.install) + } } func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { From 8d69203599ab201a03adb2f7d1fd3f715f4f4210 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 12:50:47 -0400 Subject: [PATCH 08/58] Add manual restart --- DuckDuckGo/Localizable.xcstrings | 3 ++ .../Preferences/Model/AboutPreferences.swift | 9 +++- .../View/PreferencesAboutView.swift | 52 ++++++++----------- DuckDuckGo/Updates/UpdateController.swift | 12 ++--- DuckDuckGo/Updates/UpdateUserDriver.swift | 22 ++++---- 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index 2d4fc66c84..24480c646e 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -123,6 +123,9 @@ }, "- Extracting update" : { + }, + "- Extracting update (%@%%)" : { + }, "- Extracting update (%lf%%)" : { diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index a9ece7c323..239eb8fe1d 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -29,17 +29,22 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { case upToDate case newVersionAvailable(UpdateControllerProgress) + case readyToInstallAndRelaunch var isLoading: Bool { switch self { - case .upToDate: return false + case .upToDate, .readyToInstallAndRelaunch: return false case .newVersionAvailable(let progress): return !progress.isDone } } init(from update: Update?, progress: UpdateControllerProgress) { if let update, !update.isInstalled { - self = .newVersionAvailable(progress) + if progress.isDone { + self = .readyToInstallAndRelaunch + } else { + self = .newVersionAvailable(progress) + } } else { self = .upToDate } diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 426c654ea3..fa541f7643 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -152,12 +152,10 @@ extension Preferences { switch model.updateState { case .upToDate: Text(" — " + UserText.upToDate) + case .readyToInstallAndRelaunch: + Text(" — " + UserText.newerVersionAvailable) case .newVersionAvailable(let progress): - if progress.isDone { - Text(" — " + UserText.newerVersionAvailable) - } else { - text(for: progress) - } + text(for: progress) } #endif } @@ -174,7 +172,7 @@ extension Preferences { @ViewBuilder private func text(for progress: UpdateControllerProgress) -> some View { switch progress { - case .checkDidStart: + case .updateCycleDidStart: Text("- Checking for update") case .downloadDidStart: Text("- Downloading update") @@ -183,13 +181,13 @@ extension Preferences { case .extractionDidStart: Text("- Extracting update") case .extracting(let percentage): - Text("- Extracting update (\(percentage * 100)%)") + Text("- Extracting update (\(String(format: "%.1f", percentage * 100))%)") case .readyToInstallAndRelaunch: Text("- Ready to install") case .installationDidStart, .installing: Text("- Installing") - case .idle, .done: - Text("") + case .updateCycleNotStarted, .updateCycleDone: + fatalError() } } @@ -199,14 +197,12 @@ extension Preferences { case .upToDate: Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) - case .newVersionAvailable(let progress): - if progress.isDone { - Image(systemName: "exclamationmark.circle.fill") - .foregroundColor(.red) - } else { - ProgressView() - .scaleEffect(0.6) - } + case .readyToInstallAndRelaunch: + Image(systemName: "exclamationmark.circle.fill") + .foregroundColor(.red) + case .newVersionAvailable: + ProgressView() + .scaleEffect(0.6) } } @@ -238,19 +234,17 @@ extension Preferences { model.checkForUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) - case .newVersionAvailable(let progress): - if progress.isDone { - Button(UserText.restartToUpdate) { - model.restartToUpdate() - } - .buttonStyle(UpdateButtonStyle(enabled: true)) - } else { - Button(UserText.checkForUpdate) { - model.checkForUpdate() - } - .buttonStyle(UpdateButtonStyle(enabled: false)) - .disabled(true) + case .readyToInstallAndRelaunch: + Button(UserText.restartToUpdate) { + model.restartToUpdate() + } + .buttonStyle(UpdateButtonStyle(enabled: true)) + case .newVersionAvailable: + Button(UserText.checkForUpdate) { + model.checkForUpdate() } + .buttonStyle(UpdateButtonStyle(enabled: false)) + .disabled(true) } } #endif diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 30299d9dd0..449b096cda 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -27,8 +27,8 @@ import SwiftUI import os.log enum UpdateControllerProgress { - case idle - case checkDidStart + case updateCycleNotStarted + case updateCycleDidStart case downloadDidStart case downloading(UInt64, UInt64) case extractionDidStart @@ -36,13 +36,13 @@ enum UpdateControllerProgress { case readyToInstallAndRelaunch case installationDidStart case installing - case done + case updateCycleDone - static var `default` = UpdateControllerProgress.idle + static var `default` = UpdateControllerProgress.updateCycleNotStarted var isDone: Bool { switch self { - case .done: return true + case .updateCycleDone, .readyToInstallAndRelaunch: return true default: return false } } @@ -273,7 +273,7 @@ extension UpdateController: SPUUpdaterDelegate { func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { Logger.updates.debug("Updater did finish update cycle") - updateProgress = .done + updateProgress = .updateCycleDone } } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 0672913abe..9eb23125fc 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -61,20 +61,17 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) { Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") - delegate?.userDriverUpdateCheckProgress(self, progress: .checkDidStart) + delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDidStart) } - func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState, reply: @escaping (SPUUserUpdateChoice) -> Void) { + func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) delegate?.userDriverUpdateCheckEnd(self, item: appcastItem, isInstalled: false) + delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDone) - if deferInstallation { - onManualInstall = { reply(.install) } - } else { - reply(.install) - } + return appcastItem.isInformationOnlyUpdate ? .dismiss : .install } func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { @@ -129,8 +126,13 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { delegate?.userDriverUpdateCheckProgress(self, progress: .extracting(progress)) } - func showReadyToInstallAndRelaunch() async -> SPUUserUpdateChoice { - deferInstallation ? .dismiss : .install + func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { + if deferInstallation { + onManualInstall = { reply(.install) } + } else { + reply(.install) + } + delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDone) } func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { @@ -146,7 +148,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func dismissUpdateInstallation() { - delegate?.userDriverUpdateCheckProgress(self, progress: .done) + delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDone) } } From af2fd57a19e6f4b5f3cc41dfccb7b27bc82e2323 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 12:53:36 -0400 Subject: [PATCH 09/58] Rename delegate methods --- DuckDuckGo/Updates/UpdateController.swift | 4 ++-- DuckDuckGo/Updates/UpdateUserDriver.swift | 29 ++++++++++++----------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 449b096cda..533d2d46e3 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -214,11 +214,11 @@ final class UpdateController: NSObject, UpdateControllerProtocol { //} extension UpdateController: UpdateUserDriverDelegate { - func userDriverUpdateCheckEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) { + func userDriverUpdateCycleEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) { onUpdateCheckEnd(item: item, isInstalled: isInstalled) } - func userDriverUpdateCheckProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) { + func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) { updateProgress = progress } } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 9eb23125fc..96aecec9bc 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -25,8 +25,8 @@ import os.log #if SPARKLE protocol UpdateUserDriverDelegate: AnyObject { - func userDriverUpdateCheckEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) - func userDriverUpdateCheckProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) + func userDriverUpdateCycleEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) + func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) } final class UpdateUserDriver: NSObject, SPUUserDriver { @@ -61,15 +61,15 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) { Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") - delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDidStart) + delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDidStart) } func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - delegate?.userDriverUpdateCheckEnd(self, item: appcastItem, isInstalled: false) - delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDone) + delegate?.userDriverUpdateCycleEnd(self, item: appcastItem, isInstalled: false) + delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) return appcastItem.isInformationOnlyUpdate ? .dismiss : .install } @@ -92,7 +92,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) // User is running the latest version - delegate?.userDriverUpdateCheckEnd(self, item: item, isInstalled: true) + delegate?.userDriverUpdateCycleEnd(self, item: item, isInstalled: true) acknowledgement() } @@ -102,7 +102,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showDownloadInitiated(cancellation: @escaping () -> Void) { - delegate?.userDriverUpdateCheckProgress(self, progress: .downloadDidStart) + delegate?.userDriverUpdateCycleProgress(self, progress: .downloadDidStart) } func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) { @@ -115,15 +115,15 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { if bytesDownloaded > bytesToDownload { bytesToDownload = bytesDownloaded } - delegate?.userDriverUpdateCheckProgress(self, progress: .downloading(bytesDownloaded, bytesToDownload)) + delegate?.userDriverUpdateCycleProgress(self, progress: .downloading(bytesDownloaded, bytesToDownload)) } func showDownloadDidStartExtractingUpdate() { - delegate?.userDriverUpdateCheckProgress(self, progress: .extractionDidStart) + delegate?.userDriverUpdateCycleProgress(self, progress: .extractionDidStart) } func showExtractionReceivedProgress(_ progress: Double) { - delegate?.userDriverUpdateCheckProgress(self, progress: .extracting(progress)) + delegate?.userDriverUpdateCycleProgress(self, progress: .extracting(progress)) } func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { @@ -132,23 +132,24 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } else { reply(.install) } - delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDone) + delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) } func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { - delegate?.userDriverUpdateCheckProgress(self, progress: .installationDidStart) + delegate?.userDriverUpdateCycleProgress(self, progress: .installationDidStart) } func showUpdateInstalledAndRelaunched(_ relaunched: Bool, acknowledgement: @escaping () -> Void) { - delegate?.userDriverUpdateCheckProgress(self, progress: .installing) + delegate?.userDriverUpdateCycleProgress(self, progress: .installing) acknowledgement() } func showUpdateInFocus() { + // no-op } func dismissUpdateInstallation() { - delegate?.userDriverUpdateCheckProgress(self, progress: .updateCycleDone) + delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) } } From 7fa88b3e5d3fb4981357a9776f33cd8c44ad3b0f Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 12:55:33 -0400 Subject: [PATCH 10/58] Remove AppRestarter --- DuckDuckGo.xcodeproj/project.pbxproj | 6 -- DuckDuckGo/Updates/AppRestarter.swift | 83 ----------------------- DuckDuckGo/Updates/UpdateController.swift | 5 +- 3 files changed, 1 insertion(+), 93 deletions(-) delete mode 100644 DuckDuckGo/Updates/AppRestarter.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 76bf28c142..3ee547c432 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -54,8 +54,6 @@ 1D01A3D92B88DF8B00FE8150 /* PreferencesSyncView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D01A3D72B88DF8B00FE8150 /* PreferencesSyncView.swift */; }; 1D02633628D8A9A9005CBB41 /* BWEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D02633528D8A9A9005CBB41 /* BWEncryption.m */; settings = {COMPILER_FLAGS = "-Wno-deprecated -Wno-strict-prototypes"; }; }; 1D074B272909A433006E4AC3 /* PasswordManagerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D074B262909A433006E4AC3 /* PasswordManagerCoordinator.swift */; }; - 1D0DE93E2C3BA9840037ABC2 /* AppRestarter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D0DE93D2C3BA9840037ABC2 /* AppRestarter.swift */; }; - 1D0DE93F2C3BA9840037ABC2 /* AppRestarter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D0DE93D2C3BA9840037ABC2 /* AppRestarter.swift */; }; 1D0DE9412C3BB9CC0037ABC2 /* ReleaseNotesParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D0DE9402C3BB9CC0037ABC2 /* ReleaseNotesParser.swift */; }; 1D0DE9422C3BB9CC0037ABC2 /* ReleaseNotesParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D0DE9402C3BB9CC0037ABC2 /* ReleaseNotesParser.swift */; }; 1D12F2E2298BC660009A65FD /* InternalUserDeciderStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D12F2E1298BC660009A65FD /* InternalUserDeciderStoreMock.swift */; }; @@ -3295,7 +3293,6 @@ 1D02633428D8A9A9005CBB41 /* BWEncryption.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BWEncryption.h; sourceTree = ""; }; 1D02633528D8A9A9005CBB41 /* BWEncryption.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BWEncryption.m; sourceTree = ""; }; 1D074B262909A433006E4AC3 /* PasswordManagerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordManagerCoordinator.swift; sourceTree = ""; }; - 1D0DE93D2C3BA9840037ABC2 /* AppRestarter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRestarter.swift; sourceTree = ""; }; 1D0DE9402C3BB9CC0037ABC2 /* ReleaseNotesParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseNotesParser.swift; sourceTree = ""; }; 1D12F2E1298BC660009A65FD /* InternalUserDeciderStoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalUserDeciderStoreMock.swift; sourceTree = ""; }; 1D1A33482A6FEB170080ACED /* BurnerMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurnerMode.swift; sourceTree = ""; }; @@ -5227,7 +5224,6 @@ 1DA84D312C119AE70011C80F /* UpdateMenuItemFactory.swift */, 1D72D59B2BFF61B200AEDE36 /* UpdateNotificationPresenter.swift */, 1D9297BE2C1B062900A38521 /* ApplicationUpdateDetector.swift */, - 1D0DE93D2C3BA9840037ABC2 /* AppRestarter.swift */, 1D39E5762C2BFD5700757339 /* ReleaseNotesTabExtension.swift */, 1D39E5792C2C0F3700757339 /* ReleaseNotesUserScript.swift */, 1D0DE9402C3BB9CC0037ABC2 /* ReleaseNotesParser.swift */, @@ -11711,7 +11707,6 @@ 3707C71E294B5D2900682A9F /* URLRequestExtension.swift in Sources */, 3706FCA3293F65D500E42796 /* WKProcessPool+GeolocationProvider.swift in Sources */, 372A0FED2B2379310033BF7F /* SyncMetricsEventsHandler.swift in Sources */, - 1D0DE93F2C3BA9840037ABC2 /* AppRestarter.swift in Sources */, 3706FCA4293F65D500E42796 /* RecentlyClosedMenu.swift in Sources */, 8400DC4C2C6E26AE006509D2 /* ItemCachingCollectionView.swift in Sources */, 4B9DB02D2A983B24000927DB /* WaitlistKeychainStorage.swift in Sources */, @@ -12633,7 +12628,6 @@ B6BF5D8929470BC4006742B1 /* HTTPSUpgradeTabExtension.swift in Sources */, 1D36E65B298ACD2900AA485D /* AppIconChanger.swift in Sources */, 4B4D60E22A0C883A00BCD287 /* AppMain.swift in Sources */, - 1D0DE93E2C3BA9840037ABC2 /* AppRestarter.swift in Sources */, 7BCB90C22C18626E008E3543 /* VPNControllerXPCClient+ConvenienceInitializers.swift in Sources */, 4B9DB0202A983B24000927DB /* ProductWaitlistRequest.swift in Sources */, 7B60B0022C5145EC008E32A3 /* VPNUIActionHandler.swift in Sources */, diff --git a/DuckDuckGo/Updates/AppRestarter.swift b/DuckDuckGo/Updates/AppRestarter.swift deleted file mode 100644 index fa498016f7..0000000000 --- a/DuckDuckGo/Updates/AppRestarter.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// AppRestarter.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// 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. -// - -import Foundation - -protocol AppRestarting { - - func restart() - -} - -final class AppRestarter: AppRestarting { - - func restart() { - let pid = ProcessInfo.processInfo.processIdentifier - let destinationPath = Bundle.main.bundlePath - - guard isValidApplicationBundle(at: destinationPath) else { - print("Invalid destination path") - return - } - - let preOpenCmd = "/usr/bin/xattr -d -r com.apple.quarantine \(shellQuotedString(destinationPath))" - let openCmd = "/usr/bin/open \(shellQuotedString(destinationPath))" - - let script = """ - (while /bin/kill -0 \(pid) >&/dev/null; do /bin/sleep 0.1; done; \(preOpenCmd); \(openCmd)) & - """ - - let task = Process() - task.launchPath = "/bin/sh" - task.arguments = ["-c", script] - - do { - try task.run() - } catch { - print("Unable to launch the task: \(error)") - return - } - - // Terminate the current app instance - exit(0) - } - - private func isValidApplicationBundle(at path: String) -> Bool { - let fileManager = FileManager.default - var isDirectory: ObjCBool = false - let exists = fileManager.fileExists(atPath: path, isDirectory: &isDirectory) - let isAppBundle = path.hasSuffix(".app") && isDirectory.boolValue - return exists && isAppBundle - } - - private func shellQuotedString(_ string: String) -> String { - // Validate that the string is a valid file path - guard isValidFilePath(string) else { - fatalError("Invalid file path") - } - let escapedString = string.replacingOccurrences(of: "'", with: "'\\''") - return "'\(escapedString)'" - } - - private func isValidFilePath(_ path: String) -> Bool { - // Perform validation to ensure the path is a valid and safe file path - let fileManager = FileManager.default - return fileManager.fileExists(atPath: path) - } - -} diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 533d2d46e3..57f2efae97 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -81,11 +81,9 @@ final class UpdateController: NSObject, UpdateControllerProtocol { lazy var notificationPresenter = UpdateNotificationPresenter() let willRelaunchAppPublisher: AnyPublisher - init(internalUserDecider: InternalUserDecider, - appRestarter: AppRestarting = AppRestarter()) { + init(internalUserDecider: InternalUserDecider) { willRelaunchAppPublisher = willRelaunchAppSubject.eraseToAnyPublisher() self.internalUserDecider = internalUserDecider - self.appRestarter = appRestarter super.init() configureUpdater() @@ -139,7 +137,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { private(set) var updater: SPUUpdater! private(set) var userDriver: UpdateUserDriver! - private var appRestarter: AppRestarting private let willRelaunchAppSubject = PassthroughSubject() private var internalUserDecider: InternalUserDecider From 100a367ddc22754d75629a3199ba3371a3c3098b Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 13:08:16 -0400 Subject: [PATCH 11/58] Fix updateCycleDone state --- DuckDuckGo/Updates/UpdateUserDriver.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 96aecec9bc..730b507515 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -69,7 +69,6 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) delegate?.userDriverUpdateCycleEnd(self, item: appcastItem, isInstalled: false) - delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) return appcastItem.isInformationOnlyUpdate ? .dismiss : .install } @@ -129,10 +128,10 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { if deferInstallation { onManualInstall = { reply(.install) } + delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) } else { reply(.install) } - delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) } func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { From ea6abac27bd449fcaa1095184007985dc9cea341 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 13:32:35 -0400 Subject: [PATCH 12/58] Remove shouldShowManualUpdateDialog --- DuckDuckGo/Updates/UpdateController.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 57f2efae97..e9969beb7f 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -133,8 +133,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } } - var shouldShowManualUpdateDialog = false - private(set) var updater: SPUUpdater! private(set) var userDriver: UpdateUserDriver! private let willRelaunchAppSubject = PassthroughSubject() @@ -174,7 +172,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { delegate: self) updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) try? updater.start() - shouldShowManualUpdateDialog = false if updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled @@ -200,16 +197,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } -//extension UpdateController: SPUStandardUserDriverDelegate { -// -// func standardUserDriverShouldHandleShowingScheduledUpdate(_ update: SUAppcastItem, andInImmediateFocus immediateFocus: Bool) -> Bool { -// return shouldShowManualUpdateDialog -// } -// -// func standardUserDriverWillHandleShowingUpdate(_ handleShowingUpdate: Bool, forUpdate update: SUAppcastItem, state: SPUUserUpdateState) {} -// -//} - extension UpdateController: UpdateUserDriverDelegate { func userDriverUpdateCycleEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) { onUpdateCheckEnd(item: item, isInstalled: isInstalled) From 0b7b1683618914d002fcc1b26d78bdc24464ee97 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 14:36:50 -0400 Subject: [PATCH 13/58] Simplify the states (double check needed) --- .../Preferences/Model/AboutPreferences.swift | 15 ++---- .../View/PreferencesAboutView.swift | 52 ++++++++++-------- DuckDuckGo/Updates/UpdateController.swift | 54 +++++-------------- DuckDuckGo/Updates/UpdateUserDriver.swift | 35 +++++++++--- 4 files changed, 77 insertions(+), 79 deletions(-) diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index 239eb8fe1d..4111eb4cbc 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -28,23 +28,18 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { enum UpdateState { case upToDate - case newVersionAvailable(UpdateControllerProgress) - case readyToInstallAndRelaunch + case updateCycle(UpdateCycleProgress) var isLoading: Bool { switch self { - case .upToDate, .readyToInstallAndRelaunch: return false - case .newVersionAvailable(let progress): return !progress.isDone + case .upToDate: return false + case .updateCycle(let progress): return !progress.isDone } } - init(from update: Update?, progress: UpdateControllerProgress) { + init(from update: Update?, progress: UpdateCycleProgress) { if let update, !update.isInstalled { - if progress.isDone { - self = .readyToInstallAndRelaunch - } else { - self = .newVersionAvailable(progress) - } + self = .updateCycle(progress) } else { self = .upToDate } diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index fa541f7643..56f96f6717 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -152,10 +152,12 @@ extension Preferences { switch model.updateState { case .upToDate: Text(" — " + UserText.upToDate) - case .readyToInstallAndRelaunch: - Text(" — " + UserText.newerVersionAvailable) - case .newVersionAvailable(let progress): - text(for: progress) + case .updateCycle(let progress): + if progress.isDone { + Text(" — " + UserText.newerVersionAvailable) + } else { + text(for: progress) + } } #endif } @@ -170,7 +172,7 @@ extension Preferences { } @ViewBuilder - private func text(for progress: UpdateControllerProgress) -> some View { + private func text(for progress: UpdateCycleProgress) -> some View { switch progress { case .updateCycleDidStart: Text("- Checking for update") @@ -186,8 +188,8 @@ extension Preferences { Text("- Ready to install") case .installationDidStart, .installing: Text("- Installing") - case .updateCycleNotStarted, .updateCycleDone: - fatalError() + case .updateCycleNotStarted, .updateCycleDone, .updateFound, .updateNotFound: + EmptyView() } } @@ -197,12 +199,14 @@ extension Preferences { case .upToDate: Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) - case .readyToInstallAndRelaunch: - Image(systemName: "exclamationmark.circle.fill") - .foregroundColor(.red) - case .newVersionAvailable: - ProgressView() - .scaleEffect(0.6) + case .updateCycle(let progress): + if progress.isDone { + Image(systemName: "exclamationmark.circle.fill") + .foregroundColor(.red) + } else { + ProgressView() + .scaleEffect(0.6) + } } } @@ -234,17 +238,19 @@ extension Preferences { model.checkForUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) - case .readyToInstallAndRelaunch: - Button(UserText.restartToUpdate) { - model.restartToUpdate() - } - .buttonStyle(UpdateButtonStyle(enabled: true)) - case .newVersionAvailable: - Button(UserText.checkForUpdate) { - model.checkForUpdate() + case .updateCycle(let progress): + if progress.isDone { + Button(UserText.restartToUpdate) { + model.restartToUpdate() + } + .buttonStyle(UpdateButtonStyle(enabled: true)) + } else { + Button(UserText.checkForUpdate) { + model.checkForUpdate() + } + .buttonStyle(UpdateButtonStyle(enabled: false)) + .disabled(true) } - .buttonStyle(UpdateButtonStyle(enabled: false)) - .disabled(true) } } #endif diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index e9969beb7f..acd0d806a3 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -26,28 +26,6 @@ import PixelKit import SwiftUI import os.log -enum UpdateControllerProgress { - case updateCycleNotStarted - case updateCycleDidStart - case downloadDidStart - case downloading(UInt64, UInt64) - case extractionDidStart - case extracting(Double) - case readyToInstallAndRelaunch - case installationDidStart - case installing - case updateCycleDone - - static var `default` = UpdateControllerProgress.updateCycleNotStarted - - var isDone: Bool { - switch self { - case .updateCycleDone, .readyToInstallAndRelaunch: return true - default: return false - } - } -} - protocol UpdateControllerProtocol: AnyObject { var latestUpdate: Update? { get } @@ -56,8 +34,8 @@ protocol UpdateControllerProtocol: AnyObject { var isUpdateAvailableToInstall: Bool { get } var isUpdateAvailableToInstallPublisher: Published.Publisher { get } - var updateProgress: UpdateControllerProgress { get } - var updateProgressPublisher: Published.Publisher { get } + var updateProgress: UpdateCycleProgress { get } + var updateProgressPublisher: Published.Publisher { get } var lastUpdateCheckDate: Date? { get } @@ -89,8 +67,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { configureUpdater() } - @Published private(set) var updateProgress = UpdateControllerProgress.default - var updateProgressPublisher: Published.Publisher { $updateProgress } + @Published private(set) var updateProgress = UpdateCycleProgress.default + var updateProgressPublisher: Published.Publisher { $updateProgress } @Published private(set) var latestUpdate: Update? { didSet { @@ -198,11 +176,15 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } extension UpdateController: UpdateUserDriverDelegate { - func userDriverUpdateCycleEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) { - onUpdateCheckEnd(item: item, isInstalled: isInstalled) - } - - func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) { + func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateCycleProgress) { + switch progress { + case .updateFound(let item): + latestUpdate = Update(appcastItem: item, isInstalled: false) + case .updateNotFound(let item, _): + latestUpdate = Update(appcastItem: item, isInstalled: true) + default: + break + } updateProgress = progress } } @@ -242,19 +224,11 @@ extension UpdateController: SPUUpdaterDelegate { return } // Automatic updates present the available update after it's downloaded - onUpdateCheckEnd(item: item, isInstalled: false) + updateProgress = .updateFound(item) PixelKit.fire(DebugEvent(GeneralPixel.updaterDidDownloadUpdate)) } - private func onUpdateCheckEnd(item: SUAppcastItem?, isInstalled: Bool) { - if let item { - latestUpdate = Update(appcastItem: item, isInstalled: isInstalled) - } else { - latestUpdate = nil - } - } - func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { Logger.updates.debug("Updater did finish update cycle") updateProgress = .updateCycleDone diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 730b507515..33db0723f6 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -24,9 +24,32 @@ import os.log #if SPARKLE +enum UpdateCycleProgress { + case updateCycleNotStarted + case updateCycleDidStart + case updateFound(SUAppcastItem) + case updateNotFound(SUAppcastItem, NSError) + case downloadDidStart + case downloading(UInt64, UInt64) + case extractionDidStart + case extracting(Double) + case readyToInstallAndRelaunch + case installationDidStart + case installing + case updateCycleDone + + static var `default` = UpdateCycleProgress.updateCycleNotStarted + + var isDone: Bool { + switch self { + case .updateCycleDone: return true + default: return false + } + } +} + protocol UpdateUserDriverDelegate: AnyObject { - func userDriverUpdateCycleEnd(_ userDriver: UpdateUserDriver, item: SUAppcastItem?, isInstalled: Bool) - func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateControllerProgress) + func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateCycleProgress) } final class UpdateUserDriver: NSObject, SPUUserDriver { @@ -68,7 +91,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - delegate?.userDriverUpdateCycleEnd(self, item: appcastItem, isInstalled: false) + delegate?.userDriverUpdateCycleProgress(self, progress: .updateFound(appcastItem)) return appcastItem.isInformationOnlyUpdate ? .dismiss : .install } @@ -82,7 +105,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showUpdateNotFoundWithError(_ error: any Error, acknowledgement: @escaping () -> Void) { - guard let item = (error as NSError).userInfo["SULatestAppcastItemFound"] as? SUAppcastItem else { + let nsError = error as NSError + guard let item = nsError.userInfo["SULatestAppcastItemFound"] as? SUAppcastItem else { acknowledgement() return } @@ -90,8 +114,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) - // User is running the latest version - delegate?.userDriverUpdateCycleEnd(self, item: item, isInstalled: true) + delegate?.userDriverUpdateCycleProgress(self, progress: .updateNotFound(item, nsError)) acknowledgement() } From 18c53f00fbe2918235479dbce058bcfa1af32676 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 14:58:13 -0400 Subject: [PATCH 14/58] Add more logging, tweak last check date --- .../Preferences/View/PreferencesAboutView.swift | 2 +- DuckDuckGo/Updates/UpdateController.swift | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 56f96f6717..23f4ad1a52 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -211,7 +211,7 @@ extension Preferences { } private var lastCheckedText: some View { - let lastChecked = !model.updateState.isLoading ? "\(lastCheckedFormattedDate(model.lastUpdateCheckDate))" : "-" + let lastChecked = lastCheckedFormattedDate(model.lastUpdateCheckDate) return Text("\(UserText.lastChecked): \(lastChecked)") .foregroundColor(.secondary) } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index acd0d806a3..13b7b89bb2 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -218,15 +218,15 @@ extension UpdateController: SPUUpdaterDelegate { func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { Logger.updates.debug("Updater did download update: \(item.displayVersionString)(\(item.versionString))") + PixelKit.fire(DebugEvent(GeneralPixel.updaterDidDownloadUpdate)) + } - guard areAutomaticUpdatesEnabled else { - // If manual are enabled, we don't download - return - } - // Automatic updates present the available update after it's downloaded - updateProgress = .updateFound(item) + func updater(_ updater: SPUUpdater, didExtractUpdate item: SUAppcastItem) { + Logger.updates.debug("Updater did extract update: \(item.displayVersionString)(\(item.versionString))") + } - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidDownloadUpdate)) + func updater(_ updater: SPUUpdater, willInstallUpdate item: SUAppcastItem) { + Logger.updates.debug("Updater will install update: \(item.displayVersionString)(\(item.versionString))") } func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { From 3c1d134158f495061d6a9e85aaf4116bf879081a Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 15:22:50 -0400 Subject: [PATCH 15/58] Refactor updateCycleDone state --- DuckDuckGo/Updates/UpdateUserDriver.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 33db0723f6..f7578909ff 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -115,6 +115,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) delegate?.userDriverUpdateCycleProgress(self, progress: .updateNotFound(item, nsError)) + delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) acknowledgement() } @@ -151,10 +152,11 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { if deferInstallation { onManualInstall = { reply(.install) } - delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) } else { reply(.install) } + + delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) } func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { From 3509d82df0e380afe409dea58b27192285fe60cd Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 15:39:10 -0400 Subject: [PATCH 16/58] Move from delegate method to Combine --- DuckDuckGo/Updates/UpdateController.swift | 31 +++++++++---------- DuckDuckGo/Updates/UpdateUserDriver.swift | 37 +++++++++++------------ 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 13b7b89bb2..e8205784f3 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -115,6 +115,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { private(set) var userDriver: UpdateUserDriver! private let willRelaunchAppSubject = PassthroughSubject() private var internalUserDecider: InternalUserDecider + private var updateProcessCancellable: AnyCancellable! // MARK: - Public @@ -146,8 +147,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { private func configureUpdater() { // The default configuration of Sparkle updates is in Info.plist userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, - deferInstallation: !areAutomaticUpdatesEnabled, - delegate: self) + deferInstallation: !areAutomaticUpdatesEnabled) updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) try? updater.start() @@ -155,6 +155,19 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled } + updateProcessCancellable = userDriver.updateProgressPublisher + .sink { [weak self] progress in + switch progress { + case .updateFound(let item): + self?.latestUpdate = Update(appcastItem: item, isInstalled: false) + case .updateNotFound(let item, _): + self?.latestUpdate = Update(appcastItem: item, isInstalled: true) + default: + break + } + self?.updateProgress = progress + } + #if DEBUG updater.automaticallyChecksForUpdates = false updater.automaticallyDownloadsUpdates = false @@ -175,20 +188,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } -extension UpdateController: UpdateUserDriverDelegate { - func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateCycleProgress) { - switch progress { - case .updateFound(let item): - latestUpdate = Update(appcastItem: item, isInstalled: false) - case .updateNotFound(let item, _): - latestUpdate = Update(appcastItem: item, isInstalled: true) - default: - break - } - updateProgress = progress - } -} - extension UpdateController: SPUUpdaterDelegate { func allowedChannels(for updater: SPUUpdater) -> Set { diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index f7578909ff..addfe280d9 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -20,6 +20,7 @@ import Foundation import Sparkle import PixelKit import BrowserServicesKit +import Combine import os.log #if SPARKLE @@ -48,26 +49,22 @@ enum UpdateCycleProgress { } } -protocol UpdateUserDriverDelegate: AnyObject { - func userDriverUpdateCycleProgress(_ userDriver: UpdateUserDriver, progress: UpdateCycleProgress) -} - final class UpdateUserDriver: NSObject, SPUUserDriver { private var internalUserDecider: InternalUserDecider private var deferInstallation: Bool - private weak var delegate: UpdateUserDriverDelegate? private var bytesToDownload: UInt64 = 0 private var bytesDownloaded: UInt64 = 0 private var onManualInstall: () -> Void = {} + private let subject = CurrentValueSubject(.updateCycleNotStarted) + public lazy var updateProgressPublisher = subject.eraseToAnyPublisher() + init(internalUserDecider: InternalUserDecider, - deferInstallation: Bool, - delegate: UpdateUserDriverDelegate?) { + deferInstallation: Bool) { self.internalUserDecider = internalUserDecider self.deferInstallation = deferInstallation - self.delegate = delegate } func install() { @@ -84,14 +81,14 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) { Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") - delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDidStart) + subject.send(.updateCycleDidStart) } func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - delegate?.userDriverUpdateCycleProgress(self, progress: .updateFound(appcastItem)) + subject.send(.updateFound(appcastItem)) return appcastItem.isInformationOnlyUpdate ? .dismiss : .install } @@ -114,8 +111,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) - delegate?.userDriverUpdateCycleProgress(self, progress: .updateNotFound(item, nsError)) - delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) + subject.send(.updateNotFound(item, nsError)) + subject.send(.updateCycleDone) acknowledgement() } @@ -125,7 +122,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showDownloadInitiated(cancellation: @escaping () -> Void) { - delegate?.userDriverUpdateCycleProgress(self, progress: .downloadDidStart) + subject.send(.downloadDidStart) } func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) { @@ -138,15 +135,15 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { if bytesDownloaded > bytesToDownload { bytesToDownload = bytesDownloaded } - delegate?.userDriverUpdateCycleProgress(self, progress: .downloading(bytesDownloaded, bytesToDownload)) + subject.send(.downloading(bytesDownloaded, bytesToDownload)) } func showDownloadDidStartExtractingUpdate() { - delegate?.userDriverUpdateCycleProgress(self, progress: .extractionDidStart) + subject.send(.extractionDidStart) } func showExtractionReceivedProgress(_ progress: Double) { - delegate?.userDriverUpdateCycleProgress(self, progress: .extracting(progress)) + subject.send(.extracting(progress)) } func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { @@ -156,15 +153,15 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { reply(.install) } - delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) + subject.send(.updateCycleDone) } func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { - delegate?.userDriverUpdateCycleProgress(self, progress: .installationDidStart) + subject.send(.installationDidStart) } func showUpdateInstalledAndRelaunched(_ relaunched: Bool, acknowledgement: @escaping () -> Void) { - delegate?.userDriverUpdateCycleProgress(self, progress: .installing) + subject.send(.installing) acknowledgement() } @@ -173,7 +170,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func dismissUpdateInstallation() { - delegate?.userDriverUpdateCycleProgress(self, progress: .updateCycleDone) + subject.send(.updateCycleDone) } } From d9177823afd19ac34a163d5c1fc76ca536584145 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 16:12:10 -0400 Subject: [PATCH 17/58] Put popup on hold till update cycle is done --- .../NavigationBar/View/MoreOptionsMenu.swift | 6 ++-- DuckDuckGo/Updates/UpdateController.swift | 31 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index 8d05402487..d911a52882 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -311,8 +311,10 @@ final class MoreOptionsMenu: NSMenu { private func addUpdateItem() { #if SPARKLE guard NSApp.runType != .uiTests, - let update = Application.appDelegate.updateController.latestUpdate, - !update.isInstalled + let updateController = Application.appDelegate.updateController, + let update = updateController.latestUpdate, + !update.isInstalled, + updateController.updateProgress.isDone else { return } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index e8205784f3..14dd18ce78 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -70,21 +70,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { @Published private(set) var updateProgress = UpdateCycleProgress.default var updateProgressPublisher: Published.Publisher { $updateProgress } - @Published private(set) var latestUpdate: Update? { - didSet { - if let latestUpdate, !latestUpdate.isInstalled { - switch latestUpdate.type { - case .critical: - notificationPresenter.showUpdateNotification(icon: NSImage.criticalUpdateNotificationInfo, text: UserText.criticalUpdateNotification, presentMultiline: true) - case .regular: - notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) - } - isUpdateAvailableToInstall = !latestUpdate.isInstalled - } else { - isUpdateAvailableToInstall = false - } - } - } + @Published private(set) var latestUpdate: Update? var latestUpdatePublisher: Published.Publisher { $latestUpdate } @@ -166,6 +152,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { break } self?.updateProgress = progress + self?.showUpdateNotificationIfNeeded() } #if DEBUG @@ -177,6 +164,20 @@ final class UpdateController: NSObject, UpdateControllerProtocol { // checkForUpdateInBackground() } + private func showUpdateNotificationIfNeeded() { + if let latestUpdate, !latestUpdate.isInstalled, updateProgress.isDone { + switch latestUpdate.type { + case .critical: + notificationPresenter.showUpdateNotification(icon: NSImage.criticalUpdateNotificationInfo, text: UserText.criticalUpdateNotification, presentMultiline: true) + case .regular: + notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) + } + isUpdateAvailableToInstall = !latestUpdate.isInstalled + } else { + isUpdateAvailableToInstall = false + } + } + @objc func openUpdatesPage() { notificationPresenter.openUpdatesPage() } From 5a987413e4b152e538581a8b360fe073e7e292f5 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 4 Sep 2024 16:30:10 -0400 Subject: [PATCH 18/58] Reconfigure updater whenever setting changes --- DuckDuckGo/Updates/UpdateController.swift | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 14dd18ce78..0bc10d371c 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -64,7 +64,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { self.internalUserDecider = internalUserDecider super.init() - configureUpdater() + try? configureUpdater() } @Published private(set) var updateProgress = UpdateCycleProgress.default @@ -85,15 +85,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { var areAutomaticUpdatesEnabled: Bool { didSet { Logger.updates.debug("areAutomaticUpdatesEnabled: \(self.areAutomaticUpdatesEnabled)") - if updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { - updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled - - // Reinitialize in order to reset the current loaded state - if !areAutomaticUpdatesEnabled { - configureUpdater() - latestUpdate = nil - } - } + try? configureUpdater() } } @@ -130,12 +122,11 @@ final class UpdateController: NSObject, UpdateControllerProtocol { // MARK: - Private - private func configureUpdater() { + private func configureUpdater() throws { // The default configuration of Sparkle updates is in Info.plist userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, deferInstallation: !areAutomaticUpdatesEnabled) updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) - try? updater.start() if updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled @@ -155,6 +146,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { self?.showUpdateNotificationIfNeeded() } + try updater.start() + #if DEBUG updater.automaticallyChecksForUpdates = false updater.automaticallyDownloadsUpdates = false From e0cea620a49631383ff247cd17c5c5a12cf92cf1 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 5 Sep 2024 12:14:11 -0400 Subject: [PATCH 19/58] Refactor updateProgress --- DuckDuckGo/Updates/UpdateController.swift | 28 ++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 0bc10d371c..ef83e5455a 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -67,7 +67,20 @@ final class UpdateController: NSObject, UpdateControllerProtocol { try? configureUpdater() } - @Published private(set) var updateProgress = UpdateCycleProgress.default + @Published private(set) var updateProgress = UpdateCycleProgress.default { + didSet { + switch updateProgress { + case .updateFound(let item): + latestUpdate = Update(appcastItem: item, isInstalled: false) + case .updateNotFound(let item, _): + latestUpdate = Update(appcastItem: item, isInstalled: true) + default: + break + } + showUpdateNotificationIfNeeded() + } + } + var updateProgressPublisher: Published.Publisher { $updateProgress } @Published private(set) var latestUpdate: Update? @@ -133,18 +146,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } updateProcessCancellable = userDriver.updateProgressPublisher - .sink { [weak self] progress in - switch progress { - case .updateFound(let item): - self?.latestUpdate = Update(appcastItem: item, isInstalled: false) - case .updateNotFound(let item, _): - self?.latestUpdate = Update(appcastItem: item, isInstalled: true) - default: - break - } - self?.updateProgress = progress - self?.showUpdateNotificationIfNeeded() - } + .assign(to: \.updateProgress, onWeaklyHeld: self) try updater.start() From 2d8f5690bfe746a461df2a79eef6dee702e6ecd3 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 5 Sep 2024 13:30:18 -0400 Subject: [PATCH 20/58] Minor refactoring --- .../Preferences/Model/AboutPreferences.swift | 2 +- DuckDuckGo/Updates/UpdateController.swift | 32 ++++++++++++------- DuckDuckGo/Updates/UpdateUserDriver.swift | 19 +---------- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index 4111eb4cbc..8d239604c5 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -92,7 +92,7 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { #if SPARKLE func checkForUpdate() { - updateController?.checkForUpdate() + updateController?.checkForUpdateIfNeeded() } func restartToUpdate() { diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index ef83e5455a..e4da809170 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -39,8 +39,7 @@ protocol UpdateControllerProtocol: AnyObject { var lastUpdateCheckDate: Date? { get } - func checkForUpdate() - func checkForUpdateInBackground() + func checkForUpdateIfNeeded() func runUpdate() @@ -121,18 +120,14 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } } - func checkForUpdate() { + func checkForUpdateIfNeeded() { + guard !updater.sessionInProgress else { return } + Logger.updates.debug("Checking for updates") updater.checkForUpdates() } - func checkForUpdateInBackground() { - Logger.updates.debug("Checking for updates in background") - - updater.checkForUpdatesInBackground() - } - // MARK: - Private private func configureUpdater() throws { @@ -155,8 +150,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater.automaticallyDownloadsUpdates = false updater.updateCheckInterval = 0 #endif - -// checkForUpdateInBackground() } private func showUpdateNotificationIfNeeded() { @@ -211,6 +204,23 @@ extension UpdateController: SPUUpdaterDelegate { PixelKit.fire(DebugEvent(GeneralPixel.updaterAborted, error: error)) } + func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) { + Logger.updates.debug("Updater did find valid update: \(item.displayVersionString)(\(item.versionString))") + PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) + updateProgress = .updateFound(item) + } + + func updaterDidNotFindUpdate(_ updater: SPUUpdater, error: any Error) { + let nsError = error as NSError + guard let item = nsError.userInfo["SULatestAppcastItemFound"] as? SUAppcastItem else { return } + + Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") + PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) + + updateProgress = .updateNotFound(item, nsError) + updateProgress = .updateCycleDone + } + func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { Logger.updates.debug("Updater did download update: \(item.displayVersionString)(\(item.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidDownloadUpdate)) diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index addfe280d9..8acfe13285 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -85,12 +85,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { - Logger.updates.debug("Updater did find valid update: \(appcastItem.displayVersionString)(\(appcastItem.versionString))") - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - - subject.send(.updateFound(appcastItem)) - - return appcastItem.isInformationOnlyUpdate ? .dismiss : .install + appcastItem.isInformationOnlyUpdate ? .dismiss : .install } func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { @@ -102,18 +97,6 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showUpdateNotFoundWithError(_ error: any Error, acknowledgement: @escaping () -> Void) { - let nsError = error as NSError - guard let item = nsError.userInfo["SULatestAppcastItemFound"] as? SUAppcastItem else { - acknowledgement() - return - } - - Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) - - subject.send(.updateNotFound(item, nsError)) - subject.send(.updateCycleDone) - acknowledgement() } From 2cf2ae36f90b012f756bf34ab7aba0f214c360b7 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 5 Sep 2024 16:19:19 -0400 Subject: [PATCH 21/58] Reintroduce cached result --- .../View/PreferencesAboutView.swift | 2 +- DuckDuckGo/Updates/UpdateController.swift | 27 ++++++++++++------- DuckDuckGo/Updates/UpdateUserDriver.swift | 5 ++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 23f4ad1a52..017e98d025 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -188,7 +188,7 @@ extension Preferences { Text("- Ready to install") case .installationDidStart, .installing: Text("- Installing") - case .updateCycleNotStarted, .updateCycleDone, .updateFound, .updateNotFound: + case .updateCycleNotStarted, .updateCycleDone: EmptyView() } } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index e4da809170..52c4bf76aa 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -58,6 +58,13 @@ final class UpdateController: NSObject, UpdateControllerProtocol { lazy var notificationPresenter = UpdateNotificationPresenter() let willRelaunchAppPublisher: AnyPublisher + // Struct used to cache data until the updater finishes checking for updates + struct UpdateCheckResult { + let item: SUAppcastItem + let isInstalled: Bool + } + private var updateCheckResult: UpdateCheckResult? + init(internalUserDecider: InternalUserDecider) { willRelaunchAppPublisher = willRelaunchAppSubject.eraseToAnyPublisher() self.internalUserDecider = internalUserDecider @@ -68,15 +75,16 @@ final class UpdateController: NSObject, UpdateControllerProtocol { @Published private(set) var updateProgress = UpdateCycleProgress.default { didSet { - switch updateProgress { - case .updateFound(let item): - latestUpdate = Update(appcastItem: item, isInstalled: false) - case .updateNotFound(let item, _): - latestUpdate = Update(appcastItem: item, isInstalled: true) - default: - break + if let updateCheckResult { + latestUpdate = Update(appcastItem: updateCheckResult.item, isInstalled: updateCheckResult.isInstalled) + } else { + latestUpdate = nil } showUpdateNotificationIfNeeded() + + if updateProgress.isDone { + updateCheckResult = nil + } } } @@ -207,7 +215,7 @@ extension UpdateController: SPUUpdaterDelegate { func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) { Logger.updates.debug("Updater did find valid update: \(item.displayVersionString)(\(item.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - updateProgress = .updateFound(item) + updateCheckResult = UpdateCheckResult(item: item, isInstalled: false) } func updaterDidNotFindUpdate(_ updater: SPUUpdater, error: any Error) { @@ -217,8 +225,7 @@ extension UpdateController: SPUUpdaterDelegate { Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) - updateProgress = .updateNotFound(item, nsError) - updateProgress = .updateCycleDone + updateCheckResult = UpdateCheckResult(item: item, isInstalled: true) } func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 8acfe13285..8aafa34bad 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -28,8 +28,8 @@ import os.log enum UpdateCycleProgress { case updateCycleNotStarted case updateCycleDidStart - case updateFound(SUAppcastItem) - case updateNotFound(SUAppcastItem, NSError) + case updateCycleDone + case downloadDidStart case downloading(UInt64, UInt64) case extractionDidStart @@ -37,7 +37,6 @@ enum UpdateCycleProgress { case readyToInstallAndRelaunch case installationDidStart case installing - case updateCycleDone static var `default` = UpdateCycleProgress.updateCycleNotStarted From b8fd0a47ebfb93493cb0c8799639f121c677e1ff Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 5 Sep 2024 17:18:52 -0400 Subject: [PATCH 22/58] Refactor new version check for UI --- DuckDuckGo/Preferences/View/PreferencesAboutView.swift | 10 +++++++--- DuckDuckGo/Updates/UpdateUserDriver.swift | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 017e98d025..f1a0ac081e 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -139,6 +139,10 @@ extension Preferences { } } + private var isNewVersionAvailable: Bool { + model.updateController?.isUpdateAvailableToInstall == true + } + @ViewBuilder private var versionText: some View { HStack(spacing: 0) { @@ -153,7 +157,7 @@ extension Preferences { case .upToDate: Text(" — " + UserText.upToDate) case .updateCycle(let progress): - if progress.isDone { + if isNewVersionAvailable { Text(" — " + UserText.newerVersionAvailable) } else { text(for: progress) @@ -200,7 +204,7 @@ extension Preferences { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) case .updateCycle(let progress): - if progress.isDone { + if isNewVersionAvailable { Image(systemName: "exclamationmark.circle.fill") .foregroundColor(.red) } else { @@ -239,7 +243,7 @@ extension Preferences { } .buttonStyle(UpdateButtonStyle(enabled: true)) case .updateCycle(let progress): - if progress.isDone { + if isNewVersionAvailable { Button(UserText.restartToUpdate) { model.restartToUpdate() } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 8aafa34bad..7387783ec7 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -29,7 +29,6 @@ enum UpdateCycleProgress { case updateCycleNotStarted case updateCycleDidStart case updateCycleDone - case downloadDidStart case downloading(UInt64, UInt64) case extractionDidStart From b6bcdf526132edab38be7b771f7586f71fe8b495 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 5 Sep 2024 17:37:19 -0400 Subject: [PATCH 23/58] Fix overaggressive updater reconfiguration --- DuckDuckGo/Updates/UpdateController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 52c4bf76aa..34528d902a 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -105,7 +105,9 @@ final class UpdateController: NSObject, UpdateControllerProtocol { var areAutomaticUpdatesEnabled: Bool { didSet { Logger.updates.debug("areAutomaticUpdatesEnabled: \(self.areAutomaticUpdatesEnabled)") - try? configureUpdater() + if oldValue != areAutomaticUpdatesEnabled { + try? configureUpdater() + } } } From 8c27679896c86886952576d703e55c36560db0fd Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 5 Sep 2024 17:37:36 -0400 Subject: [PATCH 24/58] Fix release note tab state --- DuckDuckGo/Preferences/Model/AboutPreferences.swift | 7 ------- DuckDuckGo/Preferences/View/PreferencesAboutView.swift | 2 +- DuckDuckGo/Updates/ReleaseNotesTabExtension.swift | 2 +- DuckDuckGo/Updates/UpdateUserDriver.swift | 7 +++++++ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index 8d239604c5..cc985c7477 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -30,13 +30,6 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { case upToDate case updateCycle(UpdateCycleProgress) - var isLoading: Bool { - switch self { - case .upToDate: return false - case .updateCycle(let progress): return !progress.isDone - } - } - init(from update: Update?, progress: UpdateCycleProgress) { if let update, !update.isInstalled { self = .updateCycle(progress) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index f1a0ac081e..7fd92f5855 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -203,7 +203,7 @@ extension Preferences { case .upToDate: Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) - case .updateCycle(let progress): + case .updateCycle: if isNewVersionAvailable { Image(systemName: "exclamationmark.circle.fill") .foregroundColor(.red) diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index cafbab141e..38346ac6c8 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -125,7 +125,7 @@ extension ReleaseNotesValues { let status: String let latestVersion: String - guard let updateController, updateController.updateProgress.isDone else { + guard let updateController, updateController.updateProgress.isIdle else { self.init(status: "loading", currentVersion: currentVersion, lastUpdate: lastUpdate) diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 7387783ec7..729ff0f6b9 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -45,6 +45,13 @@ enum UpdateCycleProgress { default: return false } } + + var isIdle: Bool { + switch self { + case .updateCycleDone, .updateCycleNotStarted: return true + default: return false + } + } } final class UpdateUserDriver: NSObject, SPUUserDriver { From 72f395b6038547ff9cfebb2df79628a0b274afba Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 6 Sep 2024 10:46:24 -0400 Subject: [PATCH 25/58] Different checkpoints for different update options --- .../View/PreferencesAboutView.swift | 2 +- DuckDuckGo/Updates/UpdateController.swift | 10 ++---- DuckDuckGo/Updates/UpdateUserDriver.swift | 35 +++++++++++++------ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 7fd92f5855..85da9e374a 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -242,7 +242,7 @@ extension Preferences { model.checkForUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) - case .updateCycle(let progress): + case .updateCycle: if isNewVersionAvailable { Button(UserText.restartToUpdate) { model.restartToUpdate() diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 34528d902a..634cefbc59 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -81,10 +81,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { latestUpdate = nil } showUpdateNotificationIfNeeded() - - if updateProgress.isDone { - updateCheckResult = nil - } } } @@ -143,7 +139,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { private func configureUpdater() throws { // The default configuration of Sparkle updates is in Info.plist userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, - deferInstallation: !areAutomaticUpdatesEnabled) + areAutomaticUpdatesEnabled: areAutomaticUpdatesEnabled) updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) if updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { @@ -170,7 +166,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { case .regular: notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) } - isUpdateAvailableToInstall = !latestUpdate.isInstalled + isUpdateAvailableToInstall = true } else { isUpdateAvailableToInstall = false } @@ -182,7 +178,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { @objc func runUpdate() { PixelKit.fire(DebugEvent(GeneralPixel.updaterDidRunUpdate)) - userDriver.install() + userDriver.resume() } } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 729ff0f6b9..db0463b259 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -55,25 +55,29 @@ enum UpdateCycleProgress { } final class UpdateUserDriver: NSObject, SPUUserDriver { + enum Checkpoint: Equatable { + case download + case restart + } + private var internalUserDecider: InternalUserDecider - private var deferInstallation: Bool + private var checkpoint: Checkpoint + private var onResuming: () -> Void = {} private var bytesToDownload: UInt64 = 0 private var bytesDownloaded: UInt64 = 0 - private var onManualInstall: () -> Void = {} - private let subject = CurrentValueSubject(.updateCycleNotStarted) public lazy var updateProgressPublisher = subject.eraseToAnyPublisher() init(internalUserDecider: InternalUserDecider, - deferInstallation: Bool) { + areAutomaticUpdatesEnabled: Bool) { self.internalUserDecider = internalUserDecider - self.deferInstallation = deferInstallation + self.checkpoint = areAutomaticUpdatesEnabled ? .restart : .download } - func install() { - onManualInstall() + func resume() { + onResuming() } func show(_ request: SPUUpdatePermissionRequest) async -> SUUpdatePermissionResponse { @@ -89,8 +93,17 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { subject.send(.updateCycleDidStart) } - func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState) async -> SPUUserUpdateChoice { - appcastItem.isInformationOnlyUpdate ? .dismiss : .install + func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState, reply: @escaping (SPUUserUpdateChoice) -> Void) { + if appcastItem.isInformationOnlyUpdate { + reply(.dismiss) + } + + if checkpoint == .download { + onResuming = { reply(.install) } + subject.send(.updateCycleDone) + } else { + reply(.install) + } } func showUpdateReleaseNotes(with downloadData: SPUDownloadData) { @@ -135,8 +148,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { - if deferInstallation { - onManualInstall = { reply(.install) } + if checkpoint == .restart { + onResuming = { reply(.install) } } else { reply(.install) } From c949cd5844121e8e7cdd39bd2d6fd60a06c3c349 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 6 Sep 2024 10:55:37 -0400 Subject: [PATCH 26/58] Remove unnecessary code --- DuckDuckGo/Updates/UpdateController.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 634cefbc59..df1af97455 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -142,10 +142,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { areAutomaticUpdatesEnabled: areAutomaticUpdatesEnabled) updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) - if updater.automaticallyDownloadsUpdates != areAutomaticUpdatesEnabled { - updater.automaticallyDownloadsUpdates = areAutomaticUpdatesEnabled - } - updateProcessCancellable = userDriver.updateProgressPublisher .assign(to: \.updateProgress, onWeaklyHeld: self) From 926874e6ad2cd6bc1e2e59fedec213d6f36962da Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 9 Sep 2024 15:01:05 -0400 Subject: [PATCH 27/58] Disable update mode switching if session is in progress --- .../View/PreferencesAboutView.swift | 1 + DuckDuckGo/Updates/UpdateController.swift | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 85da9e374a..f1f9b3a140 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -277,6 +277,7 @@ extension Preferences { .pickerStyle(.radioGroup) .offset(x: PreferencesViews.Const.pickerHorizontalOffset) .accessibilityIdentifier("PreferencesAboutView.automaticUpdatesPicker") + .disabled(model.updateController?.canCheckForUpdates == false) .onChange(of: areAutomaticUpdatesEnabled) { newValue in model.areAutomaticUpdatesEnabled = newValue } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index df1af97455..8e138fef9a 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -31,6 +31,8 @@ protocol UpdateControllerProtocol: AnyObject { var latestUpdate: Update? { get } var latestUpdatePublisher: Published.Publisher { get } + var canCheckForUpdates: Bool { get } + var isUpdateAvailableToInstall: Bool { get } var isUpdateAvailableToInstallPublisher: Published.Publisher { get } @@ -94,7 +96,11 @@ final class UpdateController: NSObject, UpdateControllerProtocol { var isUpdateAvailableToInstallPublisher: Published.Publisher { $isUpdateAvailableToInstall } var lastUpdateCheckDate: Date? { - updater.lastUpdateCheckDate + updater?.lastUpdateCheckDate + } + + var canCheckForUpdates: Bool { + updater?.canCheckForUpdates == true } @UserDefaultsWrapper(key: .automaticUpdates, defaultValue: true) @@ -107,8 +113,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } } - private(set) var updater: SPUUpdater! - private(set) var userDriver: UpdateUserDriver! + private(set) var updater: SPUUpdater? + private(set) var userDriver: UpdateUserDriver? private let willRelaunchAppSubject = PassthroughSubject() private var internalUserDecider: InternalUserDecider private var updateProcessCancellable: AnyCancellable! @@ -127,7 +133,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } func checkForUpdateIfNeeded() { - guard !updater.sessionInProgress else { return } + guard let updater, !updater.sessionInProgress else { return } Logger.updates.debug("Checking for updates") @@ -140,18 +146,22 @@ final class UpdateController: NSObject, UpdateControllerProtocol { // The default configuration of Sparkle updates is in Info.plist userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, areAutomaticUpdatesEnabled: areAutomaticUpdatesEnabled) + guard let userDriver else { return } + updater = SPUUpdater(hostBundle: Bundle.main, applicationBundle: Bundle.main, userDriver: userDriver, delegate: self) updateProcessCancellable = userDriver.updateProgressPublisher .assign(to: \.updateProgress, onWeaklyHeld: self) - try updater.start() + try updater?.start() #if DEBUG updater.automaticallyChecksForUpdates = false updater.automaticallyDownloadsUpdates = false updater.updateCheckInterval = 0 #endif + +// checkForUpdateIfNeeded() } private func showUpdateNotificationIfNeeded() { @@ -173,8 +183,10 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } @objc func runUpdate() { - PixelKit.fire(DebugEvent(GeneralPixel.updaterDidRunUpdate)) - userDriver.resume() + if let userDriver { + PixelKit.fire(DebugEvent(GeneralPixel.updaterDidRunUpdate)) + userDriver.resume() + } } } From e5936b75a3449f003dadc4c7742a9c9b59dc0e9a Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 9 Sep 2024 15:19:35 -0400 Subject: [PATCH 28/58] Rename isNewVersionAvailable to hasPendingUpdate and fix edge case where UI stuck loading when switching between browser update modes --- .../View/MoreOptionsMenuButton.swift | 2 +- .../View/PreferencesAboutView.swift | 10 +++---- DuckDuckGo/Updates/UpdateController.swift | 28 ++++++++----------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift index 4e2934e4be..50f52604b0 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift @@ -61,7 +61,7 @@ final class MoreOptionsMenuButton: MouseOverButton { private func subscribeToUpdateInfo() { #if SPARKLE - cancellable = updateController?.isUpdateAvailableToInstallPublisher + cancellable = updateController?.hasPendingUpdatePublisher .receive(on: DispatchQueue.main) .sink { [weak self] isAvailable in self?.isNotificationVisible = isAvailable diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index f1f9b3a140..25966638ae 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -139,8 +139,8 @@ extension Preferences { } } - private var isNewVersionAvailable: Bool { - model.updateController?.isUpdateAvailableToInstall == true + private var hasPendingUpdate: Bool { + model.updateController?.hasPendingUpdate == true } @ViewBuilder @@ -157,7 +157,7 @@ extension Preferences { case .upToDate: Text(" — " + UserText.upToDate) case .updateCycle(let progress): - if isNewVersionAvailable { + if hasPendingUpdate { Text(" — " + UserText.newerVersionAvailable) } else { text(for: progress) @@ -204,7 +204,7 @@ extension Preferences { Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) case .updateCycle: - if isNewVersionAvailable { + if hasPendingUpdate { Image(systemName: "exclamationmark.circle.fill") .foregroundColor(.red) } else { @@ -243,7 +243,7 @@ extension Preferences { } .buttonStyle(UpdateButtonStyle(enabled: true)) case .updateCycle: - if isNewVersionAvailable { + if hasPendingUpdate { Button(UserText.restartToUpdate) { model.restartToUpdate() } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 8e138fef9a..368108ca58 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -33,8 +33,8 @@ protocol UpdateControllerProtocol: AnyObject { var canCheckForUpdates: Bool { get } - var isUpdateAvailableToInstall: Bool { get } - var isUpdateAvailableToInstallPublisher: Published.Publisher { get } + var hasPendingUpdate: Bool { get } + var hasPendingUpdatePublisher: Published.Publisher { get } var updateProgress: UpdateCycleProgress { get } var updateProgressPublisher: Published.Publisher { get } @@ -79,8 +79,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { didSet { if let updateCheckResult { latestUpdate = Update(appcastItem: updateCheckResult.item, isInstalled: updateCheckResult.isInstalled) - } else { - latestUpdate = nil + hasPendingUpdate = latestUpdate?.isInstalled == false } showUpdateNotificationIfNeeded() } @@ -92,8 +91,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { var latestUpdatePublisher: Published.Publisher { $latestUpdate } - @Published private(set) var isUpdateAvailableToInstall = false - var isUpdateAvailableToInstallPublisher: Published.Publisher { $isUpdateAvailableToInstall } + @Published private(set) var hasPendingUpdate = false + var hasPendingUpdatePublisher: Published.Publisher { $hasPendingUpdate } var lastUpdateCheckDate: Date? { updater?.lastUpdateCheckDate @@ -165,16 +164,13 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } private func showUpdateNotificationIfNeeded() { - if let latestUpdate, !latestUpdate.isInstalled, updateProgress.isDone { - switch latestUpdate.type { - case .critical: - notificationPresenter.showUpdateNotification(icon: NSImage.criticalUpdateNotificationInfo, text: UserText.criticalUpdateNotification, presentMultiline: true) - case .regular: - notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) - } - isUpdateAvailableToInstall = true - } else { - isUpdateAvailableToInstall = false + guard let latestUpdate, hasPendingUpdate, updateProgress.isIdle else { return } + + switch latestUpdate.type { + case .critical: + notificationPresenter.showUpdateNotification(icon: NSImage.criticalUpdateNotificationInfo, text: UserText.criticalUpdateNotification, presentMultiline: true) + case .regular: + notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) } } From aac36c08c423180a1f6f0592a530bbac821c31b6 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 9 Sep 2024 15:50:06 -0400 Subject: [PATCH 29/58] Fix stuck hasPendingStatus --- DuckDuckGo/Updates/UpdateController.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 368108ca58..d6093c2a3e 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -79,7 +79,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { didSet { if let updateCheckResult { latestUpdate = Update(appcastItem: updateCheckResult.item, isInstalled: updateCheckResult.isInstalled) - hasPendingUpdate = latestUpdate?.isInstalled == false + hasPendingUpdate = latestUpdate?.isInstalled == false && updateProgress.isIdle } showUpdateNotificationIfNeeded() } @@ -142,6 +142,10 @@ final class UpdateController: NSObject, UpdateControllerProtocol { // MARK: - Private private func configureUpdater() throws { + // Workaround to restart the updater state + updateCheckResult = nil + latestUpdate = nil + // The default configuration of Sparkle updates is in Info.plist userDriver = UpdateUserDriver(internalUserDecider: internalUserDecider, areAutomaticUpdatesEnabled: areAutomaticUpdatesEnabled) @@ -164,7 +168,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } private func showUpdateNotificationIfNeeded() { - guard let latestUpdate, hasPendingUpdate, updateProgress.isIdle else { return } + guard let latestUpdate, hasPendingUpdate else { return } switch latestUpdate.type { case .critical: From 5fda6dc27088c3fe66983a644e9a6a555b903ec6 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 9 Sep 2024 16:47:39 -0400 Subject: [PATCH 30/58] Check for update upon starting the app --- DuckDuckGo/Updates/UpdateController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index d6093c2a3e..64af31414d 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -164,7 +164,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater.updateCheckInterval = 0 #endif -// checkForUpdateIfNeeded() + checkForUpdateIfNeeded() } private func showUpdateNotificationIfNeeded() { From 855e2d8e7b61badc8a892a4b6d3bce88737f7a36 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 9 Sep 2024 17:16:08 -0400 Subject: [PATCH 31/58] Simplify user driver --- DuckDuckGo/Updates/UpdateUserDriver.swift | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index db0463b259..2af0573291 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -67,8 +67,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { private var bytesToDownload: UInt64 = 0 private var bytesDownloaded: UInt64 = 0 - private let subject = CurrentValueSubject(.updateCycleNotStarted) - public lazy var updateProgressPublisher = subject.eraseToAnyPublisher() + @Published var updateProgress = UpdateCycleProgress.default + public var updateProgressPublisher: Published.Publisher { $updateProgress } init(internalUserDecider: InternalUserDecider, areAutomaticUpdatesEnabled: Bool) { @@ -90,7 +90,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) { Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") - subject.send(.updateCycleDidStart) + updateProgress = .updateCycleDidStart } func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState, reply: @escaping (SPUUserUpdateChoice) -> Void) { @@ -100,7 +100,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { if checkpoint == .download { onResuming = { reply(.install) } - subject.send(.updateCycleDone) + updateProgress = .updateCycleDone } else { reply(.install) } @@ -123,7 +123,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showDownloadInitiated(cancellation: @escaping () -> Void) { - subject.send(.downloadDidStart) + updateProgress = .downloadDidStart } func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) { @@ -136,15 +136,15 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { if bytesDownloaded > bytesToDownload { bytesToDownload = bytesDownloaded } - subject.send(.downloading(bytesDownloaded, bytesToDownload)) + updateProgress = .downloading(bytesDownloaded, bytesToDownload) } func showDownloadDidStartExtractingUpdate() { - subject.send(.extractionDidStart) + updateProgress = .extractionDidStart } func showExtractionReceivedProgress(_ progress: Double) { - subject.send(.extracting(progress)) + updateProgress = .extracting(progress) } func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { @@ -154,15 +154,15 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { reply(.install) } - subject.send(.updateCycleDone) + updateProgress = .updateCycleDone } func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) { - subject.send(.installationDidStart) + updateProgress = .installationDidStart } func showUpdateInstalledAndRelaunched(_ relaunched: Bool, acknowledgement: @escaping () -> Void) { - subject.send(.installing) + updateProgress = .installing acknowledgement() } @@ -171,7 +171,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func dismissUpdateInstallation() { - subject.send(.updateCycleDone) + updateProgress = .updateCycleDone } } From a0dbbad9a44043bb7a02c9914a26ff1fda7f3beb Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 9 Sep 2024 22:11:20 -0400 Subject: [PATCH 32/58] Dot goes away when menu opens --- .../Common/Utilities/UserDefaultsWrapper.swift | 1 + .../NavigationBar/View/MoreOptionsMenu.swift | 10 +++++++++- .../NavigationBar/View/MoreOptionsMenuButton.swift | 7 ++++--- DuckDuckGo/Updates/UpdateController.swift | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift b/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift index 508b4ff1fd..a18850b028 100644 --- a/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift +++ b/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift @@ -184,6 +184,7 @@ public struct UserDefaultsWrapper { // Updates case automaticUpdates = "updates.automatic" + case pendingUpdateShown = "pending.update.shown" // Experiments case pixelExperimentInstalled = "pixel.experiment.installed" diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index d911a52882..c8de736b48 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -49,7 +49,7 @@ protocol OptionsButtonMenuDelegate: AnyObject { func optionsButtonMenuRequestedIdentityTheftRestoration(_ menu: NSMenu) } -final class MoreOptionsMenu: NSMenu { +final class MoreOptionsMenu: NSMenu, NSMenuDelegate { weak var actionDelegate: OptionsButtonMenuDelegate? @@ -117,6 +117,8 @@ final class MoreOptionsMenu: NSMenu { self.sharingMenu = sharingMenu } self.emailManager.requestDelegate = self + + delegate = self setupMenuItems() } @@ -480,6 +482,12 @@ final class MoreOptionsMenu: NSMenu { return networkProtectionItem } + func menuWillOpen(_ menu: NSMenu) { + guard let updateController = Application.appDelegate.updateController else { return } + if updateController.hasPendingUpdate && updateController.needsNotificationDot { + updateController.needsNotificationDot = false + } + } } final class EmailOptionsButtonSubMenu: NSMenu { diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift index 50f52604b0..dc4aa3f45d 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift @@ -61,10 +61,11 @@ final class MoreOptionsMenuButton: MouseOverButton { private func subscribeToUpdateInfo() { #if SPARKLE - cancellable = updateController?.hasPendingUpdatePublisher + guard let updateController else { return } + cancellable = Publishers.CombineLatest(updateController.hasPendingUpdatePublisher, updateController.notificationDotPublisher) .receive(on: DispatchQueue.main) - .sink { [weak self] isAvailable in - self?.isNotificationVisible = isAvailable + .sink { [weak self] hasPendingUpdate, needsNotificationDot in + self?.isNotificationVisible = hasPendingUpdate && needsNotificationDot } #endif } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 64af31414d..17c797fa39 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -36,6 +36,9 @@ protocol UpdateControllerProtocol: AnyObject { var hasPendingUpdate: Bool { get } var hasPendingUpdatePublisher: Published.Publisher { get } + var needsNotificationDot: Bool { get } + var notificationDotPublisher: AnyPublisher { get } + var updateProgress: UpdateCycleProgress { get } var updateProgressPublisher: Published.Publisher { get } @@ -80,6 +83,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { if let updateCheckResult { latestUpdate = Update(appcastItem: updateCheckResult.item, isInstalled: updateCheckResult.isInstalled) hasPendingUpdate = latestUpdate?.isInstalled == false && updateProgress.isIdle + needsNotificationDot = hasPendingUpdate } showUpdateNotificationIfNeeded() } @@ -112,6 +116,16 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } } + @UserDefaultsWrapper(key: .pendingUpdateShown, defaultValue: false) + var needsNotificationDot: Bool { + didSet { + notificationDotSubject.send(needsNotificationDot) + } + } + + private let notificationDotSubject = CurrentValueSubject(false) + lazy var notificationDotPublisher = notificationDotSubject.eraseToAnyPublisher() + private(set) var updater: SPUUpdater? private(set) var userDriver: UpdateUserDriver? private let willRelaunchAppSubject = PassthroughSubject() From 3b2342334f45be205466c6469667c2455e1c5b74 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 10 Sep 2024 10:51:52 -0400 Subject: [PATCH 33/58] Add setNeedsDisplay --- DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift index dc4aa3f45d..e3a0f4ab86 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift @@ -40,6 +40,7 @@ final class MoreOptionsMenuButton: MouseOverButton { var isNotificationVisible: Bool = false { didSet { updateNotificationVisibility() + needsDisplay = isNotificationVisible != oldValue } } From 5c50aa10d95e259279e8750588319cfae100edcb Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 10 Sep 2024 12:17:07 -0400 Subject: [PATCH 34/58] Add shouldShowUpdateNotification logic --- .../Preferences/View/PreferencesAboutView.swift | 1 - DuckDuckGo/Updates/UpdateController.swift | 15 +++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 25966638ae..3d9edd8083 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -277,7 +277,6 @@ extension Preferences { .pickerStyle(.radioGroup) .offset(x: PreferencesViews.Const.pickerHorizontalOffset) .accessibilityIdentifier("PreferencesAboutView.automaticUpdatesPicker") - .disabled(model.updateController?.canCheckForUpdates == false) .onChange(of: areAutomaticUpdatesEnabled) { newValue in model.areAutomaticUpdatesEnabled = newValue } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 17c797fa39..a014302448 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -31,18 +31,17 @@ protocol UpdateControllerProtocol: AnyObject { var latestUpdate: Update? { get } var latestUpdatePublisher: Published.Publisher { get } - var canCheckForUpdates: Bool { get } - var hasPendingUpdate: Bool { get } var hasPendingUpdatePublisher: Published.Publisher { get } - var needsNotificationDot: Bool { get } + var needsNotificationDot: Bool { get set } var notificationDotPublisher: AnyPublisher { get } var updateProgress: UpdateCycleProgress { get } var updateProgressPublisher: Published.Publisher { get } var lastUpdateCheckDate: Date? { get } + var lastUpdateNotificationShownDate: Date { get } func checkForUpdateIfNeeded() @@ -102,8 +101,10 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater?.lastUpdateCheckDate } - var canCheckForUpdates: Bool { - updater?.canCheckForUpdates == true + var lastUpdateNotificationShownDate: Date = .distantPast + + private var shouldShowUpdateNotification: Bool { + Date().timeIntervalSince(lastUpdateNotificationShownDate) > .days(7) } @UserDefaultsWrapper(key: .automaticUpdates, defaultValue: true) @@ -182,7 +183,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } private func showUpdateNotificationIfNeeded() { - guard let latestUpdate, hasPendingUpdate else { return } + guard let latestUpdate, hasPendingUpdate, shouldShowUpdateNotification else { return } switch latestUpdate.type { case .critical: @@ -190,6 +191,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { case .regular: notificationPresenter.showUpdateNotification(icon: NSImage.updateNotificationInfo, text: UserText.updateAvailableNotification, presentMultiline: true) } + + lastUpdateNotificationShownDate = Date() } @objc func openUpdatesPage() { From b8dee18cfb702229ba4ed828e19d04b04ce3bff9 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 10 Sep 2024 18:27:42 -0400 Subject: [PATCH 35/58] Check for update when release note / about page is loaded and when it's due based on SULastCheckTime --- DuckDuckGo/Preferences/View/PreferencesAboutView.swift | 3 +++ DuckDuckGo/Updates/ReleaseNotesTabExtension.swift | 6 ++++++ DuckDuckGo/Updates/UpdateController.swift | 2 -- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 3d9edd8083..82e1158a02 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -51,6 +51,9 @@ extension Preferences { } } } + .onAppear { + model.checkForUpdate() + } } } diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index 38346ac6c8..8b0a304ddb 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -93,6 +93,12 @@ final class ReleaseNotesTabExtension: NavigationResponder { .store(in: &cancellables) } + @MainActor + func navigationDidFinish(_ navigation: Navigation) { + guard NSApp.runType != .uiTests, navigation.url.isReleaseNotesScheme else { return } + let updateController = Application.appDelegate.updateController! + updateController.checkForUpdateIfNeeded() + } } protocol ReleaseNotesTabExtensionProtocol: AnyObject, NavigationResponder {} diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index a014302448..8e9ccd531b 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -178,8 +178,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater.automaticallyDownloadsUpdates = false updater.updateCheckInterval = 0 #endif - - checkForUpdateIfNeeded() } private func showUpdateNotificationIfNeeded() { From b1a5a130e07090eafa1e03e58e1bc4e20b799da4 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 11 Sep 2024 12:29:02 -0400 Subject: [PATCH 36/58] Fix stuck state when switching between modes --- DuckDuckGo/Updates/UpdateController.swift | 20 +++++++++----------- DuckDuckGo/Updates/UpdateUserDriver.swift | 9 +++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 8e9ccd531b..b2242392b3 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -67,7 +67,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { let item: SUAppcastItem let isInstalled: Bool } - private var updateCheckResult: UpdateCheckResult? + private var cachedUpdateResult: UpdateCheckResult? init(internalUserDecider: InternalUserDecider) { willRelaunchAppPublisher = willRelaunchAppSubject.eraseToAnyPublisher() @@ -79,8 +79,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { @Published private(set) var updateProgress = UpdateCycleProgress.default { didSet { - if let updateCheckResult { - latestUpdate = Update(appcastItem: updateCheckResult.item, isInstalled: updateCheckResult.isInstalled) + if let cachedUpdateResult { + latestUpdate = Update(appcastItem: cachedUpdateResult.item, isInstalled: cachedUpdateResult.isInstalled) hasPendingUpdate = latestUpdate?.isInstalled == false && updateProgress.isIdle needsNotificationDot = hasPendingUpdate } @@ -97,10 +97,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { @Published private(set) var hasPendingUpdate = false var hasPendingUpdatePublisher: Published.Publisher { $hasPendingUpdate } - var lastUpdateCheckDate: Date? { - updater?.lastUpdateCheckDate - } - + var lastUpdateCheckDate: Date? { updater?.lastUpdateCheckDate } var lastUpdateNotificationShownDate: Date = .distantPast private var shouldShowUpdateNotification: Bool { @@ -112,6 +109,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { didSet { Logger.updates.debug("areAutomaticUpdatesEnabled: \(self.areAutomaticUpdatesEnabled)") if oldValue != areAutomaticUpdatesEnabled { + userDriver?.cancelAndDismissCurrentUpdate() try? configureUpdater() } } @@ -157,8 +155,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { // MARK: - Private private func configureUpdater() throws { - // Workaround to restart the updater state - updateCheckResult = nil + // Workaround to reset the updater state + cachedUpdateResult = nil latestUpdate = nil // The default configuration of Sparkle updates is in Info.plist @@ -236,7 +234,7 @@ extension UpdateController: SPUUpdaterDelegate { func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) { Logger.updates.debug("Updater did find valid update: \(item.displayVersionString)(\(item.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) - updateCheckResult = UpdateCheckResult(item: item, isInstalled: false) + cachedUpdateResult = UpdateCheckResult(item: item, isInstalled: false) } func updaterDidNotFindUpdate(_ updater: SPUUpdater, error: any Error) { @@ -246,7 +244,7 @@ extension UpdateController: SPUUpdaterDelegate { Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) - updateCheckResult = UpdateCheckResult(item: item, isInstalled: true) + cachedUpdateResult = UpdateCheckResult(item: item, isInstalled: true) } func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 2af0573291..0c7fbca5e8 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -63,6 +63,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { private var internalUserDecider: InternalUserDecider private var checkpoint: Checkpoint private var onResuming: () -> Void = {} + private var onSkipping: () -> Void = {} private var bytesToDownload: UInt64 = 0 private var bytesDownloaded: UInt64 = 0 @@ -80,6 +81,10 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { onResuming() } + func cancelAndDismissCurrentUpdate() { + onSkipping() + } + func show(_ request: SPUUpdatePermissionRequest) async -> SUUpdatePermissionResponse { #if DEBUG .init(automaticUpdateChecks: false, sendSystemProfile: false) @@ -98,6 +103,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { reply(.dismiss) } + onSkipping = { reply(.skip) } + if checkpoint == .download { onResuming = { reply(.install) } updateProgress = .updateCycleDone @@ -148,6 +155,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) { + onSkipping = { reply(.skip) } + if checkpoint == .restart { onResuming = { reply(.install) } } else { From e2ec0fdf12397bf697ba71ae4a3b6d143d35888f Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 11 Sep 2024 12:36:41 -0400 Subject: [PATCH 37/58] Add DEBUG check --- DuckDuckGo/Preferences/View/PreferencesAboutView.swift | 2 ++ DuckDuckGo/Updates/ReleaseNotesTabExtension.swift | 2 ++ DuckDuckGo/Updates/UpdateUserDriver.swift | 2 ++ 3 files changed, 6 insertions(+) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 82e1158a02..0d3886a3f2 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -52,7 +52,9 @@ extension Preferences { } } .onAppear { +#if SPARKLE && !DEBUG model.checkForUpdate() +#endif } } } diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index 8b0a304ddb..41c88ea007 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -95,9 +95,11 @@ final class ReleaseNotesTabExtension: NavigationResponder { @MainActor func navigationDidFinish(_ navigation: Navigation) { +#if !DEBUG guard NSApp.runType != .uiTests, navigation.url.isReleaseNotesScheme else { return } let updateController = Application.appDelegate.updateController! updateController.checkForUpdateIfNeeded() +#endif } } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 0c7fbca5e8..7d43108b4c 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -61,8 +61,10 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } private var internalUserDecider: InternalUserDecider + private var checkpoint: Checkpoint private var onResuming: () -> Void = {} + private var onSkipping: () -> Void = {} private var bytesToDownload: UInt64 = 0 From 63d03b3e00199cfb848d23d36a392ed8a5d14772 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 12 Sep 2024 12:03:08 -0400 Subject: [PATCH 38/58] Avoid overlapping popup --- DuckDuckGo/Updates/UpdateController.swift | 2 -- DuckDuckGo/Updates/UpdateNotificationPresenter.swift | 9 +++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index b2242392b3..2d482e9a28 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -41,10 +41,8 @@ protocol UpdateControllerProtocol: AnyObject { var updateProgressPublisher: Published.Publisher { get } var lastUpdateCheckDate: Date? { get } - var lastUpdateNotificationShownDate: Date { get } func checkForUpdateIfNeeded() - func runUpdate() var areAutomaticUpdatesEnabled: Bool { get set } diff --git a/DuckDuckGo/Updates/UpdateNotificationPresenter.swift b/DuckDuckGo/Updates/UpdateNotificationPresenter.swift index 5c68f0c64b..310abddd98 100644 --- a/DuckDuckGo/Updates/UpdateNotificationPresenter.swift +++ b/DuckDuckGo/Updates/UpdateNotificationPresenter.swift @@ -34,6 +34,12 @@ final class UpdateNotificationPresenter { return } + let parentViewController = windowController.mainViewController + + guard parentViewController.view.window?.isKeyWindow == true, (parentViewController.presentedViewControllers ?? []).isEmpty else { + return + } + let buttonAction: (() -> Void)? = { [weak self] in self?.openUpdatesPage() } @@ -49,8 +55,7 @@ final class UpdateNotificationPresenter { self?.openUpdatesPage() }) - viewController.show(onParent: windowController.mainViewController, - relativeTo: button) + viewController.show(onParent: parentViewController, relativeTo: button) } } From 1e470607ac5a38fea32863778c019c9e2981fda4 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 12 Sep 2024 15:52:09 -0400 Subject: [PATCH 39/58] Code cleanup --- DuckDuckGo/Localizable.xcstrings | 59 +++++++++---------- .../View/MoreOptionsMenuButton.swift | 6 +- .../View/PreferencesAboutView.swift | 16 ++--- DuckDuckGo/Updates/UpdateController.swift | 16 ++--- 4 files changed, 47 insertions(+), 50 deletions(-) diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index 24480c646e..df5f006cfd 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -52,6 +52,34 @@ } } } + }, + " — Checking for update" : { + + }, + " — Downloading %@ / %@" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : " — Downloading %1$@ / %2$@" + } + } + } + }, + " — Downloading update" : { + + }, + " — Extracting update" : { + + }, + " — Extracting update (%@%%)" : { + + }, + " — Installing" : { + + }, + " — Ready to install" : { + }, " %@" : { "localizations" : { @@ -104,37 +132,6 @@ } } } - }, - "- Checking for update" : { - - }, - "- Downloading %@ / %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "- Downloading %1$@ / %2$@" - } - } - } - }, - "- Downloading update" : { - - }, - "- Extracting update" : { - - }, - "- Extracting update (%@%%)" : { - - }, - "- Extracting update (%lf%%)" : { - - }, - "- Installing" : { - - }, - "- Ready to install" : { - }, "**%lld** tracking attempts blocked" : { "comment" : "The number of tracking attempts blocked in the last 7 days, shown on a new tab, translate as: Tracking attempts blocked: %@", diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift index e3a0f4ab86..506fe96577 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift @@ -40,7 +40,9 @@ final class MoreOptionsMenuButton: MouseOverButton { var isNotificationVisible: Bool = false { didSet { updateNotificationVisibility() +#if SPARKLE needsDisplay = isNotificationVisible != oldValue +#endif } } @@ -51,8 +53,8 @@ final class MoreOptionsMenuButton: MouseOverButton { if NSApp.runType != .uiTests { updateController = Application.appDelegate.updateController } -#endif subscribeToUpdateInfo() +#endif } override func updateLayer() { @@ -61,14 +63,12 @@ final class MoreOptionsMenuButton: MouseOverButton { } private func subscribeToUpdateInfo() { -#if SPARKLE guard let updateController else { return } cancellable = Publishers.CombineLatest(updateController.hasPendingUpdatePublisher, updateController.notificationDotPublisher) .receive(on: DispatchQueue.main) .sink { [weak self] hasPendingUpdate, needsNotificationDot in self?.isNotificationVisible = hasPendingUpdate && needsNotificationDot } -#endif } private func setupNotificationLayerIfNeeded() { diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 0d3886a3f2..cc15c10416 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -184,19 +184,19 @@ extension Preferences { private func text(for progress: UpdateCycleProgress) -> some View { switch progress { case .updateCycleDidStart: - Text("- Checking for update") + Text(" — " + UserText.checkingForUpdate) case .downloadDidStart: - Text("- Downloading update") + Text(" — Downloading update") case .downloading(let bytesDownloaded, let bytesToDownload): - Text("- Downloading \(formatter.string(fromByteCount: Int64(bytesDownloaded))) / \(formatter.string(fromByteCount: Int64(bytesToDownload)))") + Text(" — Downloading \(formatter.string(fromByteCount: Int64(bytesDownloaded))) / \(formatter.string(fromByteCount: Int64(bytesToDownload)))") case .extractionDidStart: - Text("- Extracting update") + Text(" — Extracting update") case .extracting(let percentage): - Text("- Extracting update (\(String(format: "%.1f", percentage * 100))%)") + Text(" — Extracting update (\(String(format: "%.1f", percentage * 100))%)") case .readyToInstallAndRelaunch: - Text("- Ready to install") + Text(" — Ready to install") case .installationDidStart, .installing: - Text("- Installing") + Text(" — Installing") case .updateCycleNotStarted, .updateCycleDone: EmptyView() } @@ -220,7 +220,7 @@ extension Preferences { } private var lastCheckedText: some View { - let lastChecked = lastCheckedFormattedDate(model.lastUpdateCheckDate) + let lastChecked = model.updateController?.updateProgress.isDone == true ? lastCheckedFormattedDate(model.lastUpdateCheckDate) : "-" return Text("\(UserText.lastChecked): \(lastChecked)") .foregroundColor(.secondary) } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 2d482e9a28..9acf7b4986 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -67,14 +67,6 @@ final class UpdateController: NSObject, UpdateControllerProtocol { } private var cachedUpdateResult: UpdateCheckResult? - init(internalUserDecider: InternalUserDecider) { - willRelaunchAppPublisher = willRelaunchAppSubject.eraseToAnyPublisher() - self.internalUserDecider = internalUserDecider - super.init() - - try? configureUpdater() - } - @Published private(set) var updateProgress = UpdateCycleProgress.default { didSet { if let cachedUpdateResult { @@ -131,6 +123,14 @@ final class UpdateController: NSObject, UpdateControllerProtocol { // MARK: - Public + init(internalUserDecider: InternalUserDecider) { + willRelaunchAppPublisher = willRelaunchAppSubject.eraseToAnyPublisher() + self.internalUserDecider = internalUserDecider + super.init() + + try? configureUpdater() + } + func checkNewApplicationVersion() { let updateStatus = ApplicationUpdateDetector.isApplicationUpdated() switch updateStatus { From b80311466de42430dda7aa81ee23ca1beabebf75 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 13 Sep 2024 10:34:02 -0400 Subject: [PATCH 40/58] Update copy --- DuckDuckGo/Common/Localizables/UserText.swift | 6 +- DuckDuckGo/Localizable.xcstrings | 59 +++++++++---------- .../Preferences/Model/AboutPreferences.swift | 2 +- .../View/PreferencesAboutView.swift | 4 +- .../Updates/ReleaseNotesUserScript.swift | 3 - 5 files changed, 34 insertions(+), 40 deletions(-) diff --git a/DuckDuckGo/Common/Localizables/UserText.swift b/DuckDuckGo/Common/Localizables/UserText.swift index ac7f6cd5b0..8531c36676 100644 --- a/DuckDuckGo/Common/Localizables/UserText.swift +++ b/DuckDuckGo/Common/Localizables/UserText.swift @@ -1237,7 +1237,7 @@ struct UserText { static let downloadsOpenDownloadsFolder = NSLocalizedString("downloads.open-downloads-folder", value: "Open Downloads Folder", comment: "Button in the downloads manager that allows the user to open the downloads folder") // MARK: Updates - static let updateAvailableMenuItem = NSLocalizedString("update.available.menu.item", value: "Update Available - Restart Now", comment: "Title of the menu item that informs user that a new update is available. Clicking on the menu item restarts the app and installs the update") + static let updateAvailableMenuItem = NSLocalizedString("update.available.menu.item", value: "Update Available - Install Now", comment: "Title of the menu item that informs user that a new update is available. Clicking on the menu item installs the update") static let releaseNotesMenuItem = NSLocalizedString("release.notes.menu.item", value: "Release Notes", comment: "Title of the dialog menu item that opens release notes") static let whatsNewMenuItem = NSLocalizedString("whats.new.menu.item", value: "What's New", comment: "Title of the dialog menu item that opens the 'What's New' page") static let browserUpdatesTitle = NSLocalizedString("settings.browser.updates.title", value: "Browser Updates", comment: "Title of the section in Settings where people set up automatic vs manual updates") @@ -1247,11 +1247,11 @@ struct UserText { static let upToDate = NSLocalizedString("settings.up.to.date", value: "DuckDuckGo is up to date", comment: "Label informing users the app is currently up to date and no update is required.") static let newerVersionAvailable = NSLocalizedString("settings.newer.version.available", value: "Newer version available", comment: "Label informing users the newer version of the app is available to install.") static let lastChecked = NSLocalizedString("settings.last.checked", value: "Last checked", comment: "Label informing users what is the last time the app checked for the update.") - static let restartToUpdate = NSLocalizedString("settings.restart.to.update", value: "Restart to Update", comment: "Button label trigering restart and update of the application.") + static let runUpdate = NSLocalizedString("settings.restart.to.update", value: "Update DuckDuckGo", comment: "Button label trigering restart and update of the application.") static let browserUpdatedNotification = NSLocalizedString("notification.browser.updated", value: "Browser Updated", comment: "Notification informing user the app has been updated") static let browserDowngradedNotification = NSLocalizedString("notification.browser.downgraded", value: "Browser Downgraded", comment: "Notification informing user the app has been downgraded") static let criticalUpdateNotification = NSLocalizedString("notification.critical.update", value: "Critical update required. Restart to update.", comment: "Notification informing user a critical update is required.") - static let updateAvailableNotification = NSLocalizedString("notification.update.available", value: "New version available. Restart to update.", comment: "Notification informing user the a version of app is available") + static let updateAvailableNotification = NSLocalizedString("notification.update.available", value: "New version available. Click here to update.", comment: "Notification informing user the a version of app is available") static let viewDetails = NSLocalizedString("view.details.button", value: "View Details", comment: "Button title to open more details about the update") enum Bookmarks { diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index df5f006cfd..da0efc9de2 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -52,9 +52,6 @@ } } } - }, - " — Checking for update" : { - }, " — Downloading %@ / %@" : { "localizations" : { @@ -38218,55 +38215,55 @@ "localizations" : { "de" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Neue Version verfügbar. Zum Aktualisieren neu starten." } }, "en" : { "stringUnit" : { "state" : "new", - "value" : "New version available. Restart to update." + "value" : "New version available. Click here to update." } }, "es" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Nueva versión disponible. Reinicia para actualizar." } }, "fr" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Une nouvelle version est disponible. Redémarrer pour mettre à jour." } }, "it" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Nuova versione disponibile. Riavvia per aggiornare." } }, "nl" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Nieuwe versie beschikbaar. Start opnieuw om bij te werken." } }, "pl" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Dostępna nowa wersja. Uruchom ponownie, aby zaktualizować." } }, "pt" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Nova versão disponível. Reiniciar para atualizar." } }, "ru" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Доступна новая версия. Перезапустите приложение." } } @@ -56958,55 +56955,55 @@ "localizations" : { "de" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Zum Aktualisieren neu starten" } }, "en" : { "stringUnit" : { "state" : "new", - "value" : "Restart to Update" + "value" : "Update DuckDuckGo" } }, "es" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Reiniciar para actualizar" } }, "fr" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Redémarrer pour mettre à jour" } }, "it" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Riavvia per aggiornare" } }, "nl" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Start opnieuw om bij te werken" } }, "pl" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Uruchom ponownie, aby zaktualizować" } }, "pt" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Reiniciar para atualizar" } }, "ru" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Перезапустите для обновления" } } @@ -61666,60 +61663,60 @@ } }, "update.available.menu.item" : { - "comment" : "Title of the menu item that informs user that a new update is available. Clicking on the menu item restarts the app and installs the update", + "comment" : "Title of the menu item that informs user that a new update is available. Clicking on the menu item installs the update", "extractionState" : "extracted_with_value", "localizations" : { "de" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Update verfügbar – Jetzt neu starten" } }, "en" : { "stringUnit" : { "state" : "new", - "value" : "Update Available - Restart Now" + "value" : "Update Available - Install Now" } }, "es" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Actualización disponible - Reiniciar ahora" } }, "fr" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Mise à jour disponible : redémarrer" } }, "it" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Aggiornamento disponibile - Riavvia ora" } }, "nl" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Update beschikbaar - nu opnieuw opstarten" } }, "pl" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Dostępna aktualizacja — uruchom ponownie teraz" } }, "pt" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Atualização disponível – Reiniciar agora" } }, "ru" : { "stringUnit" : { - "state" : "translated", + "state" : "needs_review", "value" : "Доступно обновление: перезапустить" } } diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index cc985c7477..b561d547ac 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -88,7 +88,7 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { updateController?.checkForUpdateIfNeeded() } - func restartToUpdate() { + func runUpdate() { updateController?.runUpdate() } diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index cc15c10416..4698580bd5 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -249,8 +249,8 @@ extension Preferences { .buttonStyle(UpdateButtonStyle(enabled: true)) case .updateCycle: if hasPendingUpdate { - Button(UserText.restartToUpdate) { - model.restartToUpdate() + Button(UserText.runUpdate) { + model.runUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) } else { diff --git a/DuckDuckGo/Updates/ReleaseNotesUserScript.swift b/DuckDuckGo/Updates/ReleaseNotesUserScript.swift index 199d0da2d0..7306d573c5 100644 --- a/DuckDuckGo/Updates/ReleaseNotesUserScript.swift +++ b/DuckDuckGo/Updates/ReleaseNotesUserScript.swift @@ -124,9 +124,6 @@ extension ReleaseNotesUserScript { } private func browserRestart(params: Any, original: WKScriptMessage) async throws -> Encodable? { - DispatchQueue.main.async { [weak self] in - self?.updateController.runUpdate() - } return nil } From 85bac5fd284bdcc4491a75bbe6cc9ee681f37ae5 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 13 Sep 2024 10:44:35 -0400 Subject: [PATCH 41/58] Fix last checked date showing - when idling --- DuckDuckGo/Preferences/View/PreferencesAboutView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index 4698580bd5..e7fb31a5c5 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -220,7 +220,7 @@ extension Preferences { } private var lastCheckedText: some View { - let lastChecked = model.updateController?.updateProgress.isDone == true ? lastCheckedFormattedDate(model.lastUpdateCheckDate) : "-" + let lastChecked = model.updateController?.updateProgress.isIdle == true ? lastCheckedFormattedDate(model.lastUpdateCheckDate) : "-" return Text("\(UserText.lastChecked): \(lastChecked)") .foregroundColor(.secondary) } From 9ddd883df9d2eb37ddc96846b18812c8df65f7e3 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 13 Sep 2024 11:57:28 -0400 Subject: [PATCH 42/58] Revert "Increase build number (to be reverted)" and update appcast2 URL (to be reverted) This reverts commit e1deee3dd5623eb54ba5051c3c6d9520e610a78c. --- scripts/appcast_manager/appcastManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/appcast_manager/appcastManager.swift b/scripts/appcast_manager/appcastManager.swift index 2726da6200..3979ab7935 100755 --- a/scripts/appcast_manager/appcastManager.swift +++ b/scripts/appcast_manager/appcastManager.swift @@ -12,7 +12,7 @@ signal(SIGINT) { _ in } let isCI = ProcessInfo.processInfo.environment["CI"] != nil -let appcastURLString = "https://staticcdn.duckduckgo.com/macos-desktop-browser/appcast2.xml" +let appcastURLString = "https://github.com/quanganhdo/playground/raw/main/appcast2.xml" let appcastURL = URL(string: appcastURLString)! let tmpDir = isCI ? "." : NSString(string: "~/Developer").expandingTildeInPath let tmpDirURL = URL(fileURLWithPath: tmpDir, isDirectory: true) From 3ffbeea53f84bc5e904c072a66b2fdc07a5744c6 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 13 Sep 2024 12:19:51 -0400 Subject: [PATCH 43/58] Oops, wrong URL --- DuckDuckGo/Info.plist | 2 +- scripts/appcast_manager/appcastManager.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/Info.plist b/DuckDuckGo/Info.plist index a6011aea11..17f7e4313c 100644 --- a/DuckDuckGo/Info.plist +++ b/DuckDuckGo/Info.plist @@ -547,7 +547,7 @@ SUEnableAutomaticChecks SUFeedURL - https://staticcdn.duckduckgo.com/macos-desktop-browser/appcast2.xml + https://github.com/quanganhdo/playground/raw/main/appcast2.xml SUPublicEDKey ZaO/DNMzMPBldh40b5xVrpNBmqRkuGY0BNRCUng2qRo= SUScheduledCheckInterval diff --git a/scripts/appcast_manager/appcastManager.swift b/scripts/appcast_manager/appcastManager.swift index 3979ab7935..2726da6200 100755 --- a/scripts/appcast_manager/appcastManager.swift +++ b/scripts/appcast_manager/appcastManager.swift @@ -12,7 +12,7 @@ signal(SIGINT) { _ in } let isCI = ProcessInfo.processInfo.environment["CI"] != nil -let appcastURLString = "https://github.com/quanganhdo/playground/raw/main/appcast2.xml" +let appcastURLString = "https://staticcdn.duckduckgo.com/macos-desktop-browser/appcast2.xml" let appcastURL = URL(string: appcastURLString)! let tmpDir = isCI ? "." : NSString(string: "~/Developer").expandingTildeInPath let tmpDirURL = URL(fileURLWithPath: tmpDir, isDirectory: true) From 5e4b8408998386c2355104d7d97c008e1efd4a01 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 13 Sep 2024 14:45:08 -0400 Subject: [PATCH 44/58] Revert browserRestart behaviour and undo debug changes --- DuckDuckGo/Info.plist | 2 +- DuckDuckGo/Updates/ReleaseNotesUserScript.swift | 3 +++ DuckDuckGo/Updates/UpdateController.swift | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/Info.plist b/DuckDuckGo/Info.plist index 17f7e4313c..a6011aea11 100644 --- a/DuckDuckGo/Info.plist +++ b/DuckDuckGo/Info.plist @@ -547,7 +547,7 @@ SUEnableAutomaticChecks SUFeedURL - https://github.com/quanganhdo/playground/raw/main/appcast2.xml + https://staticcdn.duckduckgo.com/macos-desktop-browser/appcast2.xml SUPublicEDKey ZaO/DNMzMPBldh40b5xVrpNBmqRkuGY0BNRCUng2qRo= SUScheduledCheckInterval diff --git a/DuckDuckGo/Updates/ReleaseNotesUserScript.swift b/DuckDuckGo/Updates/ReleaseNotesUserScript.swift index 7306d573c5..199d0da2d0 100644 --- a/DuckDuckGo/Updates/ReleaseNotesUserScript.swift +++ b/DuckDuckGo/Updates/ReleaseNotesUserScript.swift @@ -124,6 +124,9 @@ extension ReleaseNotesUserScript { } private func browserRestart(params: Any, original: WKScriptMessage) async throws -> Encodable? { + DispatchQueue.main.async { [weak self] in + self?.updateController.runUpdate() + } return nil } diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 9acf7b4986..4efa2f5fa6 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -170,9 +170,9 @@ final class UpdateController: NSObject, UpdateControllerProtocol { try updater?.start() #if DEBUG - updater.automaticallyChecksForUpdates = false - updater.automaticallyDownloadsUpdates = false - updater.updateCheckInterval = 0 + updater?.automaticallyChecksForUpdates = false + updater?.automaticallyDownloadsUpdates = false + updater?.updateCheckInterval = 0 #endif } From 0c7f2e6d1a28f1fffd4913e9958810c394e424aa Mon Sep 17 00:00:00 2001 From: Anh Do Date: Fri, 13 Sep 2024 15:11:21 -0400 Subject: [PATCH 45/58] Fix swiftlint --- DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index c8de736b48..f821da5332 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -117,7 +117,7 @@ final class MoreOptionsMenu: NSMenu, NSMenuDelegate { self.sharingMenu = sharingMenu } self.emailManager.requestDelegate = self - + delegate = self setupMenuItems() From 8a896365c255ebd66b2a22b1b12a71642a234208 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 16 Sep 2024 11:15:34 -0400 Subject: [PATCH 46/58] Add missing SPARKLE checks --- DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift | 2 ++ DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift | 2 ++ DuckDuckGo/Preferences/Model/AboutPreferences.swift | 2 ++ DuckDuckGo/Preferences/View/PreferencesAboutView.swift | 2 ++ 4 files changed, 8 insertions(+) diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index f821da5332..554d665a9e 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -483,10 +483,12 @@ final class MoreOptionsMenu: NSMenu, NSMenuDelegate { } func menuWillOpen(_ menu: NSMenu) { +#if SPARKLE guard let updateController = Application.appDelegate.updateController else { return } if updateController.hasPendingUpdate && updateController.needsNotificationDot { updateController.needsNotificationDot = false } +#endif } } diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift index 506fe96577..a81893b20e 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenuButton.swift @@ -63,12 +63,14 @@ final class MoreOptionsMenuButton: MouseOverButton { } private func subscribeToUpdateInfo() { +#if SPARKLE guard let updateController else { return } cancellable = Publishers.CombineLatest(updateController.hasPendingUpdatePublisher, updateController.notificationDotPublisher) .receive(on: DispatchQueue.main) .sink { [weak self] hasPendingUpdate, needsNotificationDot in self?.isNotificationVisible = hasPendingUpdate && needsNotificationDot } +#endif } private func setupNotificationLayerIfNeeded() { diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index b561d547ac..1ec96bc6d0 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -41,9 +41,11 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { @Published var updateState = UpdateState.upToDate +#if SPARKLE var updateController: UpdateControllerProtocol? { return Application.appDelegate.updateController } +#endif var areAutomaticUpdatesEnabled: Bool { get { diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index e7fb31a5c5..fc30e700a0 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -144,9 +144,11 @@ extension Preferences { } } +#if SPARKLE private var hasPendingUpdate: Bool { model.updateController?.hasPendingUpdate == true } +#endif @ViewBuilder private var versionText: some View { From ca19821f6e70bf41d74291f7647eb20981d1e38d Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:15:10 -0400 Subject: [PATCH 47/58] Design improvements to automatic update flow (#3344) Task/Issue URL: https://app.asana.com/0/1201037661562251/1208183525704010/f Tech Design URL: CC: Figma: https://www.figma.com/design/vDBpSFDzW0eN7nwpS95gju/macOS-update-process?node-id=2359-52971&t=KnvIdm2f7PRjNwfK-4 **Description**: **Steps to test this PR**: 1. Change the appcast URL to an invalid URL to test the error state 2. Play with different update options to see if update works **Definition of Done**: * [ ] Does this PR satisfy our [Definition of Done](https://app.asana.com/0/1202500774821704/1207634633537039/f)? --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- DuckDuckGo/Common/Localizables/UserText.swift | 6 +- DuckDuckGo/Localizable.xcstrings | 78 ++++++++++++------- .../Preferences/Model/AboutPreferences.swift | 2 + .../View/PreferencesAboutView.swift | 38 ++++----- DuckDuckGo/Updates/UpdateController.swift | 8 +- DuckDuckGo/Updates/UpdateUserDriver.swift | 18 ++++- 6 files changed, 96 insertions(+), 54 deletions(-) diff --git a/DuckDuckGo/Common/Localizables/UserText.swift b/DuckDuckGo/Common/Localizables/UserText.swift index 8531c36676..ebbf98641d 100644 --- a/DuckDuckGo/Common/Localizables/UserText.swift +++ b/DuckDuckGo/Common/Localizables/UserText.swift @@ -1244,10 +1244,14 @@ struct UserText { static let automaticUpdates = NSLocalizedString("settings.automatic.updates", value: "Automatically install updates (recommended)", comment: "Title of the checkbox item to set up automatic updates of the browser") static let manualUpdates = NSLocalizedString("settings.manual.updates", value: "Check for updates but let you choose to install them", comment: "Title of the checkbox item to set up manual updates of the browser") static let checkingForUpdate = NSLocalizedString("settings.checking.for.update", value: "Checking for update", comment: "Label informing users the app is currently checking for new update") + static let downloadingUpdate = NSLocalizedString("settings.downloading.update", value: "Downloading update %@", comment: "Label informing users the app is currently downloading the update. This will contain a percentage") + static let preparingUpdate = NSLocalizedString("settings.preparing.update", value: "Preparing update", comment: "Label informing users the app is preparing to update.") + static let updateFailed = NSLocalizedString("settings.update.failed", value: "Update failed", comment: "Label informing users the app is unable to update.") static let upToDate = NSLocalizedString("settings.up.to.date", value: "DuckDuckGo is up to date", comment: "Label informing users the app is currently up to date and no update is required.") static let newerVersionAvailable = NSLocalizedString("settings.newer.version.available", value: "Newer version available", comment: "Label informing users the newer version of the app is available to install.") static let lastChecked = NSLocalizedString("settings.last.checked", value: "Last checked", comment: "Label informing users what is the last time the app checked for the update.") - static let runUpdate = NSLocalizedString("settings.restart.to.update", value: "Update DuckDuckGo", comment: "Button label trigering restart and update of the application.") + static let runUpdate = NSLocalizedString("settings.restart.to.update", value: "Update DuckDuckGo", comment: "Button label triggering restart and update of the application.") + static let retryUpdate = NSLocalizedString("settings.retry.update", value: "Retry Update", comment: "Button label triggering a retry of the update.") static let browserUpdatedNotification = NSLocalizedString("notification.browser.updated", value: "Browser Updated", comment: "Notification informing user the app has been updated") static let browserDowngradedNotification = NSLocalizedString("notification.browser.downgraded", value: "Browser Downgraded", comment: "Notification informing user the app has been downgraded") static let criticalUpdateNotification = NSLocalizedString("notification.critical.update", value: "Critical update required. Restart to update.", comment: "Notification informing user a critical update is required.") diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index da0efc9de2..d9dc5724a6 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -52,31 +52,6 @@ } } } - }, - " — Downloading %@ / %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : " — Downloading %1$@ / %2$@" - } - } - } - }, - " — Downloading update" : { - - }, - " — Extracting update" : { - - }, - " — Extracting update (%@%%)" : { - - }, - " — Installing" : { - - }, - " — Ready to install" : { - }, " %@" : { "localizations" : { @@ -12920,9 +12895,6 @@ } } } - }, - "Checking for update" : { - }, "clear" : { "comment" : "Clear button", @@ -56769,6 +56741,18 @@ } } }, + "settings.downloading.update" : { + "comment" : "Label informing users the app is currently downloading the update. This will contain a percentage", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Downloading update %@" + } + } + } + }, "settings.last.checked" : { "comment" : "Label informing users what is the last time the app checked for the update.", "extractionState" : "extracted_with_value", @@ -56949,8 +56933,20 @@ } } }, + "settings.preparing.update" : { + "comment" : "Label informing users the app is preparing to update.", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Preparing update" + } + } + } + }, "settings.restart.to.update" : { - "comment" : "Button label trigering restart and update of the application.", + "comment" : "Button label triggering restart and update of the application.", "extractionState" : "extracted_with_value", "localizations" : { "de" : { @@ -57009,6 +57005,18 @@ } } }, + "settings.retry.update" : { + "comment" : "Button label triggering a retry of the update.", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Retry Update" + } + } + } + }, "settings.up.to.date" : { "comment" : "Label informing users the app is currently up to date and no update is required.", "extractionState" : "extracted_with_value", @@ -57069,6 +57077,18 @@ } } }, + "settings.update.failed" : { + "comment" : "Label informing users the app is unable to update.", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Update failed" + } + } + } + }, "share.menu.item" : { "comment" : "Menu item title", "extractionState" : "extracted_with_value", diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index 1ec96bc6d0..dafbb1e361 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -33,6 +33,8 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { init(from update: Update?, progress: UpdateCycleProgress) { if let update, !update.isInstalled { self = .updateCycle(progress) + } else if progress.isFailed { + self = .updateCycle(progress) } else { self = .upToDate } diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index fc30e700a0..ee753f679a 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -175,10 +175,10 @@ extension Preferences { } #if SPARKLE - private var formatter: ByteCountFormatter { - let formatter = ByteCountFormatter() - formatter.allowsNonnumericFormatting = false - formatter.allowedUnits = [.useKB, .useMB, .useGB] + private var formatter: NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .percent + formatter.maximumFractionDigits = 0 return formatter } @@ -188,17 +188,14 @@ extension Preferences { case .updateCycleDidStart: Text(" — " + UserText.checkingForUpdate) case .downloadDidStart: - Text(" — Downloading update") - case .downloading(let bytesDownloaded, let bytesToDownload): - Text(" — Downloading \(formatter.string(fromByteCount: Int64(bytesDownloaded))) / \(formatter.string(fromByteCount: Int64(bytesToDownload)))") - case .extractionDidStart: - Text(" — Extracting update") - case .extracting(let percentage): - Text(" — Extracting update (\(String(format: "%.1f", percentage * 100))%)") - case .readyToInstallAndRelaunch: - Text(" — Ready to install") - case .installationDidStart, .installing: - Text(" — Installing") + Text(" — " + String(format: UserText.downloadingUpdate, "")) + case .downloading(let percentage): + Text(" — " + String(format: UserText.downloadingUpdate, + formatter.string(from: NSNumber(value: percentage)) ?? "")) + case .extractionDidStart, .extracting, .readyToInstallAndRelaunch, .installationDidStart, .installing: + Text(" — " + UserText.preparingUpdate) + case .updaterError: + Text(" — " + UserText.updateFailed) case .updateCycleNotStarted, .updateCycleDone: EmptyView() } @@ -210,8 +207,8 @@ extension Preferences { case .upToDate: Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) - case .updateCycle: - if hasPendingUpdate { + case .updateCycle(let progress): + if hasPendingUpdate || progress.isFailed { Image(systemName: "exclamationmark.circle.fill") .foregroundColor(.red) } else { @@ -249,12 +246,17 @@ extension Preferences { model.checkForUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) - case .updateCycle: + case .updateCycle(let progress): if hasPendingUpdate { Button(UserText.runUpdate) { model.runUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) + } else if progress.isFailed { + Button(UserText.retryUpdate) { + model.checkForUpdate() + } + .buttonStyle(UpdateButtonStyle(enabled: true)) } else { Button(UserText.checkForUpdate) { model.checkForUpdate() diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 4efa2f5fa6..69bf87dc92 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -259,8 +259,12 @@ extension UpdateController: SPUUpdaterDelegate { } func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { - Logger.updates.debug("Updater did finish update cycle") - updateProgress = .updateCycleDone + if error == nil { + Logger.updates.debug("Updater did finish update cycle") + updateProgress = .updateCycleDone + } else { + Logger.updates.debug("Updater did finish update cycle with error") + } } } diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 7d43108b4c..a6c9ae034c 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -30,12 +30,13 @@ enum UpdateCycleProgress { case updateCycleDidStart case updateCycleDone case downloadDidStart - case downloading(UInt64, UInt64) + case downloading(Double) case extractionDidStart case extracting(Double) case readyToInstallAndRelaunch case installationDidStart case installing + case updaterError(Error) static var `default` = UpdateCycleProgress.updateCycleNotStarted @@ -48,7 +49,14 @@ enum UpdateCycleProgress { var isIdle: Bool { switch self { - case .updateCycleDone, .updateCycleNotStarted: return true + case .updateCycleDone, .updateCycleNotStarted, .updaterError: return true + default: return false + } + } + + var isFailed: Bool { + switch self { + case .updaterError: return true default: return false } } @@ -128,7 +136,8 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showUpdaterError(_ error: any Error, acknowledgement: @escaping () -> Void) { - // no-op + updateProgress = .updaterError(error) + acknowledgement() } func showDownloadInitiated(cancellation: @escaping () -> Void) { @@ -145,7 +154,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { if bytesDownloaded > bytesToDownload { bytesToDownload = bytesDownloaded } - updateProgress = .downloading(bytesDownloaded, bytesToDownload) + updateProgress = .downloading(Double(bytesDownloaded) / Double(bytesToDownload)) } func showDownloadDidStartExtractingUpdate() { @@ -182,6 +191,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func dismissUpdateInstallation() { + guard !updateProgress.isFailed else { return } updateProgress = .updateCycleDone } } From 7fe501a25b2d4c3a9a09ba3c6b210770860a5a56 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 16 Oct 2024 15:19:06 -0400 Subject: [PATCH 48/58] Connect refactored update flow to the new release notes page --- DuckDuckGo/Common/Localizables/UserText.swift | 3 +- DuckDuckGo/Localizable.xcstrings | 14 ++- .../Preferences/Model/AboutPreferences.swift | 18 ---- .../View/PreferencesAboutView.swift | 2 +- .../Updates/ReleaseNotesTabExtension.swift | 93 +++++++++++++------ .../Updates/ReleaseNotesUserScript.swift | 12 ++- DuckDuckGo/Updates/UpdateUserDriver.swift | 15 +++ 7 files changed, 109 insertions(+), 48 deletions(-) diff --git a/DuckDuckGo/Common/Localizables/UserText.swift b/DuckDuckGo/Common/Localizables/UserText.swift index ebbf98641d..682342b529 100644 --- a/DuckDuckGo/Common/Localizables/UserText.swift +++ b/DuckDuckGo/Common/Localizables/UserText.swift @@ -1250,7 +1250,8 @@ struct UserText { static let upToDate = NSLocalizedString("settings.up.to.date", value: "DuckDuckGo is up to date", comment: "Label informing users the app is currently up to date and no update is required.") static let newerVersionAvailable = NSLocalizedString("settings.newer.version.available", value: "Newer version available", comment: "Label informing users the newer version of the app is available to install.") static let lastChecked = NSLocalizedString("settings.last.checked", value: "Last checked", comment: "Label informing users what is the last time the app checked for the update.") - static let runUpdate = NSLocalizedString("settings.restart.to.update", value: "Update DuckDuckGo", comment: "Button label triggering restart and update of the application.") + static let restartToUpdate = NSLocalizedString("settings.restart.to.update", value: "Restart to Update", comment: "Button label triggering restart and update of the application.") + static let runUpdate = NSLocalizedString("settings.run.update", value: "Update DuckDuckGo", comment: "Button label triggering update of the application.") static let retryUpdate = NSLocalizedString("settings.retry.update", value: "Retry Update", comment: "Button label triggering a retry of the update.") static let browserUpdatedNotification = NSLocalizedString("notification.browser.updated", value: "Browser Updated", comment: "Notification informing user the app has been updated") static let browserDowngradedNotification = NSLocalizedString("notification.browser.downgraded", value: "Browser Downgraded", comment: "Notification informing user the app has been downgraded") diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index d9dc5724a6..b0c3176204 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -56958,7 +56958,7 @@ "en" : { "stringUnit" : { "state" : "new", - "value" : "Update DuckDuckGo" + "value" : "Restart to Update" } }, "es" : { @@ -57017,6 +57017,18 @@ } } }, + "settings.run.update" : { + "comment" : "Button label triggering update of the application.", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Update DuckDuckGo" + } + } + } + }, "settings.up.to.date" : { "comment" : "Label informing users the app is currently up to date and no update is required.", "extractionState" : "extracted_with_value", diff --git a/DuckDuckGo/Preferences/Model/AboutPreferences.swift b/DuckDuckGo/Preferences/Model/AboutPreferences.swift index dafbb1e361..63b5433ce8 100644 --- a/DuckDuckGo/Preferences/Model/AboutPreferences.swift +++ b/DuckDuckGo/Preferences/Model/AboutPreferences.swift @@ -25,29 +25,11 @@ final class AboutPreferences: ObservableObject, PreferencesTabOpening { static let shared = AboutPreferences() #if SPARKLE - enum UpdateState { - - case upToDate - case updateCycle(UpdateCycleProgress) - - init(from update: Update?, progress: UpdateCycleProgress) { - if let update, !update.isInstalled { - self = .updateCycle(progress) - } else if progress.isFailed { - self = .updateCycle(progress) - } else { - self = .upToDate - } - } - } - @Published var updateState = UpdateState.upToDate -#if SPARKLE var updateController: UpdateControllerProtocol? { return Application.appDelegate.updateController } -#endif var areAutomaticUpdatesEnabled: Bool { get { diff --git a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift index ee753f679a..9dc9ce618b 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAboutView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAboutView.swift @@ -248,7 +248,7 @@ extension Preferences { .buttonStyle(UpdateButtonStyle(enabled: true)) case .updateCycle(let progress): if hasPendingUpdate { - Button(UserText.runUpdate) { + Button(model.areAutomaticUpdatesEnabled ? UserText.restartToUpdate : UserText.runUpdate) { model.runUpdate() } .buttonStyle(UpdateButtonStyle(enabled: true)) diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index 41c88ea007..797975ead7 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -32,6 +32,14 @@ protocol ReleaseNotesUserScriptProvider { extension UserScripts: ReleaseNotesUserScriptProvider {} public struct ReleaseNotesValues: Codable { + enum Status: String { + case loaded + case loading + case updateReady + case updateDownloading + case updatePreparing + case updateError + } let status: String let currentVersion: String @@ -40,7 +48,7 @@ public struct ReleaseNotesValues: Codable { let releaseTitle: String? let releaseNotes: [String]? let releaseNotesPrivacyPro: [String]? - + let downloadProgress: Double? } final class ReleaseNotesTabExtension: NavigationResponder { @@ -115,49 +123,82 @@ extension TabExtensions { extension ReleaseNotesValues { - init(status: String, + init(status: Status, currentVersion: String, - lastUpdate: UInt) { - self.init(status: status, - currentVersion: currentVersion, - latestVersion: nil, - lastUpdate: lastUpdate, - releaseTitle: nil, - releaseNotes: nil, - releaseNotesPrivacyPro: nil) + latestVersion: String? = nil, + lastUpdate: UInt, + releaseTitle: String? = nil, + releaseNotes: [String]? = nil, + releaseNotesPrivacyPro: [String]? = nil, + downloadProgress: Double? = nil) { + self.status = status.rawValue + self.currentVersion = currentVersion + self.latestVersion = latestVersion + self.lastUpdate = lastUpdate + self.releaseTitle = releaseTitle + self.releaseNotes = releaseNotes + self.releaseNotesPrivacyPro = releaseNotesPrivacyPro + self.downloadProgress = downloadProgress } init(from updateController: UpdateController?) { let currentVersion = "\(AppVersion().versionNumber) (\(AppVersion().buildNumber))" let lastUpdate = UInt((updateController?.lastUpdateCheckDate ?? Date()).timeIntervalSince1970) - let status: String let latestVersion: String - guard let updateController, updateController.updateProgress.isIdle else { - self.init(status: "loading", + guard let updateController else { + self.init(status: .loaded, currentVersion: currentVersion, lastUpdate: lastUpdate) return } - if let latestUpdate = updateController.latestUpdate { - status = latestUpdate.isInstalled ? "loaded" : "updateReady" - latestVersion = "\(latestUpdate.version) (\(latestUpdate.build))" - self.init(status: status, - currentVersion: currentVersion, - latestVersion: latestVersion, - lastUpdate: lastUpdate, - releaseTitle: latestUpdate.title, - releaseNotes: latestUpdate.releaseNotes, - releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro) - return - } else { - self.init(status: "loaded", + let updateState = UpdateState(from: updateController.latestUpdate, progress: updateController.updateProgress) + let hasPendingUpdate = updateController.hasPendingUpdate + + switch updateState { + case .upToDate: + self.init(status: .loaded, currentVersion: currentVersion, lastUpdate: lastUpdate) + case .updateCycle(let progress): + if let latestUpdate = updateController.latestUpdate { + latestVersion = "\(latestUpdate.version) (\(latestUpdate.build))" + let status = hasPendingUpdate ? .updateReady : progress.toStatus + self.init(status: status, + currentVersion: currentVersion, + latestVersion: latestVersion, + lastUpdate: lastUpdate, + releaseTitle: latestUpdate.title, + releaseNotes: latestUpdate.releaseNotes, + releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro, + downloadProgress: progress.toDownloadProgress) + } else { + self.init(status: .loaded, + currentVersion: currentVersion, + lastUpdate: lastUpdate) + } + } + } +} + +private extension UpdateCycleProgress { + var toStatus: ReleaseNotesValues.Status { + switch self { + case .updateCycleDidStart: return .loading + case .downloadDidStart, .downloading: return .updateDownloading + case .extractionDidStart, .extracting, .readyToInstallAndRelaunch, .installationDidStart, .installing: return .updatePreparing + case .updaterError: return .updateError + case .updateCycleNotStarted, .updateCycleDone: return .updateReady } } + var toDownloadProgress: Double? { + guard case .downloading(let percentage) = self else { + return nil + } + return percentage + } } #else diff --git a/DuckDuckGo/Updates/ReleaseNotesUserScript.swift b/DuckDuckGo/Updates/ReleaseNotesUserScript.swift index 199d0da2d0..620c2ff3b9 100644 --- a/DuckDuckGo/Updates/ReleaseNotesUserScript.swift +++ b/DuckDuckGo/Updates/ReleaseNotesUserScript.swift @@ -43,6 +43,7 @@ final class ReleaseNotesUserScript: NSObject, Subfeature { case reportPageException case reportInitException case browserRestart + case retryUpdate } override init() { @@ -57,7 +58,8 @@ final class ReleaseNotesUserScript: NSObject, Subfeature { .initialSetup: initialSetup, .reportPageException: reportPageException, .reportInitException: reportInitException, - .browserRestart: browserRestart + .browserRestart: browserRestart, + .retryUpdate: retryUpdate, ] @MainActor @@ -108,6 +110,14 @@ extension ReleaseNotesUserScript { return InitialSetupResult(env: env, locale: Locale.current.identifier) } + @MainActor + private func retryUpdate(params: Any, original: WKScriptMessage) async throws -> Encodable? { + DispatchQueue.main.async { [weak self] in + self?.updateController.checkForUpdateIfNeeded() + } + return nil + } + struct InitialSetupResult: Encodable { let env: String let locale: String diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index a6c9ae034c..067197fc79 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -25,6 +25,21 @@ import os.log #if SPARKLE +enum UpdateState { + case upToDate + case updateCycle(UpdateCycleProgress) + + init(from update: Update?, progress: UpdateCycleProgress) { + if let update, !update.isInstalled { + self = .updateCycle(progress) + } else if progress.isFailed { + self = .updateCycle(progress) + } else { + self = .upToDate + } + } +} + enum UpdateCycleProgress { case updateCycleNotStarted case updateCycleDidStart From c9563d26017e661b2a2cf60e9513480824cadd01 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Wed, 16 Oct 2024 17:56:23 -0400 Subject: [PATCH 49/58] Show latest update info --- .../Updates/ReleaseNotesTabExtension.swift | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index 797975ead7..8f434b23a9 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -144,9 +144,8 @@ extension ReleaseNotesValues { init(from updateController: UpdateController?) { let currentVersion = "\(AppVersion().versionNumber) (\(AppVersion().buildNumber))" let lastUpdate = UInt((updateController?.lastUpdateCheckDate ?? Date()).timeIntervalSince1970) - let latestVersion: String - guard let updateController else { + guard let updateController, let latestUpdate = updateController.latestUpdate else { self.init(status: .loaded, currentVersion: currentVersion, lastUpdate: lastUpdate) @@ -160,28 +159,31 @@ extension ReleaseNotesValues { case .upToDate: self.init(status: .loaded, currentVersion: currentVersion, - lastUpdate: lastUpdate) + latestVersion: latestUpdate.versionString, + lastUpdate: lastUpdate, + releaseTitle: latestUpdate.title, + releaseNotes: latestUpdate.releaseNotes, + releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro) case .updateCycle(let progress): - if let latestUpdate = updateController.latestUpdate { - latestVersion = "\(latestUpdate.version) (\(latestUpdate.build))" - let status = hasPendingUpdate ? .updateReady : progress.toStatus - self.init(status: status, - currentVersion: currentVersion, - latestVersion: latestVersion, - lastUpdate: lastUpdate, - releaseTitle: latestUpdate.title, - releaseNotes: latestUpdate.releaseNotes, - releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro, - downloadProgress: progress.toDownloadProgress) - } else { - self.init(status: .loaded, - currentVersion: currentVersion, - lastUpdate: lastUpdate) - } + let status = hasPendingUpdate ? .updateReady : progress.toStatus + self.init(status: status, + currentVersion: currentVersion, + latestVersion: latestUpdate.versionString, + lastUpdate: lastUpdate, + releaseTitle: latestUpdate.title, + releaseNotes: latestUpdate.releaseNotes, + releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro, + downloadProgress: progress.toDownloadProgress) } } } +private extension Update { + var versionString: String? { + "\(version) \(build)" + } +} + private extension UpdateCycleProgress { var toStatus: ReleaseNotesValues.Status { switch self { From 91a32e8761275a62576337b6804da153ee45373c Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 17 Oct 2024 10:48:36 -0400 Subject: [PATCH 50/58] Pass automaticUpdate to CSS --- .../Updates/ReleaseNotesTabExtension.swift | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index 8f434b23a9..dbc9f3f9fe 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -49,6 +49,7 @@ public struct ReleaseNotesValues: Codable { let releaseNotes: [String]? let releaseNotesPrivacyPro: [String]? let downloadProgress: Double? + let automaticUpdate: Bool? } final class ReleaseNotesTabExtension: NavigationResponder { @@ -130,7 +131,8 @@ extension ReleaseNotesValues { releaseTitle: String? = nil, releaseNotes: [String]? = nil, releaseNotesPrivacyPro: [String]? = nil, - downloadProgress: Double? = nil) { + downloadProgress: Double? = nil, + automaticUpdate: Bool? = nil) { self.status = status.rawValue self.currentVersion = currentVersion self.latestVersion = latestVersion @@ -139,6 +141,7 @@ extension ReleaseNotesValues { self.releaseNotes = releaseNotes self.releaseNotesPrivacyPro = releaseNotesPrivacyPro self.downloadProgress = downloadProgress + self.automaticUpdate = automaticUpdate } init(from updateController: UpdateController?) { @@ -153,28 +156,27 @@ extension ReleaseNotesValues { } let updateState = UpdateState(from: updateController.latestUpdate, progress: updateController.updateProgress) - let hasPendingUpdate = updateController.hasPendingUpdate + let status: Status + let downloadProgress: Double? switch updateState { case .upToDate: - self.init(status: .loaded, - currentVersion: currentVersion, - latestVersion: latestUpdate.versionString, - lastUpdate: lastUpdate, - releaseTitle: latestUpdate.title, - releaseNotes: latestUpdate.releaseNotes, - releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro) + status = .loaded + downloadProgress = nil case .updateCycle(let progress): - let status = hasPendingUpdate ? .updateReady : progress.toStatus - self.init(status: status, - currentVersion: currentVersion, - latestVersion: latestUpdate.versionString, - lastUpdate: lastUpdate, - releaseTitle: latestUpdate.title, - releaseNotes: latestUpdate.releaseNotes, - releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro, - downloadProgress: progress.toDownloadProgress) + status = updateController.hasPendingUpdate ? .updateReady : progress.toStatus + downloadProgress = progress.toDownloadProgress } + + self.init(status: status, + currentVersion: currentVersion, + latestVersion: latestUpdate.versionString, + lastUpdate: lastUpdate, + releaseTitle: latestUpdate.title, + releaseNotes: latestUpdate.releaseNotes, + releaseNotesPrivacyPro: latestUpdate.releaseNotesPrivacyPro, + downloadProgress: downloadProgress, + automaticUpdate: updateController.areAutomaticUpdatesEnabled) } } @@ -196,9 +198,7 @@ private extension UpdateCycleProgress { } var toDownloadProgress: Double? { - guard case .downloading(let percentage) = self else { - return nil - } + guard case .downloading(let percentage) = self else { return nil } return percentage } } From e3b26772f9e9b09136ae1f0e9a6981cc1bdbe620 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 17 Oct 2024 15:55:55 -0400 Subject: [PATCH 51/58] Check for update more often --- DuckDuckGo/Updates/UpdateController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index 69bf87dc92..5cbe7969c1 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -173,6 +173,8 @@ final class UpdateController: NSObject, UpdateControllerProtocol { updater?.automaticallyChecksForUpdates = false updater?.automaticallyDownloadsUpdates = false updater?.updateCheckInterval = 0 +#else + checkForUpdateIfNeeded() #endif } From c22ab3583f6ca283f7f486e4042d135695974960 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 24 Oct 2024 10:07:53 -0400 Subject: [PATCH 52/58] Fix wrong status --- DuckDuckGo/Updates/ReleaseNotesTabExtension.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift index dbc9f3f9fe..692545634b 100644 --- a/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift +++ b/DuckDuckGo/Updates/ReleaseNotesTabExtension.swift @@ -149,7 +149,7 @@ extension ReleaseNotesValues { let lastUpdate = UInt((updateController?.lastUpdateCheckDate ?? Date()).timeIntervalSince1970) guard let updateController, let latestUpdate = updateController.latestUpdate else { - self.init(status: .loaded, + self.init(status: updateController?.updateProgress.toStatus ?? .loaded, currentVersion: currentVersion, lastUpdate: lastUpdate) return From 254662f4a5a598636510695354e26d9933695e46 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 24 Oct 2024 10:10:10 -0400 Subject: [PATCH 53/58] Update BSK --- DuckDuckGo.xcodeproj/project.pbxproj | 41 ---------------------------- 1 file changed, 41 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 3ee547c432..5749754f48 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1055,14 +1055,6 @@ 37197EAC294244D600394917 /* FutureExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B634DBE6293C98C500C3C99E /* FutureExtension.swift */; }; 371C0A2927E33EDC0070591F /* FeedbackPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */; }; 371D00E129D8509400EC8598 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 371D00E029D8509400EC8598 /* OpenSSL */; }; - 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; - 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; - 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; - 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; - 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; - 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; - 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; - 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; 372217802B3337FE00B8E9C2 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 3722177F2B3337FE00B8E9C2 /* TestUtils */; }; 372217822B33380700B8E9C2 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 372217812B33380700B8E9C2 /* TestUtils */; }; 37269EFB2B332F9E005E8E46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 37269EFA2B332F9E005E8E46 /* Common */; }; @@ -1085,8 +1077,6 @@ 373D9B4829EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; }; 373D9B4929EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; }; 373FB4B32B4D6C4B004C88D6 /* PreferencesViews in Frameworks */ = {isa = PBXBuildFile; productRef = 373FB4B22B4D6C4B004C88D6 /* PreferencesViews */; }; - 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; - 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; 37445F992A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; }; 37445F9A2A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; }; 37445F9C2A1569F00029F789 /* SyncBookmarksAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */; }; @@ -1145,8 +1135,6 @@ 378205F8283BC6A600D1D4AA /* StartupPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378205F7283BC6A600D1D4AA /* StartupPreferencesTests.swift */; }; 378205FB283C277800D1D4AA /* MainMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378205FA283C277800D1D4AA /* MainMenuTests.swift */; }; 3783F92329432E1800BCA897 /* WebViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3783F92229432E1800BCA897 /* WebViewTests.swift */; }; - 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; - 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; 378F44E429B4BDE900899924 /* SwiftUIExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 378F44E329B4BDE900899924 /* SwiftUIExtensions */; }; 378F44E629B4BDEE00899924 /* SwiftUIExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 378F44E529B4BDEE00899924 /* SwiftUIExtensions */; }; 378F44EB29B4C73E00899924 /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378F44EA29B4C73E00899924 /* ViewExtension.swift */; }; @@ -1169,8 +1157,6 @@ 37A6A8F62AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */; }; 37A6A8F72AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */; }; 37A803DB27FD69D300052F4C /* DataImportResources in Resources */ = {isa = PBXBuildFile; fileRef = 37A803DA27FD69D300052F4C /* DataImportResources */; }; - 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; - 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; 37AFCE8127DA2CA600471A10 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8027DA2CA600471A10 /* PreferencesViewController.swift */; }; 37AFCE8527DA2D3900471A10 /* PreferencesSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8427DA2D3900471A10 /* PreferencesSidebar.swift */; }; 37AFCE8727DA334800471A10 /* PreferencesRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8627DA334800471A10 /* PreferencesRootView.swift */; }; @@ -3465,10 +3451,6 @@ 3714B1E628EDB7FA0056C57A /* DuckPlayerPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerPreferencesTests.swift; sourceTree = ""; }; 3714B1E828EDBAAB0056C57A /* DuckPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerTests.swift; sourceTree = ""; }; 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPresenter.swift; sourceTree = ""; }; - 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperiment.swift; sourceTree = ""; }; - 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabSearchBoxExperimentPixel.swift; sourceTree = ""; }; - 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NewTabPageSearchBoxExperiment+Logger.swift"; sourceTree = ""; }; - 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperimentTests.swift; sourceTree = ""; }; 372A0FEB2B2379310033BF7F /* SyncMetricsEventsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetricsEventsHandler.swift; sourceTree = ""; }; 372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncCredentialsAdapter.swift; sourceTree = ""; }; 373A1AA7283ED1B900586521 /* BookmarkHTMLReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkHTMLReader.swift; sourceTree = ""; }; @@ -3478,7 +3460,6 @@ 373A26962964CF0B0043FC57 /* TestsTargetsBase.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TestsTargetsBase.xcconfig; sourceTree = ""; }; 373B2F802C384DEB0013A94B /* ActiveRemoteMessageModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveRemoteMessageModelTests.swift; sourceTree = ""; }; 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetadataDatabase.swift; sourceTree = ""; }; - 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageSettingsVisibilityModelTests.swift; sourceTree = ""; }; 37445F982A1566420029F789 /* SyncDataProviders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncDataProviders.swift; sourceTree = ""; }; 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBookmarksAdapter.swift; sourceTree = ""; }; 37479F142891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TabCollectionViewModelTests+WithoutPinnedTabsManager.swift"; sourceTree = ""; }; @@ -3520,7 +3501,6 @@ 378205F7283BC6A600D1D4AA /* StartupPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupPreferencesTests.swift; sourceTree = ""; }; 378205FA283C277800D1D4AA /* MainMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTests.swift; sourceTree = ""; }; 3783F92229432E1800BCA897 /* WebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTests.swift; sourceTree = ""; }; - 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageAddressBarModel.swift; sourceTree = ""; }; 378B5887295CF2A4002C0CC0 /* Version.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; 378B5888295CF2A4002C0CC0 /* Common.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; 378B588B295CF3B9002C0CC0 /* AppTargetsBase.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppTargetsBase.xcconfig; sourceTree = ""; }; @@ -3542,7 +3522,6 @@ 37A6A8F02AFCC988008580A3 /* FaviconsFetcherOnboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsFetcherOnboarding.swift; sourceTree = ""; }; 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsFetcherOnboardingViewController.swift; sourceTree = ""; }; 37A803DA27FD69D300052F4C /* DataImportResources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DataImportResources; sourceTree = ""; }; - 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressBarTextFieldView.swift; sourceTree = ""; }; 37AFCE8027DA2CA600471A10 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; 37AFCE8427DA2D3900471A10 /* PreferencesSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesSidebar.swift; sourceTree = ""; }; 37AFCE8627DA334800471A10 /* PreferencesRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesRootView.swift; sourceTree = ""; }; @@ -6598,13 +6577,11 @@ 4BF6961B28BE90E800D402D4 /* HomePage */ = { isa = PBXGroup; children = ( - 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */, 56534DEB29DF251C00121467 /* Mocks */, 4BF6961C28BE911100D402D4 /* RecentlyVisitedSiteModelTests.swift */, 569277C329DEE09D00B633EF /* ContinueSetUpModelTests.swift */, 56D145ED29E6DAD900E3488A /* DataImportProviderTests.swift */, 370270BF2C78EB13002E44E4 /* HomePageSettingsModelTests.swift */, - 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */, 37D046A02C7DA9A200AEAA50 /* UserBackgroundImagesManagerTests.swift */, 376731842C7EF97400EB097B /* ColorSchemeLosslessStringConvertibleExtensionTests.swift */, 376731962C7F36AA00EB097B /* UserBackgroundImageTests.swift */, @@ -6964,9 +6941,6 @@ 85589E8527BBB8DD0038AD11 /* Model */ = { isa = PBXGroup; children = ( - 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */, - 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */, - 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */, 370C23172C77C47700A80A3E /* HomePageSettings */, 85589E8627BBB8F20038AD11 /* HomePageFavoritesModel.swift */, 85AC7ADC27BEB6EE00FFB69B /* HomePageDefaultBrowserModel.swift */, @@ -8550,7 +8524,6 @@ 85589E9327BFE1E70038AD11 /* FavoritesView.swift */, 56D6A3D529DB2BAB0055215A /* ContinueSetUpView.swift */, 85589E7C27BBB8630038AD11 /* HomePageView.swift */, - 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */, 1DCFBC8929ADF32B00313531 /* BurnerHomePageView.swift */, 85589E7D27BBB8630038AD11 /* HomePageViewController.swift */, 857FFEBF27D239DC00415E7A /* HyperLink.swift */, @@ -10836,7 +10809,6 @@ BB7B5F992C4ED73800BA4AF8 /* BookmarksSearchAndSortMetrics.swift in Sources */, B60D644A2AAF1B7C00B26F50 /* AddressBarTextSelectionNavigation.swift in Sources */, 1D01A3D52B88CF7700FE8150 /* AccessibilityPreferences.swift in Sources */, - 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, 3706FA98293F65D500E42796 /* SecureVaultSorting.swift in Sources */, 1DEDB3652C19934C006B6D1B /* MoreOptionsMenuButton.swift in Sources */, 3767318C2C7F32C500EB097B /* GradientBackground.swift in Sources */, @@ -10888,7 +10860,6 @@ 56A054202C1CA1F5007D8FAB /* OnboardingTabExtension.swift in Sources */, 3706FABA293F65D500E42796 /* BookmarkOutlineViewDataSource.swift in Sources */, 560EB9332C78946F0080DBC8 /* ContextualOnboardingDialogs.swift in Sources */, - 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, 3706FABB293F65D500E42796 /* PasswordManagementBitwardenItemView.swift in Sources */, CD2AB5CB2C8225E70019EB49 /* URLTokenValidator.swift in Sources */, 1D220BF92B86192200F8BBC6 /* PreferencesEmailProtectionView.swift in Sources */, @@ -11236,7 +11207,6 @@ 3706FB8D293F65D500E42796 /* BookmarkTreeController.swift in Sources */, B66260E129AC6EBD00E9E3EE /* HistoryTabExtension.swift in Sources */, 3706FB8E293F65D500E42796 /* FirefoxEncryptionKeyReader.swift in Sources */, - 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, 3706FB8F293F65D500E42796 /* BookmarkManagementSplitViewController.swift in Sources */, 316913272BD2B76F0051B46D /* DataBrokerPrerequisitesStatusVerifier.swift in Sources */, 3706FB90293F65D500E42796 /* CookieManagedNotificationContainerView.swift in Sources */, @@ -11539,7 +11509,6 @@ 3706FC3E293F65D500E42796 /* VariantManager.swift in Sources */, 3706FC3F293F65D500E42796 /* ApplicationDockMenu.swift in Sources */, B68412152B694BA10092F66A /* NSObject+performSelector.m in Sources */, - 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, 4B9DB03C2A983B24000927DB /* InvitedToWaitlistView.swift in Sources */, 3706FC40293F65D500E42796 /* SaveIdentityViewController.swift in Sources */, 3706FC41293F65D500E42796 /* FileStore.swift in Sources */, @@ -11560,7 +11529,6 @@ 3706FC49293F65D500E42796 /* RoundedSelectionRowView.swift in Sources */, 4B9DB01E2A983B24000927DB /* Waitlist.swift in Sources */, 3706FC4A293F65D500E42796 /* LocalStatisticsStore.swift in Sources */, - 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, 3706FC4B293F65D500E42796 /* BackForwardListItem.swift in Sources */, 4B4D60DD2A0C875E00BCD287 /* NetworkProtectionOptionKeyExtension.swift in Sources */, BDBA85912C5D252A00BC54F5 /* VPNFeedbackSender.swift in Sources */, @@ -11882,7 +11850,6 @@ 3706FE2A293F661700E42796 /* SafariVersionReaderTests.swift in Sources */, 3706FE2B293F661700E42796 /* AtbParserTests.swift in Sources */, 3706FE2C293F661700E42796 /* PermissionStoreMock.swift in Sources */, - 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, 3706FE2D293F661700E42796 /* ChromiumFaviconsReaderTests.swift in Sources */, 3706FE2E293F661700E42796 /* LocalBookmarkManagerTests.swift in Sources */, 9F180D132B69C665000D695F /* DownloadsTabExtensionMock.swift in Sources */, @@ -11945,7 +11912,6 @@ EEC8EB402982CD550065AA39 /* JSAlertViewModelTests.swift in Sources */, BDA764922BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */, 3706FE4B293F661700E42796 /* BookmarksHTMLImporterTests.swift in Sources */, - 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, 9FA75A3F2BA00E1400DA5FA6 /* BookmarksBarMenuFactoryTests.swift in Sources */, 56D145E929E6BB6300E3488A /* CapturingDataImportProvider.swift in Sources */, 3706FE4C293F661700E42796 /* CSVParserTests.swift in Sources */, @@ -12528,7 +12494,6 @@ 4BA1A6B3258B080A00F6F690 /* EncryptionKeyGeneration.swift in Sources */, 37B11B3928095E6600CBB621 /* TabLazyLoader.swift in Sources */, 4B9DB03B2A983B24000927DB /* InvitedToWaitlistView.swift in Sources */, - 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, 8589063C267BCDC000D23B0D /* SaveCredentialsViewController.swift in Sources */, 4BBE0AA727B9B027003B37A8 /* PopUpButton.swift in Sources */, AABEE6A524AA0A7F0043105B /* SuggestionViewController.swift in Sources */, @@ -12544,7 +12509,6 @@ B6B1E88026D5DA9B0062C350 /* DownloadsViewController.swift in Sources */, BD7090D62C540D5D009EED82 /* EmptyMetadataCollector.swift in Sources */, 85AC3AF725D5DBFD00C7D2AA /* DataExtension.swift in Sources */, - 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, 85480FCF25D1AA22009424E3 /* ConfigurationStore.swift in Sources */, AA3D531B27A2F57E00074EC1 /* Feedback.swift in Sources */, 4B0A63E8289DB58E00378EF7 /* FirefoxFaviconsReader.swift in Sources */, @@ -12940,7 +12904,6 @@ AA4BBA3B25C58FA200C4FB0F /* MainMenu.swift in Sources */, AA585D84248FD31100E9A3E2 /* BrowserTabViewController.swift in Sources */, 85707F22276A32B600DC0649 /* CallToAction.swift in Sources */, - 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, B693954B26F04BEB0015B914 /* MouseOverView.swift in Sources */, AAE7527C263B056C00B973F8 /* EncryptedHistoryStore.swift in Sources */, AAE246F32709EF3B00BEEAEE /* FirePopoverCollectionViewItem.swift in Sources */, @@ -13172,8 +13135,6 @@ B65536A62685B82B00085A79 /* Permissions.swift in Sources */, AAC82C60258B6CB5009B6B42 /* TabPreviewWindowController.swift in Sources */, AAC5E4E425D6BA9C007F5990 /* NSSizeExtension.swift in Sources */, - 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, - 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, AA6820EB25503D6A005ED0D5 /* Fire.swift in Sources */, BD6367252C877BE1009DE7A8 /* UpdateUserDriver.swift in Sources */, 3158B1492B0BF73000AF130C /* DBPHomeViewController.swift in Sources */, @@ -13344,7 +13305,6 @@ 9FAD623A2BCFDB32007F3A65 /* WebsiteInfoHelpers.swift in Sources */, 56A054282C201D04007D8FAB /* MockSchemeTask.swift in Sources */, 56A054472C22536A007D8FAB /* CapturingOnboardingActionsManager.swift in Sources */, - 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, C13909F42B85FD79001626ED /* AutofillDeleteAllPasswordsExecutorTests.swift in Sources */, 37534C9E28104D9B002621E7 /* TabLazyLoaderTests.swift in Sources */, 37D046992C7D037500AEAA50 /* CapturingUserBackgroundImagesManager.swift in Sources */, @@ -13563,7 +13523,6 @@ B6AE39F129373AF200C37AA4 /* EmptyAttributionRulesProver.swift in Sources */, 4BB99D1126FE1A84001E4761 /* SafariBookmarksReaderTests.swift in Sources */, BBC063E82C5A9E4B007BDC18 /* BookmarkManagementDetailViewModelTests.swift in Sources */, - 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, 1DA860722BE3AE950027B813 /* DockPositionProviderTests.swift in Sources */, 4BBF0925283083EC00EE1418 /* FileSystemDSLTests.swift in Sources */, 4B11060A25903EAC0039B979 /* CoreDataEncryptionTests.swift in Sources */, From 5e717475162ee17e31b4ada28d0d8f556ef648e6 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 24 Oct 2024 10:47:13 -0400 Subject: [PATCH 54/58] Reverted botched rebase --- DuckDuckGo.xcodeproj/project.pbxproj | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5749754f48..339cd76ece 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1055,6 +1055,14 @@ 37197EAC294244D600394917 /* FutureExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B634DBE6293C98C500C3C99E /* FutureExtension.swift */; }; 371C0A2927E33EDC0070591F /* FeedbackPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */; }; 371D00E129D8509400EC8598 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 371D00E029D8509400EC8598 /* OpenSSL */; }; + 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; + 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; + 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; + 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; + 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; + 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; + 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; + 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; 372217802B3337FE00B8E9C2 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 3722177F2B3337FE00B8E9C2 /* TestUtils */; }; 372217822B33380700B8E9C2 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 372217812B33380700B8E9C2 /* TestUtils */; }; 37269EFB2B332F9E005E8E46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 37269EFA2B332F9E005E8E46 /* Common */; }; @@ -1077,6 +1085,8 @@ 373D9B4829EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; }; 373D9B4929EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; }; 373FB4B32B4D6C4B004C88D6 /* PreferencesViews in Frameworks */ = {isa = PBXBuildFile; productRef = 373FB4B22B4D6C4B004C88D6 /* PreferencesViews */; }; + 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; + 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; 37445F992A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; }; 37445F9A2A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; }; 37445F9C2A1569F00029F789 /* SyncBookmarksAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */; }; @@ -1135,6 +1145,8 @@ 378205F8283BC6A600D1D4AA /* StartupPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378205F7283BC6A600D1D4AA /* StartupPreferencesTests.swift */; }; 378205FB283C277800D1D4AA /* MainMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378205FA283C277800D1D4AA /* MainMenuTests.swift */; }; 3783F92329432E1800BCA897 /* WebViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3783F92229432E1800BCA897 /* WebViewTests.swift */; }; + 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; + 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; 378F44E429B4BDE900899924 /* SwiftUIExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 378F44E329B4BDE900899924 /* SwiftUIExtensions */; }; 378F44E629B4BDEE00899924 /* SwiftUIExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 378F44E529B4BDEE00899924 /* SwiftUIExtensions */; }; 378F44EB29B4C73E00899924 /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378F44EA29B4C73E00899924 /* ViewExtension.swift */; }; @@ -1157,6 +1169,8 @@ 37A6A8F62AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */; }; 37A6A8F72AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */; }; 37A803DB27FD69D300052F4C /* DataImportResources in Resources */ = {isa = PBXBuildFile; fileRef = 37A803DA27FD69D300052F4C /* DataImportResources */; }; + 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; + 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; 37AFCE8127DA2CA600471A10 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8027DA2CA600471A10 /* PreferencesViewController.swift */; }; 37AFCE8527DA2D3900471A10 /* PreferencesSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8427DA2D3900471A10 /* PreferencesSidebar.swift */; }; 37AFCE8727DA334800471A10 /* PreferencesRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8627DA334800471A10 /* PreferencesRootView.swift */; }; @@ -3451,6 +3465,10 @@ 3714B1E628EDB7FA0056C57A /* DuckPlayerPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerPreferencesTests.swift; sourceTree = ""; }; 3714B1E828EDBAAB0056C57A /* DuckPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerTests.swift; sourceTree = ""; }; 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPresenter.swift; sourceTree = ""; }; + 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperiment.swift; sourceTree = ""; }; + 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabSearchBoxExperimentPixel.swift; sourceTree = ""; }; + 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NewTabPageSearchBoxExperiment+Logger.swift"; sourceTree = ""; }; + 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperimentTests.swift; sourceTree = ""; }; 372A0FEB2B2379310033BF7F /* SyncMetricsEventsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetricsEventsHandler.swift; sourceTree = ""; }; 372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncCredentialsAdapter.swift; sourceTree = ""; }; 373A1AA7283ED1B900586521 /* BookmarkHTMLReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkHTMLReader.swift; sourceTree = ""; }; @@ -3460,6 +3478,7 @@ 373A26962964CF0B0043FC57 /* TestsTargetsBase.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TestsTargetsBase.xcconfig; sourceTree = ""; }; 373B2F802C384DEB0013A94B /* ActiveRemoteMessageModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveRemoteMessageModelTests.swift; sourceTree = ""; }; 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetadataDatabase.swift; sourceTree = ""; }; + 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageSettingsVisibilityModelTests.swift; sourceTree = ""; }; 37445F982A1566420029F789 /* SyncDataProviders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncDataProviders.swift; sourceTree = ""; }; 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBookmarksAdapter.swift; sourceTree = ""; }; 37479F142891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TabCollectionViewModelTests+WithoutPinnedTabsManager.swift"; sourceTree = ""; }; @@ -3501,6 +3520,7 @@ 378205F7283BC6A600D1D4AA /* StartupPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupPreferencesTests.swift; sourceTree = ""; }; 378205FA283C277800D1D4AA /* MainMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTests.swift; sourceTree = ""; }; 3783F92229432E1800BCA897 /* WebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTests.swift; sourceTree = ""; }; + 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageAddressBarModel.swift; sourceTree = ""; }; 378B5887295CF2A4002C0CC0 /* Version.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; 378B5888295CF2A4002C0CC0 /* Common.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; 378B588B295CF3B9002C0CC0 /* AppTargetsBase.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppTargetsBase.xcconfig; sourceTree = ""; }; @@ -3522,6 +3542,7 @@ 37A6A8F02AFCC988008580A3 /* FaviconsFetcherOnboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsFetcherOnboarding.swift; sourceTree = ""; }; 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsFetcherOnboardingViewController.swift; sourceTree = ""; }; 37A803DA27FD69D300052F4C /* DataImportResources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DataImportResources; sourceTree = ""; }; + 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressBarTextFieldView.swift; sourceTree = ""; }; 37AFCE8027DA2CA600471A10 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; 37AFCE8427DA2D3900471A10 /* PreferencesSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesSidebar.swift; sourceTree = ""; }; 37AFCE8627DA334800471A10 /* PreferencesRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesRootView.swift; sourceTree = ""; }; @@ -6577,11 +6598,13 @@ 4BF6961B28BE90E800D402D4 /* HomePage */ = { isa = PBXGroup; children = ( + 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */, 56534DEB29DF251C00121467 /* Mocks */, 4BF6961C28BE911100D402D4 /* RecentlyVisitedSiteModelTests.swift */, 569277C329DEE09D00B633EF /* ContinueSetUpModelTests.swift */, 56D145ED29E6DAD900E3488A /* DataImportProviderTests.swift */, 370270BF2C78EB13002E44E4 /* HomePageSettingsModelTests.swift */, + 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */, 37D046A02C7DA9A200AEAA50 /* UserBackgroundImagesManagerTests.swift */, 376731842C7EF97400EB097B /* ColorSchemeLosslessStringConvertibleExtensionTests.swift */, 376731962C7F36AA00EB097B /* UserBackgroundImageTests.swift */, @@ -6941,6 +6964,9 @@ 85589E8527BBB8DD0038AD11 /* Model */ = { isa = PBXGroup; children = ( + 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */, + 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */, + 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */, 370C23172C77C47700A80A3E /* HomePageSettings */, 85589E8627BBB8F20038AD11 /* HomePageFavoritesModel.swift */, 85AC7ADC27BEB6EE00FFB69B /* HomePageDefaultBrowserModel.swift */, @@ -8524,6 +8550,7 @@ 85589E9327BFE1E70038AD11 /* FavoritesView.swift */, 56D6A3D529DB2BAB0055215A /* ContinueSetUpView.swift */, 85589E7C27BBB8630038AD11 /* HomePageView.swift */, + 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */, 1DCFBC8929ADF32B00313531 /* BurnerHomePageView.swift */, 85589E7D27BBB8630038AD11 /* HomePageViewController.swift */, 857FFEBF27D239DC00415E7A /* HyperLink.swift */, @@ -10809,6 +10836,7 @@ BB7B5F992C4ED73800BA4AF8 /* BookmarksSearchAndSortMetrics.swift in Sources */, B60D644A2AAF1B7C00B26F50 /* AddressBarTextSelectionNavigation.swift in Sources */, 1D01A3D52B88CF7700FE8150 /* AccessibilityPreferences.swift in Sources */, + 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, 3706FA98293F65D500E42796 /* SecureVaultSorting.swift in Sources */, 1DEDB3652C19934C006B6D1B /* MoreOptionsMenuButton.swift in Sources */, 3767318C2C7F32C500EB097B /* GradientBackground.swift in Sources */, @@ -10860,6 +10888,7 @@ 56A054202C1CA1F5007D8FAB /* OnboardingTabExtension.swift in Sources */, 3706FABA293F65D500E42796 /* BookmarkOutlineViewDataSource.swift in Sources */, 560EB9332C78946F0080DBC8 /* ContextualOnboardingDialogs.swift in Sources */, + 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, 3706FABB293F65D500E42796 /* PasswordManagementBitwardenItemView.swift in Sources */, CD2AB5CB2C8225E70019EB49 /* URLTokenValidator.swift in Sources */, 1D220BF92B86192200F8BBC6 /* PreferencesEmailProtectionView.swift in Sources */, @@ -11207,6 +11236,7 @@ 3706FB8D293F65D500E42796 /* BookmarkTreeController.swift in Sources */, B66260E129AC6EBD00E9E3EE /* HistoryTabExtension.swift in Sources */, 3706FB8E293F65D500E42796 /* FirefoxEncryptionKeyReader.swift in Sources */, + 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, 3706FB8F293F65D500E42796 /* BookmarkManagementSplitViewController.swift in Sources */, 316913272BD2B76F0051B46D /* DataBrokerPrerequisitesStatusVerifier.swift in Sources */, 3706FB90293F65D500E42796 /* CookieManagedNotificationContainerView.swift in Sources */, @@ -11509,6 +11539,7 @@ 3706FC3E293F65D500E42796 /* VariantManager.swift in Sources */, 3706FC3F293F65D500E42796 /* ApplicationDockMenu.swift in Sources */, B68412152B694BA10092F66A /* NSObject+performSelector.m in Sources */, + 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, 4B9DB03C2A983B24000927DB /* InvitedToWaitlistView.swift in Sources */, 3706FC40293F65D500E42796 /* SaveIdentityViewController.swift in Sources */, 3706FC41293F65D500E42796 /* FileStore.swift in Sources */, @@ -11529,6 +11560,7 @@ 3706FC49293F65D500E42796 /* RoundedSelectionRowView.swift in Sources */, 4B9DB01E2A983B24000927DB /* Waitlist.swift in Sources */, 3706FC4A293F65D500E42796 /* LocalStatisticsStore.swift in Sources */, + 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, 3706FC4B293F65D500E42796 /* BackForwardListItem.swift in Sources */, 4B4D60DD2A0C875E00BCD287 /* NetworkProtectionOptionKeyExtension.swift in Sources */, BDBA85912C5D252A00BC54F5 /* VPNFeedbackSender.swift in Sources */, @@ -11850,6 +11882,7 @@ 3706FE2A293F661700E42796 /* SafariVersionReaderTests.swift in Sources */, 3706FE2B293F661700E42796 /* AtbParserTests.swift in Sources */, 3706FE2C293F661700E42796 /* PermissionStoreMock.swift in Sources */, + 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, 3706FE2D293F661700E42796 /* ChromiumFaviconsReaderTests.swift in Sources */, 3706FE2E293F661700E42796 /* LocalBookmarkManagerTests.swift in Sources */, 9F180D132B69C665000D695F /* DownloadsTabExtensionMock.swift in Sources */, @@ -11912,6 +11945,7 @@ EEC8EB402982CD550065AA39 /* JSAlertViewModelTests.swift in Sources */, BDA764922BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */, 3706FE4B293F661700E42796 /* BookmarksHTMLImporterTests.swift in Sources */, + 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, 9FA75A3F2BA00E1400DA5FA6 /* BookmarksBarMenuFactoryTests.swift in Sources */, 56D145E929E6BB6300E3488A /* CapturingDataImportProvider.swift in Sources */, 3706FE4C293F661700E42796 /* CSVParserTests.swift in Sources */, @@ -12494,6 +12528,7 @@ 4BA1A6B3258B080A00F6F690 /* EncryptionKeyGeneration.swift in Sources */, 37B11B3928095E6600CBB621 /* TabLazyLoader.swift in Sources */, 4B9DB03B2A983B24000927DB /* InvitedToWaitlistView.swift in Sources */, + 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, 8589063C267BCDC000D23B0D /* SaveCredentialsViewController.swift in Sources */, 4BBE0AA727B9B027003B37A8 /* PopUpButton.swift in Sources */, AABEE6A524AA0A7F0043105B /* SuggestionViewController.swift in Sources */, @@ -12509,6 +12544,7 @@ B6B1E88026D5DA9B0062C350 /* DownloadsViewController.swift in Sources */, BD7090D62C540D5D009EED82 /* EmptyMetadataCollector.swift in Sources */, 85AC3AF725D5DBFD00C7D2AA /* DataExtension.swift in Sources */, + 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, 85480FCF25D1AA22009424E3 /* ConfigurationStore.swift in Sources */, AA3D531B27A2F57E00074EC1 /* Feedback.swift in Sources */, 4B0A63E8289DB58E00378EF7 /* FirefoxFaviconsReader.swift in Sources */, @@ -12904,6 +12940,7 @@ AA4BBA3B25C58FA200C4FB0F /* MainMenu.swift in Sources */, AA585D84248FD31100E9A3E2 /* BrowserTabViewController.swift in Sources */, 85707F22276A32B600DC0649 /* CallToAction.swift in Sources */, + 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, B693954B26F04BEB0015B914 /* MouseOverView.swift in Sources */, AAE7527C263B056C00B973F8 /* EncryptedHistoryStore.swift in Sources */, AAE246F32709EF3B00BEEAEE /* FirePopoverCollectionViewItem.swift in Sources */, @@ -13135,6 +13172,8 @@ B65536A62685B82B00085A79 /* Permissions.swift in Sources */, AAC82C60258B6CB5009B6B42 /* TabPreviewWindowController.swift in Sources */, AAC5E4E425D6BA9C007F5990 /* NSSizeExtension.swift in Sources */, + 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, + 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, AA6820EB25503D6A005ED0D5 /* Fire.swift in Sources */, BD6367252C877BE1009DE7A8 /* UpdateUserDriver.swift in Sources */, 3158B1492B0BF73000AF130C /* DBPHomeViewController.swift in Sources */, @@ -13305,6 +13344,7 @@ 9FAD623A2BCFDB32007F3A65 /* WebsiteInfoHelpers.swift in Sources */, 56A054282C201D04007D8FAB /* MockSchemeTask.swift in Sources */, 56A054472C22536A007D8FAB /* CapturingOnboardingActionsManager.swift in Sources */, + 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, C13909F42B85FD79001626ED /* AutofillDeleteAllPasswordsExecutorTests.swift in Sources */, 37534C9E28104D9B002621E7 /* TabLazyLoaderTests.swift in Sources */, 37D046992C7D037500AEAA50 /* CapturingUserBackgroundImagesManager.swift in Sources */, @@ -13523,6 +13563,7 @@ B6AE39F129373AF200C37AA4 /* EmptyAttributionRulesProver.swift in Sources */, 4BB99D1126FE1A84001E4761 /* SafariBookmarksReaderTests.swift in Sources */, BBC063E82C5A9E4B007BDC18 /* BookmarkManagementDetailViewModelTests.swift in Sources */, + 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, 1DA860722BE3AE950027B813 /* DockPositionProviderTests.swift in Sources */, 4BBF0925283083EC00EE1418 /* FileSystemDSLTests.swift in Sources */, 4B11060A25903EAC0039B979 /* CoreDataEncryptionTests.swift in Sources */, From 810f47f757b32d4173d83ce6d8df48a62b461ca0 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Thu, 24 Oct 2024 10:59:10 -0400 Subject: [PATCH 55/58] Fix indentation --- DuckDuckGo.xcodeproj/project.pbxproj | 82 ++++++++++++++-------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 339cd76ece..3ee547c432 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1055,14 +1055,14 @@ 37197EAC294244D600394917 /* FutureExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B634DBE6293C98C500C3C99E /* FutureExtension.swift */; }; 371C0A2927E33EDC0070591F /* FeedbackPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */; }; 371D00E129D8509400EC8598 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 371D00E029D8509400EC8598 /* OpenSSL */; }; - 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; - 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; - 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; - 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; - 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; - 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; - 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; - 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; + 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; + 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */; }; + 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; + 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */; }; + 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; + 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */; }; + 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; + 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */; }; 372217802B3337FE00B8E9C2 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 3722177F2B3337FE00B8E9C2 /* TestUtils */; }; 372217822B33380700B8E9C2 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 372217812B33380700B8E9C2 /* TestUtils */; }; 37269EFB2B332F9E005E8E46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 37269EFA2B332F9E005E8E46 /* Common */; }; @@ -1085,8 +1085,8 @@ 373D9B4829EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; }; 373D9B4929EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; }; 373FB4B32B4D6C4B004C88D6 /* PreferencesViews in Frameworks */ = {isa = PBXBuildFile; productRef = 373FB4B22B4D6C4B004C88D6 /* PreferencesViews */; }; - 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; - 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; + 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; + 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; }; 37445F992A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; }; 37445F9A2A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; }; 37445F9C2A1569F00029F789 /* SyncBookmarksAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */; }; @@ -1145,8 +1145,8 @@ 378205F8283BC6A600D1D4AA /* StartupPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378205F7283BC6A600D1D4AA /* StartupPreferencesTests.swift */; }; 378205FB283C277800D1D4AA /* MainMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378205FA283C277800D1D4AA /* MainMenuTests.swift */; }; 3783F92329432E1800BCA897 /* WebViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3783F92229432E1800BCA897 /* WebViewTests.swift */; }; - 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; - 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; + 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; + 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */; }; 378F44E429B4BDE900899924 /* SwiftUIExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 378F44E329B4BDE900899924 /* SwiftUIExtensions */; }; 378F44E629B4BDEE00899924 /* SwiftUIExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 378F44E529B4BDEE00899924 /* SwiftUIExtensions */; }; 378F44EB29B4C73E00899924 /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378F44EA29B4C73E00899924 /* ViewExtension.swift */; }; @@ -1169,8 +1169,8 @@ 37A6A8F62AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */; }; 37A6A8F72AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */; }; 37A803DB27FD69D300052F4C /* DataImportResources in Resources */ = {isa = PBXBuildFile; fileRef = 37A803DA27FD69D300052F4C /* DataImportResources */; }; - 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; - 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; + 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; + 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */; }; 37AFCE8127DA2CA600471A10 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8027DA2CA600471A10 /* PreferencesViewController.swift */; }; 37AFCE8527DA2D3900471A10 /* PreferencesSidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8427DA2D3900471A10 /* PreferencesSidebar.swift */; }; 37AFCE8727DA334800471A10 /* PreferencesRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AFCE8627DA334800471A10 /* PreferencesRootView.swift */; }; @@ -3465,10 +3465,10 @@ 3714B1E628EDB7FA0056C57A /* DuckPlayerPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerPreferencesTests.swift; sourceTree = ""; }; 3714B1E828EDBAAB0056C57A /* DuckPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerTests.swift; sourceTree = ""; }; 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPresenter.swift; sourceTree = ""; }; - 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperiment.swift; sourceTree = ""; }; - 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabSearchBoxExperimentPixel.swift; sourceTree = ""; }; - 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NewTabPageSearchBoxExperiment+Logger.swift"; sourceTree = ""; }; - 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperimentTests.swift; sourceTree = ""; }; + 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperiment.swift; sourceTree = ""; }; + 37219B362CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabSearchBoxExperimentPixel.swift; sourceTree = ""; }; + 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NewTabPageSearchBoxExperiment+Logger.swift"; sourceTree = ""; }; + 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperimentTests.swift; sourceTree = ""; }; 372A0FEB2B2379310033BF7F /* SyncMetricsEventsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetricsEventsHandler.swift; sourceTree = ""; }; 372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncCredentialsAdapter.swift; sourceTree = ""; }; 373A1AA7283ED1B900586521 /* BookmarkHTMLReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkHTMLReader.swift; sourceTree = ""; }; @@ -3478,7 +3478,7 @@ 373A26962964CF0B0043FC57 /* TestsTargetsBase.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TestsTargetsBase.xcconfig; sourceTree = ""; }; 373B2F802C384DEB0013A94B /* ActiveRemoteMessageModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveRemoteMessageModelTests.swift; sourceTree = ""; }; 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetadataDatabase.swift; sourceTree = ""; }; - 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageSettingsVisibilityModelTests.swift; sourceTree = ""; }; + 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageSettingsVisibilityModelTests.swift; sourceTree = ""; }; 37445F982A1566420029F789 /* SyncDataProviders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncDataProviders.swift; sourceTree = ""; }; 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBookmarksAdapter.swift; sourceTree = ""; }; 37479F142891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TabCollectionViewModelTests+WithoutPinnedTabsManager.swift"; sourceTree = ""; }; @@ -3520,7 +3520,7 @@ 378205F7283BC6A600D1D4AA /* StartupPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupPreferencesTests.swift; sourceTree = ""; }; 378205FA283C277800D1D4AA /* MainMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTests.swift; sourceTree = ""; }; 3783F92229432E1800BCA897 /* WebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTests.swift; sourceTree = ""; }; - 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageAddressBarModel.swift; sourceTree = ""; }; + 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageAddressBarModel.swift; sourceTree = ""; }; 378B5887295CF2A4002C0CC0 /* Version.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; 378B5888295CF2A4002C0CC0 /* Common.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Common.xcconfig; sourceTree = ""; }; 378B588B295CF3B9002C0CC0 /* AppTargetsBase.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppTargetsBase.xcconfig; sourceTree = ""; }; @@ -3542,7 +3542,7 @@ 37A6A8F02AFCC988008580A3 /* FaviconsFetcherOnboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsFetcherOnboarding.swift; sourceTree = ""; }; 37A6A8F52AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsFetcherOnboardingViewController.swift; sourceTree = ""; }; 37A803DA27FD69D300052F4C /* DataImportResources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DataImportResources; sourceTree = ""; }; - 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressBarTextFieldView.swift; sourceTree = ""; }; + 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressBarTextFieldView.swift; sourceTree = ""; }; 37AFCE8027DA2CA600471A10 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = ""; }; 37AFCE8427DA2D3900471A10 /* PreferencesSidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesSidebar.swift; sourceTree = ""; }; 37AFCE8627DA334800471A10 /* PreferencesRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesRootView.swift; sourceTree = ""; }; @@ -6598,13 +6598,13 @@ 4BF6961B28BE90E800D402D4 /* HomePage */ = { isa = PBXGroup; children = ( - 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */, + 37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */, 56534DEB29DF251C00121467 /* Mocks */, 4BF6961C28BE911100D402D4 /* RecentlyVisitedSiteModelTests.swift */, 569277C329DEE09D00B633EF /* ContinueSetUpModelTests.swift */, 56D145ED29E6DAD900E3488A /* DataImportProviderTests.swift */, 370270BF2C78EB13002E44E4 /* HomePageSettingsModelTests.swift */, - 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */, + 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */, 37D046A02C7DA9A200AEAA50 /* UserBackgroundImagesManagerTests.swift */, 376731842C7EF97400EB097B /* ColorSchemeLosslessStringConvertibleExtensionTests.swift */, 376731962C7F36AA00EB097B /* UserBackgroundImageTests.swift */, @@ -6964,9 +6964,9 @@ 85589E8527BBB8DD0038AD11 /* Model */ = { isa = PBXGroup; children = ( - 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */, - 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */, - 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */, + 37219B332CBFBBDB00C9D7A8 /* NewTabPageSearchBoxExperiment.swift */, + 37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */, + 37878E542CA332F800CC9EB5 /* HomePageAddressBarModel.swift */, 370C23172C77C47700A80A3E /* HomePageSettings */, 85589E8627BBB8F20038AD11 /* HomePageFavoritesModel.swift */, 85AC7ADC27BEB6EE00FFB69B /* HomePageDefaultBrowserModel.swift */, @@ -8550,7 +8550,7 @@ 85589E9327BFE1E70038AD11 /* FavoritesView.swift */, 56D6A3D529DB2BAB0055215A /* ContinueSetUpView.swift */, 85589E7C27BBB8630038AD11 /* HomePageView.swift */, - 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */, + 37AAA41B2C9CB9C0002A5377 /* AddressBarTextFieldView.swift */, 1DCFBC8929ADF32B00313531 /* BurnerHomePageView.swift */, 85589E7D27BBB8630038AD11 /* HomePageViewController.swift */, 857FFEBF27D239DC00415E7A /* HyperLink.swift */, @@ -10836,7 +10836,7 @@ BB7B5F992C4ED73800BA4AF8 /* BookmarksSearchAndSortMetrics.swift in Sources */, B60D644A2AAF1B7C00B26F50 /* AddressBarTextSelectionNavigation.swift in Sources */, 1D01A3D52B88CF7700FE8150 /* AccessibilityPreferences.swift in Sources */, - 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, + 37219B342CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, 3706FA98293F65D500E42796 /* SecureVaultSorting.swift in Sources */, 1DEDB3652C19934C006B6D1B /* MoreOptionsMenuButton.swift in Sources */, 3767318C2C7F32C500EB097B /* GradientBackground.swift in Sources */, @@ -10888,7 +10888,7 @@ 56A054202C1CA1F5007D8FAB /* OnboardingTabExtension.swift in Sources */, 3706FABA293F65D500E42796 /* BookmarkOutlineViewDataSource.swift in Sources */, 560EB9332C78946F0080DBC8 /* ContextualOnboardingDialogs.swift in Sources */, - 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, + 37878E552CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, 3706FABB293F65D500E42796 /* PasswordManagementBitwardenItemView.swift in Sources */, CD2AB5CB2C8225E70019EB49 /* URLTokenValidator.swift in Sources */, 1D220BF92B86192200F8BBC6 /* PreferencesEmailProtectionView.swift in Sources */, @@ -11236,7 +11236,7 @@ 3706FB8D293F65D500E42796 /* BookmarkTreeController.swift in Sources */, B66260E129AC6EBD00E9E3EE /* HistoryTabExtension.swift in Sources */, 3706FB8E293F65D500E42796 /* FirefoxEncryptionKeyReader.swift in Sources */, - 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, + 37AAA41D2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, 3706FB8F293F65D500E42796 /* BookmarkManagementSplitViewController.swift in Sources */, 316913272BD2B76F0051B46D /* DataBrokerPrerequisitesStatusVerifier.swift in Sources */, 3706FB90293F65D500E42796 /* CookieManagedNotificationContainerView.swift in Sources */, @@ -11539,7 +11539,7 @@ 3706FC3E293F65D500E42796 /* VariantManager.swift in Sources */, 3706FC3F293F65D500E42796 /* ApplicationDockMenu.swift in Sources */, B68412152B694BA10092F66A /* NSObject+performSelector.m in Sources */, - 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, + 37219B382CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, 4B9DB03C2A983B24000927DB /* InvitedToWaitlistView.swift in Sources */, 3706FC40293F65D500E42796 /* SaveIdentityViewController.swift in Sources */, 3706FC41293F65D500E42796 /* FileStore.swift in Sources */, @@ -11560,7 +11560,7 @@ 3706FC49293F65D500E42796 /* RoundedSelectionRowView.swift in Sources */, 4B9DB01E2A983B24000927DB /* Waitlist.swift in Sources */, 3706FC4A293F65D500E42796 /* LocalStatisticsStore.swift in Sources */, - 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, + 37219B3B2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, 3706FC4B293F65D500E42796 /* BackForwardListItem.swift in Sources */, 4B4D60DD2A0C875E00BCD287 /* NetworkProtectionOptionKeyExtension.swift in Sources */, BDBA85912C5D252A00BC54F5 /* VPNFeedbackSender.swift in Sources */, @@ -11882,7 +11882,7 @@ 3706FE2A293F661700E42796 /* SafariVersionReaderTests.swift in Sources */, 3706FE2B293F661700E42796 /* AtbParserTests.swift in Sources */, 3706FE2C293F661700E42796 /* PermissionStoreMock.swift in Sources */, - 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, + 374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, 3706FE2D293F661700E42796 /* ChromiumFaviconsReaderTests.swift in Sources */, 3706FE2E293F661700E42796 /* LocalBookmarkManagerTests.swift in Sources */, 9F180D132B69C665000D695F /* DownloadsTabExtensionMock.swift in Sources */, @@ -11945,7 +11945,7 @@ EEC8EB402982CD550065AA39 /* JSAlertViewModelTests.swift in Sources */, BDA764922BC4E57200D0400C /* MockVPNLocationFormatter.swift in Sources */, 3706FE4B293F661700E42796 /* BookmarksHTMLImporterTests.swift in Sources */, - 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, + 37219B3E2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, 9FA75A3F2BA00E1400DA5FA6 /* BookmarksBarMenuFactoryTests.swift in Sources */, 56D145E929E6BB6300E3488A /* CapturingDataImportProvider.swift in Sources */, 3706FE4C293F661700E42796 /* CSVParserTests.swift in Sources */, @@ -12528,7 +12528,7 @@ 4BA1A6B3258B080A00F6F690 /* EncryptionKeyGeneration.swift in Sources */, 37B11B3928095E6600CBB621 /* TabLazyLoader.swift in Sources */, 4B9DB03B2A983B24000927DB /* InvitedToWaitlistView.swift in Sources */, - 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, + 37219B372CBFBC8200C9D7A8 /* NewTabSearchBoxExperimentPixel.swift in Sources */, 8589063C267BCDC000D23B0D /* SaveCredentialsViewController.swift in Sources */, 4BBE0AA727B9B027003B37A8 /* PopUpButton.swift in Sources */, AABEE6A524AA0A7F0043105B /* SuggestionViewController.swift in Sources */, @@ -12544,7 +12544,7 @@ B6B1E88026D5DA9B0062C350 /* DownloadsViewController.swift in Sources */, BD7090D62C540D5D009EED82 /* EmptyMetadataCollector.swift in Sources */, 85AC3AF725D5DBFD00C7D2AA /* DataExtension.swift in Sources */, - 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, + 37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */, 85480FCF25D1AA22009424E3 /* ConfigurationStore.swift in Sources */, AA3D531B27A2F57E00074EC1 /* Feedback.swift in Sources */, 4B0A63E8289DB58E00378EF7 /* FirefoxFaviconsReader.swift in Sources */, @@ -12940,7 +12940,7 @@ AA4BBA3B25C58FA200C4FB0F /* MainMenu.swift in Sources */, AA585D84248FD31100E9A3E2 /* BrowserTabViewController.swift in Sources */, 85707F22276A32B600DC0649 /* CallToAction.swift in Sources */, - 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, + 37219B352CBFBBE800C9D7A8 /* NewTabPageSearchBoxExperiment.swift in Sources */, B693954B26F04BEB0015B914 /* MouseOverView.swift in Sources */, AAE7527C263B056C00B973F8 /* EncryptedHistoryStore.swift in Sources */, AAE246F32709EF3B00BEEAEE /* FirePopoverCollectionViewItem.swift in Sources */, @@ -13172,8 +13172,8 @@ B65536A62685B82B00085A79 /* Permissions.swift in Sources */, AAC82C60258B6CB5009B6B42 /* TabPreviewWindowController.swift in Sources */, AAC5E4E425D6BA9C007F5990 /* NSSizeExtension.swift in Sources */, - 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, - 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, + 37219B3A2CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift in Sources */, + 37AAA41C2C9CB9C0002A5377 /* AddressBarTextFieldView.swift in Sources */, AA6820EB25503D6A005ED0D5 /* Fire.swift in Sources */, BD6367252C877BE1009DE7A8 /* UpdateUserDriver.swift in Sources */, 3158B1492B0BF73000AF130C /* DBPHomeViewController.swift in Sources */, @@ -13344,7 +13344,7 @@ 9FAD623A2BCFDB32007F3A65 /* WebsiteInfoHelpers.swift in Sources */, 56A054282C201D04007D8FAB /* MockSchemeTask.swift in Sources */, 56A054472C22536A007D8FAB /* CapturingOnboardingActionsManager.swift in Sources */, - 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, + 37219B3D2CC27DB700C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift in Sources */, C13909F42B85FD79001626ED /* AutofillDeleteAllPasswordsExecutorTests.swift in Sources */, 37534C9E28104D9B002621E7 /* TabLazyLoaderTests.swift in Sources */, 37D046992C7D037500AEAA50 /* CapturingUserBackgroundImagesManager.swift in Sources */, @@ -13563,7 +13563,7 @@ B6AE39F129373AF200C37AA4 /* EmptyAttributionRulesProver.swift in Sources */, 4BB99D1126FE1A84001E4761 /* SafariBookmarksReaderTests.swift in Sources */, BBC063E82C5A9E4B007BDC18 /* BookmarkManagementDetailViewModelTests.swift in Sources */, - 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, + 374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */, 1DA860722BE3AE950027B813 /* DockPositionProviderTests.swift in Sources */, 4BBF0925283083EC00EE1418 /* FileSystemDSLTests.swift in Sources */, 4B11060A25903EAC0039B979 /* CoreDataEncryptionTests.swift in Sources */, From b650882bd2ad5bc684bf298ff0ec32cd0b3c1f48 Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:30:05 -0400 Subject: [PATCH 56/58] Update icons & copy (#3461) Task/Issue URL: https://app.asana.com/0/414235014887631/1208183525704010/f Tech Design URL: CC: **Description**: Addresses Ship Review comments. **Steps to test this PR**: 1. Just copy & icon updates so this should be good if CI passes. **Definition of Done**: * [ ] Does this PR satisfy our [Definition of Done](https://app.asana.com/0/1202500774821704/1207634633537039/f)? --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- .../Images/Check.imageset/Check-Color-16.svg | 5 + .../Images/Check.imageset/Contents.json | 12 + .../Contents.json | 2 +- .../Exclamation-High-Color-16.svg | 5 + .../Icon 22.pdf | Bin 1141 -> 0 bytes .../Contents.json | 2 +- .../Document-Color-16.pdf | Bin 1327 -> 0 bytes .../Release-Notes-Color-16.svg | 7 + .../Contents.json | 2 +- .../Exclamation-Color-16-2.svg | 5 + .../Icon 19.pdf | Bin 1140 -> 0 bytes DuckDuckGo/Common/Localizables/UserText.swift | 9 +- DuckDuckGo/Localizable.xcstrings | 526 ++++++++++++++++-- .../View/PreferencesAboutView.swift | 23 +- .../Updates/ReleaseNotesTabExtension.swift | 7 +- DuckDuckGo/Updates/UpdateController.swift | 14 +- .../PopoverMessageView.swift | 2 +- 17 files changed, 554 insertions(+), 67 deletions(-) create mode 100644 DuckDuckGo/Assets.xcassets/Images/Check.imageset/Check-Color-16.svg create mode 100644 DuckDuckGo/Assets.xcassets/Images/Check.imageset/Contents.json create mode 100644 DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Exclamation-High-Color-16.svg delete mode 100644 DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Icon 22.pdf delete mode 100644 DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Document-Color-16.pdf create mode 100644 DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Release-Notes-Color-16.svg create mode 100644 DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Exclamation-Color-16-2.svg delete mode 100644 DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Icon 19.pdf diff --git a/DuckDuckGo/Assets.xcassets/Images/Check.imageset/Check-Color-16.svg b/DuckDuckGo/Assets.xcassets/Images/Check.imageset/Check-Color-16.svg new file mode 100644 index 0000000000..644b9a9c61 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Images/Check.imageset/Check-Color-16.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/DuckDuckGo/Assets.xcassets/Images/Check.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Images/Check.imageset/Contents.json new file mode 100644 index 0000000000..14f73a2877 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Images/Check.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Check-Color-16.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Contents.json index c4a79c7d54..f1d879d17e 100644 --- a/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Icon 22.pdf", + "filename" : "Exclamation-High-Color-16.svg", "idiom" : "universal" } ], diff --git a/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Exclamation-High-Color-16.svg b/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Exclamation-High-Color-16.svg new file mode 100644 index 0000000000..67505545b4 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Exclamation-High-Color-16.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Icon 22.pdf b/DuckDuckGo/Assets.xcassets/Images/CriticalUpdateNotificationInfo.imageset/Icon 22.pdf deleted file mode 100644 index 97d2e7db8964ba0eb18a8870a48a8a28fe11e1fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1141 zcmY!laBvK;I`dFTEr~!5FAK2q*+Jp}3?dH8Gc~f^qJYvwnvS1X|wLbs4m* z$D*%ad$Q|BAn(SO;~Li%7j6@qp2a#Rxv0CNL1NMKuPHUJ-d*xN&UmiOZ|O6O%KT!U zx^o+p8&0HGddg^ZrEzt!k^la=1sfMe zE^k@cHHp)>O7m9B+dp$><>ci_ZrHTcB7RlGG!>=qV&}GrEO$9361zrGqxhQn(y45l zz6#$z7L(pSv2~M2VA%76JK2TK?0!F`XL;h%#+<)Lwi~{C{o(z`>b2U3TMk^Z;hJfG zeuMVkBac27={{np-7mU3$2+zly>3D7=ORAA3D!L})-i!kzGm6XE>oSfKQXVos=-F( zLYVKx3#Bc$-))gPzaX~rU(In(4TI#E-kwl~zcEf;0(y@QZ`vYxecmeln-^V{xII6` zaFbe4T{=b64#oYmjd+~Vz|E;lJH@HVTNK7 z$b-dj3qcNpc-%R$BrzvH9aU>ZQED2Ofr2>~#QPvp!OYau*i->13%pZ!Vo?d$JC;TUT&k+B{%!zjyNZVZ diff --git a/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Contents.json index 552be25080..e224dcfc1c 100644 --- a/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Document-Color-16.pdf", + "filename" : "Release-Notes-Color-16.svg", "idiom" : "universal" } ], diff --git a/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Document-Color-16.pdf b/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Document-Color-16.pdf deleted file mode 100644 index 5db4a4627fe956be30544b99cfa3d54452b4a537..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1327 zcmY!laBvK;I`dFTEr~!5FAK2q*+Jp}3?dH8Gc~f^ll-={ylbfjz&qzxA{f{=T?v zeZvvSA1#3b9Y26N6c{}?*KTBE-U<8~(|j zf45y&zAg3;%lpn3-TNLe3aDAgT|agG!Ow~!@7C4w=0-oSIZf?wRkGbyHqmHq%!U<* zMdzEQU+vvv^;IaFvsC$NxbI45i{GCdtq|H;TsWo|2P~sBnR%7 zFj6$MZ%I6M_V0> zVcVVS|5tHHD6c9|n$z?k`lM}y-uDB3jJKWkx~sIQ=M;udeyOS+d57&?tjG~%%x)1#*M3tL0YHBruE;!QF>#5?F_-Q|-O{cT*FJl) zW?#i2ftMGyX7MfXZeBmxGG_h+8NSMjy|$n2QWyGn?V8~*Nubl{nNU-=tD~ z1-=Ovj$WU&q;KIg4b7w{i~Uz#NfURSEl_^u<-T22(aon9`^@#q3=2u;>9)F;)jpTm z#VkjAYbmGyjIAzCdR+z2EfUfz5>_*GTNRi-;=Jv#TmNHA?(8=z-O>3Bjhk8DTv-(G zUQ{9S%Ir7WPAi48N%YMWk=c+to%36hFN0~8PagAkJuZa@oZp!i{MeCl;Ye4@PqtP4 z{tmAMjK%J^2MK7pt8g)2B$i8jzqK(`R$y}fy}5bT=Eb~u(`KLh zVa1#IJE8Y&!IU=1(qpMr2?_d6@hX?s2{$ycZws9&;`Pz(Tv+#Kr(fK+>L&aLC2nZy zge7xOx`iZvGgC`=;x~bd0yBlaV_sf_{hGsA!1Pf+TP-=00X;E@&u>vR?g7Ob2OF8G~m89kYZ3N{(m|#F+5l~FQ z6e@(2Q$a%T9IWr1nF4gG0>}qJ3NU+|^Gktx4Kdtb49T&uz%WBG3FN_IxP>5xK|Jo9 zSdy5NpN^`vq9`?u%Rs@L3*vncsbFSmYHX?i6ovvr0|hWkArCHO40I<5AcV}#%`t>5 zEzyN6&4FQqqS4U61n7LIP)Si@W=?7m7bx~TU4X%%QJkNfs;QunnW6~^6#bz5{1OF_ hhrt1?ADmg03UocV_)07)0ei>L(7=>SRn^ts4FDq#*%JT& diff --git a/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Release-Notes-Color-16.svg b/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Release-Notes-Color-16.svg new file mode 100644 index 0000000000..440fa7505c --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Images/ReleaseNotesIndicator.imageset/Release-Notes-Color-16.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Contents.json index 8976fe4303..a68acc82ac 100644 --- a/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Icon 19.pdf", + "filename" : "Exclamation-Color-16-2.svg", "idiom" : "universal" } ], diff --git a/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Exclamation-Color-16-2.svg b/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Exclamation-Color-16-2.svg new file mode 100644 index 0000000000..39ad1e8ec7 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Exclamation-Color-16-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Icon 19.pdf b/DuckDuckGo/Assets.xcassets/Images/UpdateNotificationInfo.imageset/Icon 19.pdf deleted file mode 100644 index 64eb71ba5fbc2bd1f8383136d02f8e19254f4734..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1140 zcmY!laBvK;I`dFTEr~!5FAK2q*+Jp}3?dH8Gc~f^lxhX+LH|o;}aCzx5p1bn;mx ztID|p>Mm;?va}a^)!0wYG2+W=ZIGFDbE9pU%<}U7-RHmWsjl2LUoP(I+?uDGe+5*< zy_z+RFRznr_F=Q8nvl~njSY-*e7gnz7u#vR{T2Sg@pxm*^Q)~d-Y)t2X6?n*?R-2D zLJn%$za}g1FbPyx?DZzXS?-s&DUX1bcBep@L-?90Ctvx#z3OTEf8XIBM}-2c_grJW zv~0&3P6f-!Yu3s3-3{fk(6WAgRnJ?(L(TEz#obfF7OrS~T;{>aH0y;QTjBIolcH5T z0+|=8unRhFF>+aPDe+s^w@b!vA|{({zIw5x<9Asgd-KmPcTX@Z4B%C6k-Z?QohslH zbjh&E)v&>5=9_2!eGe|VG#35)dp6!p=gcy{=SRF=cVx=%7LNf7`cg*SC-G??FTV}E+;hA>fjg1Lw7M(7U*r~Pa zD!;*>?hA8y@-kPKcwF%d-ZSgK0h30d@QWAPonQXne{22z|DFGC)YpXEe`!`Sw}|nr zfYY{L51wxio8~RJD)pJA!!FBjkIPK2_fDD`va{`)ykezCfUoZg#n&F&Ub*z9Pgt|$ z)r9M6^^9BUy(&RT1RBS%!~%+aNFp*ZH-;x66SycaY3V!W<>i+YD@4aa6HZ8FL8^j& zKw>&9CHbbNWF|W0S13d)80Z;*0fJ#@1`|TCU?v5n7U!21C8riEfD$t(6@n6`bADb) zY97!=P)ddg1|${%#S~1TLP+TyBm~a~`rer-K&L8zd=R7nv&T8V6sXq_!~MmObPo#* zGZd3R9xR4i2yz(2 Date: Tue, 29 Oct 2024 10:33:40 -0400 Subject: [PATCH 57/58] Update BSK --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++-- .../xcshareddata/swiftpm/Package.resolved | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 3ee547c432..0ea18d63a4 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -14665,8 +14665,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 201.0.0; + kind = revision; + revision = 8f0f19bcaccf60a7bbc4166f363b4ddca6aa10b7; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 51a522e596..2f832bc554 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "e5946eee6af859690cc1cc5e51daef3c8368981b", - "version" : "201.0.0" + "revision" : "8f0f19bcaccf60a7bbc4166f363b4ddca6aa10b7" } }, { @@ -41,8 +40,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "b74549bd869fdecc16fad851f2f608b1724764df", - "version" : "6.25.0" + "revision" : "f4b1e50b4719de107dfd7dd6c46899f7f11d659a" } }, { From ded664d1f3482470de1d25f628a474f087ce8296 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Tue, 29 Oct 2024 10:37:48 -0400 Subject: [PATCH 58/58] Change updates log to default instead of debug --- DuckDuckGo/Updates/UpdateController.swift | 18 +++++++++--------- .../Updates/UpdateNotificationPresenter.swift | 2 +- DuckDuckGo/Updates/UpdateUserDriver.swift | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/DuckDuckGo/Updates/UpdateController.swift b/DuckDuckGo/Updates/UpdateController.swift index d1ab3f1ab2..f044522f63 100644 --- a/DuckDuckGo/Updates/UpdateController.swift +++ b/DuckDuckGo/Updates/UpdateController.swift @@ -97,7 +97,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { @UserDefaultsWrapper(key: .automaticUpdates, defaultValue: true) var areAutomaticUpdatesEnabled: Bool { didSet { - Logger.updates.debug("areAutomaticUpdatesEnabled: \(self.areAutomaticUpdatesEnabled)") + Logger.updates.log("areAutomaticUpdatesEnabled: \(self.areAutomaticUpdatesEnabled)") if oldValue != areAutomaticUpdatesEnabled { userDriver?.cancelAndDismissCurrentUpdate() try? configureUpdater() @@ -145,7 +145,7 @@ final class UpdateController: NSObject, UpdateControllerProtocol { func checkForUpdateIfNeeded() { guard let updater, !updater.sessionInProgress else { return } - Logger.updates.debug("Checking for updates") + Logger.updates.log("Checking for updates") updater.checkForUpdates() } @@ -242,7 +242,7 @@ extension UpdateController: SPUUpdaterDelegate { } func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) { - Logger.updates.debug("Updater did find valid update: \(item.displayVersionString)(\(item.versionString))") + Logger.updates.log("Updater did find valid update: \(item.displayVersionString)(\(item.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidFindUpdate)) cachedUpdateResult = UpdateCheckResult(item: item, isInstalled: false) } @@ -251,31 +251,31 @@ extension UpdateController: SPUUpdaterDelegate { let nsError = error as NSError guard let item = nsError.userInfo["SULatestAppcastItemFound"] as? SUAppcastItem else { return } - Logger.updates.debug("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") + Logger.updates.log("Updater did not find update: \(String(describing: item.displayVersionString))(\(String(describing: item.versionString)))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidNotFindUpdate, error: error)) cachedUpdateResult = UpdateCheckResult(item: item, isInstalled: true) } func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) { - Logger.updates.debug("Updater did download update: \(item.displayVersionString)(\(item.versionString))") + Logger.updates.log("Updater did download update: \(item.displayVersionString)(\(item.versionString))") PixelKit.fire(DebugEvent(GeneralPixel.updaterDidDownloadUpdate)) } func updater(_ updater: SPUUpdater, didExtractUpdate item: SUAppcastItem) { - Logger.updates.debug("Updater did extract update: \(item.displayVersionString)(\(item.versionString))") + Logger.updates.log("Updater did extract update: \(item.displayVersionString)(\(item.versionString))") } func updater(_ updater: SPUUpdater, willInstallUpdate item: SUAppcastItem) { - Logger.updates.debug("Updater will install update: \(item.displayVersionString)(\(item.versionString))") + Logger.updates.log("Updater will install update: \(item.displayVersionString)(\(item.versionString))") } func updater(_ updater: SPUUpdater, didFinishUpdateCycleFor updateCheck: SPUUpdateCheck, error: (any Error)?) { if error == nil { - Logger.updates.debug("Updater did finish update cycle") + Logger.updates.log("Updater did finish update cycle") updateProgress = .updateCycleDone } else { - Logger.updates.debug("Updater did finish update cycle with error") + Logger.updates.log("Updater did finish update cycle with error") } } diff --git a/DuckDuckGo/Updates/UpdateNotificationPresenter.swift b/DuckDuckGo/Updates/UpdateNotificationPresenter.swift index 310abddd98..bea8259c16 100644 --- a/DuckDuckGo/Updates/UpdateNotificationPresenter.swift +++ b/DuckDuckGo/Updates/UpdateNotificationPresenter.swift @@ -26,7 +26,7 @@ final class UpdateNotificationPresenter { static let presentationTimeInterval: TimeInterval = 10 func showUpdateNotification(icon: NSImage, text: String, buttonText: String? = nil, presentMultiline: Bool = false) { - Logger.updates.debug("Notification presented: \(text)") + Logger.updates.log("Notification presented: \(text)") DispatchQueue.main.async { guard let windowController = WindowControllersManager.shared.lastKeyMainWindowController ?? WindowControllersManager.shared.mainWindowControllers.last, diff --git a/DuckDuckGo/Updates/UpdateUserDriver.swift b/DuckDuckGo/Updates/UpdateUserDriver.swift index 067197fc79..960825f46e 100644 --- a/DuckDuckGo/Updates/UpdateUserDriver.swift +++ b/DuckDuckGo/Updates/UpdateUserDriver.swift @@ -119,7 +119,7 @@ final class UpdateUserDriver: NSObject, SPUUserDriver { } func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) { - Logger.updates.debug("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") + Logger.updates.log("Updater started performing the update check. (isInternalUser: \(self.internalUserDecider.isInternalUser)") updateProgress = .updateCycleDidStart }