@@ -99,7 +99,25 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
9999 }
100100 }
101101
102- lazy var outputQualityLevel : ImageQualityLevel = SSKEnvironment . shared. databaseStorageRef. read { . resolvedQuality( tx: $0) }
102+ lazy var outputQualityLevel : ImageQualityLevel = {
103+ SSKEnvironment . shared. databaseStorageRef. read { tx in
104+ // Check if there's a thread-specific quality setting
105+ if let thread = self . thread {
106+ let imageQualityStore = ImageQualitySettingStore ( )
107+ let resolvedLevel = imageQualityStore. resolvedQualityLevel ( for: thread, tx: tx)
108+ // Enable metadata preservation for original quality mode
109+ if resolvedLevel == . original {
110+ self . attachmentApprovalItemCollection. attachmentApprovalItems. forEach { item in
111+ item. attachment. preserveMetadata = true
112+ }
113+ }
114+ return resolvedLevel
115+ }
116+ return . resolvedQuality( tx: tx)
117+ }
118+ } ( )
119+
120+ private let thread : TSThread ?
103121
104122 public weak var approvalDelegate : AttachmentApprovalViewControllerDelegate ?
105123 public weak var approvalDataSource : AttachmentApprovalViewControllerDataSource ?
@@ -125,10 +143,11 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
125143 }
126144 }
127145
128- public init ( options: AttachmentApprovalViewControllerOptions , attachmentApprovalItems: [ AttachmentApprovalItem ] ) {
146+ public init ( options: AttachmentApprovalViewControllerOptions , attachmentApprovalItems: [ AttachmentApprovalItem ] , thread : TSThread ? = nil ) {
129147 assert ( attachmentApprovalItems. count > 0 )
130148
131149 self . receivedOptions = options
150+ self . thread = thread
132151
133152 let pageOptions : [ UIPageViewController . OptionsKey : Any ] = [ . interPageSpacing: kSpacingBetweenItems]
134153 super. init ( transitionStyle: . scroll,
@@ -168,7 +187,8 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
168187 hasQuotedReplyDraft: Bool ,
169188 approvalDelegate: AttachmentApprovalViewControllerDelegate ,
170189 approvalDataSource: AttachmentApprovalViewControllerDataSource ,
171- stickerSheetDelegate: StickerPickerSheetDelegate ?
190+ stickerSheetDelegate: StickerPickerSheetDelegate ? ,
191+ thread: TSThread ? = nil
172192 ) -> OWSNavigationController {
173193
174194 let attachmentApprovalItems = attachments. map { AttachmentApprovalItem ( attachment: $0, canSave: false ) }
@@ -177,7 +197,7 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
177197 if hasQuotedReplyDraft {
178198 options. insert ( . disallowViewOnce)
179199 }
180- let vc = AttachmentApprovalViewController ( options: options, attachmentApprovalItems: attachmentApprovalItems)
200+ let vc = AttachmentApprovalViewController ( options: options, attachmentApprovalItems: attachmentApprovalItems, thread : thread )
181201 // The data source needs to be set before the message body because it is needed to hydrate mentions.
182202 vc. approvalDataSource = approvalDataSource
183203 vc. setMessageBody ( initialMessageBody, txProvider: DependenciesBridge . shared. db. readTxProvider)
@@ -1064,9 +1084,25 @@ extension AttachmentApprovalViewController {
10641084 let localPhoneNumber = tsAccountManager. localIdentifiersWithMaybeSneakyTransaction? . phoneNumber
10651085 let standardQualityLevel = ImageQualityLevel . remoteDefault ( localPhoneNumber: localPhoneNumber)
10661086
1087+ // Determine if Original quality option should be shown
1088+ let threadSupportsOriginal : Bool
1089+ if let thread = thread {
1090+ // Single-recipient: show Original only if enabled for this specific thread
1091+ threadSupportsOriginal = SSKEnvironment . shared. databaseStorageRef. read { tx in
1092+ let imageQualityStore = ImageQualitySettingStore ( )
1093+ let setting = imageQualityStore. fetchSetting ( for: thread, tx: tx)
1094+ return setting == . original
1095+ }
1096+ } else {
1097+ // Multi-recipient: always show Original option
1098+ // It will apply to threads that have it enabled, others will get High quality
1099+ threadSupportsOriginal = true
1100+ }
1101+
10671102 let selectionControl = MediaQualitySelectionControl (
10681103 standardQualityLevel: standardQualityLevel,
1069- currentQualityLevel: outputQualityLevel
1104+ currentQualityLevel: outputQualityLevel,
1105+ supportsOriginal: threadSupportsOriginal
10701106 )
10711107 selectionControl. callback = { [ weak self, weak actionSheet] qualityLevel in
10721108 self ? . outputQualityLevel = qualityLevel
@@ -1118,14 +1154,24 @@ extension AttachmentApprovalViewController {
11181154 )
11191155 )
11201156
1157+ private let buttonQualityOriginal = MediaQualityButton (
1158+ title: ImageQualityLevel . original. localizedString,
1159+ subtitle: OWSLocalizedString (
1160+ " ATTACHMENT_APPROVAL_MEDIA_QUALITY_ORIGINAL_OPTION_SUBTITLE " ,
1161+ comment: " Subtitle for the 'original' option for media quality. "
1162+ )
1163+ )
1164+
11211165 private let standardQualityLevel : ImageQualityLevel
1166+ private let supportsOriginal : Bool
11221167 private( set) var qualityLevel : ImageQualityLevel
11231168
11241169 var callback : ( ( ImageQualityLevel ) -> Void ) ?
11251170
1126- init ( standardQualityLevel: ImageQualityLevel , currentQualityLevel: ImageQualityLevel ) {
1171+ init ( standardQualityLevel: ImageQualityLevel , currentQualityLevel: ImageQualityLevel , supportsOriginal : Bool = false ) {
11271172 self . standardQualityLevel = standardQualityLevel
11281173 self . qualityLevel = currentQualityLevel
1174+ self . supportsOriginal = supportsOriginal
11291175
11301176 self . buttonQualityStandard = MediaQualityButton (
11311177 title: standardQualityLevel. localizedString,
@@ -1143,14 +1189,26 @@ extension AttachmentApprovalViewController {
11431189 addSubview ( buttonQualityStandard)
11441190 buttonQualityStandard. autoPinEdgesToSuperviewEdges ( with: . zero, excludingEdge: . trailing)
11451191
1146- buttonQualityHigh. block = { [ weak self] in
1147- self ? . didSelectQualityLevel ( . high)
1192+ // Two-button layout: Always show Standard + either Original or High
1193+ if supportsOriginal {
1194+ // Standard | Original
1195+ buttonQualityOriginal. block = { [ weak self] in
1196+ self ? . didSelectQualityLevel ( . original)
1197+ }
1198+ addSubview ( buttonQualityOriginal)
1199+ buttonQualityOriginal. autoPinEdgesToSuperviewEdges ( with: . zero, excludingEdge: . leading)
1200+ buttonQualityOriginal. autoPinEdge ( . leading, to: . trailing, of: buttonQualityStandard, withOffset: 20 )
1201+ buttonQualityOriginal. autoPinWidth ( toWidthOf: buttonQualityStandard)
1202+ } else {
1203+ // Standard | High
1204+ buttonQualityHigh. block = { [ weak self] in
1205+ self ? . didSelectQualityLevel ( . high)
1206+ }
1207+ addSubview ( buttonQualityHigh)
1208+ buttonQualityHigh. autoPinEdgesToSuperviewEdges ( with: . zero, excludingEdge: . leading)
1209+ buttonQualityHigh. autoPinEdge ( . leading, to: . trailing, of: buttonQualityStandard, withOffset: 20 )
1210+ buttonQualityHigh. autoPinWidth ( toWidthOf: buttonQualityStandard)
11481211 }
1149- addSubview ( buttonQualityHigh)
1150- buttonQualityHigh. autoPinEdgesToSuperviewEdges ( with: . zero, excludingEdge: . leading)
1151-
1152- buttonQualityHigh. autoPinWidth ( toWidthOf: buttonQualityStandard)
1153- buttonQualityHigh. autoPinEdge ( . leading, to: . trailing, of: buttonQualityStandard, withOffset: 20 )
11541212
11551213 updateButtonAppearance ( )
11561214 }
@@ -1168,6 +1226,7 @@ extension AttachmentApprovalViewController {
11681226 private func updateButtonAppearance( ) {
11691227 buttonQualityStandard. isSelected = qualityLevel == standardQualityLevel
11701228 buttonQualityHigh. isSelected = qualityLevel == . high
1229+ buttonQualityOriginal. isSelected = qualityLevel == . original
11711230 }
11721231
11731232 private class MediaQualityButton : OWSButton {
@@ -1200,6 +1259,11 @@ extension AttachmentApprovalViewController {
12001259 topLabel. text = title
12011260 bottomLabel. text = subtitle
12021261
1262+ // Parent MediaQualitySelectionControl handles accessibility
1263+ isAccessibilityElement = false
1264+ topLabel. isAccessibilityElement = false
1265+ bottomLabel. isAccessibilityElement = false
1266+
12031267 let stackView = UIStackView ( arrangedSubviews: [ topLabel, bottomLabel ] )
12041268 stackView. alignment = . center
12051269 stackView. axis = . vertical
@@ -1248,8 +1312,15 @@ extension AttachmentApprovalViewController {
12481312
12491313 override var accessibilityValue : String ? {
12501314 get {
1251- let selectedButton = qualityLevel == . high ? buttonQualityHigh : buttonQualityStandard
1252- return [ selectedButton. topLabel, selectedButton. bottomLabel ] . compactMap { $0. text } . joined ( separator: " , " )
1315+ let selectedButton : MediaQualityButton
1316+ if qualityLevel == . original {
1317+ selectedButton = buttonQualityOriginal
1318+ } else if qualityLevel == . high {
1319+ selectedButton = buttonQualityHigh
1320+ } else {
1321+ selectedButton = buttonQualityStandard
1322+ }
1323+ return [ selectedButton. topLabel, selectedButton. bottomLabel ] . compactMap { $0. text } . joined ( separator: " , " )
12531324 }
12541325 set { super. accessibilityValue = newValue }
12551326 }
@@ -1266,13 +1337,15 @@ extension AttachmentApprovalViewController {
12661337
12671338 override func accessibilityIncrement( ) {
12681339 if qualityLevel == standardQualityLevel {
1269- qualityLevel = . high
1340+ // Increment to either Original or High depending on what's available
1341+ qualityLevel = supportsOriginal ? . original : . high
12701342 updateButtonAppearance ( )
12711343 }
12721344 }
12731345
12741346 override func accessibilityDecrement( ) {
1275- if qualityLevel == . high {
1347+ if qualityLevel == . high || qualityLevel == . original {
1348+ // Decrement back to standard
12761349 qualityLevel = standardQualityLevel
12771350 updateButtonAppearance ( )
12781351 }
0 commit comments