diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index dd74241154..adbf8de7d3 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -2763,6 +2763,7 @@ D9708B5C29E4CCCB004306FA /* OWSDeviceManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9708B5B29E4CCCB004306FA /* OWSDeviceManagerTest.swift */; }; D972E2FF2C542BCD001D7337 /* Backup.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D972E2FE2C542BCD001D7337 /* Backup.pb.swift */; }; D97411BB28D277C900BB1865 /* GroupManager+GenericGroupUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97411BA28D277C900BB1865 /* GroupManager+GenericGroupUpdates.swift */; }; + D977D2042F3DB151005FEFD8 /* OWSPercentFormatStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D977D2032F3DB151005FEFD8 /* OWSPercentFormatStyle.swift */; }; D9791BB92EAA84830016AA5A /* SheetDisplayableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9791BB82EAA847E0016AA5A /* SheetDisplayableError.swift */; }; D9791BC42EAADF010016AA5A /* HTTPResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9791BC32EAADEFD0016AA5A /* HTTPResponse.swift */; }; D97992A12D9E55F20080A4F5 /* CurrencyFormatterTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D97992A02D9E55E10080A4F5 /* CurrencyFormatterTest.swift */; }; @@ -6935,6 +6936,7 @@ D9708B5B29E4CCCB004306FA /* OWSDeviceManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSDeviceManagerTest.swift; sourceTree = ""; }; D972E2FE2C542BCD001D7337 /* Backup.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Backup.pb.swift; sourceTree = ""; }; D97411BA28D277C900BB1865 /* GroupManager+GenericGroupUpdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GroupManager+GenericGroupUpdates.swift"; sourceTree = ""; }; + D977D2032F3DB151005FEFD8 /* OWSPercentFormatStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSPercentFormatStyle.swift; sourceTree = ""; }; D9791BB82EAA847E0016AA5A /* SheetDisplayableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetDisplayableError.swift; sourceTree = ""; }; D9791BC32EAADEFD0016AA5A /* HTTPResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPResponse.swift; sourceTree = ""; }; D97992A02D9E55E10080A4F5 /* CurrencyFormatterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyFormatterTest.swift; sourceTree = ""; }; @@ -8792,6 +8794,7 @@ 76E08F3C2AE8761D00032BC9 /* ContactSharing */, 34A95521271B510400B05242 /* ConversationView */, 34330A581E7875FB00DF2FB9 /* Fonts */, + D977D2022F3DB119005FEFD8 /* FormatStyles */, 34A95551271B510400B05242 /* ImageEditor */, 34A95514271B510400B05242 /* LinkPreview */, D9392DDE2D88AA4900728C01 /* Media */, @@ -11608,8 +11611,6 @@ 05104E392C8B540C00F8851F /* AccessibleLayoutMetric.swift */, 05104D172C8A151100F8851F /* AsyncViewTask.swift */, 05594CCF2C98A00200CCBFF6 /* HostingController.swift */, - D9B2E1172E748DFB00A823E4 /* OWSByteCountFormatStyle.swift */, - D9C5634D2EB438300001626A /* OWSByteCountFormatStyleTest.swift */, D9DE35002DF0B886005099D7 /* ScrollableContentPinnedFooterView.swift */, 05594CCD2C989F1900CCBFF6 /* ScrollableWhenCompact.swift */, 0510F69D2C91EB2800FA3FDE /* ScrollBounceBehaviorIfAvailable.swift */, @@ -13260,6 +13261,16 @@ path = SyncMessages; sourceTree = ""; }; + D977D2022F3DB119005FEFD8 /* FormatStyles */ = { + isa = PBXGroup; + children = ( + D9B2E1172E748DFB00A823E4 /* OWSByteCountFormatStyle.swift */, + D9C5634D2EB438300001626A /* OWSByteCountFormatStyleTest.swift */, + D977D2032F3DB151005FEFD8 /* OWSPercentFormatStyle.swift */, + ); + path = FormatStyles; + sourceTree = ""; + }; D9791BB72EAA84210016AA5A /* ActionSheets */ = { isa = PBXGroup; children = ( @@ -17426,6 +17437,7 @@ 3402AA71271D9E180084CBAE /* OWSLayerView.swift in Sources */, 3402AAB1271D9E180084CBAE /* OWSNavigationBar.swift in Sources */, 665EF86D290C385B00F490D2 /* OWSNavigationController.swift in Sources */, + D977D2042F3DB151005FEFD8 /* OWSPercentFormatStyle.swift in Sources */, 3402AA70271D9E180084CBAE /* OWSStackView.swift in Sources */, 765283AE2A00D8B4004583A9 /* OWSTableContents.swift in Sources */, 765283B22A00D8EC004583A9 /* OWSTableItem.swift in Sources */, diff --git a/Signal/AppLaunch/LoadingViewController.swift b/Signal/AppLaunch/LoadingViewController.swift index bab81e872f..73862517b7 100644 --- a/Signal/AppLaunch/LoadingViewController.swift +++ b/Signal/AppLaunch/LoadingViewController.swift @@ -228,7 +228,7 @@ class LoadingViewController: UIViewController { "LINK_NEW_DEVICE_SYNC_PROGRESS_PERCENT", comment: "On a progress modal indicating the percent complete the sync process is. Embeds {{ formatted percentage }}", ), - percentComplete.formatted(.percent.precision(.fractionLength(0))), + percentComplete.formatted(.owsPercent()), ) unitCountLabel.text = "\(unitCountCompleted.formatted(.number)) / \(unitCountToComplete.formatted(.number))" diff --git a/Signal/Backups/BackupSettingsViewController.swift b/Signal/Backups/BackupSettingsViewController.swift index 2f254b5543..76a1dca23c 100644 --- a/Signal/Backups/BackupSettingsViewController.swift +++ b/Signal/Backups/BackupSettingsViewController.swift @@ -2092,7 +2092,7 @@ private struct BackupExportProgressView: View { "BACKUP_SETTINGS_BACKUP_EXPORT_PROGRESS_DESCRIPTION_PREPARING_BACKUP", comment: "Description for a progress bar tracking the preparation of a Backup. Embeds 1:{{ the percentage completed preformatted as a percent, e.g. 10% }}.", ), - percentComplete.formatted(.percent.precision(.fractionLength(0))), + percentComplete.formatted(.owsPercent()), ), ) @@ -2390,7 +2390,7 @@ private struct BackupAttachmentDownloadProgressView: View { ), latestDownloadUpdate.bytesDownloaded.formatted(.owsByteCount()), latestDownloadUpdate.totalBytesToDownload.formatted(.owsByteCount()), - latestDownloadUpdate.percentageDownloaded.formatted(.percent.precision(.fractionLength(0))), + latestDownloadUpdate.percentageDownloaded.formatted(.owsPercent()), ) case .pausedLowBattery: OWSLocalizedString( @@ -2523,7 +2523,7 @@ private struct BackupAttachmentUploadProgressView: View { ), bytesUploaded.formatted(.owsByteCount()), totalBytesToUpload.formatted(.owsByteCount()), - percentageUploaded.formatted(.percent.precision(.fractionLength(0))), + percentageUploaded.formatted(.owsPercent()), ) case .pausedLowBattery: return OWSLocalizedString( diff --git a/Signal/DeviceTransfer/DeviceTransferStatusViewController.swift b/Signal/DeviceTransfer/DeviceTransferStatusViewController.swift index 7d5f9faf71..f0b1f5cc4c 100644 --- a/Signal/DeviceTransfer/DeviceTransferStatusViewController.swift +++ b/Signal/DeviceTransfer/DeviceTransferStatusViewController.swift @@ -149,7 +149,7 @@ struct TransferStatusView: View { .foregroundStyle(Color.Signal.secondaryLabel) Spacer() - Text("\(progress.formatted(.percent.precision(.fractionLength(0))))") + Text("\(progress.formatted(.owsPercent()))") .font(.body.monospacedDigit()) .padding(.bottom, 12) LinearProgressView(progress: progress) diff --git a/Signal/Provisioning/UserInterface/LinkAndSyncProvisioningProgressViewController.swift b/Signal/Provisioning/UserInterface/LinkAndSyncProvisioningProgressViewController.swift index 9a72444a06..592b3b297e 100644 --- a/Signal/Provisioning/UserInterface/LinkAndSyncProvisioningProgressViewController.swift +++ b/Signal/Provisioning/UserInterface/LinkAndSyncProvisioningProgressViewController.swift @@ -215,7 +215,7 @@ struct LinkAndSyncProvisioningProgressView: View { ), downloadProgress.downloadedByteCount.formatted(byteCountFormat), downloadProgress.totalByteCount.formatted(byteCountFormat), - progressToShow.formatted(.percent.precision(.fractionLength(0))), + progressToShow.formatted(.owsPercent()), ) } else if !viewModel.isFinalizing { String( @@ -223,7 +223,7 @@ struct LinkAndSyncProvisioningProgressView: View { "LINK_NEW_DEVICE_SYNC_PROGRESS_PERCENT", comment: "On a progress modal indicating the percent complete the sync process is. Embeds {{ formatted percentage }}", ), - progressToShow.formatted(.percent.precision(.fractionLength(0))), + progressToShow.formatted(.owsPercent()), ) } else { OWSLocalizedString( diff --git a/Signal/src/ViewControllers/AppSettings/Linked Devices/BackupProgressModal.swift b/Signal/src/ViewControllers/AppSettings/Linked Devices/BackupProgressModal.swift index dc07c77eac..325442a701 100644 --- a/Signal/src/ViewControllers/AppSettings/Linked Devices/BackupProgressModal.swift +++ b/Signal/src/ViewControllers/AppSettings/Linked Devices/BackupProgressModal.swift @@ -247,7 +247,7 @@ struct BackupProgressView: View { ), downloadProgress.downloadedByteCount.formatted(byteCountFormat), downloadProgress.totalByteCount.formatted(byteCountFormat), - progressToShow.formatted(.percent.precision(.fractionLength(0))), + progressToShow.formatted(.owsPercent()), ) } else { percentCompleteString @@ -261,7 +261,7 @@ struct BackupProgressView: View { "LINK_NEW_DEVICE_SYNC_PROGRESS_PERCENT", comment: "On a progress modal indicating the percent complete the sync process is. Embeds {{ formatted percentage }}", ), - progressToShow.formatted(.percent.precision(.fractionLength(0))), + progressToShow.formatted(.owsPercent()), ) } diff --git a/SignalUI/SwiftUIExtensions/OWSByteCountFormatStyle.swift b/SignalUI/FormatStyles/OWSByteCountFormatStyle.swift similarity index 100% rename from SignalUI/SwiftUIExtensions/OWSByteCountFormatStyle.swift rename to SignalUI/FormatStyles/OWSByteCountFormatStyle.swift diff --git a/SignalUI/SwiftUIExtensions/OWSByteCountFormatStyleTest.swift b/SignalUI/FormatStyles/OWSByteCountFormatStyleTest.swift similarity index 100% rename from SignalUI/SwiftUIExtensions/OWSByteCountFormatStyleTest.swift rename to SignalUI/FormatStyles/OWSByteCountFormatStyleTest.swift diff --git a/SignalUI/FormatStyles/OWSPercentFormatStyle.swift b/SignalUI/FormatStyles/OWSPercentFormatStyle.swift new file mode 100644 index 0000000000..c5fc9a4f6a --- /dev/null +++ b/SignalUI/FormatStyles/OWSPercentFormatStyle.swift @@ -0,0 +1,24 @@ +// +// Copyright 2025 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +public struct OWSPercentFormatStyle: FormatStyle { + private let fractionDigits: Int + + public init(fractionDigits: Int = 0) { + self.fractionDigits = fractionDigits + } + + public func format(_ percent: Float) -> String { + return percent.formatted(.percent.precision(.fractionLength(fractionDigits))) + } +} + +extension FormatStyle where Self == OWSPercentFormatStyle { + public static func owsPercent( + fractionDigits: Int = 0, + ) -> OWSPercentFormatStyle { + return OWSPercentFormatStyle(fractionDigits: fractionDigits) + } +}