Merge branch 'formatted-date-api'

This commit is contained in:
Ilya Laktyushin 2026-02-06 14:25:09 +04:00
commit 96bb624b2d
34 changed files with 501 additions and 321 deletions

View file

@ -117,7 +117,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Login_Announce_Notify, icon: { theme in
if !announceSignUp {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, iconPosition: .left, action: { [weak self] _, a in
@ -128,7 +128,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Login_Announce_DontNotify, icon: { theme in
if announceSignUp {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, iconPosition: .left, action: { [weak self] _, a in

View file

@ -3742,7 +3742,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsTopics, icon: { theme in
if !isViewingAsTopics {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, a in
@ -3778,7 +3778,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
})))
items.append(.action(ContextMenuActionItem(text: strings.Chat_ContextViewAsMessages, icon: { theme in
if isViewingAsTopics {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak sourceController] _, a in

View file

@ -5587,7 +5587,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_AllChats, icon: { theme in
return scope == .everywhere ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return scope == .everywhere ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { [weak self] _, f in
guard let self else {
return
@ -5596,7 +5596,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
self.searchScopePromise.set(.everywhere)
})))
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_PrivateChats, icon: { theme in
return scope == .privateChats ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return scope == .privateChats ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { [weak self] _, f in
guard let self else {
return
@ -5605,7 +5605,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
self.searchScopePromise.set(.privateChats)
})))
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_GroupChats, icon: { theme in
return scope == .groups ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return scope == .groups ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { [weak self] _, f in
guard let self else {
return
@ -5614,7 +5614,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
self.searchScopePromise.set(.groups)
})))
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_Search_Messages_Menu_Channels, icon: { theme in
return scope == .channels ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return scope == .channels ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { [weak self] _, f in
guard let self else {
return

View file

@ -739,11 +739,7 @@
bool processLive = false;
if (asset.subtypes & TGMediaAssetSubtypePhotoLive) {
if (iosMajorVersion() < 9 || (iosMajorVersion() == 9 && iosMinorVersion() < 1)) {
processLive = false;
} else {
processLive = true;
}
processLive = true;
}
if (processLive)

View file

@ -369,32 +369,7 @@
count = MIN(count, _itemsLimit);
for (NSUInteger i = 0; i < count; i++)
{
// TGMediaAsset *asset = [fetchResult assetAtIndex:i];
//
// TGMediaPickerGalleryItem<TGModernGallerySelectableItem, TGModernGalleryEditableItem> *galleryItem = nil;
// switch (asset.type)
// {
// case TGMediaAssetVideoType:
// {
// galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:(id<TGMediaEditableItem,TGMediaSelectableItem>)asset];
// }
// break;
//
// case TGMediaAssetGifType:
// {
// TGCameraCapturedVideo *convertedAsset = [[TGCameraCapturedVideo alloc] initWithAsset:asset livePhoto:false];
// galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:convertedAsset];
// }
// break;
//
// default:
// {
// galleryItem = [[TGMediaPickerGalleryPhotoItem alloc] initWithAsset:(id<TGMediaEditableItem,TGMediaSelectableItem>)asset];
// }
// break;
// }
{
TGMediaPickerGalleryFetchResultItem *galleryItem = [[TGMediaPickerGalleryFetchResultItem alloc] initWithFetchResult:fetchResult index:i];
galleryItem.selectionContext = selectionContext;
galleryItem.editingContext = editingContext;

View file

@ -401,7 +401,7 @@ public func storageUsageExceptionsScreen(
if currentValue == value {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}, action: { _, f in
applyValue(value)

View file

@ -649,6 +649,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[681706865] = { return Api.MessageEntity.parse_messageEntityCode($0) }
dict[-925956616] = { return Api.MessageEntity.parse_messageEntityCustomEmoji($0) }
dict[1692693954] = { return Api.MessageEntity.parse_messageEntityEmail($0) }
dict[-1874147385] = { return Api.MessageEntity.parse_messageEntityFormattedDate($0) }
dict[1868782349] = { return Api.MessageEntity.parse_messageEntityHashtag($0) }
dict[-2106619040] = { return Api.MessageEntity.parse_messageEntityItalic($0) }
dict[-100378723] = { return Api.MessageEntity.parse_messageEntityMention($0) }
@ -1172,7 +1173,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1094555409] = { return Api.Update.parse_updateNotifySettings($0) }
dict[-1955438642] = { return Api.Update.parse_updatePaidReactionPrivacy($0) }
dict[-337610926] = { return Api.Update.parse_updatePeerBlocked($0) }
dict[1115654513] = { return Api.Update.parse_updatePeerHistoryNoForward($0) }
dict[1463202714] = { return Api.Update.parse_updatePeerHistoryNoForwards($0) }
dict[-1147422299] = { return Api.Update.parse_updatePeerHistoryTTL($0) }
dict[-1263546448] = { return Api.Update.parse_updatePeerLocated($0) }
dict[1786671974] = { return Api.Update.parse_updatePeerSettings($0) }

View file

@ -78,6 +78,18 @@ public extension Api {
self.length = length
}
}
public class Cons_messageEntityFormattedDate {
public var flags: Int32
public var offset: Int32
public var length: Int32
public var date: Int32
public init(flags: Int32, offset: Int32, length: Int32, date: Int32) {
self.flags = flags
self.offset = offset
self.length = length
self.date = date
}
}
public class Cons_messageEntityHashtag {
public var offset: Int32
public var length: Int32
@ -189,6 +201,7 @@ public extension Api {
case messageEntityCode(Cons_messageEntityCode)
case messageEntityCustomEmoji(Cons_messageEntityCustomEmoji)
case messageEntityEmail(Cons_messageEntityEmail)
case messageEntityFormattedDate(Cons_messageEntityFormattedDate)
case messageEntityHashtag(Cons_messageEntityHashtag)
case messageEntityItalic(Cons_messageEntityItalic)
case messageEntityMention(Cons_messageEntityMention)
@ -270,6 +283,15 @@ public extension Api {
serializeInt32(_data.offset, buffer: buffer, boxed: false)
serializeInt32(_data.length, buffer: buffer, boxed: false)
break
case .messageEntityFormattedDate(let _data):
if boxed {
buffer.appendInt32(-1874147385)
}
serializeInt32(_data.flags, buffer: buffer, boxed: false)
serializeInt32(_data.offset, buffer: buffer, boxed: false)
serializeInt32(_data.length, buffer: buffer, boxed: false)
serializeInt32(_data.date, buffer: buffer, boxed: false)
break
case .messageEntityHashtag(let _data):
if boxed {
buffer.appendInt32(1868782349)
@ -380,6 +402,8 @@ public extension Api {
return ("messageEntityCustomEmoji", [("offset", _data.offset as Any), ("length", _data.length as Any), ("documentId", _data.documentId as Any)])
case .messageEntityEmail(let _data):
return ("messageEntityEmail", [("offset", _data.offset as Any), ("length", _data.length as Any)])
case .messageEntityFormattedDate(let _data):
return ("messageEntityFormattedDate", [("flags", _data.flags as Any), ("offset", _data.offset as Any), ("length", _data.length as Any), ("date", _data.date as Any)])
case .messageEntityHashtag(let _data):
return ("messageEntityHashtag", [("offset", _data.offset as Any), ("length", _data.length as Any)])
case .messageEntityItalic(let _data):
@ -544,6 +568,26 @@ public extension Api {
return nil
}
}
public static func parse_messageEntityFormattedDate(_ reader: BufferReader) -> MessageEntity? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.MessageEntity.messageEntityFormattedDate(Cons_messageEntityFormattedDate(flags: _1!, offset: _2!, length: _3!, date: _4!))
}
else {
return nil
}
}
public static func parse_messageEntityHashtag(_ reader: BufferReader) -> MessageEntity? {
var _1: Int32?
_1 = reader.readInt32()

View file

@ -969,7 +969,7 @@ public extension Api {
self.peerId = peerId
}
}
public class Cons_updatePeerHistoryNoForward {
public class Cons_updatePeerHistoryNoForwards {
public var flags: Int32
public var peer: Api.Peer
public init(flags: Int32, peer: Api.Peer) {
@ -1539,7 +1539,7 @@ public extension Api {
case updateNotifySettings(Cons_updateNotifySettings)
case updatePaidReactionPrivacy(Cons_updatePaidReactionPrivacy)
case updatePeerBlocked(Cons_updatePeerBlocked)
case updatePeerHistoryNoForward(Cons_updatePeerHistoryNoForward)
case updatePeerHistoryNoForwards(Cons_updatePeerHistoryNoForwards)
case updatePeerHistoryTTL(Cons_updatePeerHistoryTTL)
case updatePeerLocated(Cons_updatePeerLocated)
case updatePeerSettings(Cons_updatePeerSettings)
@ -2497,9 +2497,9 @@ public extension Api {
serializeInt32(_data.flags, buffer: buffer, boxed: false)
_data.peerId.serialize(buffer, true)
break
case .updatePeerHistoryNoForward(let _data):
case .updatePeerHistoryNoForwards(let _data):
if boxed {
buffer.appendInt32(1115654513)
buffer.appendInt32(1463202714)
}
serializeInt32(_data.flags, buffer: buffer, boxed: false)
_data.peer.serialize(buffer, true)
@ -3208,8 +3208,8 @@ public extension Api {
return ("updatePaidReactionPrivacy", [("`private`", _data.`private` as Any)])
case .updatePeerBlocked(let _data):
return ("updatePeerBlocked", [("flags", _data.flags as Any), ("peerId", _data.peerId as Any)])
case .updatePeerHistoryNoForward(let _data):
return ("updatePeerHistoryNoForward", [("flags", _data.flags as Any), ("peer", _data.peer as Any)])
case .updatePeerHistoryNoForwards(let _data):
return ("updatePeerHistoryNoForwards", [("flags", _data.flags as Any), ("peer", _data.peer as Any)])
case .updatePeerHistoryTTL(let _data):
return ("updatePeerHistoryTTL", [("flags", _data.flags as Any), ("peer", _data.peer as Any), ("ttlPeriod", _data.ttlPeriod as Any)])
case .updatePeerLocated(let _data):
@ -5176,7 +5176,7 @@ public extension Api {
return nil
}
}
public static func parse_updatePeerHistoryNoForward(_ reader: BufferReader) -> Update? {
public static func parse_updatePeerHistoryNoForwards(_ reader: BufferReader) -> Update? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.Peer?
@ -5186,7 +5186,7 @@ public extension Api {
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.Update.updatePeerHistoryNoForward(Cons_updatePeerHistoryNoForward(flags: _1!, peer: _2!))
return Api.Update.updatePeerHistoryNoForwards(Cons_updatePeerHistoryNoForwards(flags: _1!, peer: _2!))
}
else {
return nil

View file

@ -571,7 +571,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
if isSelected && value == nil {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}), action: { [weak self] _, f in
scheduleTooltip(nil)

View file

@ -398,7 +398,7 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
case let .messageMediaDocument(messageMediaDocumentData):
let (flags, document, altDocuments, coverPhoto, videoTimestamp, ttlSeconds) = (messageMediaDocumentData.flags, messageMediaDocumentData.document, messageMediaDocumentData.altDocuments, messageMediaDocumentData.videoCover, messageMediaDocumentData.videoTimestamp, messageMediaDocumentData.ttlSeconds)
if let document = document {
if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments, videoCover: coverPhoto) {
if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments, videoCover: coverPhoto, isLivePhoto: (flags & (1 << 11)) != 0) {
return (mediaFile, ttlSeconds, (flags & (1 << 3)) != 0, (flags & (1 << 4)) != 0, nil, videoTimestamp)
}
} else {
@ -785,6 +785,31 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
case let .messageEntityCustomEmoji(messageEntityCustomEmojiData):
let (offset, length, documentId) = (messageEntityCustomEmojiData.offset, messageEntityCustomEmojiData.length, messageEntityCustomEmojiData.documentId)
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .CustomEmoji(stickerPack: nil, fileId: documentId)))
case let .messageEntityFormattedDate(messageEntityFormattedDateData):
let (flags, offset, length, date) = (messageEntityFormattedDateData.flags, messageEntityFormattedDateData.offset, messageEntityFormattedDateData.length, messageEntityFormattedDateData.date)
let format: MessageTextEntityType.DateTimeFormat
if (flags & (1 << 0)) != 0 {
format = .relative
} else {
let timeFormat: MessageTextEntityType.DateTimeFormat.TimeFormat
if (flags & (1 << 1)) != 0 {
timeFormat = .short
} else if (flags & (1 << 2)) != 0 {
timeFormat = .long
} else {
timeFormat = .short
}
let dateFormat: MessageTextEntityType.DateTimeFormat.DateFormat
if (flags & (1 << 3)) != 0 {
dateFormat = .short
} else if (flags & (1 << 4)) != 0 {
dateFormat = .long
} else {
dateFormat = .short
}
format = .full(timeFormat: timeFormat, dateFormat: dateFormat)
}
result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .FormattedDate(format: format, date: date)))
}
}
return result

View file

@ -108,7 +108,7 @@ extension StickerMaskCoords {
}
}
func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAttribute]) -> [TelegramMediaFileAttribute] {
func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAttribute], isLivePhoto: Bool = false) -> [TelegramMediaFileAttribute] {
var result: [TelegramMediaFileAttribute] = []
for attribute in attributes {
switch attribute {
@ -137,6 +137,9 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt
if (flags & (1 << 3)) != 0 {
videoFlags.insert(.isSilent)
}
if isLivePhoto {
videoFlags.insert(.isLivePhoto)
}
result.append(.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: preloadSize, coverTime: videoStart, videoCodec: videoCodec))
case let .documentAttributeAudio(documentAttributeAudioData):
let (flags, duration, title, performer, waveform) = (documentAttributeAudioData.flags, documentAttributeAudioData.duration, documentAttributeAudioData.title, documentAttributeAudioData.performer, documentAttributeAudioData.waveform)
@ -192,11 +195,11 @@ func telegramMediaFileThumbnailRepresentationsFromApiSizes(datacenterId: Int32,
return (immediateThumbnailData, representations)
}
func telegramMediaFileFromApiDocument(_ document: Api.Document, altDocuments: [Api.Document]?, videoCover: Api.Photo? = nil) -> TelegramMediaFile? {
func telegramMediaFileFromApiDocument(_ document: Api.Document, altDocuments: [Api.Document]?, videoCover: Api.Photo? = nil, isLivePhoto: Bool = false) -> TelegramMediaFile? {
switch document {
case let .document(documentData):
let (id, accessHash, fileReference, mimeType, size, thumbs, videoThumbs, dcId, attributes) = (documentData.id, documentData.accessHash, documentData.fileReference, documentData.mimeType, documentData.size, documentData.thumbs, documentData.videoThumbs, documentData.dcId, documentData.attributes)
var parsedAttributes = telegramMediaFileAttributesFromApiAttributes(attributes)
var parsedAttributes = telegramMediaFileAttributesFromApiAttributes(attributes, isLivePhoto: isLivePhoto)
var isSticker = false
var isAnimated = false
for attribute in parsedAttributes {
@ -238,7 +241,7 @@ func telegramMediaFileFromApiDocument(_ document: Api.Document, altDocuments: [A
alternativeRepresentations = altDocuments.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: []) }
}
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: size, fileReference: fileReference.makeData(), fileName: fileNameFromFileAttributes(parsedAttributes)), previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, videoCover: videoCover.flatMap(telegramMediaImageFromApiPhoto), immediateThumbnailData: immediateThumbnail, mimeType: mimeType, size: size, attributes: parsedAttributes, alternativeRepresentations: alternativeRepresentations)
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: size, fileReference: fileReference.makeData(), fileName: fileNameFromFileAttributes(parsedAttributes)), previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, videoCover: videoCover.flatMap(telegramMediaImageFromApiPhoto), immediateThumbnailData: immediateThumbnail, mimeType: mimeType, size: size, attributes: parsedAttributes, alternativeRepresentations: alternativeRepresentations)
case .documentEmpty:
return nil
}

View file

@ -10,52 +10,72 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa
let offset: Int32 = Int32(entity.range.lowerBound)
let length: Int32 = Int32(entity.range.upperBound - entity.range.lowerBound)
switch entity.type {
case .Unknown:
break
case .Mention:
apiEntities.append(.messageEntityMention(.init(offset: offset, length: length)))
case .Hashtag:
apiEntities.append(.messageEntityHashtag(.init(offset: offset, length: length)))
case .BotCommand:
apiEntities.append(.messageEntityBotCommand(.init(offset: offset, length: length)))
case .Url:
apiEntities.append(.messageEntityUrl(.init(offset: offset, length: length)))
case .Email:
apiEntities.append(.messageEntityEmail(.init(offset: offset, length: length)))
case .Bold:
apiEntities.append(.messageEntityBold(.init(offset: offset, length: length)))
case .Italic:
apiEntities.append(.messageEntityItalic(.init(offset: offset, length: length)))
case .Code:
apiEntities.append(.messageEntityCode(.init(offset: offset, length: length)))
case let .Pre(language):
apiEntities.append(.messageEntityPre(.init(offset: offset, length: length, language: language ?? "")))
case let .TextUrl(url):
apiEntities.append(.messageEntityTextUrl(.init(offset: offset, length: length, url: url)))
case let .TextMention(peerId):
if let peer = associatedPeers[peerId], let inputUser = apiInputUser(peer) {
apiEntities.append(.inputMessageEntityMentionName(.init(offset: offset, length: length, userId: inputUser)))
case .Unknown:
break
case .Mention:
apiEntities.append(.messageEntityMention(.init(offset: offset, length: length)))
case .Hashtag:
apiEntities.append(.messageEntityHashtag(.init(offset: offset, length: length)))
case .BotCommand:
apiEntities.append(.messageEntityBotCommand(.init(offset: offset, length: length)))
case .Url:
apiEntities.append(.messageEntityUrl(.init(offset: offset, length: length)))
case .Email:
apiEntities.append(.messageEntityEmail(.init(offset: offset, length: length)))
case .Bold:
apiEntities.append(.messageEntityBold(.init(offset: offset, length: length)))
case .Italic:
apiEntities.append(.messageEntityItalic(.init(offset: offset, length: length)))
case .Code:
apiEntities.append(.messageEntityCode(.init(offset: offset, length: length)))
case let .Pre(language):
apiEntities.append(.messageEntityPre(.init(offset: offset, length: length, language: language ?? "")))
case let .TextUrl(url):
apiEntities.append(.messageEntityTextUrl(.init(offset: offset, length: length, url: url)))
case let .TextMention(peerId):
if let peer = associatedPeers[peerId], let inputUser = apiInputUser(peer) {
apiEntities.append(.inputMessageEntityMentionName(.init(offset: offset, length: length, userId: inputUser)))
}
case .PhoneNumber:
break
case .Strikethrough:
apiEntities.append(.messageEntityStrike(.init(offset: offset, length: length)))
case let .BlockQuote(isCollapsed):
var flags: Int32 = 0
if isCollapsed {
flags |= 1 << 0
}
apiEntities.append(.messageEntityBlockquote(.init(flags: flags, offset: offset, length: length)))
case .Underline:
apiEntities.append(.messageEntityUnderline(.init(offset: offset, length: length)))
case .BankCard:
apiEntities.append(.messageEntityBankCard(.init(offset: offset, length: length)))
case .Spoiler:
apiEntities.append(.messageEntitySpoiler(.init(offset: offset, length: length)))
case let .CustomEmoji(_, fileId):
apiEntities.append(.messageEntityCustomEmoji(.init(offset: offset, length: length, documentId: fileId)))
case let .FormattedDate(format, date):
var flags: Int32 = 0
switch format {
case .relative:
flags |= 1 << 0
case let .full(timeFormat, dateFormat):
switch timeFormat {
case .short:
flags |= 1 << 1
case .long:
flags |= 1 << 2
}
case .PhoneNumber:
break
case .Strikethrough:
apiEntities.append(.messageEntityStrike(.init(offset: offset, length: length)))
case let .BlockQuote(isCollapsed):
var flags: Int32 = 0
if isCollapsed {
flags |= 1 << 0
switch dateFormat {
case .short:
flags |= 1 << 3
case .long:
flags |= 1 << 4
}
apiEntities.append(.messageEntityBlockquote(.init(flags: flags, offset: offset, length: length)))
case .Underline:
apiEntities.append(.messageEntityUnderline(.init(offset: offset, length: length)))
case .BankCard:
apiEntities.append(.messageEntityBankCard(.init(offset: offset, length: length)))
case .Spoiler:
apiEntities.append(.messageEntitySpoiler(.init(offset: offset, length: length)))
case let .CustomEmoji(_, fileId):
apiEntities.append(.messageEntityCustomEmoji(.init(offset: offset, length: length, documentId: fileId)))
case .Custom:
break
}
apiEntities.append(.messageEntityFormattedDate(.init(flags: flags, offset: offset, length: length, date: date)))
case .Custom:
break
}
}

View file

@ -1169,7 +1169,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
if let _ = videoCoverPhoto {
flags |= 1 << 6
}
var ttlSeconds: Int32?
var videoTimestamp: Int32?
for attribute in attributes {
@ -1213,6 +1213,10 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
flags |= 1 << 7
}
if file.isLivePhoto {
flags |= 1 << 8
}
if ttlSeconds != nil {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(.init(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, videoCover: videoCoverPhoto, videoTimestamp: videoTimestamp, ttlSeconds: ttlSeconds)), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)))
}

View file

@ -2010,8 +2010,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
updatedState.updateStarGiftAuctionMyState(giftId: giftId, state: GiftAuctionContext.State.MyState(apiAuctionUserState: userState))
case let .updateEmojiGameInfo(updateEmojiGameInfoData):
updatedState.updateEmojiGameInfo(info: EmojiGameInfo(apiEmojiGameInfo: updateEmojiGameInfoData.info))
case let .updatePeerHistoryNoForward(updatePeerHistoryNoForwardData):
let (flags, peer) = (updatePeerHistoryNoForwardData.flags, updatePeerHistoryNoForwardData.peer)
case let .updatePeerHistoryNoForwards(updatePeerHistoryNoForwardsData):
let (flags, peer) = (updatePeerHistoryNoForwardsData.flags, updatePeerHistoryNoForwardsData.peer)
let peerId = peer.peerId
updatedState.updateCachedPeerData(peerId, { current in
if let previous = current as? CachedUserData {

View file

@ -822,6 +822,8 @@ private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi
break
case .CustomEmoji:
break
case .FormattedDate:
break
case .Custom:
break
}
@ -875,6 +877,8 @@ private func decryptedEntities101(_ entities: [MessageTextEntity]?) -> [SecretAp
break
case .CustomEmoji:
break
case .FormattedDate:
break
case .Custom:
break
}
@ -928,6 +932,8 @@ private func decryptedEntities144(_ entities: [MessageTextEntity]?) -> [SecretAp
result.append(.messageEntitySpoiler(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count)))
case let .CustomEmoji(_, fileId):
result.append(.messageEntityCustomEmoji(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count), documentId: fileId))
case .FormattedDate:
break
case .Custom:
break
}

View file

@ -311,6 +311,7 @@ public struct TelegramMediaVideoFlags: OptionSet {
public static let instantRoundVideo = TelegramMediaVideoFlags(rawValue: 1 << 0)
public static let supportsStreaming = TelegramMediaVideoFlags(rawValue: 1 << 1)
public static let isSilent = TelegramMediaVideoFlags(rawValue: 1 << 3)
public static let isLivePhoto = TelegramMediaVideoFlags(rawValue: 1 << 4)
}
public struct StickerMaskCoords: PostboxCoding, Equatable {
@ -1060,6 +1061,15 @@ public final class TelegramMediaFile: Media, Equatable, Codable {
return false
}
public var isLivePhoto: Bool {
for attribute in self.attributes {
if case .Video(_, _, let flags, _, _, _) = attribute {
return flags.contains(.isLivePhoto)
}
}
return false
}
public var preloadSize: Int32? {
for attribute in self.attributes {
if case .Video(_, _, _, let preloadSize, _, _) = attribute {

View file

@ -4,6 +4,67 @@ import Postbox
public enum MessageTextEntityType: Equatable {
public typealias CustomEntityType = Int32
public enum DateTimeFormat: Equatable {
public enum TimeFormat: Equatable {
case short
case long
}
public enum DateFormat: Equatable {
case short
case long
}
case relative
case full(timeFormat: TimeFormat, dateFormat: DateFormat)
public init(rawValue: Int32) {
if (rawValue & (1 << 0)) != 0 {
self = .relative
} else {
let timeFormat: MessageTextEntityType.DateTimeFormat.TimeFormat
if (rawValue & (1 << 1)) != 0 {
timeFormat = .short
} else if (rawValue & (1 << 2)) != 0 {
timeFormat = .long
} else {
timeFormat = .short
}
let dateFormat: MessageTextEntityType.DateTimeFormat.DateFormat
if (rawValue & (1 << 3)) != 0 {
dateFormat = .short
} else if (rawValue & (1 << 4)) != 0 {
dateFormat = .long
} else {
dateFormat = .short
}
self = .full(timeFormat: timeFormat, dateFormat: dateFormat)
}
}
public var rawValue: Int32 {
var rawValue: Int32 = 0
switch self {
case .relative:
rawValue |= 1 << 0
case let .full(timeFormat, dateFormat):
switch timeFormat {
case .short:
rawValue |= 1 << 1
case .long:
rawValue |= 1 << 2
}
switch dateFormat {
case .short:
rawValue |= 1 << 3
case .long:
rawValue |= 1 << 4
}
}
return rawValue
}
}
case Unknown
case Mention
case Hashtag
@ -23,6 +84,7 @@ public enum MessageTextEntityType: Equatable {
case BankCard
case Spoiler
case CustomEmoji(stickerPack: StickerPackReference?, fileId: Int64)
case FormattedDate(format: DateTimeFormat, date: Int32)
case Custom(type: CustomEntityType)
}
@ -39,47 +101,49 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
self.range = Int(decoder.decodeInt32ForKey("start", orElse: 0)) ..< Int(decoder.decodeInt32ForKey("end", orElse: 0))
let type: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
switch type {
case 1:
self.type = .Mention
case 2:
self.type = .Hashtag
case 3:
self.type = .BotCommand
case 4:
self.type = .Url
case 5:
self.type = .Email
case 6:
self.type = .Bold
case 7:
self.type = .Italic
case 8:
self.type = .Code
case 9:
self.type = .Pre(language: decoder.decodeOptionalStringForKey("language"))
case 10:
self.type = .TextUrl(url: decoder.decodeStringForKey("url", orElse: ""))
case 11:
self.type = .TextMention(peerId: PeerId(decoder.decodeInt64ForKey("peerId", orElse: 0)))
case 12:
self.type = .PhoneNumber
case 13:
self.type = .Strikethrough
case 14:
self.type = .BlockQuote(isCollapsed: decoder.decodeBoolForKey("cl", orElse: false))
case 15:
self.type = .Underline
case 16:
self.type = .BankCard
case 17:
self.type = .Spoiler
case 18:
let stickerPack = decoder.decodeObjectForKey("s", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference
self.type = .CustomEmoji(stickerPack: stickerPack, fileId: decoder.decodeInt64ForKey("f", orElse: 0))
case Int32.max:
self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0))
default:
self.type = .Unknown
case 1:
self.type = .Mention
case 2:
self.type = .Hashtag
case 3:
self.type = .BotCommand
case 4:
self.type = .Url
case 5:
self.type = .Email
case 6:
self.type = .Bold
case 7:
self.type = .Italic
case 8:
self.type = .Code
case 9:
self.type = .Pre(language: decoder.decodeOptionalStringForKey("language"))
case 10:
self.type = .TextUrl(url: decoder.decodeStringForKey("url", orElse: ""))
case 11:
self.type = .TextMention(peerId: PeerId(decoder.decodeInt64ForKey("peerId", orElse: 0)))
case 12:
self.type = .PhoneNumber
case 13:
self.type = .Strikethrough
case 14:
self.type = .BlockQuote(isCollapsed: decoder.decodeBoolForKey("cl", orElse: false))
case 15:
self.type = .Underline
case 16:
self.type = .BankCard
case 17:
self.type = .Spoiler
case 18:
let stickerPack = decoder.decodeObjectForKey("s", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference
self.type = .CustomEmoji(stickerPack: stickerPack, fileId: decoder.decodeInt64ForKey("f", orElse: 0))
case 19:
self.type = .FormattedDate(format: MessageTextEntityType.DateTimeFormat(rawValue: decoder.decodeInt32ForKey("format", orElse: 0)), date: decoder.decodeInt32ForKey("date", orElse: 0))
case Int32.max:
self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0))
default:
self.type = .Unknown
}
}
@ -95,49 +159,51 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
self.range = Int(rangeStart) ..< Int(rangeEnd)
switch type {
case 1:
self.type = .Mention
case 2:
self.type = .Hashtag
case 3:
self.type = .BotCommand
case 4:
self.type = .Url
case 5:
self.type = .Email
case 6:
self.type = .Bold
case 7:
self.type = .Italic
case 8:
self.type = .Code
case 9:
self.type = .Pre(language: try? container.decodeIfPresent(String.self, forKey: "language"))
case 10:
let url = (try? container.decode(String.self, forKey: "url")) ?? ""
self.type = .TextUrl(url: url)
case 11:
let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0
self.type = .TextMention(peerId: PeerId(peerId))
case 12:
self.type = .PhoneNumber
case 13:
self.type = .Strikethrough
case 14:
self.type = .BlockQuote(isCollapsed: try container.decodeIfPresent(Bool.self, forKey: "cl") ?? false)
case 15:
self.type = .Underline
case 16:
self.type = .BankCard
case 17:
self.type = .Spoiler
case 18:
self.type = .CustomEmoji(stickerPack: try container.decodeIfPresent(StickerPackReference.self, forKey: "s"), fileId: try container.decode(Int64.self, forKey: "f"))
case Int32.max:
let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0
self.type = .Custom(type: customType)
default:
self.type = .Unknown
case 1:
self.type = .Mention
case 2:
self.type = .Hashtag
case 3:
self.type = .BotCommand
case 4:
self.type = .Url
case 5:
self.type = .Email
case 6:
self.type = .Bold
case 7:
self.type = .Italic
case 8:
self.type = .Code
case 9:
self.type = .Pre(language: try? container.decodeIfPresent(String.self, forKey: "language"))
case 10:
let url = (try? container.decode(String.self, forKey: "url")) ?? ""
self.type = .TextUrl(url: url)
case 11:
let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0
self.type = .TextMention(peerId: PeerId(peerId))
case 12:
self.type = .PhoneNumber
case 13:
self.type = .Strikethrough
case 14:
self.type = .BlockQuote(isCollapsed: try container.decodeIfPresent(Bool.self, forKey: "cl") ?? false)
case 15:
self.type = .Underline
case 16:
self.type = .BankCard
case 17:
self.type = .Spoiler
case 18:
self.type = .CustomEmoji(stickerPack: try container.decodeIfPresent(StickerPackReference.self, forKey: "s"), fileId: try container.decode(Int64.self, forKey: "f"))
case 19:
self.type = .FormattedDate(format: MessageTextEntityType.DateTimeFormat(rawValue: try container.decode(Int32.self, forKey: "format")), date: try container.decode(Int32.self, forKey: "date"))
case Int32.max:
let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0
self.type = .Custom(type: customType)
default:
self.type = .Unknown
}
}
@ -145,61 +211,65 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
encoder.encodeInt32(Int32(self.range.lowerBound), forKey: "start")
encoder.encodeInt32(Int32(self.range.upperBound), forKey: "end")
switch self.type {
case .Unknown:
encoder.encodeInt32(0, forKey: "_rawValue")
case .Mention:
encoder.encodeInt32(1, forKey: "_rawValue")
case .Hashtag:
encoder.encodeInt32(2, forKey: "_rawValue")
case .BotCommand:
encoder.encodeInt32(3, forKey: "_rawValue")
case .Url:
encoder.encodeInt32(4, forKey: "_rawValue")
case .Email:
encoder.encodeInt32(5, forKey: "_rawValue")
case .Bold:
encoder.encodeInt32(6, forKey: "_rawValue")
case .Italic:
encoder.encodeInt32(7, forKey: "_rawValue")
case .Code:
encoder.encodeInt32(8, forKey: "_rawValue")
case let .Pre(language):
encoder.encodeInt32(9, forKey: "_rawValue")
if let language = language {
encoder.encodeString(language, forKey: "language")
} else {
encoder.encodeNil(forKey: "language")
}
case let .TextUrl(url):
encoder.encodeInt32(10, forKey: "_rawValue")
encoder.encodeString(url, forKey: "url")
case let .TextMention(peerId):
encoder.encodeInt32(11, forKey: "_rawValue")
encoder.encodeInt64(peerId.toInt64(), forKey: "peerId")
case .PhoneNumber:
encoder.encodeInt32(12, forKey: "_rawValue")
case .Strikethrough:
encoder.encodeInt32(13, forKey: "_rawValue")
case let .BlockQuote(isCollapsed):
encoder.encodeInt32(14, forKey: "_rawValue")
encoder.encodeBool(isCollapsed, forKey: "cl")
case .Underline:
encoder.encodeInt32(15, forKey: "_rawValue")
case .BankCard:
encoder.encodeInt32(16, forKey: "_rawValue")
case .Spoiler:
encoder.encodeInt32(17, forKey: "_rawValue")
case let .CustomEmoji(stickerPack, fileId):
encoder.encodeInt32(18, forKey: "_rawValue")
if let stickerPack = stickerPack {
encoder.encodeObject(stickerPack, forKey: "s")
} else {
encoder.encodeNil(forKey: "s")
}
encoder.encodeInt64(fileId, forKey: "f")
case let .Custom(type):
encoder.encodeInt32(Int32.max, forKey: "_rawValue")
encoder.encodeInt32(type, forKey: "type")
case .Unknown:
encoder.encodeInt32(0, forKey: "_rawValue")
case .Mention:
encoder.encodeInt32(1, forKey: "_rawValue")
case .Hashtag:
encoder.encodeInt32(2, forKey: "_rawValue")
case .BotCommand:
encoder.encodeInt32(3, forKey: "_rawValue")
case .Url:
encoder.encodeInt32(4, forKey: "_rawValue")
case .Email:
encoder.encodeInt32(5, forKey: "_rawValue")
case .Bold:
encoder.encodeInt32(6, forKey: "_rawValue")
case .Italic:
encoder.encodeInt32(7, forKey: "_rawValue")
case .Code:
encoder.encodeInt32(8, forKey: "_rawValue")
case let .Pre(language):
encoder.encodeInt32(9, forKey: "_rawValue")
if let language = language {
encoder.encodeString(language, forKey: "language")
} else {
encoder.encodeNil(forKey: "language")
}
case let .TextUrl(url):
encoder.encodeInt32(10, forKey: "_rawValue")
encoder.encodeString(url, forKey: "url")
case let .TextMention(peerId):
encoder.encodeInt32(11, forKey: "_rawValue")
encoder.encodeInt64(peerId.toInt64(), forKey: "peerId")
case .PhoneNumber:
encoder.encodeInt32(12, forKey: "_rawValue")
case .Strikethrough:
encoder.encodeInt32(13, forKey: "_rawValue")
case let .BlockQuote(isCollapsed):
encoder.encodeInt32(14, forKey: "_rawValue")
encoder.encodeBool(isCollapsed, forKey: "cl")
case .Underline:
encoder.encodeInt32(15, forKey: "_rawValue")
case .BankCard:
encoder.encodeInt32(16, forKey: "_rawValue")
case .Spoiler:
encoder.encodeInt32(17, forKey: "_rawValue")
case let .CustomEmoji(stickerPack, fileId):
encoder.encodeInt32(18, forKey: "_rawValue")
if let stickerPack = stickerPack {
encoder.encodeObject(stickerPack, forKey: "s")
} else {
encoder.encodeNil(forKey: "s")
}
encoder.encodeInt64(fileId, forKey: "f")
case let .FormattedDate(format, date):
encoder.encodeInt32(19, forKey: "_rawValue")
encoder.encodeInt32(format.rawValue, forKey: "format")
encoder.encodeInt32(date, forKey: "date")
case let .Custom(type):
encoder.encodeInt32(Int32.max, forKey: "_rawValue")
encoder.encodeInt32(type, forKey: "type")
}
}
@ -209,53 +279,57 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable {
try container.encode(Int32(self.range.lowerBound), forKey: "start")
try container.encode(Int32(self.range.upperBound), forKey: "end")
switch self.type {
case .Unknown:
try container.encode(0 as Int32, forKey: "_rawValue")
case .Mention:
try container.encode(1 as Int32, forKey: "_rawValue")
case .Hashtag:
try container.encode(2 as Int32, forKey: "_rawValue")
case .BotCommand:
try container.encode(3 as Int32, forKey: "_rawValue")
case .Url:
try container.encode(4 as Int32, forKey: "_rawValue")
case .Email:
try container.encode(5 as Int32, forKey: "_rawValue")
case .Bold:
try container.encode(6 as Int32, forKey: "_rawValue")
case .Italic:
try container.encode(7 as Int32, forKey: "_rawValue")
case .Code:
try container.encode(8 as Int32, forKey: "_rawValue")
case let .Pre(language):
try container.encode(9 as Int32, forKey: "_rawValue")
try container.encodeIfPresent(language, forKey: "language")
case let .TextUrl(url):
try container.encode(10 as Int32, forKey: "_rawValue")
try container.encode(url, forKey: "url")
case let .TextMention(peerId):
try container.encode(11 as Int32, forKey: "_rawValue")
try container.encode(peerId.toInt64(), forKey: "peerId")
case .PhoneNumber:
try container.encode(12 as Int32, forKey: "_rawValue")
case .Strikethrough:
try container.encode(13 as Int32, forKey: "_rawValue")
case let .BlockQuote(isCollapsed):
try container.encode(14 as Int32, forKey: "_rawValue")
try container.encode(isCollapsed, forKey: "cl")
case .Underline:
try container.encode(15 as Int32, forKey: "_rawValue")
case .BankCard:
try container.encode(16 as Int32, forKey: "_rawValue")
case .Spoiler:
try container.encode(17 as Int32, forKey: "_rawValue")
case let .CustomEmoji(stickerPack, fileId):
try container.encode(18 as Int32, forKey: "_rawValue")
try container.encodeIfPresent(stickerPack, forKey: "s")
try container.encode(fileId, forKey: "f")
case let .Custom(type):
try container.encode(Int32.max as Int32, forKey: "_rawValue")
try container.encode(type as Int32, forKey: "type")
case .Unknown:
try container.encode(0 as Int32, forKey: "_rawValue")
case .Mention:
try container.encode(1 as Int32, forKey: "_rawValue")
case .Hashtag:
try container.encode(2 as Int32, forKey: "_rawValue")
case .BotCommand:
try container.encode(3 as Int32, forKey: "_rawValue")
case .Url:
try container.encode(4 as Int32, forKey: "_rawValue")
case .Email:
try container.encode(5 as Int32, forKey: "_rawValue")
case .Bold:
try container.encode(6 as Int32, forKey: "_rawValue")
case .Italic:
try container.encode(7 as Int32, forKey: "_rawValue")
case .Code:
try container.encode(8 as Int32, forKey: "_rawValue")
case let .Pre(language):
try container.encode(9 as Int32, forKey: "_rawValue")
try container.encodeIfPresent(language, forKey: "language")
case let .TextUrl(url):
try container.encode(10 as Int32, forKey: "_rawValue")
try container.encode(url, forKey: "url")
case let .TextMention(peerId):
try container.encode(11 as Int32, forKey: "_rawValue")
try container.encode(peerId.toInt64(), forKey: "peerId")
case .PhoneNumber:
try container.encode(12 as Int32, forKey: "_rawValue")
case .Strikethrough:
try container.encode(13 as Int32, forKey: "_rawValue")
case let .BlockQuote(isCollapsed):
try container.encode(14 as Int32, forKey: "_rawValue")
try container.encode(isCollapsed, forKey: "cl")
case .Underline:
try container.encode(15 as Int32, forKey: "_rawValue")
case .BankCard:
try container.encode(16 as Int32, forKey: "_rawValue")
case .Spoiler:
try container.encode(17 as Int32, forKey: "_rawValue")
case let .CustomEmoji(stickerPack, fileId):
try container.encode(18 as Int32, forKey: "_rawValue")
try container.encodeIfPresent(stickerPack, forKey: "s")
try container.encode(fileId, forKey: "f")
case let .FormattedDate(format, date):
try container.encode(19 as Int32, forKey: "_rawValue")
try container.encode(format.rawValue, forKey: "format")
try container.encode(date, forKey: "date")
case let .Custom(type):
try container.encode(Int32.max as Int32, forKey: "_rawValue")
try container.encode(type as Int32, forKey: "type")
}
}

View file

@ -3594,6 +3594,8 @@ private func serializeGroupCallMessage(randomId: Int64, text: String, entities:
case let .CustomEmoji(_, fileId):
entityDict["_"] = "messageEntityCustomEmoji"
entityDict["document_id"] = "\(fileId)"
case .FormattedDate:
return nil
case .Custom:
return nil
}

View file

@ -408,7 +408,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
items.append(.action(ContextMenuActionItem(text: title, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: emptyAction)))
items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_ViewOnce, icon: { theme in
return currentValue == viewOnceTimeout ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return currentValue == viewOnceTimeout ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, a in
a(.default)
@ -419,7 +419,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
for value in values {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_Seconds(value), icon: { theme in
return currentValue == value ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return currentValue == value ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, a in
a(.default)
@ -428,7 +428,7 @@ public class LegacyMessageInputPanelNode: ASDisplayNode, TGCaptionPanelView {
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPicker_Timer_DoNotDelete, icon: { theme in
return currentValue == nil ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return currentValue == nil ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, a in
a(.default)

View file

@ -7286,7 +7286,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
color: theme.contextMenu.primaryColor
)
} else {
return nil
return UIImage()
}
},
action: { [weak self] _, a in

View file

@ -561,7 +561,7 @@ public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollVi
if isSelected && value == nil {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}), action: { [weak self] _, f in
scheduleTooltip(nil)

View file

@ -476,7 +476,7 @@ final class AffiliateProgramSetupScreenComponent: Component {
if isSelected {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor)
} else {
return nil
return UIImage()
}
}, action: { [weak self] _, f in
f(.default)

View file

@ -451,7 +451,7 @@ func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationD
if canManageEmojiStatus || data.webAppPermissions?.emojiStatus?.isRequested == true {
items[.permissions]!.append(PeerInfoScreenSwitchItem(id: ItemBotPermissionsEmojiStatus, text: presentationData.strings.PeerInfo_Permissions_EmojiStatus, value: canManageEmojiStatus, icon: UIImage(bundleImageName: "Chat/Info/Status"), isLocked: false, toggled: { value in
let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: user.id, enabled: value)
|> deliverOnMainQueue).startStandalone()
|> deliverOnMainQueue).startStandalone()
let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in
return WebAppPermissionsState(location: current?.location, emojiStatus: WebAppPermissionsState.EmojiStatus(isRequested: true))
@ -869,6 +869,7 @@ func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, s
case peerNote
case peerDataSettings
case peerVerifySettings
case peerPrivacySettings
case peerSettings
case linkedMonoforum
case peerAdditionalSettings
@ -901,6 +902,10 @@ func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, s
let ItemBotSettings = 13
let ItemBotInfo = 14
let ItemPeerCopyProtectionHeader = 15
let ItemPeerCopyProtection = 16
let ItemPeerCopyProtectionInfo = 17
if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_Username, icon: PresentationResourcesSettings.bot, action: {
interaction.editingOpenPublicLinkSetup()
@ -1020,6 +1025,21 @@ func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, s
items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemInfo, text: presentationData.strings.UserInfo_CustomPhotoInfo(compactName).string))
}
if "".isEmpty {
var copyProtectionEnabled = false
if let cachedData = data.cachedData as? CachedUserData, cachedData.flags.contains(.myCopyProtectionEnabled) {
copyProtectionEnabled = true
}
items[.peerPrivacySettings]!.append(PeerInfoScreenHeaderItem(id: ItemPeerCopyProtectionHeader, text: "FORWARDING FROM THIS CHAT") )
items[.peerPrivacySettings]!.append(PeerInfoScreenSwitchItem(id: ItemPeerCopyProtection, text: "Restrict Saving Content", value: copyProtectionEnabled, icon: nil, isLocked: false, toggled: { value in
let _ = context.engine.peers.toggleMessageCopyProtection(peerId: user.id, enabled: !copyProtectionEnabled).start()
}))
let peerName = data.peer.flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
let text = copyProtectionEnabled ? "\(peerName) is not able to copy, save and forward content from the chat." : "\(peerName) is able to copy, save and forward content from the chat."
items[.peerPrivacySettings]!.append(PeerInfoScreenCommentItem(id: ItemPeerCopyProtectionInfo, text: text, attributedPrefix: nil, useAccentLinkColor: false))
}
if data.isContact {
items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemDelete, text: presentationData.strings.UserInfo_DeleteContact, color: .destructive, action: {
interaction.requestDeleteContact()

View file

@ -172,28 +172,28 @@ extension PeerInfoScreenNode {
}
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in
return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.unlimited)
}, longPressAction: { _, f in
switchToFilter(.unlimited)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in
return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.limitedNonUpgradable)
}, longPressAction: { _, f in
switchToFilter(.limitedNonUpgradable)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in
return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.limitedUpgradable)
}, longPressAction: { _, f in
switchToFilter(.limitedUpgradable)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in
return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.unique)
}, longPressAction: { _, f in
@ -204,14 +204,14 @@ extension PeerInfoScreenNode {
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in
return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.displayed)
}, longPressAction: { _, f in
switchToVisiblityFilter(.displayed)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in
return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.hidden)
}, longPressAction: { _, f in

View file

@ -381,28 +381,28 @@ public final class AddGiftsScreen: ViewControllerComponentContainer {
}
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in
return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.unlimited)
}, longPressAction: { _, f in
switchToFilter(.unlimited)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in
return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.limitedNonUpgradable)
}, longPressAction: { _, f in
switchToFilter(.limitedNonUpgradable)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in
return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.limitedUpgradable)
}, longPressAction: { _, f in
switchToFilter(.limitedUpgradable)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in
return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.unique)
}, longPressAction: { _, f in
@ -412,14 +412,14 @@ public final class AddGiftsScreen: ViewControllerComponentContainer {
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in
return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.displayed)
}, longPressAction: { _, f in
switchToVisiblityFilter(.displayed)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in
return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage()
}, action: { _, f in
toggleFilter(.hidden)
}, longPressAction: { _, f in

View file

@ -1033,7 +1033,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
let isAdded = gift.collectionIds?.contains(collection.id) ?? false
subItems.append(.action(ContextMenuActionItem(text: title, entities: entities, entityFiles: entityFiles, enableEntityAnimations: false, icon: { theme in
return entities.isEmpty ? generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/Collection"), color: theme.contextMenu.primaryColor) : (isAdded ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil)
return entities.isEmpty ? generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/Collection"), color: theme.contextMenu.primaryColor) : (isAdded ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : UIImage())
}, iconPosition: collection.icon == nil ? .left : .right, action: { [weak self] _, f in
f(.default)

View file

@ -488,7 +488,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if canHideNames {
items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_ShowSendersName : presentationData.strings.Conversation_ForwardOptions_ShowSendersNames, icon: { theme in
if hideNames {
return nil
return UIImage()
} else {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}
@ -506,7 +506,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if hideNames {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}, action: { _, f in
self?.interfaceInteraction?.updateForwardOptionsState({ current in
@ -523,7 +523,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if hasCaptions {
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowCaption, icon: { theme in
if hideCaptions {
return nil
return UIImage()
} else {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}
@ -543,7 +543,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
if hideCaptions {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}, action: { _, f in
self?.interfaceInteraction?.updateForwardOptionsState({ current in

View file

@ -958,7 +958,7 @@ final class ShareWithPeersScreenComponent: Component {
let isSelected = self.shareToFolders.contains(where: { $0.id == folderPreview.folder.id })
items.append(.action(ContextMenuActionItem(text: folderPreview.folder.title, icon: icon, additionalLeftIcon: { theme in
if !isSelected {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, iconSource: iconSource, iconPosition: .left, action: { [weak self] c, f in

View file

@ -3241,7 +3241,7 @@ final class StorageUsageScreenComponent: Component {
if currentValue == value {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}, action: { _, f in
applyValue(value)

View file

@ -6388,7 +6388,7 @@ public final class StoryItemSetContainerComponent: Component {
if isSelected && value == nil {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white)
} else {
return nil
return UIImage()
}
}), action: { [weak self] _, f in
f(.default)

View file

@ -1397,7 +1397,7 @@ public final class StoryItemSetViewListComponent: Component {
return generateTintedImage(image: UIImage(bundleImageName: "Stories/Context Menu/Repost"), color: theme.contextMenu.primaryColor)
}, additionalLeftIcon: { theme in
if sortMode != .repostsFirst {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
@ -1416,7 +1416,7 @@ public final class StoryItemSetViewListComponent: Component {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: theme.contextMenu.primaryColor)
}, additionalLeftIcon: { theme in
if sortMode != .reactionsFirst {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
@ -1435,7 +1435,7 @@ public final class StoryItemSetViewListComponent: Component {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Time"), color: theme.contextMenu.primaryColor)
}, additionalLeftIcon: { theme in
if sortMode != .recentFirst {
return nil
return UIImage()
}
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in

View file

@ -1185,7 +1185,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
if currentValue == 0 {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}, action: { _, f in
applyValue(0)
@ -1208,7 +1208,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
if currentValue == value {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}, action: { _, f in
applyValue(value)

View file

@ -1310,7 +1310,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
if isSelected && value == nil {
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor)
} else {
return nil
return UIImage()
}
}), action: { [weak self] _, f in
scheduleTooltip(nil)