mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Story covers
This commit is contained in:
parent
d8d68722ae
commit
3e448e833e
@ -1707,7 +1707,7 @@ private final class NotificationServiceHandler {
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
resource = file.resource
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(_, _, _, preloadSize) = attribute {
|
||||
if case let .Video(_, _, _, preloadSize, _) = attribute {
|
||||
fetchSize = preloadSize.flatMap(Int64.init)
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Boo
|
||||
return false
|
||||
}
|
||||
for attribute in media.attributes {
|
||||
if case let .Video(_, _, flags, _) = attribute {
|
||||
if case let .Video(_, _, flags, _, _) = attribute {
|
||||
if flags.contains(.supportsStreaming) {
|
||||
return true
|
||||
}
|
||||
@ -41,7 +41,7 @@ public func isMediaStreamable(media: TelegramMediaFile) -> Bool {
|
||||
return false
|
||||
}
|
||||
for attribute in media.attributes {
|
||||
if case let .Video(_, _, flags, _) = attribute {
|
||||
if case let .Video(_, _, flags, _, _) = attribute {
|
||||
if flags.contains(.supportsStreaming) {
|
||||
return true
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ public final class AvatarVideoNode: ASDisplayNode {
|
||||
self.backgroundNode.image = nil
|
||||
|
||||
let videoId = photo.id?.id ?? peer.id.id._internalGetInt64Value()
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: false, storeAfterDownload: nil)
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
self.videoNode?.removeFromSupernode()
|
||||
|
@ -460,7 +460,7 @@ public final class ChatImportActivityScreen: ViewController {
|
||||
if let path = getAppBundle().path(forResource: "BlankVideo", ofType: "m4v"), let size = fileSize(path) {
|
||||
let decoration = ChatBubbleVideoDecoration(corners: ImageCorners(), nativeSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, backgroundColor: .black)
|
||||
|
||||
let dummyFile = TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [], preloadSize: nil)])
|
||||
let dummyFile = TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [], preloadSize: nil, coverTime: nil)])
|
||||
|
||||
let videoContent = NativeVideoContent(id: .message(1, EngineMedia.Id(namespace: 0, id: 1)), userLocation: .other, fileReference: .standalone(media: dummyFile), streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black, storeAfterDownload: nil)
|
||||
|
||||
|
@ -2576,7 +2576,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
case let .preview(dimensions, immediateThumbnailData, videoDuration):
|
||||
if let immediateThumbnailData {
|
||||
if let videoDuration {
|
||||
let thumbnailMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: index), partialReference: nil, resource: EmptyMediaResource(), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Video(duration: Double(videoDuration), size: dimensions ?? PixelDimensions(width: 1, height: 1), flags: [], preloadSize: nil)])
|
||||
let thumbnailMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: index), partialReference: nil, resource: EmptyMediaResource(), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Video(duration: Double(videoDuration), size: dimensions ?? PixelDimensions(width: 1, height: 1), flags: [], preloadSize: nil, coverTime: nil)])
|
||||
contentImageSpecs.append(ContentImageSpec(message: message, media: .file(thumbnailMedia), size: fitSize))
|
||||
} else {
|
||||
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: index), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||
|
@ -246,7 +246,7 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
||||
processed = true
|
||||
break inner
|
||||
}
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
messageText = strings.Message_VideoMessage
|
||||
processed = true
|
||||
|
@ -836,7 +836,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
||||
} else if let media = media as? TelegramMediaFile, !media.isAnimated {
|
||||
for attribute in media.attributes {
|
||||
switch attribute {
|
||||
case let .Video(_, dimensions, _, _):
|
||||
case let .Video(_, dimensions, _, _, _):
|
||||
isVideo = true
|
||||
if dimensions.height > 0 {
|
||||
if CGFloat(dimensions.width) / CGFloat(dimensions.height) > 1.33 {
|
||||
|
@ -1235,7 +1235,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
}
|
||||
if let file = file {
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(duration, _, _, _) = attribute, duration >= 30 {
|
||||
if case let .Video(duration, _, _, _, _) = attribute, duration >= 30 {
|
||||
hintSeekable = true
|
||||
break
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem {
|
||||
} else {
|
||||
return SharedMediaPlaybackData(type: .music, source: .telegramFile(reference: .webPage(webPage: WebpageReference(self.webPage), media: file), isCopyProtected: false, isViewOnce: false))
|
||||
}
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
return SharedMediaPlaybackData(type: .instantVideo, source: .telegramFile(reference: .webPage(webPage: WebpageReference(self.webPage), media: file), isCopyProtected: false, isViewOnce: false))
|
||||
} else {
|
||||
@ -99,7 +99,7 @@ final class InstantPageMediaPlaylistItem: SharedMediaPlaylistItem {
|
||||
|
||||
return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: false, caption: nil)
|
||||
}
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
return SharedMediaPlaybackDisplayData.instantVideo(author: nil, peer: nil, timestamp: 0)
|
||||
} else {
|
||||
|
@ -294,7 +294,7 @@ public func legacyEnqueueGifMessage(account: Account, data: Data, correlationId:
|
||||
let finalDimensions = TGMediaVideoConverter.dimensions(for: dimensions, adjustments: nil, preset: TGMediaVideoConversionPresetAnimation)
|
||||
|
||||
var fileAttributes: [TelegramMediaFileAttribute] = []
|
||||
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
|
||||
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil, coverTime: nil))
|
||||
fileAttributes.append(.FileName(fileName: fileName))
|
||||
fileAttributes.append(.Animated)
|
||||
|
||||
@ -336,7 +336,7 @@ public func legacyEnqueueVideoMessage(account: Account, data: Data, correlationI
|
||||
let finalDimensions = TGMediaVideoConverter.dimensions(for: dimensions, adjustments: nil, preset: TGMediaVideoConversionPresetAnimation)
|
||||
|
||||
var fileAttributes: [TelegramMediaFileAttribute] = []
|
||||
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
|
||||
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil, coverTime: nil))
|
||||
fileAttributes.append(.FileName(fileName: fileName))
|
||||
fileAttributes.append(.Animated)
|
||||
|
||||
@ -857,7 +857,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
||||
fileAttributes.append(.Animated)
|
||||
}
|
||||
if !asFile {
|
||||
fileAttributes.append(.Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
|
||||
fileAttributes.append(.Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil, coverTime: nil))
|
||||
if let adjustments = adjustments {
|
||||
if adjustments.sendAsGif {
|
||||
fileAttributes.append(.Animated)
|
||||
|
@ -187,7 +187,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
let subject: ShareControllerSubject
|
||||
var actionCompletionText: String?
|
||||
if let video = entry.videoRepresentations.last, let peerReference = PeerReference(peer._asPeer()) {
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
subject = .media(videoFileReference.abstract)
|
||||
actionCompletionText = strongSelf.presentationData.strings.Gallery_VideoSaved
|
||||
} else {
|
||||
@ -279,7 +279,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
if let video = entry.videoRepresentations.last, let peerReference = PeerReference(self.peer._asPeer()) {
|
||||
if video != previousVideoRepresentations?.last {
|
||||
let mediaManager = self.context.sharedContext.mediaManager
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: entry.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: entry.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, category), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, useLargeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, storeAfterDownload: nil)
|
||||
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .overlay)
|
||||
videoNode.isUserInteractionEnabled = false
|
||||
|
@ -515,7 +515,7 @@ public final class PeerInfoAvatarListItemNode: ASDisplayNode {
|
||||
self.isReady.set(.single(true))
|
||||
}
|
||||
} else if let video = videoRepresentations.last, let peerReference = PeerReference(self.peer._asPeer()) {
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: fullSizeOnly, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, storeAfterDownload: nil)
|
||||
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
|
@ -279,7 +279,7 @@ public final class ShareProlongedLoadingContainerNode: ASDisplayNode, ShareConte
|
||||
if let postbox, let mediaManager = environment.mediaManager, let path = getAppBundle().path(forResource: "BlankVideo", ofType: "m4v"), let size = fileSize(path) {
|
||||
let decoration = ChatBubbleVideoDecoration(corners: ImageCorners(), nativeSize: CGSize(width: 100.0, height: 100.0), contentMode: .aspectFit, backgroundColor: .black)
|
||||
|
||||
let dummyFile = TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [], preloadSize: nil)])
|
||||
let dummyFile = TelegramMediaFile(fileId: EngineMedia.Id(namespace: 0, id: 1), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: 12345), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: size, attributes: [.Video(duration: 1, size: PixelDimensions(width: 100, height: 100), flags: [], preloadSize: nil, coverTime: nil)])
|
||||
|
||||
let videoContent = NativeVideoContent(id: .message(1, EngineMedia.Id(namespace: 0, id: 1)), userLocation: .other, fileReference: .standalone(media: dummyFile), streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black, storeAfterDownload: nil)
|
||||
|
||||
|
@ -144,7 +144,7 @@ private func preparedShareItem(postbox: Postbox, network: Network, to peerId: Pe
|
||||
let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true)
|
||||
|
||||
let resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: asset.url.path, adjustments: resourceAdjustments)
|
||||
return standaloneUploadedFile(postbox: postbox, network: network, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: finalDuration, size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags, preloadSize: nil)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024)
|
||||
return standaloneUploadedFile(postbox: postbox, network: network, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: finalDuration, size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags, preloadSize: nil, coverTime: nil)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024)
|
||||
|> mapError { _ -> PreparedShareItemError in
|
||||
return .generic
|
||||
}
|
||||
@ -210,7 +210,7 @@ private func preparedShareItem(postbox: Postbox, network: Network, to peerId: Pe
|
||||
let mimeType: String
|
||||
if converted {
|
||||
mimeType = "video/mp4"
|
||||
attributes = [.Video(duration: duration, size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height)), flags: [.supportsStreaming], preloadSize: nil), .Animated, .FileName(fileName: "animation.mp4")]
|
||||
attributes = [.Video(duration: duration, size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height)), flags: [.supportsStreaming], preloadSize: nil, coverTime: nil), .Animated, .FileName(fileName: "animation.mp4")]
|
||||
} else {
|
||||
mimeType = "animation/gif"
|
||||
attributes = [.ImageSize(size: PixelDimensions(width: Int32(dimensions.width), height: Int32(dimensions.height))), .Animated, .FileName(fileName: fileName ?? "animation.gif")]
|
||||
|
@ -1677,7 +1677,7 @@ private func monetizationEntries(
|
||||
}
|
||||
}
|
||||
|
||||
if isCreator {
|
||||
if isCreator && canViewRevenue {
|
||||
var switchOffAdds: Bool? = nil
|
||||
if let boostData, boostData.level >= premiumConfiguration.minChannelRestrictAdsLevel {
|
||||
switchOffAdds = adsRestricted
|
||||
|
@ -50,7 +50,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
||||
var isAnimated = false
|
||||
inner: for attribute in file.attributes {
|
||||
switch attribute {
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
refinedTag = .voiceOrInstantVideo
|
||||
} else {
|
||||
|
@ -6,7 +6,7 @@ import TelegramApi
|
||||
func dimensionsForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) -> PixelDimensions? {
|
||||
for attribute in attributes {
|
||||
switch attribute {
|
||||
case let .Video(_, size, _, _):
|
||||
case let .Video(_, size, _, _, _):
|
||||
return size
|
||||
case let .ImageSize(size):
|
||||
return size
|
||||
@ -20,7 +20,7 @@ func dimensionsForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) ->
|
||||
func durationForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) -> Double? {
|
||||
for attribute in attributes {
|
||||
switch attribute {
|
||||
case let .Video(duration, _, _, _):
|
||||
case let .Video(duration, _, _, _, _):
|
||||
return duration
|
||||
case let .Audio(_, duration, _, _, _):
|
||||
return Double(duration)
|
||||
@ -99,7 +99,7 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
|
||||
result.append(.ImageSize(size: PixelDimensions(width: w, height: h)))
|
||||
case .documentAttributeAnimated:
|
||||
result.append(.Animated)
|
||||
case let .documentAttributeVideo(flags, duration, w, h, preloadSize, _):
|
||||
case let .documentAttributeVideo(flags, duration, w, h, preloadSize, videoStart):
|
||||
var videoFlags = TelegramMediaVideoFlags()
|
||||
if (flags & (1 << 0)) != 0 {
|
||||
videoFlags.insert(.instantRoundVideo)
|
||||
@ -110,7 +110,7 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
videoFlags.insert(.isSilent)
|
||||
}
|
||||
result.append(.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: preloadSize))
|
||||
result.append(.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: preloadSize, coverTime: videoStart))
|
||||
case let .documentAttributeAudio(flags, duration, title, performer, waveform):
|
||||
let isVoice = (flags & (1 << 10)) != 0
|
||||
let waveformBuffer: Data? = waveform?.makeData()
|
||||
|
@ -701,7 +701,7 @@ func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaF
|
||||
attributes.append(.documentAttributeSticker(flags: flags, alt: displayText, stickerset: stickerSet, maskCoords: inputMaskCoords))
|
||||
case .HasLinkedStickers:
|
||||
attributes.append(.documentAttributeHasStickers)
|
||||
case let .Video(duration, size, videoFlags, preloadSize):
|
||||
case let .Video(duration, size, videoFlags, preloadSize, coverTime):
|
||||
var flags: Int32 = 0
|
||||
if videoFlags.contains(.instantRoundVideo) {
|
||||
flags |= (1 << 0)
|
||||
@ -715,8 +715,10 @@ func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaF
|
||||
if videoFlags.contains(.isSilent) {
|
||||
flags |= (1 << 3)
|
||||
}
|
||||
|
||||
attributes.append(.documentAttributeVideo(flags: flags, duration: duration, w: Int32(size.width), h: Int32(size.height), preloadPrefixSize: preloadSize, videoStartTs: nil))
|
||||
if let coverTime = coverTime, coverTime > 0.0 {
|
||||
flags |= (1 << 4)
|
||||
}
|
||||
attributes.append(.documentAttributeVideo(flags: flags, duration: duration, w: Int32(size.width), h: Int32(size.height), preloadPrefixSize: preloadSize, videoStartTs: coverTime))
|
||||
case let .Audio(isVoice, duration, title, performer, waveform):
|
||||
var flags: Int32 = 0
|
||||
if isVoice {
|
||||
@ -786,7 +788,7 @@ public func statsCategoryForFileWithAttributes(_ attributes: [TelegramMediaFileA
|
||||
} else {
|
||||
return .audio
|
||||
}
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(TelegramMediaVideoFlags.instantRoundVideo) {
|
||||
return .voiceMessages
|
||||
} else {
|
||||
|
@ -553,7 +553,7 @@ private func decryptedAttributes46(_ attributes: [TelegramMediaFileAttribute], t
|
||||
result.append(.documentAttributeSticker(alt: displayText, stickerset: stickerSet))
|
||||
case let .ImageSize(size):
|
||||
result.append(.documentAttributeImageSize(w: Int32(size.width), h: Int32(size.height)))
|
||||
case let .Video(duration, size, _, _):
|
||||
case let .Video(duration, size, _, _, _):
|
||||
result.append(.documentAttributeVideo(duration: Int32(duration), w: Int32(size.width), h: Int32(size.height)))
|
||||
case let .Audio(isVoice, duration, title, performer, waveform):
|
||||
var flags: Int32 = 0
|
||||
@ -612,7 +612,7 @@ private func decryptedAttributes73(_ attributes: [TelegramMediaFileAttribute], t
|
||||
result.append(.documentAttributeSticker(alt: displayText, stickerset: stickerSet))
|
||||
case let .ImageSize(size):
|
||||
result.append(.documentAttributeImageSize(w: Int32(size.width), h: Int32(size.height)))
|
||||
case let .Video(duration, size, videoFlags, _):
|
||||
case let .Video(duration, size, videoFlags, _, _):
|
||||
var flags: Int32 = 0
|
||||
if videoFlags.contains(.instantRoundVideo) {
|
||||
flags |= 1 << 0
|
||||
@ -675,7 +675,7 @@ private func decryptedAttributes101(_ attributes: [TelegramMediaFileAttribute],
|
||||
result.append(.documentAttributeSticker(alt: displayText, stickerset: stickerSet))
|
||||
case let .ImageSize(size):
|
||||
result.append(.documentAttributeImageSize(w: Int32(size.width), h: Int32(size.height)))
|
||||
case let .Video(duration, size, videoFlags, _):
|
||||
case let .Video(duration, size, videoFlags, _, _):
|
||||
var flags: Int32 = 0
|
||||
if videoFlags.contains(.instantRoundVideo) {
|
||||
flags |= 1 << 0
|
||||
@ -738,7 +738,7 @@ private func decryptedAttributes144(_ attributes: [TelegramMediaFileAttribute],
|
||||
result.append(.documentAttributeSticker(alt: displayText, stickerset: stickerSet))
|
||||
case let .ImageSize(size):
|
||||
result.append(.documentAttributeImageSize(w: Int32(size.width), h: Int32(size.height)))
|
||||
case let .Video(duration, size, videoFlags, _):
|
||||
case let .Video(duration, size, videoFlags, _, _):
|
||||
var flags: Int32 = 0
|
||||
if videoFlags.contains(.instantRoundVideo) {
|
||||
flags |= 1 << 0
|
||||
|
@ -610,7 +610,7 @@ extension TelegramMediaFileAttribute {
|
||||
}
|
||||
self = .Sticker(displayText: alt, packReference: packReference, maskData: nil)
|
||||
case let .documentAttributeVideo(duration, w, h):
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil)
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil, coverTime: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -642,7 +642,7 @@ extension TelegramMediaFileAttribute {
|
||||
if (flags & (1 << 0)) != 0 {
|
||||
videoFlags.insert(.instantRoundVideo)
|
||||
}
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: nil)
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: nil, coverTime: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -674,7 +674,7 @@ extension TelegramMediaFileAttribute {
|
||||
if (flags & (1 << 0)) != 0 {
|
||||
videoFlags.insert(.instantRoundVideo)
|
||||
}
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: nil)
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: nil, coverTime: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -706,7 +706,7 @@ extension TelegramMediaFileAttribute {
|
||||
if (flags & (1 << 0)) != 0 {
|
||||
videoFlags.insert(.instantRoundVideo)
|
||||
}
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: nil)
|
||||
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: nil, coverTime: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -821,7 +821,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
text = caption
|
||||
}
|
||||
if let file = file {
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil), .FileName(fileName: "video.mov")]
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil, coverTime: nil), .FileName(fileName: "video.mov")]
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if thumb.size != 0 {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
@ -1021,7 +1021,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
|
||||
loop: for attr in parsedAttributes {
|
||||
switch attr {
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
attributes.append(ConsumableContentMessageAttribute(consumed: false))
|
||||
}
|
||||
@ -1040,7 +1040,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
text = caption
|
||||
}
|
||||
if let file = file {
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil), .FileName(fileName: "video.mov")]
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil, coverTime: nil), .FileName(fileName: "video.mov")]
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if thumb.size != 0 {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
@ -1300,7 +1300,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
|
||||
loop: for attr in parsedAttributes {
|
||||
switch attr {
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
attributes.append(ConsumableContentMessageAttribute(consumed: false))
|
||||
}
|
||||
@ -1319,7 +1319,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
text = caption
|
||||
}
|
||||
if let file = file {
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil), .FileName(fileName: "video.mov")]
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil, coverTime: nil), .FileName(fileName: "video.mov")]
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if thumb.size != 0 {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
@ -1501,7 +1501,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
|
||||
loop: for attr in parsedAttributes {
|
||||
switch attr {
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
attributes.append(ConsumableContentMessageAttribute(consumed: false))
|
||||
}
|
||||
@ -1520,7 +1520,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
|
||||
text = caption
|
||||
}
|
||||
if let file = file {
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil), .FileName(fileName: "video.mov")]
|
||||
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil, coverTime: nil), .FileName(fileName: "video.mov")]
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if thumb.size != 0 {
|
||||
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
@ -235,7 +235,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
|
||||
case Sticker(displayText: String, packReference: StickerPackReference?, maskData: StickerMaskCoords?)
|
||||
case ImageSize(size: PixelDimensions)
|
||||
case Animated
|
||||
case Video(duration: Double, size: PixelDimensions, flags: TelegramMediaVideoFlags, preloadSize: Int32?)
|
||||
case Video(duration: Double, size: PixelDimensions, flags: TelegramMediaVideoFlags, preloadSize: Int32?, coverTime: Double?)
|
||||
case Audio(isVoice: Bool, duration: Int, title: String?, performer: String?, waveform: Data?)
|
||||
case HasLinkedStickers
|
||||
case hintFileIsLarge
|
||||
@ -262,7 +262,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
|
||||
duration = Double(decoder.decodeInt32ForKey("du", orElse: 0))
|
||||
}
|
||||
|
||||
self = .Video(duration: duration, size: PixelDimensions(width: decoder.decodeInt32ForKey("w", orElse: 0), height: decoder.decodeInt32ForKey("h", orElse: 0)), flags: TelegramMediaVideoFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0)), preloadSize: decoder.decodeOptionalInt32ForKey("prs"))
|
||||
self = .Video(duration: duration, size: PixelDimensions(width: decoder.decodeInt32ForKey("w", orElse: 0), height: decoder.decodeInt32ForKey("h", orElse: 0)), flags: TelegramMediaVideoFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0)), preloadSize: decoder.decodeOptionalInt32ForKey("prs"), coverTime: decoder.decodeOptionalDoubleForKey("ct"))
|
||||
case typeAudio:
|
||||
let waveformBuffer = decoder.decodeBytesForKeyNoCopy("wf")
|
||||
var waveform: Data?
|
||||
@ -309,7 +309,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
|
||||
encoder.encodeInt32(Int32(size.height), forKey: "h")
|
||||
case .Animated:
|
||||
encoder.encodeInt32(typeAnimated, forKey: "t")
|
||||
case let .Video(duration, size, flags, preloadSize):
|
||||
case let .Video(duration, size, flags, preloadSize, coverTime):
|
||||
encoder.encodeInt32(typeVideo, forKey: "t")
|
||||
encoder.encodeDouble(duration, forKey: "dur")
|
||||
encoder.encodeInt32(Int32(size.width), forKey: "w")
|
||||
@ -320,6 +320,11 @@ public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "prs")
|
||||
}
|
||||
if let coverTime = coverTime {
|
||||
encoder.encodeDouble(coverTime, forKey: "ct")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "ct")
|
||||
}
|
||||
case let .Audio(isVoice, duration, title, performer, waveform):
|
||||
encoder.encodeInt32(typeAudio, forKey: "t")
|
||||
encoder.encodeInt32(isVoice ? 1 : 0, forKey: "iv")
|
||||
@ -592,7 +597,7 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
|
||||
|
||||
public var isInstantVideo: Bool {
|
||||
for attribute in self.attributes {
|
||||
if case .Video(_, _, let flags, _) = attribute {
|
||||
if case .Video(_, _, let flags, _, _) = attribute {
|
||||
return flags.contains(.instantRoundVideo)
|
||||
}
|
||||
}
|
||||
@ -601,7 +606,7 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
|
||||
|
||||
public var preloadSize: Int32? {
|
||||
for attribute in self.attributes {
|
||||
if case .Video(_, _, _, let preloadSize) = attribute {
|
||||
if case .Video(_, _, _, let preloadSize, _) = attribute {
|
||||
return preloadSize
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, threadId:
|
||||
if let dimensions = externalReference.content?.dimensions {
|
||||
fileAttributes.append(.ImageSize(size: dimensions))
|
||||
if externalReference.type == "gif" {
|
||||
fileAttributes.append(.Video(duration: externalReference.content?.duration ?? 0.0, size: dimensions, flags: [], preloadSize: nil))
|
||||
fileAttributes.append(.Video(duration: externalReference.content?.duration ?? 0.0, size: dimensions, flags: [], preloadSize: nil, coverTime: nil))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,12 @@ import TelegramApi
|
||||
|
||||
public enum EngineStoryInputMedia {
|
||||
case image(dimensions: PixelDimensions, data: Data, stickers: [TelegramMediaFile])
|
||||
case video(dimensions: PixelDimensions, duration: Double, resource: TelegramMediaResource, firstFrameFile: TempBoxFile?, stickers: [TelegramMediaFile])
|
||||
case video(dimensions: PixelDimensions, duration: Double, resource: TelegramMediaResource, firstFrameFile: TempBoxFile?, stickers: [TelegramMediaFile], coverTime: Double?)
|
||||
case existing(media: Media)
|
||||
|
||||
var embeddedStickers: [TelegramMediaFile] {
|
||||
switch self {
|
||||
case let .image(_, _, stickers), let .video(_, _, _, _, stickers):
|
||||
case let .image(_, _, stickers), let .video(_, _, _, _, stickers, _):
|
||||
return stickers
|
||||
case .existing:
|
||||
return []
|
||||
@ -849,7 +849,7 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput
|
||||
flags: []
|
||||
)
|
||||
return imageMedia
|
||||
case let .video(dimensions, duration, resource, firstFrameFile, _):
|
||||
case let .video(dimensions, duration, resource, firstFrameFile, _, coverTime):
|
||||
var previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
if let firstFrameFile = firstFrameFile {
|
||||
account.postbox.mediaBox.storeCachedResourceRepresentation(resource.id.stringRepresentation, representationId: "first-frame", keepDuration: .general, tempFile: firstFrameFile)
|
||||
@ -871,7 +871,7 @@ private func prepareUploadStoryContent(account: Account, media: EngineStoryInput
|
||||
mimeType: "video/mp4",
|
||||
size: nil,
|
||||
attributes: [
|
||||
TelegramMediaFileAttribute.Video(duration: duration, size: dimensions, flags: .supportsStreaming, preloadSize: nil)
|
||||
TelegramMediaFileAttribute.Video(duration: duration, size: dimensions, flags: .supportsStreaming, preloadSize: nil, coverTime: coverTime)
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -144,7 +144,7 @@ public extension ImportSticker {
|
||||
fileAttributes.append(.FileName(fileName: "sticker.webm"))
|
||||
fileAttributes.append(.Animated)
|
||||
fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil))
|
||||
fileAttributes.append(.Video(duration: self.duration ?? 3.0, size: self.dimensions, flags: [], preloadSize: nil))
|
||||
fileAttributes.append(.Video(duration: self.duration ?? 3.0, size: self.dimensions, flags: [], preloadSize: nil, coverTime: nil))
|
||||
} else if self.mimeType == "application/x-tgsticker" {
|
||||
fileAttributes.append(.FileName(fileName: "sticker.tgs"))
|
||||
fileAttributes.append(.Animated)
|
||||
|
@ -330,7 +330,7 @@ public func mediaContentKind(_ media: EngineMedia, message: EngineMessage? = nil
|
||||
return .file(performer)
|
||||
}
|
||||
}
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if file.isAnimated {
|
||||
result = .animation
|
||||
} else {
|
||||
|
@ -235,7 +235,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
} else {
|
||||
for attribute in file.attributes {
|
||||
switch attribute {
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
type = .round
|
||||
} else {
|
||||
|
@ -172,7 +172,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
|
||||
imageDimensions = externalReference.content?.dimensions?.cgSize
|
||||
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = imageResource
|
||||
, let dimensions = content.dimensions {
|
||||
videoFileReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil)]))
|
||||
videoFileReference = .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
imageResource = nil
|
||||
}
|
||||
case let .internalReference(internalReference):
|
||||
|
@ -272,7 +272,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
strongSelf.mediaBackgroundNode.image = backgroundImage
|
||||
|
||||
if let image = image, let video = image.videoRepresentations.last, let id = image.id?.id {
|
||||
let videoFileReference = FileMediaReference.message(message: MessageReference(item.message), media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: image.representations, videoThumbnails: [], immediateThumbnailData: image.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.message(message: MessageReference(item.message), media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: image.representations, videoThumbnails: [], immediateThumbnailData: image.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, "action"), userLocation: .peer(item.message.id.peerId), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, storeAfterDownload: nil)
|
||||
if videoContent.id != strongSelf.videoContent?.id {
|
||||
let mediaManager = item.context.sharedContext.mediaManager
|
||||
|
@ -650,7 +650,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
let messageTheme = arguments.incoming ? arguments.presentationData.theme.theme.chat.message.incoming : arguments.presentationData.theme.theme.chat.message.outgoing
|
||||
let isInstantVideo = arguments.file.isInstantVideo
|
||||
for attribute in arguments.file.attributes {
|
||||
if case let .Video(videoDuration, _, flags, _) = attribute, flags.contains(.instantRoundVideo) {
|
||||
if case let .Video(videoDuration, _, flags, _, _) = attribute, flags.contains(.instantRoundVideo) {
|
||||
isAudio = true
|
||||
isVoice = true
|
||||
|
||||
@ -1558,7 +1558,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
var isVoice = false
|
||||
var audioDuration: Int32?
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(duration, _, flags, _) = attribute, flags.contains(.instantRoundVideo) {
|
||||
if case let .Video(duration, _, flags, _, _) = attribute, flags.contains(.instantRoundVideo) {
|
||||
isAudio = true
|
||||
isVoice = true
|
||||
audioDuration = Int32(duration)
|
||||
|
@ -27,7 +27,7 @@ private func mediaMergeableStyle(_ media: Media) -> ChatMessageMerge {
|
||||
switch attribute {
|
||||
case .Sticker:
|
||||
return .semanticallyMerged
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
return .none
|
||||
}
|
||||
@ -423,7 +423,7 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible
|
||||
viewClassName = ChatMessageStickerItemNode.self
|
||||
}
|
||||
break loop
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
viewClassName = ChatMessageBubbleItemNode.self
|
||||
break loop
|
||||
|
@ -203,7 +203,7 @@ public final class ChatMessageAccessibilityData {
|
||||
text = item.presentationData.strings.VoiceOver_Chat_MusicTitle(title, performer).string
|
||||
text.append(item.presentationData.strings.VoiceOver_Chat_Duration(durationString).string)
|
||||
}
|
||||
case let .Video(duration, _, flags, _):
|
||||
case let .Video(duration, _, flags, _, _):
|
||||
isSpecialFile = true
|
||||
if isSelected == nil {
|
||||
hint = item.presentationData.strings.VoiceOver_Chat_PlayHint
|
||||
|
@ -218,7 +218,7 @@ public class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleCont
|
||||
}
|
||||
|
||||
if let photo = photo, let video = photo.videoRepresentations.last, let id = photo.id?.id {
|
||||
let videoFileReference = FileMediaReference.message(message: MessageReference(item.message), media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.message(message: MessageReference(item.message), media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: photo.representations, videoThumbnails: [], immediateThumbnailData: photo.immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(id, "action"), userLocation: .peer(item.message.id.peerId), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, storeAfterDownload: nil)
|
||||
if videoContent.id != strongSelf.videoContent?.id {
|
||||
let mediaManager = item.context.sharedContext.mediaManager
|
||||
|
@ -126,7 +126,7 @@ public func paneGifSearchForQuery(context: AccountContext, query: String, offset
|
||||
))
|
||||
}
|
||||
}
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: uniqueId ?? 0), partialReference: nil, resource: resource, previewRepresentations: previews, videoThumbnails: videoThumbnails, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil)])
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: uniqueId ?? 0), partialReference: nil, resource: resource, previewRepresentations: previews, videoThumbnails: videoThumbnails, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil, coverTime: nil)])
|
||||
references.append(MultiplexedVideoNodeFile(file: FileMediaReference.standalone(media: file), contextResult: (collection, result)))
|
||||
}
|
||||
case let .internalReference(internalReference):
|
||||
|
@ -231,7 +231,7 @@ public func legacyInstantVideoController(theme: PresentationTheme, forStory: Boo
|
||||
}
|
||||
}
|
||||
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo], preloadSize: nil)])
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo], preloadSize: nil, coverTime: nil)])
|
||||
var message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
|
||||
|
||||
let scheduleTime: Int32? = scheduleTimestamp > 0 ? scheduleTimestamp : nil
|
||||
|
@ -476,6 +476,7 @@ public final class MediaEditor {
|
||||
audioTrackOffset: nil,
|
||||
audioTrackVolume: nil,
|
||||
audioTrackSamples: nil,
|
||||
coverImageTimestamp: nil,
|
||||
qualityPreset: nil
|
||||
)
|
||||
}
|
||||
@ -1733,6 +1734,12 @@ public final class MediaEditor {
|
||||
}
|
||||
}
|
||||
|
||||
public func setCoverImageTimestamp(_ coverImageTimestamp: Double?) {
|
||||
self.updateValues(mode: .skipRendering) { values in
|
||||
return values.withUpdatedCoverImageTimestamp(coverImageTimestamp)
|
||||
}
|
||||
}
|
||||
|
||||
public func setDrawingAndEntities(data: Data?, image: UIImage?, entities: [CodableDrawingEntity]) {
|
||||
self.updateValues(mode: .skipRendering) { values in
|
||||
return values.withUpdatedDrawingAndEntities(drawing: image, entities: entities)
|
||||
|
@ -324,6 +324,9 @@ public final class MediaEditorValues: Codable, Equatable {
|
||||
if lhs.audioTrackSamples != rhs.audioTrackSamples {
|
||||
return false
|
||||
}
|
||||
if lhs.coverImageTimestamp != rhs.coverImageTimestamp {
|
||||
return false
|
||||
}
|
||||
if lhs.nightTheme != rhs.nightTheme {
|
||||
return false
|
||||
}
|
||||
@ -394,6 +397,7 @@ public final class MediaEditorValues: Codable, Equatable {
|
||||
case audioTrackTrimRange
|
||||
case audioTrackOffset
|
||||
case audioTrackVolume
|
||||
case coverImageTimestamp
|
||||
case qualityPreset
|
||||
}
|
||||
|
||||
@ -438,6 +442,8 @@ public final class MediaEditorValues: Codable, Equatable {
|
||||
public let audioTrackVolume: CGFloat?
|
||||
public let audioTrackSamples: MediaAudioTrackSamples?
|
||||
|
||||
public let coverImageTimestamp: Double?
|
||||
|
||||
public let qualityPreset: MediaQualityPreset?
|
||||
|
||||
var isStory: Bool {
|
||||
@ -486,6 +492,7 @@ public final class MediaEditorValues: Codable, Equatable {
|
||||
audioTrackOffset: Double?,
|
||||
audioTrackVolume: CGFloat?,
|
||||
audioTrackSamples: MediaAudioTrackSamples?,
|
||||
coverImageTimestamp: Double?,
|
||||
qualityPreset: MediaQualityPreset?
|
||||
) {
|
||||
self.peerId = peerId
|
||||
@ -521,6 +528,7 @@ public final class MediaEditorValues: Codable, Equatable {
|
||||
self.audioTrackOffset = audioTrackOffset
|
||||
self.audioTrackVolume = audioTrackVolume
|
||||
self.audioTrackSamples = audioTrackSamples
|
||||
self.coverImageTimestamp = coverImageTimestamp
|
||||
self.qualityPreset = qualityPreset
|
||||
}
|
||||
|
||||
@ -591,6 +599,8 @@ public final class MediaEditorValues: Codable, Equatable {
|
||||
|
||||
self.audioTrackSamples = nil
|
||||
|
||||
self.coverImageTimestamp = try container.decodeIfPresent(Double.self, forKey: .coverImageTimestamp)
|
||||
|
||||
self.qualityPreset = (try container.decodeIfPresent(Int32.self, forKey: .qualityPreset)).flatMap { MediaQualityPreset(rawValue: $0) }
|
||||
}
|
||||
|
||||
@ -652,109 +662,115 @@ public final class MediaEditorValues: Codable, Equatable {
|
||||
try container.encodeIfPresent(self.audioTrackOffset, forKey: .audioTrackOffset)
|
||||
try container.encodeIfPresent(self.audioTrackVolume, forKey: .audioTrackVolume)
|
||||
|
||||
try container.encodeIfPresent(self.coverImageTimestamp, forKey: .coverImageTimestamp)
|
||||
|
||||
try container.encodeIfPresent(self.qualityPreset?.rawValue, forKey: .qualityPreset)
|
||||
}
|
||||
|
||||
public func makeCopy() -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: offset, cropRect: self.cropRect, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: offset, cropRect: self.cropRect, cropScale: scale, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
public func withUpdatedCropRect(cropRect: CGRect, rotation: CGFloat, mirroring: Bool) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: .zero, cropRect: cropRect, cropScale: 1.0, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: .zero, cropRect: cropRect, cropScale: 1.0, cropRotation: rotation, cropMirroring: mirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedGradientColors(gradientColors: [UIColor]) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedVideoIsMuted(_ videoIsMuted: Bool) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedVideoIsFullHd(_ videoIsFullHd: Bool) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
|
||||
func withUpdatedVideoIsMirrored(_ videoIsMirrored: Bool) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedVideoVolume(_ videoVolume: CGFloat?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAdditionalVideo(path: String?, isDual: Bool, positionChanges: [VideoPositionChange]) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: path, additionalVideoIsDual: isDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: path, additionalVideoIsDual: isDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: positionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAdditionalVideo(position: CGPoint, scale: CGFloat, rotation: CGFloat) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: position, additionalVideoScale: scale, additionalVideoRotation: rotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAdditionalVideoTrimRange(_ additionalVideoTrimRange: Range<Double>?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
|
||||
func withUpdatedAdditionalVideoOffset(_ additionalVideoOffset: Double?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAdditionalVideoVolume(_ additionalVideoVolume: CGFloat?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedVideoTrimRange(_ videoTrimRange: Range<Double>) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedDrawingAndEntities(drawing: UIImage?, entities: [CodableDrawingEntity]) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: drawing, maskDrawing: self.maskDrawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: drawing, maskDrawing: self.maskDrawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
public func withUpdatedMaskDrawing(maskDrawing: UIImage?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedToolValues(_ toolValues: [EditorToolKey: Any]) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAudioTrack(_ audioTrack: MediaAudioTrack?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: self.videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAudioTrackTrimRange(_ audioTrackTrimRange: Range<Double>?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAudioTrackOffset(_ audioTrackOffset: Double?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAudioTrackVolume(_ audioTrackVolume: CGFloat?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedAudioTrackSamples(_ audioTrackSamples: MediaAudioTrackSamples?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
func withUpdatedNightTheme(_ nightTheme: Bool) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
public func withUpdatedEntities(_ entities: [CodableDrawingEntity]) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: self.qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
public func withUpdatedCoverImageTimestamp(_ coverImageTimestamp: Double?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: coverImageTimestamp, qualityPreset: self.qualityPreset)
|
||||
}
|
||||
|
||||
public func withUpdatedQualityPreset(_ qualityPreset: MediaQualityPreset?) -> MediaEditorValues {
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, qualityPreset: qualityPreset)
|
||||
return MediaEditorValues(peerId: self.peerId, originalDimensions: self.originalDimensions, cropOffset: self.cropOffset, cropRect: self.cropRect, cropScale: self.cropScale, cropRotation: self.cropRotation, cropMirroring: self.cropMirroring, cropOrientation: self.cropOrientation, gradientColors: self.gradientColors, videoTrimRange: videoTrimRange, videoIsMuted: self.videoIsMuted, videoIsFullHd: self.videoIsFullHd, videoIsMirrored: self.videoIsMirrored, videoVolume: self.videoVolume, additionalVideoPath: self.additionalVideoPath, additionalVideoIsDual: self.additionalVideoIsDual, additionalVideoPosition: self.additionalVideoPosition, additionalVideoScale: self.additionalVideoScale, additionalVideoRotation: self.additionalVideoRotation, additionalVideoPositionChanges: self.additionalVideoPositionChanges, additionalVideoTrimRange: self.additionalVideoTrimRange, additionalVideoOffset: self.additionalVideoOffset, additionalVideoVolume: self.additionalVideoVolume, nightTheme: self.nightTheme, drawing: self.drawing, maskDrawing: self.maskDrawing, entities: self.entities, toolValues: self.toolValues, audioTrack: self.audioTrack, audioTrackTrimRange: self.audioTrackTrimRange, audioTrackOffset: self.audioTrackOffset, audioTrackVolume: self.audioTrackVolume, audioTrackSamples: self.audioTrackSamples, coverImageTimestamp: self.coverImageTimestamp, qualityPreset: qualityPreset)
|
||||
}
|
||||
|
||||
public var resultDimensions: PixelDimensions {
|
||||
|
@ -216,7 +216,7 @@ public extension MediaEditorScreen {
|
||||
}
|
||||
}
|
||||
|
||||
update((context.engine.messages.editStory(peerId: peer.id, id: storyItem.id, media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers), mediaAreas: result.mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil)
|
||||
update((context.engine.messages.editStory(peerId: peer.id, id: storyItem.id, media: .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers, coverTime: nil), mediaAreas: result.mediaAreas, text: updatedText, entities: updatedEntities, privacy: nil)
|
||||
|> deliverOnMainQueue).startStrict(next: { result in
|
||||
switch result {
|
||||
case let .progress(progress):
|
||||
|
@ -0,0 +1,589 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import ViewControllerComponent
|
||||
import ComponentDisplayAdapters
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import TelegramCore
|
||||
import MultilineTextComponent
|
||||
import MediaEditor
|
||||
import MediaScrubberComponent
|
||||
import ButtonComponent
|
||||
|
||||
private final class MediaCoverScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let context: AccountContext
|
||||
let mediaEditor: MediaEditor
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
mediaEditor: MediaEditor
|
||||
) {
|
||||
self.context = context
|
||||
self.mediaEditor = mediaEditor
|
||||
}
|
||||
|
||||
static func ==(lhs: MediaCoverScreenComponent, rhs: MediaCoverScreenComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
enum ImageKey: Hashable {
|
||||
case done
|
||||
}
|
||||
private var cachedImages: [ImageKey: UIImage] = [:]
|
||||
func image(_ key: ImageKey) -> UIImage {
|
||||
if let image = self.cachedImages[key] {
|
||||
return image
|
||||
} else {
|
||||
var image: UIImage
|
||||
switch key {
|
||||
case .done:
|
||||
image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Done"), color: .white)!
|
||||
}
|
||||
cachedImages[key] = image
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
var playerStateDisposable: Disposable?
|
||||
var playerState: MediaEditorPlayerState?
|
||||
|
||||
init(mediaEditor: MediaEditor) {
|
||||
super.init()
|
||||
|
||||
self.playerStateDisposable = (mediaEditor.playerState(framesCount: 16)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] playerState in
|
||||
if let self {
|
||||
if self.playerState != playerState {
|
||||
self.playerState = playerState
|
||||
self.updated()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.playerStateDisposable?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State(mediaEditor: self.mediaEditor)
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let buttonsContainerView = UIView()
|
||||
private let buttonsBackgroundView = UIImageView()
|
||||
private let previewContainerView = UIView()
|
||||
private let cancelButton = ComponentView<Empty>()
|
||||
private let label = ComponentView<Empty>()
|
||||
private let doneButton = ComponentView<Empty>()
|
||||
private let scrubber = ComponentView<Empty>()
|
||||
|
||||
private let fadeView = UIView()
|
||||
|
||||
private var component: MediaCoverScreenComponent?
|
||||
private weak var state: State?
|
||||
private var environment: ViewControllerComponentContainer.Environment?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.buttonsContainerView.clipsToBounds = true
|
||||
|
||||
self.fadeView.alpha = 0.0
|
||||
self.fadeView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.7)
|
||||
|
||||
self.buttonsBackgroundView.image = generateImage(CGSize(width: 22.0, height: 22.0), rotatedContext: { size, context in
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.addPath(CGPath(roundedRect: CGRect(x: 0.0, y: -11.0, width: size.width, height: 22.0), cornerWidth: 11.0, cornerHeight: 11.0, transform: nil))
|
||||
context.fillPath()
|
||||
})?.stretchableImage(withLeftCapWidth: 11, topCapHeight: 11)
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundColor = .clear
|
||||
|
||||
self.addSubview(self.buttonsContainerView)
|
||||
self.buttonsContainerView.addSubview(self.buttonsBackgroundView)
|
||||
|
||||
self.addSubview(self.fadeView)
|
||||
self.addSubview(self.previewContainerView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func animateInFromEditor() {
|
||||
self.buttonsBackgroundView.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.2, additive: true)
|
||||
|
||||
self.label.view?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
if let view = self.doneButton.view {
|
||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
|
||||
private var animatingOut = false
|
||||
func animateOutToEditor(completion: @escaping () -> Void) {
|
||||
self.animatingOut = true
|
||||
|
||||
self.fadeView.layer.animateAlpha(from: self.fadeView.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.buttonsBackgroundView.layer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: 44.0), duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
|
||||
self.label.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
|
||||
if let view = self.scrubber.view {
|
||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2)
|
||||
}
|
||||
|
||||
if let view = self.cancelButton.view {
|
||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2)
|
||||
}
|
||||
|
||||
if let view = self.doneButton.view {
|
||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
view.layer.animateScale(from: 1.0, to: 0.1, duration: 0.2)
|
||||
}
|
||||
|
||||
self.state?.updated()
|
||||
}
|
||||
|
||||
// override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
// let result = super.hitTest(point, with: event)
|
||||
// if let controller = self.environment?.controller() as? MediaCoverScreen, [.erase, .restore].contains(controller.mode), result == self.previewContainerView {
|
||||
// return nil
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
|
||||
func update(component: MediaCoverScreenComponent, availableSize: CGSize, state: State, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
self.environment = environment
|
||||
|
||||
guard let controller = environment.controller() as? MediaCoverScreen else {
|
||||
return .zero
|
||||
}
|
||||
|
||||
// let isFirstTime = self.component == nil
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let isTablet: Bool
|
||||
if case .regular = environment.metrics.widthClass {
|
||||
isTablet = true
|
||||
} else {
|
||||
isTablet = false
|
||||
}
|
||||
|
||||
let buttonSideInset: CGFloat = 16.0
|
||||
var controlsBottomInset: CGFloat = 0.0
|
||||
let previewSize: CGSize
|
||||
var topInset: CGFloat = environment.statusBarHeight + 5.0
|
||||
if isTablet {
|
||||
let previewHeight = availableSize.height - topInset - 75.0
|
||||
previewSize = CGSize(width: floorToScreenPixels(previewHeight / 1.77778), height: previewHeight)
|
||||
} else {
|
||||
previewSize = CGSize(width: availableSize.width, height: floorToScreenPixels(availableSize.width * 1.77778))
|
||||
if availableSize.height < previewSize.height + 30.0 {
|
||||
topInset = 0.0
|
||||
controlsBottomInset = -75.0
|
||||
}
|
||||
}
|
||||
|
||||
let previewContainerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - previewSize.width) / 2.0), y: environment.safeInsets.top), size: CGSize(width: previewSize.width, height: availableSize.height - environment.safeInsets.top - environment.safeInsets.bottom + controlsBottomInset))
|
||||
let buttonsContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - environment.safeInsets.bottom + controlsBottomInset - 31.0), size: CGSize(width: availableSize.width, height: environment.safeInsets.bottom - controlsBottomInset))
|
||||
|
||||
let cancelButtonSize = self.cancelButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Cancel", font: Font.regular(17.0), textColor: .white)))
|
||||
),
|
||||
action: { [weak controller] in
|
||||
controller?.requestDismiss(animated: true)
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 120.0, height: 44.0)
|
||||
)
|
||||
let cancelButtonFrame = CGRect(
|
||||
origin: CGPoint(x: 16.0, y: 80.0),
|
||||
size: cancelButtonSize
|
||||
)
|
||||
if let cancelButtonView = self.cancelButton.view {
|
||||
if cancelButtonView.superview == nil {
|
||||
self.addSubview(cancelButtonView)
|
||||
setupButtonShadow(cancelButtonView)
|
||||
}
|
||||
transition.setFrame(view: cancelButtonView, frame: cancelButtonFrame)
|
||||
}
|
||||
|
||||
let doneButtonSize = self.doneButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: environment.theme.list.itemCheckColors.fillColor,
|
||||
foreground: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(ButtonTextContentComponent(
|
||||
text: "Save Cover",
|
||||
badge: 0,
|
||||
textColor: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
badgeBackground: .clear,
|
||||
badgeForeground: .clear
|
||||
))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
action: { [weak controller, weak self] in
|
||||
if let playerState = self?.state?.playerState, let mediaEditor = self?.component?.mediaEditor, let image = mediaEditor.resultImage {
|
||||
mediaEditor.setCoverImageTimestamp(playerState.position)
|
||||
controller?.completed(playerState.position, image)
|
||||
}
|
||||
controller?.requestDismiss(animated: true)
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - buttonSideInset * 2.0, height: 50.0)
|
||||
)
|
||||
let doneButtonFrame = CGRect(
|
||||
origin: CGPoint(x: floor((availableSize.width - doneButtonSize.width) / 2.0), y: availableSize.height - 99.0),
|
||||
size: doneButtonSize
|
||||
)
|
||||
if let doneButtonView = self.doneButton.view {
|
||||
if doneButtonView.superview == nil {
|
||||
self.addSubview(doneButtonView)
|
||||
}
|
||||
transition.setFrame(view: doneButtonView, frame: doneButtonFrame)
|
||||
}
|
||||
|
||||
let labelSize = self.label.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(Text(text: "Story Cover", font: Font.semibold(17.0), color: UIColor(rgb: 0xffffff))),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 88.0, height: 44.0)
|
||||
)
|
||||
let labelFrame = CGRect(
|
||||
origin: CGPoint(x: floorToScreenPixels((availableSize.width - labelSize.width) / 2.0), y: 80.0),
|
||||
size: labelSize
|
||||
)
|
||||
if let labelView = self.label.view {
|
||||
if labelView.superview == nil {
|
||||
self.addSubview(labelView)
|
||||
setupButtonShadow(labelView)
|
||||
}
|
||||
if labelView.bounds.width > 0.0 && labelFrame.width != labelView.bounds.width {
|
||||
if let snapshotView = labelView.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.center = labelView.center
|
||||
self.buttonsContainerView.addSubview(snapshotView)
|
||||
|
||||
labelView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
snapshotView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
labelView.bounds = CGRect(origin: .zero, size: labelFrame.size)
|
||||
transition.setPosition(view: labelView, position: labelFrame.center)
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.buttonsContainerView, frame: buttonsContainerFrame)
|
||||
transition.setFrame(view: self.buttonsBackgroundView, frame: CGRect(origin: .zero, size: buttonsContainerFrame.size))
|
||||
|
||||
transition.setFrame(view: self.previewContainerView, frame: previewContainerFrame)
|
||||
|
||||
if let playerState = state.playerState {
|
||||
let visibleTracks = playerState.tracks.filter { $0.id == 0 }.map { MediaScrubberComponent.Track($0) }
|
||||
|
||||
let mediaEditor = component.mediaEditor
|
||||
let scrubberInset: CGFloat = buttonSideInset
|
||||
let scrubberSize = self.scrubber.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MediaScrubberComponent(
|
||||
context: component.context,
|
||||
style: .cover,
|
||||
theme: environment.theme,
|
||||
generationTimestamp: playerState.generationTimestamp,
|
||||
position: playerState.position,
|
||||
minDuration: 1.0,
|
||||
maxDuration: storyMaxVideoDuration,
|
||||
isPlaying: playerState.isPlaying,
|
||||
tracks: visibleTracks,
|
||||
portalView: controller.portalView,
|
||||
positionUpdated: { [weak mediaEditor] position, apply in
|
||||
if let mediaEditor {
|
||||
mediaEditor.seek(position, andPlay: false)
|
||||
}
|
||||
},
|
||||
trackTrimUpdated: { _, _, _, _, _ in
|
||||
},
|
||||
trackOffsetUpdated: { _, _, _ in
|
||||
},
|
||||
trackLongPressed: { _, _ in
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: previewSize.width - scrubberInset * 2.0, height: availableSize.height)
|
||||
)
|
||||
|
||||
let scrubberFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - scrubberSize.width) / 2.0), y: availableSize.height - environment.safeInsets.bottom - scrubberSize.height + controlsBottomInset + 3.0 - 40.0), size: scrubberSize)
|
||||
if let scrubberView = self.scrubber.view {
|
||||
var animateIn = false
|
||||
if scrubberView.superview == nil {
|
||||
animateIn = true
|
||||
self.addSubview(scrubberView)
|
||||
}
|
||||
if animateIn {
|
||||
scrubberView.frame = scrubberFrame
|
||||
} else {
|
||||
transition.setFrame(view: scrubberView, frame: scrubberFrame)
|
||||
}
|
||||
if animateIn {
|
||||
scrubberView.layer.animatePosition(from: CGPoint(x: 0.0, y: 44.0), to: .zero, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
scrubberView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
scrubberView.layer.animateScale(from: 0.6, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View()
|
||||
}
|
||||
|
||||
public func update(view: View, availableSize: CGSize, state: State, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
final class MediaCoverScreen: ViewController {
|
||||
fileprivate final class Node: ViewControllerTracingNode, ASGestureRecognizerDelegate {
|
||||
private weak var controller: MediaCoverScreen?
|
||||
private let context: AccountContext
|
||||
|
||||
fileprivate let componentHost: ComponentView<ViewControllerComponentContainer.Environment>
|
||||
|
||||
private var presentationData: PresentationData
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
init(controller: MediaCoverScreen) {
|
||||
self.controller = controller
|
||||
self.context = controller.context
|
||||
|
||||
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.componentHost = ComponentView<ViewControllerComponentContainer.Environment>()
|
||||
|
||||
super.init()
|
||||
|
||||
self.backgroundColor = .clear
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
self.view.disablesInteractiveKeyboardGestureRecognizer = true
|
||||
}
|
||||
|
||||
@objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func animateInFromEditor() {
|
||||
if let view = self.componentHost.view as? MediaCoverScreenComponent.View {
|
||||
view.animateInFromEditor()
|
||||
}
|
||||
}
|
||||
|
||||
func animateOutToEditor(completion: @escaping () -> Void) {
|
||||
if let mediaEditor = self.controller?.mediaEditor {
|
||||
mediaEditor.play()
|
||||
}
|
||||
if let view = self.componentHost.view as? MediaCoverScreenComponent.View {
|
||||
view.animateOutToEditor(completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result === self.view {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func requestLayout(transition: ComponentTransition) {
|
||||
if let layout = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, forceUpdate: true, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, animateOut: Bool = false, transition: ComponentTransition) {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
let isFirstTime = self.validLayout == nil
|
||||
self.validLayout = layout
|
||||
|
||||
let isTablet = layout.metrics.isTablet
|
||||
|
||||
let previewSize: CGSize
|
||||
let topInset: CGFloat = (layout.statusBarHeight ?? 0.0) + 5.0
|
||||
if isTablet {
|
||||
let previewHeight = layout.size.height - topInset - 75.0
|
||||
previewSize = CGSize(width: floorToScreenPixels(previewHeight / 1.77778), height: previewHeight)
|
||||
} else {
|
||||
previewSize = CGSize(width: layout.size.width, height: floorToScreenPixels(layout.size.width * 1.77778))
|
||||
}
|
||||
let bottomInset = layout.size.height - previewSize.height - topInset
|
||||
|
||||
let environment = ViewControllerComponentContainer.Environment(
|
||||
statusBarHeight: layout.statusBarHeight ?? 0.0,
|
||||
navigationHeight: 0.0,
|
||||
safeInsets: UIEdgeInsets(
|
||||
top: topInset,
|
||||
left: layout.safeInsets.left,
|
||||
bottom: bottomInset,
|
||||
right: layout.safeInsets.right
|
||||
),
|
||||
additionalInsets: layout.additionalInsets,
|
||||
inputHeight: layout.inputHeight ?? 0.0,
|
||||
metrics: layout.metrics,
|
||||
deviceMetrics: layout.deviceMetrics,
|
||||
orientation: nil,
|
||||
isVisible: true,
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
dateTimeFormat: self.presentationData.dateTimeFormat,
|
||||
controller: { [weak self] in
|
||||
return self?.controller
|
||||
}
|
||||
)
|
||||
|
||||
let componentSize = self.componentHost.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MediaCoverScreenComponent(
|
||||
context: self.context,
|
||||
mediaEditor: controller.mediaEditor
|
||||
)
|
||||
),
|
||||
environment: {
|
||||
environment
|
||||
},
|
||||
forceUpdate: forceUpdate || animateOut,
|
||||
containerSize: layout.size
|
||||
)
|
||||
if let componentView = self.componentHost.view {
|
||||
if componentView.superview == nil {
|
||||
self.view.insertSubview(componentView, at: 3)
|
||||
componentView.clipsToBounds = true
|
||||
}
|
||||
let componentFrame = CGRect(origin: .zero, size: componentSize)
|
||||
transition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height)))
|
||||
}
|
||||
|
||||
if isFirstTime {
|
||||
self.animateInFromEditor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var node: Node {
|
||||
return self.displayNode as! Node
|
||||
}
|
||||
|
||||
fileprivate let context: AccountContext
|
||||
fileprivate let mediaEditor: MediaEditor
|
||||
fileprivate let previewView: MediaEditorPreviewView
|
||||
fileprivate let portalView: PortalView
|
||||
|
||||
var completed: (Double, UIImage) -> Void = { _, _ in }
|
||||
var dismissed: () -> Void = {}
|
||||
|
||||
private var initialValues: MediaEditorValues
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
mediaEditor: MediaEditor,
|
||||
previewView: MediaEditorPreviewView,
|
||||
portalView: PortalView
|
||||
) {
|
||||
self.context = context
|
||||
self.mediaEditor = mediaEditor
|
||||
self.previewView = previewView
|
||||
self.portalView = portalView
|
||||
self.initialValues = mediaEditor.values.makeCopy()
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
self.navigationPresentation = .flatModal
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
self.statusBar.statusBarStyle = .White
|
||||
|
||||
if let coverImageTimestamp = mediaEditor.values.coverImageTimestamp {
|
||||
mediaEditor.seek(coverImageTimestamp, andPlay: false)
|
||||
} else {
|
||||
mediaEditor.seek(0.0, andPlay: false)
|
||||
}
|
||||
}
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadDisplayNode() {
|
||||
self.displayNode = Node(controller: self)
|
||||
|
||||
super.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
func requestDismiss(animated: Bool) {
|
||||
self.dismissed()
|
||||
|
||||
self.node.animateOutToEditor(completion: {
|
||||
self.dismiss()
|
||||
})
|
||||
}
|
||||
|
||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
(self.displayNode as! Node).containerLayoutUpdated(layout: layout, transition: ComponentTransition(transition))
|
||||
}
|
||||
}
|
||||
|
||||
private func setupButtonShadow(_ view: UIView, radius: CGFloat = 2.0) {
|
||||
view.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
|
||||
view.layer.shadowRadius = radius
|
||||
view.layer.shadowColor = UIColor.black.cgColor
|
||||
view.layer.shadowOpacity = 0.35
|
||||
}
|
@ -76,6 +76,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
case cutout
|
||||
case cutoutErase
|
||||
case cutoutRestore
|
||||
case cover
|
||||
}
|
||||
|
||||
let context: AccountContext
|
||||
@ -827,7 +828,6 @@ final class MediaEditorScreenComponent: Component {
|
||||
doneButtonTitle = nil
|
||||
doneButtonIcon = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/Apply"), color: .white)!
|
||||
case .botPreview:
|
||||
//TODO:localize
|
||||
doneButtonTitle = environment.strings.Story_Editor_Add
|
||||
doneButtonIcon = nil
|
||||
}
|
||||
@ -841,31 +841,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
title: doneButtonTitle)),
|
||||
effectAlignment: .center,
|
||||
action: { [weak controller] in
|
||||
guard let controller else {
|
||||
return
|
||||
}
|
||||
switch controller.mode {
|
||||
case .storyEditor:
|
||||
guard !controller.node.recording.isActive else {
|
||||
return
|
||||
}
|
||||
guard controller.checkCaptionLimit() else {
|
||||
return
|
||||
}
|
||||
if controller.isEditingStory {
|
||||
controller.requestStoryCompletion(animated: true)
|
||||
} else {
|
||||
if controller.checkIfCompletionIsAllowed() {
|
||||
controller.openPrivacySettings(completion: { [weak controller] in
|
||||
controller?.requestStoryCompletion(animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
case .stickerEditor:
|
||||
controller.requestStickerCompletion(animated: true)
|
||||
case .botPreview:
|
||||
controller.requestStoryCompletion(animated: true)
|
||||
}
|
||||
controller?.node.requestCompletion()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
@ -2555,6 +2531,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
fileprivate var drawingScreen: DrawingScreen?
|
||||
fileprivate var stickerScreen: StickerPickerScreen?
|
||||
fileprivate weak var cutoutScreen: MediaCutoutScreen?
|
||||
fileprivate weak var coverScreen: MediaCoverScreen?
|
||||
private var defaultToEmoji = false
|
||||
|
||||
private var previousDrawingData: Data?
|
||||
@ -4614,6 +4591,75 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
)
|
||||
}
|
||||
|
||||
func requestCompletion() {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
switch controller.mode {
|
||||
case .storyEditor:
|
||||
guard !controller.node.recording.isActive else {
|
||||
return
|
||||
}
|
||||
guard controller.checkCaptionLimit() else {
|
||||
return
|
||||
}
|
||||
if controller.isEditingStory {
|
||||
controller.requestStoryCompletion(animated: true)
|
||||
} else {
|
||||
if controller.checkIfCompletionIsAllowed() {
|
||||
controller.openPrivacySettings(completion: { [weak controller] in
|
||||
controller?.requestStoryCompletion(animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
case .stickerEditor:
|
||||
controller.requestStickerCompletion(animated: true)
|
||||
case .botPreview:
|
||||
controller.requestStoryCompletion(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
func openCoverSelection() {
|
||||
guard let mediaEditor = self.mediaEditor else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let portalView = PortalView(matchPosition: false) else {
|
||||
return
|
||||
}
|
||||
portalView.view.layer.rasterizationScale = UIScreenScale
|
||||
self.previewContentContainerView.addPortal(view: portalView)
|
||||
|
||||
let scale = 48.0 / self.previewContentContainerView.frame.height
|
||||
portalView.view.transform = CGAffineTransformMakeScale(scale, scale)
|
||||
|
||||
if self.entitiesView.hasSelection {
|
||||
self.entitiesView.selectEntity(nil)
|
||||
}
|
||||
let coverController = MediaCoverScreen(
|
||||
context: self.context,
|
||||
mediaEditor: mediaEditor,
|
||||
previewView: self.previewView,
|
||||
portalView: portalView
|
||||
)
|
||||
coverController.dismissed = { [weak self] in
|
||||
if let self {
|
||||
self.animateInFromTool()
|
||||
Queue.mainQueue().after(0.1) {
|
||||
self.requestCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
coverController.completed = { [weak self] position, image in
|
||||
if let self {
|
||||
self.controller?.currentCoverImage = image
|
||||
}
|
||||
}
|
||||
self.controller?.present(coverController, in: .window(.root))
|
||||
self.coverScreen = coverController
|
||||
self.animateOutToTool(tool: .cover)
|
||||
}
|
||||
|
||||
func updateModalTransitionFactor(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
guard let layout = self.validLayout, case .compact = layout.metrics.widthClass else {
|
||||
return
|
||||
@ -5077,6 +5123,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
}
|
||||
self.controller?.present(controller, in: .window(.root))
|
||||
self.animateOutToTool(tool: .tools)
|
||||
case .cover:
|
||||
self.openCoverSelection()
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -5652,8 +5700,12 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
return self.isEditingStory || self.forwardSource != nil
|
||||
}
|
||||
|
||||
private var currentCoverImage: UIImage?
|
||||
func openPrivacySettings(_ privacy: MediaEditorResultPrivacy? = nil, completion: @escaping () -> Void = {}) {
|
||||
self.node.mediaEditor?.maybePauseVideo()
|
||||
guard let mediaEditor = self.node.mediaEditor else {
|
||||
return
|
||||
}
|
||||
mediaEditor.maybePauseVideo()
|
||||
|
||||
self.hapticFeedback.impact(.light)
|
||||
|
||||
@ -5662,6 +5714,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
let text = self.getCaption().string
|
||||
let mentions = generateTextEntities(text, enabledTypes: [.mention], currentEntities: []).map { (text as NSString).substring(with: NSRange(location: $0.range.lowerBound + 1, length: $0.range.upperBound - $0.range.lowerBound - 1)) }
|
||||
|
||||
let coverImage = self.currentCoverImage ?? mediaEditor.resultImage
|
||||
|
||||
let stateContext = ShareWithPeersScreen.StateContext(
|
||||
context: self.context,
|
||||
subject: .stories(editing: false),
|
||||
@ -5679,6 +5733,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
let initialPrivacy = privacy.privacy
|
||||
let timeout = privacy.timeout
|
||||
|
||||
var editCoverImpl: (() -> Void)?
|
||||
|
||||
let controller = ShareWithPeersScreen(
|
||||
context: self.context,
|
||||
initialPrivacy: initialPrivacy,
|
||||
@ -5687,6 +5743,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
pin: privacy.pin,
|
||||
timeout: privacy.timeout,
|
||||
mentions: mentions,
|
||||
coverImage: coverImage,
|
||||
stateContext: stateContext,
|
||||
completion: { [weak self] sendAsPeerId, privacy, allowScreenshots, pin, _, completed in
|
||||
guard let self else {
|
||||
@ -5736,6 +5793,9 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
pin: pin
|
||||
), completion: completion)
|
||||
})
|
||||
},
|
||||
editCover: {
|
||||
editCoverImpl?()
|
||||
}
|
||||
)
|
||||
controller.customModalStyleOverlayTransitionFactorUpdated = { [weak self, weak controller] transition in
|
||||
@ -5748,6 +5808,17 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
self.node.mediaEditor?.play()
|
||||
}
|
||||
self.push(controller)
|
||||
|
||||
editCoverImpl = { [weak self, weak controller] in
|
||||
if let self {
|
||||
Queue.mainQueue().after(0.25, {
|
||||
self.node.openCoverSelection()
|
||||
})
|
||||
}
|
||||
if let controller {
|
||||
controller.dismiss()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -8074,7 +8145,7 @@ private func stickerFile(resource: TelegramMediaResource, thumbnailResource: Tel
|
||||
fileAttributes.append(.FileName(fileName: isVideo ? "sticker.webm" : "sticker.webp"))
|
||||
fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil))
|
||||
if isVideo {
|
||||
fileAttributes.append(.Video(duration: duration ?? 3.0, size: dimensions, flags: [], preloadSize: nil))
|
||||
fileAttributes.append(.Video(duration: duration ?? 3.0, size: dimensions, flags: [], preloadSize: nil, coverTime: nil))
|
||||
} else {
|
||||
fileAttributes.append(.ImageSize(size: dimensions))
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ public final class MediaScrubberComponent: Component {
|
||||
public enum Style {
|
||||
case editor
|
||||
case videoMessage
|
||||
case cover
|
||||
}
|
||||
|
||||
let context: AccountContext
|
||||
@ -84,6 +85,7 @@ public final class MediaScrubberComponent: Component {
|
||||
let isPlaying: Bool
|
||||
|
||||
let tracks: [Track]
|
||||
let portalView: PortalView?
|
||||
|
||||
let positionUpdated: (Double, Bool) -> Void
|
||||
let trackTrimUpdated: (Int32, Double, Double, Bool, Bool) -> Void
|
||||
@ -100,6 +102,7 @@ public final class MediaScrubberComponent: Component {
|
||||
maxDuration: Double,
|
||||
isPlaying: Bool,
|
||||
tracks: [Track],
|
||||
portalView: PortalView? = nil,
|
||||
positionUpdated: @escaping (Double, Bool) -> Void,
|
||||
trackTrimUpdated: @escaping (Int32, Double, Double, Bool, Bool) -> Void,
|
||||
trackOffsetUpdated: @escaping (Int32, Double, Bool) -> Void,
|
||||
@ -114,6 +117,7 @@ public final class MediaScrubberComponent: Component {
|
||||
self.maxDuration = maxDuration
|
||||
self.isPlaying = isPlaying
|
||||
self.tracks = tracks
|
||||
self.portalView = portalView
|
||||
self.positionUpdated = positionUpdated
|
||||
self.trackTrimUpdated = trackTrimUpdated
|
||||
self.trackOffsetUpdated = trackOffsetUpdated
|
||||
@ -152,6 +156,7 @@ public final class MediaScrubberComponent: Component {
|
||||
private var trackViews: [Int32: TrackView] = [:]
|
||||
private let trimView: TrimView
|
||||
private let ghostTrimView: TrimView
|
||||
private let cursorContentView: UIView
|
||||
private let cursorView: HandleView
|
||||
|
||||
private var cursorDisplayLink: SharedDisplayLinkDriver.Link?
|
||||
@ -169,6 +174,7 @@ public final class MediaScrubberComponent: Component {
|
||||
self.trimView = TrimView(frame: .zero)
|
||||
self.ghostTrimView = TrimView(frame: .zero)
|
||||
self.ghostTrimView.isHollow = true
|
||||
self.cursorContentView = UIView()
|
||||
self.cursorView = HandleView()
|
||||
|
||||
super.init(frame: frame)
|
||||
@ -178,6 +184,10 @@ public final class MediaScrubberComponent: Component {
|
||||
self.disablesInteractiveModalDismiss = true
|
||||
self.disablesInteractiveKeyboardGestureRecognizer = true
|
||||
|
||||
self.cursorContentView.isUserInteractionEnabled = false
|
||||
self.cursorContentView.clipsToBounds = true
|
||||
self.cursorContentView.layer.cornerRadius = 10.0
|
||||
|
||||
let positionImage = generateImage(CGSize(width: handleWidth, height: 50.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
@ -187,13 +197,13 @@ public final class MediaScrubberComponent: Component {
|
||||
context.addPath(path.cgPath)
|
||||
context.fillPath()
|
||||
})?.stretchableImage(withLeftCapWidth: Int(handleWidth / 2.0), topCapHeight: 25)
|
||||
|
||||
self.cursorView.image = positionImage
|
||||
self.cursorView.isUserInteractionEnabled = true
|
||||
self.cursorView.hitTestSlop = UIEdgeInsets(top: -8.0, left: -9.0, bottom: -8.0, right: -9.0)
|
||||
|
||||
self.addSubview(self.ghostTrimView)
|
||||
self.addSubview(self.trimView)
|
||||
self.addSubview(self.cursorContentView)
|
||||
self.addSubview(self.cursorView)
|
||||
|
||||
self.cursorView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handleCursorPan(_:))))
|
||||
@ -328,10 +338,23 @@ public final class MediaScrubberComponent: Component {
|
||||
}
|
||||
|
||||
private func cursorFrame(size: CGSize, height: CGFloat, position: Double, duration : Double) -> CGRect {
|
||||
var cursorWidth = handleWidth
|
||||
var cursorMargin = handleWidth
|
||||
var height = height
|
||||
var isCover = false
|
||||
var y: CGFloat = -5.0 - UIScreenPixel
|
||||
if let component = self.component, case .cover = component.style {
|
||||
cursorWidth = 30.0 + 12.0
|
||||
cursorMargin = 0.0
|
||||
height = 50.0
|
||||
isCover = true
|
||||
y += 1.0
|
||||
}
|
||||
|
||||
let cursorPadding: CGFloat = 8.0
|
||||
let cursorPositionFraction = duration > 0.0 ? position / duration : 0.0
|
||||
let cursorPosition = floorToScreenPixels(handleWidth - 1.0 + (size.width - handleWidth * 2.0 + 2.0) * cursorPositionFraction)
|
||||
var cursorFrame = CGRect(origin: CGPoint(x: cursorPosition - handleWidth / 2.0, y: -5.0 - UIScreenPixel), size: CGSize(width: handleWidth, height: height))
|
||||
let cursorPosition = floorToScreenPixels(cursorMargin - 1.0 + (size.width - handleWidth * 2.0 + 2.0) * cursorPositionFraction)
|
||||
var cursorFrame = CGRect(origin: CGPoint(x: cursorPosition - cursorWidth / 2.0, y: y), size: CGSize(width: cursorWidth, height: height))
|
||||
|
||||
var leftEdge = self.ghostTrimView.leftHandleView.frame.maxX
|
||||
var rightEdge = self.ghostTrimView.rightHandleView.frame.minX
|
||||
@ -339,9 +362,13 @@ public final class MediaScrubberComponent: Component {
|
||||
leftEdge = self.trimView.leftHandleView.frame.maxX
|
||||
rightEdge = self.trimView.rightHandleView.frame.minX
|
||||
}
|
||||
if isCover {
|
||||
leftEdge = 0.0
|
||||
rightEdge = size.width
|
||||
}
|
||||
|
||||
cursorFrame.origin.x = max(leftEdge - cursorPadding, cursorFrame.origin.x)
|
||||
cursorFrame.origin.x = min(rightEdge - handleWidth + cursorPadding, cursorFrame.origin.x)
|
||||
cursorFrame.origin.x = min(rightEdge - cursorWidth + cursorPadding, cursorFrame.origin.x)
|
||||
return cursorFrame
|
||||
}
|
||||
|
||||
@ -377,6 +404,7 @@ public final class MediaScrubberComponent: Component {
|
||||
updatedPosition = max(self.startPosition, min(self.endPosition, position + advance))
|
||||
}
|
||||
self.cursorView.frame = cursorFrame(size: scrubberSize, height: self.effectiveCursorHeight, position: updatedPosition, duration: self.trimDuration)
|
||||
self.cursorContentView.frame = self.cursorView.frame.insetBy(dx: 6.0, dy: 2.0).offsetBy(dx: -1.0 - UIScreenPixel, dy: 0.0)
|
||||
}
|
||||
|
||||
public func update(component: MediaScrubberComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
@ -384,11 +412,36 @@ public final class MediaScrubberComponent: Component {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
if let portalView = component.portalView, portalView.view.superview == nil {
|
||||
portalView.view.frame = CGRect(x: 0.0, y: 0.0, width: 30.0, height: 48.0)
|
||||
portalView.view.clipsToBounds = true
|
||||
self.cursorContentView.addSubview(portalView.view)
|
||||
}
|
||||
|
||||
switch component.style {
|
||||
case .editor:
|
||||
self.cursorView.isHidden = false
|
||||
case .videoMessage:
|
||||
self.cursorView.isHidden = true
|
||||
case .cover:
|
||||
self.cursorView.isHidden = false
|
||||
self.trimView.isHidden = true
|
||||
self.ghostTrimView.isHidden = true
|
||||
|
||||
if isFirstTime {
|
||||
let positionImage = generateImage(CGSize(width: 30.0 + 12.0, height: 50.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
context.setStrokeColor(UIColor.white.cgColor)
|
||||
let lineWidth = 2.0 - UIScreenPixel
|
||||
context.setLineWidth(lineWidth)
|
||||
context.setShadow(offset: .zero, blur: 2.0, color: UIColor(rgb: 0x000000, alpha: 0.55).cgColor)
|
||||
|
||||
let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: 6.0 - lineWidth / 2.0, y: 2.0 - lineWidth / 2.0), size: CGSize(width: 30.0 - lineWidth, height: 48.0 - lineWidth)), cornerRadius: 9.0)
|
||||
context.addPath(path.cgPath)
|
||||
context.strokePath()
|
||||
})
|
||||
self.cursorView.image = positionImage
|
||||
}
|
||||
}
|
||||
|
||||
var totalHeight: CGFloat = 0.0
|
||||
@ -520,7 +573,7 @@ public final class MediaScrubberComponent: Component {
|
||||
|
||||
let fullTrackHeight: CGFloat
|
||||
switch component.style {
|
||||
case .editor:
|
||||
case .editor, .cover:
|
||||
fullTrackHeight = trackHeight
|
||||
case .videoMessage:
|
||||
fullTrackHeight = 33.0
|
||||
@ -610,7 +663,9 @@ public final class MediaScrubberComponent: Component {
|
||||
if let offset = self.mainAudioTrackOffset {
|
||||
cursorPosition -= offset
|
||||
}
|
||||
transition.setFrame(view: self.cursorView, frame: cursorFrame(size: scrubberSize, height: self.effectiveCursorHeight, position: cursorPosition, duration: trimDuration))
|
||||
let cursorFrame = cursorFrame(size: scrubberSize, height: self.effectiveCursorHeight, position: cursorPosition, duration: trimDuration)
|
||||
transition.setFrame(view: self.cursorView, frame: cursorFrame)
|
||||
transition.setFrame(view: self.cursorContentView, frame: cursorFrame.insetBy(dx: 6.0, dy: 2.0).offsetBy(dx: -1.0 - UIScreenPixel, dy: 0.0))
|
||||
} else {
|
||||
if let (_, _, end, ended) = self.cursorPositionAnimation {
|
||||
if ended, component.position >= self.startPosition && component.position < end - 1.0 {
|
||||
@ -839,7 +894,7 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
let fullTrackHeight: CGFloat
|
||||
let framesCornerRadius: CGFloat
|
||||
switch style {
|
||||
case .editor:
|
||||
case .editor, .cover:
|
||||
fullTrackHeight = trackHeight
|
||||
framesCornerRadius = 9.0
|
||||
case .videoMessage:
|
||||
@ -1362,7 +1417,7 @@ private class TrimView: UIView {
|
||||
let highlightColor: UIColor
|
||||
|
||||
switch style {
|
||||
case .editor:
|
||||
case .editor, .cover:
|
||||
effectiveHandleWidth = handleWidth
|
||||
fullTrackHeight = trackHeight
|
||||
capsuleOffset = 5.0 - UIScreenPixel
|
||||
|
@ -327,7 +327,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
markupNode.update(markup: markup, size: CGSize(width: 320.0, height: 320.0))
|
||||
markupNode.updateVisibility(true)
|
||||
} else if threadInfo == nil, let video = videoRepresentations.last, let peerReference = PeerReference(peer) {
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil)
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
self.videoNode?.removeFromSupernode()
|
||||
|
@ -162,7 +162,7 @@ final class PeerInfoEditingAvatarNode: ASDisplayNode {
|
||||
markupNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil)]))
|
||||
let videoFileReference = FileMediaReference.avatarList(peer: peerReference, media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.representation.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.representation.dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
let videoContent = NativeVideoContent(id: .profileVideo(videoId, nil), userLocation: .other, fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.representation.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, useLargeThumbnail: true, autoFetchFullSizeThumbnail: true, startTimestamp: video.representation.startTimestamp, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear, captureProtected: peer.isCopyProtectionEnabled, storeAfterDownload: nil)
|
||||
if videoContent.id != self.videoContent?.id {
|
||||
self.videoNode?.removeFromSupernode()
|
||||
|
@ -910,6 +910,7 @@ private extension MediaEditorValues {
|
||||
audioTrackOffset: nil,
|
||||
audioTrackVolume: nil,
|
||||
audioTrackSamples: nil,
|
||||
coverImageTimestamp: nil,
|
||||
qualityPreset: qualityPreset
|
||||
)
|
||||
}
|
||||
@ -1053,6 +1054,7 @@ private extension MediaEditorValues {
|
||||
audioTrackOffset: nil,
|
||||
audioTrackVolume: nil,
|
||||
audioTrackSamples: nil,
|
||||
coverImageTimestamp: nil,
|
||||
qualityPreset: qualityPreset
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,144 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import MultilineTextComponent
|
||||
import TelegramPresentationData
|
||||
import SwitchComponent
|
||||
|
||||
final class CoverListItemComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let title: String
|
||||
let image: UIImage?
|
||||
let hasNext: Bool
|
||||
let action: () -> Void
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
title: String,
|
||||
image: UIImage?,
|
||||
hasNext: Bool,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.theme = theme
|
||||
self.title = title
|
||||
self.image = image
|
||||
self.hasNext = hasNext
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: CoverListItemComponent, rhs: CoverListItemComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.image !== rhs.image {
|
||||
return false
|
||||
}
|
||||
if lhs.hasNext != rhs.hasNext {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let containerButton: HighlightTrackingButton
|
||||
|
||||
private let title = ComponentView<Empty>()
|
||||
private let icon = ComponentView<Empty>()
|
||||
private let separatorLayer: SimpleLayer
|
||||
|
||||
private var component: CoverListItemComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.separatorLayer = SimpleLayer()
|
||||
|
||||
self.containerButton = HighlightTrackingButton()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.layer.addSublayer(self.separatorLayer)
|
||||
self.addSubview(self.containerButton)
|
||||
|
||||
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.action()
|
||||
}
|
||||
|
||||
func update(component: CoverListItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let themeUpdated = self.component?.theme !== component.theme
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let height: CGFloat = 44.0
|
||||
let verticalInset: CGFloat = 0.0
|
||||
let leftInset: CGFloat = 16.0
|
||||
let rightInset: CGFloat = 16.0
|
||||
|
||||
let iconSize = self.icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Image(image: component.image, contentMode: .scaleAspectFill)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 30.0, height: 30.0)
|
||||
)
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.title, font: Font.regular(17.0), textColor: component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||
)
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: floorToScreenPixels((height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
if let iconView = self.icon.view {
|
||||
if iconView.superview == nil {
|
||||
iconView.clipsToBounds = true
|
||||
iconView.layer.cornerRadius = 5.0
|
||||
self.containerButton.addSubview(iconView)
|
||||
}
|
||||
transition.setFrame(view: iconView, frame: CGRect(origin: CGPoint(x: availableSize.width - rightInset - iconSize.width, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize))
|
||||
}
|
||||
|
||||
if themeUpdated {
|
||||
self.separatorLayer.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor
|
||||
}
|
||||
transition.setFrame(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: leftInset, y: height), size: CGSize(width: availableSize.width - leftInset, height: UIScreenPixel)))
|
||||
self.separatorLayer.isHidden = !component.hasNext
|
||||
|
||||
let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: verticalInset), size: CGSize(width: availableSize.width, height: height - verticalInset * 2.0))
|
||||
transition.setFrame(view: self.containerButton, frame: containerFrame)
|
||||
|
||||
return CGSize(width: availableSize.width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
@ -38,9 +38,11 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
let mentions: [String]
|
||||
let categoryItems: [CategoryItem]
|
||||
let optionItems: [OptionItem]
|
||||
let coverItem: CoverItem?
|
||||
let completion: (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], Bool) -> Void
|
||||
let editCategory: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||
let editBlockedPeers: (EngineStoryPrivacy, Bool, Bool) -> Void
|
||||
let editCover: () -> Void
|
||||
let peerCompletion: (EnginePeer.Id) -> Void
|
||||
|
||||
init(
|
||||
@ -54,9 +56,11 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
mentions: [String],
|
||||
categoryItems: [CategoryItem],
|
||||
optionItems: [OptionItem],
|
||||
coverItem: CoverItem?,
|
||||
completion: @escaping (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], Bool) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
||||
editBlockedPeers: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void,
|
||||
editCover: @escaping () -> Void,
|
||||
peerCompletion: @escaping (EnginePeer.Id) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
@ -69,9 +73,11 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
self.mentions = mentions
|
||||
self.categoryItems = categoryItems
|
||||
self.optionItems = optionItems
|
||||
self.coverItem = coverItem
|
||||
self.completion = completion
|
||||
self.editCategory = editCategory
|
||||
self.editBlockedPeers = editBlockedPeers
|
||||
self.editCover = editCover
|
||||
self.peerCompletion = peerCompletion
|
||||
}
|
||||
|
||||
@ -106,6 +112,9 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
if lhs.optionItems != rhs.optionItems {
|
||||
return false
|
||||
}
|
||||
if lhs.coverItem != rhs.coverItem {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -258,6 +267,33 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
enum CoverId: Int, Hashable {
|
||||
case choose = 0
|
||||
}
|
||||
|
||||
final class CoverItem: Equatable {
|
||||
let id: CoverId
|
||||
let title: String
|
||||
let image: UIImage?
|
||||
|
||||
init(
|
||||
id: CoverId,
|
||||
title: String,
|
||||
image: UIImage?
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.image = image
|
||||
}
|
||||
|
||||
static func ==(lhs: CoverItem, rhs: CoverItem) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
final class View: UIView, UIScrollViewDelegate {
|
||||
private let dimView: UIView
|
||||
@ -1607,6 +1643,90 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
footerText = isSendAsGroup ? environment.strings.Story_Privacy_KeepOnGroupPageInfo(footerValue).string : environment.strings.Story_Privacy_KeepOnChannelPageInfo(footerValue).string
|
||||
}
|
||||
|
||||
let footerSize = sectionFooter.update(
|
||||
transition: sectionFooterTransition,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: footerText, font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor)),
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: itemLayout.containerSize.width - 16.0 * 2.0, height: itemLayout.contentHeight)
|
||||
)
|
||||
let footerFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset + 16.0, y: sectionOffset + section.totalHeight + 7.0), size: footerSize)
|
||||
if let footerView = sectionFooter.view {
|
||||
if footerView.superview == nil {
|
||||
self.itemContainerView.addSubview(footerView)
|
||||
}
|
||||
sectionFooterTransition.setFrame(view: footerView, frame: footerFrame)
|
||||
}
|
||||
sectionOffset += footerSize.height
|
||||
} else if section.id == 4 && section.itemCount > 0 {
|
||||
if let item = component.coverItem {
|
||||
let itemFrame = CGRect(origin: CGPoint(x: itemLayout.sideInset, y: sectionOffset + section.insets.top + CGFloat(0) * section.itemHeight), size: CGSize(width: itemLayout.containerSize.width, height: section.itemHeight))
|
||||
if !visibleBounds.intersects(itemFrame) {
|
||||
continue
|
||||
}
|
||||
|
||||
let itemId = AnyHashable(item.id)
|
||||
validIds.append(itemId)
|
||||
|
||||
var itemTransition = transition
|
||||
let visibleItem: ComponentView<Empty>
|
||||
if let current = self.visibleItems[itemId] {
|
||||
visibleItem = current
|
||||
} else {
|
||||
visibleItem = ComponentView()
|
||||
if !transition.animation.isImmediate {
|
||||
itemTransition = .immediate
|
||||
}
|
||||
self.visibleItems[itemId] = visibleItem
|
||||
}
|
||||
|
||||
let _ = visibleItem.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(CoverListItemComponent(
|
||||
theme: environment.theme,
|
||||
title: item.title,
|
||||
image: item.image,
|
||||
hasNext: false,
|
||||
action: {
|
||||
component.editCover()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: itemFrame.size
|
||||
)
|
||||
if let itemView = visibleItem.view {
|
||||
if itemView.superview == nil {
|
||||
if let minSectionHeader {
|
||||
self.itemContainerView.insertSubview(itemView, belowSubview: minSectionHeader)
|
||||
} else {
|
||||
self.itemContainerView.addSubview(itemView)
|
||||
}
|
||||
}
|
||||
itemTransition.setFrame(view: itemView, frame: itemFrame)
|
||||
}
|
||||
}
|
||||
|
||||
let sectionFooter: ComponentView<Empty>
|
||||
var sectionFooterTransition = transition
|
||||
if let current = self.visibleSectionFooters[section.id] {
|
||||
sectionFooter = current
|
||||
} else {
|
||||
if !transition.animation.isImmediate {
|
||||
sectionFooterTransition = .immediate
|
||||
}
|
||||
sectionFooter = ComponentView()
|
||||
self.visibleSectionFooters[section.id] = sectionFooter
|
||||
}
|
||||
|
||||
var footerText = "Choose a frame from the story to show in your Profile."
|
||||
|
||||
if let sendAsPeerId = self.sendAsPeerId, sendAsPeerId.isGroupOrChannel == true {
|
||||
footerText = isSendAsGroup ? "Choose a frame from the story to show in group profile.": "Choose a frame from the story to show in channel profile."
|
||||
}
|
||||
|
||||
let footerSize = sectionFooter.update(
|
||||
transition: sectionFooterTransition,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
@ -1625,7 +1745,6 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
sectionFooterTransition.setFrame(view: footerView, frame: footerFrame)
|
||||
}
|
||||
}
|
||||
|
||||
sectionOffset += section.totalHeight
|
||||
}
|
||||
|
||||
@ -1873,6 +1992,8 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
}
|
||||
|
||||
private var currentHasChannels: Bool?
|
||||
private var currentHasCover: Bool?
|
||||
|
||||
func update(component: ShareWithPeersScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
guard !self.isDismissed else {
|
||||
return availableSize
|
||||
@ -1886,19 +2007,28 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
|
||||
var hasCategories = false
|
||||
var hasChannels = false
|
||||
var hasCover = false
|
||||
if case .stories = component.stateContext.subject {
|
||||
if let peerId = self.sendAsPeerId, peerId.isGroupOrChannel {
|
||||
} else {
|
||||
hasCategories = true
|
||||
}
|
||||
let sendAsPeersCount = component.stateContext.stateValue?.sendAsPeers.count ?? 1
|
||||
if sendAsPeersCount > 1 {
|
||||
if sendAsPeersCount > 1 && !"".isEmpty {
|
||||
hasChannels = true
|
||||
}
|
||||
if let currentHasChannels = self.currentHasChannels, currentHasChannels != hasChannels {
|
||||
contentTransition = .spring(duration: 0.4)
|
||||
}
|
||||
self.currentHasChannels = hasChannels
|
||||
|
||||
if self.selectedOptions.contains(.pin) {
|
||||
hasCover = true
|
||||
}
|
||||
if let currentHasCover = self.currentHasCover, currentHasCover != hasCover {
|
||||
contentTransition = .spring(duration: 0.4)
|
||||
}
|
||||
self.currentHasCover = hasCover
|
||||
} else if case .members = component.stateContext.subject {
|
||||
self.dismissPanGesture?.isEnabled = false
|
||||
} else if case .channels = component.stateContext.subject {
|
||||
@ -2306,6 +2436,15 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
itemHeight: optionItemSize.height,
|
||||
itemCount: component.optionItems.count
|
||||
))
|
||||
|
||||
if hasCover {
|
||||
sections.append(ItemLayout.Section(
|
||||
id: 4,
|
||||
insets: UIEdgeInsets(top: 28.0, left: 0.0, bottom: 0.0, right: 0.0),
|
||||
itemHeight: optionItemSize.height,
|
||||
itemCount: 1
|
||||
))
|
||||
}
|
||||
} else {
|
||||
sections.append(ItemLayout.Section(
|
||||
id: 1,
|
||||
@ -2489,8 +2628,12 @@ final class ShareWithPeersScreenComponent: Component {
|
||||
inset = 1000.0
|
||||
}
|
||||
} else {
|
||||
inset = 464.0
|
||||
inset += 10.0 + environment.safeInsets.bottom + 50.0 + footersTotalHeight
|
||||
if self.selectedOptions.contains(.pin) {
|
||||
inset = 1000.0
|
||||
} else {
|
||||
inset = 464.0
|
||||
inset += 10.0 + environment.safeInsets.bottom + 50.0 + footersTotalHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2849,10 +2992,12 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
pin: Bool = false,
|
||||
timeout: Int = 0,
|
||||
mentions: [String] = [],
|
||||
coverImage: UIImage? = nil,
|
||||
stateContext: StateContext,
|
||||
completion: @escaping (EnginePeer.Id?, EngineStoryPrivacy, Bool, Bool, [EnginePeer], Bool) -> Void,
|
||||
editCategory: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void = { _, _, _ in },
|
||||
editBlockedPeers: @escaping (EngineStoryPrivacy, Bool, Bool) -> Void = { _, _, _ in },
|
||||
editCover: @escaping () -> Void = { },
|
||||
peerCompletion: @escaping (EnginePeer.Id) -> Void = { _ in }
|
||||
) {
|
||||
self.context = context
|
||||
@ -2861,6 +3006,7 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
|
||||
var categoryItems: [ShareWithPeersScreenComponent.CategoryItem] = []
|
||||
var optionItems: [ShareWithPeersScreenComponent.OptionItem] = []
|
||||
var coverItem: ShareWithPeersScreenComponent.CoverItem?
|
||||
if case let .stories(editing) = stateContext.subject {
|
||||
var everyoneSubtitle = presentationData.strings.Story_Privacy_ExcludePeople
|
||||
if (stateContext.stateValue?.savedSelectedPeers[.everyone]?.count ?? 0) > 0 {
|
||||
@ -2994,6 +3140,10 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
title: presentationData.strings.Story_Privacy_KeepOnMyPage
|
||||
))
|
||||
}
|
||||
|
||||
if !editing || pin {
|
||||
coverItem = ShareWithPeersScreenComponent.CoverItem(id: .choose, title: "Choose Story Cover", image: coverImage)
|
||||
}
|
||||
}
|
||||
|
||||
var theme: ViewControllerComponentContainer.Theme = .dark
|
||||
@ -3013,9 +3163,11 @@ public class ShareWithPeersScreen: ViewControllerComponentContainer {
|
||||
mentions: mentions,
|
||||
categoryItems: categoryItems,
|
||||
optionItems: optionItems,
|
||||
coverItem: coverItem,
|
||||
completion: completion,
|
||||
editCategory: editCategory,
|
||||
editBlockedPeers: editBlockedPeers,
|
||||
editCover: editCover,
|
||||
peerCompletion: peerCompletion
|
||||
), navigationBarAppearance: .none, theme: theme)
|
||||
|
||||
|
@ -1820,7 +1820,7 @@ public func preloadStoryMedia(context: AccountContext, info: StoryPreloadInfo) -
|
||||
case let .file(file):
|
||||
var fetchRange: (Range<Int64>, MediaBoxFetchPriority)?
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(_, _, _, preloadSize) = attribute {
|
||||
if case let .Video(_, _, _, preloadSize, _) = attribute {
|
||||
if let preloadSize {
|
||||
fetchRange = (0 ..< Int64(preloadSize), .default)
|
||||
}
|
||||
@ -2045,7 +2045,7 @@ public func waitUntilStoryMediaPreloaded(context: AccountContext, peerId: Engine
|
||||
case let .file(file):
|
||||
var fetchRange: (Range<Int64>, MediaBoxFetchPriority)?
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(_, _, _, preloadSize) = attribute {
|
||||
if case let .Video(_, _, _, preloadSize, _) = attribute {
|
||||
if let preloadSize {
|
||||
fetchRange = (0 ..< Int64(preloadSize), .default)
|
||||
}
|
||||
|
@ -1136,7 +1136,7 @@ private final class StoryContainerScreenComponent: Component {
|
||||
var isSilentVideo = false
|
||||
if case let .file(file) = slice.item.storyItem.media {
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(_, _, flags, _) = attribute {
|
||||
if case let .Video(_, _, flags, _, _) = attribute {
|
||||
if flags.contains(.isSilent) {
|
||||
isSilentVideo = true
|
||||
}
|
||||
|
@ -3798,7 +3798,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
isVideo = true
|
||||
soundAlpha = 1.0
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(_, _, flags, _) = attribute {
|
||||
if case let .Video(_, _, flags, _, _) = attribute {
|
||||
if flags.contains(.isSilent) {
|
||||
isSilentVideo = true
|
||||
soundAlpha = 0.5
|
||||
@ -3831,7 +3831,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
var isSilentVideo = false
|
||||
if case let .file(file) = component.slice.item.storyItem.media {
|
||||
for attribute in file.attributes {
|
||||
if case let .Video(_, _, flags, _) = attribute {
|
||||
if case let .Video(_, _, flags, _, _) = attribute {
|
||||
if flags.contains(.isSilent) {
|
||||
isSilentVideo = true
|
||||
}
|
||||
|
@ -1793,7 +1793,7 @@ public class VideoMessageCameraScreen: ViewController {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: dimensions, cropOffset: .zero, cropRect: CGRect(origin: .zero, size: dimensions.cgSize), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, maskDrawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, qualityPreset: .videoMessage)
|
||||
let values = MediaEditorValues(peerId: self.context.account.peerId, originalDimensions: dimensions, cropOffset: .zero, cropRect: CGRect(origin: .zero, size: dimensions.cgSize), cropScale: 1.0, cropRotation: 0.0, cropMirroring: false, cropOrientation: nil, gradientColors: nil, videoTrimRange: self.node.previewState?.trimRange, videoIsMuted: false, videoIsFullHd: false, videoIsMirrored: false, videoVolume: nil, additionalVideoPath: nil, additionalVideoIsDual: false, additionalVideoPosition: nil, additionalVideoScale: nil, additionalVideoRotation: nil, additionalVideoPositionChanges: [], additionalVideoTrimRange: nil, additionalVideoOffset: nil, additionalVideoVolume: nil, nightTheme: false, drawing: nil, maskDrawing: nil, entities: [], toolValues: [:], audioTrack: nil, audioTrackTrimRange: nil, audioTrackOffset: nil, audioTrackVolume: nil, audioTrackSamples: nil, coverImageTimestamp: nil, qualityPreset: .videoMessage)
|
||||
|
||||
var resourceAdjustments: VideoMediaResourceAdjustments? = nil
|
||||
if let valuesData = try? JSONEncoder().encode(values) {
|
||||
@ -1833,7 +1833,7 @@ public class VideoMessageCameraScreen: ViewController {
|
||||
context.account.postbox.mediaBox.storeCachedResourceRepresentation(resource, representation: CachedVideoFirstFrameRepresentation(), data: data)
|
||||
}
|
||||
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: finalDuration, size: video.dimensions, flags: [.instantRoundVideo], preloadSize: nil)])
|
||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: finalDuration, size: video.dimensions, flags: [.instantRoundVideo], preloadSize: nil, coverTime: nil)])
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if self.cameraState.isViewOnceEnabled {
|
||||
|
@ -186,6 +186,7 @@ extension ChatControllerImpl {
|
||||
audioTrackOffset: nil,
|
||||
audioTrackVolume: nil,
|
||||
audioTrackSamples: nil,
|
||||
coverImageTimestamp: nil,
|
||||
qualityPreset: nil
|
||||
)
|
||||
|
||||
@ -210,7 +211,7 @@ extension ChatControllerImpl {
|
||||
var fileAttributes: [TelegramMediaFileAttribute] = []
|
||||
fileAttributes.append(.FileName(fileName: "sticker.webm"))
|
||||
fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil))
|
||||
fileAttributes.append(.Video(duration: animatedImage.duration, size: PixelDimensions(width: 512, height: 512), flags: [], preloadSize: nil))
|
||||
fileAttributes.append(.Video(duration: animatedImage.duration, size: PixelDimensions(width: 512, height: 512), flags: [], preloadSize: nil, coverTime: nil))
|
||||
|
||||
let previewRepresentations: [TelegramMediaImageRepresentation] = []
|
||||
// if let thumbnailResource {
|
||||
|
@ -397,7 +397,7 @@ func messageMediaEditingOptions(message: Message) -> MessageMediaEditingOptions
|
||||
return []
|
||||
case .Animated:
|
||||
break
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
return []
|
||||
} else {
|
||||
|
@ -260,7 +260,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
||||
}
|
||||
imageDimensions = externalReference.content?.dimensions?.cgSize
|
||||
if externalReference.type == "gif", let thumbnailResource = externalReference.thumbnail?.resource, let content = externalReference.content, let dimensions = content.dimensions {
|
||||
videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil)])
|
||||
videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: thumbnailResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil, coverTime: nil)])
|
||||
imageResource = nil
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem {
|
||||
} else {
|
||||
return SharedMediaPlaybackData(type: .music, source: source)
|
||||
}
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
return SharedMediaPlaybackData(type: .instantVideo, source: source)
|
||||
} else {
|
||||
@ -129,7 +129,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem {
|
||||
displayData = SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0, caption: caption)
|
||||
}
|
||||
return displayData
|
||||
case let .Video(_, _, flags, _):
|
||||
case let .Video(_, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
return SharedMediaPlaybackDisplayData.instantVideo(author: self.message.effectiveAuthor.flatMap(EnginePeer.init), peer: self.message.peers[self.message.id.peerId].flatMap(EnginePeer.init), timestamp: self.message.timestamp)
|
||||
} else {
|
||||
|
@ -671,7 +671,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
|
||||
return nil
|
||||
}
|
||||
}
|
||||
media = .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers)
|
||||
media = .video(dimensions: dimensions, duration: duration, resource: resource, firstFrameFile: firstFrameFile, stickers: result.stickers, coverTime: values.coverImageTimestamp)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
@ -172,7 +172,7 @@ func makeBridgeMedia(message: Message, strings: PresentationStrings, chatPeer: P
|
||||
|
||||
for attribute in file.attributes {
|
||||
switch attribute {
|
||||
case let .Video(duration, size, flags, _):
|
||||
case let .Video(duration, size, flags, _, _):
|
||||
bridgeVideo.duration = Int32(duration)
|
||||
bridgeVideo.dimensions = size.cgSize
|
||||
bridgeVideo.round = flags.contains(.instantRoundVideo)
|
||||
|
@ -37,7 +37,7 @@ struct WebSearchGalleryEntry: Equatable {
|
||||
switch self.result {
|
||||
case let .externalReference(externalReference):
|
||||
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = externalReference.thumbnail?.resource, let dimensions = content.dimensions {
|
||||
let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil)]))
|
||||
let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [], preloadSize: nil, coverTime: nil)]))
|
||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, index: self.index, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), userLocation: .other, fileReference: fileReference, loopVideo: true, enableSound: false, fetchAutomatically: true, storeAfterDownload: nil), controllerInteraction: controllerInteraction)
|
||||
}
|
||||
case let .internalReference(internalReference):
|
||||
|
@ -22,7 +22,7 @@ public extension WidgetDataPeer.Message {
|
||||
switch attribute {
|
||||
case let .Sticker(altText, _, _):
|
||||
content = .sticker(WidgetDataPeer.Message.Content.Sticker(altText: altText))
|
||||
case let .Video(duration, _, flags, _):
|
||||
case let .Video(duration, _, flags, _, _):
|
||||
if flags.contains(.instantRoundVideo) {
|
||||
content = .videoMessage(WidgetDataPeer.Message.Content.VideoMessage(duration: Int32(duration)))
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user