mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Group image and video metadata in specialised structs (#3518)
This commit is contained in:
@@ -10,18 +10,18 @@ import SwiftUI
|
|||||||
extension View {
|
extension View {
|
||||||
/// Constrains the max height of a media item in the timeline, whilst preserving its aspect ratio.
|
/// Constrains the max height of a media item in the timeline, whilst preserving its aspect ratio.
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func timelineMediaFrame(height contentHeight: CGFloat?, aspectRatio contentAspectRatio: CGFloat?) -> some View {
|
func timelineMediaFrame(imageInfo: ImageInfoProxy?) -> some View {
|
||||||
let defaultMediaSize = 100.0
|
let defaultMediaSize = 100.0
|
||||||
let minMediaHeight = 100.0
|
let minMediaHeight = 100.0
|
||||||
let maxMediaHeight = 300.0
|
let maxMediaHeight = 300.0
|
||||||
|
|
||||||
if let contentHeight, contentHeight < minMediaHeight { // Special case very small images
|
if let contentHeight = imageInfo?.size?.height, contentHeight < minMediaHeight { // Special case very small images
|
||||||
aspectRatio(contentAspectRatio, contentMode: .fit)
|
aspectRatio(imageInfo?.aspectRatio, contentMode: .fit)
|
||||||
.frame(minHeight: minMediaHeight, maxHeight: minMediaHeight)
|
.frame(minHeight: minMediaHeight, maxHeight: minMediaHeight)
|
||||||
} else {
|
} else {
|
||||||
if let contentAspectRatio {
|
if let contentAspectRatio = imageInfo?.aspectRatio {
|
||||||
aspectRatio(contentAspectRatio, contentMode: .fit)
|
aspectRatio(contentAspectRatio, contentMode: .fit)
|
||||||
.frame(maxHeight: min(maxMediaHeight, max(minMediaHeight, contentHeight ?? .infinity)))
|
.frame(maxHeight: min(maxMediaHeight, max(minMediaHeight, imageInfo?.size?.height ?? .infinity)))
|
||||||
// Required to prevent the reply details to get higher priority in rendering the width of the view.
|
// Required to prevent the reply details to get higher priority in rendering the width of the view.
|
||||||
.aspectRatio(contentAspectRatio, contentMode: .fit)
|
.aspectRatio(contentAspectRatio, contentMode: .fit)
|
||||||
} else { // Otherwise force the image to be `defaultMediaSize` x `defaultMediaSize`
|
} else { // Otherwise force the image to be `defaultMediaSize` x `defaultMediaSize`
|
||||||
|
|||||||
@@ -226,8 +226,8 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview {
|
|||||||
eventID: "123",
|
eventID: "123",
|
||||||
eventContent: .message(.image(.init(filename: "head.png",
|
eventContent: .message(.image(.init(filename: "head.png",
|
||||||
caption: "Image: Pushead",
|
caption: "Image: Pushead",
|
||||||
source: .init(url: .picturesDirectory, mimeType: nil),
|
imageInfo: .mockImage,
|
||||||
thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))),
|
thumbnailInfo: .mockThumbnail)))),
|
||||||
.loaded(sender: .init(id: "Jason"),
|
.loaded(sender: .init(id: "Jason"),
|
||||||
eventID: "123",
|
eventID: "123",
|
||||||
eventContent: .message(.notice(.init(body: "Notice: Too far gone?")))),
|
eventContent: .message(.notice(.init(body: "Notice: Too far gone?")))),
|
||||||
@@ -238,9 +238,8 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview {
|
|||||||
eventID: "123",
|
eventID: "123",
|
||||||
eventContent: .message(.video(.init(filename: "never.mov",
|
eventContent: .message(.video(.init(filename: "never.mov",
|
||||||
caption: "Video: Through the never",
|
caption: "Video: Through the never",
|
||||||
duration: 100,
|
videoInfo: .mockVideo,
|
||||||
source: nil,
|
thumbnailInfo: .mockThumbnail)))),
|
||||||
thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))),
|
|
||||||
.loading(eventID: "")
|
.loading(eventID: "")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -553,11 +553,11 @@ class TimelineInteractionHandler {
|
|||||||
|
|
||||||
switch timelineItem {
|
switch timelineItem {
|
||||||
case let item as ImageRoomTimelineItem:
|
case let item as ImageRoomTimelineItem:
|
||||||
source = item.content.source
|
source = item.content.imageInfo.source
|
||||||
filename = item.content.filename
|
filename = item.content.filename
|
||||||
caption = item.content.caption
|
caption = item.content.caption
|
||||||
case let item as VideoRoomTimelineItem:
|
case let item as VideoRoomTimelineItem:
|
||||||
source = item.content.source
|
source = item.content.videoInfo.source
|
||||||
filename = item.content.filename
|
filename = item.content.filename
|
||||||
caption = item.content.caption
|
caption = item.content.caption
|
||||||
case let item as FileRoomTimelineItem:
|
case let item as FileRoomTimelineItem:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ struct TimelineReplyView: View {
|
|||||||
ReplyView(sender: sender,
|
ReplyView(sender: sender,
|
||||||
plainBody: content.caption ?? content.filename,
|
plainBody: content.caption ?? content.filename,
|
||||||
formattedBody: content.formattedCaption,
|
formattedBody: content.formattedCaption,
|
||||||
icon: .init(kind: .mediaSource(content.thumbnailSource ?? content.source), cornerRadii: iconCornerRadii))
|
icon: .init(kind: .mediaSource(content.thumbnailInfo?.source ?? content.imageInfo.source), cornerRadii: iconCornerRadii))
|
||||||
case .notice(let content):
|
case .notice(let content):
|
||||||
ReplyView(sender: sender,
|
ReplyView(sender: sender,
|
||||||
plainBody: content.body,
|
plainBody: content.body,
|
||||||
@@ -55,7 +55,7 @@ struct TimelineReplyView: View {
|
|||||||
ReplyView(sender: sender,
|
ReplyView(sender: sender,
|
||||||
plainBody: content.caption ?? content.filename,
|
plainBody: content.caption ?? content.filename,
|
||||||
formattedBody: content.formattedCaption,
|
formattedBody: content.formattedCaption,
|
||||||
icon: content.thumbnailSource.map { .init(kind: .mediaSource($0), cornerRadii: iconCornerRadii) })
|
icon: content.thumbnailInfo.map { .init(kind: .mediaSource($0.source), cornerRadii: iconCornerRadii) })
|
||||||
case .voice:
|
case .voice:
|
||||||
ReplyView(sender: sender,
|
ReplyView(sender: sender,
|
||||||
plainBody: L10n.commonVoiceMessage,
|
plainBody: L10n.commonVoiceMessage,
|
||||||
@@ -222,9 +222,7 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
static var previewItems: [TimelineReplyView] {
|
static var previewItems: [TimelineReplyView] {
|
||||||
let imageSource = MediaSourceProxy(url: "https://mock.com", mimeType: "image/png")
|
[
|
||||||
|
|
||||||
return [
|
|
||||||
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .notLoaded(eventID: "")),
|
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .notLoaded(eventID: "")),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loading(eventID: "")),
|
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loading(eventID: "")),
|
||||||
@@ -268,17 +266,16 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
|
|||||||
eventID: "123",
|
eventID: "123",
|
||||||
eventContent: .message(.image(.init(filename: "image.jpg",
|
eventContent: .message(.image(.init(filename: "image.jpg",
|
||||||
caption: "Some image",
|
caption: "Some image",
|
||||||
source: imageSource,
|
imageInfo: .mockImage,
|
||||||
thumbnailSource: imageSource))))),
|
thumbnailInfo: .mockThumbnail))))),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
eventID: "123",
|
eventID: "123",
|
||||||
eventContent: .message(.video(.init(filename: "video.mp4",
|
eventContent: .message(.video(.init(filename: "video.mp4",
|
||||||
caption: "Some video",
|
caption: "Some video",
|
||||||
duration: 0,
|
videoInfo: .mockVideo,
|
||||||
source: nil,
|
thumbnailInfo: .mockThumbnail))))),
|
||||||
thumbnailSource: imageSource))))),
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
eventID: "123",
|
eventID: "123",
|
||||||
@@ -321,7 +318,7 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
|
|||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.environmentObject(viewModel.context)
|
.environmentObject(viewModel.context)
|
||||||
// Allow member names to load. Reduce precission as the `imageSource` randomly renders slightly differently
|
// Allow member names to load. Reduce precission as the `mockThumbnail` randomly renders slightly differently
|
||||||
.snapshotPreferences(delay: 0.2, precision: 0.98)
|
.snapshotPreferences(delay: 0.2, precision: 0.98)
|
||||||
.previewLayout(.sizeThatFits)
|
.previewLayout(.sizeThatFits)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -498,8 +498,8 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "other.png",
|
content: .init(filename: "other.png",
|
||||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"),
|
imageInfo: .mockImage,
|
||||||
thumbnailSource: nil),
|
thumbnailInfo: nil),
|
||||||
|
|
||||||
properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))))
|
properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))))
|
||||||
|
|
||||||
@@ -577,8 +577,8 @@ private struct MockTimelineContent: View {
|
|||||||
isThreaded: isThreaded,
|
isThreaded: isThreaded,
|
||||||
sender: .init(id: ""),
|
sender: .init(id: ""),
|
||||||
content: .init(filename: "image.jpg",
|
content: .init(filename: "image.jpg",
|
||||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"),
|
imageInfo: .mockImage,
|
||||||
thumbnailSource: nil),
|
thumbnailInfo: nil),
|
||||||
replyDetails: replyDetails))
|
replyDetails: replyDetails))
|
||||||
|
|
||||||
LocationRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(),
|
LocationRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(),
|
||||||
|
|||||||
+14
-19
@@ -40,25 +40,23 @@ struct ImageRoomTimelineView: View {
|
|||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var loadableImage: some View {
|
private var loadableImage: some View {
|
||||||
if timelineItem.content.contentType == .gif {
|
if timelineItem.content.contentType == .gif {
|
||||||
LoadableImage(mediaSource: timelineItem.content.source,
|
LoadableImage(mediaSource: timelineItem.content.imageInfo.source,
|
||||||
mediaType: .timelineItem,
|
mediaType: .timelineItem,
|
||||||
blurhash: timelineItem.content.blurhash,
|
blurhash: timelineItem.content.blurhash,
|
||||||
size: timelineItem.content.size,
|
size: timelineItem.content.imageInfo.size,
|
||||||
mediaProvider: context.mediaProvider) {
|
mediaProvider: context.mediaProvider) {
|
||||||
placeholder
|
placeholder
|
||||||
}
|
}
|
||||||
.timelineMediaFrame(height: timelineItem.content.thumbnailSize?.height,
|
.timelineMediaFrame(imageInfo: timelineItem.content.imageInfo)
|
||||||
aspectRatio: timelineItem.content.aspectRatio)
|
|
||||||
} else {
|
} else {
|
||||||
LoadableImage(mediaSource: timelineItem.content.thumbnailSource ?? timelineItem.content.source,
|
LoadableImage(mediaSource: timelineItem.content.thumbnailInfo?.source ?? timelineItem.content.imageInfo.source,
|
||||||
mediaType: .timelineItem,
|
mediaType: .timelineItem,
|
||||||
blurhash: timelineItem.content.blurhash,
|
blurhash: timelineItem.content.blurhash,
|
||||||
size: timelineItem.content.thumbnailSize ?? timelineItem.content.size,
|
size: timelineItem.content.thumbnailInfo?.size ?? timelineItem.content.imageInfo.size,
|
||||||
mediaProvider: context.mediaProvider) {
|
mediaProvider: context.mediaProvider) {
|
||||||
placeholder
|
placeholder
|
||||||
}
|
}
|
||||||
.timelineMediaFrame(height: timelineItem.content.thumbnailSize?.height ?? timelineItem.content.size?.height,
|
.timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo ?? timelineItem.content.imageInfo)
|
||||||
aspectRatio: timelineItem.content.thumbnailAspectRatio ?? timelineItem.content.aspectRatio)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,8 +85,8 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "image.jpg",
|
content: .init(filename: "image.jpg",
|
||||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/jpg"),
|
imageInfo: .mockImage,
|
||||||
thumbnailSource: nil)))
|
thumbnailInfo: nil)))
|
||||||
|
|
||||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent,
|
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
@@ -98,8 +96,8 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "other.png",
|
content: .init(filename: "other.png",
|
||||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"),
|
imageInfo: .mockImage,
|
||||||
thumbnailSource: nil)))
|
thumbnailInfo: nil)))
|
||||||
|
|
||||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent,
|
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
@@ -109,9 +107,8 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "Blurhashed.jpg",
|
content: .init(filename: "Blurhashed.jpg",
|
||||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"),
|
imageInfo: .mockImage,
|
||||||
aspectRatio: 0.7,
|
thumbnailInfo: nil,
|
||||||
thumbnailSource: nil,
|
|
||||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW",
|
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW",
|
||||||
contentType: .gif)))
|
contentType: .gif)))
|
||||||
|
|
||||||
@@ -124,10 +121,8 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "Blurhashed.jpg",
|
content: .init(filename: "Blurhashed.jpg",
|
||||||
caption: "This is a great image 😎",
|
caption: "This is a great image 😎",
|
||||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"),
|
imageInfo: .mockImage,
|
||||||
size: .init(width: 50, height: 50),
|
thumbnailInfo: .mockThumbnail,
|
||||||
aspectRatio: 1,
|
|
||||||
thumbnailSource: nil,
|
|
||||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW",
|
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW",
|
||||||
contentType: .gif)))
|
contentType: .gif)))
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-8
@@ -14,15 +14,14 @@ struct StickerRoomTimelineView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TimelineStyler(timelineItem: timelineItem) {
|
TimelineStyler(timelineItem: timelineItem) {
|
||||||
LoadableImage(url: timelineItem.imageURL,
|
LoadableImage(mediaSource: timelineItem.imageInfo.source,
|
||||||
mediaType: .timelineItem,
|
mediaType: .timelineItem,
|
||||||
blurhash: timelineItem.blurhash,
|
blurhash: timelineItem.blurhash,
|
||||||
size: timelineItem.size,
|
size: timelineItem.imageInfo.size,
|
||||||
mediaProvider: context.mediaProvider) {
|
mediaProvider: context.mediaProvider) {
|
||||||
placeholder
|
placeholder
|
||||||
}
|
}
|
||||||
.timelineMediaFrame(height: timelineItem.size?.height,
|
.timelineMediaFrame(imageInfo: timelineItem.imageInfo)
|
||||||
aspectRatio: timelineItem.aspectRatio)
|
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel("\(L10n.commonSticker), \(timelineItem.body)")
|
.accessibilityLabel("\(L10n.commonSticker), \(timelineItem.body)")
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isEditable: false,
|
isEditable: false,
|
||||||
canBeRepliedTo: true,
|
canBeRepliedTo: true,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
imageURL: URL.picturesDirectory))
|
imageInfo: .mockImage))
|
||||||
|
|
||||||
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent,
|
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent,
|
||||||
body: "Some other image",
|
body: "Some other image",
|
||||||
@@ -60,7 +59,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isEditable: false,
|
isEditable: false,
|
||||||
canBeRepliedTo: true,
|
canBeRepliedTo: true,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
imageURL: URL.picturesDirectory))
|
imageInfo: .mockImage))
|
||||||
|
|
||||||
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent,
|
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent,
|
||||||
body: "Blurhashed image",
|
body: "Blurhashed image",
|
||||||
@@ -69,8 +68,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isEditable: false,
|
isEditable: false,
|
||||||
canBeRepliedTo: true,
|
canBeRepliedTo: true,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
imageURL: URL.picturesDirectory,
|
imageInfo: .mockImage,
|
||||||
aspectRatio: 0.7,
|
|
||||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-17
@@ -18,8 +18,7 @@ struct VideoRoomTimelineView: View {
|
|||||||
TimelineStyler(timelineItem: timelineItem) {
|
TimelineStyler(timelineItem: timelineItem) {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
thumbnail
|
thumbnail
|
||||||
.timelineMediaFrame(height: timelineItem.content.thumbnailSize?.height,
|
.timelineMediaFrame(imageInfo: timelineItem.content.thumbnailInfo)
|
||||||
aspectRatio: timelineItem.content.aspectRatio)
|
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel(L10n.commonVideo)
|
.accessibilityLabel(L10n.commonVideo)
|
||||||
// This clip shape is distinct from the one in the styler as that one
|
// This clip shape is distinct from the one in the styler as that one
|
||||||
@@ -41,11 +40,11 @@ struct VideoRoomTimelineView: View {
|
|||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
var thumbnail: some View {
|
var thumbnail: some View {
|
||||||
if let thumbnailSource = timelineItem.content.thumbnailSource {
|
if let thumbnailSource = timelineItem.content.thumbnailInfo?.source {
|
||||||
LoadableImage(mediaSource: thumbnailSource,
|
LoadableImage(mediaSource: thumbnailSource,
|
||||||
mediaType: .timelineItem,
|
mediaType: .timelineItem,
|
||||||
blurhash: timelineItem.content.blurhash,
|
blurhash: timelineItem.content.blurhash,
|
||||||
size: timelineItem.content.thumbnailSize,
|
size: timelineItem.content.thumbnailInfo?.size,
|
||||||
mediaProvider: context.mediaProvider) { imageView in
|
mediaProvider: context.mediaProvider) { imageView in
|
||||||
imageView
|
imageView
|
||||||
.overlay { playIcon }
|
.overlay { playIcon }
|
||||||
@@ -89,9 +88,8 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "video.mp4",
|
content: .init(filename: "video.mp4",
|
||||||
duration: 21,
|
videoInfo: .mockVideo,
|
||||||
source: nil,
|
thumbnailInfo: nil)))
|
||||||
thumbnailSource: nil)))
|
|
||||||
|
|
||||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent,
|
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
@@ -101,9 +99,8 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "other.mp4",
|
content: .init(filename: "other.mp4",
|
||||||
duration: 22,
|
videoInfo: .mockVideo,
|
||||||
source: nil,
|
thumbnailInfo: nil)))
|
||||||
thumbnailSource: nil)))
|
|
||||||
|
|
||||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent,
|
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
@@ -113,10 +110,8 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "Blurhashed.mp4",
|
content: .init(filename: "Blurhashed.mp4",
|
||||||
duration: 23,
|
videoInfo: .mockVideo,
|
||||||
source: nil,
|
thumbnailInfo: nil,
|
||||||
aspectRatio: 0.7,
|
|
||||||
thumbnailSource: nil,
|
|
||||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW")))
|
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW")))
|
||||||
|
|
||||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent,
|
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent,
|
||||||
@@ -128,9 +123,8 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
content: .init(filename: "video.mp4",
|
content: .init(filename: "video.mp4",
|
||||||
caption: "This is a caption",
|
caption: "This is a caption",
|
||||||
duration: 21,
|
videoInfo: .mockVideo,
|
||||||
source: nil,
|
thumbnailInfo: nil)))
|
||||||
thumbnailSource: nil)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,11 +258,8 @@ enum RoomTimelineItemFixtures {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: ""),
|
sender: .init(id: ""),
|
||||||
content: .init(filename: "video.mp4",
|
content: .init(filename: "video.mp4",
|
||||||
duration: 100,
|
videoInfo: .mockVideo,
|
||||||
source: .init(url: .picturesDirectory, mimeType: nil),
|
thumbnailInfo: .mockThumbnail,
|
||||||
size: .init(width: 1920, height: 1080),
|
|
||||||
aspectRatio: 1.78,
|
|
||||||
thumbnailSource: .init(url: .picturesDirectory, mimeType: nil),
|
|
||||||
blurhash: "KtI~70X5V?yss9oyrYs:t6")),
|
blurhash: "KtI~70X5V?yss9oyrYs:t6")),
|
||||||
ImageRoomTimelineItem(id: .randomEvent,
|
ImageRoomTimelineItem(id: .randomEvent,
|
||||||
timestamp: "10:47 am",
|
timestamp: "10:47 am",
|
||||||
@@ -272,10 +269,8 @@ enum RoomTimelineItemFixtures {
|
|||||||
isThreaded: false,
|
isThreaded: false,
|
||||||
sender: .init(id: ""),
|
sender: .init(id: ""),
|
||||||
content: .init(filename: "image.jpg",
|
content: .init(filename: "image.jpg",
|
||||||
source: .init(url: .picturesDirectory, mimeType: nil),
|
imageInfo: .mockImage,
|
||||||
size: .init(width: 5120, height: 3412),
|
thumbnailInfo: nil,
|
||||||
aspectRatio: 1.5,
|
|
||||||
thumbnailSource: nil,
|
|
||||||
blurhash: "KpE4oyayR5|GbHb];3j@of"))
|
blurhash: "KpE4oyayR5|GbHb];3j@of"))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,11 +215,111 @@ struct SendHandleProxy: Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Receipt {
|
struct VideoInfoProxy: Hashable {
|
||||||
var dateTimestamp: Date? {
|
let source: MediaSourceProxy
|
||||||
guard let timestamp else {
|
private(set) var duration: TimeInterval
|
||||||
return nil
|
private(set) var size: CGSize?
|
||||||
|
private(set) var aspectRatio: CGFloat?
|
||||||
|
private(set) var mimeType: String?
|
||||||
|
|
||||||
|
init(source: MediaSource, duration: TimeInterval, width: UInt64?, height: UInt64?, mimeType: String?) {
|
||||||
|
self.source = MediaSourceProxy(source: source, mimeType: mimeType)
|
||||||
|
self.duration = duration
|
||||||
|
|
||||||
|
let mediaInfo = MediaInfoProxy(width: width, height: height, mimeType: mimeType)
|
||||||
|
size = mediaInfo.size
|
||||||
|
aspectRatio = mediaInfo.aspectRatio
|
||||||
|
self.mimeType = mediaInfo.mimeType
|
||||||
}
|
}
|
||||||
return Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
|
|
||||||
|
// MARK: - Mocks
|
||||||
|
|
||||||
|
private init(source: MediaSourceProxy, duration: TimeInterval, size: CGSize?, aspectRatio: CGFloat?, mimeType: String?) {
|
||||||
|
self.source = source
|
||||||
|
self.duration = duration
|
||||||
|
self.size = size
|
||||||
|
self.aspectRatio = aspectRatio
|
||||||
|
self.mimeType = mimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
static var mockVideo: VideoInfoProxy {
|
||||||
|
.init(source: .init(url: .picturesDirectory, mimeType: nil),
|
||||||
|
duration: 100,
|
||||||
|
size: .init(width: 1920, height: 1080),
|
||||||
|
aspectRatio: 1.78,
|
||||||
|
mimeType: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImageInfoProxy: Hashable {
|
||||||
|
let source: MediaSourceProxy
|
||||||
|
private(set) var size: CGSize?
|
||||||
|
private(set) var aspectRatio: CGFloat?
|
||||||
|
private(set) var mimeType: String?
|
||||||
|
|
||||||
|
init?(source: MediaSource?, width: UInt64?, height: UInt64?, mimeType: String?) {
|
||||||
|
guard let source else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init(source: .init(source: source, mimeType: mimeType), width: width, height: height, mimeType: mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(source: MediaSource, width: UInt64?, height: UInt64?, mimeType: String?) {
|
||||||
|
self.init(source: .init(source: source, mimeType: mimeType), width: width, height: height, mimeType: mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(url: URL, width: UInt64?, height: UInt64?, mimeType: String?) {
|
||||||
|
self.init(source: .init(url: url, mimeType: mimeType), width: width, height: height, mimeType: mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(source: MediaSourceProxy, width: UInt64?, height: UInt64?, mimeType: String?) {
|
||||||
|
self.source = source
|
||||||
|
|
||||||
|
let mediaInfo = MediaInfoProxy(width: width, height: height, mimeType: mimeType)
|
||||||
|
size = mediaInfo.size
|
||||||
|
aspectRatio = mediaInfo.aspectRatio
|
||||||
|
self.mimeType = mediaInfo.mimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Mocks
|
||||||
|
|
||||||
|
private init(source: MediaSourceProxy, size: CGSize?, aspectRatio: CGFloat?, mimeType: String?) {
|
||||||
|
self.source = source
|
||||||
|
self.size = size
|
||||||
|
self.aspectRatio = aspectRatio
|
||||||
|
self.mimeType = mimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
static var mockImage: ImageInfoProxy {
|
||||||
|
.init(source: .init(url: .picturesDirectory, mimeType: "image/png"),
|
||||||
|
size: .init(width: 100, height: 100),
|
||||||
|
aspectRatio: 1,
|
||||||
|
mimeType: "image/png")
|
||||||
|
}
|
||||||
|
|
||||||
|
static var mockThumbnail: ImageInfoProxy {
|
||||||
|
.init(source: .init(url: .picturesDirectory, mimeType: nil),
|
||||||
|
size: nil,
|
||||||
|
aspectRatio: nil,
|
||||||
|
mimeType: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MediaInfoProxy: Hashable {
|
||||||
|
private(set) var size: CGSize?
|
||||||
|
private(set) var mimeType: String?
|
||||||
|
private(set) var aspectRatio: CGFloat?
|
||||||
|
|
||||||
|
init(width: UInt64?, height: UInt64?, mimeType: String?) {
|
||||||
|
if let width, let height {
|
||||||
|
size = .init(width: CGFloat(width), height: CGFloat(height))
|
||||||
|
|
||||||
|
if width > 0, height > 0 {
|
||||||
|
aspectRatio = CGFloat(width) / CGFloat(height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mimeType = mimeType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-6
@@ -14,13 +14,9 @@ struct ImageRoomTimelineItemContent: Hashable {
|
|||||||
var formattedCaption: AttributedString?
|
var formattedCaption: AttributedString?
|
||||||
/// The original textual representation of the formatted caption directly from the event (usually HTML code)
|
/// The original textual representation of the formatted caption directly from the event (usually HTML code)
|
||||||
var formattedCaptionHTMLString: String?
|
var formattedCaptionHTMLString: String?
|
||||||
let source: MediaSourceProxy
|
|
||||||
var size: CGSize?
|
|
||||||
var aspectRatio: CGFloat?
|
|
||||||
|
|
||||||
let thumbnailSource: MediaSourceProxy?
|
let imageInfo: ImageInfoProxy
|
||||||
var thumbnailSize: CGSize?
|
let thumbnailInfo: ImageInfoProxy?
|
||||||
var thumbnailAspectRatio: CGFloat?
|
|
||||||
|
|
||||||
var blurhash: String?
|
var blurhash: String?
|
||||||
var contentType: UTType?
|
var contentType: UTType?
|
||||||
|
|||||||
+2
-8
@@ -14,15 +14,9 @@ struct VideoRoomTimelineItemContent: Hashable {
|
|||||||
var formattedCaption: AttributedString?
|
var formattedCaption: AttributedString?
|
||||||
/// The original textual representation of the formatted caption directly from the event (usually HTML code)
|
/// The original textual representation of the formatted caption directly from the event (usually HTML code)
|
||||||
var formattedCaptionHTMLString: String?
|
var formattedCaptionHTMLString: String?
|
||||||
let duration: TimeInterval
|
|
||||||
|
|
||||||
let source: MediaSourceProxy?
|
let videoInfo: VideoInfoProxy
|
||||||
var size: CGSize?
|
let thumbnailInfo: ImageInfoProxy?
|
||||||
var aspectRatio: CGFloat?
|
|
||||||
|
|
||||||
let thumbnailSource: MediaSourceProxy?
|
|
||||||
var thumbnailSize: CGSize?
|
|
||||||
var thumbnailAspectRatio: CGFloat?
|
|
||||||
|
|
||||||
var blurhash: String?
|
var blurhash: String?
|
||||||
var contentType: UTType?
|
var contentType: UTType?
|
||||||
|
|||||||
+1
-3
@@ -17,10 +17,8 @@ struct StickerRoomTimelineItem: EventBasedTimelineItemProtocol, Equatable {
|
|||||||
|
|
||||||
let sender: TimelineItemSender
|
let sender: TimelineItemSender
|
||||||
|
|
||||||
let imageURL: URL
|
let imageInfo: ImageInfoProxy
|
||||||
|
|
||||||
var size: CGSize?
|
|
||||||
var aspectRatio: CGFloat?
|
|
||||||
var blurhash: String?
|
var blurhash: String?
|
||||||
|
|
||||||
var properties = RoomTimelineItemProperties()
|
var properties = RoomTimelineItemProperties()
|
||||||
|
|||||||
@@ -118,20 +118,10 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
private func buildStickerTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildStickerTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ body: String,
|
_ body: String,
|
||||||
_ imageInfo: ImageInfo,
|
_ info: MatrixRustSDK.ImageInfo,
|
||||||
_ imageURL: URL,
|
_ imageURL: URL,
|
||||||
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
|
_ isOutgoing: Bool) -> RoomTimelineItemProtocol {
|
||||||
var imageSize: CGSize?
|
let imageInfo = ImageInfoProxy(url: imageURL, width: info.width, height: info.height, mimeType: info.mimetype)
|
||||||
var imageAspectRatio: CGFloat?
|
|
||||||
|
|
||||||
if let height = imageInfo.height,
|
|
||||||
let width = imageInfo.width {
|
|
||||||
imageSize = .init(width: CGFloat(width), height: CGFloat(height))
|
|
||||||
|
|
||||||
if width > 0, height > 0 {
|
|
||||||
imageAspectRatio = CGFloat(width) / CGFloat(height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return StickerRoomTimelineItem(id: eventItemProxy.id,
|
return StickerRoomTimelineItem(id: eventItemProxy.id,
|
||||||
body: body,
|
body: body,
|
||||||
@@ -140,10 +130,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
|
canBeRepliedTo: eventItemProxy.canBeRepliedTo,
|
||||||
sender: eventItemProxy.sender,
|
sender: eventItemProxy.sender,
|
||||||
imageURL: imageURL,
|
imageInfo: imageInfo,
|
||||||
size: imageSize,
|
blurhash: info.blurhash,
|
||||||
aspectRatio: imageAspectRatio,
|
|
||||||
blurhash: imageInfo.blurhash,
|
|
||||||
properties: RoomTimelineItemProperties(reactions: aggregateReactions(eventItemProxy.reactions),
|
properties: RoomTimelineItemProperties(reactions: aggregateReactions(eventItemProxy.reactions),
|
||||||
deliveryStatus: eventItemProxy.deliveryStatus,
|
deliveryStatus: eventItemProxy.deliveryStatus,
|
||||||
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
|
orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts),
|
||||||
@@ -522,44 +510,22 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil
|
let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil
|
||||||
let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption)
|
let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption)
|
||||||
|
|
||||||
let thumbnailSource = messageContent.info?.thumbnailSource.map { MediaSourceProxy(source: $0, mimeType: messageContent.info?.thumbnailInfo?.mimetype) }
|
let thumbnailInfo = ImageInfoProxy(source: messageContent.info?.thumbnailSource,
|
||||||
|
width: messageContent.info?.thumbnailInfo?.width,
|
||||||
|
height: messageContent.info?.thumbnailInfo?.height,
|
||||||
|
mimeType: messageContent.info?.thumbnailInfo?.mimetype)
|
||||||
|
|
||||||
var thumbnailSize: CGSize?
|
let imageInfo = ImageInfoProxy(source: messageContent.source,
|
||||||
var thumbnailAspectRatio: CGFloat?
|
width: messageContent.info?.width,
|
||||||
|
height: messageContent.info?.height,
|
||||||
if let thumbnailInfo = messageContent.info?.thumbnailInfo,
|
mimeType: messageContent.info?.mimetype)
|
||||||
let height = thumbnailInfo.height,
|
|
||||||
let width = thumbnailInfo.width {
|
|
||||||
thumbnailSize = .init(width: CGFloat(width), height: CGFloat(height))
|
|
||||||
|
|
||||||
if width > 0, height > 0 {
|
|
||||||
thumbnailAspectRatio = CGFloat(width) / CGFloat(height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var imageSize: CGSize?
|
|
||||||
var imageAspectRatio: CGFloat?
|
|
||||||
|
|
||||||
if let imageInfo = messageContent.info,
|
|
||||||
let height = imageInfo.height,
|
|
||||||
let width = imageInfo.width {
|
|
||||||
imageSize = .init(width: CGFloat(width), height: CGFloat(height))
|
|
||||||
|
|
||||||
if width > 0, height > 0 {
|
|
||||||
imageAspectRatio = CGFloat(width) / CGFloat(height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .init(filename: messageContent.filename,
|
return .init(filename: messageContent.filename,
|
||||||
caption: messageContent.caption,
|
caption: messageContent.caption,
|
||||||
formattedCaption: formattedCaption,
|
formattedCaption: formattedCaption,
|
||||||
formattedCaptionHTMLString: htmlCaption,
|
formattedCaptionHTMLString: htmlCaption,
|
||||||
source: MediaSourceProxy(source: messageContent.source, mimeType: messageContent.info?.mimetype),
|
imageInfo: imageInfo,
|
||||||
size: imageSize,
|
thumbnailInfo: thumbnailInfo,
|
||||||
aspectRatio: imageAspectRatio,
|
|
||||||
thumbnailSource: thumbnailSource,
|
|
||||||
thumbnailSize: thumbnailSize,
|
|
||||||
thumbnailAspectRatio: thumbnailAspectRatio,
|
|
||||||
blurhash: messageContent.info?.blurhash,
|
blurhash: messageContent.info?.blurhash,
|
||||||
contentType: UTType(mimeType: messageContent.info?.mimetype, fallbackFilename: messageContent.filename))
|
contentType: UTType(mimeType: messageContent.info?.mimetype, fallbackFilename: messageContent.filename))
|
||||||
}
|
}
|
||||||
@@ -568,45 +534,23 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil
|
let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil
|
||||||
let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption)
|
let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption)
|
||||||
|
|
||||||
let thumbnailSource = messageContent.info?.thumbnailSource.map { MediaSourceProxy(source: $0, mimeType: messageContent.info?.thumbnailInfo?.mimetype) }
|
let thumbnailInfo = ImageInfoProxy(source: messageContent.info?.thumbnailSource,
|
||||||
|
width: messageContent.info?.thumbnailInfo?.width,
|
||||||
|
height: messageContent.info?.thumbnailInfo?.height,
|
||||||
|
mimeType: messageContent.info?.thumbnailInfo?.mimetype)
|
||||||
|
|
||||||
var thumbnailSize: CGSize?
|
let videoInfo = VideoInfoProxy(source: messageContent.source,
|
||||||
var thumbnailAspectRatio: CGFloat?
|
duration: messageContent.info?.duration ?? 0,
|
||||||
|
width: messageContent.info?.width,
|
||||||
if let thumbnailInfo = messageContent.info?.thumbnailInfo,
|
height: messageContent.info?.height,
|
||||||
let height = thumbnailInfo.height,
|
mimeType: messageContent.info?.mimetype)
|
||||||
let width = thumbnailInfo.width {
|
|
||||||
thumbnailSize = .init(width: CGFloat(width), height: CGFloat(height))
|
|
||||||
|
|
||||||
if width > 0, height > 0 {
|
|
||||||
thumbnailAspectRatio = CGFloat(width) / CGFloat(height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var videoSize: CGSize?
|
|
||||||
var videoAspectRatio: CGFloat?
|
|
||||||
|
|
||||||
if let videoInfo = messageContent.info,
|
|
||||||
let height = videoInfo.height,
|
|
||||||
let width = videoInfo.width {
|
|
||||||
videoSize = .init(width: CGFloat(width), height: CGFloat(height))
|
|
||||||
|
|
||||||
if width > 0, height > 0 {
|
|
||||||
videoAspectRatio = CGFloat(width) / CGFloat(height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .init(filename: messageContent.filename,
|
return .init(filename: messageContent.filename,
|
||||||
caption: messageContent.caption,
|
caption: messageContent.caption,
|
||||||
formattedCaption: formattedCaption,
|
formattedCaption: formattedCaption,
|
||||||
formattedCaptionHTMLString: htmlCaption,
|
formattedCaptionHTMLString: htmlCaption,
|
||||||
duration: messageContent.info?.duration ?? 0,
|
videoInfo: videoInfo,
|
||||||
source: MediaSourceProxy(source: messageContent.source, mimeType: messageContent.info?.mimetype),
|
thumbnailInfo: thumbnailInfo,
|
||||||
size: videoSize,
|
|
||||||
aspectRatio: videoAspectRatio,
|
|
||||||
thumbnailSource: thumbnailSource,
|
|
||||||
thumbnailSize: thumbnailSize,
|
|
||||||
thumbnailAspectRatio: thumbnailAspectRatio,
|
|
||||||
blurhash: messageContent.info?.blurhash,
|
blurhash: messageContent.info?.blurhash,
|
||||||
contentType: UTType(mimeType: messageContent.info?.mimetype, fallbackFilename: messageContent.filename))
|
contentType: UTType(mimeType: messageContent.info?.mimetype, fallbackFilename: messageContent.filename))
|
||||||
}
|
}
|
||||||
@@ -821,3 +765,12 @@ private extension RepliedToEventDetails {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension Receipt {
|
||||||
|
var dateTimestamp: Date? {
|
||||||
|
guard let timestamp else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return Date(timeIntervalSince1970: TimeInterval(timestamp / 1000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e96e572bd736c19bb9f3bd37216bd464276c9a1ebbd715fdae27973f9b6f36cc
|
oid sha256:82ce96aa44eb950fb131e85d6ceeab485a5985fb48c30b4d954c65602e77686a
|
||||||
size 333847
|
size 281497
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e96e572bd736c19bb9f3bd37216bd464276c9a1ebbd715fdae27973f9b6f36cc
|
oid sha256:82ce96aa44eb950fb131e85d6ceeab485a5985fb48c30b4d954c65602e77686a
|
||||||
size 333847
|
size 281497
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:13f6dfd3e07222ae3757076e453cf99695424db88c8cc85fede448ea64e70519
|
oid sha256:d08539366199fb29c4f2b01b6b399426b928a0eaae94306441d0a6cf34be4a13
|
||||||
size 276862
|
size 225250
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:13f6dfd3e07222ae3757076e453cf99695424db88c8cc85fede448ea64e70519
|
oid sha256:d08539366199fb29c4f2b01b6b399426b928a0eaae94306441d0a6cf34be4a13
|
||||||
size 276862
|
size 225250
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:bd110afc7d00be27146e58474b810415af9dc7d5ed6faf1536419f01f41f9a3a
|
oid sha256:26f2e07fa87d8ae6a910f30e992cf45e528715093663f40739e085c1349a6b31
|
||||||
size 404161
|
size 218285
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:bd110afc7d00be27146e58474b810415af9dc7d5ed6faf1536419f01f41f9a3a
|
oid sha256:26f2e07fa87d8ae6a910f30e992cf45e528715093663f40739e085c1349a6b31
|
||||||
size 404161
|
size 218285
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ef76e5d6f293ae298df0e70744ffed330e1fcb4a1e1157fbd7578e59bbe52fd4
|
oid sha256:23b75089dd40fc4c0c8cbaeb97f9659b19c22621133442e09ec7fc962cbae091
|
||||||
size 336667
|
size 167471
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ef76e5d6f293ae298df0e70744ffed330e1fcb4a1e1157fbd7578e59bbe52fd4
|
oid sha256:23b75089dd40fc4c0c8cbaeb97f9659b19c22621133442e09ec7fc962cbae091
|
||||||
size 336667
|
size 167471
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f0f9e472dd0a7675c25bc5856aca8f3e8ed94b157aa2dea3683e8a7cf17b588c
|
oid sha256:ba0a7cbad0cae44f11230bfd240445eeca207ddf22d6e3b5882e69d448cf2a9b
|
||||||
size 94892
|
size 95167
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f0f9e472dd0a7675c25bc5856aca8f3e8ed94b157aa2dea3683e8a7cf17b588c
|
oid sha256:ba0a7cbad0cae44f11230bfd240445eeca207ddf22d6e3b5882e69d448cf2a9b
|
||||||
size 94892
|
size 95167
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8e4e25d87b4335854f8cd67072ef0e8e89a50a9a56dbc10b6cf01ee725731767
|
oid sha256:d74e45a7d9ceb6b72d3c64aa300fbc122f18aef4c3b6cef630850c3cd242a8c4
|
||||||
size 52197
|
size 52016
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8e4e25d87b4335854f8cd67072ef0e8e89a50a9a56dbc10b6cf01ee725731767
|
oid sha256:d74e45a7d9ceb6b72d3c64aa300fbc122f18aef4c3b6cef630850c3cd242a8c4
|
||||||
size 52197
|
size 52016
|
||||||
|
|||||||
@@ -150,8 +150,8 @@ class LoggingTests: XCTestCase {
|
|||||||
sender: .init(id: "sender"),
|
sender: .init(id: "sender"),
|
||||||
content: .init(filename: "ImageString",
|
content: .init(filename: "ImageString",
|
||||||
caption: "ImageString",
|
caption: "ImageString",
|
||||||
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"),
|
imageInfo: .mockImage,
|
||||||
thumbnailSource: nil))
|
thumbnailInfo: nil))
|
||||||
let videoMessage = VideoRoomTimelineItem(id: .randomEvent,
|
let videoMessage = VideoRoomTimelineItem(id: .randomEvent,
|
||||||
timestamp: "",
|
timestamp: "",
|
||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
@@ -161,9 +161,8 @@ class LoggingTests: XCTestCase {
|
|||||||
sender: .init(id: "sender"),
|
sender: .init(id: "sender"),
|
||||||
content: .init(filename: "VideoString",
|
content: .init(filename: "VideoString",
|
||||||
caption: "VideoString",
|
caption: "VideoString",
|
||||||
duration: 0,
|
videoInfo: .mockVideo,
|
||||||
source: nil,
|
thumbnailInfo: nil))
|
||||||
thumbnailSource: nil))
|
|
||||||
let fileMessage = FileRoomTimelineItem(id: .randomEvent,
|
let fileMessage = FileRoomTimelineItem(id: .randomEvent,
|
||||||
timestamp: "",
|
timestamp: "",
|
||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user