mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
277 lines
15 KiB
Swift
277 lines
15 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import SwiftSignalKit
|
|
import TelegramCore
|
|
import AccountContext
|
|
import TelegramPresentationData
|
|
import UndoUI
|
|
import PresentationDataUtils
|
|
|
|
private struct BoostState {
|
|
let level: Int32
|
|
let currentLevelBoosts: Int32
|
|
let nextLevelBoosts: Int32?
|
|
let boosts: Int32
|
|
|
|
func displayData(peer: EnginePeer, isCurrent: Bool, canBoostAgain: Bool, myBoostCount: Int32, currentMyBoostCount: Int32, replacedBoosts: Int32? = nil) -> (subject: PremiumLimitScreen.Subject, count: Int32) {
|
|
var currentLevel = self.level
|
|
var nextLevelBoosts = self.nextLevelBoosts
|
|
var currentLevelBoosts = self.currentLevelBoosts
|
|
var boosts = self.boosts
|
|
if let replacedBoosts {
|
|
boosts = max(currentLevelBoosts, boosts - replacedBoosts)
|
|
}
|
|
|
|
if currentMyBoostCount > 0 && self.boosts == currentLevelBoosts {
|
|
currentLevel = max(0, currentLevel - 1)
|
|
nextLevelBoosts = currentLevelBoosts
|
|
currentLevelBoosts = max(0, currentLevelBoosts - 1)
|
|
}
|
|
|
|
return (
|
|
.storiesChannelBoost(
|
|
peer: peer,
|
|
boostSubject: .stories,
|
|
isCurrent: isCurrent,
|
|
level: currentLevel,
|
|
currentLevelBoosts: currentLevelBoosts,
|
|
nextLevelBoosts: nextLevelBoosts,
|
|
link: nil,
|
|
myBoostCount: myBoostCount,
|
|
canBoostAgain: canBoostAgain
|
|
),
|
|
boosts
|
|
)
|
|
}
|
|
}
|
|
|
|
public func PremiumBoostScreen(
|
|
context: AccountContext,
|
|
contentContext: Any?,
|
|
peerId: EnginePeer.Id,
|
|
isCurrent: Bool,
|
|
status: ChannelBoostStatus?,
|
|
myBoostStatus: MyBoostStatus?,
|
|
replacedBoosts: (Int32, Int32)? = nil,
|
|
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),
|
|
TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)
|
|
)
|
|
|> deliverOnMainQueue).startStandalone(next: { peer, accountPeer in
|
|
guard let peer, let accountPeer, let status else {
|
|
return
|
|
}
|
|
|
|
let isPremium = accountPeer.isPremium
|
|
|
|
var myBoostCount: Int32 = 0
|
|
var currentMyBoostCount: 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
let initialState = BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts))
|
|
let updatedState = Promise<BoostState?>()
|
|
updatedState.set(.single(BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1))))
|
|
|
|
var updateImpl: (() -> Void)?
|
|
var dismissImpl: (() -> Void)?
|
|
|
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with({ $0 }))
|
|
let canBoostAgain = premiumConfiguration.boostsPerGiftCount > 0
|
|
|
|
let (initialSubject, initialCount) = initialState.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: 0, replacedBoosts: replacedBoosts?.0)
|
|
let controller = PremiumLimitScreen(context: context, subject: initialSubject, count: initialCount, forceDark: forceDark, action: {
|
|
let dismiss = false
|
|
updateImpl?()
|
|
return dismiss
|
|
},
|
|
openPeer: { peer in
|
|
openPeer(peer)
|
|
})
|
|
pushController(controller)
|
|
|
|
if let (replacedBoosts, inChannels) = replacedBoosts {
|
|
currentMyBoostCount += 1
|
|
let (subject, count) = initialState.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: 1, replacedBoosts: nil)
|
|
controller.updateSubject(subject, count: count)
|
|
|
|
Queue.mainQueue().after(0.3) {
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: presentationData.strings.ReassignBoost_Success(presentationData.strings.ReassignBoost_Boosts(replacedBoosts), presentationData.strings.ReassignBoost_OtherChannels(inChannels)).string, round: false, undoText: nil), elevatedLayout: false, position: .bottom, action: { _ in return true })
|
|
controller.present(undoController, in: .current)
|
|
}
|
|
}
|
|
|
|
controller.disposed = {
|
|
dismissed()
|
|
}
|
|
|
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
updateImpl = { [weak controller] in
|
|
if let _ = status.nextLevelBoosts {
|
|
if let availableBoost = availableBoosts.first {
|
|
currentMyBoostCount += 1
|
|
myBoostCount += 1
|
|
|
|
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot])
|
|
|> deliverOnMainQueue).startStandalone(completed: {
|
|
updatedState.set(context.engine.peers.getChannelBoostStatus(peerId: peerId)
|
|
|> map { status in
|
|
if let status {
|
|
return BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1))
|
|
} else {
|
|
return nil
|
|
}
|
|
})
|
|
})
|
|
|
|
let _ = (updatedState.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).startStandalone(next: { state in
|
|
guard let state else {
|
|
return
|
|
}
|
|
let (subject, count) = state.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: currentMyBoostCount)
|
|
controller?.updateSubject(subject, count: count)
|
|
})
|
|
|
|
availableBoosts.removeFirst()
|
|
} else if !occupiedBoosts.isEmpty, let myBoostStatus {
|
|
if canBoostAgain {
|
|
var dismissReplaceImpl: (() -> Void)?
|
|
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
|
|
var channelIds = Set<EnginePeer.Id>()
|
|
for boost in myBoostStatus.boosts {
|
|
if slots.contains(boost.slot) {
|
|
if let peer = boost.peer {
|
|
channelIds.insert(peer.id)
|
|
}
|
|
}
|
|
}
|
|
|
|
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone(completed: {
|
|
let _ = combineLatest(
|
|
queue: Queue.mainQueue(),
|
|
context.engine.peers.getChannelBoostStatus(peerId: peerId),
|
|
context.engine.peers.getMyBoostStatus()
|
|
).startStandalone(next: { boostStatus, myBoostStatus in
|
|
dismissReplaceImpl?()
|
|
PremiumBoostScreen(context: context, contentContext: contentContext, peerId: peerId, isCurrent: isCurrent, status: boostStatus, myBoostStatus: myBoostStatus, replacedBoosts: (Int32(slots.count), Int32(channelIds.count)), forceDark: forceDark, openPeer: openPeer, presentController: presentController, pushController: pushController, dismissed: dismissed)
|
|
})
|
|
})
|
|
})
|
|
dismissImpl?()
|
|
pushController(replaceController)
|
|
dismissReplaceImpl = { [weak replaceController] in
|
|
replaceController?.dismiss(animated: true)
|
|
}
|
|
} else if let boost = occupiedBoosts.first, let occupiedPeer = boost.peer {
|
|
if let cooldown = boost.cooldownUntil {
|
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
|
let timeout = cooldown - currentTime
|
|
let valueText = timeIntervalString(strings: presentationData.strings, value: timeout, usage: .afterTime, preferLowerValue: false)
|
|
let controller = textAlertController(
|
|
sharedContext: context.sharedContext,
|
|
updatedPresentationData: nil,
|
|
title: presentationData.strings.ChannelBoost_Error_BoostTooOftenTitle,
|
|
text: presentationData.strings.ChannelBoost_Error_BoostTooOftenText(valueText).string,
|
|
actions: [
|
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
|
],
|
|
parseMarkdown: true
|
|
)
|
|
presentController(controller)
|
|
} else {
|
|
let replaceController = replaceBoostConfirmationController(context: context, fromPeers: [occupiedPeer], toPeer: peer, commit: {
|
|
currentMyBoostCount += 1
|
|
myBoostCount += 1
|
|
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [boost.slot])
|
|
|> deliverOnMainQueue).startStandalone(completed: { [weak controller] in
|
|
let _ = (updatedState.get()
|
|
|> take(1)
|
|
|> deliverOnMainQueue).startStandalone(next: { [weak controller] state in
|
|
guard let state else {
|
|
return
|
|
}
|
|
let (subject, count) = state.displayData(peer: peer, isCurrent: isCurrent, canBoostAgain: canBoostAgain, myBoostCount: myBoostCount, currentMyBoostCount: currentMyBoostCount)
|
|
controller?.updateSubject(subject, count: count)
|
|
})
|
|
})
|
|
})
|
|
presentController(replaceController)
|
|
}
|
|
} else {
|
|
dismissImpl?()
|
|
}
|
|
} else {
|
|
if isPremium {
|
|
if !canBoostAgain {
|
|
dismissImpl?()
|
|
} else {
|
|
let controller = textAlertController(
|
|
sharedContext: context.sharedContext,
|
|
updatedPresentationData: nil,
|
|
title: presentationData.strings.ChannelBoost_MoreBoosts_Title,
|
|
text: presentationData.strings.ChannelBoost_MoreBoosts_Text(peer.compactDisplayTitle, "\(premiumConfiguration.boostsPerGiftCount)").string,
|
|
actions: [
|
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.ChannelBoost_MoreBoosts_Gift, action: {
|
|
dismissImpl?()
|
|
let controller = context.sharedContext.makePremiumGiftController(context: context)
|
|
pushController(controller)
|
|
}),
|
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Close, action: {})
|
|
],
|
|
actionLayout: .vertical,
|
|
parseMarkdown: true
|
|
)
|
|
presentController(controller)
|
|
}
|
|
} else {
|
|
let controller = textAlertController(
|
|
sharedContext: context.sharedContext,
|
|
updatedPresentationData: nil,
|
|
title: presentationData.strings.ChannelBoost_Error_PremiumNeededTitle,
|
|
text: presentationData.strings.ChannelBoost_Error_PremiumNeededText,
|
|
actions: [
|
|
TextAlertAction(type: .genericAction, 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()
|
|
}
|
|
})
|
|
}
|