From cdd58e05f7c00cec057802de69fca503a4d07abf Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sat, 12 Aug 2023 00:24:03 +0400 Subject: [PATCH] Stories --- .../Telegram-iOS/en.lproj/Localizable.strings | 2 ++ .../Messages/EngineStoryViewListContext.swift | 4 +-- .../TelegramEngine/Messages/Stories.swift | 12 +++++-- .../Messages/StoryListContext.swift | 31 +++++++++++----- .../Sources/StoryChatContent.swift | 6 ++-- .../StoryItemSetViewListComponent.swift | 35 ++++++++++++++----- 6 files changed, 66 insertions(+), 24 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 7ec6654b81..8cecadca30 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -9712,6 +9712,7 @@ Sorry for the inconvenience."; "Story.Privacy.PostStory" = "Post Story"; "Story.Views.ViewsExpired" = "List of viewers becomes unavailable **24 hours** after the story expires."; +"Story.Views.ViewsNotRecorded" = "Information about viewers wasn’t recorded."; "Story.Views.NoViews" = "Nobody has viewed\nyour story yet."; "AutoDownloadSettings.Stories" = "Stories"; @@ -9823,6 +9824,7 @@ Sorry for the inconvenience."; "Story.ViewList.PremiumUpgradeText" = "List of viewers isn't available after 24 hours of story expiration.\n\nTo unlock viewers' lists for expired and saved stories, subscribe to [Telegram Premium]()."; "Story.ViewList.PremiumUpgradeAction" = "Learn More"; "Story.ViewList.PremiumUpgradeInlineText" = "To unlock viewers' lists for expired and saved stories, subscribe to [Telegram Premium]()."; +"Story.ViewList.NotFullyRecorded" = "Information about the other viewers wasn’t recorded."; "Story.ViewList.EmptyTextSearch" = "No views found"; "Story.ViewList.EmptyTextContacts" = "None of your contacts viewed this story."; "Story.ViewList.ContextSortReactions" = "Reactions First"; diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift index 870eec503f..a2deee0065 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/EngineStoryViewListContext.swift @@ -335,7 +335,7 @@ public final class EngineStoryViewListContext { mediaAreas: item.mediaAreas, text: item.text, entities: item.entities, - views: Stories.Item.Views(seenCount: Int(count), reactedCount: Int(reactionsCount), seenPeerIds: currentViews.seenPeerIds), + views: Stories.Item.Views(seenCount: Int(count), reactedCount: Int(reactionsCount), seenPeerIds: currentViews.seenPeerIds, hasList: currentViews.hasList), privacy: item.privacy, isPinned: item.isPinned, isExpired: item.isExpired, @@ -364,7 +364,7 @@ public final class EngineStoryViewListContext { mediaAreas: item.mediaAreas, text: item.text, entities: item.entities, - views: Stories.Item.Views(seenCount: Int(count), reactedCount: Int(reactionsCount), seenPeerIds: currentViews.seenPeerIds), + views: Stories.Item.Views(seenCount: Int(count), reactedCount: Int(reactionsCount), seenPeerIds: currentViews.seenPeerIds, hasList: currentViews.hasList), privacy: item.privacy, isPinned: item.isPinned, isExpired: item.isExpired, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index d9a9e176d4..9bfc92e020 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -43,16 +43,19 @@ public enum Stories { case seenCount = "seenCount" case reactedCount = "reactedCount" case seenPeerIds = "seenPeerIds" + case hasList = "hasList" } public var seenCount: Int public var reactedCount: Int public var seenPeerIds: [PeerId] + public var hasList: Bool - public init(seenCount: Int, reactedCount: Int, seenPeerIds: [PeerId]) { + public init(seenCount: Int, reactedCount: Int, seenPeerIds: [PeerId], hasList: Bool) { self.seenCount = seenCount self.reactedCount = reactedCount self.seenPeerIds = seenPeerIds + self.hasList = hasList } public init(from decoder: Decoder) throws { @@ -61,6 +64,7 @@ public enum Stories { self.seenCount = Int(try container.decode(Int32.self, forKey: .seenCount)) self.reactedCount = Int(try container.decodeIfPresent(Int32.self, forKey: .reactedCount) ?? 0) self.seenPeerIds = try container.decode([Int64].self, forKey: .seenPeerIds).map(PeerId.init) + self.hasList = try container.decodeIfPresent(Bool.self, forKey: .hasList) ?? true } public func encode(to encoder: Encoder) throws { @@ -69,6 +73,7 @@ public enum Stories { try container.encode(Int32(clamping: self.seenCount), forKey: .seenCount) try container.encode(Int32(clamping: self.reactedCount), forKey: .reactedCount) try container.encode(self.seenPeerIds.map { $0.toInt64() }, forKey: .seenPeerIds) + try container.encode(self.hasList, forKey: .hasList) } } @@ -1395,12 +1400,13 @@ extension Api.StoryItem { extension Stories.Item.Views { init(apiViews: Api.StoryViews) { switch apiViews { - case let .storyViews(_, viewsCount, reactionsCount, recentViewers): + case let .storyViews(flags, viewsCount, reactionsCount, recentViewers): + let hasList = (flags & (1 << 1)) != 0 var seenPeerIds: [PeerId] = [] if let recentViewers = recentViewers { seenPeerIds = recentViewers.map { PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value($0)) } } - self.init(seenCount: Int(viewsCount), reactedCount: Int(reactionsCount), seenPeerIds: seenPeerIds) + self.init(seenCount: Int(viewsCount), reactedCount: Int(reactionsCount), seenPeerIds: seenPeerIds, hasList: hasList) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index 68e1322644..df6e06a985 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -15,11 +15,13 @@ public final class EngineStoryItem: Equatable { public let seenCount: Int public let reactedCount: Int public let seenPeers: [EnginePeer] + public let hasList: Bool - public init(seenCount: Int, reactedCount: Int, seenPeers: [EnginePeer]) { + public init(seenCount: Int, reactedCount: Int, seenPeers: [EnginePeer], hasList: Bool) { self.seenCount = seenCount self.reactedCount = reactedCount self.seenPeers = seenPeers + self.hasList = hasList } public static func ==(lhs: Views, rhs: Views) -> Bool { @@ -32,6 +34,9 @@ public final class EngineStoryItem: Equatable { if lhs.seenPeers != rhs.seenPeers { return false } + if lhs.hasList != rhs.hasList { + return false + } return true } } @@ -154,7 +159,8 @@ extension EngineStoryItem { return Stories.Item.Views( seenCount: views.seenCount, reactedCount: views.reactedCount, - seenPeerIds: views.seenPeers.map(\.id) + seenPeerIds: views.seenPeers.map(\.id), + hasList: views.hasList ) }, privacy: self.privacy.flatMap { privacy in @@ -529,7 +535,8 @@ public final class PeerStoryListContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return transaction.getPeer(id).flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), @@ -656,7 +663,8 @@ public final class PeerStoryListContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return transaction.getPeer(id).flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), @@ -807,7 +815,8 @@ public final class PeerStoryListContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return peers[id].flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), @@ -849,7 +858,8 @@ public final class PeerStoryListContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return peers[id].flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), @@ -893,7 +903,8 @@ public final class PeerStoryListContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return peers[id].flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), @@ -933,7 +944,8 @@ public final class PeerStoryListContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return peers[id].flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), @@ -1097,7 +1109,8 @@ public final class PeerExpiringStoryListContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return transaction.getPeer(id).flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift index 6ddae15928..d38b371e54 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryChatContent.swift @@ -161,7 +161,8 @@ public final class StoryContentContextImpl: StoryContentContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return peers[id].flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: item.privacy.flatMap(EngineStoryPrivacy.init), @@ -1037,7 +1038,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext { reactedCount: views.reactedCount, seenPeers: views.seenPeerIds.compactMap { id -> EnginePeer? in return peers[id].flatMap(EnginePeer.init) - } + }, + hasList: views.hasList ) }, privacy: itemValue.privacy.flatMap(EngineStoryPrivacy.init), diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift index f5d36173a7..8b6c04668c 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetViewListComponent.swift @@ -586,6 +586,9 @@ final class StoryItemSetViewListComponent: Component { if let baseContentView, baseContentView.configuration == self.configuration, baseContentView.query == nil { parentSource = baseContentView.viewList } + if component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { + parentSource = nil + } self.viewList = component.context.engine.messages.storyViewList(id: component.storyItem.id, views: views, listMode: mappedListMode, sortMode: mappedSortMode, searchQuery: query, parentSource: parentSource) } @@ -753,7 +756,7 @@ final class StoryItemSetViewListComponent: Component { } var premiumFooterSize: CGSize? - if !component.hasPremium, let viewListState = self.viewListState, viewListState.loadMoreToken == nil, !viewListState.items.isEmpty, let views = component.storyItem.views, views.seenCount > viewListState.totalCount, component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) { + if let viewListState = self.viewListState, viewListState.loadMoreToken == nil, !viewListState.items.isEmpty, let views = component.storyItem.views, views.seenCount > viewListState.totalCount, component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) { let premiumFooterText: ComponentView if let current = self.premiumFooterText { premiumFooterText = current @@ -768,7 +771,15 @@ final class StoryItemSetViewListComponent: Component { let link = MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: component.theme.list.itemAccentColor) let attributes = MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return ("URL", "") }) - let text = component.strings.Story_ViewList_PremiumUpgradeInlineText + let text: String + let fullWidth: Bool + if component.hasPremium { + text = component.strings.Story_ViewList_NotFullyRecorded + fullWidth = true + } else { + text = component.strings.Story_ViewList_PremiumUpgradeInlineText + fullWidth = false + } premiumFooterSize = premiumFooterText.update( transition: .immediate, component: AnyComponent(BalancedTextComponent( @@ -777,14 +788,14 @@ final class StoryItemSetViewListComponent: Component { maximumNumberOfLines: 0, lineSpacing: 0.2, highlightColor: component.theme.list.itemAccentColor.withMultipliedAlpha(0.5), - highlightAction: { attributes in + highlightAction: component.hasPremium ? nil : { attributes in if let _ = attributes[NSAttributedString.Key(rawValue: "URL")] { return NSAttributedString.Key(rawValue: "URL") } else { return nil } }, - tapAction: { [weak self] _, _ in + tapAction: component.hasPremium ? nil : { [weak self] _, _ in guard let self, let component = self.component else { return } @@ -792,7 +803,7 @@ final class StoryItemSetViewListComponent: Component { } )), environment: {}, - containerSize: CGSize(width: min(320.0, availableSize.width - 16.0 * 2.0), height: 1000.0) + containerSize: CGSize(width: min(fullWidth ? 500.0 : 320.0, availableSize.width - 16.0 * 2.0), height: 1000.0) ) } else { if let premiumFooterText = self.premiumFooterText { @@ -894,7 +905,11 @@ final class StoryItemSetViewListComponent: Component { if self.configuration.listMode == .everyone && (self.query == nil || self.query == "") { if component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) { if emptyButton == nil { - text = component.strings.Story_Views_ViewsExpired + if let views = component.storyItem.views, views.seenCount > 0 { + text = component.strings.Story_Views_ViewsNotRecorded + } else { + text = component.strings.Story_Views_ViewsExpired + } } else { text = component.strings.Story_ViewList_PremiumUpgradeText } @@ -909,7 +924,11 @@ final class StoryItemSetViewListComponent: Component { } else { if component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) { if emptyButton == nil { - text = component.strings.Story_Views_ViewsExpired + if let views = component.storyItem.views, views.seenCount > 0 { + text = component.strings.Story_Views_ViewsNotRecorded + } else { + text = component.strings.Story_Views_ViewsExpired + } } else { text = component.strings.Story_ViewList_PremiumUpgradeText } @@ -1341,7 +1360,7 @@ final class StoryItemSetViewListComponent: Component { if !component.hasPremium, component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) { } else { - if let views = component.storyItem.views { + if let views = component.storyItem.views, views.hasList { if views.seenCount >= 20 || component.context.sharedContext.immediateExperimentalUISettings.storiesExperiment { displayModeSelector = true displaySearchBar = true