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/>
|
<false/>
|
||||||
<key>LSApplicationQueriesSchemes</key>
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>dbapi-3</string>
|
|
||||||
<string>instagram</string>
|
<string>instagram</string>
|
||||||
<string>googledrive</string>
|
|
||||||
<string>comgooglemaps-x-callback</string>
|
<string>comgooglemaps-x-callback</string>
|
||||||
<string>foursquare</string>
|
<string>foursquare</string>
|
||||||
<string>here-location</string>
|
<string>here-location</string>
|
||||||
@ -257,6 +255,7 @@
|
|||||||
<string>opera-https</string>
|
<string>opera-https</string>
|
||||||
<string>firefox-focus</string>
|
<string>firefox-focus</string>
|
||||||
<string>ddgQuickLink</string>
|
<string>ddgQuickLink</string>
|
||||||
|
<string>moovit</string>
|
||||||
</array>
|
</array>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<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.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.";
|
"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 rotated = false
|
||||||
public final var experimentalSnapScrollToItem = 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
|
private final var invisibleInset: CGFloat = 500.0
|
||||||
public var preloadPages: Bool = true {
|
public var preloadPages: Bool = true {
|
||||||
didSet {
|
didSet {
|
||||||
|
|||||||
@ -5,6 +5,14 @@ import CoreText
|
|||||||
|
|
||||||
private let defaultFont = UIFont.systemFont(ofSize: 15.0)
|
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 {
|
private final class TextNodeLine {
|
||||||
let line: CTLine
|
let line: CTLine
|
||||||
let frame: CGRect
|
let frame: CGRect
|
||||||
@ -21,7 +29,7 @@ private final class TextNodeLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class TextNodeStrikethrough {
|
private final class TextNodeBlockQuote {
|
||||||
let frame: CGRect
|
let frame: CGRect
|
||||||
|
|
||||||
init(frame: CGRect) {
|
init(frame: CGRect) {
|
||||||
@ -83,8 +91,9 @@ public final class TextNodeLayoutArguments {
|
|||||||
public let lineSpacing: CGFloat
|
public let lineSpacing: CGFloat
|
||||||
public let cutout: TextNodeCutout?
|
public let cutout: TextNodeCutout?
|
||||||
public let insets: UIEdgeInsets
|
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.attributedString = attributedString
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.maximumNumberOfLines = maximumNumberOfLines
|
self.maximumNumberOfLines = maximumNumberOfLines
|
||||||
@ -94,6 +103,7 @@ public final class TextNodeLayoutArguments {
|
|||||||
self.lineSpacing = lineSpacing
|
self.lineSpacing = lineSpacing
|
||||||
self.cutout = cutout
|
self.cutout = cutout
|
||||||
self.insets = insets
|
self.insets = insets
|
||||||
|
self.lineColor = lineColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,9 +121,11 @@ public final class TextNodeLayout: NSObject {
|
|||||||
public let truncated: Bool
|
public let truncated: Bool
|
||||||
fileprivate let firstLineOffset: CGFloat
|
fileprivate let firstLineOffset: CGFloat
|
||||||
fileprivate let lines: [TextNodeLine]
|
fileprivate let lines: [TextNodeLine]
|
||||||
|
fileprivate let blockQuotes: [TextNodeBlockQuote]
|
||||||
|
fileprivate let lineColor: UIColor?
|
||||||
public let hasRTL: Bool
|
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.attributedString = attributedString
|
||||||
self.maximumNumberOfLines = maximumNumberOfLines
|
self.maximumNumberOfLines = maximumNumberOfLines
|
||||||
self.truncationType = truncationType
|
self.truncationType = truncationType
|
||||||
@ -126,7 +138,9 @@ public final class TextNodeLayout: NSObject {
|
|||||||
self.truncated = truncated
|
self.truncated = truncated
|
||||||
self.firstLineOffset = firstLineOffset
|
self.firstLineOffset = firstLineOffset
|
||||||
self.lines = lines
|
self.lines = lines
|
||||||
|
self.blockQuotes = blockQuotes
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
|
self.lineColor = lineColor
|
||||||
var hasRTL = false
|
var hasRTL = false
|
||||||
for line in lines {
|
for line in lines {
|
||||||
if line.isRTL {
|
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 {
|
if let attributedString = attributedString {
|
||||||
let stringLength = attributedString.length
|
let stringLength = attributedString.length
|
||||||
|
|
||||||
@ -582,11 +596,12 @@ public class TextNode: ASDisplayNode {
|
|||||||
let fontLineSpacing = floor(fontLineHeight * lineSpacingFactor)
|
let fontLineSpacing = floor(fontLineHeight * lineSpacingFactor)
|
||||||
|
|
||||||
var lines: [TextNodeLine] = []
|
var lines: [TextNodeLine] = []
|
||||||
|
var blockQuotes: [TextNodeBlockQuote] = []
|
||||||
|
|
||||||
var maybeTypesetter: CTTypesetter?
|
var maybeTypesetter: CTTypesetter?
|
||||||
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
|
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
|
||||||
if maybeTypesetter == nil {
|
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!
|
let typesetter = maybeTypesetter!
|
||||||
@ -682,11 +697,28 @@ public class TextNode: ASDisplayNode {
|
|||||||
truncated = true
|
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 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.height += fontLineHeight + fontLineSpacing
|
||||||
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
|
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
|
||||||
|
|
||||||
|
if headIndent > 0.0 {
|
||||||
|
blockQuotes.append(TextNodeBlockQuote(frame: lineFrame))
|
||||||
|
}
|
||||||
|
|
||||||
var isRTL = false
|
var isRTL = false
|
||||||
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
|
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
|
||||||
if glyphRuns.count != 0 {
|
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))
|
lines.append(TextNodeLine(line: coreTextLine, frame: lineFrame, range: NSMakeRange(lineRange.location, lineRange.length), isRTL: isRTL, strikethroughs: strikethroughs))
|
||||||
|
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
if lineCharacterCount > 0 {
|
if lineCharacterCount > 0 {
|
||||||
@ -719,11 +742,27 @@ public class TextNode: ASDisplayNode {
|
|||||||
let coreTextLine = CTTypesetterCreateLineWithOffset(typesetter, lineRange, 100.0)
|
let coreTextLine = CTTypesetterCreateLineWithOffset(typesetter, lineRange, 100.0)
|
||||||
lastLineCharacterIndex += lineCharacterCount
|
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 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.height += fontLineHeight
|
||||||
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
|
layoutSize.width = max(layoutSize.width, lineWidth + lineAdditionalWidth)
|
||||||
|
|
||||||
|
if headIndent > 0.0 {
|
||||||
|
blockQuotes.append(TextNodeBlockQuote(frame: lineFrame))
|
||||||
|
}
|
||||||
|
|
||||||
var isRTL = false
|
var isRTL = false
|
||||||
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
|
let glyphRuns = CTLineGetGlyphRuns(coreTextLine) as NSArray
|
||||||
if glyphRuns.count != 0 {
|
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))
|
lines.append(TextNodeLine(line: coreTextLine, frame: lineFrame, range: NSMakeRange(lineRange.location, lineRange.length), isRTL: isRTL, strikethroughs: strikethroughs))
|
||||||
} else {
|
} else {
|
||||||
if !lines.isEmpty {
|
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 {
|
} 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 textMatrix = context.textMatrix
|
||||||
let textPosition = context.textPosition
|
let textPosition = context.textPosition
|
||||||
//CGContextSaveGState(context)
|
|
||||||
|
|
||||||
context.textMatrix = CGAffineTransform(scaleX: 1.0, y: -1.0)
|
context.textMatrix = CGAffineTransform(scaleX: 1.0, y: -1.0)
|
||||||
|
|
||||||
//let clipRect = CGContextGetClipBoundingBox(context)
|
|
||||||
|
|
||||||
let alignment = layout.alignment
|
let alignment = layout.alignment
|
||||||
let offset = CGPoint(x: layout.insets.left, y: layout.insets.top)
|
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.textMatrix = textMatrix
|
||||||
context.textPosition = CGPoint(x: textPosition.x, y: textPosition.y)
|
context.textPosition = CGPoint(x: textPosition.x, y: textPosition.y)
|
||||||
}
|
}
|
||||||
@ -872,11 +927,11 @@ public class TextNode: ASDisplayNode {
|
|||||||
if stringMatch {
|
if stringMatch {
|
||||||
layout = existingLayout
|
layout = existingLayout
|
||||||
} else {
|
} 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
|
updated = true
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2075,6 +2075,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP
|
|||||||
var isContactUpdates: [(PeerId, Bool)] = []
|
var isContactUpdates: [(PeerId, Bool)] = []
|
||||||
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
|
var stickerPackOperations: [AccountStateUpdateStickerPacksOperation] = []
|
||||||
var recentlyUsedStickers: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
|
var recentlyUsedStickers: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
|
||||||
|
var slowModeLastMessageTimeouts:[PeerId : Int32] = [:]
|
||||||
var recentlyUsedGifs: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
|
var recentlyUsedGifs: [MediaId: (MessageIndex, TelegramMediaFile)] = [:]
|
||||||
var syncRecentGifs = false
|
var syncRecentGifs = false
|
||||||
var langPackDifferences: [String: [Api.LangPackDifference]] = [:]
|
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 {
|
if !message.flags.contains(.Incoming), message.forwardInfo == nil {
|
||||||
inner: for media in message.media {
|
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 {
|
if syncRecentGifs {
|
||||||
addSynchronizeSavedGifsOperation(transaction: transaction, operation: .sync)
|
addSynchronizeSavedGifsOperation(transaction: transaction, operation: .sync)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -73,7 +73,10 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
|
|||||||
|
|
||||||
let info: TelegramChannelInfo
|
let info: TelegramChannelInfo
|
||||||
if (flags & Int32(1 << 8)) != 0 {
|
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))
|
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
|
||||||
} else {
|
} else {
|
||||||
var infoFlags = TelegramChannelBroadcastFlags()
|
var infoFlags = TelegramChannelBroadcastFlags()
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import Foundation
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public struct AppUpdateInfo: Equatable {
|
public struct AppUpdateInfo: Equatable {
|
||||||
public let popup: Bool
|
public let blocking: Bool
|
||||||
public let version: String
|
public let version: String
|
||||||
public let text: String
|
public let text: String
|
||||||
public let entities: [MessageTextEntity]
|
public let entities: [MessageTextEntity]
|
||||||
@ -26,7 +26,7 @@ extension AppUpdateInfo {
|
|||||||
init?(apiAppUpdate: Api.help.AppUpdate) {
|
init?(apiAppUpdate: Api.help.AppUpdate) {
|
||||||
switch apiAppUpdate {
|
switch apiAppUpdate {
|
||||||
case let .appUpdate(flags, _, version, text, entities, _, _):
|
case let .appUpdate(flags, _, version, text, entities, _, _):
|
||||||
self.popup = (flags & (1 << 0)) != 0
|
self.blocking = (flags & (1 << 0)) != 0
|
||||||
self.version = version
|
self.version = version
|
||||||
self.text = text
|
self.text = text
|
||||||
self.entities = messageTextEntitiesFromApiEntities(entities)
|
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)
|
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))
|
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 {
|
for file in sentStickers {
|
||||||
@ -173,6 +187,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
|
|||||||
for file in sentGifs {
|
for file in sentGifs {
|
||||||
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 200)
|
transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentGifs, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateManager.addUpdates(result)
|
stateManager.addUpdates(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ func augmentMediaWithReference(_ mediaReference: AnyMediaReference) -> Media {
|
|||||||
if file.partialReference != nil {
|
if file.partialReference != nil {
|
||||||
return file
|
return file
|
||||||
} else {
|
} else {
|
||||||
return file.withUpdatedPartialReference(mediaReference.partial)
|
return file.withUpdatedPartialReference(mediaReference.partial)
|
||||||
}
|
}
|
||||||
} else if let image = mediaReference.media as? TelegramMediaImage {
|
} else if let image = mediaReference.media as? TelegramMediaImage {
|
||||||
if image.partialReference != nil {
|
if image.partialReference != nil {
|
||||||
@ -367,7 +367,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
let authorId: 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
|
authorId = peer.id
|
||||||
} else {
|
} else {
|
||||||
authorId = account.peerId
|
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 sourceMessage = sourceMessage, let author = sourceMessage.author ?? sourceMessage.peers[sourceMessage.id.peerId] {
|
||||||
if let peer = peer as? TelegramSecretChat {
|
if let peer = peer as? TelegramSecretChat {
|
||||||
var isAction = false
|
var isAction = false
|
||||||
var mediaDuration: Int32?
|
|
||||||
for media in sourceMessage.media {
|
for media in sourceMessage.media {
|
||||||
if let _ = media as? TelegramMediaAction {
|
if let _ = media as? TelegramMediaAction {
|
||||||
isAction = true
|
isAction = true
|
||||||
} else if let file = media as? TelegramMediaFile, let duration = file.duration {
|
|
||||||
mediaDuration = duration
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !disableAutoremove, let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction {
|
if !disableAutoremove, let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction {
|
||||||
|
|||||||
@ -476,17 +476,17 @@ private enum UploadedMediaFileAndThumbnail {
|
|||||||
|
|
||||||
private func uploadedThumbnail(network: Network, postbox: Postbox, resourceReference: MediaResourceReference) -> Signal<Api.InputFile?, PendingMessageUploadError> {
|
private func uploadedThumbnail(network: Network, postbox: Postbox, resourceReference: MediaResourceReference) -> Signal<Api.InputFile?, PendingMessageUploadError> {
|
||||||
return multipartUpload(network: network, postbox: postbox, source: .resource(resourceReference), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false)
|
return multipartUpload(network: network, postbox: postbox, source: .resource(resourceReference), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false)
|
||||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||||
|> mapToSignal { result -> Signal<Api.InputFile?, PendingMessageUploadError> in
|
|> mapToSignal { result -> Signal<Api.InputFile?, PendingMessageUploadError> in
|
||||||
switch result {
|
switch result {
|
||||||
case .progress:
|
case .progress:
|
||||||
return .complete()
|
return .complete()
|
||||||
case let .inputFile(inputFile):
|
case let .inputFile(inputFile):
|
||||||
return .single(inputFile)
|
return .single(inputFile)
|
||||||
case .inputSecretFile:
|
case .inputSecretFile:
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func statsCategoryForFileWithAttributes(_ attributes: [TelegramMediaFileAttribute]) -> MediaResourceStatsCategory {
|
public func statsCategoryForFileWithAttributes(_ attributes: [TelegramMediaFileAttribute]) -> MediaResourceStatsCategory {
|
||||||
|
|||||||
@ -30,11 +30,8 @@ public func updateChannelSlowModeInteractively(postbox: Postbox, network: Networ
|
|||||||
accountStateManager.addUpdates(updates)
|
accountStateManager.addUpdates(updates)
|
||||||
return postbox.transaction { transaction -> Void in
|
return postbox.transaction { transaction -> Void in
|
||||||
transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, currentData 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)
|
return currentData.withUpdatedSlowModeTimeout(slowModeTimeout: timeout)
|
||||||
} else {
|
|
||||||
return currentData
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|> introduceError(UpdateChannelSlowModeError.self)
|
|> introduceError(UpdateChannelSlowModeError.self)
|
||||||
|
|||||||
@ -21,6 +21,20 @@ public struct StandaloneUploadSecretFile {
|
|||||||
let key: SecretFileEncryptionKey
|
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 {
|
public enum StandaloneUploadMediaResult {
|
||||||
case media(AnyMediaReference)
|
case media(AnyMediaReference)
|
||||||
}
|
}
|
||||||
@ -30,7 +44,22 @@ public enum StandaloneUploadMediaEvent {
|
|||||||
case result(StandaloneUploadMediaResult)
|
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)
|
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 }
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|> mapToSignal { next -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|> mapToSignal { next -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
@ -91,65 +120,99 @@ 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> {
|
public func standaloneUploadedFile(account: Account, peerId: PeerId, text: String, source: MultipartUploadSource, thumbnailData: Data? = nil, 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)
|
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 }
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|> mapToSignal { next -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|
||||||
switch next {
|
let uploadThumbnail: Signal<StandaloneUploadMediaThumbnailResult, StandaloneUploadMediaError>
|
||||||
case let .inputFile(inputFile):
|
if let thumbnailData = thumbnailData {
|
||||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
uploadThumbnail = .single(.pending)
|
||||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
|> then(
|
||||||
}
|
uploadedThumbnail(network: account.network, postbox: account.postbox, data: thumbnailData)
|
||||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|> map { result in
|
||||||
if let inputPeer = inputPeer {
|
if let result = result {
|
||||||
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)))
|
return .file(result)
|
||||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
} else {
|
||||||
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
return .none
|
||||||
switch media {
|
|
||||||
case let .messageMediaDocument(_, document, _):
|
|
||||||
if let document = document {
|
|
||||||
if let mediaFile = telegramMediaFileFromApiDocument(document) {
|
|
||||||
return .single(.result(.media(.standalone(media: mediaFile))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return .fail(.generic)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .fail(.generic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .inputSecretFile(file, size, 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)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|> introduceError(StandaloneUploadMediaError.self)
|
|
||||||
|> mapToSignal { inputChat -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|
||||||
guard let inputChat = inputChat else {
|
|
||||||
return .fail(.generic)
|
|
||||||
}
|
|
||||||
return account.network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file))
|
|
||||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic
|
|
||||||
}
|
|
||||||
|> mapToSignal { result -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
|
||||||
switch result {
|
|
||||||
case let .encryptedFile(id, accessHash, size, dcId, _):
|
|
||||||
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: size, datacenterId: Int(dcId), key: key), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: attributes)
|
|
||||||
|
|
||||||
return .single(.result(.media(.standalone(media: media))))
|
|
||||||
case .encryptedFileEmpty:
|
|
||||||
return .fail(.generic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
uploadThumbnail = .single(.none)
|
||||||
|
}
|
||||||
|
|
||||||
|
return combineLatest(upload, uploadThumbnail)
|
||||||
|
|> mapToSignal { result, thumbnail in
|
||||||
|
switch result {
|
||||||
case let .progress(progress):
|
case let .progress(progress):
|
||||||
return .single(.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)
|
||||||
|
}
|
||||||
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
|
if let inputPeer = inputPeer {
|
||||||
|
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 {
|
||||||
|
case let .messageMediaDocument(_, document, _):
|
||||||
|
if let document = document {
|
||||||
|
if let mediaFile = telegramMediaFileFromApiDocument(document) {
|
||||||
|
return .single(.result(.media(.standalone(media: mediaFile))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|> introduceError(StandaloneUploadMediaError.self)
|
||||||
|
|> mapToSignal { inputChat -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
|
guard let inputChat = inputChat else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
return account.network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file))
|
||||||
|
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||||
|
|> mapToSignal { result -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||||
|
switch result {
|
||||||
|
case let .encryptedFile(id, accessHash, size, dcId, _):
|
||||||
|
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: size, datacenterId: Int(dcId), key: key), previewRepresentations: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: attributes)
|
||||||
|
|
||||||
|
return .single(.result(.media(.standalone(media: media))))
|
||||||
|
case .encryptedFileEmpty:
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .progress:
|
||||||
|
return .never()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,7 @@ public struct TelegramChannelGroupFlags: OptionSet {
|
|||||||
public init(rawValue: Int32) {
|
public init(rawValue: Int32) {
|
||||||
self.rawValue = rawValue
|
self.rawValue = rawValue
|
||||||
}
|
}
|
||||||
|
public static let isEnabledSlowMode = TelegramChannelGroupFlags(rawValue: 1 << 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct TelegramChannelGroupInfo: Equatable {
|
public struct TelegramChannelGroupInfo: Equatable {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -522,7 +522,7 @@ final class AuthorizedApplicationContext {
|
|||||||
strongSelf.currentAppUpdateInfo = appUpdateInfo
|
strongSelf.currentAppUpdateInfo = appUpdateInfo
|
||||||
if let appUpdateInfo = appUpdateInfo {
|
if let appUpdateInfo = appUpdateInfo {
|
||||||
let controller = updateInfoController(context: strongSelf.context, 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 Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
public enum ArchivedStickerPacksControllerMode {
|
public enum ArchivedStickerPacksControllerMode {
|
||||||
case stickers
|
case stickers
|
||||||
@ -65,7 +66,7 @@ private enum ArchivedStickerPacksEntryId: Hashable {
|
|||||||
|
|
||||||
private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
|
private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
|
||||||
case info(PresentationTheme, String)
|
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 {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -78,7 +79,7 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
switch self {
|
switch self {
|
||||||
case .info:
|
case .info:
|
||||||
return .index(0)
|
return .index(0)
|
||||||
case let .pack(_, _, _, info, _, _, _, _):
|
case let .pack(_, _, _, info, _, _, _, _, _):
|
||||||
return .pack(info.id)
|
return .pack(info.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,8 +92,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsEnabled, lhsEditing):
|
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsEnabled, lhsEditing):
|
||||||
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsEnabled, rhsEditing) = rhs {
|
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsEnabled, rhsEditing) = rhs {
|
||||||
if lhsIndex != rhsIndex {
|
if lhsIndex != rhsIndex {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -111,6 +112,9 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
if lhsCount != rhsCount {
|
if lhsCount != rhsCount {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhsEnabled != rhsEnabled {
|
if lhsEnabled != rhsEnabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -133,9 +137,9 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case let .pack(lhsIndex, _, _, _, _, _, _, _):
|
case let .pack(lhsIndex, _, _, _, _, _, _, _, _):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case let .pack(rhsIndex, _, _, _, _, _, _, _):
|
case let .pack(rhsIndex, _, _, _, _, _, _, _, _):
|
||||||
return lhsIndex < rhsIndex
|
return lhsIndex < rhsIndex
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@ -147,8 +151,8 @@ private enum ArchivedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
switch self {
|
switch self {
|
||||||
case let .info(theme, text):
|
case let .info(theme, text):
|
||||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section)
|
||||||
case let .pack(_, theme, strings, info, topItem, count, enabled, editing):
|
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, sectionId: self.section, action: {
|
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)
|
arguments.openStickerPack(info)
|
||||||
}, setPackIdWithRevealedOptions: { current, previous in
|
}, setPackIdWithRevealedOptions: { current, previous in
|
||||||
arguments.setPackIdWithRevealedOptions(current, previous)
|
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] = []
|
var entries: [ArchivedStickerPacksEntry] = []
|
||||||
|
|
||||||
if let packs = packs {
|
if let packs = packs {
|
||||||
@ -219,7 +223,7 @@ private func archivedStickerPacksControllerEntries(presentationData: Presentatio
|
|||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
for item in packs {
|
for item in packs {
|
||||||
if !installedIds.contains(item.info.id) {
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -369,9 +373,14 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
|
|||||||
|
|
||||||
var previousPackCount: Int?
|
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
|
|> 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?
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
if let packs = packs, packs.count != 0 {
|
if let packs = packs, packs.count != 0 {
|
||||||
if state.editing {
|
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 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))
|
return (controllerState, (listState, arguments))
|
||||||
} |> afterDisposed {
|
} |> afterDisposed {
|
||||||
actionsDisposable.dispose()
|
actionsDisposable.dispose()
|
||||||
|
|||||||
@ -277,7 +277,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
}
|
}
|
||||||
var dismissImpl: (() -> Void)?
|
var dismissImpl: (() -> Void)?
|
||||||
let alertTheme = AlertControllerTheme(presentationTheme: strongSelf.theme)
|
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: [
|
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: {
|
TextAlertAction(type: .defaultAction, title: strongSelf.strings.Login_TermsOfServiceAgree, action: {
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
|
|||||||
@ -288,7 +288,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
|
|||||||
previousState = blockedPeersState
|
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 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))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -152,7 +152,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
|||||||
updatedTextAndEntities = (item.text, generateTextEntities(item.text, enabledTypes: .all))
|
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 horizontalEdgeInset: CGFloat = 10.0 + params.leftInset
|
||||||
let horizontalContentInset: CGFloat = 12.0
|
let horizontalContentInset: CGFloat = 12.0
|
||||||
|
|||||||
@ -208,6 +208,9 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
|||||||
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||||
private var automaticMediaDownloadSettingsDisposable: Disposable?
|
private var automaticMediaDownloadSettingsDisposable: Disposable?
|
||||||
|
|
||||||
|
private var stickerSettings: ChatInterfaceStickerSettings
|
||||||
|
private var stickerSettingsDisposable: Disposable?
|
||||||
|
|
||||||
private var applicationInForegroundDisposable: Disposable?
|
private var applicationInForegroundDisposable: Disposable?
|
||||||
|
|
||||||
private var checkedPeerChatServiceActions = false
|
private var checkedPeerChatServiceActions = false
|
||||||
@ -264,6 +267,8 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
|||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.automaticMediaDownloadSettings = context.sharedContext.currentAutomaticMediaDownloadSettings.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)
|
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
|
var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none
|
||||||
@ -1378,8 +1383,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget,
|
|||||||
}, cancelInteractiveKeyboardGestures: { [weak self] in
|
}, cancelInteractiveKeyboardGestures: { [weak self] in
|
||||||
(self?.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
(self?.view.window as? WindowHost)?.cancelInteractiveKeyboardGestures()
|
||||||
self?.chatDisplayNode.cancelInteractiveKeyboardGestures()
|
self?.chatDisplayNode.cancelInteractiveKeyboardGestures()
|
||||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings)
|
||||||
pollActionState: ChatInterfacePollActionState())
|
|
||||||
|
|
||||||
self.controllerInteraction = controllerInteraction
|
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
|
self.applicationInForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
|
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
|
||||||
@ -6843,23 +6865,22 @@ 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
|
var canEdit = false
|
||||||
if let strongSelf = self {
|
self.updateChatPresentationInterfaceState(animated: false, interactive: false, { state in
|
||||||
var canEdit = false
|
if state.interfaceState.effectiveInputState.inputText.length == 0 && state.interfaceState.editMessage == nil {
|
||||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in
|
canEdit = true
|
||||||
if state.interfaceState.effectiveInputState.inputText.length == 0 && state.interfaceState.editMessage == nil {
|
|
||||||
canEdit = true
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
if canEdit {
|
|
||||||
strongSelf.interfaceInteraction?.setupEditMessage(message.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
|
||||||
|
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] = [
|
let otherShortcuts: [KeyShortcut] = [
|
||||||
|
|||||||
@ -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 {
|
public enum ChatControllerInteractionLongTapAction {
|
||||||
case url(String)
|
case url(String)
|
||||||
case mention(String)
|
case mention(String)
|
||||||
@ -104,9 +120,10 @@ public final class ChatControllerInteraction {
|
|||||||
var contextHighlightedState: ChatInterfaceHighlightedState?
|
var contextHighlightedState: ChatInterfaceHighlightedState?
|
||||||
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||||
var pollActionState: ChatInterfacePollActionState
|
var pollActionState: ChatInterfacePollActionState
|
||||||
|
var stickerSettings: ChatInterfaceStickerSettings
|
||||||
var searchTextHighightState: String?
|
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.openMessage = openMessage
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.openPeerMention = openPeerMention
|
self.openPeerMention = openPeerMention
|
||||||
@ -154,6 +171,7 @@ public final class ChatControllerInteraction {
|
|||||||
self.automaticMediaDownloadSettings = automaticMediaDownloadSettings
|
self.automaticMediaDownloadSettings = automaticMediaDownloadSettings
|
||||||
|
|
||||||
self.pollActionState = pollActionState
|
self.pollActionState = pollActionState
|
||||||
|
self.stickerSettings = stickerSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
static var `default`: ChatControllerInteraction {
|
static var `default`: ChatControllerInteraction {
|
||||||
@ -175,6 +193,6 @@ public final class ChatControllerInteraction {
|
|||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
pollActionState: ChatInterfacePollActionState())
|
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1497,6 +1497,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.historyNode.prefetchManager.updateAutoDownloadSettings(settings)
|
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 {
|
var isInputViewFocused: Bool {
|
||||||
if let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode {
|
if let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode {
|
||||||
return inputPanelNode.isFocused
|
return inputPanelNode.isFocused
|
||||||
|
|||||||
@ -145,7 +145,6 @@ final class ChatMediaInputStickerGridItem: GridItem {
|
|||||||
let node = ChatMediaInputStickerGridItemNode()
|
let node = ChatMediaInputStickerGridItemNode()
|
||||||
node.interfaceInteraction = self.interfaceInteraction
|
node.interfaceInteraction = self.interfaceInteraction
|
||||||
node.inputNodeInteraction = self.inputNodeInteraction
|
node.inputNodeInteraction = self.inputNodeInteraction
|
||||||
node.setup(account: self.account, stickerItem: self.stickerItem)
|
|
||||||
node.selected = self.selected
|
node.selected = self.selected
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
@ -157,7 +156,6 @@ final class ChatMediaInputStickerGridItem: GridItem {
|
|||||||
}
|
}
|
||||||
node.interfaceInteraction = self.interfaceInteraction
|
node.interfaceInteraction = self.interfaceInteraction
|
||||||
node.inputNodeInteraction = self.inputNodeInteraction
|
node.inputNodeInteraction = self.inputNodeInteraction
|
||||||
node.setup(account: self.account, stickerItem: self.stickerItem)
|
|
||||||
node.selected = self.selected
|
node.selected = self.selected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,11 +207,6 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
|||||||
self.imageNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.imageNodeTap(_:))))
|
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) {
|
override func updateLayout(item: GridItem, size: CGSize, isVisible: Bool, synchronousLoads: Bool) {
|
||||||
guard let item = item as? ChatMediaInputStickerGridItem else {
|
guard let item = item as? ChatMediaInputStickerGridItem else {
|
||||||
return
|
return
|
||||||
@ -232,13 +225,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
|||||||
self.addSubnode(animationNode)
|
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)))
|
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.updateVisibility()
|
||||||
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.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
if let animationNode = self.animationNode {
|
if let animationNode = self.animationNode {
|
||||||
@ -297,7 +284,10 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateVisibility() {
|
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 {
|
if self.isPlaying != isPlaying {
|
||||||
self.isPlaying = isPlaying
|
self.isPlaying = isPlaying
|
||||||
self.animationNode?.visibility = isPlaying
|
self.animationNode?.visibility = isPlaying
|
||||||
|
|||||||
@ -103,7 +103,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
override var visibility: ListViewItemNodeVisibility {
|
override var visibility: ListViewItemNodeVisibility {
|
||||||
didSet {
|
didSet {
|
||||||
self.visibilityStatus = self.visibility != .none
|
self.visibilityStatus = self.visibility != .none && false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,30 +171,30 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
|
|||||||
self.currentThumbnailItem = thumbnailItem
|
self.currentThumbnailItem = thumbnailItem
|
||||||
if let thumbnailItem = thumbnailItem {
|
if let thumbnailItem = thumbnailItem {
|
||||||
switch thumbnailItem {
|
switch thumbnailItem {
|
||||||
case let .still(representation):
|
case let .still(representation):
|
||||||
let imageSize = representation.dimensions.aspectFitted(boundingImageSize)
|
let imageSize = representation.dimensions.aspectFitted(boundingImageSize)
|
||||||
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
|
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
|
||||||
imageApply()
|
imageApply()
|
||||||
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: representation))
|
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: representation))
|
||||||
|
|
||||||
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||||
case let .animated(resource):
|
case let .animated(resource):
|
||||||
let imageSize = boundingImageSize
|
let imageSize = boundingImageSize
|
||||||
|
|
||||||
let animatedStickerNode: AnimatedStickerNode
|
let animatedStickerNode: AnimatedStickerNode
|
||||||
if let current = self.animatedStickerNode {
|
if let current = self.animatedStickerNode {
|
||||||
animatedStickerNode = current
|
animatedStickerNode = current
|
||||||
} else {
|
} else {
|
||||||
animatedStickerNode = AnimatedStickerNode()
|
animatedStickerNode = AnimatedStickerNode()
|
||||||
self.animatedStickerNode = animatedStickerNode
|
self.animatedStickerNode = animatedStickerNode
|
||||||
animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
self.addSubnode(animatedStickerNode)
|
self.addSubnode(animatedStickerNode)
|
||||||
animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached)
|
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 {
|
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)
|
animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let resourceReference = resourceReference {
|
if let resourceReference = resourceReference {
|
||||||
self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference).start())
|
self.stickerFetchedDisposable.set(fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference).start())
|
||||||
|
|||||||
@ -108,14 +108,14 @@ final class ChatMediaInputTrendingItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme)
|
self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme)
|
||||||
self.imageNode.image = PresentationResourcesChat.chatInputMediaPanelTrendingIconImage(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 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)
|
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
|
self.imageNode.frame = imageFrame
|
||||||
|
|
||||||
if let image = self.badgeBackground.image {
|
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)
|
attributedString = NSAttributedString(string: titleString, font: titleFont, textColor: primaryTextColor)
|
||||||
case let .customText(text, entities):
|
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):
|
case let .botDomainAccessGranted(domain):
|
||||||
attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).0, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: strings.AuthSessions_Message(domain).0, font: titleFont, textColor: primaryTextColor)
|
||||||
case let .botSentSecureValues(types):
|
case let .botSentSecureValues(types):
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
let imageNode: TransformImageNode
|
let imageNode: TransformImageNode
|
||||||
private let animationNode: AnimatedStickerNode
|
private let animationNode: AnimatedStickerNode
|
||||||
private var didSetUpAnimationNode = false
|
private var didSetUpAnimationNode = false
|
||||||
|
private var isPlaying = false
|
||||||
|
|
||||||
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
|
private var swipeToReplyNode: ChatMessageSwipeToReplyNode?
|
||||||
private var swipeToReplyFeedback: HapticFeedback?
|
private var swipeToReplyFeedback: HapticFeedback?
|
||||||
@ -90,8 +91,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
}
|
}
|
||||||
self.view.addGestureRecognizer(replyRecognizer)
|
self.view.addGestureRecognizer(replyRecognizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var visibilityPromise = ValuePromise<Bool>(false, ignoreRepeated: true)
|
|
||||||
override var visibility: ListViewItemNodeVisibility {
|
override var visibility: ListViewItemNodeVisibility {
|
||||||
didSet {
|
didSet {
|
||||||
let wasVisible = oldValue != .none
|
let wasVisible = oldValue != .none
|
||||||
@ -106,21 +106,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
private var visibilityStatus: Bool = false {
|
private var visibilityStatus: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
if self.visibilityStatus != oldValue {
|
if self.visibilityStatus != oldValue {
|
||||||
if self.visibilityStatus {
|
self.updateVisibility()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +119,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||||||
if self.telegramFile?.id != telegramFile.id {
|
if self.telegramFile?.id != telegramFile.id {
|
||||||
self.telegramFile = telegramFile
|
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))
|
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.didSetUpAnimationNode = true
|
||||||
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 384, height: 384, mode: .cached)
|
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) {
|
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 displaySize = CGSize(width: 184.0, height: 184.0)
|
||||||
let telegramFile = self.telegramFile
|
let telegramFile = self.telegramFile
|
||||||
|
|||||||
@ -15,6 +15,7 @@ private let textBoldFont = Font.semibold(15.0)
|
|||||||
private let textItalicFont = Font.italic(15.0)
|
private let textItalicFont = Font.italic(15.0)
|
||||||
private let textBoldItalicFont = Font.semiboldItalic(15.0)
|
private let textBoldItalicFont = Font.semiboldItalic(15.0)
|
||||||
private let textFixedFont = Font.regular(15.0)
|
private let textFixedFont = Font.regular(15.0)
|
||||||
|
private let textBlockQuoteFont = Font.regular(15.0)
|
||||||
private let buttonFont = Font.semibold(13.0)
|
private let buttonFont = Font.semibold(13.0)
|
||||||
|
|
||||||
enum ChatMessageAttachedContentActionIcon {
|
enum ChatMessageAttachedContentActionIcon {
|
||||||
@ -371,7 +372,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
string.append(NSAttributedString(string: "\n", font: textFont, textColor: messageTheme.primaryTextColor))
|
string.append(NSAttributedString(string: "\n", font: textFont, textColor: messageTheme.primaryTextColor))
|
||||||
}
|
}
|
||||||
if let entities = entities {
|
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 {
|
} else {
|
||||||
string.append(NSAttributedString(string: text + "\n", font: textFont, textColor: messageTheme.primaryTextColor))
|
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?)? {
|
override func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||||
for contentNode in self.contentNodes {
|
for contentNode in self.contentNodes {
|
||||||
if let playMediaWithSound = contentNode.playMediaWithSound() {
|
if let playMediaWithSound = contentNode.playMediaWithSound() {
|
||||||
|
|||||||
@ -698,6 +698,9 @@ public class ChatMessageItemView: ListViewItemNode {
|
|||||||
func updateAutomaticMediaDownloadSettings() {
|
func updateAutomaticMediaDownloadSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateStickerSettings() {
|
||||||
|
}
|
||||||
|
|
||||||
func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,13 +81,13 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !item.message.containsSecretMedia {
|
if !item.message.containsSecretMedia {
|
||||||
if telegramFile.isAnimated {
|
if telegramFile.isAnimated && item.controllerInteraction.automaticMediaDownloadSettings.autoplayGifs {
|
||||||
if case .full = automaticDownload {
|
if case .full = automaticDownload {
|
||||||
automaticPlayback = item.controllerInteraction.automaticMediaDownloadSettings.autoplayGifs
|
automaticPlayback = true
|
||||||
} else {
|
} else {
|
||||||
automaticPlayback = item.context.account.postbox.mediaBox.completedResourcePath(telegramFile.resource) != nil
|
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 {
|
if case .full = automaticDownload {
|
||||||
automaticPlayback = true
|
automaticPlayback = true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -230,7 +230,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
let forceStatusNewline = false
|
let forceStatusNewline = false
|
||||||
|
|
||||||
if let entities = entities {
|
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 {
|
} else {
|
||||||
attributedText = NSAttributedString(string: rawText, font: textFont, textColor: messageTheme.primaryTextColor)
|
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 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 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))
|
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 messageItalicFont: UIFont
|
||||||
let messageBoldItalicFont: UIFont
|
let messageBoldItalicFont: UIFont
|
||||||
let messageFixedFont: UIFont
|
let messageFixedFont: UIFont
|
||||||
|
let messageBlockQuoteFont: UIFont
|
||||||
|
|
||||||
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) {
|
init(theme: ChatPresentationThemeData, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, disableAnimations: Bool, largeEmoji: Bool) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
@ -103,5 +104,6 @@ public final class ChatPresentationData {
|
|||||||
self.messageItalicFont = UIFont.italicSystemFont(ofSize: baseFontSize)
|
self.messageItalicFont = UIFont.italicSystemFont(ofSize: baseFontSize)
|
||||||
self.messageBoldItalicFont = Font.semiboldItalic(baseFontSize)
|
self.messageBoldItalicFont = Font.semiboldItalic(baseFontSize)
|
||||||
self.messageFixedFont = UIFont(name: "Menlo-Regular", size: baseFontSize - 1.0) ?? UIFont.systemFont(ofSize: 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
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||||
pollActionState: ChatInterfacePollActionState())
|
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
|
|
||||||
self.listNode.displayedItemRangeChanged = { [weak self] displayedRange, opaqueTransactionState in
|
self.listNode.displayedItemRangeChanged = { [weak self] displayedRange, opaqueTransactionState in
|
||||||
|
|||||||
@ -39,6 +39,7 @@ struct ChatTextFontAttributes: OptionSet {
|
|||||||
static let bold = ChatTextFontAttributes(rawValue: 1 << 0)
|
static let bold = ChatTextFontAttributes(rawValue: 1 << 0)
|
||||||
static let italic = ChatTextFontAttributes(rawValue: 1 << 1)
|
static let italic = ChatTextFontAttributes(rawValue: 1 << 1)
|
||||||
static let monospace = ChatTextFontAttributes(rawValue: 1 << 2)
|
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 {
|
func textAttributedStringForStateText(_ stateText: NSAttributedString, fontSize: CGFloat, textColor: UIColor, accentTextColor: UIColor) -> NSAttributedString {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import SwiftSignalKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
private final class FeaturedStickerPacksControllerArguments {
|
private final class FeaturedStickerPacksControllerArguments {
|
||||||
let account: Account
|
let account: Account
|
||||||
@ -46,7 +47,7 @@ private enum FeaturedStickerPacksEntryId: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
|
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 {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -57,15 +58,15 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
var stableId: FeaturedStickerPacksEntryId {
|
var stableId: FeaturedStickerPacksEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
case let .pack(_, _, _, info, _, _, _, _):
|
case let .pack(_, _, _, info, _, _, _, _, _):
|
||||||
return .pack(info.id)
|
return .pack(info.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool {
|
static func ==(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsUnread, lhsTopItem, lhsCount, lhsInstalled):
|
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsUnread, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsInstalled):
|
||||||
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsUnread, rhsTopItem, rhsCount, rhsInstalled) = rhs {
|
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsUnread, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsInstalled) = rhs {
|
||||||
if lhsIndex != rhsIndex {
|
if lhsIndex != rhsIndex {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -87,6 +88,9 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
if lhsCount != rhsCount {
|
if lhsCount != rhsCount {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhsInstalled != rhsInstalled {
|
if lhsInstalled != rhsInstalled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -99,9 +103,9 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
static func <(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool {
|
static func <(lhs: FeaturedStickerPacksEntry, rhs: FeaturedStickerPacksEntry) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .pack(lhsIndex, _, _, _, _, _, _, _):
|
case let .pack(lhsIndex, _, _, _, _, _, _, _, _):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case let .pack(rhsIndex, _, _, _, _, _, _, _):
|
case let .pack(rhsIndex, _, _, _, _, _, _, _, _):
|
||||||
return lhsIndex < rhsIndex
|
return lhsIndex < rhsIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,8 +113,8 @@ private enum FeaturedStickerPacksEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
func item(_ arguments: FeaturedStickerPacksControllerArguments) -> ListViewItem {
|
func item(_ arguments: FeaturedStickerPacksControllerArguments) -> ListViewItem {
|
||||||
switch self {
|
switch self {
|
||||||
case let .pack(_, theme, strings, info, unread, topItem, count, installed):
|
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, sectionId: self.section, action: {
|
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)
|
arguments.openStickerPack(info)
|
||||||
}, setPackIdWithRevealedOptions: { _, _ in
|
}, setPackIdWithRevealedOptions: { _, _ in
|
||||||
}, addPack: {
|
}, 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] = []
|
var entries: [FeaturedStickerPacksEntry] = []
|
||||||
|
|
||||||
if let stickerPacksView = view.views[.itemCollectionInfos(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])] as? ItemCollectionInfosView, !featured.isEmpty {
|
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] {
|
if let value = unreadPacks[item.info.id] {
|
||||||
unread = value
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,9 +199,14 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr
|
|||||||
var previousPackCount: Int?
|
var previousPackCount: Int?
|
||||||
var initialUnreadPacks: [ItemCollectionId: Bool] = [:]
|
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
|
|> 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
|
let packCount: Int? = featured.count
|
||||||
|
|
||||||
for item in featured {
|
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 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))
|
return (controllerState, (listState, arguments))
|
||||||
} |> afterDisposed {
|
} |> afterDisposed {
|
||||||
actionsDisposable.dispose()
|
actionsDisposable.dispose()
|
||||||
@ -226,7 +235,7 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr
|
|||||||
var unreadIds: [ItemCollectionId] = []
|
var unreadIds: [ItemCollectionId] = []
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
switch entry {
|
switch entry {
|
||||||
case let .pack(_, _, _, info, unread, _, _, _):
|
case let .pack(_, _, _, info, unread, _, _, _, _):
|
||||||
if unread && !alreadyReadIds.contains(info.id) {
|
if unread && !alreadyReadIds.contains(info.id) {
|
||||||
unreadIds.append(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
|
private let fixedFont = UIFont(name: "Menlo-Regular", size: 15.0) ?? textFont
|
||||||
|
|
||||||
func galleryCaptionStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> NSAttributedString {
|
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 {
|
private func galleryMessageCaptionText(_ message: Message) -> String {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import SwiftSignalKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
private final class GroupStickerPackSetupControllerArguments {
|
private final class GroupStickerPackSetupControllerArguments {
|
||||||
let account: Account
|
let account: Account
|
||||||
@ -64,7 +65,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
|
|||||||
case currentPack(Int32, PresentationTheme, PresentationStrings, GroupStickerPackCurrentItemContent)
|
case currentPack(Int32, PresentationTheme, PresentationStrings, GroupStickerPackCurrentItemContent)
|
||||||
case searchInfo(PresentationTheme, String)
|
case searchInfo(PresentationTheme, String)
|
||||||
case packsTitle(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 {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -85,7 +86,7 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
|
|||||||
return .index(2)
|
return .index(2)
|
||||||
case .packsTitle:
|
case .packsTitle:
|
||||||
return .index(3)
|
return .index(3)
|
||||||
case let .pack(_, _, _, info, _, _, _):
|
case let .pack(_, _, _, info, _, _, _, _):
|
||||||
return .pack(info.id)
|
return .pack(info.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,8 +129,8 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsSelected):
|
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsPlayAnimatedStickers, lhsSelected):
|
||||||
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsSelected) = rhs {
|
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsPlayAnimatedStickers, rhsSelected) = rhs {
|
||||||
if lhsIndex != rhsIndex {
|
if lhsIndex != rhsIndex {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -148,6 +149,9 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
|
|||||||
if lhsCount != rhsCount {
|
if lhsCount != rhsCount {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsPlayAnimatedStickers != rhsPlayAnimatedStickers {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhsSelected != rhsSelected {
|
if lhsSelected != rhsSelected {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -188,9 +192,9 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
|
|||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case let .pack(lhsIndex, _, _, _, _, _, _):
|
case let .pack(lhsIndex, _, _, _, _, _, _, _):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case let .pack(rhsIndex, _, _, _, _, _, _):
|
case let .pack(rhsIndex, _, _, _, _, _, _, _):
|
||||||
return lhsIndex < rhsIndex
|
return lhsIndex < rhsIndex
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@ -216,8 +220,8 @@ private enum GroupStickerPackEntry: ItemListNodeEntry {
|
|||||||
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section, linkAction: nil)
|
return ItemListTextItem(theme: theme, text: .plain(text), sectionId: self.section, linkAction: nil)
|
||||||
case let .packsTitle(theme, text):
|
case let .packsTitle(theme, text):
|
||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
case let .pack(_, theme, strings, info, topItem, count, selected):
|
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, sectionId: self.section, action: {
|
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 {
|
if selected {
|
||||||
arguments.openStickerPack(info)
|
arguments.openStickerPack(info)
|
||||||
} else {
|
} else {
|
||||||
@ -258,7 +262,7 @@ private struct GroupStickerPackSetupControllerState: Equatable {
|
|||||||
var isSaving: Bool
|
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 {
|
if initialData == nil {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -288,7 +292,7 @@ private func groupStickerPackSetupControllerEntries(presentationData: Presentati
|
|||||||
if case let .found(found) = searchState {
|
if case let .found(found) = searchState {
|
||||||
selected = found.info.id == info.id
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,66 +404,71 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee
|
|||||||
|
|
||||||
let previousHadData = Atomic<Bool>(value: false)
|
let previousHadData = Atomic<Bool>(value: false)
|
||||||
|
|
||||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get() |> deliverOnMainQueue, initialData.get() |> deliverOnMainQueue, stickerPacks.get() |> deliverOnMainQueue, searchState.get() |> deliverOnMainQueue)
|
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 -> (ItemListControllerState, (ItemListNodeState<GroupStickerPackEntry>, GroupStickerPackEntry.ItemGenerationArguments)) in
|
|> map { presentationData, state, initialData, view, searchState, sharedData -> (ItemListControllerState, (ItemListNodeState<GroupStickerPackEntry>, GroupStickerPackEntry.ItemGenerationArguments)) in
|
||||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
var stickerSettings = StickerSettings.defaultSettings
|
||||||
dismissImpl?()
|
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.stickerSettings] as? StickerSettings {
|
||||||
})
|
stickerSettings = value
|
||||||
|
}
|
||||||
var rightNavigationButton: ItemListNavigationButton?
|
|
||||||
if initialData != nil {
|
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||||
if state.isSaving {
|
dismissImpl?()
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(""), style: .activity, enabled: true, action: {})
|
})
|
||||||
} else {
|
|
||||||
let enabled: Bool
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
var info: StickerPackCollectionInfo?
|
if initialData != nil {
|
||||||
switch searchState.1 {
|
if state.isSaving {
|
||||||
case .searching, .notFound:
|
rightNavigationButton = ItemListNavigationButton(content: .text(""), style: .activity, enabled: true, action: {})
|
||||||
enabled = false
|
} else {
|
||||||
case .none:
|
let enabled: Bool
|
||||||
enabled = true
|
var info: StickerPackCollectionInfo?
|
||||||
case let .found(data):
|
switch searchState.1 {
|
||||||
enabled = true
|
case .searching, .notFound:
|
||||||
info = data.info
|
enabled = false
|
||||||
}
|
case .none:
|
||||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: enabled, action: {
|
enabled = true
|
||||||
if info?.id == currentPackInfo?.id {
|
case let .found(data):
|
||||||
dismissImpl?()
|
enabled = true
|
||||||
} else {
|
info = data.info
|
||||||
|
}
|
||||||
|
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: enabled, action: {
|
||||||
|
if info?.id == currentPackInfo?.id {
|
||||||
|
dismissImpl?()
|
||||||
|
} else {
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
state.isSaving = true
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
saveDisposable.set((updateGroupSpecificStickerset(postbox: context.account.postbox, network: context.account.network, peerId: peerId, info: info)
|
||||||
|
|> deliverOnMainQueue).start(error: { _ in
|
||||||
updateState { state in
|
updateState { state in
|
||||||
var state = state
|
var state = state
|
||||||
state.isSaving = true
|
state.isSaving = false
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
saveDisposable.set((updateGroupSpecificStickerset(postbox: context.account.postbox, network: context.account.network, peerId: peerId, info: info)
|
}, completed: {
|
||||||
|> deliverOnMainQueue).start(error: { _ in
|
dismissImpl?()
|
||||||
updateState { state in
|
}))
|
||||||
var state = state
|
}
|
||||||
state.isSaving = false
|
})
|
||||||
return state
|
|
||||||
}
|
|
||||||
}, completed: {
|
|
||||||
dismissImpl?()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Info_Stickers), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
|
||||||
|
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Channel_Info_Stickers), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let hasData = initialData != nil
|
|
||||||
let hadData = previousHadData.swap(hasData)
|
let hasData = initialData != nil
|
||||||
|
let hadData = previousHadData.swap(hasData)
|
||||||
var emptyStateItem: ItemListLoadingIndicatorEmptyStateItem?
|
|
||||||
if !hasData {
|
var emptyStateItem: ItemListLoadingIndicatorEmptyStateItem?
|
||||||
emptyStateItem = ItemListLoadingIndicatorEmptyStateItem(theme: presentationData.theme)
|
if !hasData {
|
||||||
}
|
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)
|
|
||||||
return (controllerState, (listState, arguments))
|
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)
|
||||||
} |> afterDisposed {
|
return (controllerState, (listState, arguments))
|
||||||
actionsDisposable.dispose()
|
} |> afterDisposed {
|
||||||
|
actionsDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
let controller = ItemListController(context: context, state: signal)
|
let controller = ItemListController(context: context, state: signal)
|
||||||
|
|||||||
@ -209,7 +209,7 @@ func fetchICloudFileResource(resource: ICloudFileResource) -> Signal<MediaResour
|
|||||||
if resource.thumbnail {
|
if resource.thumbnail {
|
||||||
let tempFile = TempBox.shared.tempFile(fileName: "thumb.jpg")
|
let tempFile = TempBox.shared.tempFile(fileName: "thumb.jpg")
|
||||||
var data = Data()
|
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
|
data = jpegData
|
||||||
}
|
}
|
||||||
if let _ = try? data.write(to: URL(fileURLWithPath: tempFile.path)) {
|
if let _ = try? data.write(to: URL(fileURLWithPath: tempFile.path)) {
|
||||||
|
|||||||
@ -18,8 +18,9 @@ private final class InstalledStickerPacksControllerArguments {
|
|||||||
let openFeatured: () -> Void
|
let openFeatured: () -> Void
|
||||||
let openArchived: ([ArchivedStickerPackItem]?) -> Void
|
let openArchived: ([ArchivedStickerPackItem]?) -> Void
|
||||||
let openSuggestOptions: () -> 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.account = account
|
||||||
self.openStickerPack = openStickerPack
|
self.openStickerPack = openStickerPack
|
||||||
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
|
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
|
||||||
@ -29,6 +30,7 @@ private final class InstalledStickerPacksControllerArguments {
|
|||||||
self.openFeatured = openFeatured
|
self.openFeatured = openFeatured
|
||||||
self.openArchived = openArchived
|
self.openArchived = openArchived
|
||||||
self.openSuggestOptions = openSuggestOptions
|
self.openSuggestOptions = openSuggestOptions
|
||||||
|
self.toggleAnimatedStickers = toggleAnimatedStickers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,13 +87,15 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
case trending(PresentationTheme, String, Int32)
|
case trending(PresentationTheme, String, Int32)
|
||||||
case archived(PresentationTheme, String, Int32, [ArchivedStickerPackItem]?)
|
case archived(PresentationTheme, String, Int32, [ArchivedStickerPackItem]?)
|
||||||
case masks(PresentationTheme, String)
|
case masks(PresentationTheme, String)
|
||||||
|
case animatedStickers(PresentationTheme, String, Bool)
|
||||||
|
case animatedStickersInfo(PresentationTheme, String)
|
||||||
case packsTitle(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)
|
case packsInfo(PresentationTheme, String)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
case .suggestOptions, .trending, .masks, .archived:
|
case .suggestOptions, .trending, .masks, .archived, .animatedStickers, .animatedStickersInfo:
|
||||||
return InstalledStickerPacksSection.service.rawValue
|
return InstalledStickerPacksSection.service.rawValue
|
||||||
case .packsTitle, .pack, .packsInfo:
|
case .packsTitle, .pack, .packsInfo:
|
||||||
return InstalledStickerPacksSection.stickers.rawValue
|
return InstalledStickerPacksSection.stickers.rawValue
|
||||||
@ -108,12 +112,16 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
return .index(2)
|
return .index(2)
|
||||||
case .masks:
|
case .masks:
|
||||||
return .index(3)
|
return .index(3)
|
||||||
case .packsTitle:
|
case .animatedStickers:
|
||||||
return .index(4)
|
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)
|
return .pack(info.id)
|
||||||
case .packsInfo:
|
case .packsInfo:
|
||||||
return .index(5)
|
return .index(7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,14 +151,26 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
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):
|
case let .packsTitle(lhsTheme, lhsText):
|
||||||
if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .packsTitle(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsEnabled, lhsEditing):
|
case let .pack(lhsIndex, lhsTheme, lhsStrings, lhsInfo, lhsTopItem, lhsCount, lhsAnimatedStickers, lhsEnabled, lhsEditing):
|
||||||
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsEnabled, rhsEditing) = rhs {
|
if case let .pack(rhsIndex, rhsTheme, rhsStrings, rhsInfo, rhsTopItem, rhsCount, rhsAnimatedStickers, rhsEnabled, rhsEditing) = rhs {
|
||||||
if lhsIndex != rhsIndex {
|
if lhsIndex != rhsIndex {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -169,6 +189,9 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
if lhsCount != rhsCount {
|
if lhsCount != rhsCount {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhsAnimatedStickers != rhsAnimatedStickers {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhsEnabled != rhsEnabled {
|
if lhsEnabled != rhsEnabled {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -218,16 +241,30 @@ private enum InstalledStickerPacksEntry: ItemListNodeEntry {
|
|||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case .packsTitle:
|
case .animatedStickers:
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case .suggestOptions, .trending, .masks, .archived, .packsTitle:
|
case .suggestOptions, .trending, .archived, .masks, .animatedStickers:
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case let .pack(lhsIndex, _, _, _, _, _, _, _):
|
case .animatedStickersInfo:
|
||||||
switch rhs {
|
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
|
return lhsIndex < rhsIndex
|
||||||
case .packsInfo:
|
case .packsInfo:
|
||||||
return true
|
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: {
|
return ItemListDisclosureItem(theme: theme, title: text, label: count == 0 ? "" : "\(count)", sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openArchived(archived)
|
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):
|
case let .packsTitle(theme, text):
|
||||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||||
case let .pack(_, theme, strings, info, topItem, count, enabled, editing):
|
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, sectionId: self.section, action: {
|
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)
|
arguments.openStickerPack(info)
|
||||||
}, setPackIdWithRevealedOptions: { current, previous in
|
}, setPackIdWithRevealedOptions: { current, previous in
|
||||||
arguments.setPackIdWithRevealedOptions(current, previous)
|
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(.archived(presentationData.theme, presentationData.strings.StickerPacksSettings_ArchivedPacks, Int32(archived.count), archived))
|
||||||
}
|
}
|
||||||
entries.append(.masks(presentationData.theme, presentationData.strings.MaskStickerSettings_Title))
|
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))
|
entries.append(.packsTitle(presentationData.theme, presentationData.strings.StickerPacksSettings_StickerPacksSection))
|
||||||
case .masks:
|
case .masks:
|
||||||
if let archived = archived, !archived.isEmpty {
|
if let archived = archived, !archived.isEmpty {
|
||||||
@ -365,7 +412,7 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati
|
|||||||
var index: Int32 = 0
|
var index: Int32 = 0
|
||||||
for entry in packsEntries {
|
for entry in packsEntries {
|
||||||
if let info = entry.info as? StickerPackCollectionInfo {
|
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
|
index += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -505,18 +552,10 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||||
])
|
])
|
||||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
/*
|
}, toggleAnimatedStickers: { value in
|
||||||
let suggestString: String
|
let _ = updateStickerSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||||
switch stickerSettings.emojiStickerSuggestionMode {
|
return current.withUpdatedLoopAnimatedStickers(value)
|
||||||
case .none:
|
}).start()
|
||||||
suggestString = presentationData.strings.Stickers_SuggestNone
|
|
||||||
case .all:
|
|
||||||
|
|
||||||
case .installed:
|
|
||||||
suggestString = presentationData.strings.Stickers_SuggestAdded
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
})
|
})
|
||||||
let stickerPacks = Promise<CombinedView>()
|
let stickerPacks = Promise<CombinedView>()
|
||||||
stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])]))
|
stickerPacks.set(context.account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])]))
|
||||||
@ -531,10 +570,8 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
featured.set(.single([]))
|
featured.set(.single([]))
|
||||||
archivedPromise.set(.single(nil) |> then(archivedStickerPacks(account: context.account, namespace: .masks) |> map(Optional.init)))
|
archivedPromise.set(.single(nil) |> then(archivedStickerPacks(account: context.account, namespace: .masks) |> map(Optional.init)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var previousPackCount: Int?
|
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]))
|
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
|
|> deliverOnMainQueue
|
||||||
|> map { presentationData, state, view, featuredAndArchived, sharedData -> (ItemListControllerState, (ItemListNodeState<InstalledStickerPacksEntry>, InstalledStickerPacksEntry.ItemGenerationArguments)) in
|
|> 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
|
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?
|
var rightNavigationButton: ItemListNavigationButton?
|
||||||
if let packCount = packCount, packCount != 0 {
|
if let packCount = packCount, packCount != 0 {
|
||||||
if case .modal = mode {
|
if case .modal = mode {
|
||||||
@ -603,7 +634,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
|
|
||||||
controller.reorderEntry = { fromIndex, toIndex, entries in
|
controller.reorderEntry = { fromIndex, toIndex, entries in
|
||||||
let fromEntry = entries[fromIndex]
|
let fromEntry = entries[fromIndex]
|
||||||
guard case let .pack(_, _, _, fromPackInfo, _, _, _, _) = fromEntry else {
|
guard case let .pack(_, _, _, fromPackInfo, _, _, _, _, _) = fromEntry else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var referenceId: ItemCollectionId?
|
var referenceId: ItemCollectionId?
|
||||||
@ -611,7 +642,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
|
|||||||
var afterAll = false
|
var afterAll = false
|
||||||
if toIndex < entries.count {
|
if toIndex < entries.count {
|
||||||
switch entries[toIndex] {
|
switch entries[toIndex] {
|
||||||
case let .pack(_, _, _, toPackInfo, _, _, _, _):
|
case let .pack(_, _, _, toPackInfo, _, _, _, _, _):
|
||||||
referenceId = toPackInfo.id
|
referenceId = toPackInfo.id
|
||||||
default:
|
default:
|
||||||
if entries[toIndex] < fromEntry {
|
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 (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 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 (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
|
let padding: CGFloat = !item.label.isEmpty ? 39.0 : 20.0
|
||||||
|
|||||||
@ -56,7 +56,7 @@ private struct ItemListNodeTransition<Entry: ItemListNodeEntry> {
|
|||||||
let animateAlpha: Bool
|
let animateAlpha: Bool
|
||||||
let crossfade: Bool
|
let crossfade: Bool
|
||||||
let mergedEntries: [Entry]
|
let mergedEntries: [Entry]
|
||||||
let userInteractionEnabled: Bool
|
let scrollEnabled: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ItemListNodeState<Entry: ItemListNodeEntry> {
|
struct ItemListNodeState<Entry: ItemListNodeEntry> {
|
||||||
@ -66,12 +66,12 @@ struct ItemListNodeState<Entry: ItemListNodeEntry> {
|
|||||||
let searchItem: ItemListControllerSearch?
|
let searchItem: ItemListControllerSearch?
|
||||||
let animateChanges: Bool
|
let animateChanges: Bool
|
||||||
let crossfadeState: Bool
|
let crossfadeState: Bool
|
||||||
let userInteractionEnabled: Bool
|
let scrollEnabled: Bool
|
||||||
let focusItemTag: ItemListItemTag?
|
let focusItemTag: ItemListItemTag?
|
||||||
let ensureVisibleItemTag: ItemListItemTag?
|
let ensureVisibleItemTag: ItemListItemTag?
|
||||||
let initialScrollToItem: ListViewScrollToItem?
|
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.entries = entries
|
||||||
self.style = style
|
self.style = style
|
||||||
self.emptyStateItem = emptyStateItem
|
self.emptyStateItem = emptyStateItem
|
||||||
@ -81,7 +81,7 @@ struct ItemListNodeState<Entry: ItemListNodeEntry> {
|
|||||||
self.focusItemTag = focusItemTag
|
self.focusItemTag = focusItemTag
|
||||||
self.ensureVisibleItemTag = ensureVisibleItemTag
|
self.ensureVisibleItemTag = ensureVisibleItemTag
|
||||||
self.initialScrollToItem = initialScrollToItem
|
self.initialScrollToItem = initialScrollToItem
|
||||||
self.userInteractionEnabled = userInteractionEnabled
|
self.scrollEnabled = scrollEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +275,7 @@ class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UIScrollV
|
|||||||
scrollToItem = state.initialScrollToItem
|
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
|
}) |> deliverOnMainQueue).start(next: { [weak self] transition in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.enqueueTransition(transition)
|
strongSelf.enqueueTransition(transition)
|
||||||
@ -593,7 +593,7 @@ class ItemListControllerNode<Entry: ItemListNodeEntry>: ASDisplayNode, UIScrollV
|
|||||||
self.emptyStateNode = nil
|
self.emptyStateNode = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.listNode.isUserInteractionEnabled = transition.userInteractionEnabled
|
self.listNode.scrollEnabled = transition.scrollEnabled
|
||||||
|
|
||||||
if updateSearchItem {
|
if updateSearchItem {
|
||||||
self.requestLayout?(.animated(duration: 0.3, curve: .spring))
|
self.requestLayout?(.animated(duration: 0.3, curve: .spring))
|
||||||
|
|||||||
@ -185,7 +185,7 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let entities = generateTextEntities(item.text, enabledTypes: item.enabledEntityTypes)
|
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()))
|
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 control: ItemListStickerPackItemControl
|
||||||
let editing: ItemListStickerPackItemEditing
|
let editing: ItemListStickerPackItemEditing
|
||||||
let enabled: Bool
|
let enabled: Bool
|
||||||
|
let playAnimatedStickers: Bool
|
||||||
let sectionId: ItemListSectionId
|
let sectionId: ItemListSectionId
|
||||||
let action: (() -> Void)?
|
let action: (() -> Void)?
|
||||||
let setPackIdWithRevealedOptions: (ItemCollectionId?, ItemCollectionId?) -> Void
|
let setPackIdWithRevealedOptions: (ItemCollectionId?, ItemCollectionId?) -> Void
|
||||||
let addPack: () -> Void
|
let addPack: () -> Void
|
||||||
let removePack: () -> 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.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.account = account
|
self.account = account
|
||||||
@ -64,6 +65,7 @@ final class ItemListStickerPackItem: ListViewItem, ItemListItem {
|
|||||||
self.control = control
|
self.control = control
|
||||||
self.editing = editing
|
self.editing = editing
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
|
self.playAnimatedStickers = playAnimatedStickers
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.action = action
|
self.action = action
|
||||||
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
|
self.setPackIdWithRevealedOptions = setPackIdWithRevealedOptions
|
||||||
@ -162,7 +164,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let isVisible = self.visibility != .none
|
let isVisible = self.visibility != .none
|
||||||
|
|
||||||
if wasVisible != isVisible {
|
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.animationNode = animationNode
|
||||||
strongSelf.addSubnode(animationNode)
|
strongSelf.addSubnode(animationNode)
|
||||||
animationNode.setup(account: item.account, resource: resource, width: 80, height: 80, mode: .cached)
|
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 {
|
if let animationNode = strongSelf.animationNode {
|
||||||
transition.updateFrame(node: animationNode, frame: imageFrame)
|
transition.updateFrame(node: animationNode, frame: imageFrame)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -209,7 +209,7 @@ class ItemListTextWithLabelItemNode: ListViewItemNode {
|
|||||||
case .highlighted:
|
case .highlighted:
|
||||||
baseColor = item.theme.list.itemHighlightedColor
|
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 (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)
|
let contentSize = CGSize(width: params.width, height: textLayout.size.height + 39.0)
|
||||||
|
|||||||
@ -38,6 +38,11 @@ public func navigateToChatController(navigationController: NavigationController,
|
|||||||
if activateInput {
|
if activateInput {
|
||||||
controller.activateInput()
|
controller.activateInput()
|
||||||
}
|
}
|
||||||
|
if let botStart = botStart {
|
||||||
|
controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in
|
||||||
|
return state.updatedBotStartPayload(botStart.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -48,6 +53,11 @@ public func navigateToChatController(navigationController: NavigationController,
|
|||||||
let controller: ChatController
|
let controller: ChatController
|
||||||
if let chatController = chatController {
|
if let chatController = chatController {
|
||||||
controller = chatController
|
controller = chatController
|
||||||
|
if let botStart = botStart {
|
||||||
|
controller.updateChatPresentationInterfaceState(interactive: false) { state -> ChatPresentationInterfaceState in
|
||||||
|
return state.updatedBotStartPayload(botStart.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
controller = ChatController(context: context, chatLocation: chatLocation, messageId: messageId, botStart: botStart)
|
controller = ChatController(context: context, chatLocation: chatLocation, messageId: messageId, botStart: botStart)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -192,6 +192,20 @@ private func allOpenInOptions(context: AccountContext, item: OpenInItem) -> [Ope
|
|||||||
return .openUrl(url: "yandexnavi://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)")
|
return .openUrl(url: "yandexnavi://build_route_on_map?lat_to=\(lat)&lon_to=\(lon)")
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
if !withDirections {
|
||||||
options.append(OpenInOption(application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location", store: nil), action: {
|
options.append(OpenInOption(application: .other(title: "HERE Maps", identifier: 955837609, scheme: "here-location", store: nil), action: {
|
||||||
|
|||||||
@ -101,7 +101,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec
|
|||||||
}, seekToTimecode: { _, _, _ in
|
}, seekToTimecode: { _, _, _ in
|
||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState())
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||||
|
|
||||||
self.dimNode = ASDisplayNode()
|
self.dimNode = ASDisplayNode()
|
||||||
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
||||||
|
|||||||
@ -3,7 +3,13 @@ import UIKit
|
|||||||
import Display
|
import Display
|
||||||
|
|
||||||
func generatePdfPreviewImage(url: URL, size: CGSize) -> UIImage? {
|
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 }
|
guard let firstPage = document.page(at: 1) else { return nil }
|
||||||
|
|
||||||
let context = DrawingContext(size: size)
|
let context = DrawingContext(size: size)
|
||||||
|
|||||||
@ -160,7 +160,7 @@ final public class PasscodeEntryController: ViewController {
|
|||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
let isMainApp = !strongSelf.context.sharedContext.applicationBindings.isMainApp
|
let isMainApp = strongSelf.context.sharedContext.applicationBindings.isMainApp
|
||||||
let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { settings in
|
let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { settings in
|
||||||
if isMainApp {
|
if isMainApp {
|
||||||
return settings.withUpdatedBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState)
|
return settings.withUpdatedBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import TelegramCore
|
|||||||
import MobileCoreServices
|
import MobileCoreServices
|
||||||
|
|
||||||
private func rtfStringWithAppliedEntities(_ text: String, entities: [MessageTextEntity]) -> String {
|
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 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) {
|
if var rtf = String(data: data, encoding: .windowsCP1252) {
|
||||||
|
|||||||
@ -272,7 +272,7 @@ public class PeerMediaCollectionController: TelegramController {
|
|||||||
}, requestMessageUpdate: { _ in
|
}, requestMessageUpdate: { _ in
|
||||||
}, cancelInteractiveKeyboardGestures: {
|
}, cancelInteractiveKeyboardGestures: {
|
||||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||||
pollActionState: ChatInterfacePollActionState())
|
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(loopAnimatedStickers: false))
|
||||||
|
|
||||||
self.controllerInteraction = controllerInteraction
|
self.controllerInteraction = controllerInteraction
|
||||||
|
|
||||||
|
|||||||
@ -7,4 +7,5 @@ public extension PresentationSurfaceLevel {
|
|||||||
static let overlayMedia = PresentationSurfaceLevel(rawValue: 2)
|
static let overlayMedia = PresentationSurfaceLevel(rawValue: 2)
|
||||||
static let notifications = PresentationSurfaceLevel(rawValue: 3)
|
static let notifications = PresentationSurfaceLevel(rawValue: 3)
|
||||||
static let passcode = PresentationSurfaceLevel(rawValue: 4)
|
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 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))
|
return (controllerState, (listState, arguments))
|
||||||
} |> afterDisposed {
|
} |> afterDisposed {
|
||||||
|
|||||||
Binary file not shown.
@ -222,27 +222,25 @@ public class ShareRootControllerImpl {
|
|||||||
let requestUserInteraction: ([UnpreparedShareItemContent]) -> Signal<[PreparedShareItemContent], NoError> = { content in
|
let requestUserInteraction: ([UnpreparedShareItemContent]) -> Signal<[PreparedShareItemContent], NoError> = { content in
|
||||||
return Signal { [weak self] subscriber in
|
return Signal { [weak self] subscriber in
|
||||||
switch content[0] {
|
switch content[0] {
|
||||||
case let .contact(data):
|
case let .contact(data):
|
||||||
let controller = deviceContactInfoController(context: context, subject: .filter(peer: nil, contactId: nil, contactData: data, completion: { peer, contactData in
|
let controller = deviceContactInfoController(context: context, subject: .filter(peer: nil, contactId: nil, contactData: data, completion: { peer, contactData in
|
||||||
let phone = contactData.basicData.phoneNumbers[0].value
|
let phone = contactData.basicData.phoneNumbers[0].value
|
||||||
if let vCardData = contactData.serializedVCard() {
|
if let vCardData = contactData.serializedVCard() {
|
||||||
subscriber.putNext([.media(.media(.standalone(media: TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: vCardData))))])
|
subscriber.putNext([.media(.media(.standalone(media: TelegramMediaContact(firstName: contactData.basicData.firstName, lastName: contactData.basicData.lastName, phoneNumber: phone, peerId: nil, vCardData: vCardData))))])
|
||||||
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}), cancelled: {
|
||||||
|
cancelImpl?()
|
||||||
|
})
|
||||||
|
|
||||||
|
if let strongSelf = self, let window = strongSelf.mainWindow {
|
||||||
|
controller.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet)
|
||||||
|
window.present(controller, on: .root)
|
||||||
}
|
}
|
||||||
subscriber.putCompletion()
|
break
|
||||||
}), cancelled: {
|
|
||||||
cancelImpl?()
|
|
||||||
})
|
|
||||||
|
|
||||||
if let strongSelf = self, let window = strongSelf.mainWindow {
|
|
||||||
controller.presentationArguments = ViewControllerPresentationArguments(presentationAnimation: .modalSheet)
|
|
||||||
window.present(controller, on: .root)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
return EmptyDisposable
|
||||||
return ActionDisposable {
|
} |> runOn(Queue.mainQueue())
|
||||||
}
|
|
||||||
} |> runOn(Queue.mainQueue())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sentItems: ([PeerId], [PreparedShareItemContent], Account) -> Signal<ShareControllerExternalStatus, NoError> = { peerIds, contents, account in
|
let sentItems: ([PeerId], [PreparedShareItemContent], Account) -> Signal<ShareControllerExternalStatus, NoError> = { peerIds, contents, account in
|
||||||
@ -271,17 +269,17 @@ public class ShareRootControllerImpl {
|
|||||||
return .single(.done)
|
return .single(.done)
|
||||||
}
|
}
|
||||||
switch state {
|
switch state {
|
||||||
case .preparing:
|
case .preparing:
|
||||||
return .single(.preparing)
|
return .single(.preparing)
|
||||||
case let .progress(value):
|
case let .progress(value):
|
||||||
return .single(.progress(value))
|
return .single(.progress(value))
|
||||||
case let .userInteractionRequired(value):
|
case let .userInteractionRequired(value):
|
||||||
return requestUserInteraction(value)
|
return requestUserInteraction(value)
|
||||||
|> mapToSignal { contents -> Signal<ShareControllerExternalStatus, NoError> in
|
|> mapToSignal { contents -> Signal<ShareControllerExternalStatus, NoError> in
|
||||||
return sentItems(peerIds, contents, account)
|
return sentItems(peerIds, contents, account)
|
||||||
}
|
}
|
||||||
case let .done(contents):
|
case let .done(contents):
|
||||||
return sentItems(peerIds, contents, account)
|
return sentItems(peerIds, contents, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -338,7 +336,7 @@ public class ShareRootControllerImpl {
|
|||||||
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||||
})])
|
})])
|
||||||
strongSelf.mainWindow?.present(controller, on: .root)
|
strongSelf.mainWindow?.present(controller, on: .root)
|
||||||
}, completed: {}))
|
}, completed: {}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -178,7 +178,12 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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() }
|
|> mapError { _ -> Void in return Void() }
|
||||||
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
|> mapToSignal { event -> Signal<PreparedShareItem, Void> in
|
||||||
switch event {
|
switch event {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import AsyncDisplayKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
enum StickerPackPreviewControllerMode {
|
enum StickerPackPreviewControllerMode {
|
||||||
case `default`
|
case `default`
|
||||||
@ -140,8 +141,13 @@ final class StickerPackPreviewController: ViewController {
|
|||||||
}
|
}
|
||||||
let account = self.context.account
|
let account = self.context.account
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
self.stickerPackDisposable.set((self.stickerPackContents.get()
|
self.stickerPackDisposable.set((combineLatest(self.stickerPackContents.get(), self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.stickerSettings]) |> take(1))
|
||||||
|> mapToSignal { next -> Signal<LoadedStickerPack, NoError> in
|
|> 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 {
|
switch next {
|
||||||
case let .result(_, items, _):
|
case let .result(_, items, _):
|
||||||
var preloadSignals: [Signal<Bool, NoError>] = []
|
var preloadSignals: [Signal<Bool, NoError>] = []
|
||||||
@ -172,26 +178,26 @@ final class StickerPackPreviewController: ViewController {
|
|||||||
return !values.contains(false)
|
return !values.contains(false)
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|> mapToSignal { loaded -> Signal<LoadedStickerPack, NoError> in
|
|> mapToSignal { loaded -> Signal<(LoadedStickerPack, StickerSettings), NoError> in
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return .single(.fetching)
|
return .single((.fetching, stickerSettings))
|
||||||
} else {
|
} else {
|
||||||
return .single(next)
|
return .single((next, stickerSettings))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return .single(next)
|
return .single((next, stickerSettings))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] next in
|
|> deliverOnMainQueue).start(next: { [weak self] next in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if case .none = next {
|
if case .none = next.0 {
|
||||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $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.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()
|
strongSelf.dismiss()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.controllerNode.updateStickerPack(next)
|
strongSelf.controllerNode.updateStickerPack(next.0, stickerSettings: next.1)
|
||||||
strongSelf.stickerPackContentsValue = next
|
strongSelf.stickerPackContentsValue = next.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import SwiftSignalKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
|
||||||
private struct StickerPackPreviewGridEntry: Comparable, Identifiable {
|
private struct StickerPackPreviewGridEntry: Comparable, Identifiable {
|
||||||
let index: Int
|
let index: Int
|
||||||
@ -75,6 +76,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
private var stickerPack: LoadedStickerPack?
|
private var stickerPack: LoadedStickerPack?
|
||||||
private var stickerPackUpdated = false
|
private var stickerPackUpdated = false
|
||||||
private var stickerPackInitiallyInstalled : Bool?
|
private var stickerPackInitiallyInstalled : Bool?
|
||||||
|
private var stickerSettings: StickerSettings?
|
||||||
|
|
||||||
private var currentItems: [StickerPackPreviewGridEntry] = []
|
private var currentItems: [StickerPackPreviewGridEntry] = []
|
||||||
|
|
||||||
@ -131,13 +133,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.interaction = StickerPackPreviewInteraction(sendSticker: { [weak self] item in
|
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: false)
|
||||||
if let strongSelf = self, let sendSticker = strongSelf.sendSticker {
|
|
||||||
/*if sendSticker(item.file) {
|
|
||||||
strongSelf.cancel?()
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
self.backgroundColor = nil
|
self.backgroundColor = nil
|
||||||
self.isOpaque = false
|
self.isOpaque = false
|
||||||
@ -375,7 +371,8 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
|
|
||||||
if self.currentItems.isEmpty && !updatedItems.isEmpty {
|
if self.currentItems.isEmpty && !updatedItems.isEmpty {
|
||||||
let entities = generateTextEntities(info.title, enabledTypes: [.mention])
|
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
|
animateIn = true
|
||||||
}
|
}
|
||||||
transaction = StickerPackPreviewGridTransaction(previousList: self.currentItems, list: updatedItems, account: self.context.account, interaction: self.interaction)
|
transaction = StickerPackPreviewGridTransaction(previousList: self.currentItems, list: updatedItems, account: self.context.account, interaction: self.interaction)
|
||||||
@ -510,16 +507,16 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
} else {
|
} else {
|
||||||
dismissOnAction = true
|
dismissOnAction = true
|
||||||
}
|
}
|
||||||
if let stickerPack = self.stickerPack {
|
if let stickerPack = self.stickerPack, let stickerSettings = self.stickerSettings {
|
||||||
switch stickerPack {
|
switch stickerPack {
|
||||||
case let .result(info, items, installed):
|
case let .result(info, items, installed):
|
||||||
if installed {
|
if installed {
|
||||||
let _ = removeStickerPackInteractively(postbox: self.context.account.postbox, id: info.id, option: .delete).start()
|
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 {
|
} else {
|
||||||
let _ = addStickerPackInteractively(postbox: self.context.account.postbox, info: info, items: items).start()
|
let _ = addStickerPackInteractively(postbox: self.context.account.postbox, info: info, items: items).start()
|
||||||
if !dismissOnAction {
|
if !dismissOnAction {
|
||||||
updateStickerPack(.result(info: info, items: items, installed: true))
|
self.updateStickerPack(.result(info: info, items: items, installed: true), stickerSettings: stickerSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dismissOnAction {
|
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.stickerPack = stickerPack
|
||||||
|
self.stickerSettings = stickerSettings
|
||||||
self.stickerPackUpdated = true
|
self.stickerPackUpdated = true
|
||||||
|
|
||||||
|
self.interaction.playAnimatedStickers = stickerSettings.loopAnimatedStickers
|
||||||
|
|
||||||
if let _ = self.containerLayout {
|
if let _ = self.containerLayout {
|
||||||
self.dequeueUpdateStickerPack()
|
self.dequeueUpdateStickerPack()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,10 @@ import Postbox
|
|||||||
|
|
||||||
final class StickerPackPreviewInteraction {
|
final class StickerPackPreviewInteraction {
|
||||||
var previewedItem: StickerPreviewPeekItem?
|
var previewedItem: StickerPreviewPeekItem?
|
||||||
|
var playAnimatedStickers: Bool
|
||||||
|
|
||||||
let sendSticker: (StickerPackItem) -> Void
|
init(playAnimatedStickers: Bool) {
|
||||||
|
self.playAnimatedStickers = playAnimatedStickers
|
||||||
init(sendSticker: @escaping (StickerPackItem) -> Void) {
|
|
||||||
self.sendSticker = sendSticker
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,12 +52,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
|
|
||||||
override var isVisibleInGrid: Bool {
|
override var isVisibleInGrid: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
self.animationNode?.visibility = self.isVisibleInGrid
|
self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let textNode: ASTextNode
|
|
||||||
|
|
||||||
private var currentIsPreviewing = false
|
private var currentIsPreviewing = false
|
||||||
|
|
||||||
private let stickerFetchedDisposable = MetaDisposable()
|
private let stickerFetchedDisposable = MetaDisposable()
|
||||||
@ -74,16 +71,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
override init() {
|
override init() {
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
|
self.imageNode.isLayerBacked = !smartInvertColorsEnabled()
|
||||||
//self.imageNode.alphaTransitionOnFirstUpdate = true
|
|
||||||
|
|
||||||
self.textNode = ASTextNode()
|
|
||||||
self.textNode.isUserInteractionEnabled = false
|
|
||||||
self.textNode.displaysAsynchronously = true
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.addSubnode(self.imageNode)
|
self.addSubnode(self.imageNode)
|
||||||
//self.addSubnode(self.textNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -100,14 +91,6 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
|
|||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
|
|
||||||
if self.currentState == nil || self.currentState!.0 !== account || self.currentState!.1 != stickerItem {
|
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 let dimensions = stickerItem.file.dimensions {
|
||||||
if stickerItem.file.isAnimatedSticker {
|
if stickerItem.file.isAnimatedSticker {
|
||||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: account.postbox, file: stickerItem.file, small: false, size: CGSize(width: 160.0, height: 160.0)))
|
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?.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())
|
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
|
||||||
} else {
|
} else {
|
||||||
if let animationNode = self.animationNode {
|
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.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)
|
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) {
|
@objc func imageNodeTap(_ recognizer: UITapGestureRecognizer) {
|
||||||
if let interaction = self.interaction, let (_, item, _) = self.currentState, case .ended = recognizer.state {
|
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
|
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?
|
var nsString: NSString?
|
||||||
let string = NSMutableAttributedString(string: text, attributes: [NSAttributedStringKey.font: baseFont, NSAttributedStringKey.foregroundColor: baseColor])
|
let string = NSMutableAttributedString(string: text, attributes: [NSAttributedStringKey.font: baseFont, NSAttributedStringKey.foregroundColor: baseColor])
|
||||||
var skipEntity = false
|
var skipEntity = false
|
||||||
let stringLength = string.length
|
|
||||||
var underlineAllLinks = false
|
var underlineAllLinks = false
|
||||||
if linkColor.isEqual(baseColor) {
|
if linkColor.isEqual(baseColor) {
|
||||||
underlineAllLinks = true
|
underlineAllLinks = true
|
||||||
}
|
}
|
||||||
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]
|
var fontAttributes: [NSRange: ChatTextFontAttributes] = [:]
|
||||||
|
|
||||||
|
var rangeOffset: Int = 0
|
||||||
for i in 0 ..< entities.count {
|
for i in 0 ..< entities.count {
|
||||||
if skipEntity {
|
if skipEntity {
|
||||||
skipEntity = false
|
skipEntity = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
let stringLength = string.length
|
||||||
let entity = entities[i]
|
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 {
|
if nsString == nil {
|
||||||
nsString = text as NSString
|
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)
|
string.addAttribute(NSAttributedStringKey(rawValue: TelegramTextAttributes.BotCommand), value: nsString!.substring(with: range), range: range)
|
||||||
case .Code, .Pre:
|
case .Code, .Pre:
|
||||||
string.addAttribute(NSAttributedStringKey.font, value: fixedFont, range: range)
|
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):
|
case let .Custom(type):
|
||||||
if type == ApplicationSpecificEntityType.Timecode {
|
if type == ApplicationSpecificEntityType.Timecode {
|
||||||
string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range)
|
string.addAttribute(NSAttributedStringKey.foregroundColor, value: linkColor, range: range)
|
||||||
@ -208,7 +228,9 @@ func stringWithAppliedEntities(_ text: String, entities: [MessageTextEntity], ba
|
|||||||
|
|
||||||
for (range, fontAttributes) in fontAttributes {
|
for (range, fontAttributes) in fontAttributes {
|
||||||
var font: UIFont?
|
var font: UIFont?
|
||||||
if fontAttributes == [.bold, .italic] {
|
if fontAttributes.contains(.blockQuote) {
|
||||||
|
font = blockQuoteFont
|
||||||
|
} else if fontAttributes == [.bold, .italic] {
|
||||||
font = boldItalicFont
|
font = boldItalicFont
|
||||||
} else if fontAttributes == [.bold] {
|
} else if fontAttributes == [.bold] {
|
||||||
font = boldFont
|
font = boldFont
|
||||||
|
|||||||
@ -553,7 +553,7 @@ static void set_bits(uint8_t *bytes, int32_t bitOffset, int32_t numBits, int32_t
|
|||||||
// add output reader to reader
|
// add output reader to reader
|
||||||
[iPodAssetReader addOutput: readerOutput];
|
[iPodAssetReader addOutput: readerOutput];
|
||||||
|
|
||||||
if (! [iPodAssetReader startReading]) {
|
if (![iPodAssetReader startReading]) {
|
||||||
NSLog(@"Unable to start reading!");
|
NSLog(@"Unable to start reading!");
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
@ -563,7 +563,6 @@ static void set_bits(uint8_t *bytes, int32_t bitOffset, int32_t numBits, int32_t
|
|||||||
int _waveformPeakCount = 0;
|
int _waveformPeakCount = 0;
|
||||||
|
|
||||||
while (iPodAssetReader.status == AVAssetReaderStatusReading) {
|
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];
|
CMSampleBufferRef nextBuffer = [readerOutput copyNextSampleBuffer];
|
||||||
|
|
||||||
if (nextBuffer) {
|
if (nextBuffer) {
|
||||||
|
|||||||
@ -57,7 +57,7 @@ final class TermsOfServiceControllerNode: ViewControllerTracingNode {
|
|||||||
self.contentTextNode = ImmediateTextNode()
|
self.contentTextNode = ImmediateTextNode()
|
||||||
self.contentTextNode.displaysAsynchronously = false
|
self.contentTextNode.displaysAsynchronously = false
|
||||||
self.contentTextNode.maximumNumberOfLines = 0
|
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.toolbarNode = ASDisplayNode()
|
||||||
self.toolbarSeparatorNode = ASDisplayNode()
|
self.toolbarSeparatorNode = ASDisplayNode()
|
||||||
|
|||||||
@ -41,4 +41,5 @@ struct TelegramTextAttributes {
|
|||||||
static let BotCommand = "TelegramBotCommand"
|
static let BotCommand = "TelegramBotCommand"
|
||||||
static let Hashtag = "TelegramHashtag"
|
static let Hashtag = "TelegramHashtag"
|
||||||
static let Timecode = "TelegramTimecode"
|
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
|
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?()
|
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 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)
|
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 (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 (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
|
let contentSize: CGSize
|
||||||
|
|||||||
@ -270,7 +270,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let peer = peer as? TelegramUser, peer.botInfo == nil {
|
if let peer = peer as? TelegramUser, peer.botInfo == nil {
|
||||||
return .peer(peer.id, .info)
|
return .peer(peer.id, .chat(textInputState: nil, messageId: nil))
|
||||||
} else {
|
} else {
|
||||||
return .peer(peer.id, .chat(textInputState: nil, messageId: nil))
|
return .peer(peer.id, .chat(textInputState: nil, messageId: nil))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -151,14 +151,14 @@ public struct MediaAutoDownloadSettings: PreferencesEntry, Equatable {
|
|||||||
public static var defaultSettings: MediaAutoDownloadSettings {
|
public static var defaultSettings: MediaAutoDownloadSettings {
|
||||||
let mb: Int32 = 1024 * 1024
|
let mb: Int32 = 1024 * 1024
|
||||||
let presets = MediaAutoDownloadPresets(low: MediaAutoDownloadCategories(basePreset: .low, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
let presets = MediaAutoDownloadPresets(low: MediaAutoDownloadCategories(basePreset: .low, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||||
video: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false),
|
video: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false),
|
||||||
file: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false)),
|
file: MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 1 * mb, predownload: false)),
|
||||||
medium: MediaAutoDownloadCategories(basePreset: .medium, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
medium: MediaAutoDownloadCategories(basePreset: .medium, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||||
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: Int32(2.5 * CGFloat(mb)), predownload: false),
|
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: Int32(2.5 * CGFloat(mb)), predownload: false),
|
||||||
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false)),
|
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false)),
|
||||||
high: MediaAutoDownloadCategories(basePreset: .high, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
high: MediaAutoDownloadCategories(basePreset: .high, photo: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 1 * mb, predownload: false),
|
||||||
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 10 * mb, predownload: true),
|
video: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 10 * mb, predownload: true),
|
||||||
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 3 * mb, predownload: false)))
|
file: MediaAutoDownloadCategory(contacts: true, otherPrivate: true, groups: true, channels: true, sizeLimit: 3 * mb, predownload: false)))
|
||||||
let saveDownloadedPhotos = MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 0, predownload: false)
|
let saveDownloadedPhotos = MediaAutoDownloadCategory(contacts: false, otherPrivate: false, groups: false, channels: false, sizeLimit: 0, predownload: false)
|
||||||
|
|
||||||
return MediaAutoDownloadSettings(presets: presets, cellular: MediaAutoDownloadConnection(enabled: true, preset: .medium, custom: nil), wifi: MediaAutoDownloadConnection(enabled: true, preset: .high, custom: nil), saveDownloadedPhotos: saveDownloadedPhotos, autoplayGifs: true, autoplayVideos: true, downloadInBackground: true)
|
return MediaAutoDownloadSettings(presets: presets, cellular: MediaAutoDownloadConnection(enabled: true, preset: .medium, custom: nil), wifi: MediaAutoDownloadConnection(enabled: true, preset: .high, custom: nil), saveDownloadedPhotos: saveDownloadedPhotos, autoplayGifs: true, autoplayVideos: true, downloadInBackground: true)
|
||||||
|
|||||||
@ -10,21 +10,25 @@ public enum EmojiStickerSuggestionMode: Int32 {
|
|||||||
|
|
||||||
public struct StickerSettings: PreferencesEntry, Equatable {
|
public struct StickerSettings: PreferencesEntry, Equatable {
|
||||||
public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode
|
public var emojiStickerSuggestionMode: EmojiStickerSuggestionMode
|
||||||
|
public var loopAnimatedStickers: Bool
|
||||||
|
|
||||||
public static var defaultSettings: StickerSettings {
|
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.emojiStickerSuggestionMode = emojiStickerSuggestionMode
|
||||||
|
self.loopAnimatedStickers = loopAnimatedStickers
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: decoder.decodeInt32ForKey("emojiStickerSuggestionMode", orElse: 0))!
|
self.emojiStickerSuggestionMode = EmojiStickerSuggestionMode(rawValue: decoder.decodeInt32ForKey("emojiStickerSuggestionMode", orElse: 0))!
|
||||||
|
self.loopAnimatedStickers = decoder.decodeBoolForKey("loopAnimatedStickers", orElse: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
encoder.encodeInt32(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode")
|
encoder.encodeInt32(self.emojiStickerSuggestionMode.rawValue, forKey: "emojiStickerSuggestionMode")
|
||||||
|
encoder.encodeBool(self.loopAnimatedStickers, forKey: "loopAnimatedStickers")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func isEqual(to: PreferencesEntry) -> Bool {
|
public func isEqual(to: PreferencesEntry) -> Bool {
|
||||||
@ -36,11 +40,15 @@ public struct StickerSettings: PreferencesEntry, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: StickerSettings, rhs: StickerSettings) -> Bool {
|
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 {
|
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->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6));
|
||||||
stm->decoder=NULL;
|
stm->decoder=NULL;
|
||||||
}else if(stm->type==STREAM_TYPE_VIDEO){
|
}else if(stm->type==STREAM_TYPE_VIDEO){
|
||||||
if(!stm->packetReassembler){
|
// if(!stm->packetReassembler){
|
||||||
stm->packetReassembler=make_shared<PacketReassembler>();
|
// stm->packetReassembler=make_shared<PacketReassembler>();
|
||||||
stm->packetReassembler->SetCallback(bind(&VoIPController::ProcessIncomingVideoFrame, this, placeholders::_1, placeholders::_2, placeholders::_3));
|
// stm->packetReassembler->SetCallback(bind(&VoIPController::ProcessIncomingVideoFrame, this, placeholders::_1, placeholders::_2, placeholders::_3));
|
||||||
}
|
// }
|
||||||
}else{
|
}else{
|
||||||
LOGW("Unknown incoming stream type: %d", stm->type);
|
LOGW("Unknown incoming stream type: %d", stm->type);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user