Various improvements

This commit is contained in:
Ilya Laktyushin 2022-04-30 03:52:31 +04:00
parent d36b6b0b7f
commit 70ae305e99
41 changed files with 521 additions and 415 deletions

View File

@ -7542,3 +7542,5 @@ Sorry for the inconvenience.";
"Premium.Reactions.Description" = "Unlock additional reactions by subscribing to Telegram Premium.";
"Premium.Reactions.Proceed" = "Unlock Additional Reactions";
"AccessDenied.LocationPreciseDenied" = "To share your specific location in this chat, please go to Settings > Privacy > Location Services > Telegram and set Precise Location to On.";

View File

@ -1379,6 +1379,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
credibilityIconOffset = 3.0
} else if peer.isPremium {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
credibilityIconOffset = 2.0
}
}
default:
@ -1394,6 +1397,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else if peer.isVerified {
currentCredibilityIconImage = PresentationResourcesChatList.verifiedIcon(item.presentationData.theme)
credibilityIconOffset = 3.0
} else if peer.isPremium {
currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme)
credibilityIconOffset = 2.0
}
}
}
@ -1762,7 +1768,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
if let currentMutedIconImage = currentMutedIconImage {
strongSelf.mutedIconNode.image = currentMutedIconImage
strongSelf.mutedIconNode.isHidden = false
transition.updateFrame(node: strongSelf.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin - 4.0, y: contentRect.origin.y - 2.0), size: currentMutedIconImage.size))
transition.updateFrame(node: strongSelf.mutedIconNode, frame: CGRect(origin: CGPoint(x: nextTitleIconOrigin - 5.0, y: contentRect.origin.y - 3.0 + UIScreenPixel), size: currentMutedIconImage.size))
nextTitleIconOrigin += currentMutedIconImage.size.width + 1.0
} else {
strongSelf.mutedIconNode.image = nil

View File

@ -14,6 +14,7 @@ private let animationDurationFactor: Double = 1.0
public protocol ContextControllerProtocol: AnyObject {
var useComplexItemsTransitionAnimation: Bool { get set }
var immediateItemsTransitionAnimation: Bool { get set }
var getOverlayViews: (() -> [UIView])? { get set }
func getActionsMinHeight() -> ContextController.ActionsHeight?
func setItems(_ items: Signal<ContextController.Items, NoError>, minHeight: ContextController.ActionsHeight?)
@ -1238,7 +1239,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: originalProjectedContentViewFrame.1.width, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
@ -2267,6 +2267,8 @@ public final class ContextController: ViewController, StandalonePresentableContr
public var reactionSelected: ((ReactionContextItem, Bool) -> Void)?
public var getOverlayViews: (() -> [UIView])?
public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<ContextController.Items, NoError>, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, workaroundUseLegacyImplementation: Bool = false) {
self.account = account
self.presentationData = presentationData

View File

@ -638,6 +638,18 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
}
}
}
if let overlayViews = self.getController()?.getOverlayViews?(), !overlayViews.isEmpty {
for view in overlayViews {
if let snapshotView = view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = view.convert(view.bounds, to: nil)
self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
}
case let .animateOut(result, completion):
let duration: Double
let timingFunction: String
@ -785,6 +797,18 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
if let reactionContextNode = self.reactionContextNode {
reactionContextNode.animateOut(to: currentContentScreenFrame, animatingOutToReaction: self.reactionContextNodeIsAnimatingOut)
}
if let overlayViews = self.getController()?.getOverlayViews?(), !overlayViews.isEmpty {
for view in overlayViews {
if let snapshotView = view.snapshotView(afterScreenUpdates: false) {
snapshotView.frame = view.convert(view.bounds, to: nil)
self.view.addSubview(snapshotView)
snapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
}
case .none:
if animateReactionsIn, let reactionContextNode = self.reactionContextNode {
reactionContextNode.animateIn(from: contentRect)

View File

@ -64,6 +64,8 @@ public final class PeekController: ViewController, ContextControllerProtocol {
public var visibilityUpdated: ((Bool) -> Void)?
public var getOverlayViews: (() -> [UIView])?
private var animatedIn = false
public init(presentationData: PresentationData, content: PeekControllerContent, sourceNode: @escaping () -> ASDisplayNode?) {

View File

@ -378,9 +378,29 @@ public final class DeviceAccess {
}
case let .location(locationSubject):
let status = CLLocationManager.authorizationStatus()
let hasPreciseLocation: Bool
if #available(iOS 14.0, *) {
if case .fullAccuracy = CLLocationManager().accuracyAuthorization {
hasPreciseLocation = true
} else {
hasPreciseLocation = false
}
} else {
hasPreciseLocation = true
}
switch status {
case .authorizedAlways:
completion(true)
if case .live = locationSubject, !hasPreciseLocation {
completion(false)
if let presentationData = presentationData {
let text = presentationData.strings.AccessDenied_LocationPreciseDenied
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {
openSettings()
})]), nil)
}
} else {
completion(true)
}
case .authorizedWhenInUse:
switch locationSubject {
case .send, .tracking:

View File

@ -4,6 +4,7 @@ import UIKit
public struct ImmediateTextNodeLayoutInfo {
public let size: CGSize
public let truncated: Bool
public let numberOfLines: Int
}
public class ImmediateTextNode: TextNode {
@ -18,8 +19,7 @@ public class ImmediateTextNode: TextNode {
public var textStroke: (UIColor, CGFloat)?
public var cutout: TextNodeCutout?
public var displaySpoilers = false
public var countIfMoreThanOneLineOverMaximum = false
public var truncationMode: NSLineBreakMode {
get {
switch self.truncationType {
@ -91,7 +91,7 @@ public class ImmediateTextNode: TextNode {
self.constrainedSize = constrainedSize
let makeLayout = TextNode.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: self.countIfMoreThanOneLineOverMaximum))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers))
let _ = apply()
if layout.numberOfLines > 1 {
self.trailingLineWidth = layout.trailingLineWidth
@ -105,16 +105,16 @@ public class ImmediateTextNode: TextNode {
self.constrainedSize = constrainedSize
let makeLayout = TextNode.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: self.countIfMoreThanOneLineOverMaximum))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let _ = apply()
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated)
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated, numberOfLines: layout.numberOfLines)
}
public func updateLayoutFullInfo(_ constrainedSize: CGSize) -> TextNodeLayout {
self.constrainedSize = constrainedSize
let makeLayout = TextNode.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: self.countIfMoreThanOneLineOverMaximum))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let _ = apply()
return layout
}
@ -287,7 +287,7 @@ public class ImmediateTextView: TextView {
self.constrainedSize = constrainedSize
let makeLayout = TextView.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: false))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers))
let _ = apply()
if layout.numberOfLines > 1 {
self.trailingLineWidth = layout.trailingLineWidth
@ -301,16 +301,16 @@ public class ImmediateTextView: TextView {
self.constrainedSize = constrainedSize
let makeLayout = TextView.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: false))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let _ = apply()
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated)
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated, numberOfLines: layout.numberOfLines)
}
public func updateLayoutFullInfo(_ constrainedSize: CGSize) -> TextNodeLayout {
self.constrainedSize = constrainedSize
let makeLayout = TextView.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: false))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let _ = apply()
return layout
}

View File

@ -123,6 +123,14 @@ public final class NavigationBackgroundNode: ASDisplayNode {
private let backgroundNode: ASDisplayNode
private var validLayout: (CGSize, CGFloat)?
public var backgroundCornerRadius: CGFloat {
if let (_, cornerRadius) = self.validLayout {
return cornerRadius
} else {
return 0.0
}
}
public init(color: UIColor, enableBlur: Bool = true) {
self._color = .clear

View File

@ -132,9 +132,8 @@ public final class TextNodeLayoutArguments {
public let textShadowColor: UIColor?
public let textStroke: (UIColor, CGFloat)?
public let displaySpoilers: Bool
public let countIfMoreThanOneLineOverMaximum: Bool
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, minimumNumberOfLines: Int = 0, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, verticalAlignment: TextVerticalAlignment = .top, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil, textStroke: (UIColor, CGFloat)? = nil, displaySpoilers: Bool = false, countIfMoreThanOneLineOverMaximum: Bool = false) {
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, minimumNumberOfLines: Int = 0, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, verticalAlignment: TextVerticalAlignment = .top, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil, textStroke: (UIColor, CGFloat)? = nil, displaySpoilers: Bool = false) {
self.attributedString = attributedString
self.backgroundColor = backgroundColor
self.minimumNumberOfLines = minimumNumberOfLines
@ -150,7 +149,6 @@ public final class TextNodeLayoutArguments {
self.textShadowColor = textShadowColor
self.textStroke = textStroke
self.displaySpoilers = displaySpoilers
self.countIfMoreThanOneLineOverMaximum = countIfMoreThanOneLineOverMaximum
}
}
@ -179,9 +177,8 @@ public final class TextNodeLayout: NSObject {
public let hasRTL: Bool
public let spoilers: [(NSRange, CGRect)]
public let spoilerWords: [(NSRange, CGRect)]
public let hasMoreThanOneLineOverMaximum: Bool?
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, explicitAlignment: NSTextAlignment, resolvedAlignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, rawTextSize: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool, hasMoreThanOneLineOverMaximum: Bool?) {
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, explicitAlignment: NSTextAlignment, resolvedAlignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, rawTextSize: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool) {
self.attributedString = attributedString
self.maximumNumberOfLines = maximumNumberOfLines
self.truncationType = truncationType
@ -225,7 +222,6 @@ public final class TextNodeLayout: NSObject {
self.hasRTL = hasRTL
self.spoilers = spoilers
self.spoilerWords = spoilerWords
self.hasMoreThanOneLineOverMaximum = hasMoreThanOneLineOverMaximum
}
public func areLinesEqual(to other: TextNodeLayout) -> Bool {
@ -896,9 +892,6 @@ public class TextNode: ASDisplayNode {
static func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool) -> TextNodeLayout {
if let attributedString = attributedString {
if attributedString.string.hasPrefix("Д") {
print()
}
let stringLength = attributedString.length
let font: CTFont
@ -935,7 +928,7 @@ public class TextNode: ASDisplayNode {
var maybeTypesetter: CTTypesetter?
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
if maybeTypesetter == nil {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
}
let typesetter = maybeTypesetter!
@ -951,9 +944,7 @@ public class TextNode: ASDisplayNode {
var bottomCutoutEnabled = false
var bottomCutoutSize = CGSize()
let hasMoreThanOneLineOverMaximum: Bool? = nil
if let topLeft = cutout?.topLeft {
cutoutMinY = -fontLineSpacing
cutoutMaxY = topLeft.height + fontLineSpacing
@ -1268,9 +1259,9 @@ public class TextNode: ASDisplayNode {
}
}
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: hasMoreThanOneLineOverMaximum)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
} else {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
}
}
@ -1594,7 +1585,7 @@ open class TextView: UIView {
var maybeTypesetter: CTTypesetter?
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
if maybeTypesetter == nil {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
}
let typesetter = maybeTypesetter!
@ -1925,9 +1916,9 @@ open class TextView: UIView {
}
}
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
} else {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
}
}

View File

@ -316,9 +316,9 @@ final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode {
self.addSubnode(self.imageNode)
if theme.overallDarkAppearance {
self.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Premium/ReactionIcon"), color: .white)
self.imageNode.image = generateTintedImage(image: UIImage(bundleImageName: "Premium/BackgroundIcon"), color: .white)
} else {
self.imageNode.image = UIImage(bundleImageName: "Premium/ReactionIcon")
self.imageNode.image = UIImage(bundleImageName: "Premium/BackgroundIcon")
}
}

View File

@ -204,7 +204,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
return strongSelf.context.account.postbox.transaction { transaction -> (Bool, Bool) in
let isStarred = getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
var hasPremium = false
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser, peer.flags.contains(.isPremium) {
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser, peer.isPremium {
hasPremium = true
}
return (isStarred, hasPremium)

View File

@ -174,7 +174,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
lockBackground = UIImageView()
lockBackground.clipsToBounds = true
lockBackground.isUserInteractionEnabled = false
lockBackground.image = UIImage(bundleImageName: "Premium/StickerIcon")
lockBackground.image = PresentationResourcesChat.chatInputMediaStickerGridPremiumIcon(theme)
self.lockBackground = lockBackground
self.view.addSubview(lockBackground)

View File

@ -177,8 +177,8 @@ private final class StickerPackContainer: ASDisplayNode {
self.addSubnode(self.backgroundNode)
self.addSubnode(self.gridNode)
self.addSubnode(self.actionAreaBackgroundNode)
self.addSubnode(self.actionAreaSeparatorNode)
// self.addSubnode(self.actionAreaBackgroundNode)
// self.addSubnode(self.actionAreaSeparatorNode)
self.addSubnode(self.buttonNode)
self.addSubnode(self.titleBackgroundnode)
@ -294,7 +294,7 @@ private final class StickerPackContainer: ASDisplayNode {
return
}
var hasPremium = false
if case let .user(user) = peer, user.flags.contains(.isPremium) {
if let peer = peer, peer.isPremium {
hasPremium = true
}
strongSelf.updateStickerPackContents(contents, hasPremium: hasPremium)
@ -337,7 +337,7 @@ private final class StickerPackContainer: ASDisplayNode {
return strongSelf.context.account.postbox.transaction { transaction -> (Bool, Bool) in
let isStarred = getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
var hasPremium = false
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser, peer.flags.contains(.isPremium) {
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser, peer.isPremium {
hasPremium = true
}
return (isStarred, hasPremium)

View File

@ -17,6 +17,7 @@ private final class CallRatingAlertContentNode: AlertContentNode {
var rating: Int?
private let titleNode: ASTextNode
private var starContainerNode: ASDisplayNode
private let starNodes: [ASButtonNode]
private let actionNodesSeparator: ASDisplayNode
@ -38,6 +39,8 @@ private final class CallRatingAlertContentNode: AlertContentNode {
self.titleNode = ASTextNode()
self.titleNode.maximumNumberOfLines = 3
self.starContainerNode = ASDisplayNode()
var starNodes: [ASButtonNode] = []
for _ in 0 ..< 5 {
starNodes.append(ASButtonNode())
@ -65,10 +68,12 @@ private final class CallRatingAlertContentNode: AlertContentNode {
self.addSubnode(self.titleNode)
self.addSubnode(self.starContainerNode)
for node in self.starNodes {
node.addTarget(self, action: #selector(self.starPressed(_:)), forControlEvents: .touchDown)
node.addTarget(self, action: #selector(self.starReleased(_:)), forControlEvents: .touchUpInside)
self.addSubnode(node)
self.starContainerNode.addSubnode(node)
}
self.addSubnode(self.actionNodesSeparator)
@ -88,6 +93,44 @@ private final class CallRatingAlertContentNode: AlertContentNode {
self.disposable.dispose()
}
override func didLoad() {
super.didLoad()
self.starContainerNode.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
}
@objc func panGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
let location = gestureRecognizer.location(in: self.starContainerNode.view)
var selectedNode: ASButtonNode?
for node in self.starNodes {
if node.frame.contains(location) {
selectedNode = node
break
}
}
if let selectedNode = selectedNode {
switch gestureRecognizer.state {
case .began, .changed:
self.starPressed(selectedNode)
case .ended:
self.starReleased(selectedNode)
case .cancelled:
self.resetStars()
default:
break
}
} else {
self.resetStars()
}
}
private func resetStars() {
for i in 0 ..< self.starNodes.count {
let node = self.starNodes[i]
node.isSelected = false
}
}
@objc func starPressed(_ sender: ASButtonNode) {
if let index = self.starNodes.firstIndex(of: sender) {
self.rating = index + 1
@ -182,9 +225,10 @@ private final class CallRatingAlertContentNode: AlertContentNode {
let starSize = CGSize(width: 42.0, height: 38.0)
let starsOrigin = floorToScreenPixels((resultWidth - starSize.width * 5.0) / 2.0)
self.starContainerNode.frame = CGRect(origin: CGPoint(x: starsOrigin, y: origin.y), size: CGSize(width: starSize.width * CGFloat(self.starNodes.count), height: starSize.height))
for i in 0 ..< self.starNodes.count {
let node = self.starNodes[i]
transition.updateFrame(node: node, frame: CGRect(x: starsOrigin + 42.0 * CGFloat(i), y: origin.y, width: starSize.width, height: starSize.height))
transition.updateFrame(node: node, frame: CGRect(x: starSize.width * CGFloat(i), y: 0.0, width: starSize.width, height: starSize.height))
}
origin.y += titleSize.height

View File

@ -155,7 +155,7 @@ public final class AvailableReactions: Equatable, Codable {
}
private enum CodingKeys: String, CodingKey {
case hash
case newHash
case reactions
}
@ -183,14 +183,14 @@ public final class AvailableReactions: Equatable, Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.hash = try container.decode(Int32.self, forKey: .hash)
self.hash = try container.decodeIfPresent(Int32.self, forKey: .newHash) ?? 0
self.reactions = try container.decode([Reaction].self, forKey: .reactions)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.hash, forKey: .hash)
try container.encode(self.hash, forKey: .newHash)
try container.encode(self.reactions, forKey: .reactions)
}
}

View File

@ -395,6 +395,10 @@ public extension EnginePeer {
var isVerified: Bool {
return self._asPeer().isVerified
}
var isPremium: Bool {
return self._asPeer().isPremium
}
var isService: Bool {
if case let .user(peer) = self {

View File

@ -154,6 +154,15 @@ public extension Peer {
}
}
var isPremium: Bool {
switch self {
case let user as TelegramUser:
return user.flags.contains(.isPremium)
default:
return false
}
}
var isCopyProtectionEnabled: Bool {
switch self {
case let group as TelegramGroup:

View File

@ -85,6 +85,7 @@ public enum PresentationResourceKey: Int32 {
case chatListBadgeBackgroundPinned
case chatListMutedIcon
case chatListVerifiedIcon
case chatListPremiumIcon
case chatListScamRegularIcon
case chatListScamOutgoingIcon
case chatListScamServiceIcon
@ -165,6 +166,7 @@ public enum PresentationResourceKey: Int32 {
case chatInputMediaPanelTrendingGifsIcon
case chatInputMediaPanelStickersModeIcon
case chatInputMediaPanelPremiumIcon
case chatInputMediaStickerGridPremiumIcon
case chatInputButtonPanelButtonImage
case chatInputButtonPanelButtonHighlightedImage

View File

@ -294,13 +294,59 @@ public struct PresentationResourcesChat {
return theme.image(PresentationResourceKey.chatInputMediaPanelPremiumIcon.rawValue, { theme in
return generateImage(CGSize(width: 44.0, height: 42.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PremiumIcon"), color: theme.chat.inputMediaPanel.panelIconColor) {
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0), y: floor((size.height - image.size.height) / 2.0)), size: image.size))
if let image = UIImage(bundleImageName: "Peer Info/PremiumIcon") {
if let cgImage = image.cgImage {
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
}
let colorsArray: [CGColor] = [
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor
]
var locations: [CGFloat] = [0.0, 0.35, 0.65, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
}
})
})
}
public static func chatInputMediaStickerGridPremiumIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputMediaStickerGridPremiumIcon.rawValue, { theme in
return generateImage(CGSize(width: 32.0, height: 32.0), contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if let backgroundImage = UIImage(bundleImageName: "Premium/BackgroundIcon"), let foregroundImage = UIImage(bundleImageName: "Premium/ForegroundIcon") {
context.saveGState()
if let cgImage = backgroundImage.cgImage {
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
}
let colorsArray: [CGColor] = [
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor
]
var locations: [CGFloat] = [0.0, 0.35, 0.65, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
context.restoreGState()
if let cgImage = foregroundImage.cgImage {
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
}
context.setFillColor(UIColor.white.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
}
})
})
}
public static func chatInputMediaPanelRecentStickersIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatInputMediaPanelRecentStickersIconImage.rawValue, { theme in
return generateImage(CGSize(width: 42.0, height: 42.0), contextGenerator: { size, context in

View File

@ -228,6 +228,32 @@ public struct PresentationResourcesChatList {
return UIImage(bundleImageName: "Chat List/PeerVerifiedIcon")?.precomposed()
})
}
public static func premiumIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatListPremiumIcon.rawValue, { theme in
if let image = UIImage(bundleImageName: "Chat List/PeerPremiumIcon") {
return generateImage(image.size, contextGenerator: { size, context in
if let cgImage = image.cgImage {
context.clear(CGRect(origin: CGPoint(), size: size))
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
let colorsArray: [CGColor] = [
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor
]
var locations: [CGFloat] = [0.0, 0.35, 0.65, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
}
}, opaque: false)
} else {
return nil
}
})
}
public static func scamIcon(_ theme: PresentationTheme, strings: PresentationStrings, type: ScamIconType) -> UIImage? {
let key: PresentationResourceKey

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "premiumbadge_20.pdf",
"filename" : "premium_24 (2).pdf",
"idiom" : "universal"
}
],

View File

@ -0,0 +1,97 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 1.565002 2.396484 cm
0.000000 0.000000 0.000000 scn
9.882594 3.317978 m
5.183870 0.439518 l
4.695292 0.140213 4.056584 0.293650 3.757278 0.782228 c
3.611097 1.020851 3.567514 1.308409 3.636422 1.579630 c
4.363782 4.442544 l
4.626348 5.476007 5.333546 6.339901 6.294793 6.801413 c
11.420867 9.262531 l
11.659847 9.377270 11.760565 9.664015 11.645826 9.902994 c
11.552906 10.096530 11.342772 10.204829 11.131232 10.168206 c
5.425254 9.180357 l
4.265362 8.979550 3.075904 9.299845 2.173681 10.055932 c
0.371115 11.566533 l
-0.068036 11.934555 -0.125698 12.588898 0.242323 13.028049 c
0.421316 13.241636 0.678710 13.374235 0.956533 13.395977 c
6.463908 13.826983 l
6.852989 13.857433 7.192064 14.103661 7.341429 14.464218 c
9.466071 19.592974 l
9.685358 20.122318 10.292244 20.373671 10.821589 20.154385 c
11.075764 20.049089 11.277705 19.847147 11.382999 19.592974 c
13.507642 14.464218 l
13.657006 14.103661 13.996081 13.857433 14.385162 13.826983 c
19.922798 13.393608 l
20.494020 13.348906 20.920849 12.849598 20.876144 12.278376 c
20.854639 12.003585 20.724667 11.748647 20.514914 11.569828 c
16.291594 7.969350 l
15.994287 7.715888 15.864580 7.316894 15.956014 6.937058 c
17.254391 1.543314 l
17.388485 0.986258 17.045605 0.425970 16.488548 0.291876 c
16.220879 0.227442 15.938575 0.272047 15.703809 0.415865 c
10.966476 3.317978 l
10.633904 3.521713 10.215167 3.521713 9.882594 3.317978 c
h
f*
n
Q
endstream
endobj
3 0 obj
1468
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001558 00000 n
0000001581 00000 n
0000001754 00000 n
0000001828 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1887
%%EOF

View File

@ -1,97 +0,0 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 1.312988 1.997192 cm
0.000000 0.000000 0.000000 scn
8.235496 2.764818 m
4.319892 0.366102 l
3.912743 0.116680 3.380487 0.244545 3.131065 0.651693 c
3.009248 0.850546 2.972928 1.090177 3.030351 1.316196 c
3.636486 3.701958 l
3.855290 4.563177 4.444622 5.283089 5.245661 5.667681 c
9.517389 7.718613 l
9.716539 7.814228 9.800470 8.053183 9.704855 8.252333 c
9.627422 8.413612 9.452311 8.503861 9.276028 8.473343 c
4.521046 7.650135 l
3.554468 7.482796 2.563253 7.749707 1.811401 8.379781 c
0.309263 9.638615 l
-0.056697 9.945299 -0.104749 10.490585 0.201936 10.856544 c
0.351096 11.034534 0.565592 11.145033 0.797111 11.163151 c
5.386590 11.522324 l
5.710824 11.547698 5.993387 11.752888 6.117858 12.053352 c
7.888392 16.327314 l
8.071132 16.768436 8.576871 16.977896 9.017992 16.795156 c
9.229803 16.707413 9.398088 16.539127 9.485833 16.327314 c
11.256369 12.053352 l
11.380839 11.752888 11.663401 11.547698 11.987636 11.522324 c
16.602333 11.161178 l
17.078350 11.123924 17.434040 10.707836 17.396788 10.231817 c
17.378866 10.002825 17.270555 9.790377 17.095762 9.641360 c
13.576328 6.640962 l
13.328572 6.429744 13.220484 6.097248 13.296679 5.780720 c
14.378658 1.285933 l
14.490404 0.821718 14.204671 0.354813 13.740458 0.243067 c
13.517399 0.189373 13.282146 0.226543 13.086508 0.346392 c
9.138731 2.764818 l
8.861588 2.934598 8.512639 2.934598 8.235496 2.764818 c
h
f*
n
Q
endstream
endobj
3 0 obj
1445
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 20.000000 20.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001535 00000 n
0000001558 00000 n
0000001731 00000 n
0000001805 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1864
%%EOF

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "addreactions_32.pdf",
"filename" : "addreactions2_32.pdf",
"idiom" : "universal"
}
],

View File

@ -1,201 +0,0 @@
%PDF-1.7
1 0 obj
<< /Length 2 0 R
/Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ]
/Domain [ 0.000000 1.000000 ]
/FunctionType 4
>>
stream
{ 0.000000 exch 0.721569 exch 1.000000 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub 0.000000 mul 0.000000 add exch dup 0.000000 sub -0.243137 mul 0.721569 add exch dup 0.000000 sub 0.000000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.000000 exch 0.478431 exch 1.000000 exch } if pop }
endstream
endobj
2 0 obj
337
endobj
3 0 obj
<< /Pattern << /P1 << /Matrix [ 0.000000 -32.000000 32.000000 0.000000 -32.000000 32.000000 ]
/Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
/ColorSpace /DeviceRGB
/Function 1 0 R
/Domain [ 0.000000 1.000000 ]
/ShadingType 2
/Extend [ true true ]
>>
/PatternType 2
/Type /Pattern
>> >> >>
endobj
4 0 obj
<< /Length 5 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
1.000000 1.000000 1.000000 scn
0.000000 16.000000 m
0.000000 24.836555 7.163444 32.000000 16.000000 32.000000 c
16.000000 32.000000 l
24.836555 32.000000 32.000000 24.836555 32.000000 16.000000 c
32.000000 16.000000 l
32.000000 7.163445 24.836555 0.000000 16.000000 0.000000 c
16.000000 0.000000 l
7.163444 0.000000 0.000000 7.163445 0.000000 16.000000 c
0.000000 16.000000 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
/Pattern cs
/P1 scn
16.000000 0.000000 m
17.874004 0.000000 19.672758 0.322180 21.343897 0.914169 c
21.572962 0.677401 21.840872 0.478514 22.138031 0.327103 c
22.516409 0.134308 22.909628 0.062992 23.304974 0.030691 c
23.679550 0.000088 24.134321 0.000103 24.661480 0.000122 c
27.338524 0.000122 l
27.865683 0.000103 28.320454 0.000088 28.695030 0.030691 c
29.090376 0.062992 29.483595 0.134308 29.861973 0.327103 c
30.426460 0.614723 30.885403 1.073666 31.173021 1.638149 c
31.365814 2.016529 31.437132 2.409748 31.469433 2.805094 c
31.500034 3.179638 31.500019 3.634357 31.500002 4.161457 c
31.500002 4.161480 l
31.500002 4.161600 l
31.500002 4.838646 l
31.500002 4.838766 l
31.500002 4.838789 l
31.500019 5.365887 31.500034 5.820606 31.469433 6.195150 c
31.437132 6.590496 31.365814 6.983715 31.173021 7.362093 c
30.938610 7.822155 30.590397 8.212109 30.163998 8.496347 c
30.163998 8.551201 l
31.336399 10.775940 32.000000 13.310474 32.000000 16.000000 c
32.000000 24.836555 24.836555 32.000000 16.000000 32.000000 c
7.163444 32.000000 0.000000 24.836555 0.000000 16.000000 c
0.000000 7.163445 7.163444 0.000000 16.000000 0.000000 c
h
16.562183 8.757324 m
20.546453 6.359240 l
20.584953 6.699120 20.660887 7.036112 20.826982 7.362093 c
21.061394 7.822151 21.409601 8.212103 21.835997 8.496340 c
21.835997 9.500063 l
21.835997 10.039848 21.938705 10.555647 22.125647 11.028978 c
21.774387 12.530510 l
21.686312 12.907011 21.814457 13.301291 22.107056 13.554068 c
26.503191 17.351892 l
27.199739 17.953642 26.828377 19.096992 25.911175 19.174591 c
20.119438 19.664600 l
19.735479 19.697086 19.400976 19.939270 19.250256 20.293901 c
16.982876 25.628805 l
16.624168 26.472807 15.427922 26.472807 15.069214 25.628805 c
12.801836 20.293903 l
12.651114 19.939270 12.316612 19.697086 11.932652 19.664600 c
6.140916 19.174591 l
5.223714 19.096992 4.852351 17.953640 5.548900 17.351891 c
7.200050 15.925461 l
7.880803 15.337358 8.782333 15.071831 9.673182 15.197048 c
19.282505 16.547745 l
19.848860 16.627354 20.083559 15.847515 19.567341 15.601313 c
11.272570 11.645247 l
10.417443 11.237408 9.794044 10.463003 9.578240 9.540506 c
8.956986 6.884823 l
8.747575 5.989647 9.717782 5.283152 10.505456 5.757242 c
15.489906 8.757324 l
15.819762 8.955860 16.232327 8.955860 16.562183 8.757324 c
h
22.579525 1.224531 m
22.517185 1.256849 22.456810 1.292316 22.398592 1.330725 c
22.110716 1.520653 21.875710 1.782589 21.717987 2.092140 c
21.500000 2.519964 21.500000 3.080017 21.500000 4.200123 c
21.500000 4.800121 l
21.500000 5.187914 21.500002 5.508577 21.509048 5.779865 c
21.523994 6.228132 21.563643 6.541590 21.668795 6.800331 c
21.683817 6.837294 21.700174 6.873140 21.717989 6.908104 c
21.909735 7.284428 22.215696 7.590389 22.592020 7.782135 c
22.663559 7.818586 22.738798 7.848942 22.819733 7.874222 c
22.825129 7.875908 22.830549 7.877571 22.835997 7.879211 c
22.835997 7.992439 l
22.835995 9.500063 l
22.835995 11.247492 24.252565 12.664062 25.999994 12.664062 c
27.747423 12.664062 29.163994 11.247492 29.163994 9.500063 c
29.163994 7.879215 l
29.251150 7.852966 29.331669 7.821018 29.407982 7.782135 c
29.490490 7.740095 29.569614 7.692564 29.644896 7.640007 c
29.912977 7.452845 30.132307 7.201921 30.282015 6.908104 c
30.500002 6.480280 30.500002 5.920227 30.500002 4.800121 c
30.500002 4.200123 l
30.500002 3.080017 30.500002 2.519964 30.282015 2.092140 c
30.258047 2.045099 30.232294 1.999159 30.204842 1.954403 c
30.012678 1.641111 29.737267 1.385887 29.407982 1.218109 c
28.980160 1.000122 28.420107 1.000122 27.300003 1.000122 c
24.700001 1.000122 l
23.579897 1.000122 23.019844 1.000122 22.592020 1.218109 c
22.587847 1.220236 22.583681 1.222376 22.579525 1.224531 c
h
25.999994 11.336063 m
24.986000 11.336063 24.163994 10.514057 24.163994 9.500063 c
24.163994 7.998974 l
24.327213 8.000122 24.505131 8.000122 24.700001 8.000122 c
27.299999 8.000122 l
27.494865 8.000122 27.672779 8.000122 27.835995 7.998974 c
27.835995 9.500063 l
27.835995 10.514057 27.013988 11.336063 25.999994 11.336063 c
h
f*
n
Q
endstream
endobj
5 0 obj
4545
endobj
6 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 32.000000 32.000000 ]
/Resources 3 0 R
/Contents 4 0 R
/Parent 7 0 R
>>
endobj
7 0 obj
<< /Kids [ 6 0 R ]
/Count 1
/Type /Pages
>>
endobj
8 0 obj
<< /Pages 7 0 R
/Type /Catalog
>>
endobj
xref
0 9
0000000000 65535 f
0000000010 00000 n
0000000531 00000 n
0000000553 00000 n
0000001179 00000 n
0000005780 00000 n
0000005803 00000 n
0000005976 00000 n
0000006050 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 8 0 R
/Size 9
>>
startxref
6109
%%EOF

View File

@ -982,7 +982,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
var hasPremium = false
if case let .user(user) = peer, user.flags.contains(.isPremium) {
if case let .user(user) = peer, user.isPremium {
hasPremium = true
}
@ -1079,6 +1079,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: selectAll)), items: .single(actions), recognizer: recognizer, gesture: gesture)
controller.getOverlayViews = { [weak self] in
guard let strongSelf = self else {
return []
}
return [strongSelf.chatDisplayNode.navigateButtons.view]
}
strongSelf.currentContextController = controller
controller.reactionSelected = { [weak controller] value, isLarge in
@ -2347,15 +2353,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
guard let message = message else {
return
}
let context = strongSelf.context
let chatPresentationInterfaceState = strongSelf.presentationInterfaceState
let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData)
let isCopyLink: Bool
var isCopyLink = false
var isForward = false
if message.id.namespace == Namespaces.Message.Cloud, let _ = message.peers[message.id.peerId] as? TelegramChannel, !(message.media.first is TelegramMediaAction) {
isCopyLink = true
} else {
isCopyLink = false
} else if let forwardInfo = message.forwardInfo, let _ = forwardInfo.author as? TelegramChannel {
isCopyLink = true
isForward = true
}
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
@ -2368,13 +2377,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}),
ActionSheetButtonItem(title: isCopyLink ? strongSelf.presentationData.strings.Conversation_ContextMenuCopyLink : strongSelf.presentationData.strings.Conversation_LinkDialogCopy, color: .accent, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
if isCopyLink, let channel = message.peers[message.id.peerId] as? TelegramChannel {
var messageId = message.id
var channel = message.peers[message.id.peerId]
if isForward, let forwardMessageId = message.forwardInfo?.sourceMessageId, let forwardAuthor = message.forwardInfo?.author as? TelegramChannel {
messageId = forwardMessageId
channel = forwardAuthor
}
if isCopyLink, let channel = channel as? TelegramChannel {
var threadMessageId: MessageId?
if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation {
threadMessageId = replyThreadMessage.messageId
}
let _ = (context.engine.messages.exportMessageLink(peerId: message.id.peerId, messageId: message.id, isThread: threadMessageId != nil)
let _ = (context.engine.messages.exportMessageLink(peerId: messageId.peerId, messageId: messageId, isThread: threadMessageId != nil)
|> map { result -> String? in
return result
}

View File

@ -202,6 +202,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.isLoadingValue = isLoading
if isLoading {
self.historyNodeContainer.supernode?.insertSubnode(self.loadingNode, belowSubnode: self.historyNodeContainer)
self.loadingNode.isHidden = false
self.loadingNode.layer.removeAllAnimations()
self.loadingNode.alpha = 1.0
if animated {
@ -215,12 +216,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
if let strongSelf = self {
strongSelf.loadingNode.layer.removeAllAnimations()
if completed {
strongSelf.loadingNode.removeFromSupernode()
strongSelf.loadingNode.isHidden = true
}
}
})
} else {
self.loadingNode.removeFromSupernode()
self.loadingNode.isHidden = true
}
}
}
@ -651,12 +652,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.emptyType = emptyType
if let emptyType = emptyType, self.emptyNode == nil {
let emptyNode = ChatEmptyNode(context: self.context, interaction: self.interfaceInteraction)
if let (size, insets) = self.validEmptyNodeLayout {
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, emptyType: emptyType, size: size, insets: insets, transition: .immediate)
}
emptyNode.isHidden = self.restrictedNode != nil
self.emptyNode = emptyNode
self.historyNodeContainer.supernode?.insertSubnode(emptyNode, aboveSubnode: self.historyNodeContainer)
if let (size, insets) = self.validEmptyNodeLayout {
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, emptyType: emptyType, loadingNode: wasLoading && self.loadingNode.supernode != nil ? self.loadingNode : nil, size: size, insets: insets, transition: .immediate)
}
if animated {
emptyNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
@ -1211,7 +1212,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
emptyNodeInsets.bottom += inputPanelsHeight
self.validEmptyNodeLayout = (contentBounds.size, emptyNodeInsets)
if let emptyNode = self.emptyNode, let emptyType = self.emptyType {
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, emptyType: emptyType, size: contentBounds.size, insets: emptyNodeInsets, transition: transition)
emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, emptyType: emptyType, loadingNode: nil, size: contentBounds.size, insets: emptyNodeInsets, transition: transition)
transition.updateFrame(node: emptyNode, frame: contentBounds)
}

View File

@ -807,7 +807,25 @@ final class ChatEmptyNode: ASDisplayNode {
self.addSubnode(self.backgroundNode)
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, emptyType: ChatHistoryNodeLoadState.EmptyType, size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
func animateFromLoadingNode(_ loadingNode: ChatLoadingNode) {
guard let (_, node) = content else {
return
}
let duration: Double = 0.2
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
node.layer.animateScale(from: 0.0, to: 1.0, duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
let targetCornerRadius = self.backgroundNode.backgroundCornerRadius
let targetFrame = self.backgroundNode.frame
let initialFrame = loadingNode.convert(loadingNode.progressFrame, to: self)
self.backgroundNode.layer.animateFrame(from: initialFrame, to: targetFrame, duration: duration)
self.backgroundNode.update(size: initialFrame.size, cornerRadius: initialFrame.size.width / 2.0, transition: .immediate)
self.backgroundNode.update(size: targetFrame.size, cornerRadius: targetCornerRadius, transition: .animated(duration: duration, curve: .easeInOut))
}
func updateLayout(interfaceState: ChatPresentationInterfaceState, emptyType: ChatHistoryNodeLoadState.EmptyType, loadingNode: ChatLoadingNode?, size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
if self.currentTheme !== interfaceState.theme || self.currentStrings !== interfaceState.strings {
self.currentTheme = interfaceState.theme
self.currentStrings = interfaceState.strings
@ -904,5 +922,9 @@ final class ChatEmptyNode: ASDisplayNode {
transition.updateFrame(node: self.backgroundNode, frame: contentFrame)
self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: min(20.0, self.backgroundNode.bounds.height / 2.0), transition: transition)
if let loadingNode = loadingNode {
self.animateFromLoadingNode(loadingNode)
}
}
}

View File

@ -38,5 +38,9 @@ final class ChatLoadingNode: ASDisplayNode {
let activitySize = self.activityIndicator.measure(size)
transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: displayRect.minX + floor((displayRect.width - activitySize.width) / 2.0) + self.offset.x, y: displayRect.minY + floor((displayRect.height - activitySize.height) / 2.0) + self.offset.y), size: activitySize))
}
var progressFrame: CGRect {
return self.backgroundNode.frame
}
}

View File

@ -1659,7 +1659,7 @@ final class ChatMediaInputNode: ChatInputNode {
return strongSelf.context.account.postbox.transaction { transaction -> (Bool, Bool) in
let isStarred = getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
var hasPremium = false
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser, peer.flags.contains(.isPremium) {
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser, peer.isPremium {
hasPremium = true
}
return (isStarred, hasPremium)

View File

@ -320,7 +320,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
lockBackground = UIImageView()
lockBackground.clipsToBounds = true
lockBackground.isUserInteractionEnabled = false
lockBackground.image = UIImage(bundleImageName: "Premium/StickerIcon")
lockBackground.image = PresentationResourcesChat.chatInputMediaStickerGridPremiumIcon(item.theme)
self.lockBackground = lockBackground
self.view.addSubview(lockBackground)

View File

@ -37,6 +37,14 @@ private enum ChatTitleIcon {
case mute
}
private enum ChatTitleCredibilityIcon {
case none
case fake
case scam
case verified
case premium
}
final class ChatTitleView: UIView, NavigationBarTitleView {
private let account: Account
@ -59,8 +67,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
private var titleLeftIcon: ChatTitleIcon = .none
private var titleRightIcon: ChatTitleIcon = .none
private var titleFakeIcon = false
private var titleScamIcon = false
private var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
//private var networkStatusNode: ChatTitleNetworkStatusNode?
@ -106,8 +113,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
var segments: [AnimatedCountLabelNode.Segment] = []
var titleLeftIcon: ChatTitleIcon = .none
var titleRightIcon: ChatTitleIcon = .none
var titleScamIcon = false
var titleFakeIcon = false
var titleCredibilityIcon: ChatTitleCredibilityIcon = .none
var isEnabled = true
switch titleContent {
case let .peer(peerView, _, isScheduledMessages):
@ -134,9 +140,13 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
}
}
if peer.isFake {
titleFakeIcon = true
titleCredibilityIcon = .fake
} else if peer.isScam {
titleScamIcon = true
titleCredibilityIcon = .scam
} else if peer.isVerified {
titleCredibilityIcon = .verified
} else if peer.isPremium {
titleCredibilityIcon = .premium
}
}
if peerView.peerId.namespace == Namespaces.Peer.SecretChat {
@ -243,15 +253,20 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
updated = true
}
if titleFakeIcon != self.titleFakeIcon {
self.titleFakeIcon = titleFakeIcon
self.titleCredibilityIconNode.image = titleFakeIcon ? PresentationResourcesChatList.fakeIcon(titleTheme, strings: self.strings, type: .regular) : nil
updated = true
}
if titleScamIcon != self.titleScamIcon {
self.titleScamIcon = titleScamIcon
self.titleCredibilityIconNode.image = titleScamIcon ? PresentationResourcesChatList.scamIcon(titleTheme, strings: self.strings, type: .regular) : nil
if titleCredibilityIcon != self.titleCredibilityIcon {
self.titleCredibilityIcon = titleCredibilityIcon
switch titleCredibilityIcon {
case .none:
self.titleCredibilityIconNode.image = nil
case .fake:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.fakeIcon(titleTheme, strings: self.strings, type: .regular)
case .scam:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.scamIcon(titleTheme, strings: self.strings, type: .regular)
case .verified:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.verifiedIcon(titleTheme)
case .premium:
self.titleCredibilityIconNode.image = PresentationResourcesChatList.premiumIcon(titleTheme)
}
updated = true
}
@ -685,10 +700,14 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)
}
if let image = self.titleCredibilityIconNode.image {
self.titleCredibilityIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width - image.size.width - 1.0, y: 2.0), size: image.size)
var originY: CGFloat = 3.0
if [.fake, .scam].contains(self.titleCredibilityIcon) {
originY = 2.0
}
self.titleCredibilityIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width - image.size.width, y: originY), size: image.size)
}
if let image = self.titleRightIconNode.image {
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0, y: 6.0), size: image.size)
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size)
}
} else {
let titleSize = self.titleNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: transition.isAnimated)
@ -705,10 +724,14 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size)
}
if let image = self.titleCredibilityIconNode.image {
self.titleCredibilityIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width - 1.0, y: titleFrame.minY + 6.0), size: image.size)
var originY: CGFloat = titleFrame.minY + 7.0
if [.fake, .scam].contains(self.titleCredibilityIcon) {
originY = titleFrame.minY + 6.0
}
self.titleCredibilityIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: originY), size: image.size)
}
if let image = self.titleRightIconNode.image {
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width - 1.0, y: titleFrame.minY + 6.0), size: image.size)
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size)
}
}

View File

@ -86,6 +86,7 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
private let maskNode: ASImageNode
private let labelNode: ImmediateTextNode
private let textNode: ImmediateTextNode
private let measureTextNode: ImmediateTextNode
private let bottomSeparatorNode: ASDisplayNode
private let expandBackgroundNode: ASImageNode
@ -120,6 +121,10 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
self.textNode.displaysAsynchronously = false
self.textNode.isUserInteractionEnabled = false
self.measureTextNode = ImmediateTextNode()
self.measureTextNode.displaysAsynchronously = false
self.measureTextNode.isUserInteractionEnabled = false
self.bottomSeparatorNode = ASDisplayNode()
self.bottomSeparatorNode.isLayerBacked = true
@ -285,38 +290,6 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
self.labelNode.attributedText = NSAttributedString(string: item.label, font: Font.regular(14.0), textColor: presentationData.theme.list.itemPrimaryTextColor)
var text = item.text
let maxNumberOfLines: Int
switch item.textBehavior {
case .singleLine:
maxNumberOfLines = 1
self.textNode.maximumNumberOfLines = maxNumberOfLines
self.textNode.cutout = nil
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
case let .multiLine(maxLines, enabledEntities):
if !self.isExpanded {
text = trimToLineCount(text, lineCount: 3)
}
maxNumberOfLines = self.isExpanded ? maxLines : 3
self.textNode.maximumNumberOfLines = maxNumberOfLines
if enabledEntities.isEmpty {
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(17.0), textColor: textColorValue)
} else {
let fontSize: CGFloat = 17.0
let baseFont = Font.regular(fontSize)
let linkFont = baseFont
let boldFont = Font.medium(fontSize)
let italicFont = Font.italic(fontSize)
let boldItalicFont = Font.semiboldItalic(fontSize)
let titleFixedFont = Font.monospace(fontSize)
let entities = generateTextEntities(text, enabledTypes: enabledEntities)
self.textNode.attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: textColorValue, linkColor: presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont)
}
}
if let icon = item.icon {
let iconImage: UIImage?
switch icon {
@ -332,6 +305,55 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
}
let additionalSideInset: CGFloat = !self.iconNode.isHidden ? 32.0 : 0.0
var text = item.text
let maxNumberOfLines: Int
switch item.textBehavior {
case .singleLine:
maxNumberOfLines = 1
self.textNode.maximumNumberOfLines = maxNumberOfLines
self.textNode.cutout = nil
self.textNode.attributedText = NSAttributedString(string: item.text, font: Font.regular(17.0), textColor: textColorValue)
case let .multiLine(maxLines, enabledEntities):
let originalText = text
if !self.isExpanded {
text = trimToLineCount(text, lineCount: 3)
}
func createAttributedText(_ text: String) -> NSAttributedString {
if enabledEntities.isEmpty {
return NSAttributedString(string: text, font: Font.regular(17.0), textColor: textColorValue)
} else {
let fontSize: CGFloat = 17.0
let baseFont = Font.regular(fontSize)
let linkFont = baseFont
let boldFont = Font.medium(fontSize)
let italicFont = Font.italic(fontSize)
let boldItalicFont = Font.semiboldItalic(fontSize)
let titleFixedFont = Font.monospace(fontSize)
let entities = generateTextEntities(text, enabledTypes: enabledEntities)
return stringWithAppliedEntities(text, entities: entities, baseColor: textColorValue, linkColor: presentationData.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: baseFont)
}
}
self.measureTextNode.maximumNumberOfLines = 0
self.measureTextNode.attributedText = createAttributedText(originalText)
let textLayout = self.measureTextNode.updateLayoutInfo(CGSize(width: width - sideInset * 2.0 - additionalSideInset, height: .greatestFiniteMagnitude))
var collapsedNumberOfLines = 3
if textLayout.numberOfLines == 4 {
collapsedNumberOfLines = 4
}
self.textNode.attributedText = createAttributedText(text)
maxNumberOfLines = self.isExpanded ? maxLines : collapsedNumberOfLines
self.textNode.maximumNumberOfLines = maxNumberOfLines
}
let labelSize = self.labelNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude))
let textLayout = self.textNode.updateLayoutInfo(CGSize(width: width - sideInset * 2.0 - additionalSideInset, height: .greatestFiniteMagnitude))
let textSize = textLayout.size

View File

@ -2278,6 +2278,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if themeUpdated || !initializedCredibilityIcon {
let image: UIImage?
var expandedImage: UIImage?
if let peer = peer {
self.initializedCredibilityIcon = true
if peer.isFake {
@ -2297,6 +2298,36 @@ final class PeerInfoHeaderNode: ASDisplayNode {
} else {
image = nil
}
} else if peer.isPremium {
if let sourceImage = UIImage(bundleImageName: "Peer Info/PremiumIcon") {
image = generateImage(sourceImage.size, contextGenerator: { size, context in
if let cgImage = sourceImage.cgImage {
context.clear(CGRect(origin: CGPoint(), size: size))
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
let colorsArray: [CGColor] = [
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0x418eff).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor,
UIColor(rgb: 0xfc7ebd).cgColor
]
var locations: [CGFloat] = [0.0, 0.35, 0.65, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions())
}
}, opaque: false)
expandedImage = generateImage(sourceImage.size, contextGenerator: { size, context in
if let cgImage = sourceImage.cgImage {
context.clear(CGRect(origin: CGPoint(), size: size))
context.clip(to: CGRect(origin: .zero, size: size), mask: cgImage)
context.setFillColor(UIColor(rgb: 0xffffff, alpha: 0.75).cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
}
}, opaque: false)
} else {
image = nil
}
} else {
image = nil
}
@ -2304,7 +2335,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
image = nil
}
self.titleCredibilityIconNode.image = image
self.titleExpandedCredibilityIconNode.image = image
self.titleExpandedCredibilityIconNode.image = expandedImage ?? image
}
self.regularContentNode.alpha = state.isEditing ? 0.0 : 1.0
@ -2524,7 +2555,6 @@ final class PeerInfoHeaderNode: ASDisplayNode {
if let image = self.titleCredibilityIconNode.image {
transition.updateFrame(node: self.titleCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleSize.width + 4.0, y: floor((titleSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
transition.updateFrame(node: self.titleExpandedCredibilityIconNode, frame: CGRect(origin: CGPoint(x: titleExpandedSize.width + 4.0, y: floor((titleExpandedSize.height - image.size.height) / 2.0) + 1.0), size: image.size))
}

View File

@ -16,6 +16,8 @@ import DatePickerNode
import DebugSettingsUI
import TabBarUI
import TelegramCallsUI
public final class TelegramRootController: NavigationController {
private let context: AccountContext

View File

@ -445,7 +445,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.handleSendData(data: eventData)
}
case "web_app_setup_main_button":
if let webView = self.webView, !webView.didTouchOnce {
if let webView = self.webView, !webView.didTouchOnce && controller.url == nil {
self.delayedScriptMessage = message
} else if let eventData = (body["eventData"] as? String)?.data(using: .utf8), let json = try? JSONSerialization.jsonObject(with: eventData, options: []) as? [String: Any] {
if var isVisible = json["is_visible"] as? Bool {