Indent blank lines and address avatar crop review feedback

Indent the new method's blank lines to satisfy SwiftFormat's
indent-blank-lines rule, which failed the Tests CI jobs.

Also address review findings on the crop itself:
- Request a 256pt thumbnail box at both donation sites so cropping a
  scaled (aspect-fit) thumbnail to its minor axis keeps enough
  resolution for the share sheet's circle.
- Round the draw offsets to whole pixels so odd-sized crops blit
  instead of resampling.
- Log when fetched avatar data can't be decoded rather than silently
  swapping in the placeholder, and document the non-obvious
  format.scale pinning.
This commit is contained in:
Matthew Hodgson
2026-08-03 22:55:03 +03:00
committed by Mauro
parent 39805d080e
commit 58e1a0212d
3 changed files with 23 additions and 10 deletions
+6 -1
View File
@@ -53,11 +53,16 @@ enum Avatars {
} }
let format = UIGraphicsImageRendererFormat() let format = UIGraphicsImageRendererFormat()
// UIImage(data:) decodes at scale 1; pin the renderer to that so the
// output isn't silently multiplied by the device's screen scale.
format.scale = image.scale format.scale = image.scale
return UIGraphicsImageRenderer(size: CGSize(width: side, height: side), format: format) return UIGraphicsImageRenderer(size: CGSize(width: side, height: side), format: format)
.pngData { _ in .pngData { _ in
image.draw(at: CGPoint(x: (side - image.size.width) / 2, y: (side - image.size.height) / 2)) // Round the offsets to pixel boundaries so the crop is a straight
// copy rather than resampling (and softening) the whole image.
image.draw(at: CGPoint(x: ((side - image.size.width) / 2).rounded(),
y: ((side - image.size.height) / 2).rounded()))
} }
} }
@@ -650,9 +650,14 @@ class TimelineController: TimelineControllerProtocol {
} }
if let avatarURL, let mediaSource = try? MediaSourceProxy(url: avatarURL, mimeType: nil) { if let avatarURL, let mediaSource = try? MediaSourceProxy(url: avatarURL, mimeType: nil) {
if case let .success(avatarData) = await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)), // Request a generous size so the square crop of a non-square avatar keeps enough resolution.
let squareAvatarData = Avatars.squareAvatarImageData(from: avatarData) { if case let .success(avatarData) = await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 256, height: 256)) {
sendMessageIntent.setImage(INImage(imageData: squareAvatarData), forParameterNamed: \.speakableGroupName) if let squareAvatarData = Avatars.squareAvatarImageData(from: avatarData) {
sendMessageIntent.setImage(INImage(imageData: squareAvatarData), forParameterNamed: \.speakableGroupName)
} else {
MXLog.error("Failed processing the room avatar for the send message intent, using a placeholder.")
addPlacehoder()
}
} else { } else {
addPlacehoder() addPlacehoder()
} }
+4 -1
View File
@@ -257,10 +257,13 @@ nonisolated struct NotificationContentBuilder {
let image: INImage let image: INImage
if !forcePlaceholder, if !forcePlaceholder,
let mediaSource = icon.mediaSource { let mediaSource = icon.mediaSource {
switch await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) { // Request a generous size so the square crop of a non-square avatar keeps enough resolution.
switch await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 256, height: 256)) {
case .success(let data): case .success(let data):
if let squareData = Avatars.squareAvatarImageData(from: data) { if let squareData = Avatars.squareAvatarImageData(from: data) {
fetchedImage = INImage(imageData: squareData) fetchedImage = INImage(imageData: squareData)
} else {
MXLog.error("Couldn't decode the fetched sender icon, using a placeholder.")
} }
case .failure(let error): case .failure(let error):
MXLog.error("Couldn't add sender icon: \(error)") MXLog.error("Couldn't add sender icon: \(error)")