Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
594eea9
adjust api to append SendableImage
johnxnguyen Oct 6, 2025
ad70d1f
use new api
johnxnguyen Oct 7, 2025
1da1e82
set default name
johnxnguyen Oct 7, 2025
e06d337
groom
johnxnguyen Oct 7, 2025
d629338
change default file name
johnxnguyen Oct 7, 2025
dd3c001
add name to protobuf asset
johnxnguyen Oct 7, 2025
bf9813f
include extra metadata
johnxnguyen Oct 7, 2025
bf54c2b
Merge branch 'release/cycle-4.8' into feat/asset-audit-log-metadata-w…
johnxnguyen Oct 8, 2025
f8647fd
delete dead code
johnxnguyen Oct 8, 2025
3dc5798
include metadata for profile image uploads
johnxnguyen Oct 8, 2025
286e0ad
include metadata for link previews
johnxnguyen Oct 8, 2025
75455fe
Merge branch 'release/cycle-4.8' into feat/asset-audit-log-metadata-w…
johnxnguyen Oct 8, 2025
7dfd1b1
Merge branch 'feat/asset-audit-log-metadata-wpb-20714' of github.com:…
johnxnguyen Oct 8, 2025
d1b9dd7
format
johnxnguyen Oct 9, 2025
d4c8583
use feature flag
johnxnguyen Oct 9, 2025
2e09c95
format
johnxnguyen Oct 9, 2025
552000e
fix data model tests
johnxnguyen Oct 9, 2025
6b68f29
fix request strategy tests
johnxnguyen Oct 9, 2025
936ceba
fix sync engine tests
johnxnguyen Oct 9, 2025
6f9dd8e
fix ui tests
johnxnguyen Oct 9, 2025
fe65c29
format
johnxnguyen Oct 9, 2025
c2f20a6
Merge branch 'release/cycle-4.8' into feat/asset-audit-log-metadata-w…
johnxnguyen Oct 9, 2025
d6c0661
move type to own file
johnxnguyen Oct 10, 2025
ca0114b
Merge branch 'release/cycle-4.8' into feat/asset-audit-log-metadata-w…
johnxnguyen Oct 10, 2025
ab0c634
factor out cloud detection
johnxnguyen Oct 13, 2025
0b35a1e
Merge branch 'feat/asset-audit-log-metadata-wpb-20714' of github.com:…
johnxnguyen Oct 13, 2025
91a4ce7
read feature from db at time of need
johnxnguyen Oct 13, 2025
07ac421
preserve forwarded image name
johnxnguyen Oct 13, 2025
a8176ae
add comment
johnxnguyen Oct 13, 2025
54d40f6
remove debug description
johnxnguyen Oct 13, 2025
5da81d4
format
johnxnguyen Oct 13, 2025
db94634
update comment
johnxnguyen Oct 13, 2025
53c3872
Merge branch 'release/cycle-4.8' into feat/asset-audit-log-metadata-w…
johnxnguyen Oct 13, 2025
dbe251d
fix test
johnxnguyen Oct 13, 2025
8ad6fcd
fix tests
johnxnguyen Oct 13, 2025
4200031
fix test
johnxnguyen Oct 14, 2025
2eeef72
fix multithread violation
johnxnguyen Oct 14, 2025
04e4835
fix tests
johnxnguyen Oct 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ public extension ZMConversation {
/// Append an image message.
///
/// - Parameters:
/// - url: A url locating some image data.
/// - image: The image to append.
/// - nonce: The nonce of the message.
///
/// - Throws:
Expand All @@ -268,55 +268,42 @@ public extension ZMConversation {
/// The appended message.

@discardableResult
func appendImage(at URL: URL, nonce: UUID = UUID()) throws -> ZMConversationMessage {
guard
URL.isFileURL,
ZMImagePreprocessor.sizeOfPrerotatedImage(at: URL) != .zero,
let imageData = try? Data(contentsOf: URL, options: [])
else {
throw AppendMessageError.invalidImageUrl
}

return try appendImage(from: imageData)
}

/// Append an image message.
///
/// - Parameters:
/// - imageData: Data representing an image.
/// - nonce: The nonce of the message.
///
/// - Throws:
/// - `AppendMessageError` if the message couldn't be appended.
///
/// - Returns:
/// The appended message.

@discardableResult
func appendImage(from imageData: Data, nonce: UUID = UUID()) throws -> ZMConversationMessage {
func appendImage(
_ image: SendableImage,
nonce: UUID
) throws -> ZMConversationMessage {
guard let moc = managedObjectContext else {
throw AppendMessageError.missingManagedObjectContext
}

guard let imageData = try? imageData.wr_removingImageMetadata() else {
guard let imageData = try? image.data.wr_removingImageMetadata() else {
throw AppendMessageError.failedToRemoveImageMetadata
}

// mimeType is assigned first, to make sure UI can handle animated GIF file correctly.
let mimeType = imageData.mimeType ?? ""
let mimeType = image.utType?.preferredMIMEType

// We update the size again when the the preprocessing is done.
let imageSize = ZMImagePreprocessor.sizeOfPrerotatedImage(with: imageData)

let asset = GenericMessageProtocol.Asset(
name: image.name,
mimeType: mimeType ?? "",
imageSize: imageSize,
mimeType: mimeType,
size: UInt64(imageData.count)
)

return try append(asset: asset, nonce: nonce, expires: true, prepareMessage: { message in
moc.zm_fileAssetCache.storeOriginalImage(data: imageData, for: message)
})
return try append(
asset: asset,
nonce: nonce,
expires: true,
prepareMessage: { message in
moc.zm_fileAssetCache.storeOriginalImage(
data: imageData,
for: message
)
}
)
}

/// Append a file message.
Expand Down Expand Up @@ -563,21 +550,6 @@ public extension ZMConversation {
try? appendLocation(with: locationData)
}

@discardableResult @objc(appendMessageWithImageData:)
func _appendImage(from imageData: Data) -> ZMConversationMessage? {
try? appendImage(from: imageData)
}

@discardableResult @objc(appendImageFromData:nonce:)
func _appendImage(from imageData: Data, nonce: UUID) -> ZMConversationMessage? {
try? appendImage(from: imageData, nonce: nonce)
}

@discardableResult @objc(appendImageAtURL:nonce:)
func _appendImage(at URL: URL, nonce: UUID) -> ZMConversationMessage? {
try? appendImage(at: URL, nonce: nonce)
}

@discardableResult @objc(appendMessageWithFileMetadata:)
func _appendFile(with fileMetadata: ZMFileMetadata) -> ZMConversationMessage? {
try? appendFile(with: fileMetadata)
Expand All @@ -589,3 +561,55 @@ public extension ZMConversation {
}

}

import UniformTypeIdentifiers

public struct SendableImage {

public let name: String
public let utType: UTType?
public let data: Data

public init(
name: String?,
utType: UTType?,
data: Data
) {
if let utType {
self.utType = utType
} else {
self.utType = Self.determineUTType(from: data)
}

if let name {
self.name = name
} else if let fileExtension = self.utType?.preferredFilenameExtension {
self.name = "picture.\(fileExtension)"
} else {
self.name = "picture"
}

self.data = data
}

private static func determineUTType(from data: Data) -> UTType? {
guard
!data.isEmpty,
let imageSource = CGImageSourceCreateWithData(data as CFData, nil),
let uti = CGImageSourceGetType(imageSource) as String?
else {
return nil
}

return UTType(uti)
}

}

extension SendableImage: CustomDebugStringConvertible {

public var debugDescription: String {
"SendableImage(name: \(name), type: \(utType?.preferredMIMEType ?? "unknown"))"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ open class ZMFileMetadata: NSObject {
} else {
nil
}
let endName = name ?? (fileURL.lastPathComponent.isEmpty ? "unnamed" : fileURL.lastPathComponent)

let endName: String = if let name {
name
} else if !fileURL.lastPathComponent.isEmpty {
fileURL.lastPathComponent
} else {
"file"
}

self.filename = endName.removingExtremeCombiningCharacters
super.init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public protocol LegacyFeatureRepositoryInterface {
func storeConsumableNotifications(_ consumableNotifications: Feature.ConsumableNotifications)
func fetchChatBubblesSimple() -> Feature.ChatBubblesSimple
func storeChatBubblesSimple(_ chatBubblesSimple: Feature.ChatBubblesSimple)
func fetchAssetAuditLog() -> Feature.AssetAuditLog
}

/// **Do not use it for new code, use FeatureConfigRepository instead**
Expand Down Expand Up @@ -536,6 +537,16 @@ public class LegacyFeatureRepository: LegacyFeatureRepositoryInterface {
}
}

// MARK: - Asset audit log

public func fetchAssetAuditLog() -> Feature.AssetAuditLog {
guard let feature = Feature.fetch(name: .assetAuditLog, context: context) else {
return .init()
}

return .init(status: feature.status)
}

// MARK: - Methods

func createDefaultConfigsIfNeeded() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,17 @@ public extension GenericMessageProtocol.Asset {
}
}

init(imageSize: CGSize, mimeType: String, size: UInt64) {
init(
name: String,
mimeType: String,
imageSize: CGSize,
size: UInt64
) {
self = GenericMessageProtocol.Asset.with {
$0.original = GenericMessageProtocol.Asset.Original.with {
$0.size = size
$0.name = name
$0.mimeType = mimeType
$0.size = size
$0.image = GenericMessageProtocol.Asset.ImageMetaData.with {
$0.width = Int32(imageSize.width)
$0.height = Int32(imageSize.height)
Expand Down Expand Up @@ -228,12 +234,14 @@ public extension GenericMessageProtocol.Asset.RemoteData {

extension GenericMessage {
mutating func updateAssetOriginal(withImageProperties imageProperties: ZMIImageProperties) {
let asset = GenericMessageProtocol.Asset(
imageSize: imageProperties.size,
mimeType: imageProperties.mimeType,
size: UInt64(imageProperties.length)
)
update(asset: asset)
updateAsset { existingAsset in
existingAsset.original.mimeType = imageProperties.mimeType
existingAsset.original.size = UInt64(imageProperties.length)
existingAsset.original.image = GenericMessageProtocol.Asset.ImageMetaData.with {
$0.width = Int32(imageProperties.size.width)
$0.height = Int32(imageProperties.size.height)
}
}
}

mutating func updateAssetPreview(withUploadedOTRKey otrKey: Data, sha256: Data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -774,8 +774,9 @@ public extension LinkPreview {
$0.summary = articleMetadata.summary ?? ""
if let imageData = articleMetadata.imageData.first {
$0.image = GenericMessageProtocol.Asset(
imageSize: CGSize(width: 0, height: 0),
name: "picture.jpeg",
mimeType: "image/jpeg",
imageSize: CGSize(width: 0, height: 0),
size: UInt64(imageData.count)
)
}
Expand All @@ -791,8 +792,9 @@ public extension LinkPreview {
$0.title = twitterMetadata.message ?? ""
if let imageData = twitterMetadata.imageData.first {
$0.image = GenericMessageProtocol.Asset(
imageSize: CGSize(width: 0, height: 0),
name: "picture.jpeg",
mimeType: "image/jpeg",
imageSize: CGSize(width: 0, height: 0),
size: UInt64(imageData.count)
)
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 20 additions & 5 deletions wire-ios-data-model/Tests/OneOnOne/OneOnOneMigratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,10 @@ final class OneOnOneMigratorTests: XCTestCase {
message = try proteusConversation.appendKnock()
message.updateServerTimestamp(with: 1)

message = try proteusConversation.appendImage(from: ZMTBaseTest.verySmallJPEGData())
message = try proteusConversation.appendImage(
SendableImage(name: "picture.jpg", utType: .jpeg, data: ZMTBaseTest.verySmallJPEGData()),
nonce: UUID()
)
message.updateServerTimestamp(with: 2)

XCTAssertEqual(proteusConversation.allMessages.count, 3)
Expand Down Expand Up @@ -322,7 +325,10 @@ final class OneOnOneMigratorTests: XCTestCase {
message = try proteusConversation.appendKnock()
message.updateServerTimestamp(with: 1)

message = try proteusConversation.appendImage(from: ZMTBaseTest.verySmallJPEGData())
message = try proteusConversation.appendImage(
SendableImage(name: "picture.jpg", utType: .jpeg, data: ZMTBaseTest.verySmallJPEGData()),
nonce: UUID()
)
message.updateServerTimestamp(with: 2)

XCTAssertEqual(proteusConversation.allMessages.count, 3)
Expand All @@ -337,7 +343,10 @@ final class OneOnOneMigratorTests: XCTestCase {
message = try duplicateProteusConversation.appendKnock()
message.updateServerTimestamp(with: 11)

message = try duplicateProteusConversation.appendImage(from: ZMTBaseTest.verySmallJPEGData())
message = try duplicateProteusConversation.appendImage(
SendableImage(name: "picture.jpg", utType: .jpeg, data: ZMTBaseTest.verySmallJPEGData()),
nonce: UUID()
)
message.updateServerTimestamp(with: 12)

XCTAssertEqual(proteusConversation.allMessages.count, 3)
Expand Down Expand Up @@ -453,7 +462,10 @@ final class OneOnOneMigratorTests: XCTestCase {
message = try proteusConversation.appendKnock()
message.updateServerTimestamp(with: 1)

message = try proteusConversation.appendImage(from: ZMTBaseTest.verySmallJPEGData())
message = try proteusConversation.appendImage(
SendableImage(name: "picture.jpg", utType: .jpeg, data: ZMTBaseTest.verySmallJPEGData()),
nonce: UUID()
)
message.updateServerTimestamp(with: 2)

XCTAssertEqual(proteusConversation.allMessages.count, 3)
Expand All @@ -468,7 +480,10 @@ final class OneOnOneMigratorTests: XCTestCase {
message = try duplicateProteusConversation.appendKnock()
message.updateServerTimestamp(with: 11)

message = try duplicateProteusConversation.appendImage(from: ZMTBaseTest.verySmallJPEGData())
message = try duplicateProteusConversation.appendImage(
SendableImage(name: "picture.jpg", utType: .jpeg, data: ZMTBaseTest.verySmallJPEGData()),
nonce: UUID()
)
message.updateServerTimestamp(with: 12)

XCTAssertEqual(proteusConversation.allMessages.count, 3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ final class AssetColletionBatchedTests: ModelObjectsTests {
var offset: TimeInterval = 0
var messages = [ZMMessage]()
(0 ..< count).forEach { _ in
let message = try! conversation.appendImage(from: verySmallJPEGData()) as! ZMMessage
let message = try! conversation.appendImage(
SendableImage(name: "picture.jpg", utType: .jpeg, data: verySmallJPEGData()),
nonce: UUID()
) as! ZMMessage
offset += 5
message.setValue(Date().addingTimeInterval(offset), forKey: "serverTimestamp")
messages.append(message)
Expand Down Expand Up @@ -300,7 +303,8 @@ final class AssetColletionBatchedTests: ModelObjectsTests {
func testThatItExcludesDefinedCategories_PreCategorized() {
// given
let data = data(forResource: "animated", extension: "gif")!
_ = try! conversation.appendImage(from: data) as! ZMAssetClientMessage
let image = SendableImage(name: "picture.gif", utType: .gif, data: data)
_ = try! conversation.appendImage(image, nonce: UUID()) as! ZMAssetClientMessage
uiMOC.saveOrRollback()

// when
Expand All @@ -322,7 +326,8 @@ final class AssetColletionBatchedTests: ModelObjectsTests {
func testThatItExcludesDefinedCategories_NotPreCategorized() {
// given
let data = data(forResource: "animated", extension: "gif")!
_ = try! conversation.appendImage(from: data) as! ZMAssetClientMessage
let image = SendableImage(name: "picture.gif", utType: .gif, data: data)
_ = try! conversation.appendImage(image, nonce: UUID()) as! ZMAssetClientMessage
uiMOC.saveOrRollback()

// when
Expand Down Expand Up @@ -370,7 +375,8 @@ final class AssetColletionBatchedTests: ModelObjectsTests {
// given
insertAssetMessages(count: 1)
let data = data(forResource: "animated", extension: "gif")!
_ = try! conversation.appendImage(from: data) as! ZMAssetClientMessage
let image = SendableImage(name: "picture.gif", utType: .gif, data: data)
_ = try! conversation.appendImage(image, nonce: UUID()) as! ZMAssetClientMessage
uiMOC.saveOrRollback()

// when
Expand Down
Loading
Loading