[WIP] Birthdays

This commit is contained in:
Ilya Laktyushin 2024-03-14 16:20:50 +04:00
parent 636f35cb8e
commit a509b096a9
21 changed files with 129 additions and 21 deletions

View File

@ -2308,8 +2308,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}, openStorageManagement: {
}, openPasswordSetup: {
}, openPremiumIntro: {
}, openPremiumGift: {
}, openPremiumGift: { _ in
}, openActiveSessions: {
}, openBirthdaySetup: {
}, performActiveSessionAction: { _, _ in
}, openChatFolderUpdates: {
}, hideChatFolderUpdates: {
@ -3685,7 +3686,8 @@ public final class ChatListSearchShimmerNode: 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: {}, openActiveSessions: {
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: {
}, openBirthdaySetup: {
}, performActiveSessionAction: { _, _ in
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
}, openStories: { _, _ in

View File

@ -156,7 +156,7 @@ 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: {}, openActiveSessions: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, dismissNotice: { _ in
}, editPeer: { _ in
})
interaction.isInlineMode = isInlineMode

View File

@ -101,8 +101,9 @@ public final class ChatListNodeInteraction {
let openStorageManagement: () -> Void
let openPasswordSetup: () -> Void
let openPremiumIntro: () -> Void
let openPremiumGift: () -> Void
let openPremiumGift: ([EnginePeer.Id]) -> Void
let openActiveSessions: () -> Void
let openBirthdaySetup: () -> Void
let performActiveSessionAction: (NewSessionReview, Bool) -> Void
let openChatFolderUpdates: () -> Void
let hideChatFolderUpdates: () -> Void
@ -154,8 +155,9 @@ public final class ChatListNodeInteraction {
openStorageManagement: @escaping () -> Void,
openPasswordSetup: @escaping () -> Void,
openPremiumIntro: @escaping () -> Void,
openPremiumGift: @escaping () -> Void,
openPremiumGift: @escaping ([EnginePeer.Id]) -> Void,
openActiveSessions: @escaping () -> Void,
openBirthdaySetup: @escaping () -> Void,
performActiveSessionAction: @escaping (NewSessionReview, Bool) -> Void,
openChatFolderUpdates: @escaping () -> Void,
hideChatFolderUpdates: @escaping () -> Void,
@ -196,6 +198,7 @@ public final class ChatListNodeInteraction {
self.openPremiumIntro = openPremiumIntro
self.openPremiumGift = openPremiumGift
self.openActiveSessions = openActiveSessions
self.openBirthdaySetup = openBirthdaySetup
self.performActiveSessionAction = performActiveSessionAction
self.openChatFolderUpdates = openChatFolderUpdates
self.hideChatFolderUpdates = hideChatFolderUpdates
@ -732,7 +735,11 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore:
nodeInteraction?.openPremiumIntro()
case .xmasPremiumGift:
nodeInteraction?.openPremiumGift()
nodeInteraction?.openPremiumGift([])
case .setupBirthday:
nodeInteraction?.openBirthdaySetup()
case let .birthdayPremiumGift(peers):
nodeInteraction?.openPremiumGift(peers.map { $0.id })
case .reviewLogin:
break
}
@ -1064,7 +1071,11 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore:
nodeInteraction?.openPremiumIntro()
case .xmasPremiumGift:
nodeInteraction?.openPremiumGift()
nodeInteraction?.openPremiumGift([])
case .setupBirthday:
nodeInteraction?.openBirthdaySetup()
case let .birthdayPremiumGift(peers):
nodeInteraction?.openPremiumGift(peers.map { $0.id })
case .reviewLogin:
break
}
@ -1682,7 +1693,7 @@ public final class ChatListNode: ListView {
}
let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .ads, forceDark: false, dismissed: nil)
self.push?(controller)
}, openPremiumGift: { [weak self] in
}, openPremiumGift: { [weak self] peerIds in
guard let self else {
return
}
@ -1707,6 +1718,8 @@ public final class ChatListNode: ListView {
let recentSessionsController = self.context.sharedContext.makeRecentSessionsController(context: self.context, activeSessionsContext: activeSessionsContext)
self.push?(recentSessionsController)
})
}, openBirthdaySetup: {
}, performActiveSessionAction: { [weak self] newSessionReview, isPositive in
guard let self else {
return
@ -1785,6 +1798,12 @@ public final class ChatListNode: ListView {
self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in
return true
}))
case .setupBirthday:
//TODO:localize
let _ = dismissServerProvidedSuggestion(account: self.context.account, suggestion: .setupBirthday).startStandalone()
self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in
return true
}))
default:
break
}
@ -1899,7 +1918,9 @@ public final class ChatListNode: ListView {
return .single(.setupPassword)
}
}
if suggestions.contains(.xmasPremiumGift) {
if suggestions.contains(.setupBirthday) {
return .single(.setupBirthday)
} else if suggestions.contains(.xmasPremiumGift) {
return .single(.xmasPremiumGift)
} else if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium) || suggestions.contains(.restorePremium), let inAppPurchaseManager = context.inAppPurchaseManager {
return inAppPurchaseManager.availableProducts

View File

@ -86,6 +86,8 @@ public enum ChatListNotice: Equatable {
case premiumAnnualDiscount(discount: Int32)
case premiumRestore(discount: Int32)
case xmasPremiumGift
case setupBirthday
case birthdayPremiumGift(peers: [EnginePeer])
case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int)
}

View File

@ -220,6 +220,23 @@ class ChatListStorageInfoItemNode: ItemListRevealOptionsItemNode {
case .xmasPremiumGift:
titleString = parseMarkdownIntoAttributedString(item.strings.ChatList_PremiumXmasGiftTitle, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), linkAttribute: { _ in return nil }))
textString = NSAttributedString(string: item.strings.ChatList_PremiumXmasGiftText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
case .setupBirthday:
//TODO:localize
titleString = NSAttributedString(string: "Add your birthday! 🎂", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)
textString = NSAttributedString(string: "Let your contacts know when you're celebrating.", font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
case let .birthdayPremiumGift(peers):
//TODO:localize
let title: String
let text: String
if peers.count == 1, let peer = peers.first {
title = "It's \(peer.compactDisplayTitle)'s [birthday]() today! 🎂"
text = "Gift them Telegram Premium."
} else {
title = "\(peers.count) contacts have [birthdays]() today! 🎂"
text = "Gift them Telegram Premium."
}
titleString = parseMarkdownIntoAttributedString(title, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), linkAttribute: { _ in return nil }))
textString = NSAttributedString(string: text, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor)
case let .reviewLogin(newSessionReview, totalCount):
spacing = 2.0
alignment = .center

View File

@ -95,8 +95,9 @@ public final class HashtagSearchController: TelegramBaseController {
}, openStorageManagement: {
}, openPasswordSetup: {
}, openPremiumIntro: {
}, openPremiumGift: {
}, openPremiumGift: { _ in
}, openActiveSessions: {
}, openBirthdaySetup: {
}, performActiveSessionAction: { _, _ in
}, openChatFolderUpdates: {
}, hideChatFolderUpdates: {

View File

@ -847,7 +847,6 @@ struct PremiumIntroConfiguration {
}
#endif
var businessPerks: [PremiumPerk] = []
if let values = data["business_promo_order"] as? [String] {
for value in values {

View File

@ -222,7 +222,8 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
}, 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: {}, openActiveSessions: {
}, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: {
}, openBirthdaySetup: {
}, performActiveSessionAction: { _, _ in
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
}, openStories: { _, _ in

View File

@ -371,7 +371,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: {}, openActiveSessions: {
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: {
}, openBirthdaySetup: {
}, performActiveSessionAction: { _, _ in
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
}, openStories: { _, _ in

View File

@ -13,6 +13,7 @@ public enum ServerProvidedSuggestion: String {
case annualPremium = "PREMIUM_ANNUAL"
case restorePremium = "PREMIUM_RESTORE"
case xmasPremiumGift = "PREMIUM_CHRISTMAS"
case setupBirthday = "BIRTHDAY_SETUP"
}
private var dismissedSuggestionsPromise = ValuePromise<[AccountRecordId: Set<ServerProvidedSuggestion>]>([:])

View File

@ -620,10 +620,12 @@ public final class ChatInlineSearchResultsListComponent: Component {
},
openPremiumIntro: {
},
openPremiumGift: {
openPremiumGift: { _ in
},
openActiveSessions: {
},
openBirthdaySetup: {
},
performActiveSessionAction: { _, _ in
},
openChatFolderUpdates: {

View File

@ -186,10 +186,12 @@ final class GreetingMessageListItemComponent: Component {
},
openPremiumIntro: {
},
openPremiumGift: {
openPremiumGift: { _ in
},
openActiveSessions: {
},
openBirthdaySetup: {
},
performActiveSessionAction: { _, _ in
},
openChatFolderUpdates: {

View File

@ -201,10 +201,12 @@ final class QuickReplySetupScreenComponent: Component {
},
openPremiumIntro: {
},
openPremiumGift: {
openPremiumGift: { _ in
},
openActiveSessions: {
},
openBirthdaySetup: {
},
performActiveSessionAction: { _, _ in
},
openChatFolderUpdates: {

View File

@ -865,7 +865,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
}, activateChatPreview: { _, _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: {}, openActiveSessions: {
}, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _ in }, openActiveSessions: {
}, openBirthdaySetup: {
}, performActiveSessionAction: { _, _ in
}, openChatFolderUpdates: {}, hideChatFolderUpdates: {
}, openStories: { _, _ in

View File

@ -451,6 +451,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
weak var checksTooltipController: TooltipController?
weak var copyProtectionTooltipController: TooltipController?
weak var emojiPackTooltipController: TooltipScreen?
weak var birthdayTooltipController: TooltipScreen?
var currentMessageTooltipScreens: [(TooltipScreen, ListViewItemNode)] = []
@ -12239,6 +12240,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
}
// #if DEBUG
// Queue.mainQueue().after(0.5) {
// self.displayBirthdayTooltip()
// }
// #endif
}
override public func viewWillDisappear(_ animated: Bool) {
@ -15967,6 +15974,30 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
func displayBirthdayTooltip() {
guard let rect = self.chatDisplayNode.frameForGiftButton(), self.effectiveNavigationController?.topViewController === self, let peer = self.presentationInterfaceState.renderedPeer?.peer.flatMap({ EnginePeer($0) }) else {
return
}
let peerName = peer.compactDisplayTitle
let text = "🎂 \(peerName) is having a birthday today. You can give \(peerName) **Telegram Premium** as a birthday gift."
let tooltipScreen = TooltipScreen(
context: self.context,
account: self.context.account,
sharedContext: self.context.sharedContext,
text: .markdown(text: text),
location: .point(rect.offsetBy(dx: 0.0, dy: -3.0), .bottom),
displayDuration: .default,
cornerRadius: 10.0,
shouldDismissOnTouch: { _, _ in
return .ignore
}
)
self.present(tooltipScreen, in: .current)
self.birthdayTooltipController = tooltipScreen
}
func displayChecksTooltip() {
self.checksTooltipController?.dismiss()

View File

@ -3385,6 +3385,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
return nil
}
func frameForGiftButton() -> CGRect? {
if let textInputPanelNode = self.textInputPanelNode, self.inputPanelNode === textInputPanelNode {
return textInputPanelNode.frameForGiftButton().flatMap {
return $0.offsetBy(dx: textInputPanelNode.frame.minX, dy: textInputPanelNode.frame.minY)
}
}
return nil
}
var isTextInputPanelActive: Bool {
return self.inputPanelNode is ChatTextInputPanelNode
}

View File

@ -167,7 +167,11 @@ func inputTextPanelStateForChatPresentationInterfaceState(_ chatPresentationInte
if case .scheduledMessages = chatPresentationInterfaceState.subject {
} else {
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
let giftIsEnabled = !premiumConfiguration.isPremiumDisabled && premiumConfiguration.showPremiumGiftInAttachMenu && premiumConfiguration.showPremiumGiftInTextField
var giftIsEnabled = false
giftIsEnabled = !premiumConfiguration.isPremiumDisabled && premiumConfiguration.showPremiumGiftInAttachMenu && premiumConfiguration.showPremiumGiftInTextField
#if DEBUG
giftIsEnabled = true
#endif
if isTextEmpty, giftIsEnabled, let peer = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, !peer.isDeleted && peer.botInfo == nil && !peer.flags.contains(.isSupport) && !peer.isPremium && !chatPresentationInterfaceState.premiumGiftOptions.isEmpty && chatPresentationInterfaceState.suggestPremiumGift {
accessoryItems.append(.gift)
}

View File

@ -282,8 +282,9 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
}, openStorageManagement: {
}, openPasswordSetup: {
}, openPremiumIntro: {
}, openPremiumGift: {
}, openPremiumGift: { _ in
}, openActiveSessions: {
}, openBirthdaySetup: {
}, performActiveSessionAction: { _, _ in
}, openChatFolderUpdates: {
}, hideChatFolderUpdates: {

View File

@ -4595,6 +4595,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
return nil
}
func frameForGiftButton() -> CGRect? {
for (item, button) in self.accessoryItemButtons {
if case .gift = item {
return button.frame.insetBy(dx: 0.0, dy: 6.0)
}
}
return nil
}
func makeSnapshotForTransition() -> ChatMessageTransitionNodeImpl.Source.TextInput? {
guard let backgroundImage = self.transparentTextInputBackgroundImage else {
return nil

View File

@ -156,10 +156,12 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable {
},
openPremiumIntro: {
},
openPremiumGift: {
openPremiumGift: { _ in
},
openActiveSessions: {
},
openBirthdaySetup: {
},
performActiveSessionAction: { _, _ in
},
openChatFolderUpdates: {

View File

@ -159,7 +159,7 @@ final class WebAppWebView: WKWebView {
self.interactiveTransitionGestureRecognizerTest = { point -> Bool in
return point.x > 30.0
}
self.allowsBackForwardNavigationGestures = true
self.allowsBackForwardNavigationGestures = false
if #available(iOS 16.4, *) {
self.isInspectable = true
}