mirror of
https://github.com/ichmagmaus111/ghostgram.git
synced 2026-07-25 08:50:51 +02:00
chore: migrate to new version + fixed several critical bugs
- Migrated project to latest Telegram iOS base (v12.3.2+) - Fixed circular dependency between GhostModeManager and MiscSettingsManager - Fixed multiple Bazel build configuration errors (select() default conditions) - Fixed duplicate type definitions in PeerInfoScreen - Fixed swiftmodule directory resolution in build scripts - Added Ghostgram Settings tab in main Settings menu with all 5 features - Cleared sensitive credentials from config.json (template-only now) - Excluded bazel-cache from version control
This commit is contained in:
@@ -123,6 +123,9 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Premium/PremiumCoinComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/Gifts/TableComponent",
|
||||
"//submodules/TelegramUI/Components/Gifts/PeerTableCellComponent",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -493,7 +493,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
}
|
||||
})
|
||||
case let .starsMore(theme, title):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: {
|
||||
arguments.expandStars()
|
||||
})
|
||||
case let .starsInfo(_, text):
|
||||
@@ -521,14 +521,14 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
if case let .channel(channel) = peer, case .group = channel.info {
|
||||
isGroup = true
|
||||
}
|
||||
return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text(isGroup ? presentationData.strings.BoostGift_GroupBoosts($0) : presentationData.strings.BoostGift_ChannelsBoosts($0), .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: {
|
||||
return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text(isGroup ? presentationData.strings.BoostGift_GroupBoosts($0) : presentationData.strings.BoostGift_ChannelsBoosts($0), .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: {
|
||||
}, setPeerIdWithRevealedOptions: { lhs, rhs in
|
||||
arguments.setItemIdWithRevealedOptions(lhs, rhs)
|
||||
}, removePeer: { id in
|
||||
arguments.removeChannel(id)
|
||||
})
|
||||
case let .channelAdd(theme, text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.roundPlusIconImage(theme), title: text, alwaysPlain: false, hasSeparator: true, sectionId: self.section, height: .compactPeerList, color: .accent, editing: false, action: {
|
||||
return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.roundPlusIconImage(theme), title: text, alwaysPlain: false, hasSeparator: true, sectionId: self.section, height: .compactPeerList, color: .accent, editing: false, action: {
|
||||
arguments.openChannelsSelection()
|
||||
})
|
||||
case let .channelsInfo(_, text):
|
||||
@@ -582,7 +582,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
arguments.openPremiumIntro()
|
||||
})
|
||||
case let .prizeDescription(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.showPrizeDescription = value
|
||||
@@ -590,7 +590,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
}
|
||||
})
|
||||
case let .prizeDescriptionText(_, placeholder, value, count):
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: "\(count)"), text: value, placeholder: placeholder, returnKeyType: .done, spacing: 24.0, maxLength: 128, tag: CreateGiveawayEntryTag.description, sectionId: self.section, textUpdated: { value in
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "\(count)"), text: value, placeholder: placeholder, returnKeyType: .done, spacing: 24.0, maxLength: 128, tag: CreateGiveawayEntryTag.description, sectionId: self.section, textUpdated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.prizeDescription = value
|
||||
@@ -616,7 +616,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
text = presentationData.strings.InviteLink_Create_TimeLimitExpiryDateNever
|
||||
}
|
||||
return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.BoostGift_DateEnds, label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: presentationData.strings.BoostGift_DateEnds, label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: {
|
||||
arguments.dismissInput()
|
||||
var focus = false
|
||||
arguments.updateState { state in
|
||||
@@ -677,7 +677,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
case let .timeInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .winners(_, text, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
updatedState.showWinners = value
|
||||
|
||||
@@ -212,7 +212,7 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
self.activateArea = AccessibilityAreaNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false)
|
||||
super.init(layerBacked: false, rotated: false, seeThrough: false)
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import TelegramCore
|
||||
import AccountContext
|
||||
import TelegramStringFormatting
|
||||
import TelegramPresentationData
|
||||
import Markdown
|
||||
import AlertUI
|
||||
import ComponentFlow
|
||||
import AlertComponent
|
||||
|
||||
public func presentGiveawayInfoController(
|
||||
context: AccountContext,
|
||||
@@ -23,6 +23,8 @@ public func presentGiveawayInfoController(
|
||||
peerIds.append(adminId)
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let _ = (context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Messages.Message(id: messageId),
|
||||
EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init))
|
||||
@@ -121,22 +123,16 @@ public func presentGiveawayInfoController(
|
||||
channelsCount = 1 + giveawayResults.additionalChannelsCount
|
||||
}
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
|
||||
|
||||
let timeZone = TimeZone.current
|
||||
let untilDate = stringForDate(timestamp: untilDateValue, timeZone: timeZone, strings: presentationData.strings)
|
||||
|
||||
let title: String
|
||||
let text: String
|
||||
var text: String
|
||||
var warning: String?
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
|
||||
var actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||
dismissImpl?()
|
||||
})]
|
||||
|
||||
var actions: [AlertScreen.Action] = [
|
||||
.init(title: presentationData.strings.Common_OK, type: .default)
|
||||
]
|
||||
|
||||
var additionalPrizes = ""
|
||||
if let prizeDescription, !prizeDescription.isEmpty {
|
||||
@@ -322,278 +318,65 @@ public func presentGiveawayInfoController(
|
||||
case .refunded:
|
||||
result = ""
|
||||
warning = presentationData.strings.Chat_Giveaway_Info_Refunded
|
||||
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Close, action: {
|
||||
dismissImpl?()
|
||||
})]
|
||||
case .notWon:
|
||||
result = "**\(presentationData.strings.Chat_Giveaway_Info_DidntWin)**\n\n"
|
||||
case let .wonPremium(slug):
|
||||
result = "**\(presentationData.strings.Chat_Giveaway_Info_Won("").string)**\n\n"
|
||||
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, action: {
|
||||
dismissImpl?()
|
||||
openLink(slug)
|
||||
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?()
|
||||
})]
|
||||
actions = [
|
||||
.init(title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, type: .default, action: {
|
||||
openLink(slug)
|
||||
}),
|
||||
.init(title: presentationData.strings.Common_Cancel)
|
||||
]
|
||||
case let .wonStars(stars):
|
||||
let _ = stars
|
||||
result = "**\(presentationData.strings.Chat_Giveaway_Info_Won("").string)**\n\n"
|
||||
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, action: {
|
||||
dismissImpl?()
|
||||
openLink("")
|
||||
}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?()
|
||||
})]
|
||||
actions = [
|
||||
.init(title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, type: .default, action: {
|
||||
openLink("")
|
||||
}),
|
||||
.init(title: presentationData.strings.Common_Cancel)
|
||||
]
|
||||
}
|
||||
|
||||
text = "\(result)\(intro)\(additionalPrizes)\n\n\(ending)"
|
||||
}
|
||||
|
||||
let alertController = giveawayInfoAlertController(
|
||||
context: context,
|
||||
updatedPresentationData: updatedPresentationData,
|
||||
title: title,
|
||||
text: text,
|
||||
warning: warning,
|
||||
actions: actions
|
||||
)
|
||||
dismissImpl = { [weak alertController] in
|
||||
alertController?.dismissAnimated()
|
||||
var content: [AnyComponentWithIdentity<AlertComponentEnvironment>] = []
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "title",
|
||||
component: AnyComponent(
|
||||
AlertTitleComponent(title: title)
|
||||
)
|
||||
))
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "text",
|
||||
component: AnyComponent(
|
||||
AlertTextComponent(content: .plain(text))
|
||||
)
|
||||
))
|
||||
if let warning {
|
||||
content.append(AnyComponentWithIdentity(
|
||||
id: "warning",
|
||||
component: AnyComponent(
|
||||
AlertTextComponent(content: .plain(warning), color: .destructive, style: .background(.bold))
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
var effectiveUpdatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)
|
||||
if let updatedPresentationData {
|
||||
effectiveUpdatedPresentationData = updatedPresentationData
|
||||
} else {
|
||||
effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData)
|
||||
}
|
||||
|
||||
let alertController = AlertScreen(
|
||||
configuration: AlertScreen.Configuration(actionAlignment: .vertical),
|
||||
content: content,
|
||||
actions: actions,
|
||||
updatedPresentationData: effectiveUpdatedPresentationData
|
||||
)
|
||||
present(alertController)
|
||||
})
|
||||
}
|
||||
|
||||
private final class GiveawayInfoAlertContentNode: AlertContentNode {
|
||||
private let title: String
|
||||
private let text: String
|
||||
private let warning: String?
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let textNode: ASTextNode
|
||||
fileprivate let warningBackgroundNode: ASImageNode
|
||||
fileprivate let warningTextNode: ImmediateTextNode
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [TextAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
public var theme: PresentationTheme
|
||||
|
||||
public override var dismissOnOutsideTap: Bool {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
public init(theme: AlertControllerTheme, ptheme: PresentationTheme, title: String, text: String, warning: String?, actions: [TextAlertAction]) {
|
||||
self.theme = ptheme
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.warning = warning
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.maximumNumberOfLines = 0
|
||||
|
||||
self.warningBackgroundNode = ASImageNode()
|
||||
self.warningBackgroundNode.displaysAsynchronously = false
|
||||
|
||||
self.warningTextNode = ImmediateTextNode()
|
||||
self.warningTextNode.maximumNumberOfLines = 0
|
||||
self.warningTextNode.lineSpacing = 0.1
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
self.actionNodesSeparator.isLayerBacked = true
|
||||
|
||||
self.actionNodes = actions.map { action -> TextAlertContentActionNode in
|
||||
return TextAlertContentActionNode(theme: theme, action: action)
|
||||
}
|
||||
|
||||
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||
if actions.count > 1 {
|
||||
for _ in 0 ..< actions.count - 1 {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.isLayerBacked = true
|
||||
actionVerticalSeparators.append(separatorNode)
|
||||
}
|
||||
}
|
||||
self.actionVerticalSeparators = actionVerticalSeparators
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.addSubnode(self.warningBackgroundNode)
|
||||
self.addSubnode(self.warningTextNode)
|
||||
|
||||
self.addSubnode(self.actionNodesSeparator)
|
||||
|
||||
for actionNode in self.actionNodes {
|
||||
self.addSubnode(actionNode)
|
||||
}
|
||||
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
self.addSubnode(separatorNode)
|
||||
}
|
||||
|
||||
self.updateTheme(theme)
|
||||
}
|
||||
|
||||
public override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
|
||||
self.textNode.attributedText = attributedText
|
||||
|
||||
self.warningTextNode.attributedText = NSAttributedString(string: self.warning ?? "", font: Font.semibold(13.0), textColor: theme.destructiveColor, paragraphAlignment: .center)
|
||||
self.warningBackgroundNode.image = generateStretchableFilledCircleImage(radius: 5.0, color: theme.destructiveColor.withAlphaComponent(0.1))
|
||||
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
for actionNode in self.actionNodes {
|
||||
actionNode.updateTheme(theme)
|
||||
}
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
_ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
public override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var size = size
|
||||
size.width = min(size.width, 270.0)
|
||||
let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude)
|
||||
|
||||
self.validLayout = size
|
||||
|
||||
var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
|
||||
|
||||
let titleSize = self.titleNode.measure(measureSize)
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||
origin.y += titleSize.height + 4.0
|
||||
|
||||
let textSize = self.textNode.measure(measureSize)
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
|
||||
origin.y += textSize.height + 6.0
|
||||
|
||||
let actionButtonHeight: CGFloat = 44.0
|
||||
var minActionsWidth: CGFloat = 0.0
|
||||
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||
let actionTitleInsets: CGFloat = 8.0
|
||||
|
||||
var effectiveActionLayout = TextAlertContentActionLayout.horizontal
|
||||
for actionNode in self.actionNodes {
|
||||
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 {
|
||||
effectiveActionLayout = .vertical
|
||||
}
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
minActionsWidth += actionTitleSize.width + actionTitleInsets
|
||||
case .vertical:
|
||||
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||
}
|
||||
}
|
||||
if "".isEmpty {
|
||||
effectiveActionLayout = .vertical
|
||||
}
|
||||
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||
|
||||
var contentWidth = max(titleSize.width, minActionsWidth)
|
||||
contentWidth = max(contentWidth, 234.0)
|
||||
|
||||
var actionsHeight: CGFloat = 0.0
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionsHeight = actionButtonHeight
|
||||
case .vertical:
|
||||
actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
}
|
||||
|
||||
let resultWidth = contentWidth + insets.left + insets.right
|
||||
|
||||
var warningHeight: CGFloat = 0.0
|
||||
if let _ = self.warning {
|
||||
let warningSize = self.warningTextNode.updateLayout(measureSize)
|
||||
let warningFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - warningSize.width) / 2.0), y: origin.y + 20.0), size: warningSize)
|
||||
transition.updateFrame(node: self.warningTextNode, frame: warningFrame)
|
||||
|
||||
transition.updateFrame(node: self.warningBackgroundNode, frame: warningFrame.insetBy(dx: -8.0, dy: -8.0))
|
||||
|
||||
warningHeight += warningSize.height + 26.0
|
||||
}
|
||||
|
||||
let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + 8.0 + actionsHeight + warningHeight + insets.top + insets.bottom)
|
||||
|
||||
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
|
||||
var actionOffset: CGFloat = 0.0
|
||||
let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
|
||||
var separatorIndex = -1
|
||||
var nodeIndex = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
|
||||
case .vertical:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
separatorIndex += 1
|
||||
|
||||
let currentActionWidth: CGFloat
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
if nodeIndex == self.actionNodes.count - 1 {
|
||||
currentActionWidth = resultSize.width - actionOffset
|
||||
} else {
|
||||
currentActionWidth = actionWidth
|
||||
}
|
||||
case .vertical:
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += currentActionWidth
|
||||
case .vertical:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += actionButtonHeight
|
||||
}
|
||||
|
||||
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||
|
||||
nodeIndex += 1
|
||||
}
|
||||
|
||||
return resultSize
|
||||
}
|
||||
}
|
||||
|
||||
private func giveawayInfoAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, title: String, text: String, warning: String?, actions: [TextAlertAction]) -> AlertController {
|
||||
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let contentNode = GiveawayInfoAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, title: title, text: text, warning: warning, actions: actions)
|
||||
|
||||
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
||||
let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller] presentationData in
|
||||
controller?.theme = AlertControllerTheme(presentationData: presentationData)
|
||||
})
|
||||
controller.dismissed = { _ in
|
||||
presentationDataDisposable.dispose()
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode {
|
||||
self.textNode.contentMode = .left
|
||||
self.textNode.contentsScale = UIScreen.main.scale
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
@@ -1133,7 +1133,7 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
|
||||
let closeButton = closeButton.update(
|
||||
component: GlassBarButtonComponent(
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
size: CGSize(width: 44.0, height: 44.0),
|
||||
backgroundColor: UIColor(rgb: 0x7f76f4),
|
||||
isDark: false,
|
||||
state: .tintedGlass,
|
||||
@@ -1147,7 +1147,7 @@ private final class DemoSheetContent: CombinedComponent {
|
||||
component.dismiss()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: 40.0, height: 40.0),
|
||||
availableSize: CGSize(width: 44.0, height: 44.0),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(closeButton
|
||||
|
||||
@@ -23,6 +23,8 @@ import InvisibleInkDustNode
|
||||
import PremiumStarComponent
|
||||
import GlassBarButtonComponent
|
||||
import ButtonComponent
|
||||
import TableComponent
|
||||
import PeerTableCellComponent
|
||||
|
||||
private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@@ -163,7 +165,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor
|
||||
tintColor: theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
)),
|
||||
action: { _ in
|
||||
@@ -326,7 +328,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
title: strings.GiftLink_From,
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: fromPeer)),
|
||||
content: AnyComponent(PeerTableCellComponent(context: context.component.context, theme: theme, strings: strings, peer: fromPeer)),
|
||||
action: {
|
||||
if let peer = fromPeer, peer.id != accountContext.account.peerId {
|
||||
component.openPeer(peer)
|
||||
@@ -344,7 +346,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
title: strings.GiftLink_To,
|
||||
component: AnyComponent(
|
||||
Button(
|
||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
||||
content: AnyComponent(PeerTableCellComponent(context: context.component.context, theme: theme, strings: strings, peer: toPeer)),
|
||||
action: {
|
||||
if toPeer.id != accountContext.account.peerId {
|
||||
component.openPeer(toPeer)
|
||||
@@ -857,306 +859,6 @@ final class GiftLinkButtonContentComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private final class TableComponent: CombinedComponent {
|
||||
class Item: Equatable {
|
||||
public let id: AnyHashable
|
||||
public let title: String
|
||||
public let component: AnyComponent<Empty>
|
||||
|
||||
public init<IdType: Hashable>(id: IdType, title: String, component: AnyComponent<Empty>) {
|
||||
self.id = AnyHashable(id)
|
||||
self.title = title
|
||||
self.component = component
|
||||
}
|
||||
|
||||
public static func == (lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.component != rhs.component {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let theme: PresentationTheme
|
||||
private let items: [Item]
|
||||
|
||||
public init(theme: PresentationTheme, items: [Item]) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
}
|
||||
|
||||
public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var cachedBorderImage: (UIImage, PresentationTheme)?
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
public static var body: Body {
|
||||
let leftColumnBackground = Child(Rectangle.self)
|
||||
let verticalBorder = Child(Rectangle.self)
|
||||
let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let borderChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let outerBorder = Child(Image.self)
|
||||
|
||||
return { context in
|
||||
let verticalPadding: CGFloat = 11.0
|
||||
let horizontalPadding: CGFloat = 12.0
|
||||
let borderWidth: CGFloat = 1.0
|
||||
|
||||
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
|
||||
let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
|
||||
|
||||
var leftColumnWidth: CGFloat = 0.0
|
||||
|
||||
var updatedTitleChildren: [_UpdatedChildComponent] = []
|
||||
var updatedValueChildren: [_UpdatedChildComponent] = []
|
||||
var updatedBorderChildren: [_UpdatedChildComponent] = []
|
||||
|
||||
for item in context.component.items {
|
||||
let titleChild = titleChildren[item.id].update(
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: item.title, font: Font.regular(15.0), textColor: context.component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
updatedTitleChildren.append(titleChild)
|
||||
|
||||
if titleChild.size.width > leftColumnWidth {
|
||||
leftColumnWidth = titleChild.size.width
|
||||
}
|
||||
}
|
||||
|
||||
leftColumnWidth = max(100.0, leftColumnWidth + horizontalPadding * 2.0)
|
||||
let rightColumnWidth = context.availableSize.width - leftColumnWidth
|
||||
|
||||
var i = 0
|
||||
var rowHeights: [Int: CGFloat] = [:]
|
||||
var totalHeight: CGFloat = 0.0
|
||||
|
||||
for item in context.component.items {
|
||||
let titleChild = updatedTitleChildren[i]
|
||||
let valueChild = valueChildren[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: rightColumnWidth - horizontalPadding * 2.0, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedValueChildren.append(valueChild)
|
||||
|
||||
let rowHeight = max(40.0, max(titleChild.size.height, valueChild.size.height) + verticalPadding * 2.0)
|
||||
rowHeights[i] = rowHeight
|
||||
totalHeight += rowHeight
|
||||
|
||||
if i < context.component.items.count - 1 {
|
||||
let borderChild = borderChildren[item.id].update(
|
||||
component: AnyComponent(Rectangle(color: borderColor)),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: borderWidth),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedBorderChildren.append(borderChild)
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
let leftColumnBackground = leftColumnBackground.update(
|
||||
component: Rectangle(color: context.component.theme.list.itemInputField.backgroundColor),
|
||||
availableSize: CGSize(width: leftColumnWidth, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
leftColumnBackground
|
||||
.position(CGPoint(x: leftColumnWidth / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
let borderImage: UIImage
|
||||
if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme {
|
||||
borderImage = currentImage
|
||||
} else {
|
||||
let borderRadius: CGFloat = 14.0
|
||||
borderImage = generateImage(CGSize(width: borderRadius * 2.0 + 6.0, height: borderRadius * 2.0 + 6.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil)
|
||||
context.setBlendMode(.clear)
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setStrokeColor(borderColor.cgColor)
|
||||
context.setLineWidth(borderWidth)
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
})!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius))
|
||||
context.state.cachedBorderImage = (borderImage, context.component.theme)
|
||||
}
|
||||
|
||||
let outerBorder = outerBorder.update(
|
||||
component: Image(image: borderImage),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(outerBorder
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
let verticalBorder = verticalBorder.update(
|
||||
component: Rectangle(color: borderColor),
|
||||
availableSize: CGSize(width: borderWidth, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
verticalBorder
|
||||
.position(CGPoint(x: leftColumnWidth - borderWidth / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
i = 0
|
||||
var originY: CGFloat = 0.0
|
||||
for (titleChild, valueChild) in zip(updatedTitleChildren, updatedValueChildren) {
|
||||
let rowHeight = rowHeights[i] ?? 0.0
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size)
|
||||
let valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + horizontalPadding, y: originY + verticalPadding), size: valueChild.size)
|
||||
|
||||
context.add(titleChild
|
||||
.position(titleFrame.center)
|
||||
)
|
||||
|
||||
context.add(valueChild
|
||||
.position(valueFrame.center)
|
||||
)
|
||||
|
||||
if i < updatedBorderChildren.count {
|
||||
let borderChild = updatedBorderChildren[i]
|
||||
context.add(borderChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + rowHeight - borderWidth / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
originY += rowHeight
|
||||
i += 1
|
||||
}
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: totalHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class PeerCellComponent: Component {
|
||||
let context: AccountContext
|
||||
let textColor: UIColor
|
||||
let peer: EnginePeer?
|
||||
|
||||
init(context: AccountContext, textColor: UIColor, peer: EnginePeer?) {
|
||||
self.context = context
|
||||
self.textColor = textColor
|
||||
self.peer = peer
|
||||
}
|
||||
|
||||
static func ==(lhs: PeerCellComponent, rhs: PeerCellComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.textColor !== rhs.textColor {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let avatarNode: AvatarNode
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private var component: PeerCellComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 13.0))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: PeerCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
self.avatarNode.setPeer(
|
||||
context: component.context,
|
||||
theme: component.context.sharedContext.currentPresentationData.with({ $0 }).theme,
|
||||
peer: component.peer,
|
||||
synchronousLoad: true
|
||||
)
|
||||
|
||||
let avatarSize = CGSize(width: 22.0, height: 22.0)
|
||||
let spacing: CGFloat = 6.0
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.peer?.compactDisplayTitle ?? "", font: Font.regular(15.0), textColor: component.textColor, paragraphAlignment: .left))
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - avatarSize.width - spacing, height: availableSize.height)
|
||||
)
|
||||
|
||||
let size = CGSize(width: avatarSize.width + textSize.width + spacing, height: textSize.height)
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - avatarSize.height) / 2.0)), size: avatarSize)
|
||||
self.avatarNode.frame = avatarFrame
|
||||
|
||||
if let view = self.text.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
let textFrame = CGRect(origin: CGPoint(x: avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
|
||||
transition.setFrame(view: view, frame: textFrame)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class DustComponent: Component {
|
||||
let color: UIColor
|
||||
|
||||
|
||||
@@ -1679,34 +1679,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.newPerksDisposable = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
ApplicationSpecificNotice.dismissedBusinessBadge(accountManager: context.sharedContext.accountManager),
|
||||
ApplicationSpecificNotice.dismissedBusinessLinksBadge(accountManager: context.sharedContext.accountManager),
|
||||
ApplicationSpecificNotice.dismissedBusinessIntroBadge(accountManager: context.sharedContext.accountManager),
|
||||
ApplicationSpecificNotice.dismissedBusinessChatbotsBadge(accountManager: context.sharedContext.accountManager)
|
||||
).startStrict(next: { [weak self] dismissedBusinessBadge, dismissedBusinessLinksBadge, dismissedBusinessIntroBadge, dismissedBusinessChatbotsBadge in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var newPerks: [String] = []
|
||||
if !dismissedBusinessBadge {
|
||||
newPerks.append(PremiumPerk.business.identifier)
|
||||
}
|
||||
if !dismissedBusinessLinksBadge {
|
||||
newPerks.append(PremiumPerk.businessLinks.identifier)
|
||||
}
|
||||
if !dismissedBusinessIntroBadge {
|
||||
newPerks.append(PremiumPerk.businessIntro.identifier)
|
||||
}
|
||||
if !dismissedBusinessChatbotsBadge {
|
||||
newPerks.append(PremiumPerk.businessChatBots.identifier)
|
||||
}
|
||||
self.newPerks = newPerks
|
||||
self.updated()
|
||||
})
|
||||
|
||||
|
||||
self.adsEnabledDisposable = (context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.AdsEnabled(id: context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] adsEnabled in
|
||||
guard let self else {
|
||||
@@ -2219,7 +2192,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
demoSubject = .todo
|
||||
case .business:
|
||||
demoSubject = .business
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
default:
|
||||
demoSubject = .doubleLimits
|
||||
}
|
||||
@@ -2418,7 +2390,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
}
|
||||
push(accountContext.sharedContext.makeChatbotSetupScreen(context: accountContext, initialData: initialData))
|
||||
})
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessChatbotsBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessIntro:
|
||||
let _ = (accountContext.sharedContext.makeBusinessIntroSetupScreenInitialData(context: accountContext)
|
||||
|> take(1)
|
||||
@@ -2428,7 +2399,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
}
|
||||
push(accountContext.sharedContext.makeBusinessIntroSetupScreen(context: accountContext, initialData: initialData))
|
||||
})
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessIntroBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessLinks:
|
||||
let _ = (accountContext.sharedContext.makeBusinessLinksSetupScreenInitialData(context: accountContext)
|
||||
|> take(1)
|
||||
@@ -2438,7 +2408,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
}
|
||||
push(accountContext.sharedContext.makeBusinessLinksSetupScreen(context: accountContext, initialData: initialData))
|
||||
})
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessLinksBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
@@ -2457,13 +2426,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
demoSubject = .businessAwayMessage
|
||||
case .businessChatBots:
|
||||
demoSubject = .businessChatBots
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessChatbotsBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessIntro:
|
||||
demoSubject = .businessIntro
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessIntroBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
case .businessLinks:
|
||||
demoSubject = .businessLinks
|
||||
let _ = ApplicationSpecificNotice.setDismissedBusinessLinksBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone()
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
@@ -2960,6 +2926,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
||||
private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
let overNavigationContainer: UIView
|
||||
let screenContext: PremiumIntroScreen.ScreenContext
|
||||
let mode: PremiumIntroScreen.Mode
|
||||
let source: PremiumSource
|
||||
@@ -2972,7 +2939,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
let copyLink: (String) -> Void
|
||||
let shareLink: (String) -> Void
|
||||
|
||||
init(screenContext: PremiumIntroScreen.ScreenContext, mode: PremiumIntroScreen.Mode, source: PremiumSource, forceDark: Bool, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping () -> Void, copyLink: @escaping (String) -> Void, shareLink: @escaping (String) -> Void) {
|
||||
init(overNavigationContainer: UIView, screenContext: PremiumIntroScreen.ScreenContext, mode: PremiumIntroScreen.Mode, source: PremiumSource, forceDark: Bool, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping () -> Void, copyLink: @escaping (String) -> Void, shareLink: @escaping (String) -> Void) {
|
||||
self.overNavigationContainer = overNavigationContainer
|
||||
self.screenContext = screenContext
|
||||
self.mode = mode
|
||||
self.source = source
|
||||
@@ -3415,8 +3383,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
let star = Child(PremiumStarComponent.self)
|
||||
let emoji = Child(EmojiHeaderComponent.self)
|
||||
let coin = Child(PremiumCoinComponent.self)
|
||||
let topPanel = Child(BlurredBackgroundComponent.self)
|
||||
let topSeparator = Child(Rectangle.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let secondaryTitle = Child(MultilineTextWithEntitiesComponent.self)
|
||||
let bottomEdgeEffect = Child(EdgeEffectComponent.self)
|
||||
@@ -3504,22 +3470,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
)
|
||||
}
|
||||
|
||||
let topPanel = topPanel.update(
|
||||
component: BlurredBackgroundComponent(
|
||||
color: environment.theme.rootController.navigationBar.blurredBackgroundColor
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: environment.navigationHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let topSeparator = topSeparator.update(
|
||||
component: Rectangle(
|
||||
color: environment.theme.rootController.navigationBar.separatorColor
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: UIScreenPixel),
|
||||
transition: context.transition
|
||||
)
|
||||
|
||||
let titleString: String
|
||||
if case .premiumGift = context.component.source {
|
||||
titleString = environment.strings.Premium_PremiumGift_Title
|
||||
@@ -3743,14 +3693,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
let topPanelAlpha: CGFloat
|
||||
let titleOffset: CGFloat
|
||||
let titleScale: CGFloat
|
||||
let titleOffsetDelta = (topInset + 160.0) - (environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)
|
||||
let titleAlpha: CGFloat
|
||||
|
||||
if let topContentOffset = state.topContentOffset {
|
||||
topPanelAlpha = min(20.0, max(0.0, topContentOffset - 95.0)) / 20.0
|
||||
let topContentOffset = topContentOffset + max(0.0, min(1.0, topContentOffset / titleOffsetDelta)) * 10.0
|
||||
titleOffset = topContentOffset
|
||||
let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta))
|
||||
@@ -3762,36 +3710,29 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
||||
titleAlpha = 1.0
|
||||
}
|
||||
} else {
|
||||
topPanelAlpha = 0.0
|
||||
titleScale = 1.0
|
||||
titleOffset = 0.0
|
||||
titleAlpha = state.otherPeerName != nil ? 0.0 : 1.0
|
||||
}
|
||||
|
||||
context.add(header
|
||||
context.addWithExternalContainer(header
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: topInset + header.size.height / 2.0 - 30.0 - titleOffset * titleScale))
|
||||
.scale(titleScale)
|
||||
.scale(titleScale),
|
||||
container: context.component.overNavigationContainer
|
||||
)
|
||||
|
||||
context.add(topPanel
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height / 2.0))
|
||||
.opacity(topPanelAlpha)
|
||||
)
|
||||
context.add(topSeparator
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height))
|
||||
.opacity(topPanelAlpha)
|
||||
)
|
||||
|
||||
context.add(title
|
||||
context.addWithExternalContainer(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)))
|
||||
.scale(titleScale)
|
||||
.opacity(titleAlpha)
|
||||
.opacity(titleAlpha),
|
||||
container: context.component.overNavigationContainer
|
||||
)
|
||||
|
||||
context.add(secondaryTitle
|
||||
context.addWithExternalContainer(secondaryTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0)))
|
||||
.scale(titleScale)
|
||||
.opacity(max(0.0, 1.0 - titleAlpha * 1.8))
|
||||
.opacity(max(0.0, 1.0 - titleAlpha * 1.8)),
|
||||
container: context.component.overNavigationContainer
|
||||
)
|
||||
|
||||
var isUnusedGift = false
|
||||
@@ -3989,6 +3930,8 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
public weak var containerView: UIView?
|
||||
public var animationColor: UIColor?
|
||||
|
||||
private let overNavigationContainer: UIView
|
||||
|
||||
public convenience init(context: AccountContext, mode: Mode = .premium, source: PremiumSource, modal: Bool = true, forceDark: Bool = false, forceHasPremium: Bool = false) {
|
||||
self.init(screenContext: .accountContext(context), mode: mode, source: source, modal: modal, forceDark: forceDark, forceHasPremium: forceHasPremium)
|
||||
}
|
||||
@@ -4005,7 +3948,11 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
var completionImpl: (() -> Void)?
|
||||
var copyLinkImpl: ((String) -> Void)?
|
||||
var shareLinkImpl: ((String) -> Void)?
|
||||
|
||||
self.overNavigationContainer = UIView()
|
||||
|
||||
super.init(component: PremiumIntroScreenComponent(
|
||||
overNavigationContainer: self.overNavigationContainer,
|
||||
screenContext: screenContext,
|
||||
mode: mode,
|
||||
source: source,
|
||||
@@ -4029,11 +3976,11 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
shareLink: { link in
|
||||
shareLinkImpl?(link)
|
||||
}
|
||||
), navigationBarAppearance: .transparent, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default, updatedPresentationData: screenContext.updatedPresentationData)
|
||||
), navigationBarAppearance: .default, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default, updatedPresentationData: screenContext.updatedPresentationData)
|
||||
|
||||
self._hasGlassStyle = true
|
||||
|
||||
if modal {
|
||||
let cancelItem = UIBarButtonItem(title: presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
self.navigationItem.setLeftBarButton(cancelItem, animated: false)
|
||||
self.navigationPresentation = .modal
|
||||
} else {
|
||||
self.navigationPresentation = .modalInLargeLayout
|
||||
@@ -4107,12 +4054,20 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
context.account.viewTracker.keepQuickRepliesApproximatelyUpdated()
|
||||
context.account.viewTracker.keepBusinessLinksApproximatelyUpdated()
|
||||
}
|
||||
|
||||
if let navigationBar = self.navigationBar {
|
||||
navigationBar.view.insertSubview(self.overNavigationContainer, aboveSubview: navigationBar.backgroundView)
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
public override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
self.dismissAllTooltips()
|
||||
@@ -4145,10 +4100,10 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
if !self.didSetReady {
|
||||
if let view = self.node.hostView.findTaggedView(tag: PremiumCoinComponent.View.Tag()) as? PremiumCoinComponent.View {
|
||||
if let view = findTaggedComponentViewImpl(view: self.view, tag: PremiumCoinComponent.View.Tag()) as? PremiumCoinComponent.View {
|
||||
self.didSetReady = true
|
||||
self._ready.set(view.ready)
|
||||
} else if let view = self.node.hostView.findTaggedView(tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View {
|
||||
} else if let view = findTaggedComponentViewImpl(view: self.view, tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View {
|
||||
self.didSetReady = true
|
||||
self._ready.set(view.ready)
|
||||
|
||||
@@ -4161,7 +4116,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
||||
self.containerView = nil
|
||||
self.animationColor = nil
|
||||
}
|
||||
} else if let view = self.node.hostView.findTaggedView(tag: EmojiHeaderComponent.View.Tag()) as? EmojiHeaderComponent.View {
|
||||
} else if let view = findTaggedComponentViewImpl(view: self.view, tag: EmojiHeaderComponent.View.Tag()) as? EmojiHeaderComponent.View {
|
||||
self.didSetReady = true
|
||||
self._ready.set(view.ready)
|
||||
|
||||
|
||||
@@ -864,7 +864,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor
|
||||
tintColor: theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
)),
|
||||
action: { _ in
|
||||
|
||||
@@ -170,7 +170,7 @@ private final class SheetContent: CombinedComponent {
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor
|
||||
tintColor: theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
)),
|
||||
action: { _ in
|
||||
|
||||
@@ -91,7 +91,7 @@ private final class SubscriptionsCountItemNode: ListViewItemNode {
|
||||
return textNode
|
||||
}
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.textNodes.forEach(self.addSubnode)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user