Merge commit '72dd8f1151cfd34e40ef4cf3986a8b2dd42eeb23'

This commit is contained in:
Peter 2019-07-17 17:45:30 +01:00
commit 093ec63cc5
70 changed files with 4168 additions and 3839 deletions

View File

@ -228,9 +228,7 @@
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>dbapi-3</string>
<string>instagram</string>
<string>googledrive</string>
<string>comgooglemaps-x-callback</string>
<string>foursquare</string>
<string>here-location</string>
@ -257,6 +255,7 @@
<string>opera-https</string>
<string>firefox-focus</string>
<string>ddgQuickLink</string>
<string>moovit</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>

View File

@ -4471,3 +4471,6 @@ Any member of this group will be able to see messages in the channel.";
"Channel.TooMuchBots" = "Sorry, there are already too many bots in this group. Please remove some of the bots you're not using first.";
"Channel.BotDoesntSupportGroups" = "Sorry, this bot is telling us it doesn't want to be added to groups. You can't add this bot unless its developers change their mind.";
"StickerPacksSettings.AnimatedStickers" = "Loop Animated Stickers";
"StickerPacksSettings.AnimatedStickersInfo" = "Animated stickers will play in chat continuously.";

View File

@ -149,6 +149,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
public final var rotated = false
public final var experimentalSnapScrollToItem = false
public final var scrollEnabled: Bool = true {
didSet {
self.scroller.isScrollEnabled = self.scrollEnabled
}
}
private final var invisibleInset: CGFloat = 500.0
public var preloadPages: Bool = true {
didSet {

View File

@ -5,6 +5,14 @@ import CoreText
private let defaultFont = UIFont.systemFont(ofSize: 15.0)
private final class TextNodeStrikethrough {
let frame: CGRect
init(frame: CGRect) {
self.frame = frame
}
}
private final class TextNodeLine {
let line: CTLine
let frame: CGRect
@ -21,7 +29,7 @@ private final class TextNodeLine {
}
}
private final class TextNodeStrikethrough {
private final class TextNodeBlockQuote {
let frame: CGRect
init(frame: CGRect) {
@ -83,8 +91,9 @@ public final class TextNodeLayoutArguments {
public let lineSpacing: CGFloat
public let cutout: TextNodeCutout?
public let insets: UIEdgeInsets
public let lineColor: UIColor?
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets()) {
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil) {
self.attributedString = attributedString
self.backgroundColor = backgroundColor
self.maximumNumberOfLines = maximumNumberOfLines
@ -94,6 +103,7 @@ public final class TextNodeLayoutArguments {
self.lineSpacing = lineSpacing
self.cutout = cutout
self.insets = insets
self.lineColor = lineColor
}
}
@ -111,9 +121,11 @@ public final class TextNodeLayout: NSObject {
public let truncated: Bool
fileprivate let firstLineOffset: CGFloat
fileprivate let lines: [TextNodeLine]
fileprivate let blockQuotes: [TextNodeBlockQuote]
fileprivate let lineColor: UIColor?
public let hasRTL: Bool
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], backgroundColor: UIColor?) {
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?) {
self.attributedString = attributedString
self.maximumNumberOfLines = maximumNumberOfLines
self.truncationType = truncationType
@ -126,7 +138,9 @@ public final class TextNodeLayout: NSObject {
self.truncated = truncated
self.firstLineOffset = firstLineOffset
self.lines = lines
self.blockQuotes = blockQuotes
self.backgroundColor = backgroundColor
self.lineColor = lineColor
var hasRTL = false
for line in lines {
if line.isRTL {
@ -561,7 +575,7 @@ public class TextNode: ASDisplayNode {
}
}
private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets) -> TextNodeLayout {
private class func calculateLayout(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?) -> TextNodeLayout {
if let attributedString = attributedString {
let stringLength = attributedString.length
@ -582,11 +596,12 @@ public class TextNode: ASDisplayNode {
let fontLineSpacing = floor(fontLineHeight * lineSpacingFactor)
var lines: [TextNodeLine] = []
var blockQuotes: [TextNodeBlockQuote] = []
var maybeTypesetter: CTTypesetter?
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
if maybeTypesetter == nil {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor)
}
let typesetter = maybeTypesetter!
@ -682,11 +697,28 @@ public class TextNode: ASDisplayNode {
truncated = true
}
var headIndent: CGFloat = 0.0
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
let x = lowerX < upperX ? lowerX : upperX
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
} else if let paragraphStyle = attributes[NSAttributedStringKey.paragraphStyle] as? NSParagraphStyle {
headIndent = paragraphStyle.headIndent
}
}
let lineWidth = min(constrainedSize.width, ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine))))
let lineFrame = CGRect(x: lineCutoutOffset, y: lineOriginY, width: lineWidth, height: fontLineHeight)
let lineFrame = CGRect(x: lineCutoutOffset + headIndent, y: lineOriginY, width: lineWidth, height: fontLineHeight)
layoutSize.height += fontLineHeight + fontLineSpacing
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
if headIndent > 0.0 {
blockQuotes.append(TextNodeBlockQuote(frame: lineFrame))
}
var isRTL = false
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
if glyphRuns.count != 0 {
@ -696,16 +728,7 @@ public class TextNode: ASDisplayNode {
}
}
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
let x = lowerX < upperX ? lowerX : upperX
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
}
}
lines.append(TextNodeLine(line: coreTextLine, frame: lineFrame, range: NSMakeRange(lineRange.location, lineRange.length), isRTL: isRTL, strikethroughs: strikethroughs))
break
} else {
if lineCharacterCount > 0 {
@ -719,11 +742,27 @@ public class TextNode: ASDisplayNode {
let coreTextLine = CTTypesetterCreateLineWithOffset(typesetter, lineRange, 100.0)
lastLineCharacterIndex += lineCharacterCount
var headIndent: CGFloat = 0.0
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
let x = lowerX < upperX ? lowerX : upperX
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
} else if let paragraphStyle = attributes[NSAttributedStringKey.paragraphStyle] as? NSParagraphStyle {
headIndent = paragraphStyle.headIndent
}
}
let lineWidth = ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine)))
let lineFrame = CGRect(x: lineCutoutOffset, y: lineOriginY, width: lineWidth, height: fontLineHeight)
let lineFrame = CGRect(x: lineCutoutOffset + headIndent, y: lineOriginY, width: lineWidth, height: fontLineHeight)
layoutSize.height += fontLineHeight
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
if headIndent > 0.0 {
blockQuotes.append(TextNodeBlockQuote(frame: lineFrame))
}
var isRTL = false
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
if glyphRuns.count != 0 {
@ -733,14 +772,6 @@ public class TextNode: ASDisplayNode {
}
}
attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil))
let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil))
let x = lowerX < upperX ? lowerX : upperX
strikethroughs.append(TextNodeStrikethrough(frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: fontLineHeight)))
}
}
lines.append(TextNodeLine(line: coreTextLine, frame: lineFrame, range: NSMakeRange(lineRange.location, lineRange.length), isRTL: isRTL, strikethroughs: strikethroughs))
} else {
if !lines.isEmpty {
@ -762,9 +793,9 @@ public class TextNode: ASDisplayNode {
}
}
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, backgroundColor: backgroundColor)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor)
} else {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor)
}
}
@ -799,11 +830,8 @@ public class TextNode: ASDisplayNode {
let textMatrix = context.textMatrix
let textPosition = context.textPosition
//CGContextSaveGState(context)
context.textMatrix = CGAffineTransform(scaleX: 1.0, y: -1.0)
//let clipRect = CGContextGetClipBoundingBox(context)
let alignment = layout.alignment
let offset = CGPoint(x: layout.insets.left, y: layout.insets.top)
@ -832,7 +860,34 @@ public class TextNode: ASDisplayNode {
}
}
//CGContextRestoreGState(context)
var blockQuoteFrames: [CGRect] = []
var currentBlockQuoteFrame: CGRect?
for blockQuote in layout.blockQuotes {
if let frame = currentBlockQuoteFrame {
if blockQuote.frame.minY - frame.maxY < 20.0 {
currentBlockQuoteFrame = frame.union(blockQuote.frame)
} else {
blockQuoteFrames.append(frame)
currentBlockQuoteFrame = frame
}
} else {
currentBlockQuoteFrame = blockQuote.frame
}
}
if let frame = currentBlockQuoteFrame {
blockQuoteFrames.append(frame)
}
for frame in blockQuoteFrames {
if let lineColor = layout.lineColor {
context.setFillColor(lineColor.cgColor)
}
let rect = UIBezierPath(roundedRect: CGRect(x: frame.minX - 9.0, y: frame.minY - 14.0, width: 2.0, height: frame.height), cornerRadius: 1.0)
context.addPath(rect.cgPath)
context.fillPath()
}
context.textMatrix = textMatrix
context.textPosition = CGPoint(x: textPosition.x, y: textPosition.y)
}
@ -872,11 +927,11 @@ public class TextNode: ASDisplayNode {
if stringMatch {
layout = existingLayout
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor)
updated = true
}
} else {
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets)
layout = TextNode.calculateLayout(attributedString: arguments.attributedString, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor)
updated = true
}

View File

@ -2075,6 +2075,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
var isContactUpdates: [(PeerId, Bool)] = []
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
var recentlyUsedStickers: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
var slowModeLastMessageTimeouts:[PeerId : Int32] = [:]
var recentlyUsedGifs: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
var syncRecentGifs = false
var langPackDifferences: [String: [Api.LangPackDifference]] = [:]
@ -2181,6 +2182,11 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
}
}
}
if !message.flags.contains(.Incoming) && !message.flags.contains(.Unsent) {
if message.id.peerId.namespace == Namespaces.Peer.CloudChannel {
slowModeLastMessageTimeouts[message.id.peerId] = max(slowModeLastMessageTimeouts[message.id.peerId] ?? 0, message.timestamp)
}
}
if !message.flags.contains(.Incoming), message.forwardInfo == nil {
inner: for media in message.media {
@ -2713,6 +2719,22 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
}
}
if !slowModeLastMessageTimeouts.isEmpty {
var peerIds:Set<PeerId> = Set()
var cachedDatas:[PeerId : CachedChannelData] = [:]
for (peerId, timeout) in slowModeLastMessageTimeouts {
var cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData ?? CachedChannelData()
if let slowModeTimeout = cachedData.slowModeTimeout {
cachedData = cachedData.withUpdatedSlowModeValidUntilTimestamp(slowModeValidUntilTimestamp: timeout + slowModeTimeout)
peerIds.insert(peerId)
cachedDatas[peerId] = cachedData
}
}
transaction.updatePeerCachedData(peerIds: peerIds, update: { peerId, current in
return cachedDatas[peerId] ?? current
})
}
if syncRecentGifs {
addSynchronizeSavedGifsOperation(transaction: transaction, operation: .sync)
} else {

View File

@ -73,7 +73,10 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
let info: TelegramChannelInfo
if (flags & Int32(1 << 8)) != 0 {
let infoFlags = TelegramChannelGroupFlags()
var infoFlags = TelegramChannelGroupFlags()
if (flags & Int32(1 << 22)) != 0 {
infoFlags.insert(.isEnabledSlowMode)
}
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
} else {
var infoFlags = TelegramChannelBroadcastFlags()

View File

@ -16,7 +16,7 @@ import Foundation
#endif
public struct AppUpdateInfo: Equatable {
public let popup: Bool
public let blocking: Bool
public let version: String
public let text: String
public let entities: [MessageTextEntity]
@ -26,7 +26,7 @@ extension AppUpdateInfo {
init?(apiAppUpdate: Api.help.AppUpdate) {
switch apiAppUpdate {
case let .appUpdate(flags, _, version, text, entities, _, _):
self.popup = (flags & (1 << 0)) != 0
self.blocking = (flags & (1 << 0)) != 0
self.version = version
self.text = text
self.entities = messageTextEntitiesFromApiEntities(entities)

View File

@ -165,6 +165,20 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities)
if currentMessage.id.peerId.namespace == Namespaces.Peer.CloudChannel, !currentMessage.flags.contains(.Incoming) {
let peerId = currentMessage.id.peerId
transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, current in
var cachedData = current as? CachedChannelData ?? CachedChannelData()
if let slowModeTimeout = cachedData.slowModeTimeout {
cachedData = cachedData.withUpdatedSlowModeValidUntilTimestamp(slowModeValidUntilTimestamp: currentMessage.timestamp + slowModeTimeout)
return cachedData
} else {
return current
}
})
}
return .update(StoreMessage(id: updatedId, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, timestamp: updatedTimestamp ?? currentMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: forwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media))
})
for file in sentStickers {
@ -173,6 +187,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
for file in sentGifs {
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 200)
}
stateManager.addUpdates(result)
}
}

View File

@ -367,7 +367,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
}
let authorId: PeerId?
if let peer = peer as? TelegramChannel, case let .broadcast(info) = peer.info {
if let peer = peer as? TelegramChannel, case .broadcast = peer.info {
authorId = peer.id
} else {
authorId = account.peerId
@ -388,12 +388,9 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
if let sourceMessage = sourceMessage, let author = sourceMessage.author ?? sourceMessage.peers[sourceMessage.id.peerId] {
if let peer = peer as? TelegramSecretChat {
var isAction = false
var mediaDuration: Int32?
for media in sourceMessage.media {
if let _ = media as? TelegramMediaAction {
isAction = true
} else if let file = media as? TelegramMediaFile, let duration = file.duration {
mediaDuration = duration
}
}
if !disableAutoremove, let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction {

View File

@ -30,11 +30,8 @@ public func updateChannelSlowModeInteractively(postbox: Postbox, network: Networ
accountStateManager.addUpdates(updates)
return postbox.transaction { transaction -> Void in
transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, currentData in
if let currentData = currentData as? CachedChannelData {
let currentData = currentData as? CachedChannelData ?? CachedChannelData()
return currentData.withUpdatedSlowModeTimeout(slowModeTimeout: timeout)
} else {
return currentData
}
})
}
|> introduceError(UpdateChannelSlowModeError.self)

View File

@ -21,6 +21,20 @@ public struct StandaloneUploadSecretFile {
let key: SecretFileEncryptionKey
}
public enum StandaloneUploadMediaThumbnailResult {
case pending
case file(Api.InputFile)
case none
var file: Api.InputFile? {
if case let .file(file) = self {
return file
} else {
return nil
}
}
}
public enum StandaloneUploadMediaResult {
case media(AnyMediaReference)
}
@ -30,7 +44,22 @@ public enum StandaloneUploadMediaEvent {
case result(StandaloneUploadMediaResult)
}
public func standaloneUploadedImage(account: Account, peerId: PeerId, text: String, data: Data, dimensions: CGSize) -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
private func uploadedThumbnail(network: Network, postbox: Postbox, data: Data) -> Signal<Api.InputFile?, StandaloneUploadMediaError> {
return multipartUpload(network: network, postbox: postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false)
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { result -> Signal<Api.InputFile?, StandaloneUploadMediaError> in
switch result {
case .progress:
return .complete()
case let .inputFile(inputFile):
return .single(inputFile)
case .inputSecretFile:
return .single(nil)
}
}
}
public func standaloneUploadedImage(account: Account, peerId: PeerId, text: String, data: Data, thumbnailData: Data? = nil, dimensions: CGSize) -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false)
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { next -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
@ -91,11 +120,39 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri
}
}
public func standaloneUploadedFile(account: Account, peerId: PeerId, text: String, source: MultipartUploadSource, mimeType: String, attributes: [TelegramMediaFileAttribute], hintFileIsLarge: Bool) -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
return multipartUpload(network: account.network, postbox: account.postbox, source: source, encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes)), hintFileSize: nil, hintFileIsLarge: hintFileIsLarge)
public func standaloneUploadedFile(account: Account, peerId: PeerId, text: String, source: MultipartUploadSource, thumbnailData: Data? = nil, mimeType: String, attributes: [TelegramMediaFileAttribute], hintFileIsLarge: Bool) -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> {
let upload = multipartUpload(network: account.network, postbox: account.postbox, source: source, encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes)), hintFileSize: nil, hintFileIsLarge: hintFileIsLarge)
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { next -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch next {
let uploadThumbnail: Signal<StandaloneUploadMediaThumbnailResult, StandaloneUploadMediaError>
if let thumbnailData = thumbnailData {
uploadThumbnail = .single(.pending)
|> then(
uploadedThumbnail(network: account.network, postbox: account.postbox, data: thumbnailData)
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> map { result in
if let result = result {
return .file(result)
} else {
return .none
}
}
)
} else {
uploadThumbnail = .single(.none)
}
return combineLatest(upload, uploadThumbnail)
|> mapToSignal { result, thumbnail in
switch result {
case let .progress(progress):
return .single(.progress(progress))
default:
switch thumbnail {
case .pending:
return .complete()
default:
switch result {
case let .inputFile(inputFile):
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
@ -103,7 +160,12 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
if let inputPeer = inputPeer {
return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil)))
var flags: Int32 = 0
let thumbnailFile = thumbnail.file
if let _ = thumbnailFile {
flags |= 1 << 2
}
return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil)))
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch media {
@ -122,7 +184,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin
return .fail(.generic)
}
}
case let .inputSecretFile(file, size, key):
case let .inputSecretFile(file, _, key):
return account.postbox.transaction { transaction -> Api.InputEncryptedChat? in
if let peer = transaction.getPeer(peerId) as? TelegramSecretChat {
return Api.InputEncryptedChat.inputEncryptedChat(chatId: peer.id.id, accessHash: peer.accessHash)
@ -135,8 +197,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin
return .fail(.generic)
}
return account.network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file))
|> mapError { _ -> StandaloneUploadMediaError in return .generic
}
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { result -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch result {
case let .encryptedFile(id, accessHash, size, dcId, _):
@ -148,8 +209,10 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin
}
}
}
case let .progress(progress):
return .single(.progress(progress))
case .progress:
return .never()
}
}
}
}
}

View File

@ -72,6 +72,7 @@ public struct TelegramChannelGroupFlags: OptionSet {
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let isEnabledSlowMode = TelegramChannelGroupFlags(rawValue: 1 << 0)
}
public struct TelegramChannelGroupInfo: Equatable {

View File

@ -522,7 +522,7 @@ final class AuthorizedApplicationContext {
strongSelf.currentAppUpdateInfo = appUpdateInfo
if let appUpdateInfo = appUpdateInfo {
let controller = updateInfoController(context: strongSelf.context, appUpdateInfo: appUpdateInfo)
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
strongSelf.mainWindow.present(controller, on: .update)
}
}))

View File

@ -5,6 +5,7 @@ import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
public enum ArchivedStickerPacksControllerMode {
case stickers
@ -65,7 +66,7 @@ private enum ArchivedStickerPacksEntryId: Hashable {
private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
case info(PresentationTheme, String)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, ItemListStickerPackItemEditing)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing)
var section: ItemListSectionId {
switch self {
@ -78,7 +79,7 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
switch self {
case .info:
return .index(0)
case let .pack(_, _, _, info, _, _, _, _):
case let .pack(_, _, _, info, _, _, _, _, _):
return .pack(info.id)
}
}
@ -91,8 +92,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
} else {
return false
}
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsEnabled, lhsEditing):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsEnabled, rhsEditing) = rhs {
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsEnabled, lhsEditing):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsEnabled, rhsEditing) = rhs {
if lhsIndex != rhsIndex {
return false
}
@ -111,6 +112,9 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
if lhsCount != rhsCount {
return false
}
if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers {
return false
}
if lhsEnabled != rhsEnabled {
return false
}
@ -133,9 +137,9 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
default:
return true
}
case let .pack(lhsIndex, _, _, _, _, _, _, _):
case let .pack(lhsIndex, _, _, _, _, _, _, _, _):
switch rhs {
case let .pack(rhsIndex, _, _, _, _, _, _, _):
case let .pack(rhsIndex, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex
default:
return false
@ -147,8 +151,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
switch self {
case let .info(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .pack(_, theme, strings, info, topItem, count, enabled, editing):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .installation(installed: false), editing: editing, enabled: enabled, sectionId: self.section, action: {
case let .pack(_, theme, strings, info, topItem, count, animatedStickers, enabled, editing):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .installation(installed: false), editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: {
arguments.openStickerPack(info)
}, setPackIdWithRevealedOptions: { current, previous in
arguments.setPackIdWithRevealedOptions(current, previous)
@ -205,7 +209,7 @@ private struct ArchivedStickerPacksControllerState: Equatable {
}
}
private func archivedStickerPacksControllerEntries(presentationData: PresentationData, state: ArchivedStickerPacksControllerState, packs: [ArchivedStickerPackItem]?, installedView: CombinedView) -> [ArchivedStickerPacksEntry] {
private func archivedStickerPacksControllerEntries(presentationData: PresentationData, state: ArchivedStickerPacksControllerState, packs: [ArchivedStickerPackItem]?, installedView: CombinedView, stickerSettings: StickerSettings) -> [ArchivedStickerPacksEntry] {
var entries: [ArchivedStickerPacksEntry] = []
if let packs = packs {
@ -219,7 +223,7 @@ private func archivedStickerPacksControllerEntries(presentationData: Presentatio
var index: Int32 = 0
for item in packs {
if !installedIds.contains(item.info.id) {
entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false)))
entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), stickerSettings.loopAnimatedStickers, !state.removingPackIds.contains(item.info.id), ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == item.info.id, reorderable: false)))
index += 1
}
}
@ -369,9 +373,14 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
var previousPackCount: Int?
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, installedStickerPacks.get() |> deliverOnMainQueue)
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, installedStickerPacks.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue)
|> deliverOnMainQueue
|> map { presentationData, state, packs, installedView -> (ItemListControllerState, (ItemListNodeState<ArchivedStickerPacksEntry>, ArchivedStickerPacksEntry.ItemGenerationArguments)) in
|> map { presentationData, state, packs, installedView, sharedData -> (ItemListControllerState, (ItemListNodeState<ArchivedStickerPacksEntry>, ArchivedStickerPacksEntry.ItemGenerationArguments)) in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
stickerSettings = value
}
var rightNavigationButton: ItemListNavigationButton?
if let packs = packs, packs.count != 0 {
if state.editing {
@ -399,7 +408,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.StickerPacksSettings_ArchivedPacks), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(entries: archivedStickerPacksControllerEntries(presentationData: presentationData, state: state, packs: packs, installedView: installedView), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previous != nil && packs != nil && (previous! != 0 && previous! >= packs!.count - 10))
let listState = ItemListNodeState(entries: archivedStickerPacksControllerEntries(presentationData: presentationData, state: state, packs: packs, installedView: installedView, stickerSettings: stickerSettings), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previous != nil && packs != nil && (previous! != 0 && previous! >= packs!.count - 10))
return (controllerState, (listState, arguments))
} |> afterDisposed {
actionsDisposable.dispose()

View File

@ -277,7 +277,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
}
var dismissImpl: (() -> Void)?
let alertTheme = AlertControllerTheme(presentationTheme: strongSelf.theme)
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0))
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0), blockQuoteFont: Font.regular(13.0))
let contentNode = TextAlertContentNode(theme: alertTheme, title: NSAttributedString(string: strongSelf.strings.Login_TermsOfServiceHeader, font: Font.medium(17.0), textColor: alertTheme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.strings.Login_TermsOfServiceAgree, action: {
dismissImpl?()

View File

@ -288,7 +288,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
previousState = blockedPeersState
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.BlockedUsers_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(entries: blockedPeersControllerEntries(presentationData: presentationData, state: state, blockedPeersState: blockedPeersState), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previousStateValue != nil && previousStateValue!.peers.count >= blockedPeersState.peers.count)
let listState = ItemListNodeState(entries: blockedPeersControllerEntries(presentationData: presentationData, state: state, blockedPeersState: blockedPeersState), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: previousStateValue != nil && previousStateValue!.peers.count >= blockedPeersState.peers.count, scrollEnabled: emptyStateItem == nil)
return (controllerState, (listState, arguments))
}

View File

@ -152,7 +152,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
updatedTextAndEntities = (item.text, generateTextEntities(item.text, enabledTypes: .all))
}
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.message.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, boldItalicFont: messageBoldItalicFont, fixedFont: messageFixedFont)
let attributedText = stringWithAppliedEntities(updatedTextAndEntities.0, entities: updatedTextAndEntities.1, baseColor: item.presentationData.theme.theme.chat.message.infoPrimaryTextColor, linkColor: item.presentationData.theme.theme.chat.message.infoLinkTextColor, baseFont: messageFont, linkFont: messageFont, boldFont: messageBoldFont, italicFont: messageItalicFont, boldItalicFont: messageBoldItalicFont, fixedFont: messageFixedFont, blockQuoteFont: messageFont)
let horizontalEdgeInset: CGFloat = 10.0 + params.leftInset
let horizontalContentInset: CGFloat = 12.0

View File

@ -208,6 +208,9 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
private var automaticMediaDownloadSettingsDisposable: Disposable?
private var stickerSettings: ChatInterfaceStickerSettings
private var stickerSettingsDisposable: Disposable?
private var applicationInForegroundDisposable: Disposable?
private var checkedPeerChatServiceActions = false
@ -264,6 +267,8 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.with { $0 }
self.stickerSettings = ChatInterfaceStickerSettings(loopAnimatedStickers: false)
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.fontSize, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation)
var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none
@ -1378,8 +1383,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
}, cancelInteractiveKeyboardGestures: { [weak self] in
(self?.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
self?.chatDisplayNode.cancelInteractiveKeyboardGestures()
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
pollActionState: ChatInterfacePollActionState())
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings)
self.controllerInteraction = controllerInteraction
@ -1884,6 +1888,24 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
}
})
self.stickerSettingsDisposable = (context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings])
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
stickerSettings = value
}
let chatStickerSettings = ChatInterfaceStickerSettings(stickerSettings: stickerSettings)
if let strongSelf = self, strongSelf.stickerSettings != chatStickerSettings {
strongSelf.stickerSettings = chatStickerSettings
strongSelf.controllerInteraction?.stickerSettings = chatStickerSettings
if strongSelf.isNodeLoaded {
strongSelf.chatDisplayNode.updateStickerSettings(chatStickerSettings)
}
}
})
self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|> distinctUntilChanged
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
@ -6843,24 +6865,23 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
}
})
]
}
if let message = self.chatDisplayNode.historyNode.latestMessageInCurrentHistoryView(), !message.flags.contains(.Incoming) {
inputShortcuts.append(KeyShortcut(input: UIKeyInputUpArrow, action: { [weak self] in
if let strongSelf = self {
var canEdit = false
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
if state.interfaceState.effectiveInputState.inputText.length == 0 && state.interfaceState.editMessage == nil {
canEdit = true
}
return state
})
if canEdit {
if canEdit, let message = self.chatDisplayNode.historyNode.firstMessageForEditInCurrentHistoryView() {
inputShortcuts.append(KeyShortcut(input: UIKeyInputUpArrow, action: { [weak self] in
if let strongSelf = self {
strongSelf.interfaceInteraction?.setupEditMessage(message.id)
}
}
}))
}
}
let otherShortcuts: [KeyShortcut] = [
KeyShortcut(title: strings.KeyCommand_ScrollUp, input: UIKeyInputUpArrow, modifiers: [.shift], action: { [weak self] in

View File

@ -31,6 +31,22 @@ struct ChatInterfaceHighlightedState: Equatable {
}
}
struct ChatInterfaceStickerSettings: Equatable {
let loopAnimatedStickers: Bool
public init(loopAnimatedStickers: Bool) {
self.loopAnimatedStickers = loopAnimatedStickers
}
public init(stickerSettings: StickerSettings) {
self.loopAnimatedStickers = stickerSettings.loopAnimatedStickers
}
static func ==(lhs: ChatInterfaceStickerSettings, rhs: ChatInterfaceStickerSettings) -> Bool {
return lhs.loopAnimatedStickers == rhs.loopAnimatedStickers
}
}
public enum ChatControllerInteractionLongTapAction {
case url(String)
case mention(String)
@ -104,9 +120,10 @@ public final class ChatControllerInteraction {
var contextHighlightedState: ChatInterfaceHighlightedState?
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
var pollActionState: ChatInterfacePollActionState
var stickerSettings: ChatInterfaceStickerSettings
var searchTextHighightState: String?
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState) {
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool) -> Void, sendGif: @escaping (FileMediaReference) -> Void, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> Bool, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOption: @escaping (MessageId, Data) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
self.openMessage = openMessage
self.openPeer = openPeer
self.openPeerMention = openPeerMention
@ -154,6 +171,7 @@ public final class ChatControllerInteraction {
self.automaticMediaDownloadSettings = automaticMediaDownloadSettings
self.pollActionState = pollActionState
self.stickerSettings = stickerSettings
}
static var `default`: ChatControllerInteraction {
@ -175,6 +193,6 @@ public final class ChatControllerInteraction {
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
pollActionState: ChatInterfacePollActionState())
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
}
}

View File

@ -1497,6 +1497,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.historyNode.prefetchManager.updateAutoDownloadSettings(settings)
}
func updateStickerSettings(_ settings: ChatInterfaceStickerSettings) {
self.historyNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? ChatMessageItemView {
itemNode.updateStickerSettings()
}
}
}
var isInputViewFocused: Bool {
if let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode {
return inputPanelNode.isFocused

View File

@ -145,7 +145,6 @@ final class ChatMediaInputStickerGridItem: GridItem {
let node = ChatMediaInputStickerGridItemNode()
node.interfaceInteraction = self.interfaceInteraction
node.inputNodeInteraction = self.inputNodeInteraction
node.setup(account: self.account, stickerItem: self.stickerItem)
node.selected = self.selected
return node
}
@ -157,7 +156,6 @@ final class ChatMediaInputStickerGridItem: GridItem {
}
node.interfaceInteraction = self.interfaceInteraction
node.inputNodeInteraction = self.inputNodeInteraction
node.setup(account: self.account, stickerItem: self.stickerItem)
node.selected = self.selected
}
}
@ -209,11 +207,6 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
self.imageNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
}
func setup(account: Account, stickerItem: StickerPackItem) {
//self.updateSelectionState(animated: false)
//self.updateHiddenMedia()
}
override func updateLayout(item: GridItem, size: CGSize, isVisible: Bool, synchronousLoads: Bool) {
guard let item = item as? ChatMediaInputStickerGridItem else {
return
@ -232,13 +225,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
self.addSubnode(animationNode)
}
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.account.postbox, file: item.stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0)))
if self.isPlaying {
self.didSetUpAnimationNode = true
self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: 160, height: 160, mode: .cached)
} else {
self.didSetUpAnimationNode = false
}
self.animationNode?.visibility = self.isPlaying
self.updateVisibility()
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
} else {
if let animationNode = self.animationNode {
@ -297,7 +284,10 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
}
func updateVisibility() {
let isPlaying = self.isPanelVisible && self.isVisibleInGrid
guard let item = self.item else {
return
}
let isPlaying = self.isPanelVisible && self.isVisibleInGrid && (item.interfaceInteraction?.stickerSettings.loopAnimatedStickers ?? true)
if self.isPlaying != isPlaying {
self.isPlaying = isPlaying
self.animationNode?.visibility = isPlaying

View File

@ -103,7 +103,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
override var visibility: ListViewItemNodeVisibility {
didSet {
self.visibilityStatus = self.visibility != .none
self.visibilityStatus = self.visibility != .none && false
}
}
@ -190,7 +190,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
self.addSubnode(animatedStickerNode)
animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached)
animatedStickerNode.visibility = self.visibilityStatus
animatedStickerNode.visibility = self.visibilityStatus && false
}
if let animatedStickerNode = self.animatedStickerNode {
animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)

View File

@ -108,14 +108,14 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode {
self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme)
self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelTrendingIconImage(theme)
self.badgeBackground.image = generateFilledCircleImage(diameter: 16.0, color: theme.chat.inputPanel.mediaRecordingDotColor)
self.badgeBackground.image = generateFilledCircleImage(diameter: 10.0, color: theme.chat.inputPanel.mediaRecordingDotColor)
let imageSize = CGSize(width: 26.0, height: 26.0)
let imageFrame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0) + UIScreenPixel), size: imageSize)
self.imageNode.frame = imageFrame
if let image = self.badgeBackground.image {
self.badgeBackground.frame = CGRect(origin: CGPoint(x: imageFrame.maxX - image.size.width + 2.0, y: imageFrame.maxY - image.size.width + 3.0), size: image.size)
self.badgeBackground.frame = CGRect(origin: CGPoint(x: imageFrame.maxX - image.size.width - 1.0, y: imageFrame.maxY - image.size.width + 1.0), size: image.size)
}
}

View File

@ -385,7 +385,7 @@ private func universalServiceMessageString(theme: ChatPresentationThemeData?, st
}
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
case let .customText(text, entities):
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, underlineLinks: false)
attributedString = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false)
case let .botDomainAccessGranted(domain):
attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).0, font: titleFont, textColor: primaryTextColor)
case let .botSentSecureValues(types):

View File

@ -17,6 +17,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let imageNode: TransformImageNode
private let animationNode: AnimatedStickerNode
private var didSetUpAnimationNode = false
private var isPlaying = false
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
private var swipeToReplyFeedback: HapticFeedback?
@ -91,7 +92,6 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.view.addGestureRecognizer(replyRecognizer)
}
private var visibilityPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
override var visibility: ListViewItemNodeVisibility {
didSet {
let wasVisible = oldValue != .none
@ -106,21 +106,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private var visibilityStatus: Bool = false {
didSet {
if self.visibilityStatus != oldValue {
if self.visibilityStatus {
self.animationNode.visibility = true
self.visibilityPromise.set(true)
if let item = self.item, !self.didSetUpAnimationNode {
for media in item.message.media {
if let telegramFile = media as? TelegramMediaFile {
self.didSetUpAnimationNode = true
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached)
}
}
}
} else {
self.animationNode.visibility = false
self.visibilityPromise.set(false)
}
self.updateVisibility()
}
}
}
@ -133,7 +119,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if self.telegramFile?.id != telegramFile.id {
self.telegramFile = telegramFile
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 384.0, height: 384.0), thumbnail: false))
if self.visibilityStatus {
self.updateVisibility()
if self.visibilityStatus && false {
self.didSetUpAnimationNode = true
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached)
}
@ -144,6 +131,31 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
}
func updateVisibility() {
guard let item = self.item else {
return
}
let isPlaying = self.visibilityStatus && item.controllerInteraction.stickerSettings.loopAnimatedStickers
if self.isPlaying != isPlaying {
self.isPlaying = isPlaying
self.animationNode.visibility = isPlaying
if let item = self.item, isPlaying, !self.didSetUpAnimationNode {
self.didSetUpAnimationNode = true
for media in item.message.media {
if let telegramFile = media as? TelegramMediaFile {
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached)
break
}
}
}
}
}
override func updateStickerSettings() {
self.updateVisibility()
}
override func asyncLayout() -> (_ item: ChatMessageItem, _ params: ListViewItemLayoutParams, _ mergedTop: ChatMessageMerge, _ mergedBottom: ChatMessageMerge, _ dateHeaderAtBottom: Bool) -> (ListViewItemNodeLayout, (ListViewItemUpdateAnimation, Bool) -> Void) {
let displaySize = CGSize(width: 184.0, height: 184.0)
let telegramFile = self.telegramFile

View File

@ -15,6 +15,7 @@ private let textBoldFont = Font.semibold(15.0)
private let textItalicFont = Font.italic(15.0)
private let textBoldItalicFont = Font.semiboldItalic(15.0)
private let textFixedFont = Font.regular(15.0)
private let textBlockQuoteFont = Font.regular(15.0)
private let buttonFont = Font.semibold(13.0)
enum ChatMessageAttachedContentActionIcon {
@ -371,7 +372,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
string.append(NSAttributedString(string: "\n", font: textFont, textColor: messageTheme.primaryTextColor))
}
if let entities = entities {
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont))
string.append(stringWithAppliedEntities(text, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textBlockQuoteFont))
} else {
string.append(NSAttributedString(string: text + "\n", font: textFont, textColor: messageTheme.primaryTextColor))
}

View File

@ -2191,7 +2191,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView {
}
}
override func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
for contentNode in self.contentNodes {
if let playMediaWithSound = contentNode.playMediaWithSound() {

View File

@ -698,6 +698,9 @@ public class ChatMessageItemView: ListViewItemNode {
func updateAutomaticMediaDownloadSettings() {
}
func updateStickerSettings() {
}
func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
return nil
}

View File

@ -81,13 +81,13 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
}
if !item.message.containsSecretMedia {
if telegramFile.isAnimated {
if telegramFile.isAnimated && item.controllerInteraction.automaticMediaDownloadSettings.autoplayGifs {
if case .full = automaticDownload {
automaticPlayback = item.controllerInteraction.automaticMediaDownloadSettings.autoplayGifs
automaticPlayback = true
} else {
automaticPlayback = item.context.account.postbox.mediaBox.completedResourcePath(telegramFile.resource) != nil
}
} else if telegramFile.isVideo && item.controllerInteraction.automaticMediaDownloadSettings.autoplayVideos {
} else if (telegramFile.isVideo && !telegramFile.isAnimated) && item.controllerInteraction.automaticMediaDownloadSettings.autoplayVideos {
if case .full = automaticDownload {
automaticPlayback = true
} else {

View File

@ -230,7 +230,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
let forceStatusNewline = false
if let entities = entities {
attributedText = stringWithAppliedEntities(rawText, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: item.presentationData.messageBoldFont, italicFont: item.presentationData.messageItalicFont, boldItalicFont: item.presentationData.messageBoldItalicFont, fixedFont: item.presentationData.messageFixedFont)
attributedText = stringWithAppliedEntities(rawText, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: item.presentationData.messageBoldFont, italicFont: item.presentationData.messageItalicFont, boldItalicFont: item.presentationData.messageBoldItalicFont, fixedFont: item.presentationData.messageFixedFont, blockQuoteFont: item.presentationData.messageBlockQuoteFont)
} else {
attributedText = NSAttributedString(string: rawText, font: textFont, textColor: messageTheme.primaryTextColor)
}
@ -242,7 +242,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
let textInsets = UIEdgeInsets(top: 2.0, left: 0.0, bottom: 5.0, right: 0.0)
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: textInsets))
let (textLayout, textApply) = textLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: cutout, insets: textInsets, lineColor: messageTheme.accentControlColor))
var textFrame = CGRect(origin: CGPoint(x: -textInsets.left, y: -textInsets.top), size: textLayout.size)
var textFrameWithoutInsets = CGRect(origin: CGPoint(x: textFrame.origin.x + textInsets.left, y: textFrame.origin.y + textInsets.top), size: CGSize(width: textFrame.width - textInsets.left - textInsets.right, height: textFrame.height - textInsets.top - textInsets.bottom))

View File

@ -84,6 +84,7 @@ public final class ChatPresentationData {
let messageItalicFont: UIFont
let messageBoldItalicFont: UIFont
let messageFixedFont: UIFont
let messageBlockQuoteFont: UIFont
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) {
self.theme = theme
@ -103,5 +104,6 @@ public final class ChatPresentationData {
self.messageItalicFont = UIFont.italicSystemFont(ofSize: baseFontSize)
self.messageBoldItalicFont = Font.semiboldItalic(baseFontSize)
self.messageFixedFont = UIFont(name: "Menlo-Regular", size: baseFontSize - 1.0) ?? UIFont.systemFont(ofSize: baseFontSize)
self.messageBlockQuoteFont = UIFont.systemFont(ofSize: baseFontSize - 1.0)
}
}

View File

@ -394,7 +394,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
pollActionState: ChatInterfacePollActionState())
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
self.controllerInteraction = controllerInteraction
self.listNode.displayedItemRangeChanged = { [weak self] displayedRange, opaqueTransactionState in

View File

@ -39,6 +39,7 @@ struct ChatTextFontAttributes: OptionSet {
static let bold = ChatTextFontAttributes(rawValue: 1 << 0)
static let italic = ChatTextFontAttributes(rawValue: 1 << 1)
static let monospace = ChatTextFontAttributes(rawValue: 1 << 2)
static let blockQuote = ChatTextFontAttributes(rawValue: 1 << 3)
}
func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor) -> NSAttributedString {

View File

@ -5,6 +5,7 @@ import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
private final class FeaturedStickerPacksControllerArguments {
let account: Account
@ -46,7 +47,7 @@ private enum FeaturedStickerPacksEntryId: Hashable {
}
private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, Bool, StickerPackItem?, String, Bool)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, Bool, StickerPackItem?, String, Bool, Bool)
var section: ItemListSectionId {
switch self {
@ -57,15 +58,15 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
var stableId: FeaturedStickerPacksEntryId {
switch self {
case let .pack(_, _, _, info, _, _, _, _):
case let .pack(_, _, _, info, _, _, _, _, _):
return .pack(info.id)
}
}
static func ==(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool {
switch lhs {
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsUnread, lhsTopItem, lhsCount, lhsInstalled):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsUnread, rhsTopItem, rhsCount, rhsInstalled) = rhs {
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsUnread, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsInstalled):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsUnread, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsInstalled) = rhs {
if lhsIndex != rhsIndex {
return false
}
@ -87,6 +88,9 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
if lhsCount != rhsCount {
return false
}
if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers {
return false
}
if lhsInstalled != rhsInstalled {
return false
}
@ -99,9 +103,9 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
static func <(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool {
switch lhs {
case let .pack(lhsIndex, _, _, _, _, _, _, _):
case let .pack(lhsIndex, _, _, _, _, _, _, _, _):
switch rhs {
case let .pack(rhsIndex, _, _, _, _, _, _, _):
case let .pack(rhsIndex, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex
}
}
@ -109,8 +113,8 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
func item(_ arguments: FeaturedStickerPacksControllerArguments) -> ListViewItem {
switch self {
case let .pack(_, theme, strings, info, unread, topItem, count, installed):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: unread, control: .installation(installed: installed), editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, sectionId: self.section, action: {
case let .pack(_, theme, strings, info, unread, topItem, count, playAnimatedStickers, installed):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: unread, control: .installation(installed: installed), editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: {
arguments.openStickerPack(info)
}, setPackIdWithRevealedOptions: { _, _ in
}, addPack: {
@ -130,7 +134,7 @@ private struct FeaturedStickerPacksControllerState: Equatable {
}
}
private func featuredStickerPacksControllerEntries(presentationData: PresentationData, state: FeaturedStickerPacksControllerState, view: CombinedView, featured: [FeaturedStickerPackItem], unreadPacks: [ItemCollectionId: Bool]) -> [FeaturedStickerPacksEntry] {
private func featuredStickerPacksControllerEntries(presentationData: PresentationData, state: FeaturedStickerPacksControllerState, view: CombinedView, featured: [FeaturedStickerPackItem], unreadPacks: [ItemCollectionId: Bool], stickerSettings: StickerSettings) -> [FeaturedStickerPacksEntry] {
var entries: [FeaturedStickerPacksEntry] = []
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])] as? ItemCollectionInfosView, !featured.isEmpty {
@ -145,7 +149,7 @@ private func featuredStickerPacksControllerEntries(presentationData: Presentatio
if let value = unreadPacks[item.info.id] {
unread = value
}
entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, unread, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), installedPacks.contains(item.info.id)))
entries.append(.pack(index, presentationData.theme, presentationData.strings, item.info, unread, item.topItems.first, presentationData.strings.StickerPack_StickerCount(item.info.count), stickerSettings.loopAnimatedStickers, installedPacks.contains(item.info.id)))
index += 1
}
}
@ -195,9 +199,14 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr
var previousPackCount: Int?
var initialUnreadPacks: [ItemCollectionId: Bool] = [:]
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, featured.get() |> deliverOnMainQueue)
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, featured.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue)
|> deliverOnMainQueue
|> map { presentationData, state, view, featured -> (ItemListControllerState, (ItemListNodeState<FeaturedStickerPacksEntry>, FeaturedStickerPacksEntry.ItemGenerationArguments)) in
|> map { presentationData, state, view, featured, sharedData -> (ItemListControllerState, (ItemListNodeState<FeaturedStickerPacksEntry>, FeaturedStickerPacksEntry.ItemGenerationArguments)) in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
stickerSettings = value
}
let packCount: Int? = featured.count
for item in featured {
@ -212,7 +221,7 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.FeaturedStickerPacks_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(entries: featuredStickerPacksControllerEntries(presentationData: presentationData, state: state, view: view, featured: featured, unreadPacks: initialUnreadPacks), style: .blocks, animateChanges: false)
let listState = ItemListNodeState(entries: featuredStickerPacksControllerEntries(presentationData: presentationData, state: state, view: view, featured: featured, unreadPacks: initialUnreadPacks, stickerSettings: stickerSettings), style: .blocks, animateChanges: false)
return (controllerState, (listState, arguments))
} |> afterDisposed {
actionsDisposable.dispose()
@ -226,7 +235,7 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr
var unreadIds: [ItemCollectionId] = []
for entry in entries {
switch entry {
case let .pack(_, _, _, info, unread, _, _, _):
case let .pack(_, _, _, info, unread, _, _, _, _):
if unread && !alreadyReadIds.contains(info.id) {
unreadIds.append(info.id)
}

View File

@ -122,7 +122,7 @@ private let boldItalicFont = Font.semiboldItalic(16.0)
private let fixedFont = UIFont(name: "Menlo-Regular", size: 15.0) ?? textFont
func galleryCaptionStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> NSAttributedString {
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, underlineLinks: false)
return stringWithAppliedEntities(text, entities: entities, baseColor: .white, linkColor: UIColor(rgb: 0x5ac8fa), baseFont: textFont, linkFont: textFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: fixedFont, blockQuoteFont: textFont, underlineLinks: false)
}
private func galleryMessageCaptionText(_ message: Message) -> String {

View File

@ -5,6 +5,7 @@ import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
private final class GroupStickerPackSetupControllerArguments {
let account: Account
@ -64,7 +65,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
case currentPack(Int32, PresentationTheme, PresentationStrings, GroupStickerPackCurrentItemContent)
case searchInfo(PresentationTheme, String)
case packsTitle(PresentationTheme, String)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool)
var section: ItemListSectionId {
switch self {
@ -85,7 +86,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
return .index(2)
case .packsTitle:
return .index(3)
case let .pack(_, _, _, info, _, _, _):
case let .pack(_, _, _, info, _, _, _, _):
return .pack(info.id)
}
}
@ -128,8 +129,8 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
} else {
return false
}
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsSelected):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsSelected) = rhs {
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsSelected):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsSelected) = rhs {
if lhsIndex != rhsIndex {
return false
}
@ -148,6 +149,9 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
if lhsCount != rhsCount {
return false
}
if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers {
return false
}
if lhsSelected != rhsSelected {
return false
}
@ -188,9 +192,9 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
default:
return true
}
case let .pack(lhsIndex, _, _, _, _, _, _):
case let .pack(lhsIndex, _, _, _, _, _, _, _):
switch rhs {
case let .pack(rhsIndex, _, _, _, _, _, _):
case let .pack(rhsIndex, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex
default:
return false
@ -216,8 +220,8 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section, linkAction: nil)
case let .packsTitle(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .pack(_, theme, strings, info, topItem, count, selected):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: selected ? .selection : .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, sectionId: self.section, action: {
case let .pack(_, theme, strings, info, topItem, count, playAnimatedStickers, selected):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: selected ? .selection : .none, editing: ItemListStickerPackItemEditing(editable: false, editing: false, revealed: false, reorderable: false), enabled: true, playAnimatedStickers: playAnimatedStickers, sectionId: self.section, action: {
if selected {
arguments.openStickerPack(info)
} else {
@ -258,7 +262,7 @@ private struct GroupStickerPackSetupControllerState: Equatable {
var isSaving: Bool
}
private func groupStickerPackSetupControllerEntries(presentationData: PresentationData, searchText: String, view: CombinedView, initialData: InitialStickerPackData?, searchState: GroupStickerPackSearchState) -> [GroupStickerPackEntry] {
private func groupStickerPackSetupControllerEntries(presentationData: PresentationData, searchText: String, view: CombinedView, initialData: InitialStickerPackData?, searchState: GroupStickerPackSearchState, stickerSettings: StickerSettings) -> [GroupStickerPackEntry] {
if initialData == nil {
return []
}
@ -288,7 +292,7 @@ private func groupStickerPackSetupControllerEntries(presentationData: Presentati
if case let .found(found) = searchState {
selected = found.info.id == info.id
}
entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), selected))
entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), stickerSettings.loopAnimatedStickers, selected))
index += 1
}
}
@ -400,8 +404,13 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee
let previousHadData = Atomic<Bool>(value: false)
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, initialData.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, searchState.get() |> deliverOnMainQueue)
|> map { presentationData, state, initialData, view, searchState -> (ItemListControllerState, (ItemListNodeState<GroupStickerPackEntry>, GroupStickerPackEntry.ItemGenerationArguments)) in
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, initialData.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, searchState.get() |> deliverOnMainQueue, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> deliverOnMainQueue)
|> map { presentationData, state, initialData, view, searchState, sharedData -> (ItemListControllerState, (ItemListNodeState<GroupStickerPackEntry>, GroupStickerPackEntry.ItemGenerationArguments)) in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
stickerSettings = value
}
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
dismissImpl?()
})
@ -456,7 +465,7 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
}
let listState = ItemListNodeState(entries: groupStickerPackSetupControllerEntries(presentationData: presentationData, searchText: searchState.0, view: view, initialData: initialData, searchState: searchState.1), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: hasData && hadData)
let listState = ItemListNodeState(entries: groupStickerPackSetupControllerEntries(presentationData: presentationData, searchText: searchState.0, view: view, initialData: initialData, searchState: searchState.1, stickerSettings: stickerSettings), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: hasData && hadData)
return (controllerState, (listState, arguments))
} |> afterDisposed {
actionsDisposable.dispose()

View File

@ -209,7 +209,7 @@ func fetchICloudFileResource(resource: ICloudFileResource) -> Signal<MediaResour
if resource.thumbnail {
let tempFile = TempBox.shared.tempFile(fileName: "thumb.jpg")
var data = Data()
if let image = generatePdfPreviewImage(url: url, size: CGSize(width: 320.0, height: 320.0)), let jpegData = UIImageJPEGRepresentation(image, 0.5) {
if let image = generatePdfPreviewImage(url: url, size: CGSize(width: 256, height: 256.0)), let jpegData = UIImageJPEGRepresentation(image, 0.5) {
data = jpegData
}
if let _ = try? data.write(to: URL(fileURLWithPath: tempFile.path)) {

View File

@ -18,8 +18,9 @@ private final class InstalledStickerPacksControllerArguments {
let openFeatured: () -> Void
let openArchived: ([ArchivedStickerPackItem]?) -> Void
let openSuggestOptions: () -> Void
let toggleAnimatedStickers: (Bool) -> Void
init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void) {
init(account: Account, openStickerPack: @escaping (StickerPackCollectionInfo) -> Void, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, removePack: @escaping (ArchivedStickerPackItem) -> Void, openStickersBot: @escaping () -> Void, openMasks: @escaping () -> Void, openFeatured: @escaping () -> Void, openArchived: @escaping ([ArchivedStickerPackItem]?) -> Void, openSuggestOptions: @escaping () -> Void, toggleAnimatedStickers: @escaping (Bool) -> Void) {
self.account = account
self.openStickerPack = openStickerPack
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
@ -29,6 +30,7 @@ private final class InstalledStickerPacksControllerArguments {
self.openFeatured = openFeatured
self.openArchived = openArchived
self.openSuggestOptions = openSuggestOptions
self.toggleAnimatedStickers = toggleAnimatedStickers
}
}
@ -85,13 +87,15 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
case trending(PresentationTheme, String, Int32)
case archived(PresentationTheme, String, Int32, [ArchivedStickerPackItem]?)
case masks(PresentationTheme, String)
case animatedStickers(PresentationTheme, String, Bool)
case animatedStickersInfo(PresentationTheme, String)
case packsTitle(PresentationTheme, String)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, ItemListStickerPackItemEditing)
case pack(Int32, PresentationTheme, PresentationStrings, StickerPackCollectionInfo, StickerPackItem?, String, Bool, Bool, ItemListStickerPackItemEditing)
case packsInfo(PresentationTheme, String)
var section: ItemListSectionId {
switch self {
case .suggestOptions, .trending, .masks, .archived:
case .suggestOptions, .trending, .masks, .archived, .animatedStickers, .animatedStickersInfo:
return InstalledStickerPacksSection.service.rawValue
case .packsTitle, .pack, .packsInfo:
return InstalledStickerPacksSection.stickers.rawValue
@ -108,12 +112,16 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
return .index(2)
case .masks:
return .index(3)
case .packsTitle:
case .animatedStickers:
return .index(4)
case let .pack(_, _, _, info, _, _, _, _):
case .animatedStickersInfo:
return .index(5)
case .packsTitle:
return .index(6)
case let .pack(_, _, _, info, _, _, _, _, _):
return .pack(info.id)
case .packsInfo:
return .index(5)
return .index(7)
}
}
@ -143,14 +151,26 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
} else {
return false
}
case let .animatedStickers(lhsTheme, lhsText, lhsValue):
if case let .animatedStickers(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
return true
} else {
return false
}
case let .animatedStickersInfo(lhsTheme, lhsText):
if case let .animatedStickersInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .packsTitle(lhsTheme, lhsText):
if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
return true
} else {
return false
}
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsEnabled, lhsEditing):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsEnabled, rhsEditing) = rhs {
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsAnimatedStickers, lhsEnabled, lhsEditing):
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsAnimatedStickers, rhsEnabled, rhsEditing) = rhs {
if lhsIndex != rhsIndex {
return false
}
@ -169,6 +189,9 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
if lhsCount != rhsCount {
return false
}
if lhsAnimatedStickers != rhsAnimatedStickers {
return false
}
if lhsEnabled != rhsEnabled {
return false
}
@ -218,16 +241,30 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
default:
return true
}
case .packsTitle:
case .animatedStickers:
switch rhs {
case .suggestOptions, .trending, .masks, .archived, .packsTitle:
case .suggestOptions, .trending, .archived, .masks, .animatedStickers:
return false
default:
return true
}
case let .pack(lhsIndex, _, _, _, _, _, _, _):
case .animatedStickersInfo:
switch rhs {
case let .pack(rhsIndex, _, _, _, _, _, _, _):
case .suggestOptions, .trending, .archived, .masks, .animatedStickers, .animatedStickersInfo:
return false
default:
return true
}
case .packsTitle:
switch rhs {
case .suggestOptions, .trending, .masks, .archived, .animatedStickers, .animatedStickersInfo, .packsTitle:
return false
default:
return true
}
case let .pack(lhsIndex, _, _, _, _, _, _, _, _):
switch rhs {
case let .pack(rhsIndex, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex
case .packsInfo:
return true
@ -262,10 +299,16 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
return ItemListDisclosureItem(theme: theme, title: text, label: count == 0 ? "" : "\(count)", sectionId: self.section, style: .blocks, action: {
arguments.openArchived(archived)
})
case let .animatedStickers(theme, text, value):
return ItemListSwitchItem(theme: theme, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
arguments.toggleAnimatedStickers(value)
})
case let .animatedStickersInfo(theme, text):
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
case let .packsTitle(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .pack(_, theme, strings, info, topItem, count, enabled, editing):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .none, editing: editing, enabled: enabled, sectionId: self.section, action: {
case let .pack(_, theme, strings, info, topItem, count, animatedStickers, enabled, editing):
return ItemListStickerPackItem(theme: theme, strings: strings, account: arguments.account, packInfo: info, itemCount: count, topItem: topItem, unread: false, control: .none, editing: editing, enabled: enabled, playAnimatedStickers: animatedStickers, sectionId: self.section, action: {
arguments.openStickerPack(info)
}, setPackIdWithRevealedOptions: { current, previous in
arguments.setPackIdWithRevealedOptions(current, previous)
@ -353,6 +396,10 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati
entries.append(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedPacks, Int32(archived.count), archived))
}
entries.append(.masks(presentationData.theme, presentationData.strings.MaskStickerSettings_Title))
entries.append(.animatedStickers(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickers, stickerSettings.loopAnimatedStickers))
entries.append(.animatedStickersInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickersInfo))
entries.append(.packsTitle(presentationData.theme, presentationData.strings.StickerPacksSettings_StickerPacksSection))
case .masks:
if let archived = archived, !archived.isEmpty {
@ -365,7 +412,7 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati
var index: Int32 = 0
for entry in packsEntries {
if let info = entry.info as? StickerPackCollectionInfo {
entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), true, ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == entry.id, reorderable: true)))
entries.append(.pack(index, presentationData.theme, presentationData.strings, info, entry.firstItem as? StickerPackItem, presentationData.strings.StickerPack_StickerCount(info.count == 0 ? entry.count : info.count), stickerSettings.loopAnimatedStickers, true, ItemListStickerPackItemEditing(editable: true, editing: state.editing, revealed: state.packIdWithRevealedOptions == entry.id, reorderable: true)))
index += 1
}
}
@ -505,18 +552,10 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
])
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
/*
let suggestString: String
switch stickerSettings.emojiStickerSuggestionMode {
case .none:
suggestString = presentationData.strings.Stickers_SuggestNone
case .all:
case .installed:
suggestString = presentationData.strings.Stickers_SuggestAdded
}
*/
}, toggleAnimatedStickers: { value in
let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
return current.withUpdatedLoopAnimatedStickers(value)
}).start()
})
let stickerPacks = Promise<CombinedView>()
stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])]))
@ -532,9 +571,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
archivedPromise.set(.single(nil) |> then(archivedStickerPacks(account: context.account, namespace: .masks) |> map(Optional.init)))
}
var previousPackCount: Int?
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get(), stickerPacks.get(), combineLatest(queue: .mainQueue(), featured.get(), archivedPromise.get()), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]))
|> deliverOnMainQueue
|> map { presentationData, state, view, featuredAndArchived, sharedData -> (ItemListControllerState, (ItemListNodeState<InstalledStickerPacksEntry>, InstalledStickerPacksEntry.ItemGenerationArguments)) in
@ -549,12 +586,6 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
}
let leftNavigationButton: ItemListNavigationButton? = nil
/*if case .modal = mode {
leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
dismissImpl?()
})
}*/
var rightNavigationButton: ItemListNavigationButton?
if let packCount = packCount, packCount != 0 {
if case .modal = mode {
@ -603,7 +634,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
controller.reorderEntry = { fromIndex, toIndex, entries in
let fromEntry = entries[fromIndex]
guard case let .pack(_, _, _, fromPackInfo, _, _, _, _) = fromEntry else {
guard case let .pack(_, _, _, fromPackInfo, _, _, _, _, _) = fromEntry else {
return
}
var referenceId: ItemCollectionId?
@ -611,7 +642,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
var afterAll = false
if toIndex < entries.count {
switch entries[toIndex] {
case let .pack(_, _, _, toPackInfo, _, _, _, _):
case let .pack(_, _, _, toPackInfo, _, _, _, _, _):
referenceId = toPackInfo.id
default:
if entries[toIndex] < fromEntry {

View File

@ -182,7 +182,7 @@ class ItemListAddressItemNode: ListViewItemNode {
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.label, font: labelFont, textColor: labelColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let baseColor = item.theme.list.itemPrimaryTextColor
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
let string = stringWithAppliedEntities(item.text, entities: [], baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset - 98.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let padding: CGFloat = !item.label.isEmpty ? 39.0 : 20.0

View File

@ -56,7 +56,7 @@ private struct ItemListNodeTransition<Entry: ItemListNodeEntry> {
let animateAlpha: Bool
let crossfade: Bool
let mergedEntries: [Entry]
let userInteractionEnabled: Bool
let scrollEnabled: Bool
}
struct ItemListNodeState<Entry: ItemListNodeEntry> {
@ -66,12 +66,12 @@ struct ItemListNodeState<Entry: ItemListNodeEntry> {
let searchItem: ItemListControllerSearch?
let animateChanges: Bool
let crossfadeState: Bool
let userInteractionEnabled: Bool
let scrollEnabled: Bool
let focusItemTag: ItemListItemTag?
let ensureVisibleItemTag: ItemListItemTag?
let initialScrollToItem: ListViewScrollToItem?
init(entries: [Entry], style: ItemListStyle, focusItemTag: ItemListItemTag? = nil, ensureVisibleItemTag: ItemListItemTag? = nil, emptyStateItem: ItemListControllerEmptyStateItem? = nil, searchItem: ItemListControllerSearch? = nil, initialScrollToItem: ListViewScrollToItem? = nil, crossfadeState: Bool = false, animateChanges: Bool = true, userInteractionEnabled: Bool = true) {
init(entries: [Entry], style: ItemListStyle, focusItemTag: ItemListItemTag? = nil, ensureVisibleItemTag: ItemListItemTag? = nil, emptyStateItem: ItemListControllerEmptyStateItem? = nil, searchItem: ItemListControllerSearch? = nil, initialScrollToItem: ListViewScrollToItem? = nil, crossfadeState: Bool = false, animateChanges: Bool = true, scrollEnabled: Bool = true) {
self.entries = entries
self.style = style
self.emptyStateItem = emptyStateItem
@ -81,7 +81,7 @@ struct ItemListNodeState<Entry: ItemListNodeEntry> {
self.focusItemTag = focusItemTag
self.ensureVisibleItemTag = ensureVisibleItemTag
self.initialScrollToItem = initialScrollToItem
self.userInteractionEnabled = userInteractionEnabled
self.scrollEnabled = scrollEnabled
}
}
@ -275,7 +275,7 @@ class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UIScrollV
scrollToItem = state.initialScrollToItem
}
return ItemListNodeTransition(theme: theme, entries: transition, updateStyle: updatedStyle, emptyStateItem: state.emptyStateItem, searchItem: state.searchItem, focusItemTag: state.focusItemTag, ensureVisibleItemTag: state.ensureVisibleItemTag, scrollToItem: scrollToItem, firstTime: previous == nil, animated: previous != nil && state.animateChanges, animateAlpha: previous != nil && state.animateChanges, crossfade: state.crossfadeState, mergedEntries: state.entries, userInteractionEnabled: state.userInteractionEnabled)
return ItemListNodeTransition(theme: theme, entries: transition, updateStyle: updatedStyle, emptyStateItem: state.emptyStateItem, searchItem: state.searchItem, focusItemTag: state.focusItemTag, ensureVisibleItemTag: state.ensureVisibleItemTag, scrollToItem: scrollToItem, firstTime: previous == nil, animated: previous != nil && state.animateChanges, animateAlpha: previous != nil && state.animateChanges, crossfade: state.crossfadeState, mergedEntries: state.entries, scrollEnabled: state.scrollEnabled)
}) |> deliverOnMainQueue).start(next: { [weak self] transition in
if let strongSelf = self {
strongSelf.enqueueTransition(transition)
@ -593,7 +593,7 @@ class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UIScrollV
self.emptyStateNode = nil
}
}
self.listNode.isUserInteractionEnabled = transition.userInteractionEnabled
self.listNode.scrollEnabled = transition.scrollEnabled
if updateSearchItem {
self.requestLayout?(.animated(duration: 0.3, curve: .spring))

View File

@ -185,7 +185,7 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
}
let entities = generateTextEntities(item.text, enabledTypes: item.enabledEntityTypes)
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, boldItalicFont: titleBoldItalicFont, fixedFont: titleFixedFont)
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, boldItalicFont: titleBoldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: titleFont)
let (titleLayout, titleApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))

View File

@ -47,13 +47,14 @@ final class ItemListStickerPackItem: ListViewItem, ItemListItem {
let control: ItemListStickerPackItemControl
let editing: ItemListStickerPackItemEditing
let enabled: Bool
let playAnimatedStickers: Bool
let sectionId: ItemListSectionId
let action: (() -> Void)?
let setPackIdWithRevealedOptions: (ItemCollectionId?, ItemCollectionId?) -> Void
let addPack: () -> Void
let removePack: () -> Void
init(theme: PresentationTheme, strings: PresentationStrings, account: Account, packInfo: StickerPackCollectionInfo, itemCount: String, topItem: StickerPackItem?, unread: Bool, control: ItemListStickerPackItemControl, editing: ItemListStickerPackItemEditing, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping () -> Void, removePack: @escaping () -> Void) {
init(theme: PresentationTheme, strings: PresentationStrings, account: Account, packInfo: StickerPackCollectionInfo, itemCount: String, topItem: StickerPackItem?, unread: Bool, control: ItemListStickerPackItemControl, editing: ItemListStickerPackItemEditing, enabled: Bool, playAnimatedStickers: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPackIdWithRevealedOptions: @escaping (ItemCollectionId?, ItemCollectionId?) -> Void, addPack: @escaping () -> Void, removePack: @escaping () -> Void) {
self.theme = theme
self.strings = strings
self.account = account
@ -64,6 +65,7 @@ final class ItemListStickerPackItem: ListViewItem, ItemListItem {
self.control = control
self.editing = editing
self.enabled = enabled
self.playAnimatedStickers = playAnimatedStickers
self.sectionId = sectionId
self.action = action
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
@ -162,7 +164,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
let isVisible = self.visibility != .none
if wasVisible != isVisible {
self.animationNode?.visibility = isVisible
self.animationNode?.visibility = isVisible && (self.layoutParams?.0.playAnimatedStickers ?? true)
}
}
}
@ -558,8 +560,8 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode)
animationNode.setup(account: item.account, resource: resource, width: 80, height: 80, mode: .cached)
animationNode.visibility = strongSelf.visibility != .none
}
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
if let animationNode = strongSelf.animationNode {
transition.updateFrame(node: animationNode, frame: imageFrame)
}

View File

@ -209,7 +209,7 @@ class ItemListTextWithLabelItemNode: ListViewItemNode {
case .highlighted:
baseColor = item.theme.list.itemHighlightedColor
}
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: baseColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: item.multiline ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftOffset - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let contentSize = CGSize(width: params.width, height: textLayout.size.height + 39.0)

View File

@ -38,6 +38,11 @@ public func navigateToChatController(navigationController: NavigationController,
if activateInput {
controller.activateInput()
}
if let botStart = botStart {
controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in
return state.updatedBotStartPayload(botStart.payload)
}
}
found = true
break
}
@ -48,6 +53,11 @@ public func navigateToChatController(navigationController: NavigationController,
let controller: ChatController
if let chatController = chatController {
controller = chatController
if let botStart = botStart {
controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in
return state.updatedBotStartPayload(botStart.payload)
}
}
} else {
controller = ChatController(context: context, chatLocation: chatLocation, messageId: messageId, botStart: botStart)
}

View File

@ -193,6 +193,20 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
}))
}
options.append(OpenInOption(application: .other(title: "Moovit", identifier: 498477945, scheme: "moovit", store: nil), action: {
if withDirections {
let destName: String
if let title = location.venue?.title.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed), title.count > 0 {
destName = title
} else {
destName = ""
}
return .openUrl(url: "moovit://directions?dest_lat=\(lat)&dest_lon=\(lon)&dest_name=\(destName)&partner_id=Telegram")
} else {
return .openUrl(url: "moovit://nearby?lat=\(lat)&lon=\(lon)&partner_id=Telegram")
}
}))
if !withDirections {
options.append(OpenInOption(application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location", store: nil), action: {
return .openUrl(url: "here-location://\(lat),\(lon)")

View File

@ -101,7 +101,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
}, seekToTimecode: { _, _, _ in
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState())
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)

View File

@ -3,7 +3,13 @@ import UIKit
import Display
func generatePdfPreviewImage(url: URL, size: CGSize) -> UIImage? {
guard let document = CGPDFDocument(url as CFURL) else { return nil }
guard let data = try? Data(contentsOf: url, options: .mappedIfSafe) else { return nil }
return generatePdfPreviewImage(data: data, size: size)
}
func generatePdfPreviewImage(data: Data, size: CGSize) -> UIImage? {
guard let provider = CGDataProvider(data: data as CFData) else { return nil }
guard let document = CGPDFDocument(provider) else { return nil }
guard let firstPage = document.page(at: 1) else { return nil }
let context = DrawingContext(size: size)

View File

@ -160,7 +160,7 @@ final public class PasscodeEntryController: ViewController {
}).start()
}
let isMainApp = !strongSelf.context.sharedContext.applicationBindings.isMainApp
let isMainApp = strongSelf.context.sharedContext.applicationBindings.isMainApp
let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { settings in
if isMainApp {
return settings.withUpdatedBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState)

View File

@ -5,7 +5,7 @@ import TelegramCore
import MobileCoreServices
private func rtfStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> String {
let test = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), underlineLinks: false, external: true)
let test = stringWithAppliedEntities(text, entities: entities, baseColor: .black, linkColor: .black, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), underlineLinks: false, external: true)
if let data = try? test.data(from: NSRange(location: 0, length: test.length), documentAttributes: [NSAttributedString.DocumentAttributeKey.documentType: NSAttributedString.DocumentType.rtf]) {
if var rtf = String(data: data, encoding: .windowsCP1252) {

View File

@ -272,7 +272,7 @@ public class PeerMediaCollectionController: TelegramController {
}, requestMessageUpdate: { _ in
}, cancelInteractiveKeyboardGestures: {
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
pollActionState: ChatInterfacePollActionState())
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
self.controllerInteraction = controllerInteraction

View File

@ -7,4 +7,5 @@ public extension PresentationSurfaceLevel {
static let overlayMedia = PresentationSurfaceLevel(rawValue: 2)
static let notifications = PresentationSurfaceLevel(rawValue: 3)
static let passcode = PresentationSurfaceLevel(rawValue: 4)
static let update = PresentationSurfaceLevel(rawValue: 5)
}

View File

@ -634,7 +634,7 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: title, leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
let listState = ItemListNodeState(entries: entries, style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: crossfadeState, animateChanges: animateChanges, userInteractionEnabled: emptyStateItem == nil)
let listState = ItemListNodeState(entries: entries, style: .blocks, emptyStateItem: emptyStateItem, crossfadeState: crossfadeState, animateChanges: animateChanges, scrollEnabled: emptyStateItem == nil)
return (controllerState, (listState, arguments))
} |> afterDisposed {

View File

@ -239,9 +239,7 @@ public class ShareRootControllerImpl {
}
break
}
return ActionDisposable {
}
return EmptyDisposable
} |> runOn(Queue.mainQueue())
}

View File

@ -178,7 +178,12 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
}
}
} else {
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 5 * 1024 * 1024)
var thumbnailData: Data?
if mimeType == "application/pdf", let image = generatePdfPreviewImage(data: data, size: CGSize(width: 256.0, height: 256.0)), let jpegData = UIImageJPEGRepresentation(image, 0.5) {
thumbnailData = jpegData
}
return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .data(data), thumbnailData: thumbnailData, mimeType: mimeType, attributes: [.FileName(fileName: fileName ?? "file")], hintFileIsLarge: data.count > 5 * 1024 * 1024)
|> mapError { _ -> Void in return Void() }
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
switch event {

View File

@ -5,6 +5,7 @@ import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import TelegramUIPreferences
enum StickerPackPreviewControllerMode {
case `default`
@ -140,8 +141,13 @@ final class StickerPackPreviewController: ViewController {
}
let account = self.context.account
self.displayNodeDidLoad()
self.stickerPackDisposable.set((self.stickerPackContents.get()
|> mapToSignal { next -> Signal<LoadedStickerPack, NoError> in
self.stickerPackDisposable.set((combineLatest(self.stickerPackContents.get(), self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> take(1))
|> mapToSignal { next, sharedData -> Signal<(LoadedStickerPack, StickerSettings), NoError> in
var stickerSettings = StickerSettings.defaultSettings
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
stickerSettings = value
}
switch next {
case let .result(_, items, _):
var preloadSignals: [Signal<Bool, NoError>] = []
@ -172,26 +178,26 @@ final class StickerPackPreviewController: ViewController {
return !values.contains(false)
}
|> distinctUntilChanged
|> mapToSignal { loaded -> Signal<LoadedStickerPack, NoError> in
|> mapToSignal { loaded -> Signal<(LoadedStickerPack, StickerSettings), NoError> in
if !loaded {
return .single(.fetching)
return .single((.fetching, stickerSettings))
} else {
return .single(next)
return .single((next, stickerSettings))
}
}
default:
return .single(next)
return .single((next, stickerSettings))
}
}
|> deliverOnMainQueue).start(next: { [weak self] next in
if let strongSelf = self {
if case .none = next {
if case .none = next.0 {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: presentationData.strings.StickerPack_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
strongSelf.dismiss()
} else {
strongSelf.controllerNode.updateStickerPack(next)
strongSelf.stickerPackContentsValue = next
strongSelf.controllerNode.updateStickerPack(next.0, stickerSettings: next.1)
strongSelf.stickerPackContentsValue = next.0
}
}
}))

View File

@ -6,6 +6,7 @@ import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
private struct StickerPackPreviewGridEntry: Comparable, Identifiable {
let index: Int
@ -75,6 +76,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
private var stickerPack: LoadedStickerPack?
private var stickerPackUpdated = false
private var stickerPackInitiallyInstalled : Bool?
private var stickerSettings: StickerSettings?
private var currentItems: [StickerPackPreviewGridEntry] = []
@ -131,13 +133,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
super.init()
self.interaction = StickerPackPreviewInteraction(sendSticker: { [weak self] item in
if let strongSelf = self, let sendSticker = strongSelf.sendSticker {
/*if sendSticker(item.file) {
strongSelf.cancel?()
}*/
}
})
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: false)
self.backgroundColor = nil
self.isOpaque = false
@ -375,7 +371,8 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
if self.currentItems.isEmpty && !updatedItems.isEmpty {
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: Font.medium(20.0), linkFont: Font.medium(20.0), boldFont: Font.medium(20.0), italicFont: Font.medium(20.0), boldItalicFont: Font.medium(20.0), fixedFont: Font.medium(20.0))
let font = Font.medium(20.0)
self.contentTitleNode.attributedText = stringWithAppliedEntities(info.title, entities: entities, baseColor: self.presentationData.theme.actionSheet.primaryTextColor, linkColor: self.presentationData.theme.actionSheet.controlAccentColor, baseFont: font, linkFont: font, boldFont: font, italicFont: font, boldItalicFont: font, fixedFont: font, blockQuoteFont: font)
animateIn = true
}
transaction = StickerPackPreviewGridTransaction(previousList: self.currentItems, list: updatedItems, account: self.context.account, interaction: self.interaction)
@ -510,16 +507,16 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
} else {
dismissOnAction = true
}
if let stickerPack = self.stickerPack {
if let stickerPack = self.stickerPack, let stickerSettings = self.stickerSettings {
switch stickerPack {
case let .result(info, items, installed):
if installed {
let _ = removeStickerPackInteractively(postbox: self.context.account.postbox, id: info.id, option: .delete).start()
updateStickerPack(.result(info: info, items: items, installed: false))
self.updateStickerPack(.result(info: info, items: items, installed: false), stickerSettings: stickerSettings)
} else {
let _ = addStickerPackInteractively(postbox: self.context.account.postbox, info: info, items: items).start()
if !dismissOnAction {
updateStickerPack(.result(info: info, items: items, installed: true))
self.updateStickerPack(.result(info: info, items: items, installed: true), stickerSettings: stickerSettings)
}
}
if dismissOnAction {
@ -566,9 +563,13 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
})
}
func updateStickerPack(_ stickerPack: LoadedStickerPack) {
func updateStickerPack(_ stickerPack: LoadedStickerPack, stickerSettings: StickerSettings) {
self.stickerPack = stickerPack
self.stickerSettings = stickerSettings
self.stickerPackUpdated = true
self.interaction.playAnimatedStickers = stickerSettings.loopAnimatedStickers
if let _ = self.containerLayout {
self.dequeueUpdateStickerPack()
}

View File

@ -8,11 +8,10 @@ import Postbox
final class StickerPackPreviewInteraction {
var previewedItem: StickerPreviewPeekItem?
var playAnimatedStickers: Bool
let sendSticker: (StickerPackItem) -> Void
init(sendSticker: @escaping (StickerPackItem) -> Void) {
self.sendSticker = sendSticker
init(playAnimatedStickers: Bool) {
self.playAnimatedStickers = playAnimatedStickers
}
}
@ -53,12 +52,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
override var isVisibleInGrid: Bool {
didSet {
self.animationNode?.visibility = self.isVisibleInGrid
self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true
}
}
private let textNode: ASTextNode
private var currentIsPreviewing = false
private let stickerFetchedDisposable = MetaDisposable()
@ -74,16 +71,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
override init() {
self.imageNode = TransformImageNode()
self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
//self.imageNode.alphaTransitionOnFirstUpdate = true
self.textNode = ASTextNode()
self.textNode.isUserInteractionEnabled = false
self.textNode.displaysAsynchronously = true
super.init()
self.addSubnode(self.imageNode)
//self.addSubnode(self.textNode)
}
deinit {
@ -100,14 +91,6 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
self.interaction = interaction
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem {
var text = ""
for attribute in stickerItem.file.attributes {
if case let .Sticker(displayText, _, _) = attribute {
text = displayText
break
}
}
self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: .black, paragraphAlignment: .right)
if let dimensions = stickerItem.file.dimensions {
if stickerItem.file.isAnimatedSticker {
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0)))
@ -121,7 +104,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
}
}
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached)
self.animationNode?.visibility = self.isVisibleInGrid
self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
} else {
if let animationNode = self.animationNode {
@ -157,9 +140,6 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
animationNode.frame = CGRect(origin: CGPoint(x: floor((bounds.size.width - imageSize.width) / 2.0), y: (bounds.size.height - imageSize.height) / 2.0), size: imageSize)
animationNode.updateLayout(size: imageSize)
}
let boundingFrame = CGRect(origin: CGPoint(x: floor((bounds.size.width - boundingSize.width) / 2.0), y: (bounds.size.height - boundingSize.height) / 2.0), size: boundingSize)
let textSize = CGSize(width: 32.0, height: 24.0)
self.textNode.frame = CGRect(origin: CGPoint(x: boundingFrame.maxX - 1.0 - textSize.width, y: boundingFrame.height + 10.0 - textSize.height), size: textSize)
}
}
@ -169,7 +149,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
@objc func imageNodeTap(_ recognizer: UITapGestureRecognizer) {
if let interaction = self.interaction, let (_, item, _) = self.currentState, case .ended = recognizer.state {
interaction.sendSticker(item)
//interaction.sendSticker(item)
}
}

View File

@ -45,24 +45,25 @@ func chatInputStateStringWithAppliedEntities(_ text: String, entities: [MessageT
return string
}
func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, underlineLinks: Bool = true, external: Bool = false) -> NSAttributedString {
func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], baseColor: UIColor, linkColor: UIColor, baseFont: UIFont, linkFont: UIFont, boldFont: UIFont, italicFont: UIFont, boldItalicFont: UIFont, fixedFont: UIFont, blockQuoteFont: UIFont, underlineLinks: Bool = true, external: Bool = false) -> NSAttributedString {
var nsString: NSString?
let string = NSMutableAttributedString(string: text, attributes: [NSAttributedStringKey.font: baseFont, NSAttributedStringKey.foregroundColor: baseColor])
var skipEntity = false
let stringLength = string.length
var underlineAllLinks = false
if linkColor.isEqual(baseColor) {
underlineAllLinks = true
}
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]
var rangeOffset: Int = 0
for i in 0 ..< entities.count {
if skipEntity {
skipEntity = false
continue
}
let stringLength = string.length
let entity = entities[i]
var range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound)
var range = NSRange(location: entity.range.lowerBound + rangeOffset, length: entity.range.upperBound - entity.range.lowerBound)
if nsString == nil {
nsString = text as NSString
}
@ -188,6 +189,25 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
string.addAttribute(NSAttributedStringKey(rawValue: TelegramTextAttributes.BotCommand), value: nsString!.substring(with: range), range: range)
case .Code, .Pre:
string.addAttribute(NSAttributedStringKey.font, value: fixedFont, range: range)
case .BlockQuote:
if let fontAttribute = fontAttributes[range] {
fontAttributes[range] = fontAttribute.union(.blockQuote)
} else {
fontAttributes[range] = .blockQuote
}
let paragraphBreak = "\n"
string.insert(NSAttributedString(string: paragraphBreak), at: range.lowerBound)
let paragraphRange = NSRange(location: range.lowerBound + paragraphBreak.count, length: range.upperBound - range.lowerBound)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.headIndent = 10.0
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: paragraphStyle.headIndent, options: [:])]
string.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraphStyle, range: paragraphRange)
string.insert(NSAttributedString(string: paragraphBreak), at: paragraphRange.upperBound)
rangeOffset += paragraphBreak.count
case let .Custom(type):
if type == ApplicationSpecificEntityType.Timecode {
string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range)
@ -208,7 +228,9 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
for (range, fontAttributes) in fontAttributes {
var font: UIFont?
if fontAttributes == [.bold, .italic] {
if fontAttributes.contains(.blockQuote) {
font = blockQuoteFont
} else if fontAttributes == [.bold, .italic] {
font = boldItalicFont
} else if fontAttributes == [.bold] {
font = boldFont

View File

@ -563,7 +563,6 @@ static void set_bits(uint8_t *bytes, int32_t bitOffset, int32_t numBits, int32_t
int _waveformPeakCount = 0;
while (iPodAssetReader.status == AVAssetReaderStatusReading) {
// Check if the available buffer space is enough to hold at least one cycle of the sample data
CMSampleBufferRef nextBuffer = [readerOutput copyNextSampleBuffer];
if (nextBuffer) {

View File

@ -57,7 +57,7 @@ final class TermsOfServiceControllerNode: ViewControllerTracingNode {
self.contentTextNode = ImmediateTextNode()
self.contentTextNode.displaysAsynchronously = false
self.contentTextNode.maximumNumberOfLines = 0
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primary, linkColor: theme.accent, baseFont: Font.regular(15.0), linkFont: Font.regular(15.0), boldFont: Font.semibold(15.0), italicFont: Font.italic(15.0), boldItalicFont: Font.semiboldItalic(15.0), fixedFont: Font.monospace(15.0))
self.contentTextNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: theme.primary, linkColor: theme.accent, baseFont: Font.regular(15.0), linkFont: Font.regular(15.0), boldFont: Font.semibold(15.0), italicFont: Font.italic(15.0), boldItalicFont: Font.semiboldItalic(15.0), fixedFont: Font.monospace(15.0), blockQuoteFont: Font.regular(15.0))
self.toolbarNode = ASDisplayNode()
self.toolbarSeparatorNode = ASDisplayNode()

View File

@ -41,4 +41,5 @@ struct TelegramTextAttributes {
static let BotCommand = "TelegramBotCommand"
static let Hashtag = "TelegramHashtag"
static let Timecode = "TelegramTimecode"
static let BlockQuote = "TelegramBlockQuote"
}

View File

@ -113,9 +113,9 @@ public func updateInfoController(context: AccountContext, appUpdateInfo: AppUpda
appIcon = appIcons.filter { $0.isDefault }.first
}
let leftNavigationButton = appUpdateInfo.popup ? ItemListNavigationButton(content: .text(presentationData.strings.Update_Skip), style: .regular, enabled: true, action: {
let leftNavigationButton = appUpdateInfo.blocking ? nil : ItemListNavigationButton(content: .text(presentationData.strings.Update_Skip), style: .regular, enabled: true, action: {
dismissImpl?()
}) : nil
})
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Update_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(entries: updateInfoControllerEntries(theme: presentationData.theme, strings: presentationData.strings, appIcon: appIcon, appUpdateInfo: appUpdateInfo), style: .blocks, animateChanges: false)

View File

@ -208,7 +208,7 @@ class UpdateInfoItemNode: ListViewItemNode {
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 88.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let string = stringWithAppliedEntities(item.text, entities: item.entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont)
let string = stringWithAppliedEntities(item.text, entities: item.entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFixedFont, blockQuoteFont: textFont)
let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - 28.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let contentSize: CGSize

View File

@ -270,7 +270,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
}
} else {
if let peer = peer as? TelegramUser, peer.botInfo == nil {
return .peer(peer.id, .info)
return .peer(peer.id, .chat(textInputState: nil, messageId: nil))
} else {
return .peer(peer.id, .chat(textInputState: nil, messageId: nil))
}

View File

@ -10,21 +10,25 @@ public enum EmojiStickerSuggestionMode: Int32 {
public struct StickerSettings: PreferencesEntry, Equatable {
public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode
public var loopAnimatedStickers: Bool
public static var defaultSettings: StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: .all)
return StickerSettings(emojiStickerSuggestionMode: .all, loopAnimatedStickers: true)
}
init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode) {
init(emojiStickerSuggestionMode: EmojiStickerSuggestionMode, loopAnimatedStickers: Bool) {
self.emojiStickerSuggestionMode = emojiStickerSuggestionMode
self.loopAnimatedStickers = loopAnimatedStickers
}
public init(decoder: PostboxDecoder) {
self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: decoder.decodeInt32ForKey("emojiStickerSuggestionMode", orElse: 0))!
self.loopAnimatedStickers = decoder.decodeBoolForKey("loopAnimatedStickers", orElse: true)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt32(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode")
encoder.encodeBool(self.loopAnimatedStickers, forKey: "loopAnimatedStickers")
}
public func isEqual(to: PreferencesEntry) -> Bool {
@ -36,11 +40,15 @@ public struct StickerSettings: PreferencesEntry, Equatable {
}
public static func ==(lhs: StickerSettings, rhs: StickerSettings) -> Bool {
return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode
return lhs.emojiStickerSuggestionMode == rhs.emojiStickerSuggestionMode && lhs.loopAnimatedStickers == rhs.loopAnimatedStickers
}
public func withUpdatedEmojiStickerSuggestionMode(_ emojiStickerSuggestionMode: EmojiStickerSuggestionMode) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode)
return StickerSettings(emojiStickerSuggestionMode: emojiStickerSuggestionMode, loopAnimatedStickers: self.loopAnimatedStickers)
}
public func withUpdatedLoopAnimatedStickers(_ loopAnimatedStickers: Bool) -> StickerSettings {
return StickerSettings(emojiStickerSuggestionMode: self.emojiStickerSuggestionMode, loopAnimatedStickers: loopAnimatedStickers)
}
}

View File

@ -2388,10 +2388,10 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA
stm->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6));
stm->decoder=NULL;
}else if(stm->type==STREAM_TYPE_VIDEO){
if(!stm->packetReassembler){
stm->packetReassembler=make_shared<PacketReassembler>();
stm->packetReassembler->SetCallback(bind(&VoIPController::ProcessIncomingVideoFrame, this, placeholders::_1, placeholders::_2, placeholders::_3));
}
// if(!stm->packetReassembler){
// stm->packetReassembler=make_shared<PacketReassembler>();
// stm->packetReassembler->SetCallback(bind(&VoIPController::ProcessIncomingVideoFrame, this, placeholders::_1, placeholders::_2, placeholders::_3));
// }
}else{
LOGW("Unknown incoming stream type: %d", stm->type);
continue;