mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-07 14:53:35 +00:00
Merge commit '09d2d0d716c96f924e8595533681169a06e063bd'
This commit is contained in:
commit
1f814962ad
@ -23,6 +23,7 @@ public final class ReactionIconView: PortalSourceView {
|
|||||||
private var file: TelegramMediaFile?
|
private var file: TelegramMediaFile?
|
||||||
private var animationCache: AnimationCache?
|
private var animationCache: AnimationCache?
|
||||||
private var animationRenderer: MultiAnimationRenderer?
|
private var animationRenderer: MultiAnimationRenderer?
|
||||||
|
private var contentTintColor: UIColor?
|
||||||
private var placeholderColor: UIColor?
|
private var placeholderColor: UIColor?
|
||||||
private var size: CGSize?
|
private var size: CGSize?
|
||||||
private var animateIdle: Bool?
|
private var animateIdle: Bool?
|
||||||
@ -58,6 +59,7 @@ public final class ReactionIconView: PortalSourceView {
|
|||||||
fileId: Int64,
|
fileId: Int64,
|
||||||
animationCache: AnimationCache,
|
animationCache: AnimationCache,
|
||||||
animationRenderer: MultiAnimationRenderer,
|
animationRenderer: MultiAnimationRenderer,
|
||||||
|
tintColor: UIColor?,
|
||||||
placeholderColor: UIColor,
|
placeholderColor: UIColor,
|
||||||
animateIdle: Bool,
|
animateIdle: Bool,
|
||||||
reaction: MessageReaction.Reaction,
|
reaction: MessageReaction.Reaction,
|
||||||
@ -66,6 +68,7 @@ public final class ReactionIconView: PortalSourceView {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.animationCache = animationCache
|
self.animationCache = animationCache
|
||||||
self.animationRenderer = animationRenderer
|
self.animationRenderer = animationRenderer
|
||||||
|
self.contentTintColor = tintColor
|
||||||
self.placeholderColor = placeholderColor
|
self.placeholderColor = placeholderColor
|
||||||
self.size = size
|
self.size = size
|
||||||
self.animateIdle = animateIdle
|
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))
|
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) {
|
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.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
|
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() {
|
func reset() {
|
||||||
@ -769,6 +804,17 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView {
|
|||||||
animateIdle = false
|
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(
|
iconView.update(
|
||||||
size: layout.imageFrame.size,
|
size: layout.imageFrame.size,
|
||||||
context: layout.spec.component.context,
|
context: layout.spec.component.context,
|
||||||
@ -776,6 +822,7 @@ public final class ReactionButtonAsyncNode: ContextControllerSourceView {
|
|||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
animationCache: arguments.animationCache,
|
animationCache: arguments.animationCache,
|
||||||
animationRenderer: arguments.animationRenderer,
|
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),
|
placeholderColor: layout.spec.component.chosenOrder != nil ? UIColor(argb: layout.spec.component.colors.selectedMediaPlaceholder) : UIColor(argb: layout.spec.component.colors.deselectedMediaPlaceholder),
|
||||||
animateIdle: animateIdle,
|
animateIdle: animateIdle,
|
||||||
reaction: layout.spec.component.reaction.value,
|
reaction: layout.spec.component.reaction.value,
|
||||||
|
|||||||
@ -218,6 +218,37 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
}
|
}
|
||||||
reactionLayer.frame = iconFrame
|
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 {
|
func update(presentationData: PresentationData, constrainedSize: CGSize, isSelected: Bool) -> CGSize {
|
||||||
@ -227,6 +258,8 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
if let iconNode = self.iconNode {
|
if let iconNode = self.iconNode {
|
||||||
iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: presentationData.theme.contextMenu.primaryColor)
|
iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reactions"), color: presentationData.theme.contextMenu.primaryColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updateReactionLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
let sideInset: CGFloat = 12.0
|
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) {
|
func update(size: CGSize, presentationData: PresentationData, item: EngineMessageReactionListContext.Item, isLast: Bool, syncronousLoad: Bool) {
|
||||||
let avatarInset: CGFloat = 12.0
|
let avatarInset: CGFloat = 12.0
|
||||||
let avatarSpacing: CGFloat = 8.0
|
let avatarSpacing: CGFloat = 8.0
|
||||||
@ -495,6 +557,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
if availableReaction.value == reaction {
|
if availableReaction.value == reaction {
|
||||||
self.file = availableReaction.centerAnimation
|
self.file = availableReaction.centerAnimation
|
||||||
self.updateReactionLayer()
|
self.updateReactionLayer()
|
||||||
|
self.updateReactionAccentColor(theme: presentationData.theme)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -507,6 +570,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
}
|
}
|
||||||
strongSelf.file = file
|
strongSelf.file = file
|
||||||
strongSelf.updateReactionLayer()
|
strongSelf.updateReactionLayer()
|
||||||
|
strongSelf.updateReactionAccentColor(theme: presentationData.theme)
|
||||||
}).strict()
|
}).strict()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -520,6 +584,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.updateReactionAccentColor(theme: presentationData.theme)
|
||||||
|
|
||||||
if self.item != item {
|
if self.item != item {
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|||||||
@ -39,7 +39,6 @@ public final class SheetComponentEnvironment: Equatable {
|
|||||||
|
|
||||||
public let sheetComponentTag = GenericComponentViewTag()
|
public let sheetComponentTag = GenericComponentViewTag()
|
||||||
public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||||
|
|
||||||
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
|
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
|
||||||
|
|
||||||
public enum BackgroundColor: Equatable {
|
public enum BackgroundColor: Equatable {
|
||||||
@ -54,15 +53,18 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
|||||||
|
|
||||||
public let content: AnyComponent<ChildEnvironmentType>
|
public let content: AnyComponent<ChildEnvironmentType>
|
||||||
public let backgroundColor: BackgroundColor
|
public let backgroundColor: BackgroundColor
|
||||||
|
public let followContentSizeChanges: Bool
|
||||||
public let animateOut: ActionSlot<Action<()>>
|
public let animateOut: ActionSlot<Action<()>>
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
content: AnyComponent<ChildEnvironmentType>,
|
content: AnyComponent<ChildEnvironmentType>,
|
||||||
backgroundColor: BackgroundColor,
|
backgroundColor: BackgroundColor,
|
||||||
|
followContentSizeChanges: Bool = false,
|
||||||
animateOut: ActionSlot<Action<()>>
|
animateOut: ActionSlot<Action<()>>
|
||||||
) {
|
) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
|
self.followContentSizeChanges = followContentSizeChanges
|
||||||
self.animateOut = animateOut
|
self.animateOut = animateOut
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +75,9 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
|||||||
if lhs.backgroundColor != rhs.backgroundColor {
|
if lhs.backgroundColor != rhs.backgroundColor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.followContentSizeChanges != rhs.followContentSizeChanges {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.animateOut != rhs.animateOut {
|
if lhs.animateOut != rhs.animateOut {
|
||||||
return false
|
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)
|
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
|
||||||
|
|
||||||
|
let previousContentSize = self.scrollView.contentSize
|
||||||
self.scrollView.contentSize = 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.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
|
self.ignoreScrolling = false
|
||||||
|
|
||||||
if let currentAvailableSize = self.currentAvailableSize, currentAvailableSize.height != availableSize.height {
|
if let currentAvailableSize = self.currentAvailableSize, currentAvailableSize.height != availableSize.height {
|
||||||
self.scrollView.contentOffset = CGPoint(x: 0.0, y: -(availableSize.height - contentSize.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 {
|
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))
|
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
|
self.view.disablesInteractiveModalDismiss = true
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func replace(with controller: ViewController) {
|
|
||||||
self.dismissAnimated()
|
|
||||||
super.replace(with: controller)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func dismissAnimated() {
|
public func dismissAnimated() {
|
||||||
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
if let view = self.node.hostView.findTaggedView(tag: SheetComponent<ViewControllerComponentContainer.Environment>.View.Tag()) as? SheetComponent<ViewControllerComponentContainer.Environment>.View {
|
||||||
view.dismissAnimated()
|
view.dismissAnimated()
|
||||||
|
|||||||
@ -162,6 +162,9 @@ public class PremiumLimitDisplayComponent: Component {
|
|||||||
private let badgeLabel: BadgeLabelView
|
private let badgeLabel: BadgeLabelView
|
||||||
private let badgeLabelMaskView = UIImageView()
|
private let badgeLabelMaskView = UIImageView()
|
||||||
|
|
||||||
|
private var badgeTailPosition: CGFloat = 0.0
|
||||||
|
private var badgeShapeArguments: (Double, Double, CGSize, CGFloat, CGFloat)?
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
@ -232,6 +235,10 @@ public class PremiumLimitDisplayComponent: Component {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.badgeShapeAnimator?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
private var didPlayAppearanceAnimation = false
|
private var didPlayAppearanceAnimation = false
|
||||||
func playAppearanceAnimation(component: PremiumLimitDisplayComponent, badgeFullSize: CGSize, from: CGFloat? = nil) {
|
func playAppearanceAnimation(component: PremiumLimitDisplayComponent, badgeFullSize: CGSize, from: CGFloat? = nil) {
|
||||||
if from == nil {
|
if from == nil {
|
||||||
@ -534,16 +541,29 @@ public class PremiumLimitDisplayComponent: Component {
|
|||||||
|
|
||||||
if badgePosition > 1.0 - 0.15 {
|
if badgePosition > 1.0 - 0.15 {
|
||||||
progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 1.0, y: 1.0))
|
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") {
|
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
self.badgeView.center = CGPoint(x: 3.0 + (size.width - 6.0) * badgePosition + 3.0, y: 82.0)
|
self.badgeView.center = CGPoint(x: 3.0 + (size.width - 6.0) * badgePosition + 3.0, y: 82.0)
|
||||||
}
|
}
|
||||||
} else if badgePosition < 0.15 {
|
} else if badgePosition < 0.15 {
|
||||||
progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 0.0, y: 1.0))
|
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") {
|
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||||
|
|
||||||
@ -552,7 +572,14 @@ public class PremiumLimitDisplayComponent: Component {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
progressTransition.setAnchorPoint(layer: self.badgeView.layer, anchorPoint: CGPoint(x: 0.5, y: 1.0))
|
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") {
|
if let _ = self.badgeView.layer.animation(forKey: "appearance1") {
|
||||||
|
|
||||||
@ -617,6 +644,36 @@ public class PremiumLimitDisplayComponent: Component {
|
|||||||
return size
|
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() {
|
private func setupGradientAnimations() {
|
||||||
guard let _ = self.component else {
|
guard let _ = self.component else {
|
||||||
return
|
return
|
||||||
@ -1143,12 +1200,14 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
string = strings.ChannelBoost_HelpUpgradeChannelText(peer.compactDisplayTitle, boostsString, storiesString).string
|
string = strings.ChannelBoost_HelpUpgradeChannelText(peer.compactDisplayTitle, boostsString, storiesString).string
|
||||||
}
|
}
|
||||||
actionButtonText = strings.ChannelBoost_BoostChannel
|
actionButtonText = strings.ChannelBoost_BoostChannel
|
||||||
|
buttonIconName = "Premium/BoostChannel"
|
||||||
} else {
|
} else {
|
||||||
titleText = strings.ChannelBoost_MaxLevelReached
|
titleText = strings.ChannelBoost_MaxLevelReached
|
||||||
string = strings.ChannelBoost_BoostedChannelReachedLevel("\(level)", storiesString).string
|
string = strings.ChannelBoost_BoostedChannelReachedLevel("\(level)", storiesString).string
|
||||||
actionButtonText = strings.Common_OK
|
actionButtonText = strings.Common_OK
|
||||||
|
buttonIconName = nil
|
||||||
|
actionButtonHasGloss = false
|
||||||
}
|
}
|
||||||
buttonIconName = "Premium/BoostChannel"
|
|
||||||
}
|
}
|
||||||
buttonAnimationName = nil
|
buttonAnimationName = nil
|
||||||
defaultTitle = strings.ChannelBoost_Level("\(level)").string
|
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 prefixString = isCurrent ? strings.ChannelBoost_YouBoostedChannelText(peer.compactDisplayTitle).string : strings.ChannelBoost_YouBoostedOtherChannelText
|
||||||
|
|
||||||
let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1)
|
let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1)
|
||||||
|
if let _ = remaining {
|
||||||
// buttonIconName = nil
|
|
||||||
// actionButtonText = environment.strings.Common_OK
|
|
||||||
actionButtonText = "Boost Again"
|
actionButtonText = "Boost Again"
|
||||||
|
} else {
|
||||||
|
buttonIconName = nil
|
||||||
|
actionButtonText = environment.strings.Common_OK
|
||||||
|
actionButtonHasGloss = false
|
||||||
|
}
|
||||||
if let remaining {
|
if let remaining {
|
||||||
let boostsString = strings.ChannelBoost_MoreBoosts(remaining)
|
let boostsString = strings.ChannelBoost_MoreBoosts(remaining)
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
@ -1209,7 +1270,6 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
reachedMaximumLimit = false
|
reachedMaximumLimit = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let contentSize: CGSize
|
let contentSize: CGSize
|
||||||
if state.initialized {
|
if state.initialized {
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
@ -1391,6 +1451,7 @@ private final class LimitSheetContent: CombinedComponent {
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
} else if let alternateTextChild {
|
} else if let alternateTextChild {
|
||||||
|
textOffset += 9.0
|
||||||
textSize = alternateTextChild.size
|
textSize = alternateTextChild.size
|
||||||
context.add(alternateTextChild
|
context.add(alternateTextChild
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: textOffset))
|
.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))
|
.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)
|
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
|
openGift: context.component.openGift
|
||||||
)),
|
)),
|
||||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||||
|
followContentSizeChanges: true,
|
||||||
animateOut: animateOut
|
animateOut: animateOut
|
||||||
),
|
),
|
||||||
environment: {
|
environment: {
|
||||||
|
|||||||
@ -19,6 +19,8 @@ import PeerListItemComponent
|
|||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
|
||||||
private final class ReplaceBoostScreenComponent: CombinedComponent {
|
private final class ReplaceBoostScreenComponent: CombinedComponent {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
|
|
||||||
@ -799,6 +801,7 @@ public class ReplaceBoostScreen: ViewController {
|
|||||||
self?.node.selectedSlots = selectedSlots
|
self?.node.selectedSlots = selectedSlots
|
||||||
}
|
}
|
||||||
presentControllerImpl = { [weak self] c in
|
presentControllerImpl = { [weak self] c in
|
||||||
|
self?.dismissAllTooltips()
|
||||||
self?.present(c, in: .window(.root))
|
self?.present(c, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -819,15 +822,29 @@ public class ReplaceBoostScreen: ViewController {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func cancelPressed() {
|
|
||||||
self.dismiss(animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
override open func loadDisplayNode() {
|
override open func loadDisplayNode() {
|
||||||
self.displayNode = Node(context: self.context, controller: self, component: self.component)
|
self.displayNode = Node(context: self.context, controller: self, component: self.component)
|
||||||
self.displayNodeDidLoad()
|
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) {
|
public override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
self.view.endEditing(true)
|
self.view.endEditing(true)
|
||||||
|
|
||||||
|
|||||||
@ -133,9 +133,9 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
|||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
}
|
|
||||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||||
|
}
|
||||||
|
|
||||||
let insets: UIEdgeInsets
|
let insets: UIEdgeInsets
|
||||||
let separatorHeight = UIScreenPixel
|
let separatorHeight = UIScreenPixel
|
||||||
@ -189,6 +189,11 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
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)
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||||
|
|
||||||
var topOffset: CGFloat = 8.0
|
var topOffset: CGFloat = 8.0
|
||||||
|
|||||||
@ -261,9 +261,9 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
|||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
}
|
|
||||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||||
|
}
|
||||||
|
|
||||||
let insets: UIEdgeInsets
|
let insets: UIEdgeInsets
|
||||||
let separatorHeight = UIScreenPixel
|
let separatorHeight = UIScreenPixel
|
||||||
@ -330,6 +330,11 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
strongSelf.item = item
|
strongSelf.item = item
|
||||||
|
|
||||||
|
if let currentBackgroundNode {
|
||||||
|
currentBackgroundNode.update(wallpaper: item.wallpaper)
|
||||||
|
currentBackgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners)
|
||||||
|
}
|
||||||
|
|
||||||
if strongSelf.genericReactionEffectDisposable == nil {
|
if strongSelf.genericReactionEffectDisposable == nil {
|
||||||
strongSelf.loadNextGenericReactionEffect(context: item.context)
|
strongSelf.loadNextGenericReactionEffect(context: item.context)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,7 +128,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
case let .messageActionSetSameChatWallPaper(wallpaper):
|
case let .messageActionSetSameChatWallPaper(wallpaper):
|
||||||
return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper)))
|
return TelegramMediaAction(action: .setSameChatWallpaper(wallpaper: TelegramWallpaper(apiWallpaper: wallpaper)))
|
||||||
case let .messageActionGiftCode(flags, boostPeer, months, slug):
|
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:
|
case .messageActionGiveawayLaunch:
|
||||||
return TelegramMediaAction(action: .giveawayLaunched)
|
return TelegramMediaAction(action: .giveawayLaunched)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,7 +109,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case requestedPeer(buttonId: Int32, peerId: PeerId)
|
case requestedPeer(buttonId: Int32, peerId: PeerId)
|
||||||
case setChatWallpaper(wallpaper: TelegramWallpaper)
|
case setChatWallpaper(wallpaper: TelegramWallpaper)
|
||||||
case setSameChatWallpaper(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
|
case giveawayLaunched
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -206,7 +206,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case 35:
|
case 35:
|
||||||
self = .botAppAccessGranted(appName: decoder.decodeOptionalStringForKey("app"), type: decoder.decodeOptionalInt32ForKey("atp").flatMap { BotSendMessageAccessGrantedType(rawValue: $0) })
|
self = .botAppAccessGranted(appName: decoder.decodeOptionalStringForKey("app"), type: decoder.decodeOptionalInt32ForKey("atp").flatMap { BotSendMessageAccessGrantedType(rawValue: $0) })
|
||||||
case 36:
|
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:
|
case 37:
|
||||||
self = .giveawayLaunched
|
self = .giveawayLaunched
|
||||||
default:
|
default:
|
||||||
@ -388,10 +388,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
encoder.encodeNil(forKey: "atp")
|
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.encodeInt32(36, forKey: "_rawValue")
|
||||||
encoder.encodeString(slug, forKey: "slug")
|
encoder.encodeString(slug, forKey: "slug")
|
||||||
encoder.encodeBool(fromGiveaway, forKey: "give")
|
encoder.encodeBool(fromGiveaway, forKey: "give")
|
||||||
|
encoder.encodeBool(unclaimed, forKey: "unclaimed")
|
||||||
if let boostPeerId = boostPeerId {
|
if let boostPeerId = boostPeerId {
|
||||||
encoder.encodeInt64(boostPeerId.toInt64(), forKey: "pi")
|
encoder.encodeInt64(boostPeerId.toInt64(), forKey: "pi")
|
||||||
} else {
|
} else {
|
||||||
@ -421,7 +422,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
return peerIds
|
return peerIds
|
||||||
case let .requestedPeer(_, peerId):
|
case let .requestedPeer(_, peerId):
|
||||||
return [peerId]
|
return [peerId]
|
||||||
case let .giftCode(_, _, boostPeerId, _):
|
case let .giftCode(_, _, _, boostPeerId, _):
|
||||||
return boostPeerId.flatMap { [$0] } ?? []
|
return boostPeerId.flatMap { [$0] } ?? []
|
||||||
default:
|
default:
|
||||||
return []
|
return []
|
||||||
|
|||||||
@ -166,6 +166,7 @@ private final class StatusReactionNode: ASDisplayNode {
|
|||||||
fileId: fileId,
|
fileId: fileId,
|
||||||
animationCache: animationCache,
|
animationCache: animationCache,
|
||||||
animationRenderer: animationRenderer,
|
animationRenderer: animationRenderer,
|
||||||
|
tintColor: nil,
|
||||||
placeholderColor: placeholderColor,
|
placeholderColor: placeholderColor,
|
||||||
animateIdle: animateIdle,
|
animateIdle: animateIdle,
|
||||||
reaction: value,
|
reaction: value,
|
||||||
|
|||||||
@ -189,6 +189,7 @@ 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))) {
|
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 makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
@ -220,17 +221,22 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
case let .giftPremium(_, _, monthsValue, _, _):
|
case let .giftPremium(_, _, monthsValue, _, _):
|
||||||
months = monthsValue
|
months = monthsValue
|
||||||
text = item.presentationData.strings.Notification_PremiumGift_Subtitle(item.presentationData.strings.Notification_PremiumGift_Months(months)).string
|
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.width += 34.0
|
||||||
giftSize.height += 84.0
|
|
||||||
textSpacing += 13.0
|
textSpacing += 13.0
|
||||||
|
|
||||||
|
if unclaimed {
|
||||||
|
title = "Unclaimed Prize"
|
||||||
|
} else {
|
||||||
title = "Congratulations!"
|
title = "Congratulations!"
|
||||||
|
}
|
||||||
var peerName = ""
|
var peerName = ""
|
||||||
if let channelId, let channel = item.message.peers[channelId] {
|
if let channelId, let channel = item.message.peers[channelId] {
|
||||||
peerName = EnginePeer(channel).compactDisplayTitle
|
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."
|
text = "You won a prize in a giveaway organized by **\(peerName)**.\n\nYour prize is a **Telegram Premium** subscription for **\(monthsValue)** months."
|
||||||
} else {
|
} else {
|
||||||
text = "You've received a gift from **\(peerName)**.\n\nYour gift is a **Telegram Premium** subscription for **\(monthsValue)** months."
|
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()))
|
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()
|
var labelRects = labelLayout.linesRects()
|
||||||
if labelRects.count > 1 {
|
if labelRects.count > 1 {
|
||||||
let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width })
|
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,
|
fileId: animationFileId ?? reactionFile?.fileId.id ?? 0,
|
||||||
animationCache: component.context.animationCache,
|
animationCache: component.context.animationCache,
|
||||||
animationRenderer: component.context.animationRenderer,
|
animationRenderer: component.context.animationRenderer,
|
||||||
|
tintColor: nil,
|
||||||
placeholderColor: UIColor(white: 1.0, alpha: 0.2),
|
placeholderColor: UIColor(white: 1.0, alpha: 0.2),
|
||||||
animateIdle: false,
|
animateIdle: false,
|
||||||
reaction: reaction,
|
reaction: reaction,
|
||||||
|
|||||||
@ -168,9 +168,9 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
}
|
|
||||||
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
currentBackgroundNode?.update(wallpaper: item.wallpaper)
|
||||||
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
currentBackgroundNode?.updateBubbleTheme(bubbleTheme: item.componentTheme, bubbleCorners: item.chatBubbleCorners)
|
||||||
|
}
|
||||||
|
|
||||||
let insets: UIEdgeInsets
|
let insets: UIEdgeInsets
|
||||||
let separatorHeight = UIScreenPixel
|
let separatorHeight = UIScreenPixel
|
||||||
@ -250,6 +250,11 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.item = item
|
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)
|
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 {
|
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
|
updatedState.inProgress = true
|
||||||
return updatedState
|
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
|
}, error: { error in
|
||||||
if case .channelBoostRequired = error {
|
if case .channelBoostRequired = error {
|
||||||
let _ = combineLatest(
|
let _ = combineLatest(
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import TelegramUIPreferences
|
|||||||
import UndoUI
|
import UndoUI
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
final class ShareWithPeersScreenComponent: Component {
|
final class ShareWithPeersScreenComponent: Component {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
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))
|
let controller = PremiumIntroScreen(context: strongSelf.context, source: .gift(from: fromPeerId, to: toPeerId, duration: duration))
|
||||||
strongSelf.push(controller)
|
strongSelf.push(controller)
|
||||||
return true
|
return true
|
||||||
case let .giftCode(slug, _, _, _):
|
case let .giftCode(slug, _, _, _, _):
|
||||||
strongSelf.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id)
|
strongSelf.openResolved(result: .premiumGiftCode(slug: slug), sourceMessageId: message.id)
|
||||||
return true
|
return true
|
||||||
case let .suggestedProfilePhoto(image):
|
case let .suggestedProfilePhoto(image):
|
||||||
|
|||||||
@ -842,101 +842,49 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .boost(peerId, status, myBoostStatus):
|
case let .boost(peerId, status, myBoostStatus):
|
||||||
let _ = myBoostStatus
|
|
||||||
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 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
|
var isCurrent = false
|
||||||
if case let .chat(chatPeerId, _) = urlContext, chatPeerId == peerId {
|
if case let .chat(chatPeerId, _) = urlContext, chatPeerId == peerId {
|
||||||
isCurrent = true
|
isCurrent = true
|
||||||
}
|
}
|
||||||
|
var forceDark = false
|
||||||
var currentLevel = Int32(status.level)
|
if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance {
|
||||||
var currentLevelBoosts = Int32(status.currentLevelBoosts)
|
forceDark = true
|
||||||
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)
|
var dismissedImpl: (() -> Void)?
|
||||||
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, .chat(textInputState: nil, subject: nil, peekData: nil))
|
|
||||||
})
|
|
||||||
navigationController?.pushViewController(controller)
|
|
||||||
|
|
||||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||||
storyProgressPauseContext.update(controller)
|
|
||||||
|
|
||||||
let updateExternalController = storyProgressPauseContext.update
|
let updateExternalController = storyProgressPauseContext.update
|
||||||
controller.disposed = {
|
dismissedImpl = {
|
||||||
updateExternalController(nil)
|
updateExternalController(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateImpl = { [weak controller] in
|
PremiumBoostScreen(
|
||||||
if let _ = status.nextLevelBoosts {
|
context: context,
|
||||||
if let availableBoost = availableBoosts.first {
|
contentContext: contentContext,
|
||||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot]).startStandalone()
|
peerId: peerId,
|
||||||
controller?.updateSubject(nextSubject, count: nextCount)
|
isCurrent: isCurrent,
|
||||||
|
status: status,
|
||||||
|
myBoostStatus: myBoostStatus,
|
||||||
|
forceDark: forceDark,
|
||||||
|
openPeer: { peer in
|
||||||
|
openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||||
|
},
|
||||||
|
presentController: { [weak navigationController] c in
|
||||||
|
(navigationController?.viewControllers.last as? ViewController)?.present(c, in: .window(.root))
|
||||||
|
},
|
||||||
|
pushController: { [weak navigationController] c in
|
||||||
|
navigationController?.pushViewController(c)
|
||||||
|
|
||||||
availableBoosts.removeFirst()
|
if c is PremiumLimitScreen {
|
||||||
nextCount += 1
|
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||||
} else if !occupiedBoosts.isEmpty, let myBoostStatus {
|
storyProgressPauseContext.update(c)
|
||||||
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)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dismissImpl?()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dismissImpl = { [weak controller] in
|
}, dismissed: {
|
||||||
controller?.dismissAnimated()
|
dismissedImpl?()
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
case let .premiumGiftCode(slug):
|
case let .premiumGiftCode(slug):
|
||||||
var forceDark = false
|
var forceDark = false
|
||||||
if let updatedPresentationData, updatedPresentationData.initial.theme.overallDarkAppearance {
|
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)
|
self.textNode.attributedText = NSAttributedString(string: item.text, font: titleFont, textColor: textColorValue)
|
||||||
|
|
||||||
let textSize = self.textNode.updateLayout(CGSize(width: width - (leftInset + rightInset), height: .greatestFiniteMagnitude))
|
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)
|
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) {
|
if (isCreator && (channel.addressName?.isEmpty ?? true)) || (!channel.flags.contains(.isCreator) && channel.adminRights?.rights.contains(.canInviteUsers) == true) {
|
||||||
let invitesText: String
|
let invitesText: String
|
||||||
if let count = data.invitations?.count, count > 0 {
|
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)) {
|
if isCreator || (channel.adminRights != nil && channel.hasPermission(.sendSomething)) {
|
||||||
let messagesShouldHaveSignatures: Bool
|
let messagesShouldHaveSignatures: Bool
|
||||||
switch channel.info {
|
switch channel.info {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user