Фикс: сделал subtitle в списке чатов и текст in-app баннера в одну строку с truncate
This commit is contained in:
@@ -446,8 +446,17 @@ final class NotificationService: UNNotificationServiceExtension {
|
||||
return Int(index)
|
||||
}
|
||||
|
||||
/// Single-letter initial for group avatars.
|
||||
/// Copy of RosettaColors.groupInitial(name:publicKey:) from Colors.swift.
|
||||
private static func groupInitial(name: String, publicKey: String) -> String {
|
||||
let trimmed = name.trimmingCharacters(in: .whitespaces)
|
||||
if let first = trimmed.first { return String(first).uppercased() }
|
||||
if !publicKey.isEmpty { return String(publicKey.prefix(1)).uppercased() }
|
||||
return "?"
|
||||
}
|
||||
|
||||
/// Desktop parity: 2-letter initials from display name.
|
||||
/// Exact copy of RosettaColors.initials(name:publicKey:) from Colors.swift:209-223.
|
||||
/// Exact copy of RosettaColors.initials(name:publicKey:) from Colors.swift.
|
||||
private static func initials(name: String, publicKey: String) -> String {
|
||||
let words = name.trimmingCharacters(in: .whitespaces)
|
||||
.split(whereSeparator: { $0.isWhitespace })
|
||||
@@ -525,16 +534,15 @@ final class NotificationService: UNNotificationServiceExtension {
|
||||
return image
|
||||
}
|
||||
|
||||
/// Generates a 50x50 group avatar with person.2.fill icon on solid tint circle.
|
||||
/// Matches ChatRowView.swift:99-106 (group avatar without photo).
|
||||
/// Generates a 50x50 group avatar with single-letter initial on Mantine light circle.
|
||||
private static func generateGroupAvatar(name: String, key: String) -> INImage? {
|
||||
let size: CGFloat = 50
|
||||
let colorIndex = avatarColorIndex(for: name, publicKey: key)
|
||||
let colors = avatarColors[colorIndex]
|
||||
let text = groupInitial(name: name, publicKey: key)
|
||||
|
||||
// Try 2.0 scale first; fallback to 1.0 if NSE memory is constrained.
|
||||
let image = renderGroupAvatar(size: size, tintHex: colors.tint, scale: 2.0)
|
||||
?? renderGroupAvatar(size: size, tintHex: colors.tint, scale: 1.0)
|
||||
let image = renderLetterAvatar(size: size, colors: colors, text: text, scale: 2.0)
|
||||
?? renderLetterAvatar(size: size, colors: colors, text: text, scale: 1.0)
|
||||
|
||||
guard let pngData = image?.pngData() else { return nil }
|
||||
if let tempURL = storeTemporaryImage(data: pngData, key: "group-\(key)", fileExtension: "png") {
|
||||
@@ -543,33 +551,6 @@ final class NotificationService: UNNotificationServiceExtension {
|
||||
return INImage(imageData: pngData)
|
||||
}
|
||||
|
||||
/// Renders group avatar at given scale. Returns nil if UIGraphics context can't be allocated.
|
||||
private static func renderGroupAvatar(size: CGFloat, tintHex: UInt32, scale: CGFloat) -> UIImage? {
|
||||
UIGraphicsBeginImageContextWithOptions(CGSize(width: size, height: size), false, scale)
|
||||
guard UIGraphicsGetCurrentContext() != nil else { return nil }
|
||||
|
||||
let rect = CGRect(x: 0, y: 0, width: size, height: size)
|
||||
uiColor(hex: tintHex).setFill()
|
||||
UIBezierPath(ovalIn: rect).fill()
|
||||
|
||||
let config = UIImage.SymbolConfiguration(pointSize: 20, weight: .medium)
|
||||
if let symbol = UIImage(systemName: "person.2.fill", withConfiguration: config)?
|
||||
.withTintColor(.white.withAlphaComponent(0.9), renderingMode: .alwaysOriginal) {
|
||||
let symbolSize = symbol.size
|
||||
let symbolRect = CGRect(
|
||||
x: (size - symbolSize.width) / 2,
|
||||
y: (size - symbolSize.height) / 2,
|
||||
width: symbolSize.width,
|
||||
height: symbolSize.height
|
||||
)
|
||||
symbol.draw(in: symbolRect)
|
||||
}
|
||||
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
return image
|
||||
}
|
||||
|
||||
/// Loads sender avatar from shared App Group cache written by the main app.
|
||||
/// Falls back to letter avatar when no real image is available.
|
||||
private static func loadNotificationAvatar(for senderKey: String) -> INImage? {
|
||||
|
||||
Reference in New Issue
Block a user