mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
3d4ccb4eb0
commit
2246a9f1c2
@ -23,6 +23,7 @@ public final class ReactionIconView: PortalSourceView {
|
||||
private var file: TelegramMediaFile?
|
||||
private var animationCache: AnimationCache?
|
||||
private var animationRenderer: MultiAnimationRenderer?
|
||||
private var contentTintColor: UIColor?
|
||||
private var placeholderColor: UIColor?
|
||||
private var size: CGSize?
|
||||
private var animateIdle: Bool?
|
||||
@ -58,6 +59,7 @@ public final class ReactionIconView: PortalSourceView {
|
||||
fileId: Int64,
|
||||
animationCache: AnimationCache,
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
tintColor: UIColor?,
|
||||
placeholderColor: UIColor,
|
||||
animateIdle: Bool,
|
||||
reaction: MessageReaction.Reaction,
|
||||
@ -66,6 +68,7 @@ public final class ReactionIconView: PortalSourceView {
|
||||
self.context = context
|
||||
self.animationCache = animationCache
|
||||
self.animationRenderer = animationRenderer
|
||||
self.contentTintColor = tintColor
|
||||
self.placeholderColor = placeholderColor
|
||||
self.size = size
|
||||
self.animateIdle = animateIdle
|
||||
@ -112,6 +115,8 @@ public final class ReactionIconView: PortalSourceView {
|
||||
|
||||
transition.updateFrame(layer: animationLayer, frame: CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
|
||||
}
|
||||
|
||||
self.updateTintColor()
|
||||
}
|
||||
|
||||
public func updateIsAnimationHidden(isAnimationHidden: Bool, transition: ContainedViewLayoutTransition) {
|
||||
@ -170,6 +175,36 @@ public final class ReactionIconView: PortalSourceView {
|
||||
animationLayer.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
|
||||
animationLayer.isVisibleForAnimations = animateIdle && context.sharedContext.energyUsageSettings.loopEmoji
|
||||
self.updateTintColor()
|
||||
}
|
||||
|
||||
private func updateTintColor() {
|
||||
guard let file = self.file, let animationLayer = self.animationLayer else {
|
||||
return
|
||||
}
|
||||
var accentTint = false
|
||||
if file.isCustomTemplateEmoji {
|
||||
accentTint = true
|
||||
}
|
||||
for attribute in file.attributes {
|
||||
if case let .CustomEmoji(_, _, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
accentTint = true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if accentTint, let tintColor = self.contentTintColor {
|
||||
animationLayer.contentTintColor = tintColor
|
||||
animationLayer.dynamicColor = tintColor
|
||||
} else {
|
||||
animationLayer.contentTintColor = nil
|
||||
animationLayer.dynamicColor = nil
|
||||
}
|
||||
}
|
||||
|
||||
func reset() {
|
||||
@ -769,6 +804,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView {
|
||||
animateIdle = false
|
||||
}
|
||||
|
||||
let tintColor: UIColor
|
||||
if layout.backgroundLayout.colors.isSelected {
|
||||
if layout.spec.component.colors.selectedForeground != 0 {
|
||||
tintColor = UIColor(argb: layout.spec.component.colors.selectedForeground)
|
||||
} else {
|
||||
tintColor = .white
|
||||
}
|
||||
} else {
|
||||
tintColor = UIColor(argb: layout.spec.component.colors.deselectedForeground)
|
||||
}
|
||||
|
||||
iconView.update(
|
||||
size: layout.imageFrame.size,
|
||||
context: layout.spec.component.context,
|
||||
@ -776,6 +822,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView {
|
||||
fileId: fileId,
|
||||
animationCache: arguments.animationCache,
|
||||
animationRenderer: arguments.animationRenderer,
|
||||
tintColor: tintColor,
|
||||
placeholderColor: layout.spec.component.chosenOrder != nil ? UIColor(argb: layout.spec.component.colors.selectedMediaPlaceholder) : UIColor(argb: layout.spec.component.colors.deselectedMediaPlaceholder),
|
||||
animateIdle: animateIdle,
|
||||
reaction: layout.spec.component.reaction.value,
|
||||
|
@ -218,6 +218,37 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
}
|
||||
reactionLayer.frame = iconFrame
|
||||
}
|
||||
|
||||
self.updateReactionAccentColor()
|
||||
}
|
||||
|
||||
func updateReactionAccentColor() {
|
||||
guard let file = self.file, let reactionLayer = self.reactionLayer, let theme = self.theme else {
|
||||
return
|
||||
}
|
||||
var accentTint = false
|
||||
if file.isCustomTemplateEmoji {
|
||||
accentTint = true
|
||||
}
|
||||
for attribute in file.attributes {
|
||||
if case let .CustomEmoji(_, _, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
accentTint = true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if accentTint {
|
||||
reactionLayer.contentTintColor = theme.contextMenu.badgeFillColor
|
||||
reactionLayer.dynamicColor = theme.contextMenu.badgeFillColor
|
||||
} else {
|
||||
reactionLayer.contentTintColor = nil
|
||||
reactionLayer.dynamicColor = nil
|
||||
}
|
||||
}
|
||||
|
||||
func update(presentationData: PresentationData, constrainedSize: CGSize, isSelected: Bool) -> CGSize {
|
||||
@ -227,6 +258,8 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
if let iconNode = self.iconNode {
|
||||
iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: presentationData.theme.contextMenu.primaryColor)
|
||||
}
|
||||
|
||||
self.updateReactionLayer()
|
||||
}
|
||||
|
||||
let sideInset: CGFloat = 12.0
|
||||
@ -478,6 +511,35 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
}
|
||||
}
|
||||
|
||||
func updateReactionAccentColor(theme: PresentationTheme) {
|
||||
guard let file = self.file, let reactionLayer = self.reactionLayer else {
|
||||
return
|
||||
}
|
||||
var accentTint = false
|
||||
if file.isCustomTemplateEmoji {
|
||||
accentTint = true
|
||||
}
|
||||
for attribute in file.attributes {
|
||||
if case let .CustomEmoji(_, _, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
accentTint = true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if accentTint {
|
||||
reactionLayer.contentTintColor = theme.contextMenu.badgeFillColor
|
||||
reactionLayer.dynamicColor = theme.contextMenu.badgeFillColor
|
||||
} else {
|
||||
reactionLayer.contentTintColor = nil
|
||||
reactionLayer.dynamicColor = nil
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, presentationData: PresentationData, item: EngineMessageReactionListContext.Item, isLast: Bool, syncronousLoad: Bool) {
|
||||
let avatarInset: CGFloat = 12.0
|
||||
let avatarSpacing: CGFloat = 8.0
|
||||
@ -495,6 +557,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
if availableReaction.value == reaction {
|
||||
self.file = availableReaction.centerAnimation
|
||||
self.updateReactionLayer()
|
||||
self.updateReactionAccentColor(theme: presentationData.theme)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -507,6 +570,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
}
|
||||
strongSelf.file = file
|
||||
strongSelf.updateReactionLayer()
|
||||
strongSelf.updateReactionAccentColor(theme: presentationData.theme)
|
||||
}).strict()
|
||||
}
|
||||
} else {
|
||||
@ -520,6 +584,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
||||
}
|
||||
}
|
||||
}
|
||||
self.updateReactionAccentColor(theme: presentationData.theme)
|
||||
|
||||
if self.item != item {
|
||||
self.item = item
|
||||
|
@ -39,7 +39,6 @@ public final class SheetComponentEnvironment: Equatable {
|
||||
|
||||
public let sheetComponentTag = GenericComponentViewTag()
|
||||
public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
|
||||
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
|
||||
|
||||
public enum BackgroundColor: Equatable {
|
||||
@ -54,15 +53,18 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
|
||||
public let content: AnyComponent<ChildEnvironmentType>
|
||||
public let backgroundColor: BackgroundColor
|
||||
public let followContentSizeChanges: Bool
|
||||
public let animateOut: ActionSlot<Action<()>>
|
||||
|
||||
public init(
|
||||
content: AnyComponent<ChildEnvironmentType>,
|
||||
backgroundColor: BackgroundColor,
|
||||
followContentSizeChanges: Bool = false,
|
||||
animateOut: ActionSlot<Action<()>>
|
||||
) {
|
||||
self.content = content
|
||||
self.backgroundColor = backgroundColor
|
||||
self.followContentSizeChanges = followContentSizeChanges
|
||||
self.animateOut = animateOut
|
||||
}
|
||||
|
||||
@ -73,6 +75,9 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
if lhs.backgroundColor != rhs.backgroundColor {
|
||||
return false
|
||||
}
|
||||
if lhs.followContentSizeChanges != rhs.followContentSizeChanges {
|
||||
return false
|
||||
}
|
||||
if lhs.animateOut != rhs.animateOut {
|
||||
return false
|
||||
}
|
||||
@ -338,12 +343,15 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
}
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
|
||||
|
||||
let previousContentSize = self.scrollView.contentSize
|
||||
self.scrollView.contentSize = contentSize
|
||||
self.scrollView.contentInset = UIEdgeInsets(top: max(0.0, availableSize.height - contentSize.height) + contentSize.height, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
self.ignoreScrolling = false
|
||||
|
||||
if let currentAvailableSize = self.currentAvailableSize, currentAvailableSize.height != availableSize.height {
|
||||
self.scrollView.contentOffset = CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height))
|
||||
} else if component.followContentSizeChanges, !previousContentSize.height.isZero, previousContentSize != contentSize {
|
||||
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: availableSize))
|
||||
}
|
||||
if self.currentHasInputHeight != previousHasInputHeight {
|
||||
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: self.scrollView.bounds.size))
|
||||
|
123
submodules/PremiumUI/Sources/PremiumBoostScreen.swift
Normal file
123
submodules/PremiumUI/Sources/PremiumBoostScreen.swift
Normal file
@ -0,0 +1,123 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import UndoUI
|
||||
import PresentationDataUtils
|
||||
|
||||
//TODO:localize
|
||||
|
||||
public func PremiumBoostScreen(
|
||||
context: AccountContext,
|
||||
contentContext: Any?,
|
||||
peerId: EnginePeer.Id,
|
||||
isCurrent: Bool,
|
||||
status: ChannelBoostStatus?,
|
||||
myBoostStatus: MyBoostStatus?,
|
||||
forceDark: Bool,
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
presentController: @escaping (ViewController) -> Void,
|
||||
pushController: @escaping (ViewController) -> Void,
|
||||
dismissed: @escaping () -> Void
|
||||
) {
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).startStandalone(next: { peer in
|
||||
guard let peer, let status else {
|
||||
return
|
||||
}
|
||||
|
||||
var myBoostCount: Int32 = 0
|
||||
var availableBoosts: [MyBoostStatus.Boost] = []
|
||||
var occupiedBoosts: [MyBoostStatus.Boost] = []
|
||||
if let myBoostStatus {
|
||||
for boost in myBoostStatus.boosts {
|
||||
if let boostPeer = boost.peer {
|
||||
if boostPeer.id == peer.id {
|
||||
myBoostCount += 1
|
||||
} else {
|
||||
occupiedBoosts.append(boost)
|
||||
}
|
||||
} else {
|
||||
availableBoosts.append(boost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentLevel = Int32(status.level)
|
||||
var currentLevelBoosts = Int32(status.currentLevelBoosts)
|
||||
var nextLevelBoosts = status.nextLevelBoosts.flatMap(Int32.init)
|
||||
|
||||
if myBoostCount > 0 && status.boosts == currentLevelBoosts {
|
||||
currentLevel = max(0, currentLevel - 1)
|
||||
nextLevelBoosts = currentLevelBoosts
|
||||
currentLevelBoosts = max(0, currentLevelBoosts - 1)
|
||||
}
|
||||
|
||||
let subject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount)
|
||||
let nextSubject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount + 1)
|
||||
var nextCount = Int32(status.boosts + 1)
|
||||
|
||||
var updateImpl: (() -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: subject, count: Int32(status.boosts), forceDark: forceDark, action: {
|
||||
let dismiss = false
|
||||
updateImpl?()
|
||||
return dismiss
|
||||
},
|
||||
openPeer: { peer in
|
||||
openPeer(peer)
|
||||
})
|
||||
pushController(controller)
|
||||
|
||||
controller.disposed = {
|
||||
dismissed()
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
updateImpl = { [weak controller] in
|
||||
if let _ = status.nextLevelBoosts {
|
||||
if let availableBoost = availableBoosts.first {
|
||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot]).startStandalone()
|
||||
controller?.updateSubject(nextSubject, count: nextCount)
|
||||
|
||||
availableBoosts.removeFirst()
|
||||
nextCount += 1
|
||||
} else if !occupiedBoosts.isEmpty, let myBoostStatus {
|
||||
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
|
||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone()
|
||||
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "\(slots.count) boosts are reassigned from 1 other channel.", timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, action: { _ in return true })
|
||||
presentController(undoController)
|
||||
})
|
||||
dismissImpl?()
|
||||
pushController(replaceController)
|
||||
} else {
|
||||
let controller = textAlertController(
|
||||
sharedContext: context.sharedContext,
|
||||
updatedPresentationData: nil,
|
||||
title: presentationData.strings.ChannelBoost_Error_PremiumNeededTitle,
|
||||
text: presentationData.strings.ChannelBoost_Error_PremiumNeededText,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: {
|
||||
dismissImpl?()
|
||||
let controller = context.sharedContext.makePremiumIntroController(context: context, source: .channelBoost(peerId), forceDark: forceDark, dismissed: nil)
|
||||
pushController(controller)
|
||||
})
|
||||
],
|
||||
parseMarkdown: true
|
||||
)
|
||||
presentController(controller)
|
||||
}
|
||||
} else {
|
||||
dismissImpl?()
|
||||
}
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
})
|
||||
}
|
@ -1262,11 +1262,6 @@ public class PremiumDemoScreen: ViewControllerComponentContainer {
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
}
|
||||
|
||||
public override func replace(with controller: ViewController) {
|
||||
self.dismissAnimated()
|
||||
super.replace(with: controller)
|
||||
}
|
||||
|
||||
public func dismissAnimated() {
|
||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||
view.dismissAnimated()
|
||||
|
@ -162,6 +162,9 @@ public class PremiumLimitDisplayComponent: Component {
|
||||
private let badgeLabel: BadgeLabelView
|
||||
private let badgeLabelMaskView = UIImageView()
|
||||
|
||||
private var badgeTailPosition: CGFloat = 0.0
|
||||
private var badgeShapeArguments: (Double, Double, CGSize, CGFloat, CGFloat)?
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
@ -232,6 +235,10 @@ public class PremiumLimitDisplayComponent: Component {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.badgeShapeAnimator?.invalidate()
|
||||
}
|
||||
|
||||
private var didPlayAppearanceAnimation = false
|
||||
func playAppearanceAnimation(component: PremiumLimitDisplayComponent, badgeFullSize: CGSize, from: CGFloat? = nil) {
|
||||
if from == nil {
|
||||
@ -534,17 +541,30 @@ public class PremiumLimitDisplayComponent: Component {
|
||||
|
||||
if badgePosition > 1.0 - 0.15 {
|
||||
progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 1.0, y: 1.0))
|
||||
progressTransition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 1.0).cgPath)
|
||||
|
||||
if component.isPremiumDisabled || progressTransition.animation.isImmediate {
|
||||
self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 1.0).cgPath
|
||||
} else {
|
||||
self.badgeShapeArguments = (CACurrentMediaTime(), 0.5, badgeSize, self.badgeTailPosition, 1.0)
|
||||
self.animateBadgeTailPositionChange()
|
||||
}
|
||||
self.badgeTailPosition = 1.0
|
||||
|
||||
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||
|
||||
} else {
|
||||
self.badgeView.center = CGPoint(x: 3.0 + (size.width - 6.0) * badgePosition + 3.0, y: 82.0)
|
||||
}
|
||||
} else if badgePosition < 0.15 {
|
||||
progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 0.0, y: 1.0))
|
||||
progressTransition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.0).cgPath)
|
||||
|
||||
|
||||
if component.isPremiumDisabled || progressTransition.animation.isImmediate {
|
||||
self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.0).cgPath
|
||||
} else {
|
||||
self.badgeShapeArguments = (CACurrentMediaTime(), 0.5, badgeSize, self.badgeTailPosition, 0.0)
|
||||
self.animateBadgeTailPositionChange()
|
||||
}
|
||||
self.badgeTailPosition = 0.0
|
||||
|
||||
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||
|
||||
} else {
|
||||
@ -552,8 +572,15 @@ public class PremiumLimitDisplayComponent: Component {
|
||||
}
|
||||
} else {
|
||||
progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 0.5, y: 1.0))
|
||||
progressTransition.setShapeLayerPath(layer: self.badgeShapeLayer, path: generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.5).cgPath)
|
||||
|
||||
|
||||
if component.isPremiumDisabled || progressTransition.animation.isImmediate {
|
||||
self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: component.isPremiumDisabled ? nil : 0.5).cgPath
|
||||
} else {
|
||||
self.badgeShapeArguments = (CACurrentMediaTime(), 0.5, badgeSize, self.badgeTailPosition, 0.5)
|
||||
self.animateBadgeTailPositionChange()
|
||||
}
|
||||
self.badgeTailPosition = 0.5
|
||||
|
||||
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||
|
||||
} else {
|
||||
@ -617,6 +644,36 @@ public class PremiumLimitDisplayComponent: Component {
|
||||
return size
|
||||
}
|
||||
|
||||
private var badgeShapeAnimator: ConstantDisplayLinkAnimator?
|
||||
private func animateBadgeTailPositionChange() {
|
||||
if self.badgeShapeAnimator == nil {
|
||||
self.badgeShapeAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
||||
self?.animateBadgeTailPositionChange()
|
||||
})
|
||||
self.badgeShapeAnimator?.isPaused = true
|
||||
}
|
||||
|
||||
if let (startTime, duration, badgeSize, initial, target) = self.badgeShapeArguments {
|
||||
self.badgeShapeAnimator?.isPaused = false
|
||||
|
||||
let t = CGFloat(max(0.0, min(1.0, (CACurrentMediaTime() - startTime) / duration)))
|
||||
let value = initial + (target - initial) * t
|
||||
|
||||
self.badgeShapeLayer.path = generateRoundedRectWithTailPath(rectSize: badgeSize, tailPosition: value).cgPath
|
||||
|
||||
if t >= 1.0 {
|
||||
self.badgeShapeArguments = nil
|
||||
self.badgeShapeAnimator?.isPaused = true
|
||||
self.badgeShapeAnimator?.invalidate()
|
||||
self.badgeShapeAnimator = nil
|
||||
}
|
||||
} else {
|
||||
self.badgeShapeAnimator?.isPaused = true
|
||||
self.badgeShapeAnimator?.invalidate()
|
||||
self.badgeShapeAnimator = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func setupGradientAnimations() {
|
||||
guard let _ = self.component else {
|
||||
return
|
||||
@ -1143,12 +1200,14 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
string = strings.ChannelBoost_HelpUpgradeChannelText(peer.compactDisplayTitle, boostsString, storiesString).string
|
||||
}
|
||||
actionButtonText = strings.ChannelBoost_BoostChannel
|
||||
buttonIconName = "Premium/BoostChannel"
|
||||
} else {
|
||||
titleText = strings.ChannelBoost_MaxLevelReached
|
||||
string = strings.ChannelBoost_BoostedChannelReachedLevel("\(level)", storiesString).string
|
||||
actionButtonText = strings.Common_OK
|
||||
buttonIconName = nil
|
||||
actionButtonHasGloss = false
|
||||
}
|
||||
buttonIconName = "Premium/BoostChannel"
|
||||
}
|
||||
buttonAnimationName = nil
|
||||
defaultTitle = strings.ChannelBoost_Level("\(level)").string
|
||||
@ -1161,11 +1220,13 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
let prefixString = isCurrent ? strings.ChannelBoost_YouBoostedChannelText(peer.compactDisplayTitle).string : strings.ChannelBoost_YouBoostedOtherChannelText
|
||||
|
||||
let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1)
|
||||
|
||||
// buttonIconName = nil
|
||||
// actionButtonText = environment.strings.Common_OK
|
||||
actionButtonText = "Boost Again"
|
||||
|
||||
if let _ = remaining {
|
||||
actionButtonText = "Boost Again"
|
||||
} else {
|
||||
buttonIconName = nil
|
||||
actionButtonText = environment.strings.Common_OK
|
||||
actionButtonHasGloss = false
|
||||
}
|
||||
if let remaining {
|
||||
let boostsString = strings.ChannelBoost_MoreBoosts(remaining)
|
||||
if level == 0 {
|
||||
@ -1209,7 +1270,6 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
reachedMaximumLimit = false
|
||||
}
|
||||
|
||||
|
||||
let contentSize: CGSize
|
||||
if state.initialized {
|
||||
let title = title.update(
|
||||
@ -1391,6 +1451,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
}))
|
||||
)
|
||||
} else if let alternateTextChild {
|
||||
textOffset += 9.0
|
||||
textSize = alternateTextChild.size
|
||||
context.add(alternateTextChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: textOffset))
|
||||
@ -1498,7 +1559,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: buttonFrame.maxY + 50.0 + giftText.size.height / 2.0))
|
||||
)
|
||||
|
||||
additionalContentHeight += giftText.size.height + 50.0
|
||||
additionalContentHeight += giftText.size.height + 30.0
|
||||
}
|
||||
|
||||
contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + additionalContentHeight + 5.0 + environment.safeInsets.bottom)
|
||||
@ -1598,6 +1659,7 @@ private final class LimitSheetComponent: CombinedComponent {
|
||||
openGift: context.component.openGift
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
|
@ -19,6 +19,8 @@ import PeerListItemComponent
|
||||
import TelegramStringFormatting
|
||||
import AvatarNode
|
||||
|
||||
//TODO:localize
|
||||
|
||||
private final class ReplaceBoostScreenComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
@ -799,6 +801,7 @@ public class ReplaceBoostScreen: ViewController {
|
||||
self?.node.selectedSlots = selectedSlots
|
||||
}
|
||||
presentControllerImpl = { [weak self] c in
|
||||
self?.dismissAllTooltips()
|
||||
self?.present(c, in: .window(.root))
|
||||
}
|
||||
|
||||
@ -819,15 +822,29 @@ public class ReplaceBoostScreen: ViewController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override open func loadDisplayNode() {
|
||||
self.displayNode = Node(context: self.context, controller: self, component: self.component)
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
fileprivate func dismissAllTooltips() {
|
||||
self.window?.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismiss()
|
||||
}
|
||||
})
|
||||
self.forEachController({ controller in
|
||||
if let controller = controller as? UndoOverlayController {
|
||||
controller.dismiss()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
self.view.endEditing(true)
|
||||
|
||||
|
@ -133,9 +133,9 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
return { item, params, neighbors in
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
}
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
@ -189,6 +189,11 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
if let currentBackgroundNode {
|
||||
currentBackgroundNode.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
}
|
||||
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
|
||||
var topOffset: CGFloat = 8.0
|
||||
|
@ -261,10 +261,10 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
return { item, params, neighbors in
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
}
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
|
||||
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
@ -330,6 +330,11 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
strongSelf.item = item
|
||||
|
||||
if let currentBackgroundNode {
|
||||
currentBackgroundNode.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
}
|
||||
|
||||
if strongSelf.genericReactionEffectDisposable == nil {
|
||||
strongSelf.loadNextGenericReactionEffect(context: item.context)
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
||||
case let .messageActionSetSameChatWallPaper(wallpaper):
|
||||
return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper)))
|
||||
case let .messageActionGiftCode(flags, boostPeer, months, slug):
|
||||
return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, boostPeerId: boostPeer?.peerId, months: months))
|
||||
return TelegramMediaAction(action: .giftCode(slug: slug, fromGiveaway: (flags & (1 << 0)) != 0, isUnclaimed: (flags & (1 << 1)) != 0, boostPeerId: boostPeer?.peerId, months: months))
|
||||
case .messageActionGiveawayLaunch:
|
||||
return TelegramMediaAction(action: .giveawayLaunched)
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func _internal_applyChannelBoost(account: Account, peerId: PeerId, slots: [Int32
|
||||
|> map (Optional.init)
|
||||
|> `catch` { error -> Signal<Api.premium.MyBoosts?, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> mapToSignal { result -> Signal<MyBoostStatus?, NoError> in
|
||||
if let result = result {
|
||||
return account.postbox.transaction { transaction -> MyBoostStatus? in
|
||||
|
@ -109,7 +109,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
case requestedPeer(buttonId: Int32, peerId: PeerId)
|
||||
case setChatWallpaper(wallpaper: TelegramWallpaper)
|
||||
case setSameChatWallpaper(wallpaper: TelegramWallpaper)
|
||||
case giftCode(slug: String, fromGiveaway: Bool, boostPeerId: PeerId?, months: Int32)
|
||||
case giftCode(slug: String, fromGiveaway: Bool, isUnclaimed: Bool, boostPeerId: PeerId?, months: Int32)
|
||||
case giveawayLaunched
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -206,7 +206,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
case 35:
|
||||
self = .botAppAccessGranted(appName: decoder.decodeOptionalStringForKey("app"), type: decoder.decodeOptionalInt32ForKey("atp").flatMap { BotSendMessageAccessGrantedType(rawValue: $0) })
|
||||
case 36:
|
||||
self = .giftCode(slug: decoder.decodeStringForKey("slug", orElse: ""), fromGiveaway: decoder.decodeBoolForKey("give", orElse: false), boostPeerId: PeerId(decoder.decodeInt64ForKey("pi", orElse: 0)), months: decoder.decodeInt32ForKey("months", orElse: 0))
|
||||
self = .giftCode(slug: decoder.decodeStringForKey("slug", orElse: ""), fromGiveaway: decoder.decodeBoolForKey("give", orElse: false), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: PeerId(decoder.decodeInt64ForKey("pi", orElse: 0)), months: decoder.decodeInt32ForKey("months", orElse: 0))
|
||||
case 37:
|
||||
self = .giveawayLaunched
|
||||
default:
|
||||
@ -388,10 +388,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "atp")
|
||||
}
|
||||
case let .giftCode(slug, fromGiveaway, boostPeerId, months):
|
||||
case let .giftCode(slug, fromGiveaway, unclaimed, boostPeerId, months):
|
||||
encoder.encodeInt32(36, forKey: "_rawValue")
|
||||
encoder.encodeString(slug, forKey: "slug")
|
||||
encoder.encodeBool(fromGiveaway, forKey: "give")
|
||||
encoder.encodeBool(unclaimed, forKey: "unclaimed")
|
||||
if let boostPeerId = boostPeerId {
|
||||
encoder.encodeInt64(boostPeerId.toInt64(), forKey: "pi")
|
||||
} else {
|
||||
@ -421,7 +422,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
return peerIds
|
||||
case let .requestedPeer(_, peerId):
|
||||
return [peerId]
|
||||
case let .giftCode(_, _, boostPeerId, _):
|
||||
case let .giftCode(_, _, _, boostPeerId, _):
|
||||
return boostPeerId.flatMap { [$0] } ?? []
|
||||
default:
|
||||
return []
|
||||
|
@ -166,6 +166,7 @@ private final class StatusReactionNode: ASDisplayNode {
|
||||
fileId: fileId,
|
||||
animationCache: animationCache,
|
||||
animationRenderer: animationRenderer,
|
||||
tintColor: nil,
|
||||
placeholderColor: placeholderColor,
|
||||
animateIdle: animateIdle,
|
||||
reaction: value,
|
||||
|
@ -188,7 +188,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//TODO:localize
|
||||
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
|
||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
@ -220,17 +221,22 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
case let .giftPremium(_, _, monthsValue, _, _):
|
||||
months = monthsValue
|
||||
text = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(months)).string
|
||||
case let .giftCode(_, fromGiveaway, channelId, monthsValue):
|
||||
case let .giftCode(_, fromGiveaway, unclaimed, channelId, monthsValue):
|
||||
giftSize.width += 34.0
|
||||
giftSize.height += 84.0
|
||||
textSpacing += 13.0
|
||||
|
||||
title = "Congratulations!"
|
||||
if unclaimed {
|
||||
title = "Unclaimed Prize"
|
||||
} else {
|
||||
title = "Congratulations!"
|
||||
}
|
||||
var peerName = ""
|
||||
if let channelId, let channel = item.message.peers[channelId] {
|
||||
peerName = EnginePeer(channel).compactDisplayTitle
|
||||
}
|
||||
if fromGiveaway {
|
||||
if unclaimed {
|
||||
text = "You have an unclaimed prize from a giveaway by **\(peerName)**.\n\nThis prize is a **Telegram Premium** subscription for **\(monthsValue)** months."
|
||||
} else if fromGiveaway {
|
||||
text = "You won a prize in a giveaway organized by **\(peerName)**.\n\nYour prize is a **Telegram Premium** subscription for **\(monthsValue)** months."
|
||||
} else {
|
||||
text = "You've received a gift from **\(peerName)**.\n\nYour gift is a **Telegram Premium** subscription for **\(monthsValue)** months."
|
||||
@ -273,6 +279,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
|
||||
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: buttonTitle, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
giftSize.height = titleLayout.size.height + subtitleLayout.size.height + 225.0
|
||||
|
||||
var labelRects = labelLayout.linesRects()
|
||||
if labelRects.count > 1 {
|
||||
let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width })
|
||||
|
@ -449,6 +449,7 @@ public final class MessageInputActionButtonComponent: Component {
|
||||
fileId: animationFileId ?? reactionFile?.fileId.id ?? 0,
|
||||
animationCache: component.context.animationCache,
|
||||
animationRenderer: component.context.animationRenderer,
|
||||
tintColor: nil,
|
||||
placeholderColor: UIColor(white: 1.0, alpha: 0.2),
|
||||
animateIdle: false,
|
||||
reaction: reaction,
|
||||
|
@ -168,9 +168,9 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
return { item, params, neighbors in
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
||||
}
|
||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
||||
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
@ -250,6 +250,11 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
|
||||
if let currentBackgroundNode {
|
||||
currentBackgroundNode.update(wallpaper: item.wallpaper)
|
||||
currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||
}
|
||||
|
||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||
|
||||
if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor || currentItem.messageItems.first?.backgroundEmojiId != item.messageItems.first?.backgroundEmojiId {
|
||||
|
@ -412,7 +412,8 @@ public func PeerNameColorScreen(
|
||||
updatedState.inProgress = true
|
||||
return updatedState
|
||||
}
|
||||
let _ = context.engine.peers.updatePeerNameColorAndEmoji(peerId: peerId, nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0).startStandalone(next: {
|
||||
let _ = (context.engine.peers.updatePeerNameColorAndEmoji(peerId: peerId, nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0)
|
||||
|> deliverOnMainQueue).startStandalone(next: {
|
||||
}, error: { error in
|
||||
if case .channelBoostRequired = error {
|
||||
let _ = combineLatest(
|
||||
|
@ -25,6 +25,7 @@ import TelegramUIPreferences
|
||||
import UndoUI
|
||||
import TelegramStringFormatting
|
||||
|
||||
//TODO:localize
|
||||
final class ShareWithPeersScreenComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "brush_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
193
submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/brush_30.pdf
vendored
Normal file
193
submodules/TelegramUI/Images.xcassets/Chat/Info/NameColorIcon.imageset/brush_30.pdf
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< /Type /XObject
|
||||
/Length 2 0 R
|
||||
/Group << /Type /Group
|
||||
/S /Transparency
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Resources << >>
|
||||
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
>>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
1.000000 0.584314 0.000000 scn
|
||||
0.000000 18.799999 m
|
||||
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||
18.799999 30.000000 l
|
||||
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||
30.000000 11.200001 l
|
||||
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||
11.200000 0.000000 l
|
||||
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||
0.000000 18.799999 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 5.000000 3.864258 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
17.269485 19.786890 m
|
||||
18.869095 20.947046 20.890579 18.978296 19.730423 17.325953 c
|
||||
19.405228 16.877710 16.109329 12.034937 13.402297 8.967554 c
|
||||
12.690383 11.103296 11.038039 12.755640 8.911086 13.458765 c
|
||||
11.978469 16.165796 16.821243 19.461695 17.269485 19.786890 c
|
||||
h
|
||||
8.867141 8.932398 m
|
||||
8.164016 9.635523 7.267532 10.136499 6.476516 10.285913 c
|
||||
6.590774 10.927515 6.889602 11.481226 7.408157 12.034937 c
|
||||
7.496047 12.131617 7.583938 12.219507 7.680618 12.307398 c
|
||||
9.965775 11.982203 11.908156 10.022242 12.242141 7.737085 c
|
||||
12.154250 7.640406 12.066360 7.552515 11.960891 7.464624 c
|
||||
11.415969 6.954859 10.879836 6.664820 10.229445 6.541773 c
|
||||
10.071242 7.332788 9.570266 8.229273 8.867141 8.932398 c
|
||||
h
|
||||
0.385696 3.527124 m
|
||||
-0.062546 4.072046 -0.141648 4.564234 0.262649 4.889429 c
|
||||
0.746047 5.267359 1.378860 4.915796 2.081985 5.460718 c
|
||||
2.864211 6.058374 2.310500 7.394312 3.690383 8.466578 c
|
||||
5.043899 9.565210 7.074172 9.204859 8.287063 7.666773 c
|
||||
9.623000 5.961695 9.368118 3.869898 7.592727 2.481226 c
|
||||
5.465774 0.811304 2.064407 1.400171 0.385696 3.527124 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
2104
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<< /Type /XObject
|
||||
/Length 4 0 R
|
||||
/Group << /Type /Group
|
||||
/S /Transparency
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Resources << >>
|
||||
/BBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
>>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.000000 18.799999 m
|
||||
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||
18.799999 30.000000 l
|
||||
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||
30.000000 11.200001 l
|
||||
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||
11.200000 0.000000 l
|
||||
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||
0.000000 18.799999 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
944
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /XObject << /X1 1 0 R >>
|
||||
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||
/G 3 0 R
|
||||
/S /Alpha
|
||||
>>
|
||||
/Type /ExtGState
|
||||
>> >>
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Length 7 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
/E1 gs
|
||||
/X1 Do
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
7 0 obj
|
||||
46
|
||||
endobj
|
||||
|
||||
8 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 5 0 R
|
||||
/Contents 6 0 R
|
||||
/Parent 9 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
<< /Kids [ 8 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
10 0 obj
|
||||
<< /Pages 9 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 11
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000002362 00000 n
|
||||
0000002385 00000 n
|
||||
0000003577 00000 n
|
||||
0000003599 00000 n
|
||||
0000003897 00000 n
|
||||
0000003999 00000 n
|
||||
0000004020 00000 n
|
||||
0000004193 00000 n
|
||||
0000004267 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 10 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
4327
|
||||
%%EOF
|
@ -948,7 +948,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let controller = PremiumIntroScreen(context: strongSelf.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration))
|
||||
strongSelf.push(controller)
|
||||
return true
|
||||
case let .giftCode(slug, _, _, _):
|
||||
case let .giftCode(slug, _, _, _, _):
|
||||
strongSelf.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id)
|
||||
return true
|
||||
case let .suggestedProfilePhoto(image):
|
||||
|
@ -842,101 +842,49 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
}
|
||||
})
|
||||
case let .boost(peerId, status, myBoostStatus):
|
||||
let _ = myBoostStatus
|
||||
var isCurrent = false
|
||||
if case let .chat(chatPeerId, _) = urlContext, chatPeerId == peerId {
|
||||
isCurrent = true
|
||||
}
|
||||
var forceDark = false
|
||||
if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance {
|
||||
forceDark = true
|
||||
}
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).startStandalone(next: { peer in
|
||||
guard let peer, let status else {
|
||||
return
|
||||
|
||||
var dismissedImpl: (() -> Void)?
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
let updateExternalController = storyProgressPauseContext.update
|
||||
dismissedImpl = {
|
||||
updateExternalController(nil)
|
||||
}
|
||||
|
||||
var myBoostCount: Int32 = 0
|
||||
var availableBoosts: [MyBoostStatus.Boost] = []
|
||||
var occupiedBoosts: [MyBoostStatus.Boost] = []
|
||||
if let myBoostStatus {
|
||||
for boost in myBoostStatus.boosts {
|
||||
if let boostPeer = boost.peer {
|
||||
if boostPeer.id == peer.id {
|
||||
myBoostCount += 1
|
||||
} else {
|
||||
occupiedBoosts.append(boost)
|
||||
}
|
||||
} else {
|
||||
availableBoosts.append(boost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isCurrent = false
|
||||
if case let .chat(chatPeerId, _) = urlContext, chatPeerId == peerId {
|
||||
isCurrent = true
|
||||
}
|
||||
|
||||
var currentLevel = Int32(status.level)
|
||||
var currentLevelBoosts = Int32(status.currentLevelBoosts)
|
||||
var nextLevelBoosts = status.nextLevelBoosts.flatMap(Int32.init)
|
||||
|
||||
if myBoostCount > 0 && status.boosts == currentLevelBoosts {
|
||||
currentLevel = max(0, currentLevel - 1)
|
||||
nextLevelBoosts = currentLevelBoosts
|
||||
currentLevelBoosts = max(0, currentLevelBoosts - 1)
|
||||
}
|
||||
|
||||
let subject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount)
|
||||
let nextSubject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount + 1)
|
||||
var nextCount = Int32(status.boosts + 1)
|
||||
|
||||
var updateImpl: (() -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: subject, count: Int32(status.boosts), forceDark: forceDark, action: {
|
||||
let dismiss = false
|
||||
updateImpl?()
|
||||
return dismiss
|
||||
},
|
||||
}
|
||||
|
||||
PremiumBoostScreen(
|
||||
context: context,
|
||||
contentContext: contentContext,
|
||||
peerId: peerId,
|
||||
isCurrent: isCurrent,
|
||||
status: status,
|
||||
myBoostStatus: myBoostStatus,
|
||||
forceDark: forceDark,
|
||||
openPeer: { peer in
|
||||
openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||
})
|
||||
navigationController?.pushViewController(controller)
|
||||
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
storyProgressPauseContext.update(controller)
|
||||
},
|
||||
presentController: { [weak navigationController] c in
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root))
|
||||
},
|
||||
pushController: { [weak navigationController] c in
|
||||
navigationController?.pushViewController(c)
|
||||
|
||||
let updateExternalController = storyProgressPauseContext.update
|
||||
controller.disposed = {
|
||||
updateExternalController(nil)
|
||||
}
|
||||
}
|
||||
|
||||
updateImpl = { [weak controller] in
|
||||
if let _ = status.nextLevelBoosts {
|
||||
if let availableBoost = availableBoosts.first {
|
||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot]).startStandalone()
|
||||
controller?.updateSubject(nextSubject, count: nextCount)
|
||||
|
||||
availableBoosts.removeFirst()
|
||||
nextCount += 1
|
||||
} else if !occupiedBoosts.isEmpty, let myBoostStatus {
|
||||
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
|
||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone()
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "\(slots.count) boosts are reassigned from 1 other channel.", timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, action: { _ in return true })
|
||||
(navigationController?.viewControllers.last as? ViewController)?.present(undoController, in: .window(.root))
|
||||
})
|
||||
dismissImpl?()
|
||||
navigationController?.pushViewController(replaceController)
|
||||
if c is PremiumLimitScreen {
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
storyProgressPauseContext.update(c)
|
||||
}
|
||||
} else {
|
||||
dismissImpl?()
|
||||
}
|
||||
}, dismissed: {
|
||||
dismissedImpl?()
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
})
|
||||
)
|
||||
case let .premiumGiftCode(slug):
|
||||
var forceDark = false
|
||||
if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance {
|
||||
|
@ -156,7 +156,11 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode {
|
||||
self.textNode.attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: textColorValue)
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
let labelSize = self.labelNode.updateLayout(CGSize(width: width - textSize.width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
||||
var labelConstrainWidth = width - textSize.width - (leftInset + rightInset)
|
||||
if case .semitransparentBadge = item.label {
|
||||
labelConstrainWidth -= 16.0
|
||||
}
|
||||
let labelSize = self.labelNode.updateLayout(CGSize(width: labelConstrainWidth, height: .greatestFiniteMagnitude))
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: leftInset, y: 12.0), size: textSize)
|
||||
|
||||
|
@ -1568,12 +1568,6 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
|
||||
}))
|
||||
}
|
||||
|
||||
if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) {
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemNameColor, label: .semitransparentBadge(EnginePeer(channel).compactDisplayTitle, (data.peer?.nameColor ?? .blue).color), text: "Channel Color", icon: UIImage(bundleImageName: "Settings/Menu/Appearance"), action: {
|
||||
interaction.editingOpenNameColorSetup()
|
||||
}))
|
||||
}
|
||||
|
||||
if (isCreator && (channel.addressName?.isEmpty ?? true)) || (!channel.flags.contains(.isCreator) && channel.adminRights?.rights.contains(.canInviteUsers) == true) {
|
||||
let invitesText: String
|
||||
if let count = data.invitations?.count, count > 0 {
|
||||
@ -1626,6 +1620,12 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
|
||||
}))
|
||||
}
|
||||
|
||||
if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) {
|
||||
items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemNameColor, label: .semitransparentBadge(EnginePeer(channel).compactDisplayTitle, (data.peer?.nameColor ?? .blue).color), text: "Channel Color", icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: {
|
||||
interaction.editingOpenNameColorSetup()
|
||||
}))
|
||||
}
|
||||
|
||||
if isCreator || (channel.adminRights != nil && channel.hasPermission(.sendSomething)) {
|
||||
let messagesShouldHaveSignatures: Bool
|
||||
switch channel.info {
|
||||
|
Loading…
x
Reference in New Issue
Block a user