mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Stars subscriptions
This commit is contained in:
parent
4a0d46047c
commit
df311bb022
@ -12676,3 +12676,9 @@ Sorry for the inconvenience.";
|
|||||||
"Stickers.CreateSticker" = "Create\nSticker";
|
"Stickers.CreateSticker" = "Create\nSticker";
|
||||||
|
|
||||||
"InviteLink.CreateNewInfo" = "You can create additional invite links that are limited by time, number of users, or require a paid subscription.";
|
"InviteLink.CreateNewInfo" = "You can create additional invite links that are limited by time, number of users, or require a paid subscription.";
|
||||||
|
|
||||||
|
"InviteLink.CopyShort" = "Copy";
|
||||||
|
"InviteLink.ShareShort" = "Share";
|
||||||
|
|
||||||
|
"Stars.Subscription.Terms" = "By subscribing you agree to the [Terms of Service]().";
|
||||||
|
"Stars.Subscription.Terms_URL" = "https://telegram.org/tos/stars";
|
||||||
|
@ -1007,7 +1007,8 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, extendedMedia: [TelegramExtendedMedia], inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>, completion: @escaping (Bool) -> Void) -> ViewController
|
func makeStarsTransferScreen(context: AccountContext, starsContext: StarsContext, invoice: TelegramMediaInvoice, source: BotPaymentInvoiceSource, extendedMedia: [TelegramExtendedMedia], inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>, completion: @escaping (Bool) -> Void) -> ViewController
|
||||||
func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction, peer: EnginePeer) -> ViewController
|
func makeStarsTransactionScreen(context: AccountContext, transaction: StarsContext.State.Transaction, peer: EnginePeer) -> ViewController
|
||||||
func makeStarsReceiptScreen(context: AccountContext, receipt: BotPaymentReceipt) -> ViewController
|
func makeStarsReceiptScreen(context: AccountContext, receipt: BotPaymentReceipt) -> ViewController
|
||||||
func makeStarsSubscriptionScreen(context: AccountContext, subscription: StarsContext.State.Subscription) -> ViewController
|
func makeStarsSubscriptionScreen(context: AccountContext, subscription: StarsContext.State.Subscription, update: @escaping (Bool) -> Void) -> ViewController
|
||||||
|
func makeStarsSubscriptionScreen(context: AccountContext, peer: EnginePeer, pricing: StarsSubscriptionPricing, importer: PeerInvitationImportersState.Importer, usdRate: Double) -> ViewController
|
||||||
func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController
|
func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController
|
||||||
func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController
|
func makeStarsAmountScreen(context: AccountContext, initialValue: Int64?, completion: @escaping (Int64) -> Void) -> ViewController
|
||||||
func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController
|
func makeStarsWithdrawalScreen(context: AccountContext, stats: StarsRevenueStats, completion: @escaping (Int64) -> Void) -> ViewController
|
||||||
|
@ -1225,6 +1225,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
var avatarBadgeBackground: ASImageNode?
|
var avatarBadgeBackground: ASImageNode?
|
||||||
let onlineNode: PeerOnlineMarkerNode
|
let onlineNode: PeerOnlineMarkerNode
|
||||||
var avatarTimerBadge: AvatarBadgeView?
|
var avatarTimerBadge: AvatarBadgeView?
|
||||||
|
private var starView: StarView?
|
||||||
let pinnedIconNode: ASImageNode
|
let pinnedIconNode: ASImageNode
|
||||||
var secretIconNode: ASImageNode?
|
var secretIconNode: ASImageNode?
|
||||||
var verifiedIconView: ComponentHostView<Empty>?
|
var verifiedIconView: ComponentHostView<Empty>?
|
||||||
@ -1827,6 +1828,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
if let item = self.item, case .chatList = item.index {
|
if let item = self.item, case .chatList = item.index {
|
||||||
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: self.onlineIsVoiceChat), color: nil, transition: transition)
|
self.onlineNode.setImage(PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: self.onlineIsVoiceChat), color: nil, transition: transition)
|
||||||
|
self.starView?.setOutlineColor(item.presentationData.theme.chatList.itemHighlightedBackgroundColor, transition: transition)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if self.highlightedBackgroundNode.supernode != nil {
|
if self.highlightedBackgroundNode.supernode != nil {
|
||||||
@ -1845,12 +1847,16 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
if let item = self.item {
|
if let item = self.item {
|
||||||
let onlineIcon: UIImage?
|
let onlineIcon: UIImage?
|
||||||
|
let effectiveBackgroundColor: UIColor
|
||||||
if item.isPinned {
|
if item.isPinned {
|
||||||
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned, voiceChat: self.onlineIsVoiceChat)
|
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned, voiceChat: self.onlineIsVoiceChat)
|
||||||
|
effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor
|
||||||
} else {
|
} else {
|
||||||
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: self.onlineIsVoiceChat)
|
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: self.onlineIsVoiceChat)
|
||||||
|
effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor
|
||||||
}
|
}
|
||||||
self.onlineNode.setImage(onlineIcon, color: nil, transition: transition)
|
self.onlineNode.setImage(onlineIcon, color: nil, transition: transition)
|
||||||
|
self.starView?.setOutlineColor(effectiveBackgroundColor, transition: transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2934,6 +2940,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
titleIconsWidth += currentMutedIconImage.size.width
|
titleIconsWidth += currentMutedIconImage.size.width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSubscription = false
|
||||||
var isSecret = false
|
var isSecret = false
|
||||||
if !isPeerGroup {
|
if !isPeerGroup {
|
||||||
if case let .chatList(index) = item.index, index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat {
|
if case let .chatList(index) = item.index, index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat {
|
||||||
@ -2978,6 +2985,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if case let .chat(itemPeer) = contentPeer, let peer = itemPeer.chatMainPeer {
|
} else if case let .chat(itemPeer) = contentPeer, let peer = itemPeer.chatMainPeer {
|
||||||
|
if peer.isSubscription {
|
||||||
|
isSubscription = true
|
||||||
|
}
|
||||||
if case let .peer(peerData) = item.content, peerData.customMessageListData?.hidePeerStatus == true {
|
if case let .peer(peerData) = item.content, peerData.customMessageListData?.hidePeerStatus == true {
|
||||||
currentCredibilityIconContent = nil
|
currentCredibilityIconContent = nil
|
||||||
} else if case .savedMessagesChats = item.chatListLocation, peer.id == item.context.account.peerId {
|
} else if case .savedMessagesChats = item.chatListLocation, peer.id == item.context.account.peerId {
|
||||||
@ -3635,15 +3645,39 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
transition.updateSublayerTransformScale(node: strongSelf.onlineNode, scale: (1.0 - onlineInlineNavigationFraction) * 1.0 + onlineInlineNavigationFraction * 0.00001)
|
transition.updateSublayerTransformScale(node: strongSelf.onlineNode, scale: (1.0 - onlineInlineNavigationFraction) * 1.0 + onlineInlineNavigationFraction * 0.00001)
|
||||||
|
|
||||||
let onlineIcon: UIImage?
|
let onlineIcon: UIImage?
|
||||||
|
let effectiveBackgroundColor: UIColor
|
||||||
if strongSelf.reallyHighlighted {
|
if strongSelf.reallyHighlighted {
|
||||||
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: onlineIsVoiceChat)
|
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .highlighted, voiceChat: onlineIsVoiceChat)
|
||||||
|
effectiveBackgroundColor = item.presentationData.theme.chatList.itemHighlightedBackgroundColor
|
||||||
} else if case let .chatList(index) = item.index, index.pinningIndex != nil {
|
} else if case let .chatList(index) = item.index, index.pinningIndex != nil {
|
||||||
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned, voiceChat: onlineIsVoiceChat)
|
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .pinned, voiceChat: onlineIsVoiceChat)
|
||||||
|
effectiveBackgroundColor = item.presentationData.theme.chatList.pinnedItemBackgroundColor
|
||||||
} else {
|
} else {
|
||||||
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat)
|
onlineIcon = PresentationResourcesChatList.recentStatusOnlineIcon(item.presentationData.theme, state: .regular, voiceChat: onlineIsVoiceChat)
|
||||||
|
effectiveBackgroundColor = item.presentationData.theme.chatList.itemBackgroundColor
|
||||||
}
|
}
|
||||||
strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor, transition: .immediate)
|
strongSelf.onlineNode.setImage(onlineIcon, color: item.presentationData.theme.list.itemCheckColors.foregroundColor, transition: .immediate)
|
||||||
|
|
||||||
|
if isSubscription {
|
||||||
|
let starView: StarView
|
||||||
|
if let current = strongSelf.starView {
|
||||||
|
starView = current
|
||||||
|
} else {
|
||||||
|
starView = StarView()
|
||||||
|
strongSelf.starView = starView
|
||||||
|
strongSelf.view.addSubview(starView)
|
||||||
|
// strongSelf.mainContentContainerNode.view.addSubview(starView)
|
||||||
|
}
|
||||||
|
starView.outlineColor = effectiveBackgroundColor
|
||||||
|
|
||||||
|
let starSize = CGSize(width: 20.0, height: 20.0)
|
||||||
|
let starFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - starSize.width + 1.0, y: avatarFrame.maxY - starSize.height + 1.0), size: starSize)
|
||||||
|
transition.updateFrame(view: starView, frame: starFrame)
|
||||||
|
} else if let starView = strongSelf.starView {
|
||||||
|
strongSelf.starView = nil
|
||||||
|
starView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
let autoremoveTimeoutFraction: CGFloat
|
let autoremoveTimeoutFraction: CGFloat
|
||||||
if online {
|
if online {
|
||||||
autoremoveTimeoutFraction = 0.0
|
autoremoveTimeoutFraction = 0.0
|
||||||
@ -4746,3 +4780,47 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class StarView: UIView {
|
||||||
|
let outline = SimpleLayer()
|
||||||
|
let foreground = SimpleLayer()
|
||||||
|
|
||||||
|
var outlineColor: UIColor = .white {
|
||||||
|
didSet {
|
||||||
|
self.outline.layerTintColor = self.outlineColor.cgColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.outline.contents = UIImage(bundleImageName: "Premium/Stars/StarMediumOutline")?.cgImage
|
||||||
|
self.foreground.contents = UIImage(bundleImageName: "Premium/Stars/StarMedium")?.cgImage
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.outline)
|
||||||
|
self.layer.addSublayer(self.foreground)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setOutlineColor(_ color: UIColor, transition: ContainedViewLayoutTransition) {
|
||||||
|
if case let .animated(duration, curve) = transition, color != self.outlineColor {
|
||||||
|
let snapshotLayer = SimpleLayer()
|
||||||
|
snapshotLayer.layerTintColor = self.outlineColor.cgColor
|
||||||
|
snapshotLayer.contents = self.outline.contents
|
||||||
|
snapshotLayer.frame = self.outline.bounds
|
||||||
|
self.layer.insertSublayer(snapshotLayer, above: self.outline)
|
||||||
|
snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in
|
||||||
|
snapshotLayer?.removeFromSuperlayer()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
self.outlineColor = color
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
self.outline.frame = self.bounds
|
||||||
|
self.foreground.frame = self.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -90,7 +90,7 @@ public enum ChatListNotice: Equatable {
|
|||||||
case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday])
|
case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday])
|
||||||
case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int)
|
case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int)
|
||||||
case premiumGrace
|
case premiumGrace
|
||||||
case starsSubscriptionLowBalance
|
case starsSubscriptionLowBalance(amount: Int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||||
|
@ -262,10 +262,10 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
|||||||
|
|
||||||
okButtonLayout = makeOkButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelConfirm, font: titleFont, textColor: item.theme.list.itemAccentColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
|
okButtonLayout = makeOkButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelConfirm, font: titleFont, textColor: item.theme.list.itemAccentColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
|
||||||
cancelButtonLayout = makeCancelButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelReject, font: titleFont, textColor: item.theme.list.itemDestructiveColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
|
cancelButtonLayout = makeCancelButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelReject, font: titleFont, textColor: item.theme.list.itemDestructiveColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0)))
|
||||||
case .starsSubscriptionLowBalance:
|
case let .starsSubscriptionLowBalance(amount):
|
||||||
let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: "5 Stars needed for Astro Paws", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
|
let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: "⭐️ \(amount) Stars needed for your subscriptions", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
|
||||||
titleString = titleStringValue
|
titleString = titleStringValue
|
||||||
textString = NSAttributedString(string: "Insufficient funds to cover your subscription.", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
textString = NSAttributedString(string: "Insufficient funds to cover your subscriptions.", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
var leftInset: CGFloat = sideInset
|
var leftInset: CGFloat = sideInset
|
||||||
|
@ -79,7 +79,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
|
|
||||||
case subscriptionFeeToggle(PresentationTheme, String, Bool, Bool)
|
case subscriptionFeeToggle(PresentationTheme, String, Bool, Bool)
|
||||||
case subscriptionFee(PresentationTheme, String, Bool, Int64?)
|
case subscriptionFee(PresentationTheme, String, Bool, Int64?, String)
|
||||||
case subscriptionFeeInfo(PresentationTheme, String)
|
case subscriptionFeeInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case requestApproval(PresentationTheme, String, Bool, Bool)
|
case requestApproval(PresentationTheme, String, Bool, Bool)
|
||||||
@ -182,8 +182,8 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .subscriptionFee(lhsTheme, lhsText, lhsValue, lhsEnabled):
|
case let .subscriptionFee(lhsTheme, lhsText, lhsValue, lhsEnabled, lhsLabel):
|
||||||
if case let .subscriptionFee(rhsTheme, rhsText, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
|
if case let .subscriptionFee(rhsTheme, rhsText, rhsValue, rhsEnabled, rhsLabel) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue, lhsEnabled == rhsEnabled, lhsLabel == rhsLabel {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -288,7 +288,6 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||||||
}, action: {})
|
}, action: {})
|
||||||
case let .titleInfo(_, text):
|
case let .titleInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
|
|
||||||
case let .subscriptionFeeToggle(_, text, value, enabled):
|
case let .subscriptionFeeToggle(_, text, value, enabled):
|
||||||
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: enabled, sectionId: self.section, style: .blocks, updated: { value in
|
return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enabled: enabled, sectionId: self.section, style: .blocks, updated: { value in
|
||||||
arguments.updateState { state in
|
arguments.updateState { state in
|
||||||
@ -302,13 +301,13 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .subscriptionFee(_, placeholder, enabled, value):
|
case let .subscriptionFee(_, placeholder, enabled, value, label):
|
||||||
let title = NSMutableAttributedString(string: "⭐️", font: Font.semibold(18.0), textColor: .white)
|
let title = NSMutableAttributedString(string: "⭐️", font: Font.semibold(18.0), textColor: .white)
|
||||||
if let range = title.string.range(of: "⭐️") {
|
if let range = title.string.range(of: "⭐️") {
|
||||||
title.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: title.string))
|
title.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: title.string))
|
||||||
title.addAttribute(.baselineOffset, value: -1.0, range: NSRange(range, in: title.string))
|
title.addAttribute(.baselineOffset, value: -1.0, range: NSRange(range, in: title.string))
|
||||||
}
|
}
|
||||||
return ItemListSingleLineInputItem(context: arguments.context, presentationData: presentationData, title: title, text: value.flatMap { "\($0)" } ?? "", placeholder: placeholder, type: .number, spacing: 3.0, enabled: enabled, sectionId: self.section, textUpdated: { text in
|
return ItemListSingleLineInputItem(context: arguments.context, presentationData: presentationData, title: title, text: value.flatMap { "\($0)" } ?? "", placeholder: placeholder, label: label, type: .number, spacing: 3.0, enabled: enabled, sectionId: self.section, textUpdated: { text in
|
||||||
arguments.updateState { state in
|
arguments.updateState { state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
if let value = Int64(text) {
|
if let value = Int64(text) {
|
||||||
@ -318,7 +317,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
}, action: {})
|
}, action: {})
|
||||||
case let .subscriptionFeeInfo(_, text):
|
case let .subscriptionFeeInfo(_, text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
|
||||||
case let .requestApproval(_, text, value, enabled):
|
case let .requestApproval(_, text, value, enabled):
|
||||||
@ -458,7 +457,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state: InviteLinkEditControllerState, isGroup: Bool, isPublic: Bool, presentationData: PresentationData) -> [InviteLinksEditEntry] {
|
private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state: InviteLinkEditControllerState, isGroup: Bool, isPublic: Bool, presentationData: PresentationData, starsState: StarsRevenueStats?) -> [InviteLinksEditEntry] {
|
||||||
var entries: [InviteLinksEditEntry] = []
|
var entries: [InviteLinksEditEntry] = []
|
||||||
|
|
||||||
entries.append(.titleHeader(presentationData.theme, presentationData.strings.InviteLink_Create_LinkNameTitle.uppercased()))
|
entries.append(.titleHeader(presentationData.theme, presentationData.strings.InviteLink_Create_LinkNameTitle.uppercased()))
|
||||||
@ -471,7 +470,11 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
|
|||||||
//TODO:localize
|
//TODO:localize
|
||||||
entries.append(.subscriptionFeeToggle(presentationData.theme, "Require Monthly Fee", state.subscriptionEnabled, isEditingEnabled))
|
entries.append(.subscriptionFeeToggle(presentationData.theme, "Require Monthly Fee", state.subscriptionEnabled, isEditingEnabled))
|
||||||
if state.subscriptionEnabled {
|
if state.subscriptionEnabled {
|
||||||
entries.append(.subscriptionFee(presentationData.theme, "Stars amount per month", isEditingEnabled, state.subscriptionFee))
|
var label: String = ""
|
||||||
|
if let subscriptionFee, subscriptionFee > 0, let starsState {
|
||||||
|
label = formatTonUsdValue(state.subscriptionFee, divide: false, rate: starsState.usdRate, dateTimeFormat: presentationData.dateTimeFormat)
|
||||||
|
}
|
||||||
|
entries.append(.subscriptionFee(presentationData.theme, "Stars amount per month", isEditingEnabled, state.subscriptionFee, label))
|
||||||
}
|
}
|
||||||
let infoText: String
|
let infoText: String
|
||||||
if let _ = invite, state.subscriptionEnabled {
|
if let _ = invite, state.subscriptionEnabled {
|
||||||
@ -545,7 +548,7 @@ private struct InviteLinkEditControllerState: Equatable {
|
|||||||
var updating = false
|
var updating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func inviteLinkEditController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, invite: ExportedInvitation?, completion: ((ExportedInvitation?) -> Void)? = nil) -> ViewController {
|
public func inviteLinkEditController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, invite: ExportedInvitation?, starsState: StarsRevenueStats? = nil, completion: ((ExportedInvitation?) -> Void)? = nil) -> ViewController {
|
||||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||||
let actionsDisposable = DisposableSet()
|
let actionsDisposable = DisposableSet()
|
||||||
|
|
||||||
@ -759,7 +762,7 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(invite == nil ? presentationData.strings.InviteLink_Create_Title : presentationData.strings.InviteLink_Create_EditTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(invite == nil ? presentationData.strings.InviteLink_Create_Title : presentationData.strings.InviteLink_Create_EditTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkEditControllerEntries(invite: invite, state: state, isGroup: isGroup, isPublic: isPublic, presentationData: presentationData), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: animateChanges)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkEditControllerEntries(invite: invite, state: state, isGroup: isGroup, isPublic: isPublic, presentationData: presentationData, starsState: starsState), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: animateChanges)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||||||
case let .mainLinkHeader(_, text):
|
case let .mainLinkHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .mainLink(_, invite, peers, importersCount, isPublic):
|
case let .mainLink(_, invite, peers, importersCount, isPublic):
|
||||||
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: importersCount, peers: peers, displayButton: true, displayImporters: !isPublic, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
return ItemListPermanentInviteLinkItem(context: arguments.context, presentationData: presentationData, invite: invite, count: importersCount, peers: peers, displayButton: true, separateButtons: true, displayImporters: !isPublic, buttonColor: nil, sectionId: self.section, style: .blocks, copyAction: {
|
||||||
if let invite = invite {
|
if let invite = invite {
|
||||||
arguments.copyLink(invite)
|
arguments.copyLink(invite)
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func inviteLinkListControllerEntries(presentationData: PresentationData, exportedInvitation: EngineExportedPeerInvitation?, peer: EnginePeer?, invites: [ExportedInvitation]?, revokedInvites: [ExportedInvitation]?, importers: PeerInvitationImportersState?, creators: [ExportedInvitationCreator], admin: ExportedInvitationCreator?, tick: Int32) -> [InviteLinksListEntry] {
|
private func inviteLinkListControllerEntries(presentationData: PresentationData, exportedInvitation: EngineExportedPeerInvitation?, peer: EnginePeer?, invites: [ExportedInvitation]?, revokedInvites: [ExportedInvitation]?, importers: PeerInvitationImportersState?, creators: [ExportedInvitationCreator], admin: ExportedInvitationCreator?, tick: Int32, starsState: StarsRevenueStats?) -> [InviteLinksListEntry] {
|
||||||
var entries: [InviteLinksListEntry] = []
|
var entries: [InviteLinksListEntry] = []
|
||||||
|
|
||||||
if admin == nil {
|
if admin == nil {
|
||||||
@ -393,12 +393,12 @@ private struct InviteLinkListControllerState: Equatable {
|
|||||||
var revokingPrivateLink: Bool
|
var revokingPrivateLink: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
public func inviteLinkListController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, admin: ExportedInvitationCreator?) -> ViewController {
|
public func inviteLinkListController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, admin: ExportedInvitationCreator?, starsRevenueContext: StarsRevenueStatsContext? = nil) -> ViewController {
|
||||||
var pushControllerImpl: ((ViewController) -> Void)?
|
var pushControllerImpl: ((ViewController) -> Void)?
|
||||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||||
var presentInGlobalOverlayImpl: ((ViewController) -> Void)?
|
var presentInGlobalOverlayImpl: ((ViewController) -> Void)?
|
||||||
var navigationController: (() -> NavigationController?)?
|
var navigationController: (() -> NavigationController?)?
|
||||||
|
|
||||||
var dismissTooltipsImpl: (() -> Void)?
|
var dismissTooltipsImpl: (() -> Void)?
|
||||||
|
|
||||||
let actionsDisposable = DisposableSet()
|
let actionsDisposable = DisposableSet()
|
||||||
@ -409,6 +409,9 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
|||||||
statePromise.set(stateValue.modify { f($0) })
|
statePromise.set(stateValue.modify { f($0) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let starsContext: StarsRevenueStatsContext = starsRevenueContext ?? context.engine.payments.peerStarsRevenueContext(peerId: peerId)
|
||||||
|
let starsStats = Atomic<StarsRevenueStats?>(value: nil)
|
||||||
|
|
||||||
let revokeLinkDisposable = MetaDisposable()
|
let revokeLinkDisposable = MetaDisposable()
|
||||||
actionsDisposable.add(revokeLinkDisposable)
|
actionsDisposable.add(revokeLinkDisposable)
|
||||||
|
|
||||||
@ -487,7 +490,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
presentControllerImpl?(shareController, nil)
|
presentControllerImpl?(shareController, nil)
|
||||||
}, openMainLink: { invite in
|
}, openMainLink: { invite in
|
||||||
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: nil, revokedInvitationsContext: revokedInvitesContext, importersContext: nil)
|
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: nil, revokedInvitationsContext: revokedInvitesContext, importersContext: nil, starsState: starsStats.with { $0 })
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}, copyLink: { invite in
|
}, copyLink: { invite in
|
||||||
UIPasteboard.general.string = invite.link
|
UIPasteboard.general.string = invite.link
|
||||||
@ -604,7 +607,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
|||||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||||
presentInGlobalOverlayImpl?(contextController)
|
presentInGlobalOverlayImpl?(contextController)
|
||||||
}, createLink: {
|
}, createLink: {
|
||||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, completion: { invite in
|
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, starsState: starsStats.with( { $0 }), completion: { invite in
|
||||||
if let invite = invite {
|
if let invite = invite {
|
||||||
invitesContext.add(invite)
|
invitesContext.add(invite)
|
||||||
}
|
}
|
||||||
@ -613,7 +616,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
|||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}, openLink: { invite in
|
}, openLink: { invite in
|
||||||
if let invite = invite {
|
if let invite = invite {
|
||||||
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: invitesContext, revokedInvitationsContext: revokedInvitesContext, importersContext: nil)
|
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: invitesContext, revokedInvitationsContext: revokedInvitesContext, importersContext: nil, starsState: starsStats.with { $0 })
|
||||||
pushControllerImpl?(controller)
|
pushControllerImpl?(controller)
|
||||||
}
|
}
|
||||||
}, linkContextAction: { invite, canEdit, node, gesture in
|
}, linkContextAction: { invite, canEdit, node, gesture in
|
||||||
@ -730,7 +733,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
|||||||
}, action: { _, f in
|
}, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, completion: { invite in
|
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, starsState: starsStats.with( { $0 }), completion: { invite in
|
||||||
if let invite = invite {
|
if let invite = invite {
|
||||||
if invite.isRevoked {
|
if invite.isRevoked {
|
||||||
invitesContext.remove(invite)
|
invitesContext.remove(invite)
|
||||||
@ -897,12 +900,14 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
|||||||
invitesContext.state,
|
invitesContext.state,
|
||||||
revokedInvitesContext.state,
|
revokedInvitesContext.state,
|
||||||
creators,
|
creators,
|
||||||
timerPromise.get()
|
timerPromise.get(),
|
||||||
|
starsContext.state
|
||||||
)
|
)
|
||||||
|> map { presentationData, exportedInvitation, peer, importersContext, importers, invites, revokedInvites, creators, tick -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, exportedInvitation, peer, importersContext, importers, invites, revokedInvites, creators, tick, starsState -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
let previousInvites = previousInvites.swap(invites)
|
let previousInvites = previousInvites.swap(invites)
|
||||||
let previousRevokedInvites = previousRevokedInvites.swap(revokedInvites)
|
let previousRevokedInvites = previousRevokedInvites.swap(revokedInvites)
|
||||||
let previousCreators = previousCreators.swap(creators)
|
let previousCreators = previousCreators.swap(creators)
|
||||||
|
let _ = starsStats.swap(starsState.stats)
|
||||||
|
|
||||||
var crossfade = false
|
var crossfade = false
|
||||||
if (previousInvites?.hasLoadedOnce ?? false) != (invites.hasLoadedOnce) {
|
if (previousInvites?.hasLoadedOnce ?? false) != (invites.hasLoadedOnce) {
|
||||||
@ -928,7 +933,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: title, leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkListControllerEntries(presentationData: presentationData, exportedInvitation: exportedInvitation, peer: peer, invites: invites.hasLoadedOnce ? invites.invitations : nil, revokedInvites: revokedInvites.hasLoadedOnce ? revokedInvites.invitations : nil, importers: importers, creators: creators, admin: admin, tick: tick), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: animateChanges)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkListControllerEntries(presentationData: presentationData, exportedInvitation: exportedInvitation, peer: peer, invites: invites.hasLoadedOnce ? invites.invitations : nil, revokedInvites: revokedInvites.hasLoadedOnce ? revokedInvites.invitations : nil, importers: importers, creators: creators, admin: admin, tick: tick, starsState: starsState.stats), style: .blocks, emptyStateItem: nil, crossfadeState: crossfade, animateChanges: animateChanges)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
@ -48,14 +48,24 @@ private var subscriptionLinkIcon: UIImage? = {
|
|||||||
class InviteLinkViewInteraction {
|
class InviteLinkViewInteraction {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let openPeer: (EnginePeer.Id) -> Void
|
let openPeer: (EnginePeer.Id) -> Void
|
||||||
|
let openSubscription: (StarsSubscriptionPricing, PeerInvitationImportersState.Importer) -> Void
|
||||||
let copyLink: (ExportedInvitation) -> Void
|
let copyLink: (ExportedInvitation) -> Void
|
||||||
let shareLink: (ExportedInvitation) -> Void
|
let shareLink: (ExportedInvitation) -> Void
|
||||||
let editLink: (ExportedInvitation) -> Void
|
let editLink: (ExportedInvitation) -> Void
|
||||||
let contextAction: (ExportedInvitation, ASDisplayNode, ContextGesture?) -> Void
|
let contextAction: (ExportedInvitation, ASDisplayNode, ContextGesture?) -> Void
|
||||||
|
|
||||||
init(context: AccountContext, openPeer: @escaping (EnginePeer.Id) -> Void, copyLink: @escaping (ExportedInvitation) -> Void, shareLink: @escaping (ExportedInvitation) -> Void, editLink: @escaping (ExportedInvitation) -> Void, contextAction: @escaping (ExportedInvitation, ASDisplayNode, ContextGesture?) -> Void) {
|
init(
|
||||||
|
context: AccountContext,
|
||||||
|
openPeer: @escaping (EnginePeer.Id) -> Void,
|
||||||
|
openSubscription: @escaping (StarsSubscriptionPricing, PeerInvitationImportersState.Importer) -> Void,
|
||||||
|
copyLink: @escaping (ExportedInvitation) -> Void,
|
||||||
|
shareLink: @escaping (ExportedInvitation) -> Void,
|
||||||
|
editLink: @escaping (ExportedInvitation) -> Void,
|
||||||
|
contextAction: @escaping (ExportedInvitation, ASDisplayNode, ContextGesture?) -> Void
|
||||||
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
|
self.openSubscription = openSubscription
|
||||||
self.copyLink = copyLink
|
self.copyLink = copyLink
|
||||||
self.shareLink = shareLink
|
self.shareLink = shareLink
|
||||||
self.editLink = editLink
|
self.editLink = editLink
|
||||||
@ -93,7 +103,7 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
case requestHeader(PresentationTheme, String, String, Bool)
|
case requestHeader(PresentationTheme, String, String, Bool)
|
||||||
case request(Int32, PresentationTheme, PresentationDateTimeFormat, EnginePeer, Int32, Bool)
|
case request(Int32, PresentationTheme, PresentationDateTimeFormat, EnginePeer, Int32, Bool)
|
||||||
case importerHeader(PresentationTheme, String, String, Bool)
|
case importerHeader(PresentationTheme, String, String, Bool)
|
||||||
case importer(Int32, PresentationTheme, PresentationDateTimeFormat, EnginePeer, Int32, Bool, Bool)
|
case importer(Int32, PresentationTheme, PresentationDateTimeFormat, EnginePeer, Int32, Bool, Bool, PeerInvitationImportersState.Importer?, StarsSubscriptionPricing?)
|
||||||
|
|
||||||
var stableId: InviteLinkViewEntryId {
|
var stableId: InviteLinkViewEntryId {
|
||||||
switch self {
|
switch self {
|
||||||
@ -113,7 +123,7 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
return .request(peer.id)
|
return .request(peer.id)
|
||||||
case .importerHeader:
|
case .importerHeader:
|
||||||
return .importerHeader
|
return .importerHeader
|
||||||
case let .importer(_, _, _, peer, _, _, _):
|
case let .importer(_, _, _, peer, _, _, _, _, _):
|
||||||
return .importer(peer.id)
|
return .importer(peer.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,8 +178,8 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .importer(lhsIndex, lhsTheme, lhsDateTimeFormat, lhsPeer, lhsDate, lhsJoinedViaFolderLink, lhsLoading):
|
case let .importer(lhsIndex, lhsTheme, lhsDateTimeFormat, lhsPeer, lhsDate, lhsJoinedViaFolderLink, lhsLoading, lhsImporter, lhsPricing):
|
||||||
if case let .importer(rhsIndex, rhsTheme, rhsDateTimeFormat, rhsPeer, rhsDate, rhsJoinedViaFolderLink, rhsLoading) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsPeer == rhsPeer, lhsDate == rhsDate, lhsJoinedViaFolderLink == rhsJoinedViaFolderLink, lhsLoading == rhsLoading {
|
if case let .importer(rhsIndex, rhsTheme, rhsDateTimeFormat, rhsPeer, rhsDate, rhsJoinedViaFolderLink, rhsLoading, rhsImporter, rhsPricing) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsPeer == rhsPeer, lhsDate == rhsDate, lhsJoinedViaFolderLink == rhsJoinedViaFolderLink, lhsLoading == rhsLoading, lhsImporter == rhsImporter, lhsPricing == rhsPricing {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -237,11 +247,11 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
case .importer:
|
case .importer:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
case let .importer(lhsIndex, _, _, _, _, _, _):
|
case let .importer(lhsIndex, _, _, _, _, _, _, _, _):
|
||||||
switch rhs {
|
switch rhs {
|
||||||
case .link, .subscriptionHeader, .subscriptionPricing, .creatorHeader, .creator, .importerHeader, .request, .requestHeader:
|
case .link, .subscriptionHeader, .subscriptionPricing, .creatorHeader, .creator, .importerHeader, .request, .requestHeader:
|
||||||
return false
|
return false
|
||||||
case let .importer(rhsIndex, _, _, _, _, _, _):
|
case let .importer(rhsIndex, _, _, _, _, _, _, _, _):
|
||||||
return lhsIndex < rhsIndex
|
return lhsIndex < rhsIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,7 +260,7 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
func item(account: Account, presentationData: PresentationData, interaction: InviteLinkViewInteraction) -> ListViewItem {
|
func item(account: Account, presentationData: PresentationData, interaction: InviteLinkViewInteraction) -> ListViewItem {
|
||||||
switch self {
|
switch self {
|
||||||
case let .link(_, invite):
|
case let .link(_, invite):
|
||||||
return ItemListPermanentInviteLinkItem(context: interaction.context, presentationData: ItemListPresentationData(presentationData), invite: invite, count: 0, peers: [], displayButton: !invite.isRevoked, displayImporters: false, buttonColor: nil, sectionId: 0, style: .plain, copyAction: {
|
return ItemListPermanentInviteLinkItem(context: interaction.context, presentationData: ItemListPresentationData(presentationData), invite: invite, count: 0, peers: [], displayButton: !invite.isRevoked, separateButtons: true, displayImporters: false, buttonColor: nil, sectionId: 0, style: .plain, copyAction: {
|
||||||
interaction.copyLink(invite)
|
interaction.copyLink(invite)
|
||||||
}, shareAction: {
|
}, shareAction: {
|
||||||
if invitationAvailability(invite).isZero {
|
if invitationAvailability(invite).isZero {
|
||||||
@ -290,15 +300,35 @@ private enum InviteLinkViewEntry: Comparable, Identifiable {
|
|||||||
additionalText = .none
|
additionalText = .none
|
||||||
}
|
}
|
||||||
return SectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title, additionalText: additionalText)
|
return SectionHeaderItem(presentationData: ItemListPresentationData(presentationData), title: title, additionalText: additionalText)
|
||||||
case let .importer(_, _, dateTimeFormat, peer, date, joinedViaFolderLink, loading):
|
case let .importer(_, _, dateTimeFormat, peer, date, joinedViaFolderLink, loading, importer, pricing):
|
||||||
let dateString: String
|
let dateString: String
|
||||||
if joinedViaFolderLink {
|
if joinedViaFolderLink {
|
||||||
dateString = presentationData.strings.InviteLink_LabelJoinedViaFolder
|
dateString = presentationData.strings.InviteLink_LabelJoinedViaFolder
|
||||||
} else {
|
} else {
|
||||||
dateString = stringForFullDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: dateTimeFormat)
|
dateString = stringForFullDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: dateTimeFormat)
|
||||||
}
|
}
|
||||||
return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: interaction.context, peer: peer, height: .peerList, nameStyle: .distinctBold, presence: nil, text: .text(dateString, .secondary), label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: peer.id != account.peerId, sectionId: 0, action: {
|
|
||||||
interaction.openPeer(peer.id)
|
let label: ItemListPeerItemLabel
|
||||||
|
if let pricing {
|
||||||
|
//TODO:localize
|
||||||
|
let text = NSMutableAttributedString()
|
||||||
|
text.append(NSAttributedString(string: "⭐️\(pricing.amount)\n", font: Font.semibold(17.0), textColor: presentationData.theme.list.itemPrimaryTextColor))
|
||||||
|
text.append(NSAttributedString(string: "per month", font: Font.regular(13.0), textColor: presentationData.theme.list.itemSecondaryTextColor))
|
||||||
|
if let range = text.string.range(of: "⭐️") {
|
||||||
|
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: text.string))
|
||||||
|
text.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(range, in: text.string))
|
||||||
|
text.addAttribute(.baselineOffset, value: 3.5, range: NSRange(range, in: text.string))
|
||||||
|
}
|
||||||
|
label = .attributedText(text)
|
||||||
|
} else {
|
||||||
|
label = .none
|
||||||
|
}
|
||||||
|
return ItemListPeerItem(presentationData: ItemListPresentationData(presentationData), dateTimeFormat: dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, context: interaction.context, peer: peer, height: .peerList, nameStyle: .distinctBold, presence: nil, text: .text(dateString, .secondary), label: label, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), revealOptions: nil, switchValue: nil, enabled: true, selectable: peer.id != account.peerId, sectionId: 0, action: {
|
||||||
|
if let importer, let pricing {
|
||||||
|
interaction.openSubscription(pricing, importer)
|
||||||
|
} else {
|
||||||
|
interaction.openPeer(peer.id)
|
||||||
|
}
|
||||||
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, hasTopStripe: false, noInsets: true, style: .plain, tag: nil, shimmering: loading ? ItemListPeerItemShimmering(alternationIndex: 0) : nil)
|
}, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }, hasTopStripe: false, noInsets: true, style: .plain, tag: nil, shimmering: loading ? ItemListPeerItemShimmering(alternationIndex: 0) : nil)
|
||||||
case let .request(_, _, dateTimeFormat, peer, date, loading):
|
case let .request(_, _, dateTimeFormat, peer, date, loading):
|
||||||
let dateString = stringForFullDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: dateTimeFormat)
|
let dateString = stringForFullDate(timestamp: date, strings: presentationData.strings, dateTimeFormat: dateTimeFormat)
|
||||||
@ -351,18 +381,20 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
private let invitationsContext: PeerExportedInvitationsContext?
|
private let invitationsContext: PeerExportedInvitationsContext?
|
||||||
private let revokedInvitationsContext: PeerExportedInvitationsContext?
|
private let revokedInvitationsContext: PeerExportedInvitationsContext?
|
||||||
private let importersContext: PeerInvitationImportersContext?
|
private let importersContext: PeerInvitationImportersContext?
|
||||||
|
private let starsState: StarsRevenueStats?
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
fileprivate var presentationDataPromise = Promise<PresentationData>()
|
fileprivate var presentationDataPromise = Promise<PresentationData>()
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, invite: ExportedInvitation, invitationsContext: PeerExportedInvitationsContext?, revokedInvitationsContext: PeerExportedInvitationsContext?, importersContext: PeerInvitationImportersContext?) {
|
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, invite: ExportedInvitation, invitationsContext: PeerExportedInvitationsContext?, revokedInvitationsContext: PeerExportedInvitationsContext?, importersContext: PeerInvitationImportersContext?, starsState: StarsRevenueStats? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.invite = invite
|
self.invite = invite
|
||||||
self.invitationsContext = invitationsContext
|
self.invitationsContext = invitationsContext
|
||||||
self.revokedInvitationsContext = revokedInvitationsContext
|
self.revokedInvitationsContext = revokedInvitationsContext
|
||||||
self.importersContext = importersContext
|
self.importersContext = importersContext
|
||||||
|
self.starsState = starsState
|
||||||
|
|
||||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
@ -550,14 +582,25 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
self.interaction = InviteLinkViewInteraction(context: context, openPeer: { [weak self] peerId in
|
self.interaction = InviteLinkViewInteraction(context: context, openPeer: { [weak self] peerId in
|
||||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|> deliverOnMainQueue).start(next: { peer in
|
|> deliverOnMainQueue).start(next: { peer in
|
||||||
guard let peer = peer else {
|
guard let peer else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController {
|
||||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), keepStack: .always))
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), keepStack: .always))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}, openSubscription: { [weak self] pricing, importer in
|
||||||
|
guard let controller = self?.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|
|> deliverOnMainQueue).start(next: { peer in
|
||||||
|
guard let peer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let subscriptionController = context.sharedContext.makeStarsSubscriptionScreen(context: context, peer: peer, pricing: pricing, importer: importer, usdRate: controller.starsState?.usdRate ?? 0.0)
|
||||||
|
self?.controller?.push(subscriptionController)
|
||||||
|
})
|
||||||
}, copyLink: { [weak self] invite in
|
}, copyLink: { [weak self] invite in
|
||||||
UIPasteboard.general.string = invite.link
|
UIPasteboard.general.string = invite.link
|
||||||
|
|
||||||
@ -766,7 +809,7 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
let contextController = ContextController(presentationData: presentationData, source: .reference(InviteLinkContextReferenceContentSource(controller: controller, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||||
self?.controller?.presentInGlobalOverlay(contextController)
|
self?.controller?.presentInGlobalOverlay(contextController)
|
||||||
})
|
})
|
||||||
@ -791,6 +834,8 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
context.account.postbox.loadedPeerWithId(adminId)
|
context.account.postbox.loadedPeerWithId(adminId)
|
||||||
) |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, requestsState, creatorPeer in
|
) |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, requestsState, creatorPeer in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
let usdRate = strongSelf.controller?.starsState?.usdRate
|
||||||
|
|
||||||
var entries: [InviteLinkViewEntry] = []
|
var entries: [InviteLinkViewEntry] = []
|
||||||
|
|
||||||
entries.append(.link(presentationData.theme, invite))
|
entries.append(.link(presentationData.theme, invite))
|
||||||
@ -802,7 +847,12 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
var subtitle = "No one joined yet"
|
var subtitle = "No one joined yet"
|
||||||
if state.count > 0 {
|
if state.count > 0 {
|
||||||
title += " x \(state.count)"
|
title += " x \(state.count)"
|
||||||
subtitle = "You get approximately $\(Float(pricing.amount * Int64(state.count)) * 0.01) monthly"
|
if let usdRate {
|
||||||
|
let usdValue = formatTonUsdValue(pricing.amount * Int64(state.count), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat)
|
||||||
|
subtitle = "You get approximately \(usdValue) monthly"
|
||||||
|
} else {
|
||||||
|
subtitle = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
entries.append(.subscriptionPricing(presentationData.theme, title, subtitle))
|
entries.append(.subscriptionPricing(presentationData.theme, title, subtitle))
|
||||||
}
|
}
|
||||||
@ -856,14 +906,14 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
loading = true
|
loading = true
|
||||||
let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil)
|
let fakeUser = TelegramUser(id: EnginePeer.Id(namespace: .max, id: EnginePeer.Id.Id._internalFromInt64Value(0)), accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil)
|
||||||
for i in 0 ..< count {
|
for i in 0 ..< count {
|
||||||
entries.append(.importer(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, false, true))
|
entries.append(.importer(Int32(i), presentationData.theme, presentationData.dateTimeFormat, EnginePeer.user(fakeUser), 0, false, true, nil, nil))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
count = min(4, Int32(state.importers.count))
|
count = min(4, Int32(state.importers.count))
|
||||||
loading = false
|
loading = false
|
||||||
for importer in state.importers {
|
for importer in state.importers {
|
||||||
if let peer = importer.peer.peer {
|
if let peer = importer.peer.peer {
|
||||||
entries.append(.importer(index, presentationData.theme, presentationData.dateTimeFormat, EnginePeer(peer), importer.date, importer.joinedViaFolderLink, false))
|
entries.append(.importer(index, presentationData.theme, presentationData.dateTimeFormat, EnginePeer(peer), importer.date, importer.joinedViaFolderLink, false, importer, invite.pricing))
|
||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
@ -953,7 +1003,7 @@ public final class InviteLinkViewController: ViewController {
|
|||||||
let revokedInvitationsContext = parentController.revokedInvitationsContext
|
let revokedInvitationsContext = parentController.revokedInvitationsContext
|
||||||
if let navigationController = navigationController {
|
if let navigationController = navigationController {
|
||||||
let updatedPresentationData = (self.presentationData, parentController.presentationDataPromise.get())
|
let updatedPresentationData = (self.presentationData, parentController.presentationDataPromise.get())
|
||||||
let controller = inviteLinkEditController(context: self.context, updatedPresentationData: updatedPresentationData, peerId: self.peerId, invite: self.invite, completion: { [weak self] invite in
|
let controller = inviteLinkEditController(context: self.context, updatedPresentationData: updatedPresentationData, peerId: self.peerId, invite: self.invite, starsState: self.controller?.starsState, completion: { [weak self] invite in
|
||||||
if let invite = invite {
|
if let invite = invite {
|
||||||
if invite.isRevoked {
|
if invite.isRevoked {
|
||||||
invitationsContext?.remove(invite)
|
invitationsContext?.remove(invite)
|
||||||
|
@ -198,7 +198,7 @@ public func inviteRequestsController(context: AccountContext, updatedPresentatio
|
|||||||
} else {
|
} else {
|
||||||
string = presentationData.strings.MemberRequests_UserAddedToGroup(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
|
string = presentationData.strings.MemberRequests_UserAddedToGroup(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||||
}
|
}
|
||||||
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: peer, text: string, action: nil, duration: 3), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: peer, title: nil, text: string, action: nil, duration: 3), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ private enum ItemBackgroundColor: Equatable {
|
|||||||
case .blue:
|
case .blue:
|
||||||
return (UIColor(rgb: 0x00b5f7), UIColor(rgb: 0x00b2f6), UIColor(rgb: 0xa7f4ff))
|
return (UIColor(rgb: 0x00b5f7), UIColor(rgb: 0x00b2f6), UIColor(rgb: 0xa7f4ff))
|
||||||
case .green:
|
case .green:
|
||||||
return (UIColor(rgb: 0x4aca62), UIColor(rgb: 0x43c85c), UIColor(rgb: 0xc5ffe6))
|
return (UIColor(rgb: 0x31b73b), UIColor(rgb: 0x88d93b), UIColor(rgb: 0xc5ffe6))
|
||||||
case .yellow:
|
case .yellow:
|
||||||
return (UIColor(rgb: 0xf8a953), UIColor(rgb: 0xf7a64e), UIColor(rgb: 0xfeffd7))
|
return (UIColor(rgb: 0xf8a953), UIColor(rgb: 0xf7a64e), UIColor(rgb: 0xfeffd7))
|
||||||
case .red:
|
case .red:
|
||||||
@ -208,7 +208,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
|
|
||||||
self.iconBackgroundNode = ASDisplayNode()
|
self.iconBackgroundNode = ASDisplayNode()
|
||||||
self.iconBackgroundNode.setLayerBlock { () -> CALayer in
|
self.iconBackgroundNode.setLayerBlock { () -> CALayer in
|
||||||
return CAShapeLayer()
|
return CAGradientLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.iconNode = ASImageNode()
|
self.iconNode = ASImageNode()
|
||||||
@ -283,8 +283,11 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
public override func didLoad() {
|
public override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
if let shapeLayer = self.iconBackgroundNode.layer as? CAShapeLayer {
|
self.iconBackgroundNode.cornerRadius = 20.0
|
||||||
shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0)).cgPath
|
if let iconBackgroundLayer = self.iconBackgroundNode.layer as? CAGradientLayer {
|
||||||
|
iconBackgroundLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
|
||||||
|
iconBackgroundLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
|
||||||
|
iconBackgroundLayer.type = .axial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,17 +347,24 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
transitionFraction = 0.0
|
transitionFraction = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let topColor = color.colors.top
|
let colors = color.colors
|
||||||
let nextTopColor = nextColor.colors.top
|
let nextColors = nextColor.colors
|
||||||
let iconColor: UIColor
|
let topIconColor: UIColor
|
||||||
|
let bottomIconColor: UIColor
|
||||||
if let _ = item.invite {
|
if let _ = item.invite {
|
||||||
if case .blue = color {
|
if case .green = color, item.invite?.pricing != nil {
|
||||||
iconColor = item.presentationData.theme.list.itemAccentColor
|
topIconColor = color.colors.bottom
|
||||||
|
bottomIconColor = color.colors.top
|
||||||
|
} else if case .blue = color {
|
||||||
|
topIconColor = item.presentationData.theme.list.itemAccentColor
|
||||||
|
bottomIconColor = topIconColor
|
||||||
} else {
|
} else {
|
||||||
iconColor = nextTopColor.mixedWith(topColor, alpha: transitionFraction)
|
topIconColor = nextColors.top.mixedWith(colors.top, alpha: transitionFraction)
|
||||||
|
bottomIconColor = topIconColor
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
iconColor = item.presentationData.theme.list.mediaPlaceholderColor
|
topIconColor = item.presentationData.theme.list.mediaPlaceholderColor
|
||||||
|
bottomIconColor = topIconColor
|
||||||
}
|
}
|
||||||
|
|
||||||
let inviteLink = item.invite?.link?.replacingOccurrences(of: "https://", with: "") ?? ""
|
let inviteLink = item.invite?.link?.replacingOccurrences(of: "https://", with: "") ?? ""
|
||||||
@ -400,7 +410,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
if let range = text.string.range(of: "⭐️") {
|
if let range = text.string.range(of: "⭐️") {
|
||||||
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: text.string))
|
text.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: text.string))
|
||||||
text.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(range, in: text.string))
|
text.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(range, in: text.string))
|
||||||
text.addAttribute(.baselineOffset, value: 2.5, range: NSRange(range, in: text.string))
|
text.addAttribute(.baselineOffset, value: 3.5, range: NSRange(range, in: text.string))
|
||||||
}
|
}
|
||||||
pricingAttributedText = text
|
pricingAttributedText = text
|
||||||
}
|
}
|
||||||
@ -526,8 +536,11 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
}
|
}
|
||||||
strongSelf.contextSourceNode.contentRect = extractedRect
|
strongSelf.contextSourceNode.contentRect = extractedRect
|
||||||
|
|
||||||
if let layer = strongSelf.iconBackgroundNode.layer as? CAShapeLayer {
|
if let iconBackgroundLayer = strongSelf.iconBackgroundNode.layer as? CAGradientLayer {
|
||||||
layer.fillColor = iconColor.cgColor
|
iconBackgroundLayer.colors = [
|
||||||
|
topIconColor.cgColor,
|
||||||
|
bottomIconColor.cgColor
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = updatedTheme {
|
if let _ = updatedTheme {
|
||||||
@ -633,7 +646,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.timerNode = timerNode
|
strongSelf.timerNode = timerNode
|
||||||
strongSelf.offsetContainerNode.addSubnode(timerNode)
|
strongSelf.offsetContainerNode.addSubnode(timerNode)
|
||||||
}
|
}
|
||||||
timerNode.update(color: iconColor, value: timerValue)
|
timerNode.update(color: topIconColor, value: timerValue)
|
||||||
} else if let timerNode = strongSelf.timerNode {
|
} else if let timerNode = strongSelf.timerNode {
|
||||||
strongSelf.timerNode = nil
|
strongSelf.timerNode = nil
|
||||||
timerNode.removeFromSupernode()
|
timerNode.removeFromSupernode()
|
||||||
|
@ -32,6 +32,7 @@ public class ItemListPermanentInviteLinkItem: ListViewItem, ItemListItem {
|
|||||||
let count: Int32
|
let count: Int32
|
||||||
let peers: [EnginePeer]
|
let peers: [EnginePeer]
|
||||||
let displayButton: Bool
|
let displayButton: Bool
|
||||||
|
let separateButtons: Bool
|
||||||
let displayImporters: Bool
|
let displayImporters: Bool
|
||||||
let buttonColor: UIColor?
|
let buttonColor: UIColor?
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
@ -49,6 +50,7 @@ public class ItemListPermanentInviteLinkItem: ListViewItem, ItemListItem {
|
|||||||
count: Int32,
|
count: Int32,
|
||||||
peers: [EnginePeer],
|
peers: [EnginePeer],
|
||||||
displayButton: Bool,
|
displayButton: Bool,
|
||||||
|
separateButtons: Bool = false,
|
||||||
displayImporters: Bool,
|
displayImporters: Bool,
|
||||||
buttonColor: UIColor?,
|
buttonColor: UIColor?,
|
||||||
sectionId: ItemListSectionId,
|
sectionId: ItemListSectionId,
|
||||||
@ -65,6 +67,7 @@ public class ItemListPermanentInviteLinkItem: ListViewItem, ItemListItem {
|
|||||||
self.count = count
|
self.count = count
|
||||||
self.peers = peers
|
self.peers = peers
|
||||||
self.displayButton = displayButton
|
self.displayButton = displayButton
|
||||||
|
self.separateButtons = separateButtons
|
||||||
self.displayImporters = displayImporters
|
self.displayImporters = displayImporters
|
||||||
self.buttonColor = buttonColor
|
self.buttonColor = buttonColor
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
@ -126,6 +129,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
private let addressButtonNode: HighlightTrackingButtonNode
|
private let addressButtonNode: HighlightTrackingButtonNode
|
||||||
private let addressButtonIconNode: ASImageNode
|
private let addressButtonIconNode: ASImageNode
|
||||||
private var addressShimmerNode: ShimmerEffectNode?
|
private var addressShimmerNode: ShimmerEffectNode?
|
||||||
|
private var copyButtonNode: SolidRoundedButtonNode?
|
||||||
private var shareButtonNode: SolidRoundedButtonNode?
|
private var shareButtonNode: SolidRoundedButtonNode?
|
||||||
|
|
||||||
private let avatarsButtonNode: HighlightTrackingButtonNode
|
private let avatarsButtonNode: HighlightTrackingButtonNode
|
||||||
@ -234,6 +238,11 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.copyButtonNode?.pressed = { [weak self] in
|
||||||
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
|
item.copyAction?()
|
||||||
|
}
|
||||||
|
}
|
||||||
self.shareButtonNode?.pressed = { [weak self] in
|
self.shareButtonNode?.pressed = { [weak self] in
|
||||||
if let strongSelf = self, let item = strongSelf.item {
|
if let strongSelf = self, let item = strongSelf.item {
|
||||||
item.shareAction?()
|
item.shareAction?()
|
||||||
@ -444,7 +453,31 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
|
|
||||||
strongSelf.addressButtonNode.isHidden = item.contextAction == nil
|
strongSelf.addressButtonNode.isHidden = item.contextAction == nil
|
||||||
strongSelf.addressButtonIconNode.isHidden = item.contextAction == nil
|
strongSelf.addressButtonIconNode.isHidden = item.contextAction == nil
|
||||||
|
|
||||||
|
var effectiveSeparateButtons = item.separateButtons
|
||||||
|
if let invite = item.invite, invitationAvailability(invite).isZero {
|
||||||
|
effectiveSeparateButtons = false
|
||||||
|
}
|
||||||
|
|
||||||
|
let copyButtonNode: SolidRoundedButtonNode
|
||||||
|
if let currentCopyButtonNode = strongSelf.copyButtonNode {
|
||||||
|
copyButtonNode = currentCopyButtonNode
|
||||||
|
} else {
|
||||||
|
let buttonTheme: SolidRoundedButtonTheme
|
||||||
|
if let buttonColor = item.buttonColor {
|
||||||
|
buttonTheme = SolidRoundedButtonTheme(backgroundColor: buttonColor, foregroundColor: item.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||||
|
} else {
|
||||||
|
buttonTheme = SolidRoundedButtonTheme(theme: item.presentationData.theme)
|
||||||
|
}
|
||||||
|
copyButtonNode = SolidRoundedButtonNode(theme: buttonTheme, height: 50.0, cornerRadius: 11.0)
|
||||||
|
copyButtonNode.title = item.presentationData.strings.InviteLink_CopyShort
|
||||||
|
copyButtonNode.pressed = { [weak self] in
|
||||||
|
self?.item?.copyAction?()
|
||||||
|
}
|
||||||
|
strongSelf.addSubnode(copyButtonNode)
|
||||||
|
strongSelf.copyButtonNode = copyButtonNode
|
||||||
|
}
|
||||||
|
|
||||||
let shareButtonNode: SolidRoundedButtonNode
|
let shareButtonNode: SolidRoundedButtonNode
|
||||||
if let currentShareButtonNode = strongSelf.shareButtonNode {
|
if let currentShareButtonNode = strongSelf.shareButtonNode {
|
||||||
shareButtonNode = currentShareButtonNode
|
shareButtonNode = currentShareButtonNode
|
||||||
@ -459,7 +492,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
if let invite = item.invite, invitationAvailability(invite).isZero {
|
if let invite = item.invite, invitationAvailability(invite).isZero {
|
||||||
shareButtonNode.title = item.presentationData.strings.InviteLink_ReactivateLink
|
shareButtonNode.title = item.presentationData.strings.InviteLink_ReactivateLink
|
||||||
} else {
|
} else {
|
||||||
shareButtonNode.title = item.presentationData.strings.InviteLink_Share
|
shareButtonNode.title = effectiveSeparateButtons ? item.presentationData.strings.InviteLink_ShareShort : item.presentationData.strings.InviteLink_Share
|
||||||
}
|
}
|
||||||
shareButtonNode.pressed = { [weak self] in
|
shareButtonNode.pressed = { [weak self] in
|
||||||
self?.item?.shareAction?()
|
self?.item?.shareAction?()
|
||||||
@ -468,9 +501,19 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
strongSelf.shareButtonNode = shareButtonNode
|
strongSelf.shareButtonNode = shareButtonNode
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonWidth = contentSize.width - leftInset - rightInset
|
let buttonSpacing: CGFloat = 8.0
|
||||||
|
var buttonWidth = contentSize.width - leftInset - rightInset
|
||||||
|
var shareButtonOriginX = leftInset
|
||||||
|
if effectiveSeparateButtons {
|
||||||
|
buttonWidth = (buttonWidth - buttonSpacing) / 2.0
|
||||||
|
shareButtonOriginX = leftInset + buttonWidth + buttonSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = copyButtonNode.updateLayout(width: buttonWidth, transition: .immediate)
|
||||||
|
copyButtonNode.frame = CGRect(x: leftInset, y: verticalInset + fieldHeight + fieldSpacing, width: buttonWidth, height: buttonHeight)
|
||||||
|
|
||||||
let _ = shareButtonNode.updateLayout(width: buttonWidth, transition: .immediate)
|
let _ = shareButtonNode.updateLayout(width: buttonWidth, transition: .immediate)
|
||||||
shareButtonNode.frame = CGRect(x: leftInset, y: verticalInset + fieldHeight + fieldSpacing, width: buttonWidth, height: buttonHeight)
|
shareButtonNode.frame = CGRect(x: shareButtonOriginX, y: verticalInset + fieldHeight + fieldSpacing, width: buttonWidth, height: buttonHeight)
|
||||||
|
|
||||||
var totalWidth = invitedPeersLayout.size.width
|
var totalWidth = invitedPeersLayout.size.width
|
||||||
var leftOrigin: CGFloat = floorToScreenPixels((params.width - invitedPeersLayout.size.width) / 2.0)
|
var leftOrigin: CGFloat = floorToScreenPixels((params.width - invitedPeersLayout.size.width) / 2.0)
|
||||||
@ -498,9 +541,15 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem
|
|||||||
strongSelf.fieldButtonNode.isUserInteractionEnabled = item.invite != nil
|
strongSelf.fieldButtonNode.isUserInteractionEnabled = item.invite != nil
|
||||||
strongSelf.addressButtonIconNode.alpha = item.invite != nil ? 1.0 : 0.0
|
strongSelf.addressButtonIconNode.alpha = item.invite != nil ? 1.0 : 0.0
|
||||||
|
|
||||||
|
|
||||||
|
strongSelf.copyButtonNode?.isUserInteractionEnabled = item.invite != nil
|
||||||
|
strongSelf.copyButtonNode?.alpha = item.invite != nil ? 1.0 : 0.4
|
||||||
|
strongSelf.copyButtonNode?.isHidden = !item.displayButton || !effectiveSeparateButtons
|
||||||
|
|
||||||
strongSelf.shareButtonNode?.isUserInteractionEnabled = item.invite != nil
|
strongSelf.shareButtonNode?.isUserInteractionEnabled = item.invite != nil
|
||||||
strongSelf.shareButtonNode?.alpha = item.invite != nil ? 1.0 : 0.4
|
strongSelf.shareButtonNode?.alpha = item.invite != nil ? 1.0 : 0.4
|
||||||
strongSelf.shareButtonNode?.isHidden = !item.displayButton
|
strongSelf.shareButtonNode?.isHidden = !item.displayButton
|
||||||
|
|
||||||
strongSelf.avatarsButtonNode.isHidden = !item.displayImporters
|
strongSelf.avatarsButtonNode.isHidden = !item.displayImporters
|
||||||
strongSelf.avatarsNode.isHidden = !item.displayImporters || item.invite == nil
|
strongSelf.avatarsNode.isHidden = !item.displayImporters || item.invite == nil
|
||||||
strongSelf.invitedPeersNode.isHidden = !item.displayImporters || item.invite == nil
|
strongSelf.invitedPeersNode.isHidden = !item.displayImporters || item.invite == nil
|
||||||
|
@ -251,6 +251,7 @@ public enum ItemListPeerItemLabel {
|
|||||||
case text(String, ItemListPeerItemLabelFont)
|
case text(String, ItemListPeerItemLabelFont)
|
||||||
case disclosure(String)
|
case disclosure(String)
|
||||||
case badge(String)
|
case badge(String)
|
||||||
|
case attributedText(NSAttributedString)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ItemListPeerItemSwitch {
|
public struct ItemListPeerItemSwitch {
|
||||||
@ -728,7 +729,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
private var avatarButton: HighlightTrackingButton?
|
private var avatarButton: HighlightTrackingButton?
|
||||||
|
|
||||||
private let titleNode: TextNode
|
private let titleNode: TextNode
|
||||||
private let labelNode: TextNode
|
private let labelNode: TextNodeWithEntities
|
||||||
private let labelBadgeNode: ASImageNode
|
private let labelBadgeNode: ASImageNode
|
||||||
private var labelArrowNode: ASImageNode?
|
private var labelArrowNode: ASImageNode?
|
||||||
private let statusNode: TextNode
|
private let statusNode: TextNode
|
||||||
@ -829,10 +830,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
self.statusNode.contentMode = .left
|
self.statusNode.contentMode = .left
|
||||||
self.statusNode.contentsScale = UIScreen.main.scale
|
self.statusNode.contentsScale = UIScreen.main.scale
|
||||||
|
|
||||||
self.labelNode = TextNode()
|
self.labelNode = TextNodeWithEntities()
|
||||||
self.labelNode.isUserInteractionEnabled = false
|
|
||||||
self.labelNode.contentMode = .left
|
|
||||||
self.labelNode.contentsScale = UIScreen.main.scale
|
|
||||||
|
|
||||||
self.labelBadgeNode = ASImageNode()
|
self.labelBadgeNode = ASImageNode()
|
||||||
self.labelBadgeNode.displayWithoutProcessing = true
|
self.labelBadgeNode.displayWithoutProcessing = true
|
||||||
@ -850,7 +848,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
self.containerNode.addSubnode(self.avatarNode)
|
self.containerNode.addSubnode(self.avatarNode)
|
||||||
self.containerNode.addSubnode(self.titleNode)
|
self.containerNode.addSubnode(self.titleNode)
|
||||||
self.containerNode.addSubnode(self.statusNode)
|
self.containerNode.addSubnode(self.statusNode)
|
||||||
self.containerNode.addSubnode(self.labelNode)
|
self.containerNode.addSubnode(self.labelNode.textNode)
|
||||||
|
|
||||||
self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in
|
self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in
|
||||||
if let strongSelf = self, let layoutParams = strongSelf.layoutParams {
|
if let strongSelf = self, let layoutParams = strongSelf.layoutParams {
|
||||||
@ -885,7 +883,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
public func asyncLayout() -> (_ item: ItemListPeerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors, _ headerAtTop: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) {
|
public func asyncLayout() -> (_ item: ItemListPeerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors, _ headerAtTop: Bool) -> (ListViewItemNodeLayout, (Bool, Bool) -> Void) {
|
||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||||
let makeStatusLayout = TextNode.asyncLayout(self.statusNode)
|
let makeStatusLayout = TextNode.asyncLayout(self.statusNode)
|
||||||
let makeLabelLayout = TextNode.asyncLayout(self.labelNode)
|
let makeLabelLayout = TextNodeWithEntities.asyncLayout(self.labelNode)
|
||||||
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
let editableControlLayout = ItemListEditableControlNode.asyncLayout(self.editableControlNode)
|
||||||
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
|
let reorderControlLayout = ItemListEditableReorderControlNode.asyncLayout(self.reorderControlNode)
|
||||||
|
|
||||||
@ -1156,42 +1154,49 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
editingOffset = 0.0
|
editingOffset = 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var labelMaximumNumberOfLines = 1
|
||||||
var labelInset: CGFloat = 0.0
|
var labelInset: CGFloat = 0.0
|
||||||
|
var labelAlignment: NSTextAlignment = .natural
|
||||||
var updatedLabelArrowNode: ASImageNode?
|
var updatedLabelArrowNode: ASImageNode?
|
||||||
switch item.label {
|
switch item.label {
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
case let .text(text, font):
|
case let .attributedText(text):
|
||||||
let selectedFont: UIFont
|
labelAttributedString = text
|
||||||
switch font {
|
labelInset += 15.0
|
||||||
case .standard:
|
labelMaximumNumberOfLines = 2
|
||||||
selectedFont = labelFont
|
labelAlignment = .right
|
||||||
case let .custom(value):
|
case let .text(text, font):
|
||||||
selectedFont = value
|
let selectedFont: UIFont
|
||||||
}
|
switch font {
|
||||||
labelAttributedString = NSAttributedString(string: text, font: selectedFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
case .standard:
|
||||||
labelInset += 15.0
|
selectedFont = labelFont
|
||||||
case let .disclosure(text):
|
case let .custom(value):
|
||||||
if let currentLabelArrowNode = currentLabelArrowNode {
|
selectedFont = value
|
||||||
updatedLabelArrowNode = currentLabelArrowNode
|
}
|
||||||
} else {
|
labelAttributedString = NSAttributedString(string: text, font: selectedFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||||
let arrowNode = ASImageNode()
|
labelInset += 15.0
|
||||||
arrowNode.isLayerBacked = true
|
case let .disclosure(text):
|
||||||
arrowNode.displayWithoutProcessing = true
|
if let currentLabelArrowNode = currentLabelArrowNode {
|
||||||
arrowNode.displaysAsynchronously = false
|
updatedLabelArrowNode = currentLabelArrowNode
|
||||||
arrowNode.image = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme)
|
} else {
|
||||||
updatedLabelArrowNode = arrowNode
|
let arrowNode = ASImageNode()
|
||||||
}
|
arrowNode.isLayerBacked = true
|
||||||
labelInset += 40.0
|
arrowNode.displayWithoutProcessing = true
|
||||||
labelAttributedString = NSAttributedString(string: text, font: labelDisclosureFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
arrowNode.displaysAsynchronously = false
|
||||||
case let .badge(text):
|
arrowNode.image = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme)
|
||||||
labelAttributedString = NSAttributedString(string: text, font: badgeFont, textColor: item.presentationData.theme.list.itemCheckColors.foregroundColor)
|
updatedLabelArrowNode = arrowNode
|
||||||
labelInset += 15.0
|
}
|
||||||
|
labelInset += 40.0
|
||||||
|
labelAttributedString = NSAttributedString(string: text, font: labelDisclosureFont, textColor: item.presentationData.theme.list.itemSecondaryTextColor)
|
||||||
|
case let .badge(text):
|
||||||
|
labelAttributedString = NSAttributedString(string: text, font: badgeFont, textColor: item.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||||
|
labelInset += 15.0
|
||||||
}
|
}
|
||||||
|
|
||||||
labelInset += reorderInset
|
labelInset += reorderInset
|
||||||
|
|
||||||
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedString, backgroundColor: nil, maximumNumberOfLines: labelMaximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - 16.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: labelAlignment, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets()))
|
||||||
|
|
||||||
let constrainedTitleSize = CGSize(width: params.width - leftInset - 12.0 - editingOffset - rightInset - labelLayout.size.width - labelInset - titleIconsWidth, height: CGFloat.greatestFiniteMagnitude)
|
let constrainedTitleSize = CGSize(width: params.width - leftInset - 12.0 - editingOffset - rightInset - labelLayout.size.width - labelInset - titleIconsWidth, height: CGFloat.greatestFiniteMagnitude)
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedTitleSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedTitleSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||||
@ -1351,9 +1356,10 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
|
|
||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
let _ = statusApply()
|
let _ = statusApply()
|
||||||
let _ = labelApply()
|
if case let .account(context) = item.context {
|
||||||
|
let _ = labelApply(TextNodeWithEntities.Arguments(context: context, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, attemptSynchronous: false))
|
||||||
strongSelf.labelNode.isHidden = labelAttributedString == nil
|
}
|
||||||
|
strongSelf.labelNode.textNode.isHidden = labelAttributedString == nil
|
||||||
|
|
||||||
if strongSelf.backgroundNode.supernode == nil {
|
if strongSelf.backgroundNode.supernode == nil {
|
||||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||||
@ -1496,15 +1502,15 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
let labelFrame: CGRect
|
let labelFrame: CGRect
|
||||||
if case .badge = item.label {
|
if case .badge = item.label {
|
||||||
labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelLayout.size.width) / 2.0, y: floor((contentSize.height - labelLayout.size.height) / 2.0) + 1.0), size: labelLayout.size)
|
labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelLayout.size.width) / 2.0, y: floor((contentSize.height - labelLayout.size.height) / 2.0) + 1.0), size: labelLayout.size)
|
||||||
strongSelf.labelNode.frame = labelFrame
|
strongSelf.labelNode.textNode.frame = labelFrame
|
||||||
} else {
|
} else {
|
||||||
labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - labelLayout.size.width - rightLabelInset, y: floor((contentSize.height - labelLayout.size.height) / 2.0) + 1.0), size: labelLayout.size)
|
labelFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - labelLayout.size.width - rightLabelInset, y: floor((contentSize.height - labelLayout.size.height) / 2.0) + 1.0), size: labelLayout.size)
|
||||||
transition.updateFrame(node: strongSelf.labelNode, frame: labelFrame)
|
transition.updateFrame(node: strongSelf.labelNode.textNode, frame: labelFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let updateBadgeImage = updatedLabelBadgeImage {
|
if let updateBadgeImage = updatedLabelBadgeImage {
|
||||||
if strongSelf.labelBadgeNode.supernode == nil {
|
if strongSelf.labelBadgeNode.supernode == nil {
|
||||||
strongSelf.containerNode.insertSubnode(strongSelf.labelBadgeNode, belowSubnode: strongSelf.labelNode)
|
strongSelf.containerNode.insertSubnode(strongSelf.labelBadgeNode, belowSubnode: strongSelf.labelNode.textNode)
|
||||||
}
|
}
|
||||||
strongSelf.labelBadgeNode.image = updateBadgeImage
|
strongSelf.labelBadgeNode.image = updateBadgeImage
|
||||||
}
|
}
|
||||||
@ -1853,16 +1859,16 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
}
|
}
|
||||||
|
|
||||||
let badgeDiameter: CGFloat = 20.0
|
let badgeDiameter: CGFloat = 20.0
|
||||||
let labelSize = self.labelNode.frame.size
|
let labelSize = self.labelNode.textNode.frame.size
|
||||||
|
|
||||||
let badgeWidth = max(badgeDiameter, labelSize.width + 10.0)
|
let badgeWidth = max(badgeDiameter, labelSize.width + 10.0)
|
||||||
let labelFrame: CGRect
|
let labelFrame: CGRect
|
||||||
if case .badge = item.label {
|
if case .badge = item.label {
|
||||||
labelFrame = CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: self.labelNode.frame.minY), size: labelSize)
|
labelFrame = CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: self.labelNode.textNode.frame.minY), size: labelSize)
|
||||||
} else {
|
} else {
|
||||||
labelFrame = CGRect(origin: CGPoint(x: offset + params.width - self.labelNode.bounds.size.width - rightLabelInset, y: self.labelNode.frame.minY), size: self.labelNode.bounds.size)
|
labelFrame = CGRect(origin: CGPoint(x: offset + params.width - self.labelNode.textNode.bounds.size.width - rightLabelInset, y: self.labelNode.textNode.frame.minY), size: self.labelNode.textNode.bounds.size)
|
||||||
}
|
}
|
||||||
transition.updateFrame(node: self.labelNode, frame: labelFrame)
|
transition.updateFrame(node: self.labelNode.textNode, frame: labelFrame)
|
||||||
|
|
||||||
transition.updateFrame(node: self.labelBadgeNode, frame: CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth, y: self.labelBadgeNode.frame.minY), size: CGSize(width: badgeWidth, height: badgeDiameter)))
|
transition.updateFrame(node: self.labelBadgeNode, frame: CGRect(origin: CGPoint(x: offset + params.width - rightLabelInset - badgeWidth, y: self.labelBadgeNode.frame.minY), size: CGSize(width: badgeWidth, height: badgeDiameter)))
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
|||||||
let title: NSAttributedString
|
let title: NSAttributedString
|
||||||
let text: String
|
let text: String
|
||||||
let placeholder: String
|
let placeholder: String
|
||||||
|
let label: String?
|
||||||
let type: ItemListSingleLineInputItemType
|
let type: ItemListSingleLineInputItemType
|
||||||
let returnKeyType: UIReturnKeyType
|
let returnKeyType: UIReturnKeyType
|
||||||
let alignment: ItemListSingleLineInputAlignment
|
let alignment: ItemListSingleLineInputAlignment
|
||||||
@ -68,12 +69,13 @@ public class ItemListSingleLineInputItem: ListViewItem, ItemListItem {
|
|||||||
let cleared: (() -> Void)?
|
let cleared: (() -> Void)?
|
||||||
public let tag: ItemListItemTag?
|
public let tag: ItemListItemTag?
|
||||||
|
|
||||||
public init(context: AccountContext? = nil, presentationData: ItemListPresentationData, title: NSAttributedString, text: String, placeholder: String, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, alignment: ItemListSingleLineInputAlignment = .default, spacing: CGFloat = 0.0, clearType: ItemListSingleLineInputClearType = .none, maxLength: Int = 0, enabled: Bool = true, selectAllOnFocus: Bool = false, secondaryStyle: Bool = false, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void, cleared: (() -> Void)? = nil) {
|
public init(context: AccountContext? = nil, presentationData: ItemListPresentationData, title: NSAttributedString, text: String, placeholder: String, label: String? = nil, type: ItemListSingleLineInputItemType = .regular(capitalization: true, autocorrection: true), returnKeyType: UIReturnKeyType = .`default`, alignment: ItemListSingleLineInputAlignment = .default, spacing: CGFloat = 0.0, clearType: ItemListSingleLineInputClearType = .none, maxLength: Int = 0, enabled: Bool = true, selectAllOnFocus: Bool = false, secondaryStyle: Bool = false, tag: ItemListItemTag? = nil, sectionId: ItemListSectionId, textUpdated: @escaping (String) -> Void, shouldUpdateText: @escaping (String) -> Bool = { _ in return true }, processPaste: ((String) -> String)? = nil, updatedFocus: ((Bool) -> Void)? = nil, action: @escaping () -> Void, cleared: (() -> Void)? = nil) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.title = title
|
self.title = title
|
||||||
self.text = text
|
self.text = text
|
||||||
self.placeholder = placeholder
|
self.placeholder = placeholder
|
||||||
|
self.label = label
|
||||||
self.type = type
|
self.type = type
|
||||||
self.returnKeyType = returnKeyType
|
self.returnKeyType = returnKeyType
|
||||||
self.alignment = alignment
|
self.alignment = alignment
|
||||||
|
@ -239,6 +239,7 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
itemSubtitle = peer.displayTitle(strings: item.presentationData.strings, displayOrder: .firstLast)
|
itemSubtitle = peer.displayTitle(strings: item.presentationData.strings, displayOrder: .firstLast)
|
||||||
} else {
|
} else {
|
||||||
if let _ = item.transaction.subscriptionPeriod {
|
if let _ = item.transaction.subscriptionPeriod {
|
||||||
|
//TODO:localize
|
||||||
itemSubtitle = "Monthly subscription fee"
|
itemSubtitle = "Monthly subscription fee"
|
||||||
} else {
|
} else {
|
||||||
itemSubtitle = nil
|
itemSubtitle = nil
|
||||||
@ -276,9 +277,15 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
}
|
}
|
||||||
itemLabel = NSAttributedString(string: labelString, font: Font.medium(fontBaseDisplaySize), textColor: labelString.hasPrefix("-") ? item.presentationData.theme.list.itemDestructiveColor : item.presentationData.theme.list.itemDisclosureActions.constructive.fillColor)
|
itemLabel = NSAttributedString(string: labelString, font: Font.medium(fontBaseDisplaySize), textColor: labelString.hasPrefix("-") ? item.presentationData.theme.list.itemDestructiveColor : item.presentationData.theme.list.itemDisclosureActions.constructive.fillColor)
|
||||||
|
|
||||||
|
var itemDateColor = item.presentationData.theme.list.itemSecondaryTextColor
|
||||||
itemDate = stringForMediumCompactDate(timestamp: item.transaction.date, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat)
|
itemDate = stringForMediumCompactDate(timestamp: item.transaction.date, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat)
|
||||||
if item.transaction.flags.contains(.isRefund) {
|
if item.transaction.flags.contains(.isRefund) {
|
||||||
itemDate += " – \(item.presentationData.strings.Stars_Intro_Transaction_Refund)"
|
itemDate += " – \(item.presentationData.strings.Stars_Intro_Transaction_Refund)"
|
||||||
|
} else if item.transaction.flags.contains(.isPending) {
|
||||||
|
itemDate += " – \(item.presentationData.strings.Monetization_Transaction_Pending)"
|
||||||
|
} else if item.transaction.flags.contains(.isFailed) {
|
||||||
|
itemDate += " – \(item.presentationData.strings.Monetization_Transaction_Failed)"
|
||||||
|
itemDateColor = item.presentationData.theme.list.itemDestructiveColor
|
||||||
}
|
}
|
||||||
|
|
||||||
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
|
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
|
||||||
@ -309,7 +316,7 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: itemDate,
|
string: itemDate,
|
||||||
font: Font.regular(floor(fontBaseDisplaySize * 14.0 / 17.0)),
|
font: Font.regular(floor(fontBaseDisplaySize * 14.0 / 17.0)),
|
||||||
textColor: item.presentationData.theme.list.itemSecondaryTextColor
|
textColor: itemDateColor
|
||||||
)),
|
)),
|
||||||
maximumNumberOfLines: 1
|
maximumNumberOfLines: 1
|
||||||
)))
|
)))
|
||||||
|
@ -1254,7 +1254,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
|||||||
} else {
|
} else {
|
||||||
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||||
}
|
}
|
||||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(participant.peer), text: text, action: nil, duration: 3), action: { _ in return false })
|
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: EnginePeer(participant.peer), title: nil, text: text, action: nil, duration: 3), action: { _ in return false })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if case let .channel(groupPeer) = groupPeer, let listenerLink = inviteLinks?.listenerLink, !groupPeer.hasPermission(.inviteMembers) {
|
if case let .channel(groupPeer) = groupPeer, let listenerLink = inviteLinks?.listenerLink, !groupPeer.hasPermission(.inviteMembers) {
|
||||||
@ -1362,7 +1362,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
|||||||
} else {
|
} else {
|
||||||
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||||
}
|
}
|
||||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil, duration: 3), action: { _ in return false })
|
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false })
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
} else if case let .legacyGroup(groupPeer) = groupPeer {
|
} else if case let .legacyGroup(groupPeer) = groupPeer {
|
||||||
@ -1430,7 +1430,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
|||||||
} else {
|
} else {
|
||||||
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
text = strongSelf.presentationData.strings.VoiceChat_InvitedPeerText(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||||
}
|
}
|
||||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil, duration: 3), action: { _ in return false })
|
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false })
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -2262,7 +2262,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let text = strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(event.peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
let text = strongSelf.presentationData.strings.VoiceChat_PeerJoinedText(event.peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: event.peer, text: text, action: nil, duration: 3), action: { _ in return false })
|
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: event.peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false })
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -2277,7 +2277,7 @@ public final class VoiceChatControllerImpl: ViewController, VoiceChatController
|
|||||||
} else {
|
} else {
|
||||||
text = strongSelf.presentationData.strings.VoiceChat_DisplayAsSuccess(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
text = strongSelf.presentationData.strings.VoiceChat_DisplayAsSuccess(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).string
|
||||||
}
|
}
|
||||||
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: text, action: nil, duration: 3), action: { _ in return false })
|
strongSelf.presentUndoOverlay(content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: text, action: nil, duration: 3), action: { _ in return false })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
self.stateVersionDisposable.set((self.call.stateVersion
|
self.stateVersionDisposable.set((self.call.stateVersion
|
||||||
|
@ -956,10 +956,6 @@ private final class StarsSubscriptionsContextImpl {
|
|||||||
updatedState.isLoading = false
|
updatedState.isLoading = false
|
||||||
updatedState.canLoadMore = self.nextOffset != nil
|
updatedState.canLoadMore = self.nextOffset != nil
|
||||||
self.updateState(updatedState)
|
self.updateState(updatedState)
|
||||||
|
|
||||||
if updatedState.canLoadMore {
|
|
||||||
self.loadMore()
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,6 +963,22 @@ private final class StarsSubscriptionsContextImpl {
|
|||||||
self._state = state
|
self._state = state
|
||||||
self._statePromise.set(.single(state))
|
self._statePromise.set(.single(state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateSubscription(id: String, cancel: Bool) {
|
||||||
|
var updatedState = self._state
|
||||||
|
if let index = updatedState.subscriptions.firstIndex(where: { $0.id == id }) {
|
||||||
|
let subscription = updatedState.subscriptions[index]
|
||||||
|
var updatedFlags = subscription.flags
|
||||||
|
if cancel {
|
||||||
|
updatedFlags.insert(.isCancelled)
|
||||||
|
} else {
|
||||||
|
updatedFlags.remove(.isCancelled)
|
||||||
|
}
|
||||||
|
let updatedSubscription = StarsContext.State.Subscription(flags: updatedFlags, id: subscription.id, peer: subscription.peer, untilDate: subscription.untilDate, pricing: subscription.pricing)
|
||||||
|
updatedState.subscriptions[index] = updatedSubscription
|
||||||
|
}
|
||||||
|
self.updateState(updatedState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class StarsSubscriptionsContext {
|
public final class StarsSubscriptionsContext {
|
||||||
@ -1007,6 +1019,12 @@ public final class StarsSubscriptionsContext {
|
|||||||
return StarsSubscriptionsContextImpl(account: account, starsContext: starsContext)
|
return StarsSubscriptionsContextImpl(account: account, starsContext: starsContext)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateSubscription(id: String, cancel: Bool) {
|
||||||
|
self.impl.with {
|
||||||
|
$0.updateSubscription(id: id, cancel: cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1203,7 +1221,7 @@ func _internal_updateStarsSubscription(account: Account, peerId: EnginePeer.Id,
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
let flags: Int32 = (1 << 0)
|
let flags: Int32 = (1 << 0)
|
||||||
return account.network.request(Api.functions.payments.changeStarsSubscription(flags: flags, peer: inputPeer, subscriptionId: subscriptionId, canceled: .boolTrue))
|
return account.network.request(Api.functions.payments.changeStarsSubscription(flags: flags, peer: inputPeer, subscriptionId: subscriptionId, canceled: cancel ? .boolTrue : .boolFalse))
|
||||||
|> mapError { _ -> UpdateStarsSubsciptionError in
|
|> mapError { _ -> UpdateStarsSubsciptionError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|
@ -510,6 +510,10 @@ public extension EnginePeer {
|
|||||||
var isPremium: Bool {
|
var isPremium: Bool {
|
||||||
return self._asPeer().isPremium
|
return self._asPeer().isPremium
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSubscription: Bool {
|
||||||
|
return self._asPeer().isSubscription
|
||||||
|
}
|
||||||
|
|
||||||
var isService: Bool {
|
var isService: Bool {
|
||||||
if case let .user(peer) = self {
|
if case let .user(peer) = self {
|
||||||
|
@ -191,6 +191,15 @@ public extension Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSubscription: Bool {
|
||||||
|
switch self {
|
||||||
|
case let channel as TelegramChannel:
|
||||||
|
return channel.subscriptionUntilDate != nil
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var isCloseFriend: Bool {
|
var isCloseFriend: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case let user as TelegramUser:
|
case let user as TelegramUser:
|
||||||
|
@ -905,7 +905,7 @@ private let starImage: UIImage? = {
|
|||||||
context.clear(CGRect(origin: .zero, size: size))
|
context.clear(CGRect(origin: .zero, size: size))
|
||||||
|
|
||||||
if let image = UIImage(bundleImageName: "Premium/Stars/StarLarge"), let cgImage = image.cgImage {
|
if let image = UIImage(bundleImageName: "Premium/Stars/StarLarge"), let cgImage = image.cgImage {
|
||||||
context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 4.0, dy: 4.0), byTiling: false)
|
context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 1.0, dy: 1.0), byTiling: false)
|
||||||
}
|
}
|
||||||
})?.withRenderingMode(.alwaysTemplate)
|
})?.withRenderingMode(.alwaysTemplate)
|
||||||
}()
|
}()
|
||||||
|
@ -9232,7 +9232,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
case .fallback:
|
case .fallback:
|
||||||
(strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicPhotoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
(strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicPhotoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
case .custom:
|
case .custom:
|
||||||
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessPhotoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessPhotoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
|
|
||||||
let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone()
|
let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone()
|
||||||
case .suggest:
|
case .suggest:
|
||||||
@ -9469,7 +9469,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
case .fallback:
|
case .fallback:
|
||||||
(strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicVideoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
(strongSelf.controller?.parentController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .image(image: image, title: nil, text: strongSelf.presentationData.strings.Privacy_ProfilePhoto_PublicVideoSuccess, round: true, undoText: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
case .custom:
|
case .custom:
|
||||||
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_SuccessVideoText(peer.compactDisplayTitle).string, action: nil, duration: 5), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||||
|
|
||||||
let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone()
|
let _ = (strongSelf.context.peerChannelMemberCategoriesContextsManager.profilePhotos(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, peerId: strongSelf.peerId, fetch: peerInfoProfilePhotos(context: strongSelf.context, peerId: strongSelf.peerId)) |> ignoreValues).startStandalone()
|
||||||
case .suggest:
|
case .suggest:
|
||||||
|
@ -828,22 +828,35 @@ public final class StarsImageComponent: Component {
|
|||||||
|
|
||||||
if let _ = component.icon {
|
if let _ = component.icon {
|
||||||
let smallIconView: UIImageView
|
let smallIconView: UIImageView
|
||||||
if let current = self.smallIconView {
|
let smallIconOutlineView: UIImageView
|
||||||
|
if let current = self.smallIconView, let currentOutline = self.smallIconOutlineView {
|
||||||
smallIconView = current
|
smallIconView = current
|
||||||
|
smallIconOutlineView = currentOutline
|
||||||
} else {
|
} else {
|
||||||
|
smallIconOutlineView = UIImageView()
|
||||||
|
containerNode.view.addSubview(smallIconOutlineView)
|
||||||
|
self.smallIconOutlineView = smallIconOutlineView
|
||||||
|
|
||||||
smallIconView = UIImageView()
|
smallIconView = UIImageView()
|
||||||
containerNode.view.addSubview(smallIconView)
|
containerNode.view.addSubview(smallIconView)
|
||||||
|
self.smallIconView = smallIconView
|
||||||
|
|
||||||
|
smallIconOutlineView.image = UIImage(bundleImageName: "Premium/Stars/TransactionStarOutline")?.withRenderingMode(.alwaysTemplate)
|
||||||
|
smallIconView.image = UIImage(bundleImageName: "Premium/Stars/TransactionStar")
|
||||||
}
|
}
|
||||||
|
|
||||||
smallIconView.image = UIImage(bundleImageName: "Premium/Stars/MockBigStar")
|
smallIconOutlineView.tintColor = component.backgroundColor
|
||||||
|
|
||||||
if let icon = smallIconView.image {
|
if let icon = smallIconView.image {
|
||||||
let smallIconFrame = CGRect(origin: CGPoint(x: imageFrame.maxX - icon.size.width, y: imageFrame.maxY - icon.size.height), size: icon.size)
|
let smallIconFrame = CGRect(origin: CGPoint(x: imageFrame.maxX - icon.size.width, y: imageFrame.maxY - icon.size.height), size: icon.size)
|
||||||
smallIconView.frame = smallIconFrame
|
smallIconView.frame = smallIconFrame
|
||||||
|
smallIconOutlineView.frame = smallIconFrame
|
||||||
}
|
}
|
||||||
} else if let smallIconView = self.smallIconView {
|
} else if let smallIconView = self.smallIconView, let smallIconOutlineView = self.smallIconOutlineView {
|
||||||
self.smallIconView = nil
|
self.smallIconView = nil
|
||||||
smallIconView.removeFromSuperview()
|
smallIconView.removeFromSuperview()
|
||||||
|
self.smallIconOutlineView = nil
|
||||||
|
smallIconOutlineView.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = component.action {
|
if let _ = component.action {
|
||||||
|
@ -36,6 +36,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
|
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
|
||||||
let openAppExamples: () -> Void
|
let openAppExamples: () -> Void
|
||||||
let copyTransactionId: (String) -> Void
|
let copyTransactionId: (String) -> Void
|
||||||
|
let updateSubscription: (StarsTransactionScreen.SubscriptionAction) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -45,7 +46,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||||
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
||||||
openAppExamples: @escaping () -> Void,
|
openAppExamples: @escaping () -> Void,
|
||||||
copyTransactionId: @escaping (String) -> Void
|
copyTransactionId: @escaping (String) -> Void,
|
||||||
|
updateSubscription: @escaping (StarsTransactionScreen.SubscriptionAction) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
@ -55,6 +57,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
self.openMedia = openMedia
|
self.openMedia = openMedia
|
||||||
self.openAppExamples = openAppExamples
|
self.openAppExamples = openAppExamples
|
||||||
self.copyTransactionId = copyTransactionId
|
self.copyTransactionId = copyTransactionId
|
||||||
|
self.updateSubscription = updateSubscription
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
||||||
@ -96,6 +99,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
peerIds.append(message.id.peerId)
|
peerIds.append(message.id.peerId)
|
||||||
case let .subscription(subscription):
|
case let .subscription(subscription):
|
||||||
peerIds.append(subscription.peer.id)
|
peerIds.append(subscription.peer.id)
|
||||||
|
case let .importer(_, _, importer, _):
|
||||||
|
peerIds.append(importer.peer.peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.disposable = (context.engine.data.get(
|
self.disposable = (context.engine.data.get(
|
||||||
@ -138,10 +143,11 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let description = Child(MultilineTextComponent.self)
|
let description = Child(MultilineTextComponent.self)
|
||||||
let table = Child(TableComponent.self)
|
let table = Child(TableComponent.self)
|
||||||
let additional = Child(BalancedTextComponent.self)
|
let additional = Child(BalancedTextComponent.self)
|
||||||
|
let status = Child(BalancedTextComponent.self)
|
||||||
let button = Child(SolidRoundedButtonComponent.self)
|
let button = Child(SolidRoundedButtonComponent.self)
|
||||||
|
|
||||||
let refundBackgound = Child(RoundedRectangle.self)
|
let transactionStatusBackgound = Child(RoundedRectangle.self)
|
||||||
let refundText = Child(MultilineTextComponent.self)
|
let transactionStatusText = Child(MultilineTextComponent.self)
|
||||||
|
|
||||||
let spaceRegex = try? NSRegularExpression(pattern: "\\[(.*?)\\]", options: [])
|
let spaceRegex = try? NSRegularExpression(pattern: "\\[(.*?)\\]", options: [])
|
||||||
|
|
||||||
@ -182,8 +188,11 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let titleText: String
|
let titleText: String
|
||||||
let amountText: String
|
let amountText: String
|
||||||
var descriptionText: String
|
var descriptionText: String
|
||||||
let additionalText: String
|
let additionalText = strings.Stars_Transaction_Terms
|
||||||
let buttonText: String
|
var buttonText: String? = strings.Common_OK
|
||||||
|
var buttonIsDestructive = false
|
||||||
|
var statusText: String?
|
||||||
|
var statusIsDestructive = false
|
||||||
|
|
||||||
let count: Int64
|
let count: Int64
|
||||||
var countIsGeneric = false
|
var countIsGeneric = false
|
||||||
@ -196,13 +205,28 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let transactionPeer: StarsContext.State.Transaction.Peer?
|
let transactionPeer: StarsContext.State.Transaction.Peer?
|
||||||
var media: [AnyMediaReference] = []
|
var media: [AnyMediaReference] = []
|
||||||
var photo: TelegramMediaWebFile?
|
var photo: TelegramMediaWebFile?
|
||||||
var isRefund = false
|
var transactionStatus: (String, UIColor)? = nil
|
||||||
var isGift = false
|
var isGift = false
|
||||||
var isSubscription = false
|
var isSubscription = false
|
||||||
|
var isSubscriber = false
|
||||||
var isSubscriptionFee = false
|
var isSubscriptionFee = false
|
||||||
|
var isCancelled = false
|
||||||
|
|
||||||
var delayedCloseOnOpenPeer = true
|
var delayedCloseOnOpenPeer = true
|
||||||
switch subject {
|
switch subject {
|
||||||
|
case let .importer(peer, pricing, importer, usdRate):
|
||||||
|
let usdValue = formatTonUsdValue(pricing.amount, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat)
|
||||||
|
titleText = "Subscription"
|
||||||
|
descriptionText = "appx. \(usdValue) per month"
|
||||||
|
count = pricing.amount
|
||||||
|
countOnTop = true
|
||||||
|
transactionId = nil
|
||||||
|
date = importer.date
|
||||||
|
via = nil
|
||||||
|
messageId = nil
|
||||||
|
toPeer = importer.peer.peer.flatMap(EnginePeer.init)
|
||||||
|
transactionPeer = .peer(peer)
|
||||||
|
isSubscriber = true
|
||||||
case let .subscription(subscription):
|
case let .subscription(subscription):
|
||||||
titleText = "Subscription"
|
titleText = "Subscription"
|
||||||
descriptionText = ""
|
descriptionText = ""
|
||||||
@ -214,6 +238,17 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
toPeer = subscription.peer
|
toPeer = subscription.peer
|
||||||
transactionPeer = .peer(subscription.peer)
|
transactionPeer = .peer(subscription.peer)
|
||||||
isSubscription = true
|
isSubscription = true
|
||||||
|
|
||||||
|
if subscription.flags.contains(.isCancelled) {
|
||||||
|
statusText = "You have cancelled your subscription"
|
||||||
|
statusIsDestructive = true
|
||||||
|
buttonText = "Renew Subscription"
|
||||||
|
isCancelled = true
|
||||||
|
} else {
|
||||||
|
statusText = "If you cancel now, you can still access your subscription until \(stringForMediumDate(timestamp: subscription.untilDate, strings: strings, dateTimeFormat: dateTimeFormat, withTime: false))"
|
||||||
|
buttonText = "Cancel Subscription"
|
||||||
|
buttonIsDestructive = true
|
||||||
|
}
|
||||||
case let .transaction(transaction, parentPeer):
|
case let .transaction(transaction, parentPeer):
|
||||||
if let _ = transaction.subscriptionPeriod {
|
if let _ = transaction.subscriptionPeriod {
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
@ -325,7 +360,12 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
transactionPeer = transaction.peer
|
transactionPeer = transaction.peer
|
||||||
media = transaction.media.map { AnyMediaReference.starsTransaction(transaction: StarsTransactionReference(peerId: parentPeer.id, id: transaction.id, isRefund: transaction.flags.contains(.isRefund)), media: $0) }
|
media = transaction.media.map { AnyMediaReference.starsTransaction(transaction: StarsTransactionReference(peerId: parentPeer.id, id: transaction.id, isRefund: transaction.flags.contains(.isRefund)), media: $0) }
|
||||||
photo = transaction.photo
|
photo = transaction.photo
|
||||||
isRefund = transaction.flags.contains(.isRefund)
|
|
||||||
|
if transaction.flags.contains(.isRefund) {
|
||||||
|
transactionStatus = (strings.Stars_Transaction_Refund, theme.list.itemDisclosureActions.constructive.fillColor)
|
||||||
|
} else if transaction.flags.contains(.isPending) {
|
||||||
|
transactionStatus = (strings.Monetization_Transaction_Pending, theme.list.itemDisclosureActions.warning.fillColor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case let .receipt(receipt):
|
case let .receipt(receipt):
|
||||||
titleText = receipt.invoiceMedia.title
|
titleText = receipt.invoiceMedia.title
|
||||||
@ -386,7 +426,10 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
let formattedAmount = presentationStringsFormattedNumber(abs(Int32(count)), dateTimeFormat.groupingSeparator)
|
let formattedAmount = presentationStringsFormattedNumber(abs(Int32(count)), dateTimeFormat.groupingSeparator)
|
||||||
let countColor: UIColor
|
let countColor: UIColor
|
||||||
if countIsGeneric {
|
if isSubscription || isSubscriber {
|
||||||
|
amountText = "\(formattedAmount) / month"
|
||||||
|
countColor = theme.list.itemSecondaryTextColor
|
||||||
|
} else if countIsGeneric {
|
||||||
amountText = "\(formattedAmount)"
|
amountText = "\(formattedAmount)"
|
||||||
countColor = theme.list.itemPrimaryTextColor
|
countColor = theme.list.itemPrimaryTextColor
|
||||||
} else if count < 0 {
|
} else if count < 0 {
|
||||||
@ -396,8 +439,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
amountText = "+ \(formattedAmount)"
|
amountText = "+ \(formattedAmount)"
|
||||||
countColor = theme.list.itemDisclosureActions.constructive.fillColor
|
countColor = theme.list.itemDisclosureActions.constructive.fillColor
|
||||||
}
|
}
|
||||||
additionalText = strings.Stars_Transaction_Terms
|
|
||||||
buttonText = strings.Common_OK
|
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
@ -429,7 +470,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
} else {
|
} else {
|
||||||
imageSubject = .none
|
imageSubject = .none
|
||||||
}
|
}
|
||||||
if isSubscription || isSubscriptionFee {
|
if isSubscription || isSubscriber || isSubscriptionFee {
|
||||||
imageIcon = .star
|
imageIcon = .star
|
||||||
} else {
|
} else {
|
||||||
imageIcon = nil
|
imageIcon = nil
|
||||||
@ -450,7 +491,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
|
|
||||||
let amountAttributedText = NSMutableAttributedString(string: amountText, font: Font.semibold(17.0), textColor: countColor)
|
let amountAttributedText = NSMutableAttributedString(string: amountText, font: isSubscription || isSubscriber ? Font.regular(17.0) : Font.semibold(17.0), textColor: countColor)
|
||||||
let amount = amount.update(
|
let amount = amount.update(
|
||||||
component: BalancedTextComponent(
|
component: BalancedTextComponent(
|
||||||
text: .plain(amountAttributedText),
|
text: .plain(amountAttributedText),
|
||||||
@ -500,9 +541,17 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
))
|
))
|
||||||
} else if let toPeer {
|
} else if let toPeer {
|
||||||
|
let title: String
|
||||||
|
if isSubscription {
|
||||||
|
title = "Subscription"
|
||||||
|
} else if isSubscriber {
|
||||||
|
title = "Subscriber"
|
||||||
|
} else {
|
||||||
|
title = count < 0 || countIsGeneric ? strings.Stars_Transaction_To : strings.Stars_Transaction_From
|
||||||
|
}
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "to",
|
id: "to",
|
||||||
title: count < 0 || countIsGeneric ? strings.Stars_Transaction_To : strings.Stars_Transaction_From,
|
title: title,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
Button(
|
Button(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
@ -594,9 +643,25 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dateTitle: String
|
||||||
|
if isSubscription {
|
||||||
|
if isCancelled {
|
||||||
|
if date > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
|
||||||
|
dateTitle = "Expires"
|
||||||
|
} else {
|
||||||
|
dateTitle = "Expired"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dateTitle = "Renews"
|
||||||
|
}
|
||||||
|
} else if isSubscriber {
|
||||||
|
dateTitle = "Subscribed"
|
||||||
|
} else {
|
||||||
|
dateTitle = strings.Stars_Transaction_Date
|
||||||
|
}
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "date",
|
id: "date",
|
||||||
title: strings.Stars_Transaction_Date,
|
title: dateTitle,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: date, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: date, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
||||||
)
|
)
|
||||||
@ -615,6 +680,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let boldTextFont = Font.semibold(15.0)
|
let boldTextFont = Font.semibold(15.0)
|
||||||
let textColor = theme.actionSheet.secondaryTextColor
|
let textColor = theme.actionSheet.secondaryTextColor
|
||||||
let linkColor = theme.actionSheet.controlAccentColor
|
let linkColor = theme.actionSheet.controlAccentColor
|
||||||
|
let destructiveColor = theme.actionSheet.destructiveActionTextColor
|
||||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})
|
})
|
||||||
@ -623,7 +689,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
text: .markdown(text: additionalText, attributes: markdownAttributes),
|
text: .markdown(text: additionalText, attributes: markdownAttributes),
|
||||||
horizontalAlignment: .center,
|
horizontalAlignment: .center,
|
||||||
maximumNumberOfLines: 0,
|
maximumNumberOfLines: 0,
|
||||||
lineSpacing: 0.1,
|
lineSpacing: 0.2,
|
||||||
highlightColor: linkColor.withAlphaComponent(0.2),
|
highlightColor: linkColor.withAlphaComponent(0.2),
|
||||||
highlightAction: { attributes in
|
highlightAction: { attributes in
|
||||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||||
@ -643,28 +709,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
|
|
||||||
let button = button.update(
|
|
||||||
component: SolidRoundedButtonComponent(
|
|
||||||
title: buttonText,
|
|
||||||
theme: SolidRoundedButtonComponent.Theme(theme: theme),
|
|
||||||
font: .bold,
|
|
||||||
fontSize: 17.0,
|
|
||||||
height: 50.0,
|
|
||||||
cornerRadius: 10.0,
|
|
||||||
gloss: false,
|
|
||||||
iconName: nil,
|
|
||||||
animationName: nil,
|
|
||||||
iconPosition: .left,
|
|
||||||
isLoading: state.inProgress,
|
|
||||||
action: {
|
|
||||||
component.cancel(true)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
|
||||||
transition: context.transition
|
|
||||||
)
|
|
||||||
|
|
||||||
context.add(title
|
context.add(title
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 31.0 + 125.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: 31.0 + 125.0))
|
||||||
)
|
)
|
||||||
@ -684,8 +729,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
|
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
let textFont = Font.regular(15.0)
|
let textColor = countOnTop && !isSubscriber ? theme.list.itemPrimaryTextColor : textColor
|
||||||
let textColor = countOnTop ? theme.list.itemPrimaryTextColor : textColor
|
|
||||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})
|
})
|
||||||
@ -728,21 +772,21 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let amountSpacing: CGFloat = 1.0
|
let amountSpacing: CGFloat = 1.0
|
||||||
var totalAmountWidth: CGFloat = amount.size.width + amountSpacing + amountStar.size.width
|
var totalAmountWidth: CGFloat = amount.size.width + amountSpacing + amountStar.size.width
|
||||||
var amountOriginX: CGFloat = floor(context.availableSize.width - totalAmountWidth) / 2.0
|
var amountOriginX: CGFloat = floor(context.availableSize.width - totalAmountWidth) / 2.0
|
||||||
if isRefund {
|
if let (statusText, statusColor) = transactionStatus {
|
||||||
let refundText = refundText.update(
|
let refundText = transactionStatusText.update(
|
||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: strings.Stars_Transaction_Refund,
|
string: statusText,
|
||||||
font: Font.medium(14.0),
|
font: Font.medium(14.0),
|
||||||
textColor: theme.list.itemDisclosureActions.constructive.fillColor
|
textColor: statusColor
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
availableSize: context.availableSize,
|
availableSize: context.availableSize,
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
let refundBackground = refundBackgound.update(
|
let refundBackground = transactionStatusBackgound.update(
|
||||||
component: RoundedRectangle(
|
component: RoundedRectangle(
|
||||||
color: theme.list.itemDisclosureActions.constructive.fillColor.withAlphaComponent(0.1),
|
color: statusColor.withAlphaComponent(0.1),
|
||||||
cornerRadius: 6.0
|
cornerRadius: 6.0
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: refundText.size.width + 10.0, height: refundText.size.height + 4.0),
|
availableSize: CGSize(width: refundText.size.width + 10.0, height: refundText.size.height + 4.0),
|
||||||
@ -766,11 +810,22 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
} else {
|
} else {
|
||||||
originY += amount.size.height + 20.0
|
originY += amount.size.height + 20.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let amountLabelOriginX: CGFloat
|
||||||
|
let amountStarOriginX: CGFloat
|
||||||
|
if isSubscription || isSubscriber {
|
||||||
|
amountStarOriginX = amountOriginX + amountStar.size.width / 2.0
|
||||||
|
amountLabelOriginX = amountOriginX + amountStar.size.width + amountSpacing + amount.size.width / 2.0
|
||||||
|
} else {
|
||||||
|
amountLabelOriginX = amountOriginX + amount.size.width / 2.0
|
||||||
|
amountStarOriginX = amountOriginX + amount.size.width + amountSpacing + amountStar.size.width / 2.0
|
||||||
|
}
|
||||||
|
|
||||||
context.add(amount
|
context.add(amount
|
||||||
.position(CGPoint(x: amountOriginX + amount.size.width / 2.0, y: amountOrigin + amount.size.height / 2.0))
|
.position(CGPoint(x: amountLabelOriginX, y: amountOrigin + amount.size.height / 2.0))
|
||||||
)
|
)
|
||||||
context.add(amountStar
|
context.add(amountStar
|
||||||
.position(CGPoint(x: amountOriginX + amount.size.width + amountSpacing + amountStar.size.width / 2.0, y: amountOrigin + amountStar.size.height / 2.0))
|
.position(CGPoint(x: amountStarOriginX, y: amountOrigin + amountStar.size.height / 2.0 - UIScreenPixel))
|
||||||
)
|
)
|
||||||
|
|
||||||
context.add(table
|
context.add(table
|
||||||
@ -783,16 +838,66 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
originY += additional.size.height + 23.0
|
originY += additional.size.height + 23.0
|
||||||
|
|
||||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: button.size)
|
if let statusText {
|
||||||
context.add(button
|
originY += 7.0
|
||||||
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
|
let status = status.update(
|
||||||
)
|
component: BalancedTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: statusText, font: textFont, textColor: statusIsDestructive ? destructiveColor : textColor)),
|
||||||
|
horizontalAlignment: .center,
|
||||||
|
maximumNumberOfLines: 0,
|
||||||
|
lineSpacing: 0.1
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
context.add(status
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + status.size.height / 2.0))
|
||||||
|
)
|
||||||
|
originY += status.size.height + (statusIsDestructive ? 23.0 : 13.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let buttonText {
|
||||||
|
let button = button.update(
|
||||||
|
component: SolidRoundedButtonComponent(
|
||||||
|
title: buttonText,
|
||||||
|
theme: buttonIsDestructive ? SolidRoundedButtonComponent.Theme(backgroundColor: .clear, foregroundColor: destructiveColor) : SolidRoundedButtonComponent.Theme(theme: theme),
|
||||||
|
font: buttonIsDestructive ? .regular : .bold,
|
||||||
|
fontSize: 17.0,
|
||||||
|
height: 50.0,
|
||||||
|
cornerRadius: 10.0,
|
||||||
|
gloss: false,
|
||||||
|
iconName: nil,
|
||||||
|
animationName: nil,
|
||||||
|
iconPosition: .left,
|
||||||
|
isLoading: state.inProgress,
|
||||||
|
action: {
|
||||||
|
component.cancel(true)
|
||||||
|
|
||||||
|
if isSubscription {
|
||||||
|
if buttonIsDestructive {
|
||||||
|
component.updateSubscription(.cancel)
|
||||||
|
} else {
|
||||||
|
component.updateSubscription(.renew)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
|
||||||
|
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: button.size)
|
||||||
|
context.add(button
|
||||||
|
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
|
||||||
|
)
|
||||||
|
originY += button.size.height
|
||||||
|
}
|
||||||
|
|
||||||
context.add(closeButton
|
context.add(closeButton
|
||||||
.position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0))
|
.position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0))
|
||||||
)
|
)
|
||||||
|
|
||||||
let contentSize = CGSize(width: context.availableSize.width, height: buttonFrame.maxY + 5.0 + environment.safeInsets.bottom)
|
let contentSize = CGSize(width: context.availableSize.width, height: originY + 5.0 + environment.safeInsets.bottom)
|
||||||
|
|
||||||
return contentSize
|
return contentSize
|
||||||
}
|
}
|
||||||
@ -809,6 +914,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
|
let openMedia: ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void
|
||||||
let openAppExamples: () -> Void
|
let openAppExamples: () -> Void
|
||||||
let copyTransactionId: (String) -> Void
|
let copyTransactionId: (String) -> Void
|
||||||
|
let updateSubscription: (StarsTransactionScreen.SubscriptionAction) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -817,7 +923,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||||
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
||||||
openAppExamples: @escaping () -> Void,
|
openAppExamples: @escaping () -> Void,
|
||||||
copyTransactionId: @escaping (String) -> Void
|
copyTransactionId: @escaping (String) -> Void,
|
||||||
|
updateSubscription: @escaping (StarsTransactionScreen.SubscriptionAction) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
@ -826,6 +933,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
self.openMedia = openMedia
|
self.openMedia = openMedia
|
||||||
self.openAppExamples = openAppExamples
|
self.openAppExamples = openAppExamples
|
||||||
self.copyTransactionId = copyTransactionId
|
self.copyTransactionId = copyTransactionId
|
||||||
|
self.updateSubscription = updateSubscription
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
||||||
@ -869,7 +977,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
openMessage: context.component.openMessage,
|
openMessage: context.component.openMessage,
|
||||||
openMedia: context.component.openMedia,
|
openMedia: context.component.openMedia,
|
||||||
openAppExamples: context.component.openAppExamples,
|
openAppExamples: context.component.openAppExamples,
|
||||||
copyTransactionId: context.component.copyTransactionId
|
copyTransactionId: context.component.copyTransactionId,
|
||||||
|
updateSubscription: context.component.updateSubscription
|
||||||
)),
|
)),
|
||||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||||
followContentSizeChanges: true,
|
followContentSizeChanges: true,
|
||||||
@ -936,11 +1045,17 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class StarsTransactionScreen: ViewControllerComponentContainer {
|
public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||||
|
enum SubscriptionAction {
|
||||||
|
case cancel
|
||||||
|
case renew
|
||||||
|
}
|
||||||
|
|
||||||
public enum Subject: Equatable {
|
public enum Subject: Equatable {
|
||||||
case transaction(StarsContext.State.Transaction, EnginePeer)
|
case transaction(StarsContext.State.Transaction, EnginePeer)
|
||||||
case receipt(BotPaymentReceipt)
|
case receipt(BotPaymentReceipt)
|
||||||
case gift(EngineMessage)
|
case gift(EngineMessage)
|
||||||
case subscription(StarsContext.State.Subscription)
|
case subscription(StarsContext.State.Subscription)
|
||||||
|
case importer(EnginePeer, StarsSubscriptionPricing, PeerInvitationImportersState.Importer, Double)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@ -951,7 +1066,8 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
subject: StarsTransactionScreen.Subject,
|
subject: StarsTransactionScreen.Subject,
|
||||||
forceDark: Bool = false
|
forceDark: Bool = false,
|
||||||
|
updateSubscription: @escaping (Bool) -> Void = { _ in }
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
@ -960,6 +1076,8 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
var openMediaImpl: (([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void)?
|
var openMediaImpl: (([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void)?
|
||||||
var openAppExamplesImpl: (() -> Void)?
|
var openAppExamplesImpl: (() -> Void)?
|
||||||
var copyTransactionIdImpl: ((String) -> Void)?
|
var copyTransactionIdImpl: ((String) -> Void)?
|
||||||
|
var updateSubscriptionImpl: ((StarsTransactionScreen.SubscriptionAction) -> Void)?
|
||||||
|
|
||||||
super.init(
|
super.init(
|
||||||
context: context,
|
context: context,
|
||||||
component: StarsTransactionSheetComponent(
|
component: StarsTransactionSheetComponent(
|
||||||
@ -979,6 +1097,9 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
},
|
},
|
||||||
copyTransactionId: { transactionId in
|
copyTransactionId: { transactionId in
|
||||||
copyTransactionIdImpl?(transactionId)
|
copyTransactionIdImpl?(transactionId)
|
||||||
|
},
|
||||||
|
updateSubscription: { action in
|
||||||
|
updateSubscriptionImpl?(action)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
navigationBarAppearance: .none,
|
navigationBarAppearance: .none,
|
||||||
@ -1090,6 +1211,30 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
HapticFeedback().tap()
|
HapticFeedback().tap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSubscriptionImpl = { [weak self] action in
|
||||||
|
guard let self, case let .subscription(subscription) = subject, let navigationController = self.navigationController as? NavigationController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
updateSubscription(action == .cancel)
|
||||||
|
|
||||||
|
let title: String
|
||||||
|
let text: String
|
||||||
|
switch action {
|
||||||
|
case .cancel:
|
||||||
|
title = "Subscription cancelled"
|
||||||
|
text = "You will still have access top [\(subscription.peer.compactDisplayTitle)]() until \(stringForMediumDate(timestamp: subscription.untilDate, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat))."
|
||||||
|
case .renew:
|
||||||
|
title = "Subscription renewed"
|
||||||
|
text = "You renewed your subscription to [\(subscription.peer.compactDisplayTitle)]()."
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = UndoOverlayController(presentationData: presentationData, content: .invitedToVoiceChat(context: context, peer: subscription.peer, title: title, text: text, action: nil, duration: 3.0), elevatedLayout: false, position: .bottom, action: { _ in return true })
|
||||||
|
Queue.mainQueue().after(0.6) {
|
||||||
|
navigationController.presentOverlay(controller: controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
|
@ -268,9 +268,15 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
}
|
}
|
||||||
itemLabel = NSAttributedString(string: labelString, font: Font.medium(fontBaseDisplaySize), textColor: labelString.hasPrefix("-") ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemDisclosureActions.constructive.fillColor)
|
itemLabel = NSAttributedString(string: labelString, font: Font.medium(fontBaseDisplaySize), textColor: labelString.hasPrefix("-") ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemDisclosureActions.constructive.fillColor)
|
||||||
|
|
||||||
|
var itemDateColor = environment.theme.list.itemSecondaryTextColor
|
||||||
itemDate = stringForMediumCompactDate(timestamp: item.date, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat)
|
itemDate = stringForMediumCompactDate(timestamp: item.date, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat)
|
||||||
if item.flags.contains(.isRefund) {
|
if item.flags.contains(.isRefund) {
|
||||||
itemDate += " – \(environment.strings.Stars_Intro_Transaction_Refund)"
|
itemDate += " – \(environment.strings.Stars_Intro_Transaction_Refund)"
|
||||||
|
} else if item.flags.contains(.isPending) {
|
||||||
|
itemDate += " – \(environment.strings.Monetization_Transaction_Pending)"
|
||||||
|
} else if item.flags.contains(.isFailed) {
|
||||||
|
itemDate += " – \(environment.strings.Monetization_Transaction_Failed)"
|
||||||
|
itemDateColor = environment.theme.list.itemDestructiveColor
|
||||||
}
|
}
|
||||||
|
|
||||||
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
|
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
|
||||||
@ -301,7 +307,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: itemDate,
|
string: itemDate,
|
||||||
font: Font.regular(floor(fontBaseDisplaySize * 14.0 / 17.0)),
|
font: Font.regular(floor(fontBaseDisplaySize * 14.0 / 17.0)),
|
||||||
textColor: environment.theme.list.itemSecondaryTextColor
|
textColor: itemDateColor
|
||||||
)),
|
)),
|
||||||
maximumNumberOfLines: 1
|
maximumNumberOfLines: 1
|
||||||
)))
|
)))
|
||||||
|
@ -27,6 +27,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let starsContext: StarsContext
|
let starsContext: StarsContext
|
||||||
|
let subscriptionsContext: StarsSubscriptionsContext
|
||||||
let openTransaction: (StarsContext.State.Transaction) -> Void
|
let openTransaction: (StarsContext.State.Transaction) -> Void
|
||||||
let openSubscription: (StarsContext.State.Subscription) -> Void
|
let openSubscription: (StarsContext.State.Subscription) -> Void
|
||||||
let buy: () -> Void
|
let buy: () -> Void
|
||||||
@ -35,6 +36,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
starsContext: StarsContext,
|
starsContext: StarsContext,
|
||||||
|
subscriptionsContext: StarsSubscriptionsContext,
|
||||||
openTransaction: @escaping (StarsContext.State.Transaction) -> Void,
|
openTransaction: @escaping (StarsContext.State.Transaction) -> Void,
|
||||||
openSubscription: @escaping (StarsContext.State.Subscription) -> Void,
|
openSubscription: @escaping (StarsContext.State.Subscription) -> Void,
|
||||||
buy: @escaping () -> Void,
|
buy: @escaping () -> Void,
|
||||||
@ -42,6 +44,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.starsContext = starsContext
|
self.starsContext = starsContext
|
||||||
|
self.subscriptionsContext = subscriptionsContext
|
||||||
self.openTransaction = openTransaction
|
self.openTransaction = openTransaction
|
||||||
self.openSubscription = openSubscription
|
self.openSubscription = openSubscription
|
||||||
self.buy = buy
|
self.buy = buy
|
||||||
@ -122,7 +125,6 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
|
|
||||||
private var previousBalance: Int64?
|
private var previousBalance: Int64?
|
||||||
|
|
||||||
private var subscriptionsContext: StarsSubscriptionsContext?
|
|
||||||
private var subscriptionsStateDisposable: Disposable?
|
private var subscriptionsStateDisposable: Disposable?
|
||||||
private var subscriptionsState: StarsSubscriptionsContext.State?
|
private var subscriptionsState: StarsSubscriptionsContext.State?
|
||||||
|
|
||||||
@ -312,9 +314,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let subscriptionsContext = component.context.engine.payments.peerStarsSubscriptionsContext(starsContext: component.starsContext)
|
self.subscriptionsStateDisposable = (component.subscriptionsContext.state
|
||||||
self.subscriptionsContext = subscriptionsContext
|
|
||||||
self.subscriptionsStateDisposable = (subscriptionsContext.state
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -614,10 +614,21 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
)))
|
)))
|
||||||
)
|
)
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
|
let dateText: String
|
||||||
|
let dateValue = stringForDateWithoutYear(date: Date(timeIntervalSince1970: Double(subscription.untilDate)), strings: environment.strings)
|
||||||
|
if subscription.flags.contains(.isCancelled) {
|
||||||
|
if subscription.untilDate > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
|
||||||
|
dateText = "expires on \(dateValue)"
|
||||||
|
} else {
|
||||||
|
dateText = "expired on \(dateValue)"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dateText = "renews on \(dateValue)"
|
||||||
|
}
|
||||||
titleComponents.append(
|
titleComponents.append(
|
||||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "renews on \(stringForDateWithoutYear(date: Date(timeIntervalSince1970: Double(subscription.untilDate)), strings: environment.strings))",
|
string: dateText,
|
||||||
font: Font.regular(floor(fontBaseDisplaySize * 15.0 / 17.0)),
|
font: Font.regular(floor(fontBaseDisplaySize * 15.0 / 17.0)),
|
||||||
textColor: environment.theme.list.itemSecondaryTextColor
|
textColor: environment.theme.list.itemSecondaryTextColor
|
||||||
)),
|
)),
|
||||||
@ -626,15 +637,15 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
let labelComponent: AnyComponentWithIdentity<Empty>
|
let labelComponent: AnyComponentWithIdentity<Empty>
|
||||||
if "".isEmpty {
|
if subscription.flags.contains(.isCancelled) {
|
||||||
|
labelComponent = AnyComponentWithIdentity(id: "cancelledLabel", component: AnyComponent(
|
||||||
|
MultilineTextComponent(text: .plain(NSAttributedString(string: "cancelled", font: Font.regular(floor(fontBaseDisplaySize * 13.0 / 17.0)), textColor: environment.theme.list.itemDestructiveColor)))
|
||||||
|
))
|
||||||
|
} else {
|
||||||
let itemLabel = NSAttributedString(string: "\(subscription.pricing.amount)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemPrimaryTextColor)
|
let itemLabel = NSAttributedString(string: "\(subscription.pricing.amount)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemPrimaryTextColor)
|
||||||
let itemSublabel = NSAttributedString(string: "per month", font: Font.regular(floor(fontBaseDisplaySize * 13.0 / 17.0)), textColor: environment.theme.list.itemSecondaryTextColor)
|
let itemSublabel = NSAttributedString(string: "per month", font: Font.regular(floor(fontBaseDisplaySize * 13.0 / 17.0)), textColor: environment.theme.list.itemSecondaryTextColor)
|
||||||
|
|
||||||
labelComponent = AnyComponentWithIdentity(id: "label", component: AnyComponent(StarsLabelComponent(text: itemLabel, subtext: itemSublabel)))
|
labelComponent = AnyComponentWithIdentity(id: "label", component: AnyComponent(StarsLabelComponent(text: itemLabel, subtext: itemSublabel)))
|
||||||
} else {
|
|
||||||
labelComponent = AnyComponentWithIdentity(id: "cancelledLabel", component: AnyComponent(
|
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "cancelled", font: Font.regular(floor(fontBaseDisplaySize * 13.0 / 17.0)), textColor: environment.theme.list.itemDestructiveColor)))
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptionsItems.append(AnyComponentWithIdentity(
|
subscriptionsItems.append(AnyComponentWithIdentity(
|
||||||
@ -657,6 +668,38 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
)
|
)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
if subscriptionsState.canLoadMore {
|
||||||
|
subscriptionsItems.append(AnyComponentWithIdentity(
|
||||||
|
id: "showMore",
|
||||||
|
component: AnyComponent(
|
||||||
|
ListActionItemComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
title: AnyComponent(Text(
|
||||||
|
text: "Show More",
|
||||||
|
font: Font.regular(17.0),
|
||||||
|
color: environment.theme.list.itemAccentColor
|
||||||
|
)),
|
||||||
|
leftIcon: .custom(
|
||||||
|
AnyComponentWithIdentity(
|
||||||
|
id: "icon",
|
||||||
|
component: AnyComponent(Image(
|
||||||
|
image: PresentationResourcesItemList.downArrowImage(environment.theme),
|
||||||
|
size: CGSize(width: 30.0, height: 30.0)
|
||||||
|
))
|
||||||
|
),
|
||||||
|
false
|
||||||
|
),
|
||||||
|
accessory: nil,
|
||||||
|
action: { _ in
|
||||||
|
|
||||||
|
},
|
||||||
|
highlighting: .default,
|
||||||
|
updateIsHighlighted: { view, _ in
|
||||||
|
|
||||||
|
})
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !subscriptionsItems.isEmpty {
|
if !subscriptionsItems.isEmpty {
|
||||||
@ -837,6 +880,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let starsContext: StarsContext
|
private let starsContext: StarsContext
|
||||||
|
private let subscriptionsContext: StarsSubscriptionsContext
|
||||||
|
|
||||||
private let options = Promise<[StarsTopUpOption]>()
|
private let options = Promise<[StarsTopUpOption]>()
|
||||||
|
|
||||||
@ -844,6 +888,8 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.starsContext = starsContext
|
self.starsContext = starsContext
|
||||||
|
|
||||||
|
self.subscriptionsContext = context.engine.payments.peerStarsSubscriptionsContext(starsContext: starsContext)
|
||||||
|
|
||||||
var buyImpl: (() -> Void)?
|
var buyImpl: (() -> Void)?
|
||||||
var giftImpl: (() -> Void)?
|
var giftImpl: (() -> Void)?
|
||||||
var openTransactionImpl: ((StarsContext.State.Transaction) -> Void)?
|
var openTransactionImpl: ((StarsContext.State.Transaction) -> Void)?
|
||||||
@ -851,6 +897,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
|||||||
super.init(context: context, component: StarsTransactionsScreenComponent(
|
super.init(context: context, component: StarsTransactionsScreenComponent(
|
||||||
context: context,
|
context: context,
|
||||||
starsContext: starsContext,
|
starsContext: starsContext,
|
||||||
|
subscriptionsContext: self.subscriptionsContext,
|
||||||
openTransaction: { transaction in
|
openTransaction: { transaction in
|
||||||
openTransactionImpl?(transaction)
|
openTransactionImpl?(transaction)
|
||||||
},
|
},
|
||||||
@ -887,7 +934,12 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = context.sharedContext.makeStarsSubscriptionScreen(context: context, subscription: subscription)
|
let controller = context.sharedContext.makeStarsSubscriptionScreen(context: context, subscription: subscription, update: { [weak self] cancel in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.subscriptionsContext.updateSubscription(id: subscription.id, cancel: cancel)
|
||||||
|
})
|
||||||
self.push(controller)
|
self.push(controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ private final class SheetContent: CombinedComponent {
|
|||||||
var contentSize = CGSize(width: context.availableSize.width, height: 18.0)
|
var contentSize = CGSize(width: context.availableSize.width, height: 18.0)
|
||||||
|
|
||||||
let background = background.update(
|
let background = background.update(
|
||||||
component: RoundedRectangle(color: theme.list.blocksBackgroundColor, cornerRadius: 8.0),
|
component: RoundedRectangle(color: theme.actionSheet.opaqueItemBackgroundColor, cornerRadius: 8.0),
|
||||||
availableSize: CGSize(width: context.availableSize.width, height: 1000.0),
|
availableSize: CGSize(width: context.availableSize.width, height: 1000.0),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
@ -270,7 +270,6 @@ private final class SheetContent: CombinedComponent {
|
|||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: background.size.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: background.size.height / 2.0))
|
||||||
)
|
)
|
||||||
|
|
||||||
var isSubscription = false
|
|
||||||
let subject: StarsImageComponent.Subject
|
let subject: StarsImageComponent.Subject
|
||||||
if !component.extendedMedia.isEmpty {
|
if !component.extendedMedia.isEmpty {
|
||||||
subject = .extendedMedia(component.extendedMedia)
|
subject = .extendedMedia(component.extendedMedia)
|
||||||
@ -283,13 +282,19 @@ private final class SheetContent: CombinedComponent {
|
|||||||
} else {
|
} else {
|
||||||
subject = .none
|
subject = .none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSubscription = false
|
||||||
|
if case .starsChatSubscription = context.component.source {
|
||||||
|
isSubscription = true
|
||||||
|
}
|
||||||
let star = star.update(
|
let star = star.update(
|
||||||
component: StarsImageComponent(
|
component: StarsImageComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
diameter: 90.0,
|
diameter: 90.0,
|
||||||
backgroundColor: theme.list.blocksBackgroundColor
|
backgroundColor: theme.actionSheet.opaqueItemBackgroundColor,
|
||||||
|
icon: isSubscription ? .star : nil
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),
|
availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0),
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
@ -324,10 +329,9 @@ private final class SheetContent: CombinedComponent {
|
|||||||
contentSize.height += 126.0
|
contentSize.height += 126.0
|
||||||
|
|
||||||
let titleString: String
|
let titleString: String
|
||||||
if case .starsChatSubscription = context.component.source {
|
if isSubscription {
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
titleString = "Subscribe to the Channel"
|
titleString = "Subscribe to the Channel"
|
||||||
isSubscription = true
|
|
||||||
} else {
|
} else {
|
||||||
titleString = strings.Stars_Transfer_Title
|
titleString = strings.Stars_Transfer_Title
|
||||||
}
|
}
|
||||||
@ -588,12 +592,26 @@ private final class SheetContent: CombinedComponent {
|
|||||||
let info = info.update(
|
let info = info.update(
|
||||||
component: BalancedTextComponent(
|
component: BalancedTextComponent(
|
||||||
text: .markdown(
|
text: .markdown(
|
||||||
text: "By subscribing you agree to the [Terms of Service]()",
|
text: strings.Stars_Subscription_Terms,
|
||||||
attributes: termsMarkdownAttributes
|
attributes: termsMarkdownAttributes
|
||||||
),
|
),
|
||||||
horizontalAlignment: .center,
|
horizontalAlignment: .center,
|
||||||
maximumNumberOfLines: 0,
|
maximumNumberOfLines: 0,
|
||||||
lineSpacing: 0.2
|
lineSpacing: 0.2,
|
||||||
|
highlightColor: linkColor.withAlphaComponent(0.2),
|
||||||
|
highlightAction: { attributes in
|
||||||
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||||
|
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tapAction: { [weak controller] attributes, _ in
|
||||||
|
if let controller, let navigationController = controller.navigationController as? NavigationController {
|
||||||
|
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_Subscription_Terms_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||||
|
}
|
||||||
|
}
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height),
|
availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMediumOutline.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMediumOutline.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "StarOutline.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMediumOutline.imageset/StarOutline.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Stars/StarMediumOutline.imageset/StarOutline.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Stars/TransactionStar.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Stars/TransactionStar.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "StarTransaction.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
submodules/TelegramUI/Images.xcassets/Premium/Stars/TransactionStar.imageset/StarTransaction.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Stars/TransactionStar.imageset/StarTransaction.pdf
vendored
Normal file
Binary file not shown.
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "StarTransactionOutline.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -3989,7 +3989,7 @@ extension ChatControllerImpl {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
HapticFeedback().impact()
|
HapticFeedback().impact()
|
||||||
|
|
||||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action, duration: 3), elevatedLayout: false, action: { [weak self] action in
|
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .invitedToVoiceChat(context: strongSelf.context, peer: peer, title: nil, text: strongSelf.presentationData.strings.Conversation_SendMesageAsPremiumInfo, action: strongSelf.presentationData.strings.EmojiInput_PremiumEmojiToast_Action, duration: 3), elevatedLayout: false, action: { [weak self] action in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -87,34 +87,35 @@ func chatHistoryEntriesForView(
|
|||||||
if (associatedData.subject?.isService ?? false) {
|
if (associatedData.subject?.isService ?? false) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if case let .peer(peerId) = location, case let cachedData = cachedData as? CachedChannelData, let invitedOn = cachedData?.invitedOn {
|
// if case let .peer(peerId) = location, case let cachedData = cachedData as? CachedChannelData, let invitedOn = cachedData?.invitedOn {
|
||||||
joinMessage = Message(
|
// joinMessage = Message(
|
||||||
stableId: UInt32.max - 1000,
|
// stableId: UInt32.max - 1000,
|
||||||
stableVersion: 0,
|
// stableVersion: 0,
|
||||||
id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: 0),
|
// id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: 0),
|
||||||
globallyUniqueId: nil,
|
// globallyUniqueId: nil,
|
||||||
groupingKey: nil,
|
// groupingKey: nil,
|
||||||
groupInfo: nil,
|
// groupInfo: nil,
|
||||||
threadId: nil,
|
// threadId: nil,
|
||||||
timestamp: invitedOn,
|
// timestamp: invitedOn,
|
||||||
flags: [.Incoming],
|
// flags: [.Incoming],
|
||||||
tags: [],
|
// tags: [],
|
||||||
globalTags: [],
|
// globalTags: [],
|
||||||
localTags: [],
|
// localTags: [],
|
||||||
customTags: [],
|
// customTags: [],
|
||||||
forwardInfo: nil,
|
// forwardInfo: nil,
|
||||||
author: channelPeer,
|
// author: channelPeer,
|
||||||
text: "",
|
// text: "",
|
||||||
attributes: [],
|
// attributes: [],
|
||||||
media: [TelegramMediaAction(action: .joinedByRequest)],
|
// media: [TelegramMediaAction(action: .joinedByRequest)],
|
||||||
peers: SimpleDictionary<PeerId, Peer>(),
|
// peers: SimpleDictionary<PeerId, Peer>(),
|
||||||
associatedMessages: SimpleDictionary<MessageId, Message>(),
|
// associatedMessages: SimpleDictionary<MessageId, Message>(),
|
||||||
associatedMessageIds: [],
|
// associatedMessageIds: [],
|
||||||
associatedMedia: [:],
|
// associatedMedia: [:],
|
||||||
associatedThreadInfo: nil,
|
// associatedThreadInfo: nil,
|
||||||
associatedStories: [:]
|
// associatedStories: [:]
|
||||||
)
|
// )
|
||||||
} else if let peer = channelPeer as? TelegramChannel, case .broadcast = peer.info, case .member = peer.participationStatus, !peer.flags.contains(.isCreator) {
|
// } else
|
||||||
|
if let peer = channelPeer as? TelegramChannel, case .broadcast = peer.info, case .member = peer.participationStatus, !peer.flags.contains(.isCreator) {
|
||||||
joinMessage = Message(
|
joinMessage = Message(
|
||||||
stableId: UInt32.max - 1000,
|
stableId: UInt32.max - 1000,
|
||||||
stableVersion: 0,
|
stableVersion: 0,
|
||||||
|
@ -2750,8 +2750,12 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return StarsTransactionScreen(context: context, subject: .receipt(receipt))
|
return StarsTransactionScreen(context: context, subject: .receipt(receipt))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeStarsSubscriptionScreen(context: AccountContext, subscription: StarsContext.State.Subscription) -> ViewController {
|
public func makeStarsSubscriptionScreen(context: AccountContext, subscription: StarsContext.State.Subscription, update: @escaping (Bool) -> Void) -> ViewController {
|
||||||
return StarsTransactionScreen(context: context, subject: .subscription(subscription))
|
return StarsTransactionScreen(context: context, subject: .subscription(subscription), updateSubscription: update)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeStarsSubscriptionScreen(context: AccountContext, peer: EnginePeer, pricing: StarsSubscriptionPricing, importer: PeerInvitationImportersState.Importer, usdRate: Double) -> ViewController {
|
||||||
|
return StarsTransactionScreen(context: context, subject: .importer(peer, pricing, importer, usdRate))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController {
|
public func makeStarsStatisticsScreen(context: AccountContext, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext) -> ViewController {
|
||||||
|
@ -22,7 +22,7 @@ public enum UndoOverlayContent {
|
|||||||
case chatRemovedFromFolder(chatTitle: String, folderTitle: String)
|
case chatRemovedFromFolder(chatTitle: String, folderTitle: String)
|
||||||
case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool)
|
case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool)
|
||||||
case setProximityAlert(title: String, text: String, cancelled: Bool)
|
case setProximityAlert(title: String, text: String, cancelled: Bool)
|
||||||
case invitedToVoiceChat(context: AccountContext, peer: EnginePeer, text: String, action: String?, duration: Double)
|
case invitedToVoiceChat(context: AccountContext, peer: EnginePeer, title: String?, text: String, action: String?, duration: Double)
|
||||||
case linkCopied(text: String)
|
case linkCopied(text: String)
|
||||||
case banned(text: String)
|
case banned(text: String)
|
||||||
case importedMessage(text: String)
|
case importedMessage(text: String)
|
||||||
|
@ -652,19 +652,21 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
displayUndo = false
|
displayUndo = false
|
||||||
self.originalRemainingSeconds = 3
|
self.originalRemainingSeconds = 3
|
||||||
case let .invitedToVoiceChat(context, peer, text, action, duration):
|
case let .invitedToVoiceChat(context, peer, title, text, action, duration):
|
||||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 15.0))
|
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 15.0))
|
||||||
self.iconNode = nil
|
self.iconNode = nil
|
||||||
self.iconCheckNode = nil
|
self.iconCheckNode = nil
|
||||||
self.animationNode = nil
|
self.animationNode = nil
|
||||||
self.animatedStickerNode = nil
|
self.animatedStickerNode = nil
|
||||||
|
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: title ?? "", font: Font.semibold(14.0), textColor: .white)
|
||||||
|
|
||||||
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||||
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||||
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor)
|
||||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||||
self.textNode.attributedText = attributedText
|
|
||||||
|
|
||||||
|
self.textNode.attributedText = attributedText
|
||||||
self.avatarNode?.setPeer(context: context, theme: presentationData.theme, peer: peer, overrideImage: nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: true)
|
self.avatarNode?.setPeer(context: context, theme: presentationData.theme, peer: peer, overrideImage: nil, emptyColor: presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: true)
|
||||||
|
|
||||||
if let action = action {
|
if let action = action {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user