mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
UI fixes
This commit is contained in:
parent
c27e70632d
commit
b9aeb28c0d
@ -5521,3 +5521,6 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
|
|
||||||
"Conversation.Timer.Title" = "Send With Timer";
|
"Conversation.Timer.Title" = "Send With Timer";
|
||||||
"Conversation.Timer.Send" = "Send With Timer";
|
"Conversation.Timer.Send" = "Send With Timer";
|
||||||
|
|
||||||
|
"Conversation.NoticeInvitedByInChannel" = "%@ invited you to this channel";
|
||||||
|
"Conversation.NoticeInvitedByInGroup" = "%@ invited you to this group";
|
||||||
|
@ -89,7 +89,7 @@ public enum TextLinkItemActionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum TextLinkItem {
|
public enum TextLinkItem {
|
||||||
case url(String)
|
case url(url: String, concealed: Bool)
|
||||||
case mention(String)
|
case mention(String)
|
||||||
case hashtag(String?, String)
|
case hashtag(String?, String)
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,15 @@ public class ImmediateTextNode: TextNode {
|
|||||||
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated)
|
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateLayoutFullInfo(_ constrainedSize: CGSize) -> TextNodeLayout {
|
||||||
|
self.constrainedSize = constrainedSize
|
||||||
|
|
||||||
|
let makeLayout = TextNode.asyncLayout(self)
|
||||||
|
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets))
|
||||||
|
let _ = apply()
|
||||||
|
return layout
|
||||||
|
}
|
||||||
|
|
||||||
public func redrawIfPossible() {
|
public func redrawIfPossible() {
|
||||||
if let constrainedSize = self.constrainedSize {
|
if let constrainedSize = self.constrainedSize {
|
||||||
let _ = self.updateLayout(constrainedSize)
|
let _ = self.updateLayout(constrainedSize)
|
||||||
|
@ -370,7 +370,7 @@ public class ItemListMultilineTextItemNode: ListViewItemNode {
|
|||||||
let textNodeFrame = self.textNode.frame
|
let textNodeFrame = self.textNode.frame
|
||||||
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
return .url(url)
|
return .url(url: url, concealed: false)
|
||||||
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
||||||
return .mention(peerName)
|
return .mention(peerName)
|
||||||
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
||||||
|
@ -386,7 +386,7 @@ public class ItemListTextWithLabelItemNode: ListViewItemNode {
|
|||||||
let textNodeFrame = self.textNode.frame
|
let textNodeFrame = self.textNode.frame
|
||||||
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
return .url(url)
|
return .url(url: url, concealed: false)
|
||||||
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
||||||
return .mention(peerName)
|
return .mention(peerName)
|
||||||
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
||||||
|
@ -61,6 +61,9 @@ public func legacyLocationPickerController(context: AccountContext, selfPeer: Pe
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
return requestChatContextResults(account: context.account, botId: peerId, peerId: selfPeer.id, query: query ?? "", location: .single((location?.coordinate.latitude ?? 0.0, location?.coordinate.longitude ?? 0.0)), offset: "")
|
return requestChatContextResults(account: context.account, botId: peerId, peerId: selfPeer.id, query: query ?? "", location: .single((location?.coordinate.latitude ?? 0.0, location?.coordinate.longitude ?? 0.0)), offset: "")
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,9 @@ public func nearbyVenues(account: Account, latitude: Double, longitude: Double,
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
return requestChatContextResults(account: account, botId: peerId, peerId: account.peerId, query: query ?? "", location: .single((latitude, longitude)), offset: "")
|
return requestChatContextResults(account: account, botId: peerId, peerId: account.peerId, query: query ?? "", location: .single((latitude, longitude)), offset: "")
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
@ -615,7 +615,7 @@ private func channelVisibilityControllerEntries(presentationData: PresentationDa
|
|||||||
}
|
}
|
||||||
} else if let _ = view.peers[view.peerId] as? TelegramGroup {
|
} else if let _ = view.peers[view.peerId] as? TelegramGroup {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .privateLink:
|
case .privateLink, .generic:
|
||||||
let link = (view.cachedData as? CachedGroupData)?.exportedInvitation?.link
|
let link = (view.cachedData as? CachedGroupData)?.exportedInvitation?.link
|
||||||
let text: String
|
let text: String
|
||||||
if let link = link {
|
if let link = link {
|
||||||
|
@ -486,6 +486,9 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
|
|||||||
return (geoPoint.latitude, geoPoint.longitude)
|
return (geoPoint.latitude, geoPoint.longitude)
|
||||||
}
|
}
|
||||||
return requestChatContextResults(account: self.context.account, botId: collection.botId, peerId: collection.peerId, query: searchContext.result.query, location: .single(geoPoint), offset: nextOffset)
|
return requestChatContextResults(account: self.context.account, botId: collection.botId, peerId: collection.peerId, query: searchContext.result.query, location: .single(geoPoint), offset: nextOffset)
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
@ -535,6 +538,9 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode {
|
|||||||
return (.complete() |> delay(0.1, queue: Queue.concurrentDefaultQueue()))
|
return (.complete() |> delay(0.1, queue: Queue.concurrentDefaultQueue()))
|
||||||
|> then(
|
|> then(
|
||||||
requestContextResults(account: context.account, botId: user.id, query: wallpaperQuery, peerId: context.account.peerId, limit: 16)
|
requestContextResults(account: context.account, botId: user.id, query: wallpaperQuery, peerId: context.account.peerId, limit: 16)
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> map { collection -> ([ThemeGridSearchEntry], Bool)? in
|
|> map { collection -> ([ThemeGridSearchEntry], Bool)? in
|
||||||
guard let collection = collection else {
|
guard let collection = collection else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -167,6 +167,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
public let slowModeValidUntilTimestamp: Int32?
|
public let slowModeValidUntilTimestamp: Int32?
|
||||||
public let hasScheduledMessages: Bool
|
public let hasScheduledMessages: Bool
|
||||||
public let statsDatacenterId: Int32
|
public let statsDatacenterId: Int32
|
||||||
|
public let invitedBy: PeerId?
|
||||||
|
|
||||||
public let peerIds: Set<PeerId>
|
public let peerIds: Set<PeerId>
|
||||||
public let messageIds: Set<MessageId>
|
public let messageIds: Set<MessageId>
|
||||||
@ -194,9 +195,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.slowModeValidUntilTimestamp = nil
|
self.slowModeValidUntilTimestamp = nil
|
||||||
self.hasScheduledMessages = false
|
self.hasScheduledMessages = false
|
||||||
self.statsDatacenterId = 0
|
self.statsDatacenterId = 0
|
||||||
|
self.invitedBy = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: PeerId?, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32) {
|
public init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?, linkedDiscussionPeerId: PeerId?, peerGeoLocation: PeerGeoLocation?, slowModeTimeout: Int32?, slowModeValidUntilTimestamp: Int32?, hasScheduledMessages: Bool, statsDatacenterId: Int32, invitedBy: PeerId?) {
|
||||||
self.isNotAccessible = isNotAccessible
|
self.isNotAccessible = isNotAccessible
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.about = about
|
self.about = about
|
||||||
@ -214,6 +216,7 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.slowModeValidUntilTimestamp = slowModeValidUntilTimestamp
|
self.slowModeValidUntilTimestamp = slowModeValidUntilTimestamp
|
||||||
self.hasScheduledMessages = hasScheduledMessages
|
self.hasScheduledMessages = hasScheduledMessages
|
||||||
self.statsDatacenterId = statsDatacenterId
|
self.statsDatacenterId = statsDatacenterId
|
||||||
|
self.invitedBy = invitedBy
|
||||||
|
|
||||||
var peerIds = Set<PeerId>()
|
var peerIds = Set<PeerId>()
|
||||||
for botInfo in botInfos {
|
for botInfo in botInfos {
|
||||||
@ -224,81 +227,90 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
peerIds.insert(linkedDiscussionPeerId)
|
peerIds.insert(linkedDiscussionPeerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let invitedBy = invitedBy {
|
||||||
|
peerIds.insert(invitedBy)
|
||||||
|
}
|
||||||
|
|
||||||
self.peerIds = peerIds
|
self.peerIds = peerIds
|
||||||
|
|
||||||
var messageIds = Set<MessageId>()
|
var messageIds = Set<MessageId>()
|
||||||
if let pinnedMessageId = self.pinnedMessageId {
|
if let pinnedMessageId = self.pinnedMessageId {
|
||||||
messageIds.insert(pinnedMessageId)
|
messageIds.insert(pinnedMessageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.messageIds = messageIds
|
self.messageIds = messageIds
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData {
|
public func withUpdatedIsNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData {
|
public func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedAbout(_ about: String?) -> CachedChannelData {
|
public func withUpdatedAbout(_ about: String?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData {
|
public func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData {
|
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData {
|
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData {
|
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData {
|
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData {
|
public func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData {
|
public func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData {
|
public func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: PeerId?) -> CachedChannelData {
|
public func withUpdatedLinkedDiscussionPeerId(_ linkedDiscussionPeerId: PeerId?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData {
|
public func withUpdatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData {
|
public func withUpdatedSlowModeTimeout(_ slowModeTimeout: Int32?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData {
|
public func withUpdatedSlowModeValidUntilTimestamp(_ slowModeValidUntilTimestamp: Int32?) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData {
|
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData {
|
public func withUpdatedStatsDatacenterId(_ statsDatacenterId: Int32) -> CachedChannelData {
|
||||||
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId)
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: statsDatacenterId, invitedBy: self.invitedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedChannelData {
|
||||||
|
return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference, linkedDiscussionPeerId: self.linkedDiscussionPeerId, peerGeoLocation: self.peerGeoLocation, slowModeTimeout: self.slowModeTimeout, slowModeValidUntilTimestamp: self.slowModeValidUntilTimestamp, hasScheduledMessages: self.hasScheduledMessages, statsDatacenterId: self.statsDatacenterId, invitedBy: invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -355,6 +367,8 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
|
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
|
||||||
self.statsDatacenterId = decoder.decodeInt32ForKey("sdi", orElse: 0)
|
self.statsDatacenterId = decoder.decodeInt32ForKey("sdi", orElse: 0)
|
||||||
|
|
||||||
|
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
||||||
|
|
||||||
if let linkedDiscussionPeerId = self.linkedDiscussionPeerId {
|
if let linkedDiscussionPeerId = self.linkedDiscussionPeerId {
|
||||||
peerIds.insert(linkedDiscussionPeerId)
|
peerIds.insert(linkedDiscussionPeerId)
|
||||||
}
|
}
|
||||||
@ -439,6 +453,12 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
}
|
}
|
||||||
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
|
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
|
||||||
encoder.encodeInt32(self.statsDatacenterId, forKey: "sdi")
|
encoder.encodeInt32(self.statsDatacenterId, forKey: "sdi")
|
||||||
|
|
||||||
|
if let invitedBy = self.invitedBy {
|
||||||
|
encoder.encodeInt64(invitedBy.toInt64(), forKey: "invBy")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "invBy")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: CachedPeerData) -> Bool {
|
public func isEqual(to: CachedPeerData) -> Bool {
|
||||||
@ -514,6 +534,10 @@ public final class CachedChannelData: CachedPeerData {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if other.invitedBy != self.invitedBy {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
public let about: String?
|
public let about: String?
|
||||||
public let flags: CachedGroupFlags
|
public let flags: CachedGroupFlags
|
||||||
public let hasScheduledMessages: Bool
|
public let hasScheduledMessages: Bool
|
||||||
|
public let invitedBy: PeerId?
|
||||||
|
|
||||||
public let peerIds: Set<PeerId>
|
public let peerIds: Set<PeerId>
|
||||||
public let messageIds: Set<MessageId>
|
public let messageIds: Set<MessageId>
|
||||||
@ -64,9 +65,10 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
self.about = nil
|
self.about = nil
|
||||||
self.flags = CachedGroupFlags()
|
self.flags = CachedGroupFlags()
|
||||||
self.hasScheduledMessages = false
|
self.hasScheduledMessages = false
|
||||||
|
self.invitedBy = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool) {
|
public init(participants: CachedGroupParticipants?, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], peerStatusSettings: PeerStatusSettings?, pinnedMessageId: MessageId?, about: String?, flags: CachedGroupFlags, hasScheduledMessages: Bool, invitedBy: PeerId?) {
|
||||||
self.participants = participants
|
self.participants = participants
|
||||||
self.exportedInvitation = exportedInvitation
|
self.exportedInvitation = exportedInvitation
|
||||||
self.botInfos = botInfos
|
self.botInfos = botInfos
|
||||||
@ -75,6 +77,7 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
self.about = about
|
self.about = about
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.hasScheduledMessages = hasScheduledMessages
|
self.hasScheduledMessages = hasScheduledMessages
|
||||||
|
self.invitedBy = invitedBy
|
||||||
|
|
||||||
var messageIds = Set<MessageId>()
|
var messageIds = Set<MessageId>()
|
||||||
if let pinnedMessageId = self.pinnedMessageId {
|
if let pinnedMessageId = self.pinnedMessageId {
|
||||||
@ -91,6 +94,9 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
for botInfo in botInfos {
|
for botInfo in botInfos {
|
||||||
peerIds.insert(botInfo.peerId)
|
peerIds.insert(botInfo.peerId)
|
||||||
}
|
}
|
||||||
|
if let invitedBy = invitedBy {
|
||||||
|
peerIds.insert(invitedBy)
|
||||||
|
}
|
||||||
self.peerIds = peerIds
|
self.peerIds = peerIds
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +119,8 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
self.flags = CachedGroupFlags(rawValue: decoder.decodeInt32ForKey("fl", orElse: 0))
|
self.flags = CachedGroupFlags(rawValue: decoder.decodeInt32ForKey("fl", orElse: 0))
|
||||||
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
|
self.hasScheduledMessages = decoder.decodeBoolForKey("hsm", orElse: false)
|
||||||
|
|
||||||
|
self.invitedBy = decoder.decodeOptionalInt64ForKey("invBy").flatMap(PeerId.init)
|
||||||
|
|
||||||
var messageIds = Set<MessageId>()
|
var messageIds = Set<MessageId>()
|
||||||
if let pinnedMessageId = self.pinnedMessageId {
|
if let pinnedMessageId = self.pinnedMessageId {
|
||||||
messageIds.insert(pinnedMessageId)
|
messageIds.insert(pinnedMessageId)
|
||||||
@ -165,6 +173,12 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
}
|
}
|
||||||
encoder.encodeInt32(self.flags.rawValue, forKey: "fl")
|
encoder.encodeInt32(self.flags.rawValue, forKey: "fl")
|
||||||
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
|
encoder.encodeBool(self.hasScheduledMessages, forKey: "hsm")
|
||||||
|
|
||||||
|
if let invitedBy = self.invitedBy {
|
||||||
|
encoder.encodeInt64(invitedBy.toInt64(), forKey: "invBy")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "invBy")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: CachedPeerData) -> Bool {
|
public func isEqual(to: CachedPeerData) -> Bool {
|
||||||
@ -172,38 +186,42 @@ public final class CachedGroupData: CachedPeerData {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages
|
return self.participants == other.participants && self.exportedInvitation == other.exportedInvitation && self.botInfos == other.botInfos && self.peerStatusSettings == other.peerStatusSettings && self.pinnedMessageId == other.pinnedMessageId && self.about == other.about && self.flags == other.flags && self.hasScheduledMessages == other.hasScheduledMessages && self.invitedBy == other.invitedBy
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
|
public func withUpdatedParticipants(_ participants: CachedGroupParticipants?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages)
|
return CachedGroupData(participants: participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
|
public func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages)
|
return CachedGroupData(participants: self.participants, exportedInvitation: exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
|
public func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
|
public func withUpdatedPeerStatusSettings(_ peerStatusSettings: PeerStatusSettings?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
|
public func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
|
public func withUpdatedAbout(_ about: String?) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
|
public func withUpdatedFlags(_ flags: CachedGroupFlags) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
|
public func withUpdatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> CachedGroupData {
|
||||||
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages)
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: hasScheduledMessages, invitedBy: self.invitedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withUpdatedInvitedBy(_ invitedBy: PeerId?) -> CachedGroupData {
|
||||||
|
return CachedGroupData(participants: self.participants, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, peerStatusSettings: self.peerStatusSettings, pinnedMessageId: self.pinnedMessageId, about: self.about, flags: self.flags, hasScheduledMessages: self.hasScheduledMessages, invitedBy: invitedBy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,17 @@ private struct RequestData: Codable {
|
|||||||
|
|
||||||
private let requestVersion = "3"
|
private let requestVersion = "3"
|
||||||
|
|
||||||
public func requestChatContextResults(account: Account, botId: PeerId, peerId: PeerId, query: String, location: Signal<(Double, Double)?, NoError> = .single(nil), offset: String, incompleteResults: Bool = false) -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> {
|
public struct RequestChatContextResultsResult {
|
||||||
|
public let results: ChatContextResultCollection
|
||||||
|
public let isStale: Bool
|
||||||
|
|
||||||
|
public init(results: ChatContextResultCollection, isStale: Bool) {
|
||||||
|
self.results = results
|
||||||
|
self.isStale = isStale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func requestChatContextResults(account: Account, botId: PeerId, peerId: PeerId, query: String, location: Signal<(Double, Double)?, NoError> = .single(nil), offset: String, incompleteResults: Bool = false, staleCachedResults: Bool = false) -> Signal<RequestChatContextResultsResult?, RequestChatContextResultsError> {
|
||||||
return account.postbox.transaction { transaction -> (bot: Peer, peer: Peer)? in
|
return account.postbox.transaction { transaction -> (bot: Peer, peer: Peer)? in
|
||||||
if let bot = transaction.getPeer(botId), let peer = transaction.getPeer(peerId) {
|
if let bot = transaction.getPeer(botId), let peer = transaction.getPeer(peerId) {
|
||||||
return (bot, peer)
|
return (bot, peer)
|
||||||
@ -61,12 +71,14 @@ public func requestChatContextResults(account: Account, botId: PeerId, peerId: P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> castError(RequestChatContextResultsError.self)
|
|> castError(RequestChatContextResultsError.self)
|
||||||
|> mapToSignal { botAndPeer, location -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> in
|
|> mapToSignal { botAndPeer, location -> Signal<RequestChatContextResultsResult?, RequestChatContextResultsError> in
|
||||||
guard let (bot, peer) = botAndPeer, let inputBot = apiInputUser(bot) else {
|
guard let (bot, peer) = botAndPeer, let inputBot = apiInputUser(bot) else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.postbox.transaction { transaction -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> in
|
return account.postbox.transaction { transaction -> Signal<RequestChatContextResultsResult?, RequestChatContextResultsError> in
|
||||||
|
var staleResult: RequestChatContextResultsResult?
|
||||||
|
|
||||||
if offset.isEmpty && location == nil {
|
if offset.isEmpty && location == nil {
|
||||||
let requestData = RequestData(version: requestVersion, botId: botId, peerId: peerId, query: query)
|
let requestData = RequestData(version: requestVersion, botId: botId, peerId: peerId, query: query)
|
||||||
if let keyData = try? JSONEncoder().encode(requestData) {
|
if let keyData = try? JSONEncoder().encode(requestData) {
|
||||||
@ -75,7 +87,21 @@ public func requestChatContextResults(account: Account, botId: PeerId, peerId: P
|
|||||||
if let cachedResult = try? JSONDecoder().decode(ChatContextResultCollection.self, from: cachedEntry.data) {
|
if let cachedResult = try? JSONDecoder().decode(ChatContextResultCollection.self, from: cachedEntry.data) {
|
||||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||||
if cachedEntry.timestamp + cachedResult.cacheTimeout > timestamp {
|
if cachedEntry.timestamp + cachedResult.cacheTimeout > timestamp {
|
||||||
return .single(cachedResult)
|
return .single(RequestChatContextResultsResult(results: cachedResult, isStale: false))
|
||||||
|
} else if staleCachedResults {
|
||||||
|
let staleCollection = ChatContextResultCollection(
|
||||||
|
botId: cachedResult.botId,
|
||||||
|
peerId: cachedResult.peerId,
|
||||||
|
query: cachedResult.query,
|
||||||
|
geoPoint: cachedResult.geoPoint,
|
||||||
|
queryId: cachedResult.queryId,
|
||||||
|
nextOffset: nil,
|
||||||
|
presentation: cachedResult.presentation,
|
||||||
|
switchPeer: cachedResult.switchPeer,
|
||||||
|
results: cachedResult.results,
|
||||||
|
cacheTimeout: 0
|
||||||
|
)
|
||||||
|
staleResult = RequestChatContextResultsResult(results: staleCollection, isStale: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,8 +119,7 @@ public func requestChatContextResults(account: Account, botId: PeerId, peerId: P
|
|||||||
geoPoint = Api.InputGeoPoint.inputGeoPoint(lat: latitude, long: longitude)
|
geoPoint = Api.InputGeoPoint.inputGeoPoint(lat: latitude, long: longitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var signal: Signal<RequestChatContextResultsResult?, RequestChatContextResultsError> = account.network.request(Api.functions.messages.getInlineBotResults(flags: flags, bot: inputBot, peer: inputPeer, geoPoint: geoPoint, query: query, offset: offset))
|
||||||
var signal: Signal<ChatContextResultCollection?, RequestChatContextResultsError> = account.network.request(Api.functions.messages.getInlineBotResults(flags: flags, bot: inputBot, peer: inputPeer, geoPoint: geoPoint, query: query, offset: offset))
|
|
||||||
|> map { result -> ChatContextResultCollection? in
|
|> map { result -> ChatContextResultCollection? in
|
||||||
return ChatContextResultCollection(apiResults: result, botId: bot.id, peerId: peerId, query: query, geoPoint: location)
|
return ChatContextResultCollection(apiResults: result, botId: bot.id, peerId: peerId, query: query, geoPoint: location)
|
||||||
}
|
}
|
||||||
@ -105,12 +130,12 @@ public func requestChatContextResults(account: Account, botId: PeerId, peerId: P
|
|||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<ChatContextResultCollection?, RequestChatContextResultsError> in
|
|> mapToSignal { result -> Signal<RequestChatContextResultsResult?, RequestChatContextResultsError> in
|
||||||
guard let result = result else {
|
guard let result = result else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.postbox.transaction { transaction -> ChatContextResultCollection? in
|
return account.postbox.transaction { transaction -> RequestChatContextResultsResult? in
|
||||||
if result.cacheTimeout > 10 {
|
if result.cacheTimeout > 10 {
|
||||||
if let resultData = try? JSONEncoder().encode(result) {
|
if let resultData = try? JSONEncoder().encode(result) {
|
||||||
let requestData = RequestData(version: requestVersion, botId: botId, peerId: peerId, query: query)
|
let requestData = RequestData(version: requestVersion, botId: botId, peerId: peerId, query: query)
|
||||||
@ -120,13 +145,13 @@ public func requestChatContextResults(account: Account, botId: PeerId, peerId: P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return RequestChatContextResultsResult(results: result, isStale: false)
|
||||||
}
|
}
|
||||||
|> castError(RequestChatContextResultsError.self)
|
|> castError(RequestChatContextResultsError.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
if incompleteResults {
|
if incompleteResults {
|
||||||
signal = .single(nil) |> then(signal)
|
signal = .single(staleResult) |> then(signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
return signal
|
return signal
|
||||||
|
@ -308,6 +308,9 @@ public func searchGifs(account: Account, query: String) -> Signal<ChatContextRes
|
|||||||
}
|
}
|
||||||
|> mapToSignal { peer -> Signal<ChatContextResultCollection?, NoError> in
|
|> mapToSignal { peer -> Signal<ChatContextResultCollection?, NoError> in
|
||||||
return requestChatContextResults(account: account, botId: peer.id, peerId: account.peerId, query: query, offset: "")
|
return requestChatContextResults(account: account, botId: peer.id, peerId: account.peerId, query: query, offset: "")
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
@ -240,6 +240,19 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let participants = CachedGroupParticipants(apiParticipants: chatFull.participants)
|
let participants = CachedGroupParticipants(apiParticipants: chatFull.participants)
|
||||||
|
|
||||||
|
var invitedBy: PeerId?
|
||||||
|
if let participants = participants {
|
||||||
|
for participant in participants.participants {
|
||||||
|
if participant.peerId == accountPeerId {
|
||||||
|
if participant.invitedBy != accountPeerId {
|
||||||
|
invitedBy = participant.invitedBy
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let exportedInvitation = ExportedInvitation(apiExportedInvite: chatFull.exportedInvite)
|
let exportedInvitation = ExportedInvitation(apiExportedInvite: chatFull.exportedInvite)
|
||||||
let pinnedMessageId = chatFull.pinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })
|
let pinnedMessageId = chatFull.pinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) })
|
||||||
|
|
||||||
@ -289,6 +302,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
.withUpdatedAbout(chatFull.about)
|
.withUpdatedAbout(chatFull.about)
|
||||||
.withUpdatedFlags(flags)
|
.withUpdatedFlags(flags)
|
||||||
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||||
|
.withUpdatedInvitedBy(invitedBy)
|
||||||
})
|
})
|
||||||
case .channelFull:
|
case .channelFull:
|
||||||
break
|
break
|
||||||
@ -298,7 +312,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let inputChannel = maybePeer.flatMap(apiInputChannel) {
|
} else if let inputChannel = maybePeer.flatMap(apiInputChannel) {
|
||||||
return network.request(Api.functions.channels.getFullChannel(channel: inputChannel))
|
let fullChannelSignal = network.request(Api.functions.channels.getFullChannel(channel: inputChannel))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { error -> Signal<Api.messages.ChatFull?, NoError> in
|
|> `catch` { error -> Signal<Api.messages.ChatFull?, NoError> in
|
||||||
if error.errorDescription == "CHANNEL_PRIVATE" {
|
if error.errorDescription == "CHANNEL_PRIVATE" {
|
||||||
@ -306,7 +320,14 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
}
|
}
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<Bool, NoError> in
|
let participantSignal = network.request(Api.functions.channels.getParticipant(channel: inputChannel, userId: .inputUserSelf))
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { error -> Signal<Api.channels.ChannelParticipant?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineLatest(fullChannelSignal, participantSignal)
|
||||||
|
|> mapToSignal { result, participantResult -> Signal<Bool, NoError> in
|
||||||
return postbox.transaction { transaction -> Bool in
|
return postbox.transaction { transaction -> Bool in
|
||||||
if let result = result {
|
if let result = result {
|
||||||
switch result {
|
switch result {
|
||||||
@ -423,6 +444,19 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
hasScheduledMessages = true
|
hasScheduledMessages = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var invitedBy: PeerId?
|
||||||
|
if let participantResult = participantResult {
|
||||||
|
switch participantResult {
|
||||||
|
case let.channelParticipant(participant, _):
|
||||||
|
switch participant {
|
||||||
|
case let .channelParticipantSelf(_, inviterId, _):
|
||||||
|
invitedBy = PeerId(namespace: Namespaces.Peer.CloudUser, id: inviterId)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var minAvailableMessageIdUpdated = false
|
var minAvailableMessageIdUpdated = false
|
||||||
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
|
transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in
|
||||||
var previous: CachedChannelData
|
var previous: CachedChannelData
|
||||||
@ -451,6 +485,7 @@ func fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerI
|
|||||||
.withUpdatedSlowModeValidUntilTimestamp(slowmodeNextSendDate)
|
.withUpdatedSlowModeValidUntilTimestamp(slowmodeNextSendDate)
|
||||||
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
.withUpdatedHasScheduledMessages(hasScheduledMessages)
|
||||||
.withUpdatedStatsDatacenterId(statsDc ?? 0)
|
.withUpdatedStatsDatacenterId(statsDc ?? 0)
|
||||||
|
.withUpdatedInvitedBy(invitedBy)
|
||||||
})
|
})
|
||||||
|
|
||||||
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {
|
if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated {
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1219,7 +1219,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
switch action {
|
switch action {
|
||||||
case let .url(url):
|
case let .url(url):
|
||||||
var cleanUrl = url
|
var (cleanUrl, _) = parseUrl(url: url)
|
||||||
var canAddToReadingList = true
|
var canAddToReadingList = true
|
||||||
var canOpenIn = availableOpenInOptions(context: strongSelf.context, item: .url(url: url)).count > 1
|
var canOpenIn = availableOpenInOptions(context: strongSelf.context, item: .url(url: url)).count > 1
|
||||||
let mailtoString = "mailto:"
|
let mailtoString = "mailto:"
|
||||||
@ -2301,9 +2301,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var contactStatus: ChatContactStatus?
|
var contactStatus: ChatContactStatus?
|
||||||
if let peer = peerView.peers[peerView.peerId] {
|
if let peer = peerView.peers[peerView.peerId] {
|
||||||
if let cachedData = peerView.cachedData as? CachedUserData {
|
if let cachedData = peerView.cachedData as? CachedUserData {
|
||||||
contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings)
|
contactStatus = ChatContactStatus(canAddContact: !peerView.peerIsContact, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: nil)
|
||||||
} else if let cachedData = peerView.cachedData as? CachedGroupData {
|
} else if let cachedData = peerView.cachedData as? CachedGroupData {
|
||||||
contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings)
|
var invitedBy: Peer?
|
||||||
|
if let invitedByPeerId = cachedData.invitedBy {
|
||||||
|
if let peer = peerView.peers[invitedByPeerId] {
|
||||||
|
invitedBy = peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: false, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy)
|
||||||
} else if let cachedData = peerView.cachedData as? CachedChannelData {
|
} else if let cachedData = peerView.cachedData as? CachedChannelData {
|
||||||
var canReportIrrelevantLocation = true
|
var canReportIrrelevantLocation = true
|
||||||
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member {
|
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, peer.participationStatus == .member {
|
||||||
@ -2312,7 +2318,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if let peerReportNotice = peerReportNotice, peerReportNotice {
|
if let peerReportNotice = peerReportNotice, peerReportNotice {
|
||||||
canReportIrrelevantLocation = false
|
canReportIrrelevantLocation = false
|
||||||
}
|
}
|
||||||
contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings)
|
var invitedBy: Peer?
|
||||||
|
if let invitedByPeerId = cachedData.invitedBy {
|
||||||
|
if let peer = peerView.peers[invitedByPeerId] {
|
||||||
|
invitedBy = peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
var peers = SimpleDictionary<PeerId, Peer>()
|
var peers = SimpleDictionary<PeerId, Peer>()
|
||||||
@ -3792,6 +3804,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
if let navigationController = strongSelf.effectiveNavigationController {
|
if let navigationController = strongSelf.effectiveNavigationController {
|
||||||
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always))
|
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always))
|
||||||
}
|
}
|
||||||
|
}, navigateToProfile: { [weak self] peerId in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.openPeer(peerId: peerId, navigation: .default, fromMessage: nil)
|
||||||
}, openPeerInfo: { [weak self] in
|
}, openPeerInfo: { [weak self] in
|
||||||
self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
|
self?.navigationButtonAction(.openChatInfo(expandAvatar: false))
|
||||||
}, togglePeerNotifications: { [weak self] in
|
}, togglePeerNotifications: { [weak self] in
|
||||||
@ -7801,7 +7818,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
switch self.chatLocation {
|
switch self.chatLocation {
|
||||||
case let .peer(selfPeerId):
|
case let .peer(selfPeerId):
|
||||||
switch navigation {
|
switch navigation {
|
||||||
case .info:
|
case .info, .default:
|
||||||
let peerSignal: Signal<Peer?, NoError>
|
let peerSignal: Signal<Peer?, NoError>
|
||||||
if let fromMessage = fromMessage {
|
if let fromMessage = fromMessage {
|
||||||
peerSignal = loadedPeerFromMessage(account: self.context.account, peerId: peerId, messageId: fromMessage.id)
|
peerSignal = loadedPeerFromMessage(account: self.context.account, peerId: peerId, messageId: fromMessage.id)
|
||||||
@ -8219,7 +8236,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openResolved(_ result: ResolvedUrl, message: Message? = nil) {
|
private func openResolved(_ result: ResolvedUrl) {
|
||||||
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat, navigationController: self.effectiveNavigationController, openPeer: { [weak self] peerId, navigation in
|
self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat, navigationController: self.effectiveNavigationController, openPeer: { [weak self] peerId, navigation in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -8261,113 +8278,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self?.present(c, in: .window(.root), with: a)
|
self?.present(c, in: .window(.root), with: a)
|
||||||
}, dismissInput: { [weak self] in
|
}, dismissInput: { [weak self] in
|
||||||
self?.chatDisplayNode.dismissInput()
|
self?.chatDisplayNode.dismissInput()
|
||||||
}, contentContext: message)
|
}, contentContext: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openUrl(_ url: String, concealed: Bool, message: Message? = nil) {
|
private func openUrl(_ url: String, concealed: Bool, message: Message? = nil) {
|
||||||
self.commitPurposefulAction()
|
self.commitPurposefulAction()
|
||||||
|
|
||||||
var concealed = concealed
|
openUserGeneratedUrl(context: self.context, url: url, concealed: concealed, present: { [weak self] c in
|
||||||
|
self?.present(c, in: .window(.root))
|
||||||
let openImpl: () -> Void = { [weak self] in
|
}, openResolved: { [weak self] resolved in
|
||||||
guard let strongSelf = self else {
|
self?.openResolved(resolved)
|
||||||
return
|
})
|
||||||
}
|
|
||||||
|
|
||||||
let disposable: MetaDisposable
|
|
||||||
if let current = strongSelf.resolveUrlDisposable {
|
|
||||||
disposable = current
|
|
||||||
} else {
|
|
||||||
disposable = MetaDisposable()
|
|
||||||
strongSelf.resolveUrlDisposable = disposable
|
|
||||||
}
|
|
||||||
var cancelImpl: (() -> Void)?
|
|
||||||
let presentationData = strongSelf.presentationData
|
|
||||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
|
||||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
|
||||||
cancelImpl?()
|
|
||||||
}))
|
|
||||||
self?.present(controller, in: .window(.root))
|
|
||||||
return ActionDisposable { [weak controller] in
|
|
||||||
Queue.mainQueue().async() {
|
|
||||||
controller?.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> runOn(Queue.mainQueue())
|
|
||||||
|> delay(0.15, queue: Queue.mainQueue())
|
|
||||||
let progressDisposable = progressSignal.start()
|
|
||||||
|
|
||||||
cancelImpl = { [weak self] in
|
|
||||||
self?.resolveUrlDisposable?.set(nil)
|
|
||||||
}
|
|
||||||
disposable.set((strongSelf.context.sharedContext.resolveUrl(account: strongSelf.context.account, url: url)
|
|
||||||
|> afterDisposed {
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
progressDisposable.dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.openResolved(result, message: message)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsedUrlValue: URL?
|
|
||||||
if let parsed = URL(string: url) {
|
|
||||||
parsedUrlValue = parsed
|
|
||||||
} else if let parsed = URL(string: "https://" + url) {
|
|
||||||
parsedUrlValue = parsed
|
|
||||||
} else if let encoded = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let parsed = URL(string: encoded) {
|
|
||||||
parsedUrlValue = parsed
|
|
||||||
}
|
|
||||||
let host = parsedUrlValue?.host ?? url
|
|
||||||
|
|
||||||
let rawHost = (host as NSString).removingPercentEncoding ?? host
|
|
||||||
var latin = CharacterSet()
|
|
||||||
latin.insert(charactersIn: "A"..."Z")
|
|
||||||
latin.insert(charactersIn: "a"..."z")
|
|
||||||
latin.insert(charactersIn: "0"..."9")
|
|
||||||
var punctuation = CharacterSet()
|
|
||||||
punctuation.insert(charactersIn: ".-/+_")
|
|
||||||
var hasLatin = false
|
|
||||||
var hasNonLatin = false
|
|
||||||
for c in rawHost {
|
|
||||||
if c.unicodeScalars.allSatisfy(punctuation.contains) {
|
|
||||||
} else if c.unicodeScalars.allSatisfy(latin.contains) {
|
|
||||||
hasLatin = true
|
|
||||||
} else {
|
|
||||||
hasNonLatin = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasLatin && hasNonLatin {
|
|
||||||
concealed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if let parsedUrlValue = parsedUrlValue, isConcealedUrlWhitelisted(parsedUrlValue) {
|
|
||||||
concealed = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if concealed {
|
|
||||||
var rawDisplayUrl: String
|
|
||||||
if hasNonLatin {
|
|
||||||
rawDisplayUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? url
|
|
||||||
} else {
|
|
||||||
rawDisplayUrl = url
|
|
||||||
}
|
|
||||||
let maxLength = 180
|
|
||||||
if rawDisplayUrl.count > maxLength {
|
|
||||||
rawDisplayUrl = String(rawDisplayUrl[..<rawDisplayUrl.index(rawDisplayUrl.startIndex, offsetBy: maxLength - 2)]) + "..."
|
|
||||||
}
|
|
||||||
var displayUrl = rawDisplayUrl
|
|
||||||
displayUrl = displayUrl.replacingOccurrences(of: "\u{202e}", with: "")
|
|
||||||
self.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.Generic_OpenHiddenLinkAlert(displayUrl).0, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Yes, action: {
|
|
||||||
openImpl()
|
|
||||||
})]), in: .window(.root))
|
|
||||||
} else {
|
|
||||||
openImpl()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openUrlIn(_ url: String) {
|
private func openUrlIn(_ url: String) {
|
||||||
@ -9287,3 +9208,108 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent
|
|||||||
func animatedIn() {
|
func animatedIn() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseUrl(url: String) -> (string: String, concealed: Bool) {
|
||||||
|
var parsedUrlValue: URL?
|
||||||
|
if let parsed = URL(string: url) {
|
||||||
|
parsedUrlValue = parsed
|
||||||
|
} else if let parsed = URL(string: "https://" + url) {
|
||||||
|
parsedUrlValue = parsed
|
||||||
|
} else if let encoded = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let parsed = URL(string: encoded) {
|
||||||
|
parsedUrlValue = parsed
|
||||||
|
}
|
||||||
|
let host = parsedUrlValue?.host ?? url
|
||||||
|
|
||||||
|
let rawHost = (host as NSString).removingPercentEncoding ?? host
|
||||||
|
var latin = CharacterSet()
|
||||||
|
latin.insert(charactersIn: "A"..."Z")
|
||||||
|
latin.insert(charactersIn: "a"..."z")
|
||||||
|
latin.insert(charactersIn: "0"..."9")
|
||||||
|
var punctuation = CharacterSet()
|
||||||
|
punctuation.insert(charactersIn: ".-/+_")
|
||||||
|
var hasLatin = false
|
||||||
|
var hasNonLatin = false
|
||||||
|
for c in rawHost {
|
||||||
|
if c.unicodeScalars.allSatisfy(punctuation.contains) {
|
||||||
|
} else if c.unicodeScalars.allSatisfy(latin.contains) {
|
||||||
|
hasLatin = true
|
||||||
|
} else {
|
||||||
|
hasNonLatin = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var concealed = false
|
||||||
|
if hasLatin && hasNonLatin {
|
||||||
|
concealed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawDisplayUrl: String
|
||||||
|
if hasNonLatin {
|
||||||
|
rawDisplayUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? url
|
||||||
|
} else {
|
||||||
|
rawDisplayUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
if let parsedUrlValue = parsedUrlValue, isConcealedUrlWhitelisted(parsedUrlValue) {
|
||||||
|
concealed = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rawDisplayUrl, concealed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func openUserGeneratedUrl(context: AccountContext, url: String, concealed: Bool, present: @escaping (ViewController) -> Void, openResolved: @escaping (ResolvedUrl) -> Void) {
|
||||||
|
var concealed = concealed
|
||||||
|
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
let openImpl: () -> Void = {
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
var cancelImpl: (() -> Void)?
|
||||||
|
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||||
|
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
|
||||||
|
cancelImpl?()
|
||||||
|
}))
|
||||||
|
present(controller)
|
||||||
|
return ActionDisposable { [weak controller] in
|
||||||
|
Queue.mainQueue().async() {
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> runOn(Queue.mainQueue())
|
||||||
|
|> delay(0.15, queue: Queue.mainQueue())
|
||||||
|
let progressDisposable = progressSignal.start()
|
||||||
|
|
||||||
|
cancelImpl = {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
disposable.set((context.sharedContext.resolveUrl(account: context.account, url: url)
|
||||||
|
|> afterDisposed {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
progressDisposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue).start(next: { result in
|
||||||
|
openResolved(result)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
let (parsedString, parsedConcealed) = parseUrl(url: url)
|
||||||
|
if parsedConcealed {
|
||||||
|
concealed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if concealed {
|
||||||
|
var rawDisplayUrl: String = parsedString
|
||||||
|
let maxLength = 180
|
||||||
|
if rawDisplayUrl.count > maxLength {
|
||||||
|
rawDisplayUrl = String(rawDisplayUrl[..<rawDisplayUrl.index(rawDisplayUrl.startIndex, offsetBy: maxLength - 2)]) + "..."
|
||||||
|
}
|
||||||
|
var displayUrl = rawDisplayUrl
|
||||||
|
displayUrl = displayUrl.replacingOccurrences(of: "\u{202e}", with: "")
|
||||||
|
present(textAlertController(context: context, title: nil, text: presentationData.strings.Generic_OpenHiddenLinkAlert(displayUrl).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: {
|
||||||
|
openImpl()
|
||||||
|
})]))
|
||||||
|
} else {
|
||||||
|
openImpl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -782,7 +782,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
let _ = inputMediaNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelSize?.height ?? 0.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: false)
|
let _ = inputMediaNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, standardInputHeight: layout.standardInputHeight, inputHeight: layout.inputHeight ?? 0.0, maximumHeight: maximumInputNodeHeight, inputPanelHeight: inputPanelSize?.height ?? 0.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, deviceMetrics: layout.deviceMetrics, isVisible: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 56.0)))
|
transition.updateFrame(node: self.titleAccessoryPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: 66.0)))
|
||||||
|
|
||||||
transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)))
|
||||||
|
|
||||||
|
@ -127,15 +127,20 @@ private final class ChatInfoTitlePanelButtonNode: HighlightableButtonNode {
|
|||||||
final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
|
final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||||
private var theme: PresentationTheme?
|
private var theme: PresentationTheme?
|
||||||
|
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
private let separatorNode: ASDisplayNode
|
private let separatorNode: ASDisplayNode
|
||||||
private var buttons: [(ChatInfoTitleButton, ChatInfoTitlePanelButtonNode)] = []
|
private var buttons: [(ChatInfoTitleButton, ChatInfoTitlePanelButtonNode)] = []
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
|
||||||
self.separatorNode = ASDisplayNode()
|
self.separatorNode = ASDisplayNode()
|
||||||
self.separatorNode.isLayerBacked = true
|
self.separatorNode.isLayerBacked = true
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
self.addSubnode(self.separatorNode)
|
self.addSubnode(self.separatorNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +151,7 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
let panelHeight: CGFloat = 55.0
|
let panelHeight: CGFloat = 55.0
|
||||||
|
|
||||||
if themeUpdated {
|
if themeUpdated {
|
||||||
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
self.backgroundNode.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +203,8 @@ final class ChatInfoTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: panelHeight)))
|
||||||
|
|
||||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||||
|
|
||||||
return panelHeight
|
return panelHeight
|
||||||
|
@ -256,7 +256,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee
|
|||||||
}
|
}
|
||||||
|> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
|> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||||
return { _ in
|
return { _ in
|
||||||
return .contextRequestResult(user, results)
|
return .contextRequestResult(user, results?.results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +307,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
|
|||||||
saved = []
|
saved = []
|
||||||
}
|
}
|
||||||
|
|
||||||
return (MultiplexedVideoNodeFiles(saved: saved, trending: trending?.files ?? [], isSearch: false, canLoadMore: false), nil)
|
return (MultiplexedVideoNodeFiles(saved: saved, trending: trending?.files ?? [], isSearch: false, canLoadMore: false, isStale: false), nil)
|
||||||
}
|
}
|
||||||
case .trending:
|
case .trending:
|
||||||
if let searchOffset = searchOffset {
|
if let searchOffset = searchOffset {
|
||||||
@ -319,16 +319,16 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
|
|||||||
} else {
|
} else {
|
||||||
canLoadMore = true
|
canLoadMore = true
|
||||||
}
|
}
|
||||||
return (MultiplexedVideoNodeFiles(saved: [], trending: result?.files ?? [], isSearch: true, canLoadMore: canLoadMore), result?.nextOffset)
|
return (MultiplexedVideoNodeFiles(saved: [], trending: result?.files ?? [], isSearch: true, canLoadMore: canLoadMore, isStale: false), result?.nextOffset)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filesSignal = self.trendingPromise.get()
|
filesSignal = self.trendingPromise.get()
|
||||||
|> map { trending -> (MultiplexedVideoNodeFiles, String?) in
|
|> map { trending -> (MultiplexedVideoNodeFiles, String?) in
|
||||||
return (MultiplexedVideoNodeFiles(saved: [], trending: trending?.files ?? [], isSearch: true, canLoadMore: false), trending?.nextOffset)
|
return (MultiplexedVideoNodeFiles(saved: [], trending: trending?.files ?? [], isSearch: true, canLoadMore: false, isStale: false), trending?.nextOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .emojiSearch(emoji):
|
case let .emojiSearch(emoji):
|
||||||
filesSignal = paneGifSearchForQuery(account: self.account, query: emoji, offset: searchOffset, incompleteResults: true, delayRequest: false, updateActivity: nil)
|
filesSignal = paneGifSearchForQuery(account: self.account, query: emoji, offset: searchOffset, incompleteResults: true, staleCachedResults: searchOffset == nil, delayRequest: false, updateActivity: nil)
|
||||||
|> map { result -> (MultiplexedVideoNodeFiles, String?) in
|
|> map { result -> (MultiplexedVideoNodeFiles, String?) in
|
||||||
let canLoadMore: Bool
|
let canLoadMore: Bool
|
||||||
if let result = result {
|
if let result = result {
|
||||||
@ -336,7 +336,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
|
|||||||
} else {
|
} else {
|
||||||
canLoadMore = true
|
canLoadMore = true
|
||||||
}
|
}
|
||||||
return (MultiplexedVideoNodeFiles(saved: [], trending: result?.files ?? [], isSearch: true, canLoadMore: canLoadMore), result?.nextOffset)
|
return (MultiplexedVideoNodeFiles(saved: [], trending: result?.files ?? [], isSearch: true, canLoadMore: canLoadMore, isStale: result?.isStale ?? false), result?.nextOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate {
|
|||||||
existingFileIds.insert(file.file.media.fileId)
|
existingFileIds.insert(file.file.media.fileId)
|
||||||
resultFiles.append(file)
|
resultFiles.append(file)
|
||||||
}
|
}
|
||||||
files = MultiplexedVideoNodeFiles(saved: [], trending: resultFiles, isSearch: true, canLoadMore: addedFiles.canLoadMore)
|
files = MultiplexedVideoNodeFiles(saved: [], trending: resultFiles, isSearch: true, canLoadMore: addedFiles.canLoadMore, isStale: addedFiles.isStale)
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.nextOffset = nextOffset
|
strongSelf.nextOffset = nextOffset
|
||||||
|
@ -266,7 +266,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
cutout = TextNodeCutout(bottomRight: statusSize)
|
cutout = TextNodeCutout(bottomRight: statusSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
let textInsets = UIEdgeInsets(top: 2.0, left: 0.0, bottom: 5.0, right: 0.0)
|
let textInsets = UIEdgeInsets(top: 2.0, left: 2.0, bottom: 5.0, right: 2.0)
|
||||||
|
|
||||||
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: textInsets, lineColor: messageTheme.accentControlColor))
|
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: textInsets, lineColor: messageTheme.accentControlColor))
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
let toggleMembersSearch: (Bool) -> Void
|
let toggleMembersSearch: (Bool) -> Void
|
||||||
let navigateToMessage: (MessageId) -> Void
|
let navigateToMessage: (MessageId) -> Void
|
||||||
let navigateToChat: (PeerId) -> Void
|
let navigateToChat: (PeerId) -> Void
|
||||||
|
let navigateToProfile: (PeerId) -> Void
|
||||||
let openPeerInfo: () -> Void
|
let openPeerInfo: () -> Void
|
||||||
let togglePeerNotifications: () -> Void
|
let togglePeerNotifications: () -> Void
|
||||||
let sendContextResult: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
|
let sendContextResult: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
|
||||||
@ -119,7 +120,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
|
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
|
||||||
let statuses: ChatPanelInterfaceInteractionStatuses?
|
let statuses: ChatPanelInterfaceInteractionStatuses?
|
||||||
|
|
||||||
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void, openScheduledMessages: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, navigateToProfile: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void, openScheduledMessages: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
|
||||||
self.setupReplyMessage = setupReplyMessage
|
self.setupReplyMessage = setupReplyMessage
|
||||||
self.setupEditMessage = setupEditMessage
|
self.setupEditMessage = setupEditMessage
|
||||||
self.beginMessageSelection = beginMessageSelection
|
self.beginMessageSelection = beginMessageSelection
|
||||||
@ -144,6 +145,7 @@ final class ChatPanelInterfaceInteraction {
|
|||||||
self.toggleMembersSearch = toggleMembersSearch
|
self.toggleMembersSearch = toggleMembersSearch
|
||||||
self.navigateToMessage = navigateToMessage
|
self.navigateToMessage = navigateToMessage
|
||||||
self.navigateToChat = navigateToChat
|
self.navigateToChat = navigateToChat
|
||||||
|
self.navigateToProfile = navigateToProfile
|
||||||
self.openPeerInfo = openPeerInfo
|
self.openPeerInfo = openPeerInfo
|
||||||
self.togglePeerNotifications = togglePeerNotifications
|
self.togglePeerNotifications = togglePeerNotifications
|
||||||
self.sendContextResult = sendContextResult
|
self.sendContextResult = sendContextResult
|
||||||
|
@ -243,6 +243,7 @@ struct ChatContactStatus: Equatable {
|
|||||||
var canAddContact: Bool
|
var canAddContact: Bool
|
||||||
var canReportIrrelevantLocation: Bool
|
var canReportIrrelevantLocation: Bool
|
||||||
var peerStatusSettings: PeerStatusSettings?
|
var peerStatusSettings: PeerStatusSettings?
|
||||||
|
var invitedBy: Peer?
|
||||||
|
|
||||||
var isEmpty: Bool {
|
var isEmpty: Bool {
|
||||||
guard var peerStatusSettings = self.peerStatusSettings else {
|
guard var peerStatusSettings = self.peerStatusSettings else {
|
||||||
@ -256,6 +257,22 @@ struct ChatContactStatus: Equatable {
|
|||||||
}
|
}
|
||||||
return peerStatusSettings.isEmpty
|
return peerStatusSettings.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: ChatContactStatus, rhs: ChatContactStatus) -> Bool {
|
||||||
|
if lhs.canAddContact != rhs.canAddContact {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.canReportIrrelevantLocation != rhs.canReportIrrelevantLocation {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.peerStatusSettings != rhs.peerStatusSettings {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !arePeersEqual(lhs.invitedBy, rhs.invitedBy) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatSlowmodeVariant: Equatable {
|
enum ChatSlowmodeVariant: Equatable {
|
||||||
|
@ -75,6 +75,7 @@ final class ChatRecentActionsController: TelegramBaseController {
|
|||||||
}, toggleMembersSearch: { _ in
|
}, toggleMembersSearch: { _ in
|
||||||
}, navigateToMessage: { _ in
|
}, navigateToMessage: { _ in
|
||||||
}, navigateToChat: { _ in
|
}, navigateToChat: { _ in
|
||||||
|
}, navigateToProfile: { _ in
|
||||||
}, openPeerInfo: {
|
}, openPeerInfo: {
|
||||||
}, togglePeerNotifications: {
|
}, togglePeerNotifications: {
|
||||||
}, sendContextResult: { _, _, _, _ in
|
}, sendContextResult: { _, _, _, _ in
|
||||||
|
@ -78,7 +78,109 @@ private func peerButtons(_ state: ChatPresentationInterfaceState) -> [ChatReport
|
|||||||
return buttons
|
return buttons
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ChatInfoTitlePanelInviteInfoNode: ASDisplayNode {
|
||||||
|
private var theme: PresentationTheme?
|
||||||
|
|
||||||
|
private let labelNode: ImmediateTextNode
|
||||||
|
private let filledBackgroundNode: LinkHighlightingNode
|
||||||
|
|
||||||
|
init(openInvitePeer: @escaping () -> Void) {
|
||||||
|
self.labelNode = ImmediateTextNode()
|
||||||
|
self.labelNode.maximumNumberOfLines = 1
|
||||||
|
self.labelNode.textAlignment = .center
|
||||||
|
|
||||||
|
self.filledBackgroundNode = LinkHighlightingNode(color: .clear)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.filledBackgroundNode)
|
||||||
|
self.addSubnode(self.labelNode)
|
||||||
|
|
||||||
|
self.labelNode.highlightAttributeAction = { attributes in
|
||||||
|
for (key, _) in attributes {
|
||||||
|
if key.rawValue == "_Link" {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.labelNode.tapAttributeAction = { attributes, _ in
|
||||||
|
for (key, _) in attributes {
|
||||||
|
if key.rawValue == "_Link" {
|
||||||
|
openInvitePeer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, chatPeer: Peer, invitedBy: Peer, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||||
|
let primaryTextColor = serviceMessageColorComponents(theme: theme, wallpaper: wallpaper).primaryText
|
||||||
|
|
||||||
|
if self.theme !== theme {
|
||||||
|
self.theme = theme
|
||||||
|
|
||||||
|
self.labelNode.linkHighlightColor = primaryTextColor.withAlphaComponent(0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
let topInset: CGFloat = 6.0
|
||||||
|
let bottomInset: CGFloat = 6.0
|
||||||
|
let sideInset: CGFloat = 16.0
|
||||||
|
|
||||||
|
let stringAndRanges: (String, [(Int, NSRange)])
|
||||||
|
if let channel = chatPeer as? TelegramChannel, case .broadcast = channel.info {
|
||||||
|
stringAndRanges = strings.Conversation_NoticeInvitedByInChannel(invitedBy.compactDisplayTitle)
|
||||||
|
} else {
|
||||||
|
stringAndRanges = strings.Conversation_NoticeInvitedByInGroup(invitedBy.compactDisplayTitle)
|
||||||
|
}
|
||||||
|
|
||||||
|
let attributedString = NSMutableAttributedString(string: stringAndRanges.0, font: Font.regular(13.0), textColor: primaryTextColor)
|
||||||
|
|
||||||
|
let boldAttributes = [NSAttributedString.Key.font: Font.semibold(13.0), NSAttributedString.Key(rawValue: "_Link"): true as NSNumber]
|
||||||
|
for (_, range) in stringAndRanges.1 {
|
||||||
|
attributedString.addAttributes(boldAttributes, range: range)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.labelNode.attributedText = attributedString
|
||||||
|
let labelLayout = self.labelNode.updateLayoutFullInfo(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
|
||||||
|
var labelRects = labelLayout.linesRects()
|
||||||
|
if labelRects.count > 1 {
|
||||||
|
let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width })
|
||||||
|
for i in 0 ..< sortedIndices.count {
|
||||||
|
let index = sortedIndices[i]
|
||||||
|
for j in -1 ... 1 {
|
||||||
|
if j != 0 && index + j >= 0 && index + j < sortedIndices.count {
|
||||||
|
if abs(labelRects[index + j].width - labelRects[index].width) < 40.0 {
|
||||||
|
labelRects[index + j].size.width = max(labelRects[index + j].width, labelRects[index].width)
|
||||||
|
labelRects[index].size.width = labelRects[index + j].size.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 0 ..< labelRects.count {
|
||||||
|
labelRects[i] = labelRects[i].insetBy(dx: -6.0, dy: floor((labelRects[i].height - 20.0) / 2.0))
|
||||||
|
labelRects[i].size.height = 20.0
|
||||||
|
labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundLayout = self.filledBackgroundNode.asyncLayout()
|
||||||
|
let serviceColor = serviceMessageColorComponents(theme: theme, wallpaper: wallpaper)
|
||||||
|
let backgroundApply = backgroundLayout(serviceColor.fill, labelRects, 10.0, 10.0, 0.0)
|
||||||
|
backgroundApply()
|
||||||
|
|
||||||
|
let backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + 4.0)
|
||||||
|
|
||||||
|
let labelFrame = CGRect(origin: CGPoint(x: floor((width - labelLayout.size.width) / 2.0), y: topInset + floorToScreenPixels((backgroundSize.height - labelLayout.size.height) / 2.0) - 1.0), size: labelLayout.size)
|
||||||
|
self.labelNode.frame = labelFrame
|
||||||
|
self.filledBackgroundNode.frame = labelFrame.offsetBy(dx: 0.0, dy: -11.0)
|
||||||
|
|
||||||
|
return topInset + backgroundSize.height + bottomInset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
private let separatorNode: ASDisplayNode
|
private let separatorNode: ASDisplayNode
|
||||||
|
|
||||||
private let closeButton: HighlightableButtonNode
|
private let closeButton: HighlightableButtonNode
|
||||||
@ -86,7 +188,11 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
|
|
||||||
private var theme: PresentationTheme?
|
private var theme: PresentationTheme?
|
||||||
|
|
||||||
|
private var inviteInfoNode: ChatInfoTitlePanelInviteInfoNode?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
self.separatorNode = ASDisplayNode()
|
self.separatorNode = ASDisplayNode()
|
||||||
self.separatorNode.isLayerBacked = true
|
self.separatorNode.isLayerBacked = true
|
||||||
|
|
||||||
@ -96,6 +202,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
self.addSubnode(self.separatorNode)
|
self.addSubnode(self.separatorNode)
|
||||||
|
|
||||||
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
|
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
|
||||||
@ -107,11 +214,11 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
self.theme = interfaceState.theme
|
self.theme = interfaceState.theme
|
||||||
|
|
||||||
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelEncircledCloseIconImage(interfaceState.theme), for: [])
|
self.closeButton.setImage(PresentationResourcesChat.chatInputPanelEncircledCloseIconImage(interfaceState.theme), for: [])
|
||||||
self.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
self.backgroundNode.backgroundColor = interfaceState.theme.chat.historyNavigation.fillColor
|
||||||
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
self.separatorNode.backgroundColor = interfaceState.theme.chat.historyNavigation.strokeColor
|
||||||
}
|
}
|
||||||
|
|
||||||
let panelHeight: CGFloat = 40.0
|
var panelHeight: CGFloat = 40.0
|
||||||
|
|
||||||
let contentRightInset: CGFloat = 14.0 + rightInset
|
let contentRightInset: CGFloat = 14.0 + rightInset
|
||||||
|
|
||||||
@ -190,8 +297,42 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: panelHeight)))
|
||||||
|
|
||||||
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel)))
|
||||||
|
|
||||||
|
var chatPeer: Peer?
|
||||||
|
if let renderedPeer = interfaceState.renderedPeer {
|
||||||
|
chatPeer = renderedPeer.peers[renderedPeer.peerId]
|
||||||
|
}
|
||||||
|
if let chatPeer = chatPeer, let invitedBy = interfaceState.contactStatus?.invitedBy {
|
||||||
|
var inviteInfoTransition = transition
|
||||||
|
let inviteInfoNode: ChatInfoTitlePanelInviteInfoNode
|
||||||
|
if let current = self.inviteInfoNode {
|
||||||
|
inviteInfoNode = current
|
||||||
|
} else {
|
||||||
|
inviteInfoTransition = .immediate
|
||||||
|
inviteInfoNode = ChatInfoTitlePanelInviteInfoNode(openInvitePeer: { [weak self] in
|
||||||
|
self?.interfaceInteraction?.navigateToProfile(invitedBy.id)
|
||||||
|
})
|
||||||
|
self.addSubnode(inviteInfoNode)
|
||||||
|
self.inviteInfoNode = inviteInfoNode
|
||||||
|
inviteInfoNode.alpha = 0.0
|
||||||
|
transition.updateAlpha(node: inviteInfoNode, alpha: 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let inviteInfoNode = self.inviteInfoNode {
|
||||||
|
let inviteHeight = inviteInfoNode.update(width: width, theme: interfaceState.theme, strings: interfaceState.strings, wallpaper: interfaceState.chatWallpaper, chatPeer: chatPeer, invitedBy: invitedBy, transition: inviteInfoTransition)
|
||||||
|
inviteInfoTransition.updateFrame(node: inviteInfoNode, frame: CGRect(origin: CGPoint(x: 0.0, y: panelHeight), size: CGSize(width: width, height: inviteHeight)))
|
||||||
|
panelHeight += inviteHeight
|
||||||
|
}
|
||||||
|
} else if let inviteInfoNode = self.inviteInfoNode {
|
||||||
|
self.inviteInfoNode = nil
|
||||||
|
transition.updateAlpha(node: inviteInfoNode, alpha: 0.0, completion: { [weak inviteInfoNode] _ in
|
||||||
|
inviteInfoNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return panelHeight
|
return panelHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,15 +15,17 @@ class PaneGifSearchForQueryResult {
|
|||||||
let files: [MultiplexedVideoNodeFile]
|
let files: [MultiplexedVideoNodeFile]
|
||||||
let nextOffset: String?
|
let nextOffset: String?
|
||||||
let isComplete: Bool
|
let isComplete: Bool
|
||||||
|
let isStale: Bool
|
||||||
|
|
||||||
init(files: [MultiplexedVideoNodeFile], nextOffset: String?, isComplete: Bool) {
|
init(files: [MultiplexedVideoNodeFile], nextOffset: String?, isComplete: Bool, isStale: Bool) {
|
||||||
self.files = files
|
self.files = files
|
||||||
self.nextOffset = nextOffset
|
self.nextOffset = nextOffset
|
||||||
self.isComplete = isComplete
|
self.isComplete = isComplete
|
||||||
|
self.isStale = isStale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func paneGifSearchForQuery(account: Account, query: String, offset: String?, incompleteResults: Bool = false, delayRequest: Bool = true, updateActivity: ((Bool) -> Void)?) -> Signal<PaneGifSearchForQueryResult?, NoError> {
|
func paneGifSearchForQuery(account: Account, query: String, offset: String?, incompleteResults: Bool = false, staleCachedResults: Bool = false, delayRequest: Bool = true, updateActivity: ((Bool) -> Void)?) -> Signal<PaneGifSearchForQueryResult?, NoError> {
|
||||||
let contextBot = account.postbox.transaction { transaction -> String in
|
let contextBot = account.postbox.transaction { transaction -> String in
|
||||||
let configuration = currentSearchBotsConfiguration(transaction: transaction)
|
let configuration = currentSearchBotsConfiguration(transaction: transaction)
|
||||||
return configuration.gifBotUsername ?? "gif"
|
return configuration.gifBotUsername ?? "gif"
|
||||||
@ -42,14 +44,14 @@ func paneGifSearchForQuery(account: Account, query: String, offset: String?, inc
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?, Bool), NoError> in
|
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?, Bool, Bool), NoError> in
|
||||||
if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
|
if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
|
||||||
let results = requestContextResults(account: account, botId: user.id, query: query, peerId: account.peerId, offset: offset ?? "", incompleteResults: incompleteResults, limit: 1)
|
let results = requestContextResults(account: account, botId: user.id, query: query, peerId: account.peerId, offset: offset ?? "", incompleteResults: incompleteResults, staleCachedResults: staleCachedResults, limit: 1)
|
||||||
|> map { results -> (ChatPresentationInputQueryResult?, Bool) in
|
|> map { results -> (ChatPresentationInputQueryResult?, Bool, Bool) in
|
||||||
return (.contextRequestResult(user, results), results != nil)
|
return (.contextRequestResult(user, results?.results), results != nil, results?.isStale ?? false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybeDelayedContextResults: Signal<(ChatPresentationInputQueryResult?, Bool), NoError>
|
let maybeDelayedContextResults: Signal<(ChatPresentationInputQueryResult?, Bool, Bool), NoError>
|
||||||
if delayRequest {
|
if delayRequest {
|
||||||
maybeDelayedContextResults = results |> delay(0.4, queue: Queue.concurrentDefaultQueue())
|
maybeDelayedContextResults = results |> delay(0.4, queue: Queue.concurrentDefaultQueue())
|
||||||
} else {
|
} else {
|
||||||
@ -58,7 +60,7 @@ func paneGifSearchForQuery(account: Account, query: String, offset: String?, inc
|
|||||||
|
|
||||||
return maybeDelayedContextResults
|
return maybeDelayedContextResults
|
||||||
} else {
|
} else {
|
||||||
return .single((nil, true))
|
return .single((nil, true, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contextBot
|
return contextBot
|
||||||
@ -111,7 +113,7 @@ func paneGifSearchForQuery(account: Account, query: String, offset: String?, inc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .single(PaneGifSearchForQueryResult(files: references, nextOffset: collection.nextOffset, isComplete: result.1))
|
return .single(PaneGifSearchForQueryResult(files: references, nextOffset: collection.nextOffset, isComplete: result.1, isStale: result.2))
|
||||||
} else if incompleteResults {
|
} else if incompleteResults {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
} else {
|
} else {
|
||||||
@ -232,7 +234,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
|
|||||||
} else {
|
} else {
|
||||||
strongSelf.nextOffset = nil
|
strongSelf.nextOffset = nil
|
||||||
}
|
}
|
||||||
strongSelf.multiplexedNode?.setFiles(files: MultiplexedVideoNodeFiles(saved: [], trending: result, isSearch: true, canLoadMore: false), synchronous: true, resetScrollingToOffset: nil)
|
strongSelf.multiplexedNode?.setFiles(files: MultiplexedVideoNodeFiles(saved: [], trending: result, isSearch: true, canLoadMore: false, isStale: false), synchronous: true, resetScrollingToOffset: nil)
|
||||||
strongSelf.updateActivity?(false)
|
strongSelf.updateActivity?(false)
|
||||||
strongSelf.notFoundNode.isHidden = text.isEmpty || !result.isEmpty
|
strongSelf.notFoundNode.isHidden = text.isEmpty || !result.isEmpty
|
||||||
}))
|
}))
|
||||||
@ -279,7 +281,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode {
|
|||||||
} else {
|
} else {
|
||||||
strongSelf.nextOffset = nil
|
strongSelf.nextOffset = nil
|
||||||
}
|
}
|
||||||
strongSelf.multiplexedNode?.setFiles(files: MultiplexedVideoNodeFiles(saved: [], trending: files, isSearch: true, canLoadMore: false), synchronous: true, resetScrollingToOffset: nil)
|
strongSelf.multiplexedNode?.setFiles(files: MultiplexedVideoNodeFiles(saved: [], trending: files, isSearch: true, canLoadMore: false, isStale: false), synchronous: true, resetScrollingToOffset: nil)
|
||||||
strongSelf.notFoundNode.isHidden = text.isEmpty || !files.isEmpty
|
strongSelf.notFoundNode.isHidden = text.isEmpty || !files.isEmpty
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -217,6 +217,9 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
|
|||||||
return (geoPoint.latitude, geoPoint.longitude)
|
return (geoPoint.latitude, geoPoint.longitude)
|
||||||
}
|
}
|
||||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(geoPoint), offset: nextOffset)
|
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(geoPoint), offset: nextOffset)
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] nextResults in
|
|> deliverOnMainQueue).start(next: { [weak self] nextResults in
|
||||||
guard let strongSelf = self, let nextResults = nextResults else {
|
guard let strongSelf = self, let nextResults = nextResults else {
|
||||||
return
|
return
|
||||||
|
@ -9,6 +9,7 @@ import Postbox
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import StickerPackPreviewUI
|
||||||
|
|
||||||
private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollViewDelegate {
|
private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollViewDelegate {
|
||||||
private final class DisplayItem {
|
private final class DisplayItem {
|
||||||
@ -23,6 +24,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
|
private var strings: PresentationStrings
|
||||||
|
|
||||||
private let scrollNode: ASScrollNode
|
private let scrollNode: ASScrollNode
|
||||||
private var items: [TelegramMediaFile] = []
|
private var items: [TelegramMediaFile] = []
|
||||||
@ -34,12 +36,17 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
private var ignoreScrolling: Bool = false
|
private var ignoreScrolling: Bool = false
|
||||||
private var animateInOnLayout: Bool = false
|
private var animateInOnLayout: Bool = false
|
||||||
|
|
||||||
|
var previewedStickerItem: StickerPackItem?
|
||||||
|
|
||||||
var updateBackgroundOffset: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
var updateBackgroundOffset: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
|
||||||
var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)?
|
var sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Void)?
|
||||||
|
|
||||||
init(context: AccountContext, theme: PresentationTheme) {
|
var getControllerInteraction: (() -> ChatControllerInteraction?)?
|
||||||
|
|
||||||
|
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
|
||||||
self.scrollNode = ASScrollNode()
|
self.scrollNode = ASScrollNode()
|
||||||
|
|
||||||
@ -56,6 +63,112 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
self.addSubnode(self.scrollNode)
|
self.addSubnode(self.scrollNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
self.view.addGestureRecognizer(PeekControllerGestureRecognizer(contentAtPoint: { [weak self] point in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let convertedPoint = strongSelf.scrollNode.view.convert(point, from: strongSelf.view)
|
||||||
|
guard strongSelf.scrollNode.view.bounds.contains(convertedPoint) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedNode: HorizontalStickerGridItemNode?
|
||||||
|
for (_, node) in strongSelf.itemNodes {
|
||||||
|
if node.frame.contains(convertedPoint) {
|
||||||
|
selectedNode = node
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let itemNode = selectedNode, let item = itemNode.stickerItem {
|
||||||
|
return strongSelf.context.account.postbox.transaction { transaction -> Bool in
|
||||||
|
return getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
|
||||||
|
}
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
|> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in
|
||||||
|
if let strongSelf = self, let controllerInteraction = strongSelf.getControllerInteraction?() {
|
||||||
|
var menuItems: [PeekControllerMenuItem] = []
|
||||||
|
menuItems = [
|
||||||
|
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_Send, color: .accent, font: .bold, action: { _, _ in
|
||||||
|
return controllerInteraction.sendSticker(.standalone(media: item.file), true, itemNode, itemNode.bounds)
|
||||||
|
}),
|
||||||
|
PeekControllerMenuItem(title: isStarred ? strongSelf.strings.Stickers_RemoveFromFavorites : strongSelf.strings.Stickers_AddToFavorites, color: isStarred ? .destructive : .accent, action: { _, _ in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if isStarred {
|
||||||
|
let _ = removeSavedSticker(postbox: strongSelf.context.account.postbox, mediaId: item.file.fileId).start()
|
||||||
|
} else {
|
||||||
|
let _ = addSavedSticker(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, file: item.file).start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}),
|
||||||
|
PeekControllerMenuItem(title: strongSelf.strings.StickerPack_ViewPack, color: .accent, action: { _, _ in
|
||||||
|
if let strongSelf = self, let controllerInteraction = strongSelf.getControllerInteraction?() {
|
||||||
|
loop: for attribute in item.file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .Sticker(_, packReference, _):
|
||||||
|
if let packReference = packReference {
|
||||||
|
let controller = StickerPackScreen(context: strongSelf.context, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: controllerInteraction.navigationController(), sendSticker: { file, sourceNode, sourceRect in
|
||||||
|
if let strongSelf = self, let controllerInteraction = strongSelf.getControllerInteraction?() {
|
||||||
|
return controllerInteraction.sendSticker(file, true, sourceNode, sourceRect)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
controllerInteraction.navigationController()?.view.window?.endEditing(true)
|
||||||
|
controllerInteraction.presentController(controller, nil)
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}),
|
||||||
|
PeekControllerMenuItem(title: strongSelf.strings.Common_Cancel, color: .accent, font: .bold, action: { _, _ in return true })
|
||||||
|
]
|
||||||
|
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, item: .pack(item), menu: menuItems))
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, present: { [weak self] content, sourceNode in
|
||||||
|
if let strongSelf = self {
|
||||||
|
let controller = PeekController(theme: PeekControllerTheme(presentationTheme: strongSelf.theme), content: content, sourceNode: {
|
||||||
|
return sourceNode
|
||||||
|
})
|
||||||
|
strongSelf.getControllerInteraction?()?.presentGlobalOverlayController(controller, nil)
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, updateContent: { [weak self] content in
|
||||||
|
if let strongSelf = self {
|
||||||
|
var item: StickerPackItem?
|
||||||
|
if let content = content as? StickerPreviewPeekContent, case let .pack(contentItem) = content.item {
|
||||||
|
item = contentItem
|
||||||
|
}
|
||||||
|
strongSelf.updatePreviewingItem(item: item, animated: true)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updatePreviewingItem(item: StickerPackItem?, animated: Bool) {
|
||||||
|
if self.previewedStickerItem != item {
|
||||||
|
self.previewedStickerItem = item
|
||||||
|
|
||||||
|
for (_, itemNode) in self.itemNodes {
|
||||||
|
itemNode.updatePreviewing(animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
if !self.ignoreScrolling {
|
if !self.ignoreScrolling {
|
||||||
self.updateVisibleItems(synchronous: false)
|
self.updateVisibleItems(synchronous: false)
|
||||||
@ -223,8 +336,8 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
|
|||||||
account: self.context.account,
|
account: self.context.account,
|
||||||
file: item.file,
|
file: item.file,
|
||||||
theme: self.theme,
|
theme: self.theme,
|
||||||
isPreviewed: { _ in
|
isPreviewed: { [weak self] item in
|
||||||
return false
|
return item.file.fileId == self?.previewedStickerItem?.file.fileId
|
||||||
}, sendSticker: { [weak self] file, node, rect in
|
}, sendSticker: { [weak self] file, node, rect in
|
||||||
self?.sendSticker?(file, node, rect)
|
self?.sendSticker?(file, node, rect)
|
||||||
}
|
}
|
||||||
@ -321,7 +434,7 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
|
|||||||
|
|
||||||
self.backgroundContainerNode = ASDisplayNode()
|
self.backgroundContainerNode = ASDisplayNode()
|
||||||
|
|
||||||
self.stickersNode = InlineReactionSearchStickersNode(context: context, theme: theme)
|
self.stickersNode = InlineReactionSearchStickersNode(context: context, theme: theme, strings: strings)
|
||||||
|
|
||||||
super.init(context: context, theme: theme, strings: strings, fontSize: fontSize)
|
super.init(context: context, theme: theme, strings: strings, fontSize: fontSize)
|
||||||
|
|
||||||
@ -339,6 +452,10 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
|
|||||||
|
|
||||||
self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor
|
self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor
|
||||||
|
|
||||||
|
self.stickersNode.getControllerInteraction = { [weak self] in
|
||||||
|
return self?.controllerInteraction
|
||||||
|
}
|
||||||
|
|
||||||
self.stickersNode.updateBackgroundOffset = { [weak self] offset, transition in
|
self.stickersNode.updateBackgroundOffset = { [weak self] offset, transition in
|
||||||
guard let strongSelf = self, let (_, _) = strongSelf.validLayout else {
|
guard let strongSelf = self, let (_, _) = strongSelf.validLayout else {
|
||||||
return
|
return
|
||||||
@ -362,6 +479,11 @@ final class InlineReactionSearchPanel: ChatInputContextPanelNode {
|
|||||||
self.view.disablesInteractiveKeyboardGestureRecognizer = true
|
self.view.disablesInteractiveKeyboardGestureRecognizer = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func updateResults(results: [TelegramMediaFile]) {
|
func updateResults(results: [TelegramMediaFile]) {
|
||||||
self.stickersNode.updateItems(items: results)
|
self.stickersNode.updateItems(items: results)
|
||||||
}
|
}
|
||||||
|
@ -244,11 +244,13 @@ final class ListMessageSnippetItemNode: ListMessageNode {
|
|||||||
isInstantView = true
|
isInstantView = true
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryUrl = content.url
|
let (parsedUrl, _) = parseUrl(url: content.url)
|
||||||
|
|
||||||
|
primaryUrl = parsedUrl
|
||||||
|
|
||||||
processed = true
|
processed = true
|
||||||
var hostName: String = ""
|
var hostName: String = ""
|
||||||
if let url = URL(string: content.url), let host = url.host, !host.isEmpty {
|
if let url = URL(string: parsedUrl), let host = url.host, !host.isEmpty {
|
||||||
hostName = host
|
hostName = host
|
||||||
iconText = NSAttributedString(string: host[..<host.index(after: host.startIndex)].uppercased(), font: iconFont, textColor: UIColor.white)
|
iconText = NSAttributedString(string: host[..<host.index(after: host.startIndex)].uppercased(), font: iconFont, textColor: UIColor.white)
|
||||||
}
|
}
|
||||||
@ -312,14 +314,18 @@ final class ListMessageSnippetItemNode: ListMessageNode {
|
|||||||
range.location = max(0, nsString.length - range.length)
|
range.location = max(0, nsString.length - range.length)
|
||||||
range.length = nsString.length - range.location
|
range.length = nsString.length - range.location
|
||||||
}
|
}
|
||||||
var urlString = nsString.substring(with: range)
|
let tempUrlString = nsString.substring(with: range)
|
||||||
|
|
||||||
|
var (urlString, concealed) = parseUrl(url: tempUrlString)
|
||||||
|
|
||||||
let rawUrlString = urlString
|
let rawUrlString = urlString
|
||||||
var parsedUrl = URL(string: urlString)
|
var parsedUrl = URL(string: urlString)
|
||||||
if parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty {
|
if parsedUrl == nil || parsedUrl!.host == nil || parsedUrl!.host!.isEmpty {
|
||||||
urlString = "http://" + urlString
|
urlString = "http://" + urlString
|
||||||
parsedUrl = URL(string: urlString)
|
parsedUrl = URL(string: urlString)
|
||||||
}
|
}
|
||||||
if let url = parsedUrl, let host = url.host {
|
let host: String? = concealed ? urlString : parsedUrl?.host
|
||||||
|
if let url = parsedUrl, let host = host {
|
||||||
primaryUrl = urlString
|
primaryUrl = urlString
|
||||||
if url.path.hasPrefix("/addstickers/") {
|
if url.path.hasPrefix("/addstickers/") {
|
||||||
title = NSAttributedString(string: urlString, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor)
|
title = NSAttributedString(string: urlString, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor)
|
||||||
@ -331,7 +337,10 @@ final class ListMessageSnippetItemNode: ListMessageNode {
|
|||||||
title = NSAttributedString(string: host, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor)
|
title = NSAttributedString(string: host, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor)
|
||||||
}
|
}
|
||||||
let mutableDescriptionText = NSMutableAttributedString()
|
let mutableDescriptionText = NSMutableAttributedString()
|
||||||
if item.message.text != rawUrlString {
|
|
||||||
|
let (messageTextUrl, _) = parseUrl(url: item.message.text)
|
||||||
|
|
||||||
|
if messageTextUrl != rawUrlString {
|
||||||
mutableDescriptionText.append(NSAttributedString(string: item.message.text + "\n", font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor))
|
mutableDescriptionText.append(NSAttributedString(string: item.message.text + "\n", font: descriptionFont, textColor: item.theme.list.itemSecondaryTextColor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,12 +89,14 @@ final class MultiplexedVideoNodeFiles {
|
|||||||
let trending: [MultiplexedVideoNodeFile]
|
let trending: [MultiplexedVideoNodeFile]
|
||||||
let isSearch: Bool
|
let isSearch: Bool
|
||||||
let canLoadMore: Bool
|
let canLoadMore: Bool
|
||||||
|
let isStale: Bool
|
||||||
|
|
||||||
init(saved: [MultiplexedVideoNodeFile], trending: [MultiplexedVideoNodeFile], isSearch: Bool, canLoadMore: Bool) {
|
init(saved: [MultiplexedVideoNodeFile], trending: [MultiplexedVideoNodeFile], isSearch: Bool, canLoadMore: Bool, isStale: Bool) {
|
||||||
self.saved = saved
|
self.saved = saved
|
||||||
self.trending = trending
|
self.trending = trending
|
||||||
self.isSearch = isSearch
|
self.isSearch = isSearch
|
||||||
self.canLoadMore = canLoadMore
|
self.canLoadMore = canLoadMore
|
||||||
|
self.isStale = isStale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +127,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private(set) var files: MultiplexedVideoNodeFiles = MultiplexedVideoNodeFiles(saved: [], trending: [], isSearch: false, canLoadMore: false)
|
private(set) var files: MultiplexedVideoNodeFiles = MultiplexedVideoNodeFiles(saved: [], trending: [], isSearch: false, canLoadMore: false, isStale: false)
|
||||||
|
|
||||||
func setFiles(files: MultiplexedVideoNodeFiles, synchronous: Bool, resetScrollingToOffset: CGFloat?) {
|
func setFiles(files: MultiplexedVideoNodeFiles, synchronous: Bool, resetScrollingToOffset: CGFloat?) {
|
||||||
self.files = files
|
self.files = files
|
||||||
@ -248,7 +250,11 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let (file, rect, isSaved) = strongSelf.fileAt(point: gestureLocation) {
|
if let (file, rect, isSaved) = strongSelf.fileAt(point: gestureLocation) {
|
||||||
strongSelf.fileContextMenu?(file, strongSelf, rect.offsetBy(dx: 0.0, dy: -strongSelf.scrollNode.bounds.minY), gesture, isSaved)
|
if !strongSelf.files.isStale {
|
||||||
|
strongSelf.fileContextMenu?(file, strongSelf, rect.offsetBy(dx: 0.0, dy: -strongSelf.scrollNode.bounds.minY), gesture, isSaved)
|
||||||
|
} else {
|
||||||
|
gesture.cancel()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
gesture.cancel()
|
gesture.cancel()
|
||||||
}
|
}
|
||||||
@ -682,7 +688,9 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
if case .ended = recognizer.state {
|
if case .ended = recognizer.state {
|
||||||
let point = recognizer.location(in: self.view)
|
let point = recognizer.location(in: self.view)
|
||||||
if let (file, rect, _) = self.fileAt(point: point) {
|
if let (file, rect, _) = self.fileAt(point: point) {
|
||||||
self.fileSelected?(file, self, rect)
|
if !self.files.isStale {
|
||||||
|
self.fileSelected?(file, self, rect)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
|||||||
let textNodeFrame = self.textNode.frame
|
let textNodeFrame = self.textNode.frame
|
||||||
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
return .url(url)
|
return .url(url: url, concealed: false)
|
||||||
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
||||||
return .mention(peerName)
|
return .mention(peerName)
|
||||||
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
||||||
|
@ -1674,7 +1674,17 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry)
|
self.avatarListNode.listContainerNode.updateEntryIsHidden(entry: entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, contentOffset: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, isContact: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
|
func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, notificationSettings: TelegramPeerNotificationSettings?, statusData: PeerInfoStatusData?, isContact: Bool, state: PeerInfoState, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat {
|
||||||
|
var contentOffset = contentOffset
|
||||||
|
|
||||||
|
if isMediaOnly {
|
||||||
|
if isModalOverlay {
|
||||||
|
contentOffset = 312.0
|
||||||
|
} else {
|
||||||
|
contentOffset = 212.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let themeUpdated = self.presentationData?.theme !== presentationData.theme
|
let themeUpdated = self.presentationData?.theme !== presentationData.theme
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
|
||||||
|
@ -367,6 +367,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
|||||||
}, toggleMembersSearch: { _ in
|
}, toggleMembersSearch: { _ in
|
||||||
}, navigateToMessage: { _ in
|
}, navigateToMessage: { _ in
|
||||||
}, navigateToChat: { _ in
|
}, navigateToChat: { _ in
|
||||||
|
}, navigateToProfile: { _ in
|
||||||
}, openPeerInfo: {
|
}, openPeerInfo: {
|
||||||
}, togglePeerNotifications: {
|
}, togglePeerNotifications: {
|
||||||
}, sendContextResult: { _, _, _, _ in
|
}, sendContextResult: { _, _, _, _ in
|
||||||
@ -1484,11 +1485,11 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, requestMessageActionCallback: { _, _, _ in
|
}, requestMessageActionCallback: { _, _, _ in
|
||||||
}, requestMessageActionUrlAuth: { _, _, _ in
|
}, requestMessageActionUrlAuth: { _, _, _ in
|
||||||
}, activateSwitchInline: { _, _ in
|
}, activateSwitchInline: { _, _ in
|
||||||
}, openUrl: { [weak self] url, _, external, _ in
|
}, openUrl: { [weak self] url, concealed, external, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.openUrl(url: url, external: external ?? false)
|
strongSelf.openUrl(url: url, concealed: concealed, external: external ?? false)
|
||||||
}, shareCurrentLocation: {
|
}, shareCurrentLocation: {
|
||||||
}, shareAccountContact: {
|
}, shareAccountContact: {
|
||||||
}, sendBotCommand: { _, _ in
|
}, sendBotCommand: { _, _ in
|
||||||
@ -2129,7 +2130,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}
|
}
|
||||||
strongSelf.paneContainerNode.currentPane?.node.addToTransitionSurface(view: view)
|
strongSelf.paneContainerNode.currentPane?.node.addToTransitionSurface(view: view)
|
||||||
}, openUrl: { [weak self] url in
|
}, openUrl: { [weak self] url in
|
||||||
self?.openUrl(url: url, external: false)
|
self?.openUrl(url: url, concealed: false, external: false)
|
||||||
}, openPeer: { [weak self] peer, navigation in
|
}, openPeer: { [weak self] peer, navigation in
|
||||||
self?.openPeer(peerId: peer.id, navigation: navigation)
|
self?.openPeer(peerId: peer.id, navigation: navigation)
|
||||||
}, callPeer: { peerId in
|
}, callPeer: { peerId in
|
||||||
@ -2138,7 +2139,27 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }))
|
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openUrl(url: String, external: Bool) {
|
private func openUrl(url: String, concealed: Bool, external: Bool) {
|
||||||
|
openUserGeneratedUrl(context: self.context, url: url, concealed: concealed, present: { [weak self] c in
|
||||||
|
self?.controller?.present(c, in: .window(.root))
|
||||||
|
}, openResolved: { [weak self] tempResolved in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: ResolvedUrl = external ? .externalUrl(url) : tempResolved
|
||||||
|
|
||||||
|
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peerId, navigation in
|
||||||
|
self?.openPeer(peerId: peerId, navigation: navigation)
|
||||||
|
}, sendFile: nil,
|
||||||
|
sendSticker: nil,
|
||||||
|
present: { c, a in
|
||||||
|
self?.controller?.present(c, in: .window(.root), with: a)
|
||||||
|
}, dismissInput: {
|
||||||
|
self?.view.endEditing(true)
|
||||||
|
}, contentContext: nil)
|
||||||
|
})
|
||||||
|
|
||||||
let disposable = self.resolveUrlDisposable
|
let disposable = self.resolveUrlDisposable
|
||||||
|
|
||||||
let resolvedUrl: Signal<ResolvedUrl, NoError>
|
let resolvedUrl: Signal<ResolvedUrl, NoError>
|
||||||
@ -2147,22 +2168,6 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
} else {
|
} else {
|
||||||
resolvedUrl = self.context.sharedContext.resolveUrl(account: self.context.account, url: url)
|
resolvedUrl = self.context.sharedContext.resolveUrl(account: self.context.account, url: url)
|
||||||
}
|
}
|
||||||
|
|
||||||
disposable.set((resolvedUrl
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
|
||||||
guard let strongSelf = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peerId, navigation in
|
|
||||||
self?.openPeer(peerId: peerId, navigation: navigation)
|
|
||||||
}, sendFile: nil,
|
|
||||||
sendSticker: nil,
|
|
||||||
present: { c, a in
|
|
||||||
self?.controller?.present(c, in: .window(.root), with: a)
|
|
||||||
}, dismissInput: {
|
|
||||||
self?.view.endEditing(true)
|
|
||||||
}, contentContext: nil)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func openPeer(peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer) {
|
private func openPeer(peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer) {
|
||||||
@ -3807,7 +3812,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
|
|
||||||
var contentHeight: CGFloat = 0.0
|
var contentHeight: CGFloat = 0.0
|
||||||
|
|
||||||
let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, isContact: self.data?.isContact ?? false, state: self.state, transition: transition, additive: additive)
|
let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, isContact: self.data?.isContact ?? false, state: self.state, transition: transition, additive: additive)
|
||||||
let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: headerHeight))
|
let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: headerHeight))
|
||||||
if additive {
|
if additive {
|
||||||
transition.updateFrameAdditive(node: self.headerNode, frame: headerFrame)
|
transition.updateFrameAdditive(node: self.headerNode, frame: headerFrame)
|
||||||
@ -4040,7 +4045,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
|
|
||||||
if let (layout, navigationHeight) = self.validLayout {
|
if let (layout, navigationHeight) = self.validLayout {
|
||||||
if !additive {
|
if !additive {
|
||||||
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, contentOffset: self.isMediaOnly ? 212.0 : offsetY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, isContact: self.data?.isContact ?? false, state: self.state, transition: transition, additive: additive)
|
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, notificationSettings: self.data?.notificationSettings, statusData: self.data?.status, isContact: self.data?.isContact ?? false, state: self.state, transition: transition, additive: additive)
|
||||||
}
|
}
|
||||||
|
|
||||||
let paneAreaExpansionDistance: CGFloat = 32.0
|
let paneAreaExpansionDistance: CGFloat = 32.0
|
||||||
@ -4541,7 +4546,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig
|
|||||||
|
|
||||||
self.headerNode.navigationTransition = PeerInfoHeaderNavigationTransition(sourceNavigationBar: bottomNavigationBar, sourceTitleView: previousTitleView, sourceTitleFrame: previousTitleFrame, sourceSubtitleFrame: previousStatusFrame, fraction: fraction)
|
self.headerNode.navigationTransition = PeerInfoHeaderNavigationTransition(sourceNavigationBar: bottomNavigationBar, sourceTitleView: previousTitleView, sourceTitleFrame: previousTitleFrame, sourceSubtitleFrame: previousStatusFrame, fraction: fraction)
|
||||||
if let (layout, _) = self.screenNode.validLayout {
|
if let (layout, _) = self.screenNode.validLayout {
|
||||||
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, contentOffset: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, isContact: self.screenNode.data?.isContact ?? false, state: self.screenNode.state, transition: transition, additive: false)
|
let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: layout.safeInsets.left, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, notificationSettings: self.screenNode.data?.notificationSettings, statusData: self.screenNode.data?.status, isContact: self.screenNode.data?.isContact ?? false, state: self.screenNode.state, transition: transition, additive: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleScale = (fraction * previousTitleNode.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.bounds.height
|
let titleScale = (fraction * previousTitleNode.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.bounds.height
|
||||||
|
@ -502,6 +502,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
|||||||
}, toggleMembersSearch: { _ in
|
}, toggleMembersSearch: { _ in
|
||||||
}, navigateToMessage: { _ in
|
}, navigateToMessage: { _ in
|
||||||
}, navigateToChat: { _ in
|
}, navigateToChat: { _ in
|
||||||
|
}, navigateToProfile: { _ in
|
||||||
}, openPeerInfo: {
|
}, openPeerInfo: {
|
||||||
}, togglePeerNotifications: {
|
}, togglePeerNotifications: {
|
||||||
}, sendContextResult: { _, _, _, _ in
|
}, sendContextResult: { _, _, _, _ in
|
||||||
|
@ -14,6 +14,7 @@ import InstantPageUI
|
|||||||
import HashtagSearchUI
|
import HashtagSearchUI
|
||||||
import StickerPackPreviewUI
|
import StickerPackPreviewUI
|
||||||
import JoinLinkPreviewUI
|
import JoinLinkPreviewUI
|
||||||
|
import PresentationDataUtils
|
||||||
|
|
||||||
func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigateDisposable: MetaDisposable, controller: ViewController, action: TextLinkItemActionType, itemLink: TextLinkItem) {
|
func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigateDisposable: MetaDisposable, controller: ViewController, action: TextLinkItemActionType, itemLink: TextLinkItem) {
|
||||||
let presentImpl: (ViewController, Any?) -> Void = { controllerToPresent, _ in
|
let presentImpl: (ViewController, Any?) -> Void = { controllerToPresent, _ in
|
||||||
@ -89,8 +90,26 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
|||||||
switch action {
|
switch action {
|
||||||
case .tap:
|
case .tap:
|
||||||
switch itemLink {
|
switch itemLink {
|
||||||
case let .url(url):
|
case .url(let url, var concealed):
|
||||||
openLinkImpl(url)
|
let (parsedString, parsedConcealed) = parseUrl(url: url)
|
||||||
|
if parsedConcealed {
|
||||||
|
concealed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if concealed {
|
||||||
|
var rawDisplayUrl: String = parsedString
|
||||||
|
let maxLength = 180
|
||||||
|
if rawDisplayUrl.count > maxLength {
|
||||||
|
rawDisplayUrl = String(rawDisplayUrl[..<rawDisplayUrl.index(rawDisplayUrl.startIndex, offsetBy: maxLength - 2)]) + "..."
|
||||||
|
}
|
||||||
|
var displayUrl = rawDisplayUrl
|
||||||
|
displayUrl = displayUrl.replacingOccurrences(of: "\u{202e}", with: "")
|
||||||
|
controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.Generic_OpenHiddenLinkAlert(displayUrl).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: {
|
||||||
|
openLinkImpl(url)
|
||||||
|
})]), in: .window(.root))
|
||||||
|
} else {
|
||||||
|
openLinkImpl(url)
|
||||||
|
}
|
||||||
case let .mention(mention):
|
case let .mention(mention):
|
||||||
openPeerMentionImpl(mention)
|
openPeerMentionImpl(mention)
|
||||||
case let .hashtag(_, hashtag):
|
case let .hashtag(_, hashtag):
|
||||||
@ -105,12 +124,13 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate
|
|||||||
}
|
}
|
||||||
case .longTap:
|
case .longTap:
|
||||||
switch itemLink {
|
switch itemLink {
|
||||||
case let .url(url):
|
case let .url(url, _):
|
||||||
let canOpenIn = availableOpenInOptions(context: context, item: .url(url: url)).count > 1
|
let canOpenIn = availableOpenInOptions(context: context, item: .url(url: url)).count > 1
|
||||||
let openText = canOpenIn ? presentationData.strings.Conversation_FileOpenIn : presentationData.strings.Conversation_LinkDialogOpen
|
let openText = canOpenIn ? presentationData.strings.Conversation_FileOpenIn : presentationData.strings.Conversation_LinkDialogOpen
|
||||||
let actionSheet = ActionSheetController(presentationData: presentationData)
|
let actionSheet = ActionSheetController(presentationData: presentationData)
|
||||||
|
let (displayUrl, _) = parseUrl(url: url)
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||||
ActionSheetTextItem(title: url),
|
ActionSheetTextItem(title: displayUrl),
|
||||||
ActionSheetButtonItem(title: openText, color: .accent, action: { [weak actionSheet] in
|
ActionSheetButtonItem(title: openText, color: .accent, action: { [weak actionSheet] in
|
||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
openLinkImpl(url)
|
openLinkImpl(url)
|
||||||
|
@ -335,6 +335,9 @@ final class VerticalListContextResultsChatInputContextPanelNode: ChatInputContex
|
|||||||
return (geoPoint.latitude, geoPoint.longitude)
|
return (geoPoint.latitude, geoPoint.longitude)
|
||||||
}
|
}
|
||||||
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(geoPoint), offset: nextOffset)
|
self.loadMoreDisposable.set((requestChatContextResults(account: self.context.account, botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, location: .single(geoPoint), offset: nextOffset)
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] nextResults in
|
|> deliverOnMainQueue).start(next: { [weak self] nextResults in
|
||||||
guard let strongSelf = self, let nextResults = nextResults else {
|
guard let strongSelf = self, let nextResults = nextResults else {
|
||||||
return
|
return
|
||||||
|
@ -392,7 +392,7 @@ class UpdateInfoItemNode: ListViewItemNode {
|
|||||||
let textNodeFrame = self.textNode.frame
|
let textNodeFrame = self.textNode.frame
|
||||||
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
|
||||||
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
return .url(url)
|
return .url(url: url, concealed: false)
|
||||||
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
} else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
|
||||||
return .mention(peerName)
|
return .mention(peerName)
|
||||||
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
} else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
|
||||||
|
@ -756,6 +756,9 @@ final class WatchLocationHandler: WatchRequestHandler {
|
|||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
return requestChatContextResults(account: context.account, botId: peerId, peerId: context.account.peerId, query: "", location: .single((args.coordinate.latitude, args.coordinate.longitude)), offset: "")
|
return requestChatContextResults(account: context.account, botId: peerId, peerId: context.account.peerId, query: "", location: .single((args.coordinate.latitude, args.coordinate.longitude)), offset: "")
|
||||||
|
|> map { results -> ChatContextResultCollection? in
|
||||||
|
return results?.results
|
||||||
|
}
|
||||||
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,14 @@ import LegacyComponents
|
|||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public func requestContextResults(account: Account, botId: PeerId, query: String, peerId: PeerId, offset: String = "", existingResults: ChatContextResultCollection? = nil, incompleteResults: Bool = false, limit: Int = 60) -> Signal<ChatContextResultCollection?, NoError> {
|
public func requestContextResults(account: Account, botId: PeerId, query: String, peerId: PeerId, offset: String = "", existingResults: ChatContextResultCollection? = nil, incompleteResults: Bool = false, staleCachedResults: Bool = false, limit: Int = 60) -> Signal<RequestChatContextResultsResult?, NoError> {
|
||||||
return requestChatContextResults(account: account, botId: botId, peerId: peerId, query: query, offset: offset, incompleteResults: incompleteResults)
|
return requestChatContextResults(account: account, botId: botId, peerId: peerId, query: query, offset: offset, incompleteResults: incompleteResults, staleCachedResults: staleCachedResults)
|
||||||
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
|
|> `catch` { error -> Signal<RequestChatContextResultsResult?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|> mapToSignal { results -> Signal<ChatContextResultCollection?, NoError> in
|
|> mapToSignal { resultsStruct -> Signal<RequestChatContextResultsResult?, NoError> in
|
||||||
|
let results = resultsStruct?.results
|
||||||
|
|
||||||
var collection = existingResults
|
var collection = existingResults
|
||||||
var updated: Bool = false
|
var updated: Bool = false
|
||||||
if let existingResults = existingResults, let results = results {
|
if let existingResults = existingResults, let results = results {
|
||||||
@ -40,13 +42,15 @@ public func requestContextResults(account: Account, botId: PeerId, query: String
|
|||||||
if let collection = collection, collection.results.count < limit, let nextOffset = collection.nextOffset, updated {
|
if let collection = collection, collection.results.count < limit, let nextOffset = collection.nextOffset, updated {
|
||||||
let nextResults = requestContextResults(account: account, botId: botId, query: query, peerId: peerId, offset: nextOffset, existingResults: collection, limit: limit)
|
let nextResults = requestContextResults(account: account, botId: botId, query: query, peerId: peerId, offset: nextOffset, existingResults: collection, limit: limit)
|
||||||
if collection.results.count > 10 {
|
if collection.results.count > 10 {
|
||||||
return .single(collection)
|
return .single(RequestChatContextResultsResult(results: collection, isStale: resultsStruct?.isStale ?? false))
|
||||||
|> then(nextResults)
|
|> then(nextResults)
|
||||||
} else {
|
} else {
|
||||||
return nextResults
|
return nextResults
|
||||||
}
|
}
|
||||||
|
} else if let collection = collection {
|
||||||
|
return .single(RequestChatContextResultsResult(results: collection, isStale: resultsStruct?.isStale ?? false))
|
||||||
} else {
|
} else {
|
||||||
return .single(collection)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,7 +428,7 @@ public final class WebSearchController: ViewController {
|
|||||||
let results = requestContextResults(account: account, botId: user.id, query: query, peerId: peerId, limit: 64)
|
let results = requestContextResults(account: account, botId: user.id, query: query, peerId: peerId, limit: 64)
|
||||||
|> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
|> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||||
return { _ in
|
return { _ in
|
||||||
return .contextRequestResult(user, results)
|
return .contextRequestResult(user, results?.results)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,13 +559,13 @@ class WebSearchControllerNode: ASDisplayNode {
|
|||||||
results.append(result)
|
results.append(result)
|
||||||
existingIds.insert(result.id)
|
existingIds.insert(result.id)
|
||||||
}
|
}
|
||||||
for result in nextResults.results {
|
for result in nextResults.results.results {
|
||||||
if !existingIds.contains(result.id) {
|
if !existingIds.contains(result.id) {
|
||||||
results.append(result)
|
results.append(result)
|
||||||
existingIds.insert(result.id)
|
existingIds.insert(result.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.queryId, nextOffset: nextResults.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
|
let mergedResults = ChatContextResultCollection(botId: currentProcessedResults.botId, peerId: currentProcessedResults.peerId, query: currentProcessedResults.query, geoPoint: currentProcessedResults.geoPoint, queryId: nextResults.results.queryId, nextOffset: nextResults.results.nextOffset, presentation: currentProcessedResults.presentation, switchPeer: currentProcessedResults.switchPeer, results: results, cacheTimeout: currentProcessedResults.cacheTimeout)
|
||||||
strongSelf.currentProcessedResults = mergedResults
|
strongSelf.currentProcessedResults = mergedResults
|
||||||
strongSelf.results.set(mergedResults)
|
strongSelf.results.set(mergedResults)
|
||||||
}))
|
}))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user