mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-05 05:51:42 +00:00
Merge commit '72dd8f1151cfd34e40ef4cf3986a8b2dd42eeb23'
This commit is contained in:
commit
093ec63cc5
@ -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/>
|
||||
|
||||
@ -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.";
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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?()
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -698,6 +698,9 @@ public class ChatMessageItemView: ListViewItemNode {
|
||||
func updateAutomaticMediaDownloadSettings() {
|
||||
}
|
||||
|
||||
func updateStickerSettings() {
|
||||
}
|
||||
|
||||
func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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()))
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
Binary file not shown.
@ -239,9 +239,7 @@ public class ShareRootControllerImpl {
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return ActionDisposable {
|
||||
}
|
||||
return EmptyDisposable
|
||||
} |> runOn(Queue.mainQueue())
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -41,4 +41,5 @@ struct TelegramTextAttributes {
|
||||
static let BotCommand = "TelegramBotCommand"
|
||||
static let Hashtag = "TelegramHashtag"
|
||||
static let Timecode = "TelegramTimecode"
|
||||
static let BlockQuote = "TelegramBlockQuote"
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user