mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge commit '75d884350a1b953045f99c1dbb6bb0d5fcf2e181'
This commit is contained in:
commit
65a88ae93f
@ -12702,6 +12702,7 @@ Sorry for the inconvenience.";
|
||||
"Stars.Transaction.Subscription.JoinChannel" = "Join Channel";
|
||||
"Stars.Transaction.Subscription.JoinAgainChannel" = "Join Channel";
|
||||
"Stars.Transaction.Subscription.LeftChannel" = "You left channel but you can still get back until %@";
|
||||
"Stars.Transaction.Subscription.Expired" = "Your subscription expired on %@.";
|
||||
"Stars.Transaction.Subscription.PerMonth" = "%@ / month";
|
||||
"Stars.Transaction.Subscription.PerMonthUsd" = "appx. %@ per month";
|
||||
"Stars.Transaction.Subscription.Subscription" = "Subscription";
|
||||
@ -12724,6 +12725,7 @@ Sorry for the inconvenience.";
|
||||
"Stars.Intro.Subscriptions.Expires" = "expires on %@";
|
||||
"Stars.Intro.Subscriptions.Expired" = "expired on %@";
|
||||
"Stars.Intro.Subscriptions.Cancelled" = "cancelled";
|
||||
"Stars.Intro.Subscriptions.ExpiredStatus" = "expired";
|
||||
"Stars.Intro.Subscriptions.ShowMore" = "Show More";
|
||||
|
||||
"Stars.Intro.Transaction.SubscriptionFee.Title" = "Monthly Subscription Fee";
|
||||
@ -12768,3 +12770,12 @@ Sorry for the inconvenience.";
|
||||
"InviteLink.Create.RequestApprovalFeeUnavailable" = "You can't enable admin approval for links that require a monthly fee.";
|
||||
|
||||
"WebApp.PrivacyPolicy_URL" = "https://telegram.org/privacy-tpa";
|
||||
|
||||
"ChatList.SubscriptionsLowBalance.Stars_1" = "%@ Star needed";
|
||||
"ChatList.SubscriptionsLowBalance.Stars_any" = "%@ Stars needed";
|
||||
|
||||
"ChatList.SubscriptionsLowBalance.Single.Title" = "%1$@ for %2$@";
|
||||
"ChatList.SubscriptionsLowBalance.Single.Text" = "Insufficient funds to cover your subscription.";
|
||||
|
||||
"ChatList.SubscriptionsLowBalance.Multiple.Title" = "%@ for your subscriptions";
|
||||
"ChatList.SubscriptionsLowBalance.Multiple.Text" = "Insufficient funds to cover your subscriptions.";
|
||||
|
@ -1174,6 +1174,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
self.openBirthdaySetup()
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.mainContainerNode.openStarsTopup = { [weak self] amount in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openStarsTopup(amount: amount)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.mainContainerNode.openPremiumManagement = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
@ -5978,6 +5986,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.push(controller)
|
||||
}
|
||||
|
||||
func openStarsTopup(amount: Int64?) {
|
||||
guard let starsContext = self.context.starsContext else {
|
||||
return
|
||||
}
|
||||
let controller = self.context.sharedContext.makeStarsPurchaseScreen(context: self.context, starsContext: starsContext, options: [], purpose: amount.flatMap({ .topUp(requiredStars: $0, purpose: "subs") }) ?? .generic, completion: { _ in })
|
||||
self.push(controller)
|
||||
}
|
||||
|
||||
private var storyCameraTransitionInCoordinator: StoryCameraTransitionInCoordinator?
|
||||
var hasStoryCameraTransition: Bool {
|
||||
return self.storyCameraTransitionInCoordinator != nil
|
||||
|
@ -348,6 +348,9 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
itemNode.listNode.openPremiumManagement = { [weak self] in
|
||||
self?.openPremiumManagement?()
|
||||
}
|
||||
itemNode.listNode.openStarsTopup = { [weak self] amount in
|
||||
self?.openStarsTopup?(amount)
|
||||
}
|
||||
|
||||
self.currentItemStateValue.set(itemNode.listNode.state |> map { state in
|
||||
let filterId: Int32?
|
||||
@ -413,12 +416,28 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele
|
||||
var openBirthdaySetup: (() -> Void)?
|
||||
var openPremiumManagement: (() -> Void)?
|
||||
var openStories: ((ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void)?
|
||||
var openStarsTopup: ((Int64?) -> Void)?
|
||||
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
||||
var didBeginSelectingChats: (() -> Void)?
|
||||
var canExpandHiddenItems: (() -> Bool)?
|
||||
public var displayFilterLimit: (() -> Void)?
|
||||
|
||||
public init(context: AccountContext, controller: ChatListControllerImpl?, location: ChatListControllerLocation, chatListMode: ChatListNodeMode = .chatList(appendContacts: true), previewing: Bool, controlsHistoryPreload: Bool, isInlineMode: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void, openArchiveSettings: @escaping () -> Void) {
|
||||
public init(
|
||||
context: AccountContext,
|
||||
controller: ChatListControllerImpl?,
|
||||
location: ChatListControllerLocation,
|
||||
chatListMode: ChatListNodeMode = .chatList(appendContacts: true),
|
||||
previewing: Bool,
|
||||
controlsHistoryPreload: Bool,
|
||||
isInlineMode: Bool,
|
||||
presentationData: PresentationData,
|
||||
animationCache: AnimationCache,
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
filterBecameEmpty: @escaping (ChatListFilter?) -> Void,
|
||||
filterEmptyAction: @escaping (ChatListFilter?) -> Void,
|
||||
secondaryEmptyAction: @escaping () -> Void,
|
||||
openArchiveSettings: @escaping () -> Void)
|
||||
{
|
||||
self.context = context
|
||||
self.controller = controller
|
||||
self.location = location
|
||||
|
@ -2825,6 +2825,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
if let sourceNode = sourceNode as? ChatListItemNode {
|
||||
self.interaction.openStories?(id, sourceNode.avatarNode)
|
||||
}
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
@ -3658,19 +3659,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
} else if case .apps = key {
|
||||
if let navigationController = self.navigationController {
|
||||
if isRecommended {
|
||||
#if DEBUG
|
||||
let _ = (self.context.sharedContext.makeMiniAppListScreenInitialData(context: self.context)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in
|
||||
guard let self, let navigationController = self.navigationController else {
|
||||
return
|
||||
}
|
||||
navigationController.pushViewController(self.context.sharedContext.makeMiniAppListScreen(context: self.context, initialData: initialData))
|
||||
})
|
||||
#else
|
||||
if let peerInfoScreen = self.context.sharedContext.makePeerInfoController(context: self.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) {
|
||||
navigationController.pushViewController(peerInfoScreen)
|
||||
}
|
||||
#endif
|
||||
} else if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), let parentController = self.parentController {
|
||||
self.context.sharedContext.openWebApp(
|
||||
context: self.context,
|
||||
@ -4659,6 +4650,7 @@ public final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
}, performActiveSessionAction: { _, _ in
|
||||
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
}, openStories: { _, _ in
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
|
@ -156,7 +156,8 @@ public final class ChatListShimmerNode: ASDisplayNode {
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: animationCache, animationRenderer: animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
interaction.isInlineMode = isInlineMode
|
||||
|
@ -109,6 +109,7 @@ public final class ChatListNodeInteraction {
|
||||
let openChatFolderUpdates: () -> Void
|
||||
let hideChatFolderUpdates: () -> Void
|
||||
let openStories: (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void
|
||||
let openStarsTopup: (Int64?) -> Void
|
||||
let dismissNotice: (ChatListNotice) -> Void
|
||||
let editPeer: (ChatListItem) -> Void
|
||||
|
||||
@ -164,6 +165,7 @@ public final class ChatListNodeInteraction {
|
||||
openChatFolderUpdates: @escaping () -> Void,
|
||||
hideChatFolderUpdates: @escaping () -> Void,
|
||||
openStories: @escaping (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void,
|
||||
openStarsTopup: @escaping (Int64?) -> Void,
|
||||
dismissNotice: @escaping (ChatListNotice) -> Void,
|
||||
editPeer: @escaping (ChatListItem) -> Void
|
||||
) {
|
||||
@ -206,6 +208,7 @@ public final class ChatListNodeInteraction {
|
||||
self.openChatFolderUpdates = openChatFolderUpdates
|
||||
self.hideChatFolderUpdates = hideChatFolderUpdates
|
||||
self.openStories = openStories
|
||||
self.openStarsTopup = openStarsTopup
|
||||
self.dismissNotice = dismissNotice
|
||||
self.editPeer = editPeer
|
||||
}
|
||||
@ -747,8 +750,8 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction?.openPremiumGift(birthdays)
|
||||
case .reviewLogin:
|
||||
break
|
||||
case .starsSubscriptionLowBalance:
|
||||
break
|
||||
case let .starsSubscriptionLowBalance(amount, _):
|
||||
nodeInteraction?.openStarsTopup(amount)
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
@ -1087,8 +1090,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
nodeInteraction?.openPremiumGift(birthdays)
|
||||
case .reviewLogin:
|
||||
break
|
||||
case .starsSubscriptionLowBalance:
|
||||
break
|
||||
case let .starsSubscriptionLowBalance(amount, _):
|
||||
nodeInteraction?.openStarsTopup(amount)
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
@ -1208,6 +1211,7 @@ public final class ChatListNode: ListView {
|
||||
public var openStories: ((ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void)?
|
||||
public var openBirthdaySetup: (() -> Void)?
|
||||
public var openPremiumManagement: (() -> Void)?
|
||||
public var openStarsTopup: ((Int64?) -> Void)?
|
||||
|
||||
private var theme: PresentationTheme
|
||||
|
||||
@ -1809,6 +1813,11 @@ public final class ChatListNode: ListView {
|
||||
return
|
||||
}
|
||||
self.openStories?(subject, itemNode)
|
||||
}, openStarsTopup: { [weak self] amount in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openStarsTopup?(amount)
|
||||
}, dismissNotice: { [weak self] notice in
|
||||
guard let self else {
|
||||
return
|
||||
@ -1911,6 +1920,8 @@ public final class ChatListNode: ListView {
|
||||
displayArchiveIntro = .single(false)
|
||||
}
|
||||
|
||||
let starsSubscriptionsContextPromise = Promise<StarsSubscriptionsContext?>(nil)
|
||||
|
||||
self.updateIsMainTabDisposable = (self.isMainTab.get()
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] isMainTab in
|
||||
guard let self else {
|
||||
@ -1932,9 +1943,10 @@ public final class ChatListNode: ListView {
|
||||
twoStepData,
|
||||
newSessionReviews(postbox: context.account.postbox),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId)),
|
||||
context.account.stateManager.contactBirthdays
|
||||
context.account.stateManager.contactBirthdays,
|
||||
starsSubscriptionsContextPromise.get()
|
||||
)
|
||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, birthday, birthdays -> Signal<ChatListNotice?, NoError> in
|
||||
|> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, birthday, birthdays, starsSubscriptionsContext -> Signal<ChatListNotice?, NoError> in
|
||||
if let newSessionReview = newSessionReviews.first {
|
||||
return .single(.reviewLogin(newSessionReview: newSessionReview, totalCount: newSessionReviews.count))
|
||||
}
|
||||
@ -1968,7 +1980,24 @@ public final class ChatListNode: ListView {
|
||||
todayBirthdayPeerIds = []
|
||||
}
|
||||
|
||||
if suggestions.contains(.gracePremium) {
|
||||
if suggestions.contains(.starsSubscriptionLowBalance) {
|
||||
if let starsSubscriptionsContext {
|
||||
return starsSubscriptionsContext.state
|
||||
|> map { state in
|
||||
if state.balance > 0 && !state.subscriptions.isEmpty {
|
||||
return .starsSubscriptionLowBalance(
|
||||
amount: state.balance,
|
||||
peers: state.subscriptions.map { $0.peer }
|
||||
)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
starsSubscriptionsContextPromise.set(.single(context.engine.payments.peerStarsSubscriptionsContext(starsContext: nil, missingBalance: true)))
|
||||
return .single(nil)
|
||||
}
|
||||
} else if suggestions.contains(.gracePremium) {
|
||||
return .single(.premiumGrace)
|
||||
} else if suggestions.contains(.setupBirthday) && birthday == nil {
|
||||
return .single(.setupBirthday)
|
||||
|
@ -90,7 +90,7 @@ public enum ChatListNotice: Equatable {
|
||||
case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday])
|
||||
case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int)
|
||||
case premiumGrace
|
||||
case starsSubscriptionLowBalance(amount: Int64)
|
||||
case starsSubscriptionLowBalance(amount: Int64, peers: [EnginePeer])
|
||||
}
|
||||
|
||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
|
@ -11,6 +11,8 @@ import ItemListUI
|
||||
import Markdown
|
||||
import AccountContext
|
||||
import MergedAvatarsNode
|
||||
import TextNodeWithEntities
|
||||
import TextFormat
|
||||
|
||||
class ChatListNoticeItem: ListViewItem {
|
||||
enum Action {
|
||||
@ -83,10 +85,11 @@ private let separatorHeight = 1.0 / UIScreen.main.scale
|
||||
|
||||
private let titleFont = Font.semibold(15.0)
|
||||
private let textFont = Font.regular(15.0)
|
||||
private let smallTextFont = Font.regular(14.0)
|
||||
|
||||
final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
private let contentContainer: ASDisplayNode
|
||||
private let titleNode: TextNode
|
||||
private let titleNode: TextNodeWithEntities
|
||||
private let textNode: TextNode
|
||||
private let arrowNode: ASImageNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
@ -112,7 +115,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
required init() {
|
||||
self.contentContainer = ASDisplayNode()
|
||||
|
||||
self.titleNode = TextNode()
|
||||
self.titleNode = TextNodeWithEntities()
|
||||
self.textNode = TextNode()
|
||||
self.arrowNode = ASImageNode()
|
||||
self.separatorNode = ASDisplayNode()
|
||||
@ -122,7 +125,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
self.contentContainer.clipsToBounds = true
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.contentContainer.addSubnode(self.titleNode)
|
||||
self.contentContainer.addSubnode(self.titleNode.textNode)
|
||||
self.contentContainer.addSubnode(self.textNode)
|
||||
self.contentContainer.addSubnode(self.arrowNode)
|
||||
|
||||
@ -152,7 +155,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
func asyncLayout() -> (_ item: ChatListNoticeItem, _ params: ListViewItemLayoutParams, _ isLast: Bool) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let previousItem = self.item
|
||||
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let makeTitleLayout = TextNodeWithEntities.asyncLayout(self.titleNode)
|
||||
let makeTextLayout = TextNode.asyncLayout(self.textNode)
|
||||
|
||||
let makeOkButtonTextLayout = TextNode.asyncLayout(self.okButtonText)
|
||||
@ -262,10 +265,24 @@ 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)))
|
||||
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 let .starsSubscriptionLowBalance(amount):
|
||||
let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: "⭐️ \(amount) Stars needed for your subscriptions", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor))
|
||||
titleString = titleStringValue
|
||||
textString = NSAttributedString(string: "Insufficient funds to cover your subscriptions.", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||
case let .starsSubscriptionLowBalance(amount, peers):
|
||||
let title: String
|
||||
let text: String
|
||||
let starsValue = item.strings.ChatList_SubscriptionsLowBalance_Stars(Int32(amount))
|
||||
if let peer = peers.first, peers.count == 1 {
|
||||
title = item.strings.ChatList_SubscriptionsLowBalance_Single_Title(starsValue, peer.compactDisplayTitle).string
|
||||
text = item.strings.ChatList_SubscriptionsLowBalance_Single_Text
|
||||
} else {
|
||||
title = item.strings.ChatList_SubscriptionsLowBalance_Multiple_Title(starsValue).string
|
||||
text = item.strings.ChatList_SubscriptionsLowBalance_Multiple_Text
|
||||
}
|
||||
let attributedTitle = NSMutableAttributedString(string: "⭐️\(title)", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)
|
||||
if let range = attributedTitle.string.range(of: "⭐️") {
|
||||
attributedTitle.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedTitle.string))
|
||||
attributedTitle.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: attributedTitle.string))
|
||||
}
|
||||
titleString = attributedTitle
|
||||
textString = NSAttributedString(string: text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
|
||||
}
|
||||
|
||||
var leftInset: CGFloat = sideInset
|
||||
@ -295,19 +312,19 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.arrowNode.image = PresentationResourcesItemList.disclosureArrowImage(item.theme)
|
||||
}
|
||||
|
||||
let _ = titleLayout.1()
|
||||
let _ = titleLayout.1(TextNodeWithEntities.Arguments(context: item.context, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: .white, attemptSynchronous: true))
|
||||
if case .center = alignment {
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: floor((params.width - titleLayout.0.size.width) * 0.5), y: verticalInset), size: titleLayout.0.size)
|
||||
strongSelf.titleNode.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - titleLayout.0.size.width) * 0.5), y: verticalInset), size: titleLayout.0.size)
|
||||
} else {
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.0.size)
|
||||
strongSelf.titleNode.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.0.size)
|
||||
}
|
||||
|
||||
let _ = textLayout.1()
|
||||
|
||||
if case .center = alignment {
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: strongSelf.titleNode.frame.maxY + spacing), size: textLayout.0.size)
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size)
|
||||
} else {
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.frame.maxY + spacing), size: textLayout.0.size)
|
||||
strongSelf.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size)
|
||||
}
|
||||
|
||||
if !avatarPeers.isEmpty {
|
||||
@ -343,6 +360,8 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
hasCloseButton = true
|
||||
} else if case .premiumGrace = item.notice {
|
||||
hasCloseButton = true
|
||||
} else if case .starsSubscriptionLowBalance = item.notice {
|
||||
hasCloseButton = true
|
||||
}
|
||||
|
||||
if let okButtonLayout, let cancelButtonLayout {
|
||||
|
@ -745,9 +745,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
let modalContainer = self.modalContainers[i]
|
||||
|
||||
var isStandaloneModal = false
|
||||
if let controller = modalContainer.container.controllers.first, case .standaloneModal = controller.navigationPresentation {
|
||||
if let controller = modalContainer.container.controllers.first {
|
||||
if [.standaloneModal, .standaloneFlatModal].contains(controller.navigationPresentation) {
|
||||
isStandaloneModal = true
|
||||
}
|
||||
}
|
||||
|
||||
let containerTransition: ContainedViewLayoutTransition
|
||||
if modalContainer.supernode == nil {
|
||||
|
@ -43,6 +43,11 @@ func makeNavigationLayout(mode: NavigationControllerMode, layout: ContainerViewL
|
||||
requiresModal = true
|
||||
beginsModal = true
|
||||
isStandalone = true
|
||||
case .standaloneFlatModal:
|
||||
requiresModal = true
|
||||
beginsModal = true
|
||||
isStandalone = true
|
||||
isFlat = true
|
||||
case .modalInLargeLayout:
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
|
@ -64,6 +64,7 @@ public enum ViewControllerNavigationPresentation {
|
||||
case modal
|
||||
case flatModal
|
||||
case standaloneModal
|
||||
case standaloneFlatModal
|
||||
case modalInLargeLayout
|
||||
case modalInCompactLayout
|
||||
}
|
||||
|
@ -479,7 +479,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
|
||||
private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state: InviteLinkEditControllerState, isGroup: Bool, isPublic: Bool, presentationData: PresentationData, starsState: StarsRevenueStats?, configuration: StarsSubscriptionConfiguration) -> [InviteLinksEditEntry] {
|
||||
private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state: InviteLinkEditControllerState, isGroup: Bool, isPublic: Bool, presentationData: PresentationData, configuration: StarsSubscriptionConfiguration) -> [InviteLinksEditEntry] {
|
||||
var entries: [InviteLinksEditEntry] = []
|
||||
|
||||
entries.append(.titleHeader(presentationData.theme, presentationData.strings.InviteLink_Create_LinkNameTitle.uppercased()))
|
||||
@ -492,8 +492,12 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
|
||||
entries.append(.subscriptionFeeToggle(presentationData.theme, presentationData.strings.InviteLink_Create_Fee, state.subscriptionEnabled, isEditingEnabled))
|
||||
if state.subscriptionEnabled {
|
||||
var label: String = ""
|
||||
if let subscriptionFee = state.subscriptionFee, subscriptionFee > 0, let starsState {
|
||||
label = presentationData.strings.InviteLink_Create_FeePerMonth("≈\(formatTonUsdValue(subscriptionFee, divide: false, rate: starsState.usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
||||
if let subscriptionFee = state.subscriptionFee, subscriptionFee > 0 {
|
||||
var usdRate = 0.012
|
||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||
}
|
||||
label = presentationData.strings.InviteLink_Create_FeePerMonth("≈\(formatTonUsdValue(subscriptionFee, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
||||
}
|
||||
entries.append(.subscriptionFee(presentationData.theme, presentationData.strings.InviteLink_Create_FeePlaceholder, isEditingEnabled, state.subscriptionFee, label, configuration.maxFee))
|
||||
}
|
||||
@ -569,7 +573,7 @@ private struct InviteLinkEditControllerState: Equatable {
|
||||
var updating = false
|
||||
}
|
||||
|
||||
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 {
|
||||
public func inviteLinkEditController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, invite: ExportedInvitation?, completion: ((ExportedInvitation?) -> Void)? = nil) -> ViewController {
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
@ -791,7 +795,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 listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: inviteLinkEditControllerEntries(invite: invite, state: state, isGroup: isGroup, isPublic: isPublic, presentationData: presentationData, starsState: starsState, configuration: configuration), 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, configuration: configuration), style: .blocks, emptyStateItem: nil, crossfadeState: false, animateChanges: animateChanges)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
@ -854,22 +858,22 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
|
||||
return controller
|
||||
}
|
||||
|
||||
private struct StarsSubscriptionConfiguration {
|
||||
struct StarsSubscriptionConfiguration {
|
||||
static var defaultValue: StarsSubscriptionConfiguration {
|
||||
return StarsSubscriptionConfiguration(maxFee: 2500, usdSellRate: 2000)
|
||||
return StarsSubscriptionConfiguration(maxFee: 2500, usdWithdrawRate: 1200)
|
||||
}
|
||||
|
||||
let maxFee: Int64?
|
||||
let usdSellRate: Int64?
|
||||
let usdWithdrawRate: Int64?
|
||||
|
||||
fileprivate init(maxFee: Int64?, usdSellRate: Int64?) {
|
||||
fileprivate init(maxFee: Int64?, usdWithdrawRate: Int64?) {
|
||||
self.maxFee = maxFee
|
||||
self.usdSellRate = usdSellRate
|
||||
self.usdWithdrawRate = usdWithdrawRate
|
||||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> StarsSubscriptionConfiguration {
|
||||
if let data = appConfiguration.data, let value = data["stars_subscription_amount_max"] as? Double, let usdRate = data["stars_usd_sell_rate_x1000"] as? Double {
|
||||
return StarsSubscriptionConfiguration(maxFee: Int64(value), usdSellRate: Int64(usdRate))
|
||||
if let data = appConfiguration.data, let value = data["stars_subscription_amount_max"] as? Double, let usdRate = data["stars_usd_withdraw_rate_x1000"] as? Double {
|
||||
return StarsSubscriptionConfiguration(maxFee: Int64(value), usdWithdrawRate: Int64(usdRate))
|
||||
} else {
|
||||
return .defaultValue
|
||||
}
|
||||
|
@ -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, starsState: StarsRevenueStats?) -> [InviteLinksListEntry] {
|
||||
private func inviteLinkListControllerEntries(presentationData: PresentationData, exportedInvitation: EngineExportedPeerInvitation?, peer: EnginePeer?, invites: [ExportedInvitation]?, revokedInvites: [ExportedInvitation]?, importers: PeerInvitationImportersState?, creators: [ExportedInvitationCreator], admin: ExportedInvitationCreator?, tick: Int32) -> [InviteLinksListEntry] {
|
||||
var entries: [InviteLinksListEntry] = []
|
||||
|
||||
if admin == nil {
|
||||
@ -393,7 +393,7 @@ private struct InviteLinkListControllerState: Equatable {
|
||||
var revokingPrivateLink: Bool
|
||||
}
|
||||
|
||||
public func inviteLinkListController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, admin: ExportedInvitationCreator?, starsRevenueContext: StarsRevenueStatsContext? = nil) -> ViewController {
|
||||
public func inviteLinkListController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, admin: ExportedInvitationCreator?) -> ViewController {
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var presentInGlobalOverlayImpl: ((ViewController) -> Void)?
|
||||
@ -409,9 +409,6 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
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()
|
||||
actionsDisposable.add(revokeLinkDisposable)
|
||||
|
||||
@ -490,7 +487,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
}
|
||||
presentControllerImpl?(shareController, nil)
|
||||
}, openMainLink: { invite in
|
||||
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: nil, revokedInvitationsContext: revokedInvitesContext, importersContext: nil, starsState: starsStats.with { $0 })
|
||||
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: nil, revokedInvitationsContext: revokedInvitesContext, importersContext: nil)
|
||||
pushControllerImpl?(controller)
|
||||
}, copyLink: { invite in
|
||||
UIPasteboard.general.string = invite.link
|
||||
@ -607,7 +604,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)
|
||||
presentInGlobalOverlayImpl?(contextController)
|
||||
}, createLink: {
|
||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, starsState: starsStats.with( { $0 }), completion: { invite in
|
||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: nil, completion: { invite in
|
||||
if let invite = invite {
|
||||
invitesContext.add(invite)
|
||||
}
|
||||
@ -616,7 +613,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
pushControllerImpl?(controller)
|
||||
}, openLink: { invite in
|
||||
if let invite = invite {
|
||||
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: invitesContext, revokedInvitationsContext: revokedInvitesContext, importersContext: nil, starsState: starsStats.with { $0 })
|
||||
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: invitesContext, revokedInvitationsContext: revokedInvitesContext, importersContext: nil)
|
||||
pushControllerImpl?(controller)
|
||||
}
|
||||
}, linkContextAction: { invite, canEdit, node, gesture in
|
||||
@ -733,7 +730,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
}, action: { _, f in
|
||||
f(.default)
|
||||
|
||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, starsState: starsStats.with( { $0 }), completion: { invite in
|
||||
let controller = inviteLinkEditController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, completion: { invite in
|
||||
if let invite = invite {
|
||||
if invite.isRevoked {
|
||||
invitesContext.remove(invite)
|
||||
@ -900,14 +897,12 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|
||||
invitesContext.state,
|
||||
revokedInvitesContext.state,
|
||||
creators,
|
||||
timerPromise.get(),
|
||||
starsContext.state
|
||||
timerPromise.get()
|
||||
)
|
||||
|> map { presentationData, exportedInvitation, peer, importersContext, importers, invites, revokedInvites, creators, tick, starsState -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|> map { presentationData, exportedInvitation, peer, importersContext, importers, invites, revokedInvites, creators, tick -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let previousInvites = previousInvites.swap(invites)
|
||||
let previousRevokedInvites = previousRevokedInvites.swap(revokedInvites)
|
||||
let previousCreators = previousCreators.swap(creators)
|
||||
let _ = starsStats.swap(starsState.stats)
|
||||
|
||||
var crossfade = false
|
||||
if (previousInvites?.hasLoadedOnce ?? false) != (invites.hasLoadedOnce) {
|
||||
@ -933,7 +928,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 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)
|
||||
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)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
@ -380,20 +380,18 @@ public final class InviteLinkViewController: ViewController {
|
||||
private let invitationsContext: PeerExportedInvitationsContext?
|
||||
private let revokedInvitationsContext: PeerExportedInvitationsContext?
|
||||
private let importersContext: PeerInvitationImportersContext?
|
||||
private let starsState: StarsRevenueStats?
|
||||
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
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?, starsState: StarsRevenueStats? = nil) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, invite: ExportedInvitation, invitationsContext: PeerExportedInvitationsContext?, revokedInvitationsContext: PeerExportedInvitationsContext?, importersContext: PeerInvitationImportersContext?) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.invite = invite
|
||||
self.invitationsContext = invitationsContext
|
||||
self.revokedInvitationsContext = revokedInvitationsContext
|
||||
self.importersContext = importersContext
|
||||
self.starsState = starsState
|
||||
|
||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@ -522,6 +520,8 @@ public final class InviteLinkViewController: ViewController {
|
||||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
self.controller = controller
|
||||
|
||||
let configuration = StarsSubscriptionConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
|
||||
self.importersContext = importersContext ?? context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .invite(invite: invite, requested: false))
|
||||
if case let .link(_, _, _, requestApproval, _, _, _, _, _, _, _, _, _) = invite, requestApproval {
|
||||
self.requestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .invite(invite: invite, requested: true))
|
||||
@ -589,15 +589,16 @@ public final class InviteLinkViewController: ViewController {
|
||||
}
|
||||
})
|
||||
}, 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
|
||||
|> deliverOnMainQueue).start(next: { [weak self] 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)
|
||||
var usdRate = 0.012
|
||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||
}
|
||||
let subscriptionController = context.sharedContext.makeStarsSubscriptionScreen(context: context, peer: peer, pricing: pricing, importer: importer, usdRate: usdRate)
|
||||
self?.controller?.push(subscriptionController)
|
||||
})
|
||||
}, copyLink: { [weak self] invite in
|
||||
@ -833,7 +834,10 @@ public final class InviteLinkViewController: ViewController {
|
||||
context.account.postbox.loadedPeerWithId(adminId)
|
||||
) |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, requestsState, creatorPeer in
|
||||
if let strongSelf = self {
|
||||
let usdRate = strongSelf.controller?.starsState?.usdRate
|
||||
var usdRate = 0.012
|
||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||
}
|
||||
|
||||
var entries: [InviteLinkViewEntry] = []
|
||||
|
||||
@ -845,12 +849,8 @@ public final class InviteLinkViewController: ViewController {
|
||||
var subtitle = presentationData.strings.InviteLink_SubscriptionFee_NoOneJoined
|
||||
if state.count > 0 {
|
||||
title += " x \(state.count)"
|
||||
if let usdRate {
|
||||
let usdValue = formatTonUsdValue(pricing.amount * Int64(state.count), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
subtitle = presentationData.strings.InviteLink_SubscriptionFee_ApproximateIncome(usdValue).string
|
||||
} else {
|
||||
subtitle = ""
|
||||
}
|
||||
}
|
||||
entries.append(.subscriptionPricing(presentationData.theme, title, subtitle))
|
||||
}
|
||||
@ -1001,7 +1001,7 @@ public final class InviteLinkViewController: ViewController {
|
||||
let revokedInvitationsContext = parentController.revokedInvitationsContext
|
||||
if let navigationController = navigationController {
|
||||
let updatedPresentationData = (self.presentationData, parentController.presentationDataPromise.get())
|
||||
let controller = inviteLinkEditController(context: self.context, updatedPresentationData: updatedPresentationData, peerId: self.peerId, invite: self.invite, starsState: self.controller?.starsState, completion: { [weak self] invite in
|
||||
let controller = inviteLinkEditController(context: self.context, updatedPresentationData: updatedPresentationData, peerId: self.peerId, invite: self.invite, completion: { [weak self] invite in
|
||||
if let invite = invite {
|
||||
if invite.isRevoked {
|
||||
invitationsContext?.remove(invite)
|
||||
|
@ -227,6 +227,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView
|
||||
}, performActiveSessionAction: { _, _ in
|
||||
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
}, openStories: { _, _ in
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
|
@ -376,6 +376,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
}, performActiveSessionAction: { _, _ in
|
||||
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
}, openStories: { _, _ in
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
|
@ -251,10 +251,12 @@ private func _internal_requestStarsSubscriptions(account: Account, peerId: Engin
|
||||
if let subscriptions {
|
||||
for entry in subscriptions {
|
||||
if let parsedSubscription = StarsContext.State.Subscription(apiSubscription: entry, transaction: transaction) {
|
||||
if !missingBalance || parsedSubscription.flags.contains(.missingBalance) {
|
||||
parsedSubscriptions.append(parsedSubscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return InternalStarsStatus(
|
||||
balance: balance,
|
||||
subscriptionsMissingBalance: subscriptionsMissingBalance,
|
||||
@ -910,7 +912,7 @@ public final class StarsTransactionsContext {
|
||||
|
||||
private final class StarsSubscriptionsContextImpl {
|
||||
private let account: Account
|
||||
private weak var starsContext: StarsContext?
|
||||
private let missingBalance: Bool
|
||||
|
||||
private var _state: StarsSubscriptionsContext.State
|
||||
private let _statePromise = Promise<StarsSubscriptionsContext.State>()
|
||||
@ -923,16 +925,16 @@ private final class StarsSubscriptionsContextImpl {
|
||||
private var stateDisposable: Disposable?
|
||||
private let updateDisposable = MetaDisposable()
|
||||
|
||||
init(account: Account, starsContext: StarsContext) {
|
||||
init(account: Account, starsContext: StarsContext?, missingBalance: Bool) {
|
||||
assert(Queue.mainQueue().isCurrent())
|
||||
|
||||
self.account = account
|
||||
self.starsContext = starsContext
|
||||
self.missingBalance = missingBalance
|
||||
|
||||
let currentSubscriptions = starsContext.currentState?.subscriptions ?? []
|
||||
let canLoadMore = starsContext.currentState?.canLoadMoreSubscriptions ?? true
|
||||
let currentSubscriptions = starsContext?.currentState?.subscriptions ?? []
|
||||
let canLoadMore = starsContext?.currentState?.canLoadMoreSubscriptions ?? true
|
||||
|
||||
self._state = StarsSubscriptionsContext.State(subscriptions: currentSubscriptions, canLoadMore: canLoadMore, isLoading: false)
|
||||
self._state = StarsSubscriptionsContext.State(balance: 0, subscriptions: currentSubscriptions, canLoadMore: canLoadMore, isLoading: false)
|
||||
self._statePromise.set(.single(self._state))
|
||||
|
||||
self.loadMore()
|
||||
@ -956,7 +958,7 @@ private final class StarsSubscriptionsContextImpl {
|
||||
updatedState.isLoading = true
|
||||
self.updateState(updatedState)
|
||||
|
||||
self.disposable.set((_internal_requestStarsSubscriptions(account: self.account, peerId: self.account.peerId, offset: nextOffset, missingBalance: false)
|
||||
self.disposable.set((_internal_requestStarsSubscriptions(account: self.account, peerId: self.account.peerId, offset: nextOffset, missingBalance: self.missingBalance)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
guard let self else {
|
||||
return
|
||||
@ -964,6 +966,7 @@ private final class StarsSubscriptionsContextImpl {
|
||||
self.nextOffset = status.nextSubscriptionsOffset
|
||||
|
||||
var updatedState = self._state
|
||||
updatedState.balance = status.subscriptionsMissingBalance ?? 0
|
||||
updatedState.subscriptions = nextOffset.isEmpty ? status.subscriptions : updatedState.subscriptions + status.subscriptions
|
||||
updatedState.isLoading = false
|
||||
updatedState.canLoadMore = self.nextOffset != nil
|
||||
@ -1021,11 +1024,13 @@ private final class StarsSubscriptionsContextImpl {
|
||||
|
||||
public final class StarsSubscriptionsContext {
|
||||
public struct State: Equatable {
|
||||
public var balance: Int64
|
||||
public var subscriptions: [StarsContext.State.Subscription]
|
||||
public var canLoadMore: Bool
|
||||
public var isLoading: Bool
|
||||
|
||||
init(subscriptions: [StarsContext.State.Subscription], canLoadMore: Bool, isLoading: Bool) {
|
||||
init(balance: Int64, subscriptions: [StarsContext.State.Subscription], canLoadMore: Bool, isLoading: Bool) {
|
||||
self.balance = balance
|
||||
self.subscriptions = subscriptions
|
||||
self.canLoadMore = canLoadMore
|
||||
self.isLoading = isLoading
|
||||
@ -1052,9 +1057,9 @@ public final class StarsSubscriptionsContext {
|
||||
}
|
||||
}
|
||||
|
||||
init(account: Account, starsContext: StarsContext) {
|
||||
init(account: Account, starsContext: StarsContext?, missingBalance: Bool) {
|
||||
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
|
||||
return StarsSubscriptionsContextImpl(account: account, starsContext: starsContext)
|
||||
return StarsSubscriptionsContextImpl(account: account, starsContext: starsContext, missingBalance: missingBalance)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -86,8 +86,8 @@ public extension TelegramEngine {
|
||||
return StarsTransactionsContext(account: self.account, subject: subject, mode: mode)
|
||||
}
|
||||
|
||||
public func peerStarsSubscriptionsContext(starsContext: StarsContext) -> StarsSubscriptionsContext {
|
||||
return StarsSubscriptionsContext(account: self.account, starsContext: starsContext)
|
||||
public func peerStarsSubscriptionsContext(starsContext: StarsContext?, missingBalance: Bool = false) -> StarsSubscriptionsContext {
|
||||
return StarsSubscriptionsContext(account: self.account, starsContext: starsContext, missingBalance: missingBalance)
|
||||
}
|
||||
|
||||
public func sendStarsPaymentForm(formId: Int64, source: BotPaymentInvoiceSource) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> {
|
||||
|
@ -33,6 +33,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||
public var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
|
||||
|
||||
public let statusView: ComponentView<Empty>
|
||||
private var starView: StarView?
|
||||
|
||||
private var cachedDataDisposable = MetaDisposable()
|
||||
private var hierarchyTrackingLayer: HierarchyTrackingLayer?
|
||||
@ -120,6 +121,25 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||
self.context = context
|
||||
self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad, displayDimensions: displayDimensions, storeUnrounded: storeUnrounded)
|
||||
|
||||
if let peer, peer.isSubscription {
|
||||
let starView: StarView
|
||||
if let current = self.starView {
|
||||
starView = current
|
||||
} else {
|
||||
starView = StarView()
|
||||
self.starView = starView
|
||||
self.containerNode.view.addSubview(starView)
|
||||
}
|
||||
starView.outlineColor = theme.rootController.navigationBar.opaqueBackgroundColor
|
||||
|
||||
let starSize = CGSize(width: 15.0, height: 15.0)
|
||||
let starFrame = CGRect(origin: CGPoint(x: self.containerNode.bounds.width - starSize.width + 1.0, y: self.containerNode.bounds.height - starSize.height + 1.0), size: starSize)
|
||||
starView.frame = starFrame
|
||||
} else if let starView = self.starView {
|
||||
self.starView = nil
|
||||
starView.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let peer = peer, peer.isPremium {
|
||||
self.cachedDataDisposable.set((context.account.postbox.peerView(id: peer.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peerView in
|
||||
@ -283,3 +303,33 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
self.outline.frame = self.bounds
|
||||
self.foreground.frame = self.bounds
|
||||
}
|
||||
}
|
||||
|
@ -664,6 +664,8 @@ public final class ChatInlineSearchResultsListComponent: Component {
|
||||
},
|
||||
openStories: { _, _ in
|
||||
},
|
||||
openStarsTopup: { _ in
|
||||
},
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
|
@ -306,6 +306,7 @@ private class ExtendedMediaOverlayNode: ASDisplayNode {
|
||||
func reveal(animated: Bool = false) {
|
||||
self.isRevealed = true
|
||||
if animated {
|
||||
self.dustNode.revealOnTap = true
|
||||
self.dustNode.tap(at: CGPoint(x: self.dustNode.bounds.width / 2.0, y: self.dustNode.bounds.height / 2.0))
|
||||
} else {
|
||||
self.blurredImageNode.removeFromSupernode()
|
||||
|
@ -217,8 +217,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
invite: invite,
|
||||
invitationsContext: nil,
|
||||
revokedInvitationsContext: nil,
|
||||
importersContext: nil,
|
||||
starsState: strongSelf.controller?.starsState
|
||||
importersContext: nil
|
||||
)
|
||||
strongSelf.pushController(controller)
|
||||
}
|
||||
|
@ -182,7 +182,8 @@ public final class LoadingOverlayNode: ASDisplayNode {
|
||||
let interaction = ChatListNodeInteraction(context: context, animationCache: context.animationCache, animationRenderer: context.animationRenderer, activateSearch: {}, peerSelected: { _, _, _, _ in }, disabledPeerSelected: { _, _, _ in }, togglePeerSelected: { _, _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in
|
||||
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
|
||||
@ -533,6 +534,8 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod
|
||||
|
||||
StoryContainerScreen.openPeerStories(context: item.context, peerId: item.data.peer.id, parentController: controller, avatarNode: itemNode.avatarNode)
|
||||
},
|
||||
openStarsTopup: { _ in
|
||||
},
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
|
@ -202,6 +202,8 @@ final class GreetingMessageListItemComponent: Component {
|
||||
},
|
||||
openStories: { _, _ in
|
||||
},
|
||||
openStarsTopup: { _ in
|
||||
},
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
|
@ -217,6 +217,8 @@ final class QuickReplySetupScreenComponent: Component {
|
||||
},
|
||||
openStories: { _, _ in
|
||||
},
|
||||
openStarsTopup: { _ in
|
||||
},
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { [weak listNode] _ in
|
||||
|
@ -870,6 +870,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate
|
||||
}, performActiveSessionAction: { _, _ in
|
||||
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
|
||||
}, openStories: { _, _ in
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
|
@ -467,7 +467,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
|
||||
}
|
||||
},
|
||||
tapAction: { attributes, _ in
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_Purchase_Terms_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: strings.Stars_Purchase_Terms_URL, forceExternal: false, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
}
|
||||
),
|
||||
environment: {},
|
||||
@ -1169,7 +1169,7 @@ func generateStarsIcon(count: Int) -> UIImage {
|
||||
|
||||
let mainImage = UIImage(bundleImageName: "Premium/Stars/StarLarge")
|
||||
if let cgImage = mainImage?.cgImage, let partCGImage = partImage.cgImage {
|
||||
context.draw(cgImage, in: CGRect(origin: CGPoint(x: originX, y: 0.0), size: imageSize), byTiling: false)
|
||||
context.draw(cgImage, in: CGRect(origin: CGPoint(x: originX, y: 0.0), size: imageSize).insetBy(dx: -1.5, dy: -1.5), byTiling: false)
|
||||
originX += spacing + UIScreenPixel
|
||||
|
||||
for _ in 0 ..< count - 1 {
|
||||
|
@ -241,18 +241,26 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
isSubscription = true
|
||||
|
||||
var hasLeft = false
|
||||
if let toPeer, case let .channel(channel) = toPeer, channel.participationStatus == .left {
|
||||
var isKicked = false
|
||||
if let toPeer, case let .channel(channel) = toPeer {
|
||||
switch channel.participationStatus {
|
||||
case .left:
|
||||
hasLeft = true
|
||||
case .kicked:
|
||||
isKicked = true
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasLeft {
|
||||
if hasLeft || isKicked {
|
||||
if subscription.flags.contains(.isCancelled) {
|
||||
statusText = strings.Stars_Transaction_Subscription_Cancelled
|
||||
statusIsDestructive = true
|
||||
if date > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
|
||||
buttonText = strings.Stars_Transaction_Subscription_Renew
|
||||
} else {
|
||||
if let _ = subscription.inviteHash {
|
||||
if let _ = subscription.inviteHash, !isKicked {
|
||||
buttonText = strings.Stars_Transaction_Subscription_JoinAgainChannel
|
||||
} else {
|
||||
buttonText = strings.Common_OK
|
||||
@ -260,6 +268,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
}
|
||||
} else {
|
||||
if date < Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
|
||||
statusText = strings.Stars_Transaction_Subscription_Expired(stringForMediumDate(timestamp: subscription.untilDate, strings: strings, dateTimeFormat: dateTimeFormat, withTime: false)).string
|
||||
buttonText = strings.Stars_Transaction_Subscription_Renew
|
||||
} else {
|
||||
statusText = strings.Stars_Transaction_Subscription_LeftChannel(stringForMediumDate(timestamp: subscription.untilDate, strings: strings, dateTimeFormat: dateTimeFormat, withTime: false)).string
|
||||
@ -1135,7 +1144,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
theme: forceDark ? .dark : .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .standaloneModal
|
||||
self.navigationPresentation = .standaloneFlatModal
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
|
||||
openPeerImpl = { [weak self] peer in
|
||||
|
@ -323,10 +323,15 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let isFirstTime = self.subscriptionsState == nil
|
||||
if !state.subscriptions.isEmpty {
|
||||
self.subscriptionsState = state
|
||||
} else {
|
||||
self.subscriptionsState = nil
|
||||
}
|
||||
|
||||
if !self.isUpdating {
|
||||
self.state?.updated()
|
||||
self.state?.updated(transition: isFirstTime ? .immediate : .spring(duration: 0.4))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -628,14 +633,18 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
)
|
||||
let dateText: String
|
||||
let dateValue = stringForDateWithoutYear(date: Date(timeIntervalSince1970: Double(subscription.untilDate)), strings: environment.strings)
|
||||
if subscription.flags.contains(.isCancelled) {
|
||||
var isExpired = false
|
||||
if subscription.untilDate > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
|
||||
if subscription.flags.contains(.isCancelled) {
|
||||
dateText = environment.strings.Stars_Intro_Subscriptions_Expires(dateValue).string
|
||||
} else {
|
||||
dateText = environment.strings.Stars_Intro_Subscriptions_Expired(dateValue).string
|
||||
dateText = environment.strings.Stars_Intro_Subscriptions_Renews(dateValue).string
|
||||
}
|
||||
} else {
|
||||
dateText = environment.strings.Stars_Intro_Subscriptions_Renews(dateValue).string
|
||||
dateText = environment.strings.Stars_Intro_Subscriptions_Expired(dateValue).string
|
||||
if !subscription.flags.contains(.isCancelled) {
|
||||
isExpired = true
|
||||
}
|
||||
}
|
||||
titleComponents.append(
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||
@ -648,9 +657,9 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
)))
|
||||
)
|
||||
let labelComponent: AnyComponentWithIdentity<Empty>
|
||||
if subscription.flags.contains(.isCancelled) {
|
||||
if subscription.flags.contains(.isCancelled) || isExpired {
|
||||
labelComponent = AnyComponentWithIdentity(id: "cancelledLabel", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Stars_Intro_Subscriptions_Cancelled, font: Font.regular(floor(fontBaseDisplaySize * 13.0 / 17.0)), textColor: environment.theme.list.itemDestructiveColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: isExpired ? environment.strings.Stars_Intro_Subscriptions_ExpiredStatus : environment.strings.Stars_Intro_Subscriptions_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)
|
||||
@ -710,6 +719,7 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
} else {
|
||||
self.subscriptionsExpanded = true
|
||||
}
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
component.subscriptionsContext.loadMore()
|
||||
},
|
||||
highlighting: .default,
|
||||
@ -723,7 +733,7 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
|
||||
if !subscriptionsItems.isEmpty {
|
||||
let subscriptionsSize = self.subscriptionsView.update(
|
||||
transition: .immediate,
|
||||
transition: transition,
|
||||
component: AnyComponent(ListSectionComponent(
|
||||
theme: environment.theme,
|
||||
header: AnyComponent(MultilineTextComponent(
|
||||
@ -902,6 +912,8 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
|
||||
private let options = Promise<[StarsTopUpOption]>()
|
||||
|
||||
private let navigateDisposable = MetaDisposable()
|
||||
|
||||
public init(context: AccountContext, starsContext: StarsContext, forceDark: Bool = false) {
|
||||
self.context = context
|
||||
self.starsContext = starsContext
|
||||
@ -956,7 +968,22 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.subscriptionsContext.updateSubscription(id: subscription.id, cancel: cancel)
|
||||
if subscription.untilDate > Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) {
|
||||
if let channel = subscription.peer._asPeer() as? TelegramChannel, channel.participationStatus == .left {
|
||||
let _ = self.context.engine.payments.fulfillStarsSubscription(peerId: context.account.peerId, subscriptionId: subscription.id).startStandalone()
|
||||
if subscription.flags.contains(.isCancelled) {
|
||||
self.subscriptionsContext.updateSubscription(id: subscription.id, cancel: false)
|
||||
}
|
||||
} else {
|
||||
if !subscription.flags.contains(.isCancelled) {
|
||||
self.subscriptionsContext.updateSubscription(id: subscription.id, cancel: true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let inviteHash = subscription.inviteHash {
|
||||
self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: nil, navigateDisposable: self.navigateDisposable, controller: self, action: .tap, itemLink: .url(url: "https://t.me/+\(inviteHash)", concealed: false))
|
||||
}
|
||||
}
|
||||
})
|
||||
self.push(controller)
|
||||
}
|
||||
@ -1074,7 +1101,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
deinit {
|
||||
self.navigateDisposable.dispose()
|
||||
}
|
||||
}
|
||||
|
@ -800,7 +800,7 @@ public final class StarsTransferScreen: ViewControllerComponentContainer {
|
||||
theme: .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .standaloneModal
|
||||
self.navigationPresentation = .flatModal
|
||||
|
||||
starsContext.load(force: false)
|
||||
}
|
||||
|
@ -524,7 +524,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
|
||||
theme: .default
|
||||
)
|
||||
|
||||
self.navigationPresentation = .standaloneModal
|
||||
self.navigationPresentation = .flatModal
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
@ -195,7 +195,13 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u
|
||||
return
|
||||
}
|
||||
var presentImpl: ((ViewController, Any?) -> Void)?
|
||||
let params = WebAppParameters(source: isInline ? .inline : .simple, peerId: peer.id, botId: botId, botName: botName, botVerified: botVerified, url: result.url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize))
|
||||
let source: WebAppParameters.Source
|
||||
if isInline {
|
||||
source = .inline
|
||||
} else {
|
||||
source = url.isEmpty ? .generic : .simple
|
||||
}
|
||||
let params = WebAppParameters(source: source, peerId: peer.id, botId: botId, botName: botName, botVerified: botVerified, url: result.url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize))
|
||||
let controller = standaloneWebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, threadId: threadId, openUrl: { [weak parentController] url, concealed, commit in
|
||||
ChatControllerImpl.botOpenUrl(context: context, peerId: peer.id, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, present: { c, a in
|
||||
presentImpl?(c, a)
|
||||
|
@ -290,6 +290,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe
|
||||
}, openChatFolderUpdates: {
|
||||
}, hideChatFolderUpdates: {
|
||||
}, openStories: { _, _ in
|
||||
}, openStarsTopup: { _ in
|
||||
}, dismissNotice: { _ in
|
||||
}, editPeer: { _ in
|
||||
})
|
||||
|
@ -172,6 +172,8 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
|
||||
},
|
||||
openStories: { _, _ in
|
||||
},
|
||||
openStarsTopup: { _ in
|
||||
},
|
||||
dismissNotice: { _ in
|
||||
},
|
||||
editPeer: { _ in
|
||||
|
@ -92,15 +92,15 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: EnginePeer.Id?, n
|
||||
let sourceLocation = InstantPageSourceLocation(userLocation: peerId.flatMap(MediaResourceUserLocation.peer) ?? .other, peerType: .group)
|
||||
let browserController = context.sharedContext.makeInstantPageController(context: context, webPage: webPage, anchor: anchor, sourceLocation: sourceLocation)
|
||||
(controller.navigationController as? NavigationController)?.pushViewController(browserController, animated: true)
|
||||
case let .join(link):
|
||||
controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peer, peekData in
|
||||
openResolvedPeerImpl(peer, .chat(textInputState: nil, subject: nil, peekData: peekData))
|
||||
}, parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root))
|
||||
case .boost, .chatFolder:
|
||||
// case let .join(link):
|
||||
// controller.present(JoinLinkPreviewController(context: context, link: link, navigateToPeer: { peer, peekData in
|
||||
// openResolvedPeerImpl(peer, .chat(textInputState: nil, subject: nil, peekData: peekData))
|
||||
// }, parentNavigationController: controller.navigationController as? NavigationController), in: .window(.root))
|
||||
case .boost, .chatFolder, .join:
|
||||
if let navigationController = controller.navigationController as? NavigationController {
|
||||
openResolvedUrlImpl(result, context: context, urlContext: peerId.flatMap { .chat(peerId: $0, message: nil, updatedPresentationData: nil) } ?? .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigateToPeer in
|
||||
openResolvedPeerImpl(peer, navigateToPeer)
|
||||
}, sendFile: nil, sendSticker: nil, sendEmoji: nil, joinVoiceChat: nil, present: { c, a in }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil)
|
||||
}, sendFile: nil, sendSticker: nil, sendEmoji: nil, joinVoiceChat: nil, present: { c, a in }, dismissInput: {}, contentContext: nil, progress: Promise(), completion: nil)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
Loading…
x
Reference in New Issue
Block a user