mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
9e908c9ea7
commit
ad9f0d6847
@ -33,6 +33,7 @@ public struct AttachmentMainButtonState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public let text: String?
|
public let text: String?
|
||||||
|
public let badge: String?
|
||||||
public let font: Font
|
public let font: Font
|
||||||
public let background: Background
|
public let background: Background
|
||||||
public let textColor: UIColor
|
public let textColor: UIColor
|
||||||
@ -44,6 +45,7 @@ public struct AttachmentMainButtonState {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
text: String?,
|
text: String?,
|
||||||
|
badge: String? = nil,
|
||||||
font: Font,
|
font: Font,
|
||||||
background: Background,
|
background: Background,
|
||||||
textColor: UIColor,
|
textColor: UIColor,
|
||||||
@ -54,6 +56,7 @@ public struct AttachmentMainButtonState {
|
|||||||
position: Position? = nil
|
position: Position? = nil
|
||||||
) {
|
) {
|
||||||
self.text = text
|
self.text = text
|
||||||
|
self.badge = badge
|
||||||
self.font = font
|
self.font = font
|
||||||
self.background = background
|
self.background = background
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
|
@ -79,7 +79,7 @@ public enum ContactMultiselectionControllerMode {
|
|||||||
case channelCreation
|
case channelCreation
|
||||||
case chatSelection(ChatSelection)
|
case chatSelection(ChatSelection)
|
||||||
case premiumGifting(birthdays: [EnginePeer.Id: TelegramBirthday]?, selectToday: Bool, hasActions: Bool)
|
case premiumGifting(birthdays: [EnginePeer.Id: TelegramBirthday]?, selectToday: Bool, hasActions: Bool)
|
||||||
case requestedUsersSelection
|
case requestedUsersSelection(isBot: Bool?, isPremium: Bool?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ContactListFilter {
|
public enum ContactListFilter {
|
||||||
|
@ -59,6 +59,7 @@ public final class PeerSelectionControllerParams {
|
|||||||
public let createNewGroup: (() -> Void)?
|
public let createNewGroup: (() -> Void)?
|
||||||
public let pretendPresentedInModal: Bool
|
public let pretendPresentedInModal: Bool
|
||||||
public let multipleSelection: Bool
|
public let multipleSelection: Bool
|
||||||
|
public let multipleSelectionLimit: Int32?
|
||||||
public let forwardedMessageIds: [EngineMessage.Id]
|
public let forwardedMessageIds: [EngineMessage.Id]
|
||||||
public let hasTypeHeaders: Bool
|
public let hasTypeHeaders: Bool
|
||||||
public let selectForumThreads: Bool
|
public let selectForumThreads: Bool
|
||||||
@ -80,6 +81,7 @@ public final class PeerSelectionControllerParams {
|
|||||||
createNewGroup: (() -> Void)? = nil,
|
createNewGroup: (() -> Void)? = nil,
|
||||||
pretendPresentedInModal: Bool = false,
|
pretendPresentedInModal: Bool = false,
|
||||||
multipleSelection: Bool = false,
|
multipleSelection: Bool = false,
|
||||||
|
multipleSelectionLimit: Int32? = nil,
|
||||||
forwardedMessageIds: [EngineMessage.Id] = [],
|
forwardedMessageIds: [EngineMessage.Id] = [],
|
||||||
hasTypeHeaders: Bool = false,
|
hasTypeHeaders: Bool = false,
|
||||||
selectForumThreads: Bool = false,
|
selectForumThreads: Bool = false,
|
||||||
@ -100,6 +102,7 @@ public final class PeerSelectionControllerParams {
|
|||||||
self.createNewGroup = createNewGroup
|
self.createNewGroup = createNewGroup
|
||||||
self.pretendPresentedInModal = pretendPresentedInModal
|
self.pretendPresentedInModal = pretendPresentedInModal
|
||||||
self.multipleSelection = multipleSelection
|
self.multipleSelection = multipleSelection
|
||||||
|
self.multipleSelectionLimit = multipleSelectionLimit
|
||||||
self.forwardedMessageIds = forwardedMessageIds
|
self.forwardedMessageIds = forwardedMessageIds
|
||||||
self.hasTypeHeaders = hasTypeHeaders
|
self.hasTypeHeaders = hasTypeHeaders
|
||||||
self.selectForumThreads = selectForumThreads
|
self.selectForumThreads = selectForumThreads
|
||||||
|
@ -382,12 +382,102 @@ private final class LoadingProgressNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class BadgeNode: ASDisplayNode {
|
||||||
|
private var fillColor: UIColor
|
||||||
|
private var strokeColor: UIColor
|
||||||
|
private var textColor: UIColor
|
||||||
|
|
||||||
|
private let textNode: ImmediateTextNode
|
||||||
|
private let backgroundNode: ASImageNode
|
||||||
|
|
||||||
|
private let font: UIFont = Font.with(size: 15.0, design: .round, weight: .bold)
|
||||||
|
|
||||||
|
var text: String = "" {
|
||||||
|
didSet {
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor)
|
||||||
|
self.invalidateCalculatedLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) {
|
||||||
|
self.fillColor = fillColor
|
||||||
|
self.strokeColor = strokeColor
|
||||||
|
self.textColor = textColor
|
||||||
|
|
||||||
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.isUserInteractionEnabled = false
|
||||||
|
self.textNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
self.backgroundNode = ASImageNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
self.backgroundNode.displayWithoutProcessing = true
|
||||||
|
self.backgroundNode.displaysAsynchronously = false
|
||||||
|
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: fillColor, strokeColor: nil, strokeWidth: 1.0)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
|
|
||||||
|
self.isUserInteractionEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTheme(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) {
|
||||||
|
self.fillColor = fillColor
|
||||||
|
self.strokeColor = strokeColor
|
||||||
|
self.textColor = textColor
|
||||||
|
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: fillColor, strokeColor: strokeColor, strokeWidth: 1.0)
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateBump(incremented: Bool) {
|
||||||
|
if incremented {
|
||||||
|
let firstTransition = ContainedViewLayoutTransition.animated(duration: 0.1, curve: .easeInOut)
|
||||||
|
firstTransition.updateTransformScale(layer: self.backgroundNode.layer, scale: 1.2)
|
||||||
|
firstTransition.updateTransformScale(layer: self.textNode.layer, scale: 1.2, completion: { finished in
|
||||||
|
if finished {
|
||||||
|
let secondTransition = ContainedViewLayoutTransition.animated(duration: 0.1, curve: .easeInOut)
|
||||||
|
secondTransition.updateTransformScale(layer: self.backgroundNode.layer, scale: 1.0)
|
||||||
|
secondTransition.updateTransformScale(layer: self.textNode.layer, scale: 1.0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let firstTransition = ContainedViewLayoutTransition.animated(duration: 0.1, curve: .easeInOut)
|
||||||
|
firstTransition.updateTransformScale(layer: self.backgroundNode.layer, scale: 0.8)
|
||||||
|
firstTransition.updateTransformScale(layer: self.textNode.layer, scale: 0.8, completion: { finished in
|
||||||
|
if finished {
|
||||||
|
let secondTransition = ContainedViewLayoutTransition.animated(duration: 0.1, curve: .easeInOut)
|
||||||
|
secondTransition.updateTransformScale(layer: self.backgroundNode.layer, scale: 1.0)
|
||||||
|
secondTransition.updateTransformScale(layer: self.textNode.layer, scale: 1.0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateOut() {
|
||||||
|
let timingFunction = CAMediaTimingFunctionName.easeInEaseOut.rawValue
|
||||||
|
self.backgroundNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, delay: 0.0, timingFunction: timingFunction, removeOnCompletion: true, completion: nil)
|
||||||
|
self.textNode.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, delay: 0.0, timingFunction: timingFunction, removeOnCompletion: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(_ constrainedSize: CGSize) -> CGSize {
|
||||||
|
let badgeSize = self.textNode.updateLayout(constrainedSize)
|
||||||
|
let backgroundSize = CGSize(width: max(18.0, badgeSize.width + 8.0), height: 18.0)
|
||||||
|
let backgroundFrame = CGRect(origin: CGPoint(), size: backgroundSize)
|
||||||
|
self.backgroundNode.frame = backgroundFrame
|
||||||
|
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(backgroundFrame.midX - badgeSize.width / 2.0), y: floorToScreenPixels((backgroundFrame.size.height - badgeSize.height) / 2.0) - UIScreenPixel), size: badgeSize)
|
||||||
|
|
||||||
|
return backgroundSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class MainButtonNode: HighlightTrackingButtonNode {
|
private final class MainButtonNode: HighlightTrackingButtonNode {
|
||||||
private var state: AttachmentMainButtonState
|
private var state: AttachmentMainButtonState
|
||||||
private var size: CGSize?
|
private var size: CGSize?
|
||||||
|
|
||||||
private let backgroundAnimationNode: ASImageNode
|
private let backgroundAnimationNode: ASImageNode
|
||||||
fileprivate let textNode: ImmediateTextNode
|
fileprivate let textNode: ImmediateTextNode
|
||||||
|
private var badgeNode: BadgeNode?
|
||||||
private let statusNode: SemanticStatusNode
|
private let statusNode: SemanticStatusNode
|
||||||
private var progressNode: ASImageNode?
|
private var progressNode: ASImageNode?
|
||||||
|
|
||||||
@ -616,6 +706,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
|
|||||||
progressNode.image = generateIndefiniteActivityIndicatorImage(color: state.textColor, diameter: diameter, lineWidth: 3.0)
|
progressNode.image = generateIndefiniteActivityIndicatorImage(color: state.textColor, diameter: diameter, lineWidth: 3.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var textFrame: CGRect = .zero
|
||||||
if let text = state.text {
|
if let text = state.text {
|
||||||
let font: UIFont
|
let font: UIFont
|
||||||
switch state.font {
|
switch state.font {
|
||||||
@ -627,13 +718,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
|
|||||||
self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: state.textColor)
|
self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: state.textColor)
|
||||||
|
|
||||||
let textSize = self.textNode.updateLayout(size)
|
let textSize = self.textNode.updateLayout(size)
|
||||||
let textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||||
if self.textNode.frame.width.isZero {
|
|
||||||
self.textNode.frame = textFrame
|
|
||||||
} else {
|
|
||||||
self.textNode.bounds = CGRect(origin: .zero, size: textSize)
|
|
||||||
transition.updatePosition(node: self.textNode, position: textFrame.center)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch state.background {
|
switch state.background {
|
||||||
case let .color(backgroundColor):
|
case let .color(backgroundColor):
|
||||||
@ -669,6 +754,40 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let badge = state.badge {
|
||||||
|
let badgeNode: BadgeNode
|
||||||
|
var badgeTransition = transition
|
||||||
|
if let current = self.badgeNode {
|
||||||
|
badgeNode = current
|
||||||
|
} else {
|
||||||
|
badgeTransition = .immediate
|
||||||
|
var textColor: UIColor
|
||||||
|
switch state.background {
|
||||||
|
case let .color(backgroundColor):
|
||||||
|
textColor = backgroundColor
|
||||||
|
case .premium:
|
||||||
|
textColor = UIColor(rgb: 0x0077ff)
|
||||||
|
}
|
||||||
|
badgeNode = BadgeNode(fillColor: state.textColor, strokeColor: .clear, textColor: textColor)
|
||||||
|
self.badgeNode = badgeNode
|
||||||
|
self.addSubnode(badgeNode)
|
||||||
|
}
|
||||||
|
badgeNode.text = badge
|
||||||
|
let badgeSize = badgeNode.update(CGSize(width: 100.0, height: 100.0))
|
||||||
|
textFrame.origin.x -= badgeSize.width / 2.0
|
||||||
|
badgeTransition.updateFrame(node: badgeNode, frame: CGRect(origin: CGPoint(x: textFrame.maxX + 6.0, y: textFrame.minY + floorToScreenPixels((textFrame.height - badgeSize.height) * 0.5)), size: badgeSize))
|
||||||
|
} else if let badgeNode = self.badgeNode {
|
||||||
|
self.badgeNode = nil
|
||||||
|
badgeNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.textNode.frame.width.isZero {
|
||||||
|
self.textNode.frame = textFrame
|
||||||
|
} else {
|
||||||
|
self.textNode.bounds = CGRect(origin: .zero, size: textFrame.size)
|
||||||
|
transition.updatePosition(node: self.textNode, position: textFrame.center)
|
||||||
|
}
|
||||||
|
|
||||||
if previousState.progress != state.progress {
|
if previousState.progress != state.progress {
|
||||||
if state.progress == .center {
|
if state.progress == .center {
|
||||||
self.transitionToProgress()
|
self.transitionToProgress()
|
||||||
|
@ -2491,6 +2491,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
case let .user(userType):
|
case let .user(userType):
|
||||||
if case let .user(user) = peer {
|
if case let .user(user) = peer {
|
||||||
match = true
|
match = true
|
||||||
|
if user.id.isVerificationCodes {
|
||||||
|
match = false
|
||||||
|
}
|
||||||
if let isBot = userType.isBot {
|
if let isBot = userType.isBot {
|
||||||
if isBot != (user.botInfo != nil) {
|
if isBot != (user.botInfo != nil) {
|
||||||
match = false
|
match = false
|
||||||
|
@ -878,14 +878,10 @@ private final class ChatListMediaPreviewNode: ASDisplayNode {
|
|||||||
if file.isInstantVideo {
|
if file.isInstantVideo {
|
||||||
isRound = true
|
isRound = true
|
||||||
}
|
}
|
||||||
if file.isSticker || file.isAnimatedSticker {
|
if file.isVideo && !file.isAnimated {
|
||||||
self.playIcon.isHidden = true
|
self.playIcon.isHidden = false
|
||||||
} else {
|
} else {
|
||||||
if file.isAnimated {
|
self.playIcon.isHidden = true
|
||||||
self.playIcon.isHidden = true
|
|
||||||
} else {
|
|
||||||
self.playIcon.isHidden = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let mediaDimensions = file.dimensions {
|
if let mediaDimensions = file.dimensions {
|
||||||
dimensions = mediaDimensions.cgSize
|
dimensions = mediaDimensions.cgSize
|
||||||
@ -2646,6 +2642,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
} else if contentImageIsDisplayedAsAvatar && (file.isSticker || file.isVideoSticker) {
|
} else if contentImageIsDisplayedAsAvatar && (file.isSticker || file.isVideoSticker) {
|
||||||
let fitSize = contentImageSize
|
let fitSize = contentImageSize
|
||||||
contentImageSpecs.append(ContentImageSpec(message: message, media: .file(file), size: fitSize))
|
contentImageSpecs.append(ContentImageSpec(message: message, media: .file(file), size: fitSize))
|
||||||
|
} else if !file.previewRepresentations.isEmpty, let _ = file.dimensions {
|
||||||
|
let fitSize = contentImageSize
|
||||||
|
contentImageSpecs.append(ContentImageSpec(message: message, media: .file(file), size: fitSize))
|
||||||
}
|
}
|
||||||
break inner
|
break inner
|
||||||
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||||
|
@ -2464,6 +2464,9 @@ public final class ChatListNode: ListView {
|
|||||||
case let .user(userType):
|
case let .user(userType):
|
||||||
if case let .user(user) = peer {
|
if case let .user(user) = peer {
|
||||||
match = true
|
match = true
|
||||||
|
if user.id.isVerificationCodes {
|
||||||
|
match = false
|
||||||
|
}
|
||||||
if let isBot = userType.isBot {
|
if let isBot = userType.isBot {
|
||||||
if isBot != (user.botInfo != nil) {
|
if isBot != (user.botInfo != nil) {
|
||||||
match = false
|
match = false
|
||||||
|
@ -59,7 +59,7 @@ public final class CounterControllerTitleView: UIView {
|
|||||||
let primaryTextColor = self.primaryTextColor ?? self.theme.rootController.navigationBar.primaryTextColor
|
let primaryTextColor = self.primaryTextColor ?? self.theme.rootController.navigationBar.primaryTextColor
|
||||||
let secondaryTextColor = self.secondaryTextColor ?? self.theme.rootController.navigationBar.secondaryTextColor
|
let secondaryTextColor = self.secondaryTextColor ?? self.theme.rootController.navigationBar.secondaryTextColor
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.title.title, font: Font.semibold(17.0), textColor: primaryTextColor)
|
self.titleNode.attributedText = NSAttributedString(string: self.title.title, font: Font.semibold(17.0), textColor: primaryTextColor)
|
||||||
self.subtitleNode.attributedText = NSAttributedString(string: self.title.counter, font: Font.regular(13.0), textColor: secondaryTextColor)
|
self.subtitleNode.attributedText = NSAttributedString(string: self.title.counter, font: Font.with(size: 13.0, traits: .monospacedNumbers), textColor: secondaryTextColor)
|
||||||
|
|
||||||
self.accessibilityLabel = self.title.title
|
self.accessibilityLabel = self.title.title
|
||||||
self.accessibilityValue = self.title.counter
|
self.accessibilityValue = self.title.counter
|
||||||
|
@ -381,7 +381,11 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
|||||||
|
|
||||||
- (void)setValue:(CGFloat)value animated:(BOOL)__unused animated
|
- (void)setValue:(CGFloat)value animated:(BOOL)__unused animated
|
||||||
{
|
{
|
||||||
_value = MIN(MAX(_lowerBoundValue, MAX(value, _minimumValue)), _maximumValue);
|
if (_lowerBoundValue > FLT_EPSILON) {
|
||||||
|
_value = MIN(MAX(_lowerBoundValue, MAX(value, _minimumValue)), _maximumValue);
|
||||||
|
} else {
|
||||||
|
_value = MIN(MAX(value, _minimumValue), _maximumValue);
|
||||||
|
}
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,9 +297,24 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
|
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var visibleRect: CGRect?
|
||||||
|
override func updateAbsoluteRect(_ absoluteRect: CGRect, within containerSize: CGSize) {
|
||||||
|
var y: CGFloat
|
||||||
|
if absoluteRect.minY > 0.0 {
|
||||||
|
y = 0.0
|
||||||
|
} else {
|
||||||
|
y = absoluteRect.minY * -1.0
|
||||||
|
}
|
||||||
|
var rect = CGRect(origin: CGPoint(x: 0.0, y: y), size: CGSize(width: containerSize.width, height: containerSize.height))
|
||||||
|
rect.size.height += 96.0
|
||||||
|
self.visibleRect = rect
|
||||||
|
|
||||||
|
self.updateVisibleItems(attemptSynchronousLoads: false, transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
func updateVisibleItems(attemptSynchronousLoads: Bool, transition: ContainedViewLayoutTransition) {
|
func updateVisibleItems(attemptSynchronousLoads: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
guard let item = self.item, !self.size.width.isZero else {
|
guard let item = self.item, !self.size.width.isZero, let visibleRect = self.visibleRect else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,17 +336,26 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: item.title != nil ? 61.0 : 0.0), size: CGSize(width: itemLayout.width, height: itemLayout.height))
|
self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: item.title != nil ? 61.0 : 0.0), size: CGSize(width: itemLayout.width, height: itemLayout.height))
|
||||||
|
|
||||||
for index in 0 ..< items.count {
|
for index in 0 ..< items.count {
|
||||||
|
var itemFrame = itemLayout.frame(itemIndex: index)
|
||||||
|
if !visibleRect.intersects(itemFrame) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
let item = items[index]
|
let item = items[index]
|
||||||
let itemId = EmojiKeyboardItemLayer.Key(
|
let itemId = EmojiKeyboardItemLayer.Key(
|
||||||
groupId: 0,
|
groupId: 0,
|
||||||
itemId: .animation(.file(item.file.fileId))
|
itemId: .animation(.file(item.file.fileId))
|
||||||
)
|
)
|
||||||
validIds.insert(itemId)
|
|
||||||
|
|
||||||
let itemDimensions = item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
let itemDimensions = item.file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
|
||||||
let itemNativeFitSize = itemDimensions.fitted(CGSize(width: nativeItemSize, height: nativeItemSize))
|
let itemNativeFitSize = itemDimensions.fitted(CGSize(width: nativeItemSize, height: nativeItemSize))
|
||||||
let itemVisibleFitSize = itemDimensions.fitted(CGSize(width: itemLayout.visibleItemSize, height: itemLayout.visibleItemSize))
|
let itemVisibleFitSize = itemDimensions.fitted(CGSize(width: itemLayout.visibleItemSize, height: itemLayout.visibleItemSize))
|
||||||
|
|
||||||
|
validIds.insert(itemId)
|
||||||
|
|
||||||
|
itemFrame.origin.x += floor((itemFrame.width - itemVisibleFitSize.width) / 2.0)
|
||||||
|
itemFrame.origin.y += floor((itemFrame.height - itemVisibleFitSize.height) / 2.0)
|
||||||
|
itemFrame.size = itemVisibleFitSize
|
||||||
|
|
||||||
var updateItemLayerPlaceholder = false
|
var updateItemLayerPlaceholder = false
|
||||||
var itemTransition = transition
|
var itemTransition = transition
|
||||||
let itemLayer: EmojiKeyboardItemLayer
|
let itemLayer: EmojiKeyboardItemLayer
|
||||||
@ -426,12 +450,6 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
itemLayer.layerTintColor = color.cgColor
|
itemLayer.layerTintColor = color.cgColor
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemFrame = itemLayout.frame(itemIndex: index)
|
|
||||||
|
|
||||||
itemFrame.origin.x += floor((itemFrame.width - itemVisibleFitSize.width) / 2.0)
|
|
||||||
itemFrame.origin.y += floor((itemFrame.height - itemVisibleFitSize.height) / 2.0)
|
|
||||||
itemFrame.size = itemVisibleFitSize
|
|
||||||
|
|
||||||
let itemPosition = CGPoint(x: itemFrame.midX, y: itemFrame.midY)
|
let itemPosition = CGPoint(x: itemFrame.midX, y: itemFrame.midY)
|
||||||
let itemBounds = CGRect(origin: CGPoint(), size: itemFrame.size)
|
let itemBounds = CGRect(origin: CGPoint(), size: itemFrame.size)
|
||||||
itemTransition.updatePosition(layer: itemLayer, position: itemPosition)
|
itemTransition.updatePosition(layer: itemLayer, position: itemPosition)
|
||||||
|
@ -34,6 +34,7 @@ swift_library(
|
|||||||
"//submodules/ContextUI",
|
"//submodules/ContextUI",
|
||||||
"//submodules/TextFormat",
|
"//submodules/TextFormat",
|
||||||
"//submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode",
|
"//submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode",
|
||||||
|
"//submodules/CounterControllerTitleView",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -9,6 +9,7 @@ import ProgressNavigationButtonNode
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import SearchUI
|
import SearchUI
|
||||||
import ChatListUI
|
import ChatListUI
|
||||||
|
import CounterControllerTitleView
|
||||||
|
|
||||||
public final class PeerSelectionControllerImpl: ViewController, PeerSelectionController {
|
public final class PeerSelectionControllerImpl: ViewController, PeerSelectionController {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -64,6 +65,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
|||||||
private let forwardedMessageIds: [EngineMessage.Id]
|
private let forwardedMessageIds: [EngineMessage.Id]
|
||||||
private let hasTypeHeaders: Bool
|
private let hasTypeHeaders: Bool
|
||||||
private let requestPeerType: [ReplyMarkupButtonRequestPeerType]?
|
private let requestPeerType: [ReplyMarkupButtonRequestPeerType]?
|
||||||
|
let multipleSelectionLimit: Int32?
|
||||||
private let hasCreation: Bool
|
private let hasCreation: Bool
|
||||||
let immediatelyActivateMultipleSelection: Bool
|
let immediatelyActivateMultipleSelection: Bool
|
||||||
|
|
||||||
@ -81,6 +83,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private(set) var titleView: CounterControllerTitleView?
|
||||||
private var searchContentNode: NavigationBarSearchContentNode?
|
private var searchContentNode: NavigationBarSearchContentNode?
|
||||||
var tabContainerNode: ChatListFilterTabContainerNode?
|
var tabContainerNode: ChatListFilterTabContainerNode?
|
||||||
private var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)?
|
private var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)?
|
||||||
@ -107,6 +110,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
|||||||
self.requestPeerType = params.requestPeerType
|
self.requestPeerType = params.requestPeerType
|
||||||
self.hasCreation = params.hasCreation
|
self.hasCreation = params.hasCreation
|
||||||
self.immediatelyActivateMultipleSelection = params.immediatelyActivateMultipleSelection
|
self.immediatelyActivateMultipleSelection = params.immediatelyActivateMultipleSelection
|
||||||
|
self.multipleSelectionLimit = params.multipleSelectionLimit
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||||
|
|
||||||
@ -133,7 +137,13 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.title = self.customTitle ?? self.presentationData.strings.Conversation_ForwardTitle
|
if let maxCount = params.multipleSelectionLimit {
|
||||||
|
self.titleView = CounterControllerTitleView(theme: self.presentationData.theme)
|
||||||
|
self.titleView?.title = CounterControllerTitle(title: self.customTitle ?? self.presentationData.strings.Conversation_ForwardTitle, counter: "0/\(maxCount)")
|
||||||
|
self.navigationItem.titleView = self.titleView
|
||||||
|
} else {
|
||||||
|
self.title = self.customTitle ?? self.presentationData.strings.Conversation_ForwardTitle
|
||||||
|
}
|
||||||
|
|
||||||
if params.forumPeerId == nil {
|
if params.forumPeerId == nil {
|
||||||
self.navigationPresentation = .modal
|
self.navigationPresentation = .modal
|
||||||
|
@ -24,6 +24,7 @@ import SolidRoundedButtonNode
|
|||||||
import ContextUI
|
import ContextUI
|
||||||
import TextFormat
|
import TextFormat
|
||||||
import ForwardAccessoryPanelNode
|
import ForwardAccessoryPanelNode
|
||||||
|
import CounterControllerTitleView
|
||||||
|
|
||||||
final class PeerSelectionControllerNode: ASDisplayNode {
|
final class PeerSelectionControllerNode: ASDisplayNode {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -108,7 +109,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
return (self.presentationData, self.presentationDataPromise.get())
|
return (self.presentationData, self.presentationDataPromise.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
init(context: AccountContext, controller: PeerSelectionControllerImpl, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasFilters: Bool, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, hasCreation: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
|
init(context: AccountContext, controller: PeerSelectionControllerImpl, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasFilters: Bool, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, hasCreation: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.present = present
|
self.present = present
|
||||||
@ -215,6 +216,9 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
self.mainContainerNode = nil
|
self.mainContainerNode = nil
|
||||||
self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: chatListMode, theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false, autoSetReady: true, isMainTab: false)
|
self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: chatListMode, theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false, autoSetReady: true, isMainTab: false)
|
||||||
|
if let multipleSelectionLimit = controller.multipleSelectionLimit {
|
||||||
|
self.chatListNode?.selectionLimit = multipleSelectionLimit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -926,7 +930,17 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
|||||||
} else if let chatListNode = self.chatListNode {
|
} else if let chatListNode = self.chatListNode {
|
||||||
chatListNode.selectionCountChanged = { [weak self] count in
|
chatListNode.selectionCountChanged = { [weak self] count in
|
||||||
if let self {
|
if let self {
|
||||||
|
if let _ = self.controller?.multipleSelectionLimit {
|
||||||
|
self.countPanelNode?.buttonTitle = self.presentationData.strings.Premium_Gift_ContactSelection_Proceed
|
||||||
|
} else {
|
||||||
|
self.countPanelNode?.buttonTitle = self.presentationData.strings.ShareMenu_Send
|
||||||
|
}
|
||||||
self.countPanelNode?.count = count
|
self.countPanelNode?.count = count
|
||||||
|
|
||||||
|
if let titleView = self.controller?.titleView, let maxCount = self.controller?.multipleSelectionLimit {
|
||||||
|
titleView.title = CounterControllerTitle(title: titleView.title.title, counter: "\(count)/\(maxCount)")
|
||||||
|
}
|
||||||
|
|
||||||
if let (layout, navigationBarHeight, actualNavigationBarHeight) = self.containerLayout {
|
if let (layout, navigationBarHeight, actualNavigationBarHeight) = self.containerLayout {
|
||||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, actualNavigationBarHeight: actualNavigationBarHeight, transition: .animated(duration: 0.3, curve: .spring))
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, actualNavigationBarHeight: actualNavigationBarHeight, transition: .animated(duration: 0.3, curve: .spring))
|
||||||
}
|
}
|
||||||
@ -1789,10 +1803,11 @@ private final class PeersCountPanelNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var validLayout: (CGFloat, CGFloat, CGFloat)?
|
private var validLayout: (CGFloat, CGFloat, CGFloat)?
|
||||||
|
|
||||||
|
var buttonTitle: String = ""
|
||||||
var count: Int = 0 {
|
var count: Int = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
if self.count != oldValue && self.count > 0 {
|
if self.count != oldValue && self.count > 0 {
|
||||||
self.button.title = self.strings.ShareMenu_Send
|
self.button.title = self.buttonTitle
|
||||||
self.button.badge = "\(self.count)"
|
self.button.badge = "\(self.count)"
|
||||||
|
|
||||||
if let (width, sideInset, bottomInset) = self.validLayout {
|
if let (width, sideInset, bottomInset) = self.validLayout {
|
||||||
|
@ -4060,10 +4060,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.present(controller, in: .window(.root))
|
strongSelf.present(controller, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
if case .user = peerType, maxQuantity > 1 {
|
if case let .user(requestUser) = peerType, maxQuantity > 1, requestUser.isBot == nil && requestUser.isPremium == nil {
|
||||||
let presentationData = self.presentationData
|
let presentationData = self.presentationData
|
||||||
var reachedLimitImpl: ((Int32) -> Void)?
|
var reachedLimitImpl: ((Int32) -> Void)?
|
||||||
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .requestedUsersSelection, isPeerEnabled: { peer in
|
let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .requestedUsersSelection(isBot: requestUser.isBot, isPremium: requestUser.isPremium), isPeerEnabled: { peer in
|
||||||
if case let .user(user) = peer, user.botInfo == nil {
|
if case let .user(user) = peer, user.botInfo == nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@ -4105,7 +4105,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
var createNewGroupImpl: (() -> Void)?
|
var createNewGroupImpl: (() -> Void)?
|
||||||
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: [peerType], hasContactSelector: false, createNewGroup: {
|
let controller = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: [peerType], hasContactSelector: false, createNewGroup: {
|
||||||
createNewGroupImpl?()
|
createNewGroupImpl?()
|
||||||
}, hasCreation: true))
|
}, multipleSelection: maxQuantity > 1, multipleSelectionLimit: maxQuantity > 1 ? maxQuantity : nil, hasCreation: true, immediatelyActivateMultipleSelection: maxQuantity > 1))
|
||||||
|
|
||||||
controller.peerSelected = { [weak self, weak controller] peer, _ in
|
controller.peerSelected = { [weak self, weak controller] peer, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
@ -4126,6 +4126,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
controller.multiplePeersSelected = { [weak controller] peers, _, _, _, _, _ in
|
||||||
|
let peerIds = peers.map { $0.id }
|
||||||
|
let _ = context.engine.peers.sendBotRequestedPeer(messageId: messageId, buttonId: buttonId, requestedPeerIds: peerIds).startStandalone()
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
createNewGroupImpl = { [weak controller] in
|
createNewGroupImpl = { [weak controller] in
|
||||||
switch peerType {
|
switch peerType {
|
||||||
case .user:
|
case .user:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user