diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index d9241069d1..5406dc0d9d 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10675,6 +10675,9 @@ Sorry for the inconvenience."; "Premium.Gift.ContactSelection.DeselectAll" = "DESELECT ALL"; "Premium.Gift.ContactSelection.SelectAll" = "SELECT ALL"; "Premium.Gift.ContactSelection.MaximumReached" = "You can select up to %@ users."; +"Premium.Gift.ContactSelection.BirthdayToday" = "🎂 BIRTHDAY TODAY"; +"Premium.Gift.ContactSelection.BirthdayYesterday" = "BIRTHDAY YESTERDAY"; +"Premium.Gift.ContactSelection.BirthdayTomorrow" = "BIRTHDAY TOMORROW"; "Premium.Gift.GiftMultipleSubscriptionsFormat" = "%1$@ for %2$@"; "Premium.Gift.GiftMultipleSubscriptions_1" = "Gift %@ Subscription"; @@ -11642,3 +11645,15 @@ Sorry for the inconvenience."; "Birthday.Added" = "Date of birth added."; "Chat.BirthdayTooltip" = "🎂 %1$@ is having a birthday today. You can give %2$@ **Telegram Premium** as a birthday gift."; + +"ChatList.AddBirthdayTitle" = "Add your birthday! 🎂"; +"ChatList.AddBirthdayText" = "Let your contacts know when you're celebrating."; + +"ChatList.BirthdaySingleTitle" = "It's %@ **birthday** today! 🎂"; +"ChatList.BirthdaySingleText" = "Gift them Telegram Premium."; + +"ChatList.BirthdayMultipleTitle_1" = "%@ contact have **birthday** today! 🎂"; +"ChatList.BirthdayMultipleTitle_any" = "%@ contacts have **birthdays** today! 🎂"; +"ChatList.BirthdayMultipleText" = "Gift them Telegram Premium."; + +"ChatList.BirthdayInSettingsInfo" = "You can set your date of birth later in **Settings**."; diff --git a/submodules/AccountContext/Sources/ContactMultiselectionController.swift b/submodules/AccountContext/Sources/ContactMultiselectionController.swift index 3bb073859f..aa2de659a6 100644 --- a/submodules/AccountContext/Sources/ContactMultiselectionController.swift +++ b/submodules/AccountContext/Sources/ContactMultiselectionController.swift @@ -75,7 +75,7 @@ public enum ContactMultiselectionControllerMode { case peerSelection(searchChatList: Bool, searchGroups: Bool, searchChannels: Bool) case channelCreation case chatSelection(ChatSelection) - case premiumGifting(birthdays: [EnginePeer.Id: TelegramBirthday]?) + case premiumGifting(birthdays: [EnginePeer.Id: TelegramBirthday]?, selectToday: Bool) case requestedUsersSelection } diff --git a/submodules/AccountContext/Sources/Premium.swift b/submodules/AccountContext/Sources/Premium.swift index a753fcd03a..0e87f27e28 100644 --- a/submodules/AccountContext/Sources/Premium.swift +++ b/submodules/AccountContext/Sources/Premium.swift @@ -44,7 +44,7 @@ public enum PremiumIntroSource { public enum PremiumGiftSource: Equatable { case profile case attachMenu - case settings + case settings([EnginePeer.Id: TelegramBirthday]?) case chatList([EnginePeer.Id: TelegramBirthday]?) case channelBoost case deeplink(String?) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index bb82291dd6..a5eb6d69ee 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -5794,14 +5794,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let _ = context.engine.accountData.updateBirthday(birthday: value).startStandalone() - //TODO:localize let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } self.present(UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: nil, text: self.presentationData.strings.Birthday_Added, cancel: nil, destructive: false), elevatedLayout: false, action: { _ in return true }), in: .current) }) self.push(controller) - //self.present(controller, in: .current) } private var storyCameraTransitionInCoordinator: StoryCameraTransitionInCoordinator? diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 5e7077b62c..903328a479 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -1698,21 +1698,6 @@ public final class ChatListNode: ListView { guard let self else { return } - if let birthdays { - let today = Calendar(identifier: .gregorian).component(.day, from: Date()) - var todayBirthdayPeerIds: [EnginePeer.Id] = [] - for (peerId, birthday) in birthdays { - if birthday.day == today { - todayBirthdayPeerIds.append(peerId) - } - } - let peerIds = todayBirthdayPeerIds.sorted { lhs, rhs in - return lhs < rhs - } - Queue.mainQueue().after(0.4) { - let _ = ApplicationSpecificNotice.setDismissedBirthdayPremiumGifts(accountManager: self.context.sharedContext.accountManager, values: peerIds.map { $0.toInt64() }).start() - } - } let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .chatList(birthdays), completion: nil) controller.navigationPresentation = .modal self.push?(controller) @@ -1819,9 +1804,8 @@ public final class ChatListNode: ListView { return true })) case .setupBirthday: - //TODO:localize let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: .setupBirthday).startStandalone() - self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can set your date of birth later in **Settings**.", timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in + self.present?(UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, timeout: 5.0, customUndoText: nil), elevatedLayout: false, action: { _ in return true })) case let .birthdayPremiumGift(peers, _): diff --git a/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift b/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift index c3edb90eb6..c4e6bf4b68 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListStorageInfoItem.swift @@ -225,19 +225,17 @@ class ChatListStorageInfoItemNode: ItemListRevealOptionsItemNode { 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) + titleString = NSAttributedString(string: item.strings.ChatList_AddBirthdayTitle, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor) + textString = NSAttributedString(string: item.strings.ChatList_AddBirthdayText, 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." + title = item.strings.ChatList_BirthdaySingleTitle(peer.compactDisplayTitle).string + text = item.strings.ChatList_BirthdaySingleText } else { - title = "\(peers.count) contacts have **birthdays** today! 🎂" - text = "Gift them Telegram Premium." + title = item.strings.ChatList_BirthdayMultipleTitle(Int32(peers.count)) + text = item.strings.ChatList_BirthdayMultipleText } 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) diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 3dd6c92ae9..35634523ed 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -556,7 +556,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis case let .custom(sections): if !topPeers.isEmpty { var index: Int = 0 - var sectionId: Int = 0 + var sectionId: Int = 1 for (title, peerIds) in sections { var allSelected = true if let selectedPeerIndices = selectionState?.selectedPeerIndices, !selectedPeerIndices.isEmpty { @@ -575,6 +575,9 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis for peerId in peerIds { if let peer = topPeers.first(where: { $0.id == peerId }) { + if existingPeerIds.contains(.peer(peer.id)) { + continue + } existingPeerIds.insert(.peer(peer.id)) let selection: ContactsPeerItemSelection @@ -592,6 +595,30 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis } sectionId += 1 } + + let hasDeselectAll = !(selectionState?.selectedPeerIndices ?? [:]).isEmpty + let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text(strings.Premium_Gift_ContactSelection_FrequentContacts.uppercased(), AnyHashable(hasDeselectAll ? 1 : 0)), theme: theme, strings: strings, actionTitle: hasDeselectAll ? strings.Premium_Gift_ContactSelection_DeselectAll.uppercased() : nil, action: { + interaction.deselectAll() + }) + + for peer in topPeers.prefix(15) { + if existingPeerIds.contains(.peer(peer.id)) { + continue + } + existingPeerIds.insert(.peer(peer.id)) + + let selection: ContactsPeerItemSelection + if let selectionState = selectionState { + selection = .selectable(selected: selectionState.selectedPeerIndices[.peer(peer.id)] != nil) + } else { + selection = .none + } + + let presence = presences[peer.id] + entries.append(.peer(index, .peer(peer: peer._asPeer(), isGlobal: false, participantCount: nil), presence, header, selection, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, true, nil, false)) + + index += 1 + } } case .none: break @@ -1573,17 +1600,19 @@ public final class ContactListNode: ASDisplayNode { for (_, sectionPeers) in sections { peerIds.append(contentsOf: sectionPeers) } - - topPeers = context.engine.data.get( - EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) - ) - |> map { peers in + topPeers = combineLatest( + context.engine.data.get(EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))), + context.engine.peers.recentPeers() + ) |> map { peers, recentPeers in var result: [EnginePeer] = [] for peer in peers.values { if let peer { result.append(peer) } } + if case let .peers(peers) = recentPeers { + result.append(contentsOf: peers.map(EnginePeer.init)) + } return result } case .none: diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 561e6086d7..2756ae164f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -1032,7 +1032,6 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat birthday = (data.cachedData as? CachedUserData)?.birthday } - //TODO:localize var birthDateString: String if let birthday { birthDateString = stringForCompactBirthday(birthday, strings: presentationData.strings) @@ -9040,8 +9039,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .settings, forceDark: false, dismissed: nil) self.controller?.push(controller) case .premiumGift: - let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings, completion: nil) - self.controller?.push(controller) + let _ = (self.context.account.stateManager.contactBirthdays + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] birthdays in + guard let self else { + return + } + let controller = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil) + self.controller?.push(controller) + }) case .stickers: if let settings = self.data?.globalSettings { push(installedStickerPacksController(context: self.context, mode: .general, archivedPacks: settings.archivedStickerPacks, updatedPacks: { [weak self] packs in diff --git a/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift b/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift index f45451f102..cc7d1af946 100644 --- a/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift @@ -111,7 +111,6 @@ private final class BirthdayPickerSheetContentComponent: Component { transition.setFrame(view: cancelView, frame: cancelFrame) } - //TODO:localize let buttonSize = self.button.update( transition: transition, component: AnyComponent(ButtonComponent( diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift index 3a910c6b0c..682ea1908e 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift @@ -166,8 +166,8 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection strongSelf.updateTitle() }) - case let .premiumGifting(birthdays): - if let birthdays { + case let .premiumGifting(birthdays, selectToday): + if let birthdays, selectToday { let today = Calendar(identifier: .gregorian).component(.day, from: Date()) var todayPeers: [EnginePeer.Id] = [] for (peerId, birthday) in birthdays { diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index 2b4388f2d6..3bc89d67c0 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -183,9 +183,8 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { } else { let displayTopPeers: ContactListPresentation.TopPeers var selectedPeers: [EnginePeer.Id] = [] - if case let .premiumGifting(birthdays) = mode { + if case let .premiumGifting(birthdays, selectToday) = mode { if let birthdays { - //TODO:localize let today = Calendar(identifier: .gregorian).component(.day, from: Date()) var sections: [(String, [EnginePeer.Id])] = [] var todayPeers: [EnginePeer.Id] = [] @@ -195,7 +194,9 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { for (peerId, birthday) in birthdays { if birthday.day == today { todayPeers.append(peerId) - selectedPeers.append(peerId) + if selectToday { + selectedPeers.append(peerId) + } } else if birthday.day == today - 1 || birthday.day > today + 5 { yesterdayPeers.append(peerId) } else if birthday.day == today + 1 || birthday.day < today + 5 { @@ -204,13 +205,13 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { } if !todayPeers.isEmpty { - sections.append(("🎂 BIRTHDAY TODAY", todayPeers)) + sections.append((presentationData.strings.Premium_Gift_ContactSelection_BirthdayToday, todayPeers)) } if !yesterdayPeers.isEmpty { - sections.append(("BIRTHDAY YESTERDAY", yesterdayPeers)) + sections.append((presentationData.strings.Premium_Gift_ContactSelection_BirthdayYesterday, yesterdayPeers)) } if !tomorrowPeers.isEmpty { - sections.append(("BIRTHDAY TOMORROW", tomorrowPeers)) + sections.append((presentationData.strings.Premium_Gift_ContactSelection_BirthdayTomorrow, tomorrowPeers)) } displayTopPeers = .custom(sections) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 6fbb8cfd11..68db042443 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -60,6 +60,7 @@ import StickerPickerScreen import MediaEditor import MediaEditorScreen import BusinessIntroSetupScreen +import TelegramNotices private final class AccountUserInterfaceInUseContext { let subscribers = Bag<(Bool) -> Void>() @@ -2125,11 +2126,15 @@ public final class SharedAccountContextImpl: SharedAccountContext { var reachedLimitImpl: ((Int32) -> Void)? let mode: ContactMultiselectionControllerMode - if case let .chatList(birthdays) = source, let birthdays { - //TODO:localize - mode = .premiumGifting(birthdays: birthdays) + var currentBirthdays: [EnginePeer.Id: TelegramBirthday]? + if case let .chatList(birthdays) = source, let birthdays, !birthdays.isEmpty { + mode = .premiumGifting(birthdays: birthdays, selectToday: true) + currentBirthdays = birthdays + } else if case let .settings(birthdays) = source, let birthdays, !birthdays.isEmpty { + mode = .premiumGifting(birthdays: birthdays, selectToday: false) + currentBirthdays = birthdays } else { - mode = .premiumGifting(birthdays: nil) + mode = .premiumGifting(birthdays: nil, selectToday: false) } let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: mode, options: [], isPeerEnabled: { peer in @@ -2177,6 +2182,20 @@ public final class SharedAccountContextImpl: SharedAccountContext { }, completion: { filterImpl?() completion?() + + if let currentBirthdays { + let today = Calendar(identifier: .gregorian).component(.day, from: Date()) + var todayBirthdayPeerIds: [EnginePeer.Id] = [] + for (peerId, birthday) in currentBirthdays { + if birthday.day == today { + todayBirthdayPeerIds.append(peerId) + } + } + let peerIds = todayBirthdayPeerIds.sorted { lhs, rhs in + return lhs < rhs + } + let _ = ApplicationSpecificNotice.setDismissedBirthdayPremiumGifts(accountManager: context.sharedContext.accountManager, values: peerIds.map { $0.toInt64() }).start() + } }) pushImpl = { [weak giftController] c in giftController?.push(c)