Video messages on timeline (#304)

* Create timeline item and view for video

* Create video timeline items in factory, do not bubble them

* Add changelog

* Update packages

* Revert DTCoreText update
This commit is contained in:
ismailgulek
2022-11-10 13:45:35 +03:00
committed by GitHub
parent f7e86fe73f
commit 34a5b53038
13 changed files with 285 additions and 24 deletions

View File

@@ -80,6 +80,7 @@
2BA59D0AEFB4B82A2EC2A326 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; };
2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */; };
2C0CE61E5DC177938618E0B1 /* RootRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90733775209F4D4D366A268F /* RootRouterType.swift */; };
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */; };
2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086B997409328F091EBA43CE /* RoomScreenUITests.swift */; };
2F30EFEB7BD39242D1AD96F3 /* LoginViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1FB768A24FDD2A5CA16E3C /* LoginViewModelProtocol.swift */; };
2F94054F50E312AF30BE07F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B21E611DADDEF00307E7AC /* String.swift */; };
@@ -96,6 +97,7 @@
35E975CFDA60E05362A7CF79 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 1222DB76B917EB8A55365BA5 /* target.yml */; };
368C8758FCD079E6AAA18C2C /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */; };
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */; };
36C10EDEDC0466E3A9D63132 /* VideoRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4B5B19A10D3F7C2BC5315DF /* VideoRoomTimelineItem.swift */; };
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; };
388FD50AC66E9E684DDFA9D8 /* ServerSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D2C0950F8196232D88045C /* ServerSelectionScreen.swift */; };
38C76D586404C1FDED095F3A /* LoginModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31B01468022EC826CB2FD2C0 /* LoginModels.swift */; };
@@ -151,6 +153,7 @@
6298AB0906DDD3525CD78C6B /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 9573B94B1C86C6DF751AF3FD /* SwiftState */; };
62BBF5BE7B905222F0477FF2 /* MediaSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8210612D17A39369480FC183 /* MediaSource.swift */; };
63C9AF0FB8278AF1C0388A0C /* TemplateModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAB10E673916D2B8D21FD197 /* TemplateModels.swift */; };
64F43D7390DA2A0AFD6BA911 /* VideoRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1941C8817E6B6971BA4415F5 /* VideoRoomTimelineView.swift */; };
64FF5CB4E35971255872E1BB /* AuthenticationServiceProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F0CB536D1C3CC15AA740CC6 /* AuthenticationServiceProxyProtocol.swift */; };
663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.swift */; };
6647430A45B4A8E692909A8F /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77C060C2ACC4CB7336A29E7 /* EmoteRoomTimelineItem.swift */; };
@@ -440,6 +443,7 @@
184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecorationTimelineItemProtocol.swift; sourceTree = "<group>"; };
18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxy.swift; sourceTree = "<group>"; };
193FB285430D3956B6E61E4D /* UserIndicatorViewPresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorViewPresentable.swift; sourceTree = "<group>"; };
1941C8817E6B6971BA4415F5 /* VideoRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineView.swift; sourceTree = "<group>"; };
1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+Untranslated.swift"; sourceTree = "<group>"; };
1A63815AD6A5C306453342F2 /* ImageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineItem.swift; sourceTree = "<group>"; };
1C429043E986008B97736636 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -695,6 +699,7 @@
A436057DBEA1A23CA8CB1FD7 /* UIFont+AttributedStringBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+AttributedStringBuilder.h"; sourceTree = "<group>"; };
A443FAE2EE820A5790C35C8D /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = "<group>"; };
A4756C5A8C8649AD6C10C615 /* MockUserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserSession.swift; sourceTree = "<group>"; };
A4B5B19A10D3F7C2BC5315DF /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; };
A64F0DB78E0AC23C91AD89EF /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = mk.lproj/Localizable.strings; sourceTree = "<group>"; };
A65F140F9FE5E8D4DAEFF354 /* RoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxy.swift; sourceTree = "<group>"; };
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = "<group>"; };
@@ -840,6 +845,7 @@
F15BE37BE2FB86E00C8D150A /* AggregratedReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregratedReaction.swift; sourceTree = "<group>"; };
F23BA6D4842D53C5AC9B7584 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nn; path = nn.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
F2D58333B377888012740101 /* LoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = "<group>"; };
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = "<group>"; };
F506C6ADB1E1DA6638078E11 /* UITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
F5C4AF6E3885730CD560311C /* ScreenshotDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotDetector.swift; sourceTree = "<group>"; };
F6A8C632CEF4600107792899 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = "<group>"; };
@@ -1361,6 +1367,7 @@
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */,
3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */,
A05707BF550D770168A406DB /* LoginViewModelTests.swift */,
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */,
6FB31A32C93D94930B253FBF /* PermalinkBuilderTests.swift */,
93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */,
F03C9D319676F3C0DC6B0203 /* ScreenshotDetectorTests.swift */,
@@ -1388,6 +1395,7 @@
289FA233E896FBC5956C67E0 /* RoomTimelineItemProperties.swift */,
A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */,
F6A8C632CEF4600107792899 /* TextRoomTimelineItem.swift */,
A4B5B19A10D3F7C2BC5315DF /* VideoRoomTimelineItem.swift */,
);
path = Items;
sourceTree = "<group>";
@@ -1697,6 +1705,7 @@
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */,
6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */,
F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */,
1941C8817E6B6971BA4415F5 /* VideoRoomTimelineView.swift */,
);
path = Timeline;
sourceTree = "<group>";
@@ -2349,6 +2358,7 @@
0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */,
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */,
1E59B77A0B2CE83DCC1B203C /* LoginViewModelTests.swift in Sources */,
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */,
27E9263DA75E266690A37EB1 /* PermalinkBuilderTests.swift in Sources */,
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */,
EA31DD9043B91ECB8E45A9A6 /* ScreenshotDetectorTests.swift in Sources */,
@@ -2615,6 +2625,8 @@
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */,
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */,
36C10EDEDC0466E3A9D63132 /* VideoRoomTimelineItem.swift in Sources */,
64F43D7390DA2A0AFD6BA911 /* VideoRoomTimelineView.swift in Sources */,
6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */,
6DF37000571B1BC6D134CC9E /* WeakDictionary.swift in Sources */,
32BA37B01B05261FCF2D4B45 /* WeakDictionaryKeyReference.swift in Sources */,
@@ -3244,7 +3256,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = "1.0.17-alpha";
version = "1.0.18-alpha";
};
};
96495DD8554E2F39D3954354 /* XCRemoteSwiftPackageReference "posthog-ios" */ = {

View File

@@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/openid/AppAuth-iOS",
"state" : {
"revision" : "33660c271c961f8ce1084cc13f2ea8195e864f7d",
"version" : "1.5.0"
"revision" : "3d36a58a2b736f7bc499453e996a704929b25080",
"version" : "1.6.0"
}
},
{
@@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/devicekit/DeviceKit",
"state" : {
"revision" : "20e0991f3975916ab0f6d58db84d8bc64f883537",
"version" : "4.7.0"
"revision" : "d37e70cb2646666dcf276d7d3d4a9760a41ff8a6",
"version" : "4.9.0"
}
},
{
@@ -68,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/onevcat/Kingfisher",
"state" : {
"revision" : "022e4eeb79f817748544b318b991d9a70036bbf8",
"version" : "7.2.4"
"revision" : "44e891bdb61426a95e31492a67c7c0dfad1f87c5",
"version" : "7.4.1"
}
},
{
@@ -78,7 +78,7 @@
"location" : "https://github.com/matrix-org/matrix-analytics-events",
"state" : {
"branch" : "main",
"revision" : "53ad46ba1ea1ee8f21139dda3c351890846a202f"
"revision" : "4bcc7f566b165cd2d8fde07d23bda77e1d9fbb2d"
}
},
{
@@ -86,8 +86,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"state" : {
"revision" : "85335b5f6766753100983dced122731dfa5fa308",
"version" : "1.0.17-alpha"
"revision" : "c8494d858cfb60eb6167d76790b819c9dc15e822",
"version" : "1.0.18-alpha"
}
},
{
@@ -102,16 +102,16 @@
{
"identity" : "sentry-cocoa",
"kind" : "remoteSourceControl",
"location" : "https://github.com/getsentry/sentry-cocoa.git",
"location" : "https://github.com/getsentry/sentry-cocoa",
"state" : {
"revision" : "f906913c14eadb6fd31988db1ea9ff10aec9f198",
"version" : "7.18.1"
"revision" : "71fd3032635fed58ae1c1ba22bb7ffa158dbb5ee",
"version" : "7.30.2"
}
},
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "f29e2014f6230cf7d5138fc899da51c7f513d467",
"version" : "1.10.0"

View File

@@ -142,7 +142,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
}
private var shouldAvoidBubbling: Bool {
timelineItem is ImageRoomTimelineItem
timelineItem is ImageRoomTimelineItem || timelineItem is VideoRoomTimelineItem
}
private var alignment: HorizontalAlignment {

View File

@@ -0,0 +1,112 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import SwiftUI
struct VideoRoomTimelineView: View {
let timelineItem: VideoRoomTimelineItem
var body: some View {
TimelineStyler(timelineItem: timelineItem) {
if let image = timelineItem.image {
thumbnail(with: image)
} else if let blurhash = timelineItem.blurhash,
// Build a small blurhash image so that it's fast
let image = UIImage(blurHash: blurhash, size: .init(width: 10.0, height: 10.0)) {
thumbnail(with: image)
} else {
ZStack {
Rectangle()
.foregroundColor(.element.systemGray6)
.opacity(0.3)
ProgressView("Loading")
.frame(maxWidth: .infinity)
}
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
}
}
.id(timelineItem.id)
.animation(.elementDefault, value: timelineItem.image)
.frame(maxHeight: 1000.0)
}
@ViewBuilder
private func thumbnail(with image: UIImage) -> some View {
ZStack {
Image(uiImage: image)
.resizable()
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
Image(systemName: "play.circle.fill")
.resizable()
.frame(width: 50, height: 50)
.background(.ultraThinMaterial, in: Circle())
.foregroundColor(.white)
}
}
}
struct VideoRoomTimelineView_Previews: PreviewProvider {
static var previews: some View {
body.preferredColorScheme(.light)
body.preferredColorScheme(.dark)
body.preferredColorScheme(.light)
.timelineStyle(.plain)
body.preferredColorScheme(.dark)
.timelineStyle(.plain)
}
@ViewBuilder
static var body: some View {
VStack(spacing: 20.0) {
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
text: "Some video",
timestamp: "Now",
inGroupState: .single,
isOutgoing: false,
senderId: "Bob",
duration: 21,
source: nil,
thumbnailSource: nil,
image: UIImage(systemName: "photo")))
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
text: "Some other video",
timestamp: "Now",
inGroupState: .single,
isOutgoing: false,
senderId: "Bob",
duration: 22,
source: nil,
thumbnailSource: nil,
image: nil))
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
text: "Blurhashed video",
timestamp: "Now",
inGroupState: .single,
isOutgoing: false,
senderId: "Bob",
duration: 23,
source: nil,
thumbnailSource: nil,
image: nil,
aspectRatio: 0.7,
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
}
}
}

View File

@@ -20,12 +20,7 @@ import Foundation
public extension AnalyticsEvent.UserProperties {
// Initializer for Element. Strips all Web properties.
init(ftueUseCaseSelection: FtueUseCaseSelection?, numFavouriteRooms: Int?, numSpaces: Int?, allChatsActiveFilter: AllChatsActiveFilter?) {
self.init(WebMetaSpaceFavouritesEnabled: nil,
WebMetaSpaceHomeAllRooms: nil,
WebMetaSpaceHomeEnabled: nil,
WebMetaSpaceOrphansEnabled: nil,
WebMetaSpacePeopleEnabled: nil,
allChatsActiveFilter: nil,
self.init(allChatsActiveFilter: nil,
ftueUseCaseSelection: ftueUseCaseSelection,
numFavouriteRooms: numFavouriteRooms,
numSpaces: numSpaces)

View File

@@ -98,7 +98,9 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
switch timelineItem {
case let item as ImageRoomTimelineItem:
await loadImageForTimelineItem(item)
await loadImageForImageTimelineItem(item)
case let item as VideoRoomTimelineItem:
await loadImageForVideoTimelineItem(item)
default:
break
}
@@ -235,7 +237,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
return .middle
}
private func loadImageForTimelineItem(_ timelineItem: ImageRoomTimelineItem) async {
private func loadImageForImageTimelineItem(_ timelineItem: ImageRoomTimelineItem) async {
if timelineItem.image != nil {
return
}
@@ -258,6 +260,30 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
break
}
}
private func loadImageForVideoTimelineItem(_ timelineItem: VideoRoomTimelineItem) async {
if timelineItem.image != nil {
return
}
guard let source = timelineItem.thumbnailSource else {
return
}
switch await mediaProvider.loadImageFromSource(source) {
case .success(let image):
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
var item = timelineItems[index] as? ImageRoomTimelineItem else {
return
}
item.image = image
timelineItems[index] = item
callbacks.send(.updatedTimelineItem(timelineItem.id))
case .failure:
break
}
}
private func loadUserAvatarForTimelineItem(_ timelineItem: EventBasedTimelineItemProtocol) async {
if timelineItem.shouldShowSenderDetails == false {

View File

@@ -109,3 +109,35 @@ extension MessageTimelineItem where Content == MatrixRustSDK.ImageMessageContent
content.info?.blurhash
}
}
extension MatrixRustSDK.VideoMessageContent: MessageContentProtocol { }
/// A timeline item that represents an `m.room.message` event with a `msgtype` of `m.image`.
extension MessageTimelineItem where Content == MatrixRustSDK.VideoMessageContent {
var source: MediaSource {
MediaSource(source: content.source)
}
var thumbnailSource: MediaSource? {
guard let src = content.info?.thumbnailSource else {
return nil
}
return MediaSource(source: src)
}
var duration: UInt64 {
content.info?.duration ?? 0
}
var width: CGFloat? {
content.info?.width.map(CGFloat.init)
}
var height: CGFloat? {
content.info?.height.map(CGFloat.init)
}
var blurhash: String? {
content.info?.blurhash
}
}

View File

@@ -0,0 +1,42 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
struct VideoRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equatable {
let id: String
let text: String
let timestamp: String
let inGroupState: TimelineItemInGroupState
let isOutgoing: Bool
let senderId: String
var senderDisplayName: String?
var senderAvatar: UIImage?
let duration: UInt64
let source: MediaSource?
let thumbnailSource: MediaSource?
var image: UIImage?
var width: CGFloat?
var height: CGFloat?
var aspectRatio: CGFloat?
var blurhash: String?
var properties = RoomTimelineItemProperties()
}

View File

@@ -59,6 +59,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
case .image(content: let content):
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
return buildImageTimelineItemFromMessage(message, isOutgoing, inGroupState, displayName, avatarImage)
case .video(let content):
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
return buildVideoTimelineItemFromMessage(message, isOutgoing, inGroupState, displayName, avatarImage)
case .notice(content: let content):
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
return buildNoticeTimelineItemFromMessage(message, isOutgoing, inGroupState, displayName, avatarImage)
@@ -187,6 +190,37 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
reactions: aggregateReactions(message.reactions)))
}
private func buildVideoTimelineItemFromMessage(_ message: MessageTimelineItem<VideoMessageContent>,
_ isOutgoing: Bool,
_ inGroupState: TimelineItemInGroupState,
_ displayName: String?,
_ avatarImage: UIImage?) -> RoomTimelineItemProtocol {
var aspectRatio: CGFloat?
if let width = message.width,
let height = message.height {
aspectRatio = width / height
}
return VideoRoomTimelineItem(id: message.id,
text: message.body,
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
inGroupState: inGroupState,
isOutgoing: isOutgoing,
senderId: message.sender,
senderDisplayName: displayName,
senderAvatar: avatarImage,
duration: message.duration,
source: message.source,
thumbnailSource: message.thumbnailSource,
image: mediaProvider.imageFromSource(message.thumbnailSource),
width: message.width,
height: message.height,
aspectRatio: aspectRatio,
blurhash: message.blurhash,
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
reactions: aggregateReactions(message.reactions)))
}
private func buildNoticeTimelineItemFromMessage(_ message: MessageTimelineItem<NoticeMessageContent>,
_ isOutgoing: Bool,

View File

@@ -23,6 +23,8 @@ struct RoomTimelineViewFactory: RoomTimelineViewFactoryProtocol {
return .text(item)
case let item as ImageRoomTimelineItem:
return .image(item)
case let item as VideoRoomTimelineItem:
return .video(item)
case let item as SeparatorRoomTimelineItem:
return .separator(item)
case let item as NoticeRoomTimelineItem:

View File

@@ -21,6 +21,7 @@ enum RoomTimelineViewProvider: Identifiable, Equatable {
case text(TextRoomTimelineItem)
case separator(SeparatorRoomTimelineItem)
case image(ImageRoomTimelineItem)
case video(VideoRoomTimelineItem)
case emote(EmoteRoomTimelineItem)
case notice(NoticeRoomTimelineItem)
case redacted(RedactedRoomTimelineItem)
@@ -34,6 +35,8 @@ enum RoomTimelineViewProvider: Identifiable, Equatable {
return item.id
case .image(let item):
return item.id
case .video(let item):
return item.id
case .emote(let item):
return item.id
case .notice(let item):
@@ -55,6 +58,8 @@ extension RoomTimelineViewProvider: View {
SeparatorRoomTimelineView(timelineItem: item)
case .image(let item):
ImageRoomTimelineView(timelineItem: item)
case .video(let item):
VideoRoomTimelineView(timelineItem: item)
case .emote(let item):
EmoteRoomTimelineView(timelineItem: item)
case .notice(let item):

1
changelog.d/237.feature Normal file
View File

@@ -0,0 +1 @@
Timeline: Display video messages.

View File

@@ -35,7 +35,7 @@ include:
packages:
MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.0.17-alpha
exactVersion: 1.0.18-alpha
# path: ../matrix-rust-components-swift
DesignKit:
path: ./