mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
d551ad9205
@ -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";
|
||||
|
@ -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
|
||||
|
||||
|
@ -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?
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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")]
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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?
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -5,7 +5,7 @@ public extension TelegramMediaWebFile {
|
||||
return dimensionsForFileAttributes(self.attributes)
|
||||
}
|
||||
|
||||
var duration: Int32? {
|
||||
var duration: Double? {
|
||||
return durationForFileAttributes(self.attributes)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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")
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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>?
|
||||
|
@ -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?
|
||||
|
@ -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?
|
||||
|
@ -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({})
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user