Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2023-06-07 14:48:36 +04:00
commit d551ad9205
63 changed files with 900 additions and 326 deletions

View File

@ -9355,3 +9355,5 @@ Sorry for the inconvenience.";
"Privacy.Bio.CustomHelp" = "You can restrict who can see your profile bio with granular precision.";
"Privacy.Bio.AlwaysShareWith.Title" = "Always Share With";
"Privacy.Bio.NeverShareWith.Title" = "Never Share With";
"Conversation.OpenLink" = "OPEN LINK";

View File

@ -37,7 +37,7 @@ public protocol UniversalVideoContentNode: AnyObject {
public protocol UniversalVideoContent {
var id: AnyHashable { get }
var dimensions: CGSize { get }
var duration: Int32 { get }
var duration: Double { get }
func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode

View File

@ -257,7 +257,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
})
if let duration = file.duration {
let durationString = stringForDuration(duration)
let durationString = stringForDuration(Int32(duration))
var badgeContent: ChatMessageInteractiveMediaBadgeContent?
var mediaDownloadState: ChatMessageInteractiveMediaDownloadState?

View File

@ -1144,7 +1144,8 @@ private final class DrawingScreenComponent: CombinedComponent {
}
let previewSize = CGSize(width: context.availableSize.width, height: floorToScreenPixels(context.availableSize.width * 1.77778))
let previewTopInset: CGFloat = floorToScreenPixels(context.availableSize.height - previewSize.height) / 2.0
let previewTopInset: CGFloat = environment.statusBarHeight + 12.0
let previewBottomInset = context.availableSize.height - previewSize.height - previewTopInset
var topInset = environment.safeInsets.top + 31.0
if component.sourceHint == .storyEditor {
@ -1966,7 +1967,7 @@ private final class DrawingScreenComponent: CombinedComponent {
var doneButtonPosition = CGPoint(x: context.availableSize.width - environment.safeInsets.right - doneButton.size.width / 2.0 - 3.0, y: context.availableSize.height - environment.safeInsets.bottom - doneButton.size.height / 2.0 - 2.0 - UIScreenPixel)
if component.sourceHint == .storyEditor {
doneButtonPosition.x = doneButtonPosition.x - 2.0
doneButtonPosition.y = floorToScreenPixels(context.availableSize.height - previewTopInset + 3.0 + doneButton.size.height / 2.0)
doneButtonPosition.y = floorToScreenPixels(context.availableSize.height - previewBottomInset + 3.0 + doneButton.size.height / 2.0)
}
context.add(doneButton
.position(doneButtonPosition)
@ -2044,7 +2045,7 @@ private final class DrawingScreenComponent: CombinedComponent {
)
var modeAndSizePosition = CGPoint(x: context.availableSize.width / 2.0 - (modeRightInset - 57.0) / 2.0, y: context.availableSize.height - environment.safeInsets.bottom - modeAndSize.size.height / 2.0 - 9.0)
if component.sourceHint == .storyEditor {
modeAndSizePosition.y = floorToScreenPixels(context.availableSize.height - previewTopInset + 8.0 + modeAndSize.size.height / 2.0)
modeAndSizePosition.y = floorToScreenPixels(context.availableSize.height - previewBottomInset + 8.0 + modeAndSize.size.height / 2.0)
}
context.add(modeAndSize
.position(modeAndSizePosition)
@ -2083,7 +2084,7 @@ private final class DrawingScreenComponent: CombinedComponent {
var backButtonPosition = CGPoint(x: environment.safeInsets.left + backButton.size.width / 2.0 + 3.0, y: context.availableSize.height - environment.safeInsets.bottom - backButton.size.height / 2.0 - 2.0 - UIScreenPixel)
if component.sourceHint == .storyEditor {
backButtonPosition.x = backButtonPosition.x + 2.0
backButtonPosition.y = floorToScreenPixels(context.availableSize.height - previewTopInset + 3.0 + backButton.size.height / 2.0)
backButtonPosition.y = floorToScreenPixels(context.availableSize.height - previewBottomInset + 3.0 + backButton.size.height / 2.0)
}
context.add(backButton
.position(backButtonPosition)

View File

@ -303,7 +303,7 @@ public func galleryItemForEntry(
if let file = webpageContent.file, file.isVideo {
content = NativeVideoContent(id: .message(message.stableId, file.fileId), userLocation: .peer(message.id.peerId), fileReference: .message(message: MessageReference(message), media: file), imageReference: mediaImage.flatMap({ ImageMediaReference.message(message: MessageReference(message), media: $0) }), streamVideo: .conservative, loopVideo: loopVideos, tempFilePath: tempFilePath, captureProtected: message.isCopyProtected(), storeAfterDownload: generateStoreAfterDownload?(message, file))
} else if URL(string: embedUrl)?.pathExtension == "mp4" {
content = SystemVideoContent(userLocation: .peer(message.id.peerId), url: embedUrl, imageReference: .webPage(webPage: WebpageReference(webpage), media: image), dimensions: webpageContent.embedSize?.cgSize ?? CGSize(width: 640.0, height: 640.0), duration: Int32(webpageContent.duration ?? 0))
content = SystemVideoContent(userLocation: .peer(message.id.peerId), url: embedUrl, imageReference: .webPage(webPage: WebpageReference(webpage), media: image), dimensions: webpageContent.embedSize?.cgSize ?? CGSize(width: 640.0, height: 640.0), duration: webpageContent.duration.flatMap(Double.init) ?? 0.0)
}
}
if content == nil, let webEmbedContent = WebEmbedVideoContent(userLocation: .peer(message.id.peerId), webPage: webpage, webpageContent: webpageContent, forcedTimestamp: timecode.flatMap(Int.init), openUrl: { url in

View File

@ -255,7 +255,7 @@ public final class SecretMediaPreviewController: ViewController {
if let _ = index {
if let message = strongSelf.messageView?.message, let media = mediaForMessage(message: message) {
var beginTimeAndTimeout: (Double, Double)?
var videoDuration: Int32?
var videoDuration: Double?
for media in message.media {
if let file = media as? TelegramMediaFile {
videoDuration = file.duration
@ -265,7 +265,7 @@ public final class SecretMediaPreviewController: ViewController {
if let attribute = message.autoclearAttribute {
if let countdownBeginTime = attribute.countdownBeginTime {
if let videoDuration = videoDuration {
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration))
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, videoDuration)
} else {
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
}
@ -273,7 +273,7 @@ public final class SecretMediaPreviewController: ViewController {
} else if let attribute = message.autoremoveAttribute {
if let countdownBeginTime = attribute.countdownBeginTime {
if let videoDuration = videoDuration {
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration))
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, videoDuration)
} else {
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
}
@ -444,7 +444,7 @@ public final class SecretMediaPreviewController: ViewController {
self.markMessageAsConsumedDisposable.set(self.context.engine.messages.markMessageContentAsConsumedInteractively(messageId: message.id).start())
} else {
var beginTimeAndTimeout: (Double, Double)?
var videoDuration: Int32?
var videoDuration: Double?
for media in message.media {
if let file = media as? TelegramMediaFile {
videoDuration = file.duration
@ -453,7 +453,7 @@ public final class SecretMediaPreviewController: ViewController {
if let attribute = message.autoclearAttribute {
if let countdownBeginTime = attribute.countdownBeginTime {
if let videoDuration = videoDuration {
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration))
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, videoDuration)
} else {
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
}
@ -461,7 +461,7 @@ public final class SecretMediaPreviewController: ViewController {
} else if let attribute = message.autoremoveAttribute {
if let countdownBeginTime = attribute.countdownBeginTime {
if let videoDuration = videoDuration {
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, Double(videoDuration))
beginTimeAndTimeout = (CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, videoDuration)
} else {
beginTimeAndTimeout = (Double(countdownBeginTime), Double(attribute.timeout))
}

View File

@ -348,7 +348,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: Int(0), size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
fileAttributes.append(.FileName(fileName: fileName))
fileAttributes.append(.Animated)
@ -390,7 +390,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: Int(0), size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
fileAttributes.append(.Video(duration: 0.0, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
fileAttributes.append(.FileName(fileName: fileName))
fileAttributes.append(.Animated)
@ -723,7 +723,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
fileAttributes.append(.Animated)
}
if !asFile {
fileAttributes.append(.Video(duration: Int(finalDuration), size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
fileAttributes.append(.Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.supportsStreaming], preloadSize: nil))
if let adjustments = adjustments {
if adjustments.sendAsGif {
fileAttributes.append(.Animated)

View File

@ -146,7 +146,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
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(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags, preloadSize: nil)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024)
return standaloneUploadedFile(account: account, 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)
|> mapError { _ -> PreparedShareItemError in
return .generic
}
@ -212,7 +212,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
let mimeType: String
if converted {
mimeType = "video/mp4"
attributes = [.Video(duration: Int(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), .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")]

View File

@ -209,7 +209,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1744710921] = { return Api.DocumentAttribute.parse_documentAttributeHasStickers($0) }
dict[1815593308] = { return Api.DocumentAttribute.parse_documentAttributeImageSize($0) }
dict[1662637586] = { return Api.DocumentAttribute.parse_documentAttributeSticker($0) }
dict[-381651053] = { return Api.DocumentAttribute.parse_documentAttributeVideo($0) }
dict[-745541182] = { return Api.DocumentAttribute.parse_documentAttributeVideo($0) }
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
dict[-1764723459] = { return Api.EmailVerification.parse_emailVerificationApple($0) }
@ -243,6 +243,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[206668204] = { return Api.ExportedChatlistInvite.parse_exportedChatlistInvite($0) }
dict[1103040667] = { return Api.ExportedContactToken.parse_exportedContactToken($0) }
dict[1571494644] = { return Api.ExportedMessageLink.parse_exportedMessageLink($0) }
dict[1070138683] = { return Api.ExportedStoryLink.parse_exportedStoryLink($0) }
dict[-207944868] = { return Api.FileHash.parse_fileHash($0) }
dict[-11252123] = { return Api.Folder.parse_folder($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
@ -336,6 +337,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) }
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
dict[-1702447729] = { return Api.InputMedia.parse_inputMediaStory($0) }
dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) }
dict[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($0) }
@ -526,6 +528,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-156940077] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
dict[-946147823] = { return Api.MessageMedia.parse_messageMediaStory($0) }
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
dict[784356159] = { return Api.MessageMedia.parse_messageMediaVenue($0) }
dict[-1557277184] = { return Api.MessageMedia.parse_messageMediaWebPage($0) }
@ -771,7 +774,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) }
dict[-1239335713] = { return Api.ShippingOption.parse_shippingOption($0) }
dict[-2010155333] = { return Api.SimpleWebViewResult.parse_simpleWebViewResultUrl($0) }
dict[-64636888] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[-626000021] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[1035529315] = { return Api.SponsoredWebPage.parse_sponsoredWebPage($0) }
dict[-884757282] = { return Api.StatsAbsValueAndPrev.parse_statsAbsValueAndPrev($0) }
dict[-1237848657] = { return Api.StatsDateRangeDays.parse_statsDateRangeDays($0) }
dict[-1901828938] = { return Api.StatsGraph.parse_statsGraph($0) }
@ -789,9 +793,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1087454222] = { return Api.StickerSetCovered.parse_stickerSetFullCovered($0) }
dict[872932635] = { return Api.StickerSetCovered.parse_stickerSetMultiCovered($0) }
dict[2008112412] = { return Api.StickerSetCovered.parse_stickerSetNoCovered($0) }
dict[-1882351956] = { return Api.StoryItem.parse_storyItem($0) }
dict[1445635639] = { return Api.StoryItem.parse_storyItem($0) }
dict[1374088783] = { return Api.StoryItem.parse_storyItemDeleted($0) }
dict[-1579626609] = { return Api.StoryItem.parse_storyItemSkipped($0) }
dict[1764886178] = { return Api.StoryItem.parse_storyItemSkipped($0) }
dict[-1491424062] = { return Api.StoryView.parse_storyView($0) }
dict[-748199729] = { return Api.StoryViews.parse_storyViews($0) }
dict[1964978502] = { return Api.TextWithEntities.parse_textWithEntities($0) }
@ -1386,6 +1390,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.ExportedMessageLink:
_1.serialize(buffer, boxed)
case let _1 as Api.ExportedStoryLink:
_1.serialize(buffer, boxed)
case let _1 as Api.FileHash:
_1.serialize(buffer, boxed)
case let _1 as Api.Folder:
@ -1694,6 +1700,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.SponsoredMessage:
_1.serialize(buffer, boxed)
case let _1 as Api.SponsoredWebPage:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsAbsValueAndPrev:
_1.serialize(buffer, boxed)
case let _1 as Api.StatsDateRangeDays:

View File

@ -744,6 +744,7 @@ public extension Api {
case messageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, receiptMsgId: Int32?, currency: String, totalAmount: Int64, startParam: String, extendedMedia: Api.MessageExtendedMedia?)
case messageMediaPhoto(flags: Int32, photo: Api.Photo?, ttlSeconds: Int32?)
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
case messageMediaStory(userId: Int64, id: Int32)
case messageMediaUnsupported
case messageMediaVenue(geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
case messageMediaWebPage(webpage: Api.WebPage)
@ -833,6 +834,13 @@ public extension Api {
poll.serialize(buffer, true)
results.serialize(buffer, true)
break
case .messageMediaStory(let userId, let id):
if boxed {
buffer.appendInt32(-946147823)
}
serializeInt64(userId, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
break
case .messageMediaUnsupported:
if boxed {
buffer.appendInt32(-1618676578)
@ -881,6 +889,8 @@ public extension Api {
return ("messageMediaPhoto", [("flags", flags as Any), ("photo", photo as Any), ("ttlSeconds", ttlSeconds as Any)])
case .messageMediaPoll(let poll, let results):
return ("messageMediaPoll", [("poll", poll as Any), ("results", results as Any)])
case .messageMediaStory(let userId, let id):
return ("messageMediaStory", [("userId", userId as Any), ("id", id as Any)])
case .messageMediaUnsupported:
return ("messageMediaUnsupported", [])
case .messageMediaVenue(let geo, let title, let address, let provider, let venueId, let venueType):
@ -1081,6 +1091,20 @@ public extension Api {
return nil
}
}
public static func parse_messageMediaStory(_ reader: BufferReader) -> MessageMedia? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.MessageMedia.messageMediaStory(userId: _1!, id: _2!)
}
else {
return nil
}
}
public static func parse_messageMediaUnsupported(_ reader: BufferReader) -> MessageMedia? {
return Api.MessageMedia.messageMediaUnsupported
}

View File

@ -434,13 +434,13 @@ public extension Api {
}
public extension Api {
indirect enum SponsoredMessage: TypeConstructorDescription {
case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, message: String, entities: [Api.MessageEntity]?, sponsorInfo: String?, additionalInfo: String?)
case sponsoredMessage(flags: Int32, randomId: Buffer, fromId: Api.Peer?, chatInvite: Api.ChatInvite?, chatInviteHash: String?, channelPost: Int32?, startParam: String?, webpage: Api.SponsoredWebPage?, message: String, entities: [Api.MessageEntity]?, sponsorInfo: String?, additionalInfo: String?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let message, let entities, let sponsorInfo, let additionalInfo):
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let message, let entities, let sponsorInfo, let additionalInfo):
if boxed {
buffer.appendInt32(-64636888)
buffer.appendInt32(-626000021)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeBytes(randomId, buffer: buffer, boxed: false)
@ -449,6 +449,7 @@ public extension Api {
if Int(flags) & Int(1 << 4) != 0 {serializeString(chatInviteHash!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(channelPost!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 0) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 9) != 0 {webpage!.serialize(buffer, true)}
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
@ -463,8 +464,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let message, let entities, let sponsorInfo, let additionalInfo):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("message", message as Any), ("entities", entities as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)])
case .sponsoredMessage(let flags, let randomId, let fromId, let chatInvite, let chatInviteHash, let channelPost, let startParam, let webpage, let message, let entities, let sponsorInfo, let additionalInfo):
return ("sponsoredMessage", [("flags", flags as Any), ("randomId", randomId as Any), ("fromId", fromId as Any), ("chatInvite", chatInvite as Any), ("chatInviteHash", chatInviteHash as Any), ("channelPost", channelPost as Any), ("startParam", startParam as Any), ("webpage", webpage as Any), ("message", message as Any), ("entities", entities as Any), ("sponsorInfo", sponsorInfo as Any), ("additionalInfo", additionalInfo as Any)])
}
}
@ -487,16 +488,20 @@ public extension Api {
if Int(_1!) & Int(1 << 2) != 0 {_6 = reader.readInt32() }
var _7: String?
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
var _8: String?
_8 = parseString(reader)
var _9: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
var _8: Api.SponsoredWebPage?
if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.SponsoredWebPage
} }
var _9: String?
_9 = parseString(reader)
var _10: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _10: String?
if Int(_1!) & Int(1 << 7) != 0 {_10 = parseString(reader) }
var _11: String?
if Int(_1!) & Int(1 << 8) != 0 {_11 = parseString(reader) }
if Int(_1!) & Int(1 << 7) != 0 {_11 = parseString(reader) }
var _12: String?
if Int(_1!) & Int(1 << 8) != 0 {_12 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil
@ -504,12 +509,63 @@ public extension Api {
let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
let _c8 = _8 != nil
let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil
let _c10 = (Int(_1!) & Int(1 << 7) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 8) == 0) || _11 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, message: _8!, entities: _9, sponsorInfo: _10, additionalInfo: _11)
let _c8 = (Int(_1!) & Int(1 << 9) == 0) || _8 != nil
let _c9 = _9 != nil
let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil
let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil
let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 {
return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, fromId: _3, chatInvite: _4, chatInviteHash: _5, channelPost: _6, startParam: _7, webpage: _8, message: _9!, entities: _10, sponsorInfo: _11, additionalInfo: _12)
}
else {
return nil
}
}
}
}
public extension Api {
enum SponsoredWebPage: TypeConstructorDescription {
case sponsoredWebPage(flags: Int32, url: String, siteName: String, photo: Api.Photo?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .sponsoredWebPage(let flags, let url, let siteName, let photo):
if boxed {
buffer.appendInt32(1035529315)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(url, buffer: buffer, boxed: false)
serializeString(siteName, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {photo!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .sponsoredWebPage(let flags, let url, let siteName, let photo):
return ("sponsoredWebPage", [("flags", flags as Any), ("url", url as Any), ("siteName", siteName as Any), ("photo", photo as Any)])
}
}
public static func parse_sponsoredWebPage(_ reader: BufferReader) -> SponsoredWebPage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.Photo?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.Photo
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.SponsoredWebPage.sponsoredWebPage(flags: _1!, url: _2!, siteName: _3!, photo: _4)
}
else {
return nil
@ -856,39 +912,3 @@ public extension Api {
}
}
public extension Api {
enum StatsURL: TypeConstructorDescription {
case statsURL(url: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsURL(let url):
if boxed {
buffer.appendInt32(1202287072)
}
serializeString(url, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsURL(let url):
return ("statsURL", [("url", url as Any)])
}
}
public static func parse_statsURL(_ reader: BufferReader) -> StatsURL? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.StatsURL.statsURL(url: _1!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,39 @@
public extension Api {
enum StatsURL: TypeConstructorDescription {
case statsURL(url: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .statsURL(let url):
if boxed {
buffer.appendInt32(1202287072)
}
serializeString(url, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .statsURL(let url):
return ("statsURL", [("url", url as Any)])
}
}
public static func parse_statsURL(_ reader: BufferReader) -> StatsURL? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.StatsURL.statsURL(url: _1!)
}
else {
return nil
}
}
}
}
public extension Api {
enum StickerKeyword: TypeConstructorDescription {
case stickerKeyword(documentId: Int64, keyword: [String])
@ -328,19 +364,20 @@ public extension Api {
}
public extension Api {
indirect enum StoryItem: TypeConstructorDescription {
case storyItem(flags: Int32, id: Int32, date: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?)
case storyItem(flags: Int32, id: Int32, date: Int32, expireDate: Int32, caption: String?, entities: [Api.MessageEntity]?, media: Api.MessageMedia, privacy: [Api.PrivacyRule]?, views: Api.StoryViews?)
case storyItemDeleted(id: Int32)
case storyItemSkipped(id: Int32, date: Int32)
case storyItemSkipped(id: Int32, date: Int32, expireDate: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .storyItem(let flags, let id, let date, let caption, let entities, let media, let privacy, let views):
case .storyItem(let flags, let id, let date, let expireDate, let caption, let entities, let media, let privacy, let views):
if boxed {
buffer.appendInt32(-1882351956)
buffer.appendInt32(1445635639)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(id, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(expireDate, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
@ -361,24 +398,25 @@ public extension Api {
}
serializeInt32(id, buffer: buffer, boxed: false)
break
case .storyItemSkipped(let id, let date):
case .storyItemSkipped(let id, let date, let expireDate):
if boxed {
buffer.appendInt32(-1579626609)
buffer.appendInt32(1764886178)
}
serializeInt32(id, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(expireDate, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .storyItem(let flags, let id, let date, let caption, let entities, let media, let privacy, let views):
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("privacy", privacy as Any), ("views", views as Any)])
case .storyItem(let flags, let id, let date, let expireDate, let caption, let entities, let media, let privacy, let views):
return ("storyItem", [("flags", flags as Any), ("id", id as Any), ("date", date as Any), ("expireDate", expireDate as Any), ("caption", caption as Any), ("entities", entities as Any), ("media", media as Any), ("privacy", privacy as Any), ("views", views as Any)])
case .storyItemDeleted(let id):
return ("storyItemDeleted", [("id", id as Any)])
case .storyItemSkipped(let id, let date):
return ("storyItemSkipped", [("id", id as Any), ("date", date as Any)])
case .storyItemSkipped(let id, let date, let expireDate):
return ("storyItemSkipped", [("id", id as Any), ("date", date as Any), ("expireDate", expireDate as Any)])
}
}
@ -389,34 +427,37 @@ public extension Api {
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: String?
if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) }
var _5: [Api.MessageEntity]?
var _4: Int32?
_4 = reader.readInt32()
var _5: String?
if Int(_1!) & Int(1 << 0) != 0 {_5 = parseString(reader) }
var _6: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _6: Api.MessageMedia?
var _7: Api.MessageMedia?
if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.MessageMedia
_7 = Api.parse(reader, signature: signature) as? Api.MessageMedia
}
var _7: [Api.PrivacyRule]?
var _8: [Api.PrivacyRule]?
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self)
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrivacyRule.self)
} }
var _8: Api.StoryViews?
var _9: Api.StoryViews?
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.StoryViews
_9 = Api.parse(reader, signature: signature) as? Api.StoryViews
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
let _c6 = _6 != nil
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, caption: _4, entities: _5, media: _6!, privacy: _7, views: _8)
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, expireDate: _4!, caption: _5, entities: _6, media: _7!, privacy: _8, views: _9)
}
else {
return nil
@ -438,10 +479,13 @@ public extension Api {
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.StoryItem.storyItemSkipped(id: _1!, date: _2!)
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.StoryItem.storyItemSkipped(id: _1!, date: _2!, expireDate: _3!)
}
else {
return nil

View File

@ -8494,6 +8494,22 @@ public extension Api.functions.stories {
})
}
}
public extension Api.functions.stories {
static func exportStoryLink(userId: Api.InputUser, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.ExportedStoryLink>) {
let buffer = Buffer()
buffer.appendInt32(384058318)
userId.serialize(buffer, true)
serializeInt32(id, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.exportStoryLink", parameters: [("userId", String(describing: userId)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedStoryLink? in
let reader = BufferReader(buffer)
var result: Api.ExportedStoryLink?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.ExportedStoryLink
}
return result
})
}
}
public extension Api.functions.stories {
static func getAllStories(flags: Int32, state: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stories.AllStories>) {
let buffer = Buffer()
@ -8652,9 +8668,9 @@ public extension Api.functions.stories {
}
}
public extension Api.functions.stories {
static func sendStory(flags: Int32, media: Api.InputMedia, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
static func sendStory(flags: Int32, media: Api.InputMedia, caption: String?, entities: [Api.MessageEntity]?, privacyRules: [Api.InputPrivacyRule], randomId: Int64, period: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(-1956877946)
buffer.appendInt32(1112331386)
serializeInt32(flags, buffer: buffer, boxed: false)
media.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeString(caption!, buffer: buffer, boxed: false)}
@ -8669,7 +8685,8 @@ public extension Api.functions.stories {
item.serialize(buffer, true)
}
serializeInt64(randomId, buffer: buffer, boxed: false)
return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("media", String(describing: media)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "stories.sendStory", parameters: [("flags", String(describing: flags)), ("media", String(describing: media)), ("caption", String(describing: caption)), ("entities", String(describing: entities)), ("privacyRules", String(describing: privacyRules)), ("randomId", String(describing: randomId)), ("period", String(describing: period))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {

View File

@ -1377,7 +1377,7 @@ public extension Api {
case documentAttributeHasStickers
case documentAttributeImageSize(w: Int32, h: Int32)
case documentAttributeSticker(flags: Int32, alt: String, stickerset: Api.InputStickerSet, maskCoords: Api.MaskCoords?)
case documentAttributeVideo(flags: Int32, duration: Int32, w: Int32, h: Int32, preloadPrefixSize: Int32?)
case documentAttributeVideo(flags: Int32, duration: Double, w: Int32, h: Int32, preloadPrefixSize: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -1435,10 +1435,10 @@ public extension Api {
break
case .documentAttributeVideo(let flags, let duration, let w, let h, let preloadPrefixSize):
if boxed {
buffer.appendInt32(-381651053)
buffer.appendInt32(-745541182)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(duration, buffer: buffer, boxed: false)
serializeDouble(duration, buffer: buffer, boxed: false)
serializeInt32(w, buffer: buffer, boxed: false)
serializeInt32(h, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(preloadPrefixSize!, buffer: buffer, boxed: false)}
@ -1567,8 +1567,8 @@ public extension Api {
public static func parse_documentAttributeVideo(_ reader: BufferReader) -> DocumentAttribute? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _2: Double?
_2 = reader.readDouble()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?

View File

@ -38,6 +38,42 @@ public extension Api {
}
}
public extension Api {
enum ExportedStoryLink: TypeConstructorDescription {
case exportedStoryLink(link: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .exportedStoryLink(let link):
if boxed {
buffer.appendInt32(1070138683)
}
serializeString(link, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .exportedStoryLink(let link):
return ("exportedStoryLink", [("link", link as Any)])
}
}
public static func parse_exportedStoryLink(_ reader: BufferReader) -> ExportedStoryLink? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.ExportedStoryLink.exportedStoryLink(link: _1!)
}
else {
return nil
}
}
}
}
public extension Api {
enum FileHash: TypeConstructorDescription {
case fileHash(offset: Int64, limit: Int32, hash: Buffer)

View File

@ -282,6 +282,7 @@ public extension Api {
case inputMediaPhoto(flags: Int32, id: Api.InputPhoto, ttlSeconds: Int32?)
case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?)
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
case inputMediaStory(userId: Api.InputUser, id: Int32)
case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, ttlSeconds: Int32?)
case inputMediaUploadedPhoto(flags: Int32, file: Api.InputFile, stickers: [Api.InputDocument]?, ttlSeconds: Int32?)
case inputMediaVenue(geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
@ -397,6 +398,13 @@ public extension Api {
item.serialize(buffer, true)
}}
break
case .inputMediaStory(let userId, let id):
if boxed {
buffer.appendInt32(-1702447729)
}
userId.serialize(buffer, true)
serializeInt32(id, buffer: buffer, boxed: false)
break
case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let ttlSeconds):
if boxed {
buffer.appendInt32(1530447553)
@ -470,6 +478,8 @@ public extension Api {
return ("inputMediaPhotoExternal", [("flags", flags as Any), ("url", url as Any), ("ttlSeconds", ttlSeconds as Any)])
case .inputMediaPoll(let flags, let poll, let correctAnswers, let solution, let solutionEntities):
return ("inputMediaPoll", [("flags", flags as Any), ("poll", poll as Any), ("correctAnswers", correctAnswers as Any), ("solution", solution as Any), ("solutionEntities", solutionEntities as Any)])
case .inputMediaStory(let userId, let id):
return ("inputMediaStory", [("userId", userId as Any), ("id", id as Any)])
case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let ttlSeconds):
return ("inputMediaUploadedDocument", [("flags", flags as Any), ("file", file as Any), ("thumb", thumb as Any), ("mimeType", mimeType as Any), ("attributes", attributes as Any), ("stickers", stickers as Any), ("ttlSeconds", ttlSeconds as Any)])
case .inputMediaUploadedPhoto(let flags, let file, let stickers, let ttlSeconds):
@ -714,6 +724,22 @@ public extension Api {
return nil
}
}
public static func parse_inputMediaStory(_ reader: BufferReader) -> InputMedia? {
var _1: Api.InputUser?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.InputUser
}
var _2: Int32?
_2 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputMedia.inputMediaStory(userId: _1!, id: _2!)
}
else {
return nil
}
}
public static func parse_inputMediaUploadedDocument(_ reader: BufferReader) -> InputMedia? {
var _1: Int32?
_1 = reader.readInt32()

View File

@ -10,6 +10,7 @@ public final class AdMessageAttribute: MessageAttribute {
public enum MessageTarget {
case peer(id: EnginePeer.Id, message: EngineMessage.Id?, startParam: String?)
case join(title: String, joinHash: String)
case webPage(title: String, url: String)
}
public let opaqueId: Data

View File

@ -380,6 +380,10 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
}
case let .messageMediaDice(value, emoticon):
return (TelegramMediaDice(emoji: emoticon, value: value), nil, nil, nil)
case let .messageMediaStory(userId, id):
let _ = userId
let _ = id
return (nil, nil, nil, nil)
}
}

View File

@ -17,13 +17,13 @@ func dimensionsForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) ->
return nil
}
func durationForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) -> Int32? {
func durationForFileAttributes(_ attributes: [TelegramMediaFileAttribute]) -> Double? {
for attribute in attributes {
switch attribute {
case let .Video(duration, _, _, _):
return Int32(duration)
return duration
case let .Audio(_, duration, _, _, _):
return Int32(duration)
return Double(duration)
default:
break
}
@ -42,7 +42,7 @@ public extension TelegramMediaFile {
}
}
var duration: Int32? {
var duration: Double? {
return durationForFileAttributes(self.attributes)
}
}
@ -105,7 +105,7 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
if (flags & (1 << 1)) != 0 {
videoFlags.insert(.supportsStreaming)
}
result.append(.Video(duration: Int(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))
case let .documentAttributeAudio(flags, duration, title, performer, waveform):
let isVoice = (flags & (1 << 10)) != 0
let waveformBuffer: Data? = waveform?.makeData()

View File

@ -5,7 +5,7 @@ public extension TelegramMediaWebFile {
return dimensionsForFileAttributes(self.attributes)
}
var duration: Int32? {
var duration: Double? {
return durationForFileAttributes(self.attributes)
}
}

View File

@ -561,7 +561,7 @@ func inputDocumentAttributesFromFileAttributes(_ fileAttributes: [TelegramMediaF
flags |= (1 << 2)
}
attributes.append(.documentAttributeVideo(flags: flags, duration: Int32(duration), w: Int32(size.width), h: Int32(size.height), preloadPrefixSize: preloadSize))
attributes.append(.documentAttributeVideo(flags: flags, duration: duration, w: Int32(size.width), h: Int32(size.height), preloadPrefixSize: preloadSize))
case let .Audio(isVoice, duration, title, performer, waveform):
var flags: Int32 = 0
if isVoice {

View File

@ -610,7 +610,7 @@ extension TelegramMediaFileAttribute {
}
self = .Sticker(displayText: alt, packReference: packReference, maskData: nil)
case let .documentAttributeVideo(duration, w, h):
self = .Video(duration: Int(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil)
self = .Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: [], preloadSize: nil)
}
}
}
@ -642,7 +642,7 @@ extension TelegramMediaFileAttribute {
if (flags & (1 << 0)) != 0 {
videoFlags.insert(.instantRoundVideo)
}
self = .Video(duration: Int(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)
}
}
}
@ -674,7 +674,7 @@ extension TelegramMediaFileAttribute {
if (flags & (1 << 0)) != 0 {
videoFlags.insert(.instantRoundVideo)
}
self = .Video(duration: Int(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)
}
}
}
@ -706,7 +706,7 @@ extension TelegramMediaFileAttribute {
if (flags & (1 << 0)) != 0 {
videoFlags.insert(.instantRoundVideo)
}
self = .Video(duration: Int(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)
}
}
}
@ -749,7 +749,7 @@ private func maximumMediaAutoremoveTimeout(_ media: [Media]) -> Int32 {
for media in media {
if let file = media as? TelegramMediaFile {
if let duration = file.duration {
maxDuration = max(maxDuration, duration)
maxDuration = max(maxDuration, Int32(duration))
}
}
}
@ -821,7 +821,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
text = caption
}
if let file = file {
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(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), .FileName(fileName: "video.mov")]
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
@ -1040,7 +1040,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
text = caption
}
if let file = file {
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(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), .FileName(fileName: "video.mov")]
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
@ -1319,7 +1319,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
text = caption
}
if let file = file {
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(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), .FileName(fileName: "video.mov")]
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
@ -1520,7 +1520,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
text = caption
}
if let file = file {
let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(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), .FileName(fileName: "video.mov")]
var previewRepresentations: [TelegramMediaImageRepresentation] = []
if thumb.size != 0 {
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))

View File

@ -223,7 +223,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
case Sticker(displayText: String, packReference: StickerPackReference?, maskData: StickerMaskCoords?)
case ImageSize(size: PixelDimensions)
case Animated
case Video(duration: Int, size: PixelDimensions, flags: TelegramMediaVideoFlags, preloadSize: Int32?)
case Video(duration: Double, size: PixelDimensions, flags: TelegramMediaVideoFlags, preloadSize: Int32?)
case Audio(isVoice: Bool, duration: Int, title: String?, performer: String?, waveform: Data?)
case HasLinkedStickers
case hintFileIsLarge
@ -243,7 +243,14 @@ public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
case typeAnimated:
self = .Animated
case typeVideo:
self = .Video(duration: Int(decoder.decodeInt32ForKey("du", orElse: 0)), 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"))
let duration: Double
if let value = decoder.decodeOptionalDoubleForKey("dur") {
duration = value
} else {
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"))
case typeAudio:
let waveformBuffer = decoder.decodeBytesForKeyNoCopy("wf")
var waveform: Data?
@ -292,7 +299,7 @@ public enum TelegramMediaFileAttribute: PostboxCoding, Equatable {
encoder.encodeInt32(typeAnimated, forKey: "t")
case let .Video(duration, size, flags, preloadSize):
encoder.encodeInt32(typeVideo, forKey: "t")
encoder.encodeInt32(Int32(duration), forKey: "du")
encoder.encodeDouble(duration, forKey: "dur")
encoder.encodeInt32(Int32(size.width), forKey: "w")
encoder.encodeInt32(Int32(size.height), forKey: "h")
encoder.encodeInt32(flags.rawValue, forKey: "f")

View File

@ -32,6 +32,7 @@ private class AdMessagesHistoryContextImpl {
enum CodingKeys: String, CodingKey {
case peer
case invite
case webPage
}
struct Invite: Equatable, Codable {
@ -39,8 +40,14 @@ private class AdMessagesHistoryContextImpl {
var joinHash: String
}
struct WebPage: Equatable, Codable {
var title: String
var url: String
}
case peer(PeerId)
case invite(Invite)
case webPage(WebPage)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
@ -49,6 +56,8 @@ private class AdMessagesHistoryContextImpl {
self = .peer(PeerId(peer))
} else if let invite = try container.decodeIfPresent(Invite.self, forKey: .invite) {
self = .invite(invite)
} else if let webPage = try container.decodeIfPresent(WebPage.self, forKey: .webPage) {
self = .webPage(webPage)
} else {
throw DecodingError.generic
}
@ -62,6 +71,8 @@ private class AdMessagesHistoryContextImpl {
try container.encode(peerId.toInt64(), forKey: .peer)
case let .invite(invite):
try container.encode(invite, forKey: .invite)
case let .webPage(webPage):
try container.encode(webPage, forKey: .webPage)
}
}
}
@ -205,6 +216,8 @@ private class AdMessagesHistoryContextImpl {
target = .peer(id: peerId, message: self.messageId, startParam: self.startParam)
case let .invite(invite):
target = .join(title: invite.title, joinHash: invite.joinHash)
case let .webPage(webPage):
target = .webPage(title: webPage.title, url: webPage.url)
}
let mappedMessageType: AdMessageAttribute.MessageType
switch self.messageType {
@ -251,6 +264,23 @@ private class AdMessagesHistoryContextImpl {
defaultBannedRights: nil,
usernames: []
)
case let .webPage(webPage):
author = TelegramChannel(
id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(1)),
accessHash: nil,
title: webPage.title,
username: nil,
photo: [],
creationDate: 0,
version: 0,
participationStatus: .left,
info: .broadcast(TelegramChannelBroadcastInfo(flags: [])),
flags: [],
restrictionInfo: nil,
adminRights: nil,
bannedRights: nil,
defaultBannedRights: nil,
usernames: [])
}
messagePeers[author.id] = author
@ -471,7 +501,7 @@ private class AdMessagesHistoryContextImpl {
for message in messages {
switch message {
case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, message, entities, sponsorInfo, additionalInfo):
case let .sponsoredMessage(flags, randomId, fromId, chatInvite, chatInviteHash, channelPost, startParam, webPage, message, entities, sponsorInfo, additionalInfo):
var parsedEntities: [MessageTextEntity] = []
if let entities = entities {
parsedEntities = messageTextEntitiesFromApiEntities(entities)
@ -483,6 +513,11 @@ private class AdMessagesHistoryContextImpl {
var target: CachedMessage.Target?
if let fromId = fromId {
target = .peer(fromId.peerId)
} else if let webPage = webPage {
if case let .sponsoredWebPage(_, url, siteName, photo) = webPage {
let _ = photo
target = .webPage(CachedMessage.Target.WebPage(title: siteName, url: url))
}
} else if let chatInvite = chatInvite, let chatInviteHash = chatInviteHash {
switch chatInvite {
case let .chatInvite(flags, title, _, photo, participantsCount, participants):

View File

@ -53,7 +53,7 @@ func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messa
if attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0 {
var timeout = attribute.timeout
if let duration = message.secretMediaDuration {
timeout = max(timeout, duration)
timeout = max(timeout, Int32(duration))
}
updatedAttributes[i] = AutoremoveTimeoutMessageAttribute(timeout: timeout, countdownBeginTime: timestamp)
updateMessage = true
@ -84,7 +84,7 @@ func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messa
if attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0 {
var timeout = attribute.timeout
if let duration = message.secretMediaDuration {
timeout = max(timeout, duration)
timeout = max(timeout, Int32(duration))
}
updatedAttributes[i] = AutoclearTimeoutMessageAttribute(timeout: timeout, countdownBeginTime: timestamp)
updateMessage = true

View File

@ -113,7 +113,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: Int(Int32(externalReference.content?.duration ?? 0)), size: dimensions, flags: [], preloadSize: nil))
fileAttributes.append(.Video(duration: externalReference.content?.duration ?? 0.0, size: dimensions, flags: [], preloadSize: nil))
}
}

View File

@ -5,7 +5,7 @@ import TelegramApi
public enum EngineStoryInputMedia {
case image(dimensions: PixelDimensions, data: Data)
case video(dimensions: PixelDimensions, duration: Int, resource: TelegramMediaResource)
case video(dimensions: PixelDimensions, duration: Double, resource: TelegramMediaResource)
}
public struct EngineStoryPrivacy: Equatable {
@ -99,6 +99,7 @@ public enum Stories {
private enum CodingKeys: String, CodingKey {
case id
case timestamp
case expirationTimestamp
case media
case text
case entities
@ -111,6 +112,7 @@ public enum Stories {
public let id: Int32
public let timestamp: Int32
public let expirationTimestamp: Int32
public let media: Media?
public let text: String
public let entities: [MessageTextEntity]
@ -123,6 +125,7 @@ public enum Stories {
public init(
id: Int32,
timestamp: Int32,
expirationTimestamp: Int32,
media: Media?,
text: String,
entities: [MessageTextEntity],
@ -134,6 +137,7 @@ public enum Stories {
) {
self.id = id
self.timestamp = timestamp
self.expirationTimestamp = expirationTimestamp
self.media = media
self.text = text
self.entities = entities
@ -149,6 +153,7 @@ public enum Stories {
self.id = try container.decode(Int32.self, forKey: .id)
self.timestamp = try container.decode(Int32.self, forKey: .timestamp)
self.expirationTimestamp = try container.decode(Int32.self, forKey: .expirationTimestamp)
if let mediaData = try container.decodeIfPresent(Data.self, forKey: .media) {
self.media = PostboxDecoder(buffer: MemoryBuffer(data: mediaData)).decodeRootObject() as? Media
@ -170,6 +175,7 @@ public enum Stories {
try container.encode(self.id, forKey: .id)
try container.encode(self.timestamp, forKey: .timestamp)
try container.encode(self.expirationTimestamp, forKey: .expirationTimestamp)
if let media = self.media {
let encoder = PostboxEncoder()
@ -194,6 +200,9 @@ public enum Stories {
if lhs.timestamp != rhs.timestamp {
return false
}
if lhs.expirationTimestamp != rhs.expirationTimestamp {
return false
}
if let lhsMedia = lhs.media, let rhsMedia = rhs.media {
if !lhsMedia.isEqual(to: rhsMedia) {
@ -235,17 +244,21 @@ public enum Stories {
private enum CodingKeys: String, CodingKey {
case id
case timestamp
case expirationTimestamp
}
public let id: Int32
public let timestamp: Int32
public let expirationTimestamp: Int32
public init(
id: Int32,
timestamp: Int32
timestamp: Int32,
expirationTimestamp: Int32
) {
self.id = id
self.timestamp = timestamp
self.expirationTimestamp = expirationTimestamp
}
public init(from decoder: Decoder) throws {
@ -253,6 +266,7 @@ public enum Stories {
self.id = try container.decode(Int32.self, forKey: .id)
self.timestamp = try container.decode(Int32.self, forKey: .timestamp)
self.expirationTimestamp = try container.decode(Int32.self, forKey: .expirationTimestamp)
}
public func encode(to encoder: Encoder) throws {
@ -260,6 +274,7 @@ public enum Stories {
try container.encode(self.id, forKey: .id)
try container.encode(self.timestamp, forKey: .timestamp)
try container.encode(self.expirationTimestamp, forKey: .expirationTimestamp)
}
public static func ==(lhs: Placeholder, rhs: Placeholder) -> Bool {
@ -269,6 +284,9 @@ public enum Stories {
if lhs.timestamp != rhs.timestamp {
return false
}
if lhs.expirationTimestamp != rhs.expirationTimestamp {
return false
}
return true
}
}
@ -489,7 +507,7 @@ public final class EngineStorySubscriptions: Equatable {
public enum StoryUploadResult {
case progress(Float)
case completed
case completed(Int32?)
}
private func uploadedStoryContent(account: Account, media: EngineStoryInputMedia) -> (signal: Signal<PendingMessageUploadedContentResult?, NoError>, media: Media) {
@ -615,7 +633,7 @@ private func apiInputPrivacyRules(privacy: EngineStoryPrivacy, transaction: Tran
return privacyRules
}
func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy) -> Signal<StoryUploadResult, NoError> {
func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, period: Int, randomId: Int64) -> Signal<StoryUploadResult, NoError> {
let (contentSignal, originalMedia) = uploadedStoryContent(account: account, media: media)
return contentSignal
|> mapToSignal { result -> Signal<StoryUploadResult, NoError> in
@ -659,18 +677,21 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text:
caption: apiCaption,
entities: apiEntities,
privacyRules: privacyRules,
randomId: Int64.random(in: Int64.min ... Int64.max)
randomId: randomId,
period: Int32(period)
))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.Updates?, NoError> in
return .single(nil)
}
|> mapToSignal { updates -> Signal<StoryUploadResult, NoError> in
var id: Int32?
if let updates = updates {
for update in updates.allUpdates {
if case let .updateStory(_, story) = update {
switch story {
case let .storyItem(_, _, _, _, _, media, _, _):
case let .storyItem(_, idValue, _, _, _, _, media, _, _):
id = idValue
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
if let parsedMedia = parsedMedia {
applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false)
@ -684,7 +705,7 @@ func _internal_uploadStory(account: Account, media: EngineStoryInputMedia, text:
account.stateManager.addUpdates(updates)
}
return .single(.completed)
return .single(.completed(id))
}
default:
return .complete()
@ -769,7 +790,7 @@ func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: In
for update in updates.allUpdates {
if case let .updateStory(_, story) = update {
switch story {
case let .storyItem(_, _, _, _, _, media, _, _):
case let .storyItem(_, _, _, _, _, _, media, _, _):
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, account.peerId)
if let parsedMedia = parsedMedia, let originalMedia = originalMedia {
applyMediaResourceChanges(from: originalMedia, to: parsedMedia, postbox: account.postbox, force: false)
@ -782,7 +803,7 @@ func _internal_editStory(account: Account, media: EngineStoryInputMedia?, id: In
account.stateManager.addUpdates(updates)
}
return .single(.completed)
return .single(.completed(id))
}
}
|> switchToLatest
@ -847,6 +868,7 @@ func _internal_updateStoryIsPinned(account: Account, id: Int32, isPinned: Bool)
let updatedItem = Stories.Item(
id: item.id,
timestamp: item.timestamp,
expirationTimestamp: item.expirationTimestamp,
media: item.media,
text: item.text,
entities: item.entities,
@ -878,11 +900,11 @@ func _internal_updateStoryIsPinned(account: Account, id: Int32, isPinned: Bool)
extension Api.StoryItem {
var id: Int32 {
switch self {
case let .storyItem(_, id, _, _, _, _, _, _):
case let .storyItem(_, id, _, _, _, _, _, _, _):
return id
case let .storyItemDeleted(id):
return id
case let .storyItemSkipped(id, _):
case let .storyItemSkipped(id, _, _):
return id
}
}
@ -904,8 +926,7 @@ extension Stories.Item.Views {
extension Stories.StoredItem {
init?(apiStoryItem: Api.StoryItem, peerId: PeerId, transaction: Transaction) {
switch apiStoryItem {
case let .storyItem(flags, id, date, caption, entities, media, privacy, views):
let _ = flags
case let .storyItem(flags, id, date, expireDate, caption, entities, media, privacy, views):
let (parsedMedia, _, _, _) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let parsedMedia = parsedMedia {
var parsedPrivacy: Stories.Item.Privacy?
@ -946,6 +967,7 @@ extension Stories.StoredItem {
let item = Stories.Item(
id: id,
timestamp: date,
expirationTimestamp: expireDate,
media: parsedMedia,
text: caption ?? "",
entities: entities.flatMap { entities in return messageTextEntitiesFromApiEntities(entities) } ?? [],
@ -959,8 +981,8 @@ extension Stories.StoredItem {
} else {
return nil
}
case let .storyItemSkipped(id, date):
self = .placeholder(Stories.Placeholder(id: id, timestamp: date))
case let .storyItemSkipped(id, date, expireDate):
self = .placeholder(Stories.Placeholder(id: id, timestamp: date, expirationTimestamp: expireDate))
case .storyItemDeleted:
return nil
}

View File

@ -33,6 +33,7 @@ public final class EngineStoryItem: Equatable {
public let id: Int32
public let timestamp: Int32
public let expirationTimestamp: Int32
public let media: EngineMedia
public let text: String
public let entities: [MessageTextEntity]
@ -42,9 +43,10 @@ public final class EngineStoryItem: Equatable {
public let isExpired: Bool
public let isPublic: Bool
public init(id: Int32, timestamp: Int32, media: EngineMedia, text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool) {
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool) {
self.id = id
self.timestamp = timestamp
self.expirationTimestamp = expirationTimestamp
self.media = media
self.text = text
self.entities = entities
@ -62,6 +64,9 @@ public final class EngineStoryItem: Equatable {
if lhs.timestamp != rhs.timestamp {
return false
}
if lhs.expirationTimestamp != rhs.expirationTimestamp {
return false
}
if lhs.media != rhs.media {
return false
}
@ -483,6 +488,7 @@ public final class PeerStoryListContext {
let mappedItem = EngineStoryItem(
id: item.id,
timestamp: item.timestamp,
expirationTimestamp: item.expirationTimestamp,
media: EngineMedia(media),
text: item.text,
entities: item.entities,
@ -597,6 +603,7 @@ public final class PeerStoryListContext {
updatedState.items[index] = EngineStoryItem(
id: item.id,
timestamp: item.timestamp,
expirationTimestamp: item.expirationTimestamp,
media: EngineMedia(media),
text: item.text,
entities: item.entities,

View File

@ -876,8 +876,8 @@ public extension TelegramEngine {
}
}
public func uploadStory(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy) -> Signal<StoryUploadResult, NoError> {
return _internal_uploadStory(account: self.account, media: media, text: text, entities: entities, pin: pin, privacy: privacy)
public func uploadStory(media: EngineStoryInputMedia, text: String, entities: [MessageTextEntity], pin: Bool, privacy: EngineStoryPrivacy, period: Int, randomId: Int64) -> Signal<StoryUploadResult, NoError> {
return _internal_uploadStory(account: self.account, media: media, text: text, entities: entities, pin: pin, privacy: privacy, period: period, randomId: randomId)
}
public func editStory(media: EngineStoryInputMedia?, id: Int32, text: String, entities: [MessageTextEntity], privacy: EngineStoryPrivacy?) -> Signal<StoryUploadResult, NoError> {

View File

@ -264,7 +264,7 @@ public extension Message {
}
public extension Message {
var secretMediaDuration: Int32? {
var secretMediaDuration: Double? {
var found = false
for attribute in self.attributes {
if let _ = attribute as? AutoremoveTimeoutMessageAttribute {

View File

@ -236,6 +236,13 @@ public enum PresentationResourceKey: Int32 {
case chatMessageAttachedContentHighlightedButtonIconInstantOutgoingWithWallpaper
case chatMessageAttachedContentHighlightedButtonIconInstantOutgoingWithoutWallpaper
case chatMessageAttachedContentButtonIconLinkIncoming
case chatMessageAttachedContentHighlightedButtonIconLinkIncomingWithWallpaper
case chatMessageAttachedContentHighlightedButtonIconLinkIncomingWithoutWallpaper
case chatMessageAttachedContentButtonIconLinkOutgoing
case chatMessageAttachedContentHighlightedButtonIconLinkOutgoingWithWallpaper
case chatMessageAttachedContentHighlightedButtonIconLinkOutgoingWithoutWallpaper
case chatCommandPanelArrowImage
case sharedMediaFileDownloadStartIcon

View File

@ -854,25 +854,25 @@ public struct PresentationResourcesChat {
public static func chatMessageAttachedContentButtonIncoming(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatMessageAttachedContentButtonIncoming.rawValue, { theme in
return generateStretchableFilledCircleImage(diameter: 9.0, color: nil, strokeColor: theme.chat.message.incoming.accentControlColor, strokeWidth: 1.0, backgroundColor: nil)
return generateStretchableFilledCircleImage(diameter: 16.0, color: nil, strokeColor: theme.chat.message.incoming.accentControlColor, strokeWidth: 1.0, backgroundColor: nil)
})
}
public static func chatMessageAttachedContentHighlightedButtonIncoming(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatMessageAttachedContentHighlightedButtonIncoming.rawValue, { theme in
return generateStretchableFilledCircleImage(diameter: 9.0, color: theme.chat.message.incoming.accentControlColor)
return generateStretchableFilledCircleImage(diameter: 16.0, color: theme.chat.message.incoming.accentControlColor)
})
}
public static func chatMessageAttachedContentButtonOutgoing(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatMessageAttachedContentButtonOutgoing.rawValue, { theme in
return generateStretchableFilledCircleImage(diameter: 9.0, color: nil, strokeColor: theme.chat.message.outgoing.accentControlColor, strokeWidth: 1.0, backgroundColor: nil)
return generateStretchableFilledCircleImage(diameter: 16.0, color: nil, strokeColor: theme.chat.message.outgoing.accentControlColor, strokeWidth: 1.0, backgroundColor: nil)
})
}
public static func chatMessageAttachedContentHighlightedButtonOutgoing(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatMessageAttachedContentHighlightedButtonOutgoing.rawValue, { theme in
return generateStretchableFilledCircleImage(diameter: 9.0, color: theme.chat.message.outgoing.accentControlColor)
return generateStretchableFilledCircleImage(diameter: 16.0, color: theme.chat.message.outgoing.accentControlColor)
})
}
@ -902,6 +902,32 @@ public struct PresentationResourcesChat {
})
}
public static func chatMessageAttachedContentButtonIconLinkIncoming(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatMessageAttachedContentButtonIconLinkIncoming.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLink"), color: theme.chat.message.incoming.accentControlColor)
})
}
public static func chatMessageAttachedContentHighlightedButtonIconLinkIncoming(_ theme: PresentationTheme, wallpaper: Bool) -> UIImage? {
let key: PresentationResourceKey = !wallpaper ? PresentationResourceKey.chatMessageAttachedContentHighlightedButtonIconLinkIncomingWithoutWallpaper : PresentationResourceKey.chatMessageAttachedContentHighlightedButtonIconLinkIncomingWithWallpaper
return theme.image(key.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLink"), color: bubbleColorComponents(theme: theme, incoming: true, wallpaper: wallpaper).fill[0])
})
}
public static func chatMessageAttachedContentButtonIconLinkOutgoing(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatMessageAttachedContentButtonIconLinkOutgoing.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLink"), color: theme.chat.message.outgoing.accentControlColor)
})
}
public static func chatMessageAttachedContentHighlightedButtonIconLinkOutgoing(_ theme: PresentationTheme, wallpaper: Bool) -> UIImage? {
let key: PresentationResourceKey = !wallpaper ? PresentationResourceKey.chatMessageAttachedContentHighlightedButtonIconLinkOutgoingWithoutWallpaper : PresentationResourceKey.chatMessageAttachedContentHighlightedButtonIconLinkOutgoingWithWallpaper
return theme.image(key.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotLink"), color: bubbleColorComponents(theme: theme, incoming: false, wallpaper: wallpaper).fill[0])
})
}
public static func chatBubbleReplyThumbnailPlayImage(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatBubbleReplyThumbnailPlayImage.rawValue, { theme in
return generateImage(CGSize(width: 16.0, height: 16.0), rotatedContext: { size, context in

View File

@ -1077,7 +1077,11 @@ public class CameraScreen: ViewController {
let progress = 1.0 - value
let maxScale = (layout.size.width - 16.0 * 2.0) / layout.size.width
let maxOffset = -56.0
let topInset: CGFloat = (layout.statusBarHeight ?? 0.0) + 12.0
let maxOffset = (topInset - (layout.size.height - layout.size.height * maxScale) / 2.0)
//let maxOffset = -56.0
let scale = 1.0 * progress + (1.0 - progress) * maxScale
let offset = (1.0 - progress) * maxOffset
@ -1129,7 +1133,8 @@ public class CameraScreen: ViewController {
self.validLayout = layout
let previewSize = CGSize(width: layout.size.width, height: floorToScreenPixels(layout.size.width * 1.77778))
let topInset: CGFloat = floorToScreenPixels(layout.size.height - previewSize.height) / 2.0
let topInset: CGFloat = (layout.statusBarHeight ?? 0.0) + 12.0
let bottomInset = layout.size.height - previewSize.height - topInset
let environment = ViewControllerComponentContainer.Environment(
statusBarHeight: layout.statusBarHeight ?? 0.0,
@ -1137,7 +1142,7 @@ public class CameraScreen: ViewController {
safeInsets: UIEdgeInsets(
top: topInset,
left: layout.safeInsets.left,
bottom: topInset,
bottom: bottomInset,
right: layout.safeInsets.right
),
inputHeight: layout.inputHeight ?? 0.0,

View File

@ -212,7 +212,7 @@ public func legacyInstantVideoController(theme: PresentationTheme, panelFrame: C
}
}
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: Int(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)])
var message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])
let scheduleTime: Int32? = scheduleTimestamp > 0 ? scheduleTimestamp : nil

View File

@ -131,35 +131,3 @@ public func storyDrafts(engine: TelegramEngine) -> Signal<[MediaEditorDraft], No
return result
}
}
public func saveStorySource(engine: TelegramEngine, item: MediaEditorDraft, id: Int64) {
let key = EngineDataBuffer(length: 8)
key.setInt64(0, value: id)
let _ = engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key, item: item).start()
}
public func removeStorySource(engine: TelegramEngine, id: Int64) {
let key = EngineDataBuffer(length: 8)
key.setInt64(0, value: id)
let _ = engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key)
}
public func moveStorySource(engine: TelegramEngine, from fromId: Int64, to toId: Int64) {
let fromKey = EngineDataBuffer(length: 8)
fromKey.setInt64(0, value: fromId)
let toKey = EngineDataBuffer(length: 8)
toKey.setInt64(0, value: toId)
let _ = engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: fromKey))
|> mapToSignal { item -> Signal<Never, NoError> in
if let item = item?.get(MediaEditorDraft.self) {
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: toKey, item: item)
|> then(
engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: fromKey)
)
} else {
return .complete()
}
}
}

View File

@ -681,7 +681,7 @@ public extension MediaEditorValues {
}
var hasBlur: Bool {
if let blurValue = self.toolValues[.blur] as? BlurValue, blurValue.mode != .off || blurValue.intensity > toolEpsilon {
if let blurValue = self.toolValues[.blur] as? BlurValue, blurValue.mode != .off && blurValue.intensity > toolEpsilon {
return true
} else {
return false

View File

@ -23,6 +23,7 @@ swift_library(
"//submodules/AccountContext:AccountContext",
"//submodules/AppBundle:AppBundle",
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/ContextUI",
"//submodules/LegacyComponents:LegacyComponents",

View File

@ -782,7 +782,7 @@ final class MediaEditorScreenComponent: Component {
),
action: {
if let controller = environment.controller() as? MediaEditorScreen {
controller.presentPrivacySettings()
controller.openPrivacySettings()
}
}
).tagged(privacyButtonTag)),
@ -1035,8 +1035,8 @@ private let storyDimensions = CGSize(width: 1080.0, height: 1920.0)
private let storyMaxVideoDuration: Double = 60.0
public enum MediaEditorResultPrivacy: Equatable {
case story(privacy: EngineStoryPrivacy, timeout: Int32, archive: Bool)
case message(peers: [EnginePeer.Id], timeout: Int32?)
case story(privacy: EngineStoryPrivacy, timeout: Int, archive: Bool)
case message(peers: [EnginePeer.Id], timeout: Int?)
}
public final class MediaEditorScreen: ViewController {
@ -1304,7 +1304,7 @@ public final class MediaEditorScreen: ViewController {
}
let initialValues: MediaEditorValues?
if case let .draft(draft) = subject {
if case let .draft(draft, _) = subject {
initialValues = draft.values
for entity in draft.values.entities {
@ -1576,6 +1576,10 @@ public final class MediaEditorScreen: ViewController {
}
}
}
} else {
if let view = self.componentHost.view as? MediaEditorScreenComponent.View {
view.animateIn(from: .camera)
}
}
// Queue.mainQueue().after(0.5) {
@ -1766,6 +1770,34 @@ public final class MediaEditorScreen: ViewController {
}
}
func updateEditProgress(_ progress: Float) {
guard let controller = self.controller else {
return
}
if let saveTooltip = self.saveTooltip {
if case .completion = saveTooltip.content {
saveTooltip.dismiss()
self.saveTooltip = nil
}
}
let text = "Uploading..."
if let tooltipController = self.saveTooltip {
tooltipController.content = .progress(text, progress)
} else {
let tooltipController = SaveProgressScreen(context: self.context, content: .progress(text, 0.0))
tooltipController.cancelled = { [weak self] in
if let self, let controller = self.controller {
controller.cancelVideoExport()
}
}
controller.present(tooltipController, in: .current)
self.saveTooltip = tooltipController
}
}
func updateVideoExportProgress(_ progress: Float) {
guard let controller = self.controller else {
return
@ -1847,7 +1879,8 @@ public final class MediaEditorScreen: ViewController {
self.validLayout = layout
let previewSize = CGSize(width: layout.size.width, height: floorToScreenPixels(layout.size.width * 1.77778))
let topInset: CGFloat = floorToScreenPixels(layout.size.height - previewSize.height) / 2.0
let topInset: CGFloat = (layout.statusBarHeight ?? 0.0) + 12.0 //floorToScreenPixels(layout.size.height - previewSize.height) / 2.0
let bottomInset = layout.size.height - previewSize.height - topInset
let environment = ViewControllerComponentContainer.Environment(
statusBarHeight: layout.statusBarHeight ?? 0.0,
@ -1855,7 +1888,7 @@ public final class MediaEditorScreen: ViewController {
safeInsets: UIEdgeInsets(
top: topInset,
left: layout.safeInsets.left,
bottom: topInset,
bottom: bottomInset,
right: layout.safeInsets.right
),
inputHeight: layout.inputHeight ?? 0.0,
@ -2031,7 +2064,7 @@ public final class MediaEditorScreen: ViewController {
if self.entitiesView.selectedEntityView != nil || self.isDisplayingTool {
bottomInputOffset = inputHeight / 2.0
} else {
bottomInputOffset = inputHeight - topInset - 17.0
bottomInputOffset = inputHeight - bottomInset - 17.0
}
}
@ -2061,7 +2094,7 @@ public final class MediaEditorScreen: ViewController {
case image(UIImage, PixelDimensions)
case video(String, UIImage?, PixelDimensions)
case asset(PHAsset)
case draft(MediaEditorDraft)
case draft(MediaEditorDraft, Int64?)
var dimensions: PixelDimensions {
switch self {
@ -2069,7 +2102,7 @@ public final class MediaEditorScreen: ViewController {
return dimensions
case let .asset(asset):
return PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight))
case let .draft(draft):
case let .draft(draft, _):
return draft.dimensions
}
}
@ -2082,7 +2115,7 @@ public final class MediaEditorScreen: ViewController {
return .video(videoPath, transitionImage, dimensions)
case let .asset(asset):
return .asset(asset)
case let .draft(draft):
case let .draft(draft, _):
return .draft(draft)
}
}
@ -2095,7 +2128,7 @@ public final class MediaEditorScreen: ViewController {
return .video(videoPath, dimensions)
case let .asset(asset):
return .asset(asset)
case let .draft(draft):
case let .draft(draft, _):
return .image(draft.thumbnail, draft.dimensions)
}
}
@ -2117,7 +2150,7 @@ public final class MediaEditorScreen: ViewController {
fileprivate let transitionOut: (Bool) -> TransitionOut?
public var cancelled: (Bool) -> Void = { _ in }
public var completion: (MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _ in }
public var completion: (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void = { _, _, _, _ in }
public var dismissed: () -> Void = { }
public init(
@ -2125,7 +2158,7 @@ public final class MediaEditorScreen: ViewController {
subject: Signal<Subject?, NoError>,
transitionIn: TransitionIn?,
transitionOut: @escaping (Bool) -> TransitionOut?,
completion: @escaping (MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void
completion: @escaping (Int64, MediaEditorScreen.Result, MediaEditorResultPrivacy, @escaping (@escaping () -> Void) -> Void) -> Void
) {
self.context = context
self.subject = subject
@ -2156,9 +2189,9 @@ public final class MediaEditorScreen: ViewController {
super.displayNodeDidLoad()
}
func presentPrivacySettings() {
func openPrivacySettings() {
if case .message(_, _) = self.state.privacy {
self.presentSendAsMessage()
self.openSendAsMessage()
} else {
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .stories)
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
@ -2167,7 +2200,7 @@ public final class MediaEditorScreen: ViewController {
}
var archive = true
var timeout: Int32 = 86400
var timeout: Int = 86400
let initialPrivacy: EngineStoryPrivacy
if case let .story(privacy, timeoutValue, archiveValue) = self.state.privacy {
initialPrivacy = privacy
@ -2192,19 +2225,19 @@ public final class MediaEditorScreen: ViewController {
guard let self else {
return
}
self.presentEditCategory(privacy: privacy, completion: { [weak self] privacy in
self.openEditCategory(privacy: privacy, completion: { [weak self] privacy in
guard let self else {
return
}
self.state.privacy = .story(privacy: privacy, timeout: timeout, archive: archive)
self.presentPrivacySettings()
self.openPrivacySettings()
})
},
secondaryAction: { [weak self] in
guard let self else {
return
}
self.presentSendAsMessage()
self.openSendAsMessage()
}
)
)
@ -2212,7 +2245,7 @@ public final class MediaEditorScreen: ViewController {
}
}
private func presentEditCategory(privacy: EngineStoryPrivacy, completion: @escaping (EngineStoryPrivacy) -> Void) {
private func openEditCategory(privacy: EngineStoryPrivacy, completion: @escaping (EngineStoryPrivacy) -> Void) {
let stateContext = ShareWithPeersScreen.StateContext(context: self.context, subject: .contacts(privacy.base))
let _ = (stateContext.ready |> filter { $0 } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] _ in
guard let self else {
@ -2238,14 +2271,14 @@ public final class MediaEditorScreen: ViewController {
guard let self else {
return
}
self.presentSendAsMessage()
self.openSendAsMessage()
}
)
)
})
}
private func presentSendAsMessage() {
private func openSendAsMessage() {
var initialPeerIds = Set<EnginePeer.Id>()
if case let .message(peers, _) = self.state.privacy {
initialPeerIds = Set(peers)
@ -2277,7 +2310,7 @@ public final class MediaEditorScreen: ViewController {
func presentTimeoutSetup(sourceView: UIView) {
var items: [ContextMenuItem] = []
let updateTimeout: (Int32?, Bool) -> Void = { [weak self] timeout, archive in
let updateTimeout: (Int?, Bool) -> Void = { [weak self] timeout, archive in
guard let self else {
return
}
@ -2289,49 +2322,54 @@ public final class MediaEditorScreen: ViewController {
}
}
var currentValue: Int?
var currentArchived = false
let emptyAction: ((ContextMenuActionItem.Action) -> Void)? = nil
let title: String
switch self.state.privacy {
case .story:
case let .story(_, timeoutValue, archivedValue):
title = "Choose how long the story will be kept."
case .message:
currentValue = timeoutValue
currentArchived = archivedValue
case let .message(_, timeoutValue):
title = "Choose how long the media will be kept after opening."
currentValue = timeoutValue
}
items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction)))
switch self.state.privacy {
case .story:
items.append(.action(ContextMenuActionItem(text: "6 Hours", icon: { _ in
return nil
items.append(.action(ContextMenuActionItem(text: "6 Hours", icon: { theme in
return currentValue == 3600 * 6 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in
a(.default)
updateTimeout(3600 * 6, false)
})))
items.append(.action(ContextMenuActionItem(text: "12 Hours", icon: { _ in
return nil
items.append(.action(ContextMenuActionItem(text: "12 Hours", icon: { theme in
return currentValue == 3600 * 12 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in
a(.default)
updateTimeout(3600 * 12, false)
})))
items.append(.action(ContextMenuActionItem(text: "24 Hours", icon: { _ in
return nil
items.append(.action(ContextMenuActionItem(text: "24 Hours", icon: { theme in
return currentValue == 86400 && !currentArchived ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in
a(.default)
updateTimeout(86400, false)
})))
items.append(.action(ContextMenuActionItem(text: "2 Days", icon: { _ in
return nil
items.append(.action(ContextMenuActionItem(text: "2 Days", icon: { theme in
return currentValue == 86400 * 2 ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in
a(.default)
updateTimeout(86400 * 2, false)
})))
items.append(.action(ContextMenuActionItem(text: "Forever", icon: { _ in
return nil
items.append(.action(ContextMenuActionItem(text: "Forever", icon: { theme in
return currentArchived ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, a in
a(.default)
@ -2428,11 +2466,11 @@ public final class MediaEditorScreen: ViewController {
self.dismissAllTooltips()
if saveDraft {
self.saveDraft()
self.saveDraft(id: nil)
} else {
if case let .draft(draft) = self.node.subject {
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
}
// if case let .draft(draft) = self.node.subject {
// removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
// }
}
if let mediaEditor = self.node.mediaEditor {
@ -2447,7 +2485,7 @@ public final class MediaEditorScreen: ViewController {
})
}
private func saveDraft() {
private func saveDraft(id: Int64?) {
guard let subject = self.node.subject, let values = self.node.mediaEditor?.values else {
return
}
@ -2459,22 +2497,63 @@ public final class MediaEditorScreen: ViewController {
return
}
let fittedSize = resultImage.size.aspectFitted(CGSize(width: 128.0, height: 128.0))
if case let .image(image, dimensions) = subject {
let saveImageDraft: (UIImage, PixelDimensions) -> Void = { image, dimensions in
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
let path = draftPath() + "\(Int64.random(in: .min ... .max)).jpg"
let path = draftPath() + "/\(Int64.random(in: .min ... .max)).jpg"
if let data = image.jpegData(compressionQuality: 0.87) {
try? data.write(to: URL(fileURLWithPath: path))
let draft = MediaEditorDraft(path: path, isVideo: false, thumbnail: thumbnailImage, dimensions: dimensions, values: values)
addStoryDraft(engine: self.context.engine, item: draft)
if let id {
saveStorySource(engine: self.context.engine, item: draft, id: id)
} else {
addStoryDraft(engine: self.context.engine, item: draft)
}
}
}
} else if case let .draft(draft) = subject {
}
let saveVideoDraft: (String, PixelDimensions) -> Void = { videoPath, dimensions in
if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
let draft = MediaEditorDraft(path: draft.path, isVideo: draft.isVideo, thumbnail: thumbnailImage, dimensions: draft.dimensions, values: values)
addStoryDraft(engine: self.context.engine, item: draft)
let path = draftPath() + "/\(Int64.random(in: .min ... .max)).mp4"
_ = thumbnailImage
_ = path
_ = videoPath
_ = dimensions
}
}
switch subject {
case let .image(image, dimensions):
saveImageDraft(image, dimensions)
case let .video(path, _, dimensions):
saveVideoDraft(path, dimensions)
case let .asset(asset):
if asset.mediaType == .video {
PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
let _ = avAsset
}
} else {
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { image, _ in
if let image {
saveImageDraft(image, PixelDimensions(image.size))
}
}
}
case let .draft(draft, _):
if draft.isVideo {
} else if let image = UIImage(contentsOfFile: draft.path) {
saveImageDraft(image, PixelDimensions(image.size))
}
// if let thumbnailImage = generateScaledImage(image: resultImage, size: fittedSize) {
// removeStoryDraft(engine: self.context.engine, path: draft.path, delete: false)
// let draft = MediaEditorDraft(path: draft.path, isVideo: draft.isVideo, thumbnail: thumbnailImage, dimensions: draft.dimensions, values: values)
// addStoryDraft(engine: self.context.engine, item: draft)
// }
}
})
}
}
@ -2498,6 +2577,13 @@ public final class MediaEditorScreen: ViewController {
let codableEntities = DrawingEntitiesView.encodeEntities(entities, entitiesView: self.node.entitiesView)
mediaEditor.setDrawingAndEntities(data: nil, image: mediaEditor.values.drawing, entities: codableEntities)
let randomId: Int64
if case let .draft(_, id) = subject, let id {
randomId = id
} else {
randomId = Int64.random(in: .min ... .max)
}
if mediaEditor.resultIsVideo {
let videoResult: Result.VideoResult
let duration: Double
@ -2527,7 +2613,7 @@ public final class MediaEditorScreen: ViewController {
} else {
duration = 5.0
}
case let .draft(draft):
case let .draft(draft, _):
if draft.isVideo {
videoResult = .videoFile(path: draft.path)
if let videoTrimRange = mediaEditor.values.videoTrimRange {
@ -2540,7 +2626,7 @@ public final class MediaEditorScreen: ViewController {
duration = 5.0
}
}
self.completion(.video(video: videoResult, coverImage: nil, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions, caption: caption), self.state.privacy, { [weak self] finished in
self.completion(randomId, .video(video: videoResult, coverImage: nil, values: mediaEditor.values, duration: duration, dimensions: mediaEditor.values.resultDimensions, caption: caption), self.state.privacy, { [weak self] finished in
self?.node.animateOut(finished: true, completion: { [weak self] in
self?.dismiss()
Queue.mainQueue().justDispatch {
@ -2549,14 +2635,16 @@ public final class MediaEditorScreen: ViewController {
})
})
if case let .draft(draft) = subject {
if case let .draft(draft, id) = subject, id == nil {
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
}
} else {
if let image = mediaEditor.resultImage {
makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { resultImage in
if let resultImage {
self.completion(.image(image: resultImage, dimensions: PixelDimensions(resultImage.size), caption: caption), self.state.privacy, { [weak self] finished in
self.saveDraft(id: randomId)
makeEditorImageComposition(account: self.context.account, inputImage: image, dimensions: storyDimensions, values: mediaEditor.values, time: .zero, completion: { [weak self] resultImage in
if let self, let resultImage {
self.completion(randomId, .image(image: resultImage, dimensions: PixelDimensions(resultImage.size), caption: caption), self.state.privacy, { [weak self] finished in
self?.node.animateOut(finished: true, completion: { [weak self] in
self?.dismiss()
Queue.mainQueue().justDispatch {
@ -2564,7 +2652,7 @@ public final class MediaEditorScreen: ViewController {
}
})
})
if case let .draft(draft) = subject {
if case let .draft(draft, id) = subject, id == nil {
removeStoryDraft(engine: self.context.engine, path: draft.path, delete: true)
}
}
@ -2644,7 +2732,7 @@ public final class MediaEditorScreen: ViewController {
}
return EmptyDisposable
}
case let .draft(draft):
case let .draft(draft, _):
if draft.isVideo {
let asset = AVURLAsset(url: NSURL(fileURLWithPath: draft.path) as URL)
exportSubject = .single(.video(asset))
@ -2728,6 +2816,10 @@ public final class MediaEditorScreen: ViewController {
}
}
public func updateEditProgress(_ progress: Float) {
self.node.updateEditProgress(progress)
}
private func dismissAllTooltips() {
self.window?.forEachController({ controller in
if let controller = controller as? TooltipScreen {

View File

@ -894,7 +894,8 @@ public final class MediaToolsScreen: ViewController {
self.validLayout = layout
let previewSize = CGSize(width: layout.size.width, height: floorToScreenPixels(layout.size.width * 1.77778))
let topInset: CGFloat = floorToScreenPixels(layout.size.height - previewSize.height) / 2.0
let topInset: CGFloat = (layout.statusBarHeight ?? 0.0) + 12.0
let bottomInset = layout.size.height - previewSize.height - topInset
let environment = ViewControllerComponentContainer.Environment(
statusBarHeight: layout.statusBarHeight ?? 0.0,
@ -902,7 +903,7 @@ public final class MediaToolsScreen: ViewController {
safeInsets: UIEdgeInsets(
top: topInset,
left: layout.safeInsets.left,
bottom: topInset,
bottom: bottomInset,
right: layout.safeInsets.right
),
inputHeight: layout.inputHeight ?? 0.0,

View File

@ -0,0 +1,48 @@
import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramUIPreferences
import MediaEditor
public func saveStorySource(engine: TelegramEngine, item: MediaEditorDraft, id: Int64) {
let key = EngineDataBuffer(length: 8)
key.setInt64(0, value: id)
let _ = engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key, item: item).start()
}
public func removeStorySource(engine: TelegramEngine, id: Int64) {
let key = EngineDataBuffer(length: 8)
key.setInt64(0, value: id)
let _ = engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key).start()
}
public func getStorySource(engine: TelegramEngine, id: Int64) -> Signal<MediaEditorDraft?, NoError> {
let key = EngineDataBuffer(length: 8)
key.setInt64(0, value: id)
return engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: key))
|> map { result -> MediaEditorDraft? in
return result?.get(MediaEditorDraft.self)
}
}
public func moveStorySource(engine: TelegramEngine, from fromId: Int64, to toId: Int64) {
let fromKey = EngineDataBuffer(length: 8)
fromKey.setInt64(0, value: fromId)
let toKey = EngineDataBuffer(length: 8)
toKey.setInt64(0, value: toId)
let _ = (engine.data.get(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: fromKey))
|> mapToSignal { item -> Signal<Never, NoError> in
if let item = item?.get(MediaEditorDraft.self) {
return engine.itemCache.put(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: toKey, item: item)
|> then(
engine.itemCache.remove(collectionId: ApplicationSpecificItemCacheCollectionId.storySource, id: fromKey)
)
} else {
return .complete()
}
}).start()
}

View File

@ -685,8 +685,14 @@ public final class MessageInputPanelComponent: Component {
context.draw(cgImage, in: CGRect(origin: .zero, size: size))
}
var offset: CGPoint = CGPoint(x: 0.0, y: -3.0 - UIScreenPixel)
if value == "" {
offset.x += UIScreenPixel
offset.y += 1.0 - UIScreenPixel
}
let valuePath = CGMutablePath()
valuePath.addRect(bounds.offsetBy(dx: 0.0, dy: -3.0 - UIScreenPixel))
valuePath.addRect(bounds.offsetBy(dx: offset.x, dy: offset.y))
let valueFramesetter = CTFramesetterCreateWithAttributedString(valueString as CFAttributedString)
let valyeFrame = CTFramesetterCreateFrame(valueFramesetter, CFRangeMake(0, valueString.length), valuePath, nil)
CTFrameDraw(valyeFrame, context)
@ -697,7 +703,7 @@ public final class MessageInputPanelComponent: Component {
let timeoutButtonSize = self.timeoutButton.update(
transition: transition,
component: AnyComponent(Button(
content: AnyComponent(Image(image: icon, tintColor: component.timeoutSelected ? UIColor(rgb: 0xf8d74a) : UIColor(white: 1.0, alpha: 0.5), size: CGSize(width: 20.0, height: 20.0))),
content: AnyComponent(Image(image: icon, tintColor: component.timeoutSelected ? UIColor(rgb: 0xf8d74a) : UIColor(white: 1.0, alpha: 1.0), size: CGSize(width: 20.0, height: 20.0))),
action: { [weak self] in
guard let self, let timeoutButtonView = self.timeoutButton.view else {
return

View File

@ -668,7 +668,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding {
var duration: Int32?
var isMin: Bool = false
if let file = selectedMedia as? TelegramMediaFile, !file.isAnimated {
duration = file.duration
duration = file.duration.flatMap(Int32.init)
isMin = layer.bounds.width < 80.0
}
layer.updateDuration(duration: duration, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0))

View File

@ -1107,7 +1107,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
var duration: Int32?
var isMin: Bool = false
if let file = selectedMedia as? TelegramMediaFile, !file.isAnimated {
duration = file.duration
duration = file.duration.flatMap(Int32.init)
isMin = layer.bounds.width < 80.0
}
layer.updateDuration(duration: duration, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0))

View File

@ -46,6 +46,7 @@ swift_library(
"//submodules/TelegramUI/Components/EntityKeyboard",
"//submodules/TelegramUI/Components/Stories/StoryFooterPanelComponent",
"//submodules/TelegramUI/Components/ShareWithPeersScreen",
"//submodules/TelegramUI/Components/MediaEditorScreen",
"//submodules/TelegramPresentationData",
"//submodules/ReactionSelectionNode",
"//submodules/ContextUI",
@ -53,6 +54,7 @@ swift_library(
"//submodules/ChatPresentationInterfaceState",
"//submodules/TelegramStringFormatting",
"//submodules/ShimmerEffect",
"//submodules/ImageCompression",
],
visibility = [
"//visibility:public",

View File

@ -166,6 +166,9 @@ private final class StoryContainerScreenComponent: Component {
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressGesture(_:)))
longPressRecognizer.delegate = self
self.addGestureRecognizer(longPressRecognizer)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
self.backgroundEffectView.addGestureRecognizer(tapGestureRecognizer)
}
required init?(coder: NSCoder) {
@ -330,6 +333,31 @@ private final class StoryContainerScreenComponent: Component {
}
}
@objc private func tapGesture(_ recognizer: UITapGestureRecognizer) {
guard let component = self.component, let environment = self.environment, let stateValue = component.content.stateValue, case .recognized = recognizer.state else {
return
}
let location = recognizer.location(in: recognizer.view)
if let currentItemView = self.visibleItemSetViews.first?.value {
if location.x < currentItemView.frame.minX {
if stateValue.previousSlice == nil {
} else {
self.beginHorizontalPan()
self.commitHorizontalPan(velocity: CGPoint(x: 100.0, y: 0.0))
}
} else if location.x > currentItemView.frame.maxX {
if stateValue.nextSlice == nil {
environment.controller()?.dismiss()
} else {
self.beginHorizontalPan()
self.commitHorizontalPan(velocity: CGPoint(x: -100.0, y: 0.0))
}
}
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in self.subviews.reversed() {
if !subview.isUserInteractionEnabled || subview.isHidden || subview.alpha == 0.0 {

View File

@ -17,6 +17,8 @@ import ContextUI
import TelegramCore
import Postbox
import AvatarNode
import MediaEditorScreen
import ImageCompression
public final class StoryItemSetContainerComponent: Component {
public final class ExternalState {
@ -214,6 +216,8 @@ public final class StoryItemSetContainerComponent: Component {
var displayViewList: Bool = false
var viewList: ViewList?
var isEditingStory: Bool = false
var itemLayout: ItemLayout?
var ignoreScrolling: Bool = false
@ -243,6 +247,9 @@ public final class StoryItemSetContainerComponent: Component {
self.contentContainerView = UIView()
self.contentContainerView.clipsToBounds = true
if #available(iOS 13.0, *) {
self.contentContainerView.layer.cornerCurve = .continuous
}
self.topContentGradientLayer = SimpleGradientLayer()
self.bottomContentGradientLayer = SimpleGradientLayer()
@ -543,7 +550,7 @@ public final class StoryItemSetContainerComponent: Component {
for (_, visibleItem) in self.visibleItems {
if let view = visibleItem.view.view {
if let view = view as? StoryContentItem.View {
view.setIsProgressPaused(self.inputPanelExternalState.isEditing || component.isProgressPaused || self.displayReactions || self.actionSheet != nil || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.displayViewList)
view.setIsProgressPaused(self.inputPanelExternalState.isEditing || component.isProgressPaused || self.displayReactions || self.actionSheet != nil || self.contextController != nil || self.sendMessageContext.audioRecorderValue != nil || self.sendMessageContext.videoRecorderValue != nil || self.displayViewList || self.isEditingStory)
}
}
}
@ -1033,6 +1040,17 @@ public final class StoryItemSetContainerComponent: Component {
self.openItemPrivacySettings()
})))
items.append(.action(ContextMenuActionItem(text: "Edit Story", icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
a(.default)
guard let self else {
return
}
self.openStoryEditing()
})))
items.append(.separator)
component.controller()?.forEachController { c in
@ -1203,7 +1221,7 @@ public final class StoryItemSetContainerComponent: Component {
transition.setPosition(view: self.contentContainerView, position: contentFrame.center)
transition.setBounds(view: self.contentContainerView, bounds: CGRect(origin: CGPoint(), size: contentFrame.size))
transition.setScale(view: self.contentContainerView, scale: contentVisualScale)
transition.setCornerRadius(layer: self.contentContainerView.layer, cornerRadius: 10.0 * (1.0 / contentVisualScale))
transition.setCornerRadius(layer: self.contentContainerView.layer, cornerRadius: 12.0 * (1.0 / contentVisualScale))
if self.closeButtonIconView.image == nil {
self.closeButtonIconView.image = UIImage(bundleImageName: "Media Gallery/Close")?.withRenderingMode(.alwaysTemplate)
@ -1704,6 +1722,101 @@ public final class StoryItemSetContainerComponent: Component {
private func openItemPrivacySettings() {
}
private func openStoryEditing() {
guard let context = self.component?.context, let id = self.component?.slice.item.storyItem.id else {
return
}
let _ = (getStorySource(engine: context.engine, id: Int64(id))
|> deliverOnMainQueue).start(next: { [weak self] source in
guard let self else {
return
}
self.isEditingStory = true
self.updateIsProgressPaused()
if let source {
var updateProgressImpl: ((Float) -> Void)?
let controller = MediaEditorScreen(
context: context,
subject: .single(.draft(source, Int64(id))),
transitionIn: nil,
transitionOut: { _ in return nil },
completion: { [weak self] _, mediaResult, privacy, commit in
switch mediaResult {
case let .image(image, dimensions, caption):
if let imageData = compressImageToJPEG(image, quality: 0.6), case let .story(storyPrivacy, _, _) = privacy {
let _ = (context.engine.messages.editStory(media: .image(dimensions: dimensions, data: imageData), id: id, text: caption?.string ?? "", entities: [], privacy: storyPrivacy)
|> deliverOnMainQueue).start(next: { [weak self] result in
switch result {
case let .progress(progress):
updateProgressImpl?(progress)
case .completed:
Queue.mainQueue().after(0.1) {
if let self {
self.isEditingStory = false
self.rewindCurrentItem()
self.updateIsProgressPaused()
}
commit({})
}
}
})
}
default:
break
// case let .video(content, _, values, duration, dimensions, caption):
// let adjustments: VideoMediaResourceAdjustments
// if let valuesData = try? JSONEncoder().encode(values) {
// let data = MemoryBuffer(data: valuesData)
// let digest = MemoryBuffer(data: data.md5Digest())
// adjustments = VideoMediaResourceAdjustments(data: data, digest: digest, isStory: true)
//
// let resource: TelegramMediaResource
// switch content {
// case let .imageFile(path):
// resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments)
// case let .videoFile(path):
// resource = LocalFileVideoMediaResource(randomId: Int64.random(in: .min ... .max), path: path, adjustments: adjustments)
// case let .asset(localIdentifier):
// resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments))
// }
// if case let .story(storyPrivacy, period, pin) = privacy {
// let _ = (context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy, period: period, randomId: randomId)
// |> deliverOnMainQueue).start(next: { [weak chatListController] result in
// if let chatListController {
// switch result {
// case let .progress(progress):
// let _ = progress
// break
// case .completed:
// Queue.mainQueue().after(0.1) {
// commit()
// }
// }
// }
// })
// Queue.mainQueue().justDispatch {
// commit({ [weak chatListController] in
// chatListController?.animateStoryUploadRipple()
// })
// }
// }
// }
}
}
)
controller.dismissed = { [weak self] in
self?.isEditingStory = false
self?.updateIsProgressPaused()
}
self.component?.controller()?.push(controller)
updateProgressImpl = { [weak controller] progress in
controller?.updateEditProgress(progress)
}
}
})
}
}
public func makeView() -> View {

View File

@ -138,6 +138,7 @@ public final class StoryContentContextImpl: StoryContentContext {
let mappedItem = EngineStoryItem(
id: item.id,
timestamp: item.timestamp,
expirationTimestamp: item.expirationTimestamp,
media: EngineMedia(media),
text: item.text,
entities: item.entities,
@ -161,6 +162,7 @@ public final class StoryContentContextImpl: StoryContentContext {
nextItems.append(EngineStoryItem(
id: item.id,
timestamp: item.timestamp,
expirationTimestamp: item.expirationTimestamp,
media: EngineMedia(media),
text: item.text,
entities: item.entities,
@ -743,6 +745,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
let mappedItem = EngineStoryItem(
id: itemValue.id,
timestamp: itemValue.timestamp,
expirationTimestamp: itemValue.expirationTimestamp,
media: EngineMedia(media),
text: itemValue.text,
entities: itemValue.entities,

View File

@ -2576,6 +2576,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let author = message?.author, author.isVerified {
skipConcealedAlert = true
}
if let message, let adAttribute = message.attributes.first(where: { $0 is AdMessageAttribute }) as? AdMessageAttribute {
strongSelf.chatDisplayNode.historyNode.adMessagesContext?.markAction(opaqueId: adAttribute.opaqueId)
}
strongSelf.openUrl(url, concealed: concealed, skipConcealedAlert: skipConcealedAlert, message: message)
}
}, shareCurrentLocation: { [weak self] in
@ -4351,7 +4356,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
navigationData = .chat(textInputState: nil, subject: subject, peekData: nil)
}
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: id))
|> deliverOnMainQueue).start(next: { [weak self] peer in
|> deliverOnMainQueue).start(next: { [weak self] peer in
if let self, let peer = peer {
self.openPeer(peer: peer, navigation: navigationData, fromMessage: nil)
}
@ -4359,6 +4364,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
case let .join(_, joinHash):
self.controllerInteraction?.openJoinLink(joinHash)
case let .webPage(_, url):
self.controllerInteraction?.openUrl(url, false, false, nil)
}
}, openRequestedPeerSelection: { [weak self] messageId, peerType, buttonId in
guard let self else {

View File

@ -24,6 +24,7 @@ private let buttonFont = Font.semibold(13.0)
enum ChatMessageAttachedContentActionIcon {
case instant
case link
}
struct ChatMessageAttachedContentNodeMediaFlags: OptionSet {
@ -135,7 +136,7 @@ final class ChatMessageAttachedContentButtonNode: HighlightTrackingButtonNode {
})
}
static func asyncLayout(_ current: ChatMessageAttachedContentButtonNode?) -> (_ width: CGFloat, _ regularImage: UIImage, _ highlightedImage: UIImage, _ iconImage: UIImage?, _ highlightedIconImage: UIImage?, _ title: String, _ titleColor: UIColor, _ highlightedTitleColor: UIColor, _ inProgress: Bool) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode)) {
static func asyncLayout(_ current: ChatMessageAttachedContentButtonNode?) -> (_ width: CGFloat, _ regularImage: UIImage, _ highlightedImage: UIImage, _ iconImage: UIImage?, _ highlightedIconImage: UIImage?, _ cornerIcon: Bool, _ title: String, _ titleColor: UIColor, _ highlightedTitleColor: UIColor, _ inProgress: Bool) -> (CGFloat, (CGFloat) -> (CGSize, () -> ChatMessageAttachedContentButtonNode)) {
let previousRegularImage = current?.regularImage
let previousHighlightedImage = current?.highlightedImage
let previousRegularIconImage = current?.regularIconImage
@ -144,7 +145,7 @@ final class ChatMessageAttachedContentButtonNode: HighlightTrackingButtonNode {
let maybeMakeTextLayout = (current?.textNode).flatMap(TextNode.asyncLayout)
let maybeMakeHighlightedTextLayout = (current?.highlightedTextNode).flatMap(TextNode.asyncLayout)
return { width, regularImage, highlightedImage, iconImage, highlightedIconImage, title, titleColor, highlightedTitleColor, inProgress in
return { width, regularImage, highlightedImage, iconImage, highlightedIconImage, cornerIcon, title, titleColor, highlightedTitleColor, inProgress in
let targetNode: ChatMessageAttachedContentButtonNode
if let current = current {
targetNode = current
@ -235,8 +236,12 @@ final class ChatMessageAttachedContentButtonNode: HighlightTrackingButtonNode {
var textFrame = CGRect(origin: CGPoint(x: floor((refinedWidth - textSize.size.width) / 2.0), y: floor((34.0 - textSize.size.height) / 2.0)), size: textSize.size)
targetNode.backgroundNode.frame = backgroundFrame
if let image = targetNode.iconNode.image {
textFrame.origin.x += floor(image.size.width / 2.0)
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: textFrame.minX - image.size.width - 5.0, y: textFrame.minY + 2.0), size: image.size)
if cornerIcon {
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 5.0, y: 5.0), size: image.size)
} else {
textFrame.origin.x += floor(image.size.width / 2.0)
targetNode.iconNode.frame = CGRect(origin: CGPoint(x: textFrame.minX - image.size.width - 5.0, y: textFrame.minY + 2.0), size: image.size)
}
if targetNode.iconNode.supernode == nil {
targetNode.addSubnode(targetNode.iconNode)
}
@ -791,14 +796,22 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let buttonHighlightedImage: UIImage
var buttonIconImage: UIImage?
var buttonHighlightedIconImage: UIImage?
var cornerIcon = false
let titleColor: UIColor
let titleHighlightedColor: UIColor
if incoming {
buttonImage = PresentationResourcesChat.chatMessageAttachedContentButtonIncoming(presentationData.theme.theme)!
buttonHighlightedImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIncoming(presentationData.theme.theme)!
if let actionIcon = actionIcon, case .instant = actionIcon {
buttonIconImage = PresentationResourcesChat.chatMessageAttachedContentButtonIconInstantIncoming(presentationData.theme.theme)!
buttonHighlightedIconImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIconInstantIncoming(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)!
if let actionIcon {
switch actionIcon {
case .instant:
buttonIconImage = PresentationResourcesChat.chatMessageAttachedContentButtonIconInstantIncoming(presentationData.theme.theme)!
buttonHighlightedIconImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIconInstantIncoming(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)!
case .link:
buttonIconImage = PresentationResourcesChat.chatMessageAttachedContentButtonIconLinkIncoming(presentationData.theme.theme)!
buttonHighlightedIconImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIconLinkIncoming(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)!
cornerIcon = true
}
}
titleColor = presentationData.theme.theme.chat.message.incoming.accentTextColor
let bubbleColor = bubbleColorComponents(theme: presentationData.theme.theme, incoming: true, wallpaper: !presentationData.theme.wallpaper.isEmpty)
@ -806,15 +819,22 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
} else {
buttonImage = PresentationResourcesChat.chatMessageAttachedContentButtonOutgoing(presentationData.theme.theme)!
buttonHighlightedImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonOutgoing(presentationData.theme.theme)!
if let actionIcon = actionIcon, case .instant = actionIcon {
buttonIconImage = PresentationResourcesChat.chatMessageAttachedContentButtonIconInstantOutgoing(presentationData.theme.theme)!
buttonHighlightedIconImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIconInstantOutgoing(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)!
if let actionIcon {
switch actionIcon {
case .instant:
buttonIconImage = PresentationResourcesChat.chatMessageAttachedContentButtonIconInstantOutgoing(presentationData.theme.theme)!
buttonHighlightedIconImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIconInstantOutgoing(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)!
case .link:
buttonIconImage = PresentationResourcesChat.chatMessageAttachedContentButtonIconLinkOutgoing(presentationData.theme.theme)!
buttonHighlightedIconImage = PresentationResourcesChat.chatMessageAttachedContentHighlightedButtonIconLinkOutgoing(presentationData.theme.theme, wallpaper: !presentationData.theme.wallpaper.isEmpty)!
cornerIcon = true
}
}
titleColor = presentationData.theme.theme.chat.message.outgoing.accentTextColor
let bubbleColor = bubbleColorComponents(theme: presentationData.theme.theme, incoming: false, wallpaper: !presentationData.theme.wallpaper.isEmpty)
titleHighlightedColor = bubbleColor.fill[0]
}
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, buttonIconImage, buttonHighlightedIconImage, actionTitle, titleColor, titleHighlightedColor, false)
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, buttonIconImage, buttonHighlightedIconImage, cornerIcon, actionTitle, titleColor, titleHighlightedColor, false)
boundingSize.width = max(buttonWidth, boundingSize.width)
continueActionButtonLayout = continueLayout
}

View File

@ -265,7 +265,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
avatarPlaceholderColor = item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor
}
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, nil, nil, item.presentationData.strings.Conversation_ViewContactDetails, titleColor, titleHighlightedColor, false)
let (buttonWidth, continueLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, nil, nil, false, item.presentationData.strings.Conversation_ViewContactDetails, titleColor, titleHighlightedColor, false)
var maxContentWidth: CGFloat = avatarSize.width + 7.0
if let statusSuggestedWidthAndContinue = statusSuggestedWidthAndContinue {

View File

@ -1692,7 +1692,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
if let size = file.size, size > 0 && size != .max {
let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))"
if let duration = file.duration, !message.flags.contains(.Unsent) {
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : Int32(duration), position: playerPosition)
if isMediaStreamable(message: message, media: file) {
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active)
mediaDownloadState = .fetching(progress: automaticPlayback ? nil : adjustedProgress)
@ -1727,11 +1727,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
let sizeString = "\(dataSizeString(Int64(Float(fileSize) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(fileSize, forceDecimal: true, formatting: formatting))"
if message.flags.contains(.Unsent), let duration = file.duration {
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : Int32(duration), position: playerPosition)
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: nil, muted: false, active: false)
}
else if automaticPlayback && !message.flags.contains(.Unsent), let duration = file.duration {
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : Int32(duration), position: playerPosition)
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active)
mediaDownloadState = .fetching(progress: automaticPlayback ? nil : adjustedProgress)
@ -1749,7 +1749,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
} else {
if let duration = file.duration, !file.isAnimated {
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
let durationString = stringForDuration(playerDuration > 0 ? playerDuration : Int32(duration), position: playerPosition)
if automaticPlayback, let fileSize = file.size, fileSize > 0 && fileSize != .max {
let sizeString = "\(dataSizeString(Int64(Float(fileSize) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(fileSize, forceDecimal: true, formatting: formatting))"
@ -1793,14 +1793,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
}
}
if let file = media as? TelegramMediaFile, let duration = file.duration, !file.isVideoSticker {
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition)
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : Int32(duration), position: playerPosition)
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: nil, muted: muted, active: false)
}
case .Remote, .Paused:
state = .download(messageTheme.mediaOverlayControlColors.foregroundColor)
if let file = media as? TelegramMediaFile, !file.isVideoSticker {
do {
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition)
let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration.flatMap(Int32.init) ?? 0), position: playerPosition)
if wideLayout {
if isMediaStreamable(message: message, media: file), let fileSize = file.size, fileSize > 0 && fileSize != .max {
state = automaticPlayback ? .none : .play(messageTheme.mediaOverlayControlColors.foregroundColor)

View File

@ -56,7 +56,7 @@ final class ChatMessageUnsupportedBubbleContentNode: ChatMessageBubbleContentNod
let bubbleColor = bubbleColorComponents(theme: presentationData.theme.theme, incoming: false, wallpaper: !presentationData.theme.wallpaper.isEmpty)
titleHighlightedColor = bubbleColor.fill[0]
}
let (buttonWidth, continueActionButtonLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, nil, nil, presentationData.strings.Conversation_UpdateTelegram, titleColor, titleHighlightedColor, false)
let (buttonWidth, continueActionButtonLayout) = makeButtonLayout(constrainedSize.width, buttonImage, buttonHighlightedImage, nil, nil, false, presentationData.strings.Conversation_UpdateTelegram, titleColor, titleHighlightedColor, false)
let initialWidth = buttonWidth + insets.left + insets.right

View File

@ -354,7 +354,10 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
actionTitle = item.presentationData.strings.Conversation_ViewGroup
}
} else {
if case let .peer(_, messageId, _) = adAttribute.target, messageId != nil {
if case .webPage = adAttribute.target {
actionTitle = item.presentationData.strings.Conversation_OpenLink
actionIcon = .link
} else if case let .peer(_, messageId, _) = adAttribute.target, messageId != nil {
actionTitle = item.presentationData.strings.Conversation_ViewMessage
} else {
actionTitle = item.presentationData.strings.Conversation_ViewChannel

View File

@ -2272,7 +2272,7 @@ private class MessageContentNode: ASDisplayNode, ContentNode {
mediaSize = dimensions.aspectFitted(mediaFitSize)
mediaFrame = CGRect(origin: CGPoint(x: 3.0, y: 63.0), size: mediaSize)
mediaDuration = video.duration ?? 0
mediaDuration = video.duration.flatMap(Int32.init) ?? 0
if !wasInitialized {
if self.isStatic {

View File

@ -273,18 +273,6 @@ public func fetchVideoLibraryMediaResource(account: Account, resource: VideoLibr
TempBox.shared.dispose(tempFile)
subscriber.putNext(.moveTempFile(file: remuxedTempFile))
} else {
let tempVideoPath = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max)).mp4"
if let _ = try? FileManager.default.copyItem(atPath: tempFile.path, toPath: tempVideoPath) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: tempVideoPath))
}, completionHandler: { _, error in
if let error = error {
print("\(error)")
}
let _ = try? FileManager.default.removeItem(atPath: tempVideoPath)
})
}
TempBox.shared.dispose(remuxedTempFile)
if let data = try? Data(contentsOf: URL(fileURLWithPath: tempFile.path), options: [.mappedRead]) {
var range: Range<Int64>?

View File

@ -272,7 +272,7 @@ final class GridMessageItemNode: GridItemNode {
})
if let duration = file.duration {
let durationString = stringForDuration(duration)
let durationString = stringForDuration(Int32(duration))
var badgeContent: ChatMessageInteractiveMediaBadgeContent?
var mediaDownloadState: ChatMessageInteractiveMediaDownloadState?

View File

@ -374,7 +374,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
})
if let duration = file.duration {
let durationString = stringForDuration(duration)
let durationString = stringForDuration(Int32(duration))
var badgeContent: ChatMessageInteractiveMediaBadgeContent?
var mediaDownloadState: ChatMessageInteractiveMediaDownloadState?

View File

@ -297,7 +297,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
case let .asset(asset):
return .asset(asset)
case let .draft(draft):
return .draft(draft)
return .draft(draft, nil)
}
}
@ -334,7 +334,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
} else {
return nil
}
}, completion: { [weak self] mediaResult, privacy, commit in
}, completion: { [weak self] randomId, mediaResult, privacy, commit in
guard let self else {
dismissCameraImpl?()
commit({})
@ -347,15 +347,18 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
case let .image(image, dimensions, caption):
if let imageData = compressImageToJPEG(image, quality: 0.6) {
switch privacy {
case let .story(storyPrivacy, pin):
case let .story(storyPrivacy, period, pin):
chatListController.updateStoryUploadProgress(0.0)
let _ = (self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy)
let _ = (self.context.engine.messages.uploadStory(media: .image(dimensions: dimensions, data: imageData), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy, period: period, randomId: randomId)
|> deliverOnMainQueue).start(next: { [weak chatListController] result in
if let chatListController {
switch result {
case let .progress(progress):
chatListController.updateStoryUploadProgress(progress)
case .completed:
case let .completed(id):
if let id {
moveStorySource(engine: context.engine, from: randomId, to: Int64(id))
}
Queue.mainQueue().after(0.2) {
chatListController.updateStoryUploadProgress(nil)
}
@ -363,9 +366,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
}
})
Queue.mainQueue().justDispatch {
commit({ [weak chatListController] in
chatListController?.animateStoryUploadRipple()
})
commit({})
}
case let .message(peerIds, timeout):
var randomId: Int64 = 0
@ -382,7 +383,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: imageFlags)
if let timeout, timeout > 0 && timeout <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: timeout, countdownBeginTime: nil))
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timeout), countdownBeginTime: nil))
}
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
@ -435,15 +436,18 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
case let .asset(localIdentifier):
resource = VideoLibraryMediaResource(localIdentifier: localIdentifier, conversion: .compress(adjustments))
}
if case let .story(storyPrivacy, pin) = privacy {
if case let .story(storyPrivacy, period, pin) = privacy {
chatListController.updateStoryUploadProgress(0.0)
let _ = (self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: Int(duration), resource: resource), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy)
let _ = (self.context.engine.messages.uploadStory(media: .video(dimensions: dimensions, duration: duration, resource: resource), text: caption?.string ?? "", entities: [], pin: pin, privacy: storyPrivacy, period: period, randomId: randomId)
|> deliverOnMainQueue).start(next: { [weak chatListController] result in
if let chatListController {
switch result {
case let .progress(progress):
chatListController.updateStoryUploadProgress(progress)
case .completed:
case let .completed(id):
if let id {
moveStorySource(engine: context.engine, from: randomId, to: Int64(id))
}
Queue.mainQueue().after(0.2) {
chatListController.updateStoryUploadProgress(nil)
}
@ -451,9 +455,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
}
})
Queue.mainQueue().justDispatch {
commit({ [weak chatListController] in
chatListController?.animateStoryUploadRipple()
})
commit({})
}
} else {
commit({})

View File

@ -32,7 +32,7 @@ public final class NativeVideoContent: UniversalVideoContent {
public let fileReference: FileMediaReference
let imageReference: ImageMediaReference?
public let dimensions: CGSize
public let duration: Int32
public let duration: Double
public let streamVideo: MediaPlayerStreaming
public let loopVideo: Bool
public let enableSound: Bool
@ -70,7 +70,7 @@ public final class NativeVideoContent: UniversalVideoContent {
self.dimensions = CGSize(width: 128.0, height: 128.0)
}
self.duration = fileReference.media.duration ?? 0
self.duration = fileReference.media.duration ?? 0.0
self.streamVideo = streamVideo
self.loopVideo = loopVideo
self.enableSound = enableSound

View File

@ -50,7 +50,7 @@ public final class PlatformVideoContent: UniversalVideoContent {
case file(FileMediaReference)
case url(String)
var duration: Int32? {
var duration: Double? {
switch self {
case let .file(file):
return file.media.duration
@ -74,7 +74,7 @@ public final class PlatformVideoContent: UniversalVideoContent {
let userLocation: MediaResourceUserLocation
let content: Content
public let dimensions: CGSize
public let duration: Int32
public let duration: Double
let streamVideo: Bool
let loopVideo: Bool
let enableSound: Bool
@ -87,7 +87,7 @@ public final class PlatformVideoContent: UniversalVideoContent {
self.nativeId = id
self.content = content
self.dimensions = self.content.dimensions?.cgSize ?? CGSize(width: 480, height: 320)
self.duration = self.content.duration ?? 0
self.duration = self.content.duration ?? 0.0
self.streamVideo = streamVideo
self.loopVideo = loopVideo
self.enableSound = enableSound

View File

@ -18,9 +18,9 @@ public final class SystemVideoContent: UniversalVideoContent {
let url: String
let imageReference: ImageMediaReference
public let dimensions: CGSize
public let duration: Int32
public let duration: Double
public init(userLocation: MediaResourceUserLocation, url: String, imageReference: ImageMediaReference, dimensions: CGSize, duration: Int32) {
public init(userLocation: MediaResourceUserLocation, url: String, imageReference: ImageMediaReference, dimensions: CGSize, duration: Double) {
self.id = AnyHashable(url)
self.url = url
self.userLocation = userLocation
@ -37,7 +37,7 @@ public final class SystemVideoContent: UniversalVideoContent {
private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
private let url: String
private let intrinsicDimensions: CGSize
private let approximateDuration: Int32
private let approximateDuration: Double
private let audioSessionManager: ManagedAudioSession
private let audioSessionDisposable = MetaDisposable()
@ -83,7 +83,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
private var seekId: Int = 0
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, userLocation: MediaResourceUserLocation, url: String, imageReference: ImageMediaReference, intrinsicDimensions: CGSize, approximateDuration: Int32) {
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, userLocation: MediaResourceUserLocation, url: String, imageReference: ImageMediaReference, intrinsicDimensions: CGSize, approximateDuration: Double) {
self.audioSessionManager = audioSessionManager
self.url = url
@ -164,7 +164,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
if let currentItem = self.player.currentItem {
duration = CMTimeGetSeconds(currentItem.duration)
} else {
duration = Double(self.approximateDuration)
duration = self.approximateDuration
}
if keyPath == "rate" {
@ -221,7 +221,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
func play() {
assert(Queue.mainQueue().isCurrent())
if !self.initializedStatus {
self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .buffering(initial: true, whilePlaying: true, progress: 0.0, display: true), soundEnabled: true))
self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: self.approximateDuration, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .buffering(initial: true, whilePlaying: true, progress: 0.0, display: true), soundEnabled: true))
}
if !self.hasAudioSession {
self.audioSessionDisposable.set(self.audioSessionManager.push(audioSessionType: .play, activate: { [weak self] _ in
@ -240,7 +240,7 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
func pause() {
assert(Queue.mainQueue().isCurrent())
if !self.initializedStatus {
self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: Double(self.approximateDuration), dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .paused, soundEnabled: true))
self._status.set(MediaPlayerStatus(generationTimestamp: 0.0, duration: self.approximateDuration, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: self.seekId, status: .paused, soundEnabled: true))
}
self.player.pause()
}

View File

@ -18,7 +18,7 @@ public final class WebEmbedVideoContent: UniversalVideoContent {
let webPage: TelegramMediaWebpage
public let webpageContent: TelegramMediaWebpageLoadedContent
public let dimensions: CGSize
public let duration: Int32
public let duration: Double
let forcedTimestamp: Int?
let openUrl: (URL) -> Void
@ -31,7 +31,7 @@ public final class WebEmbedVideoContent: UniversalVideoContent {
self.webPage = webPage
self.webpageContent = webpageContent
self.dimensions = webpageContent.embedSize?.cgSize ?? CGSize(width: 128.0, height: 128.0)
self.duration = Int32(webpageContent.duration ?? (0 as Int))
self.duration = webpageContent.duration.flatMap(Double.init) ?? 0.0
self.forcedTimestamp = forcedTimestamp
self.openUrl = openUrl
}

View File

@ -173,7 +173,7 @@ func makeBridgeMedia(message: Message, strings: PresentationStrings, chatPeer: P
for attribute in file.attributes {
switch attribute {
case let .Video(duration, size, flags, _):
bridgeVideo.duration = Int32(clamping: duration)
bridgeVideo.duration = Int32(duration)
bridgeVideo.dimensions = size.cgSize
bridgeVideo.round = flags.contains(.instantRoundVideo)
default: