Add close friends parameter

This commit is contained in:
Ali 2023-06-24 16:13:03 +03:00
parent a0817a831b
commit 42a6978ffd
16 changed files with 207 additions and 54 deletions

View File

@ -1889,12 +1889,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in
var chatListState = chatListState var chatListState = chatListState
var peerStoryMapping: [EnginePeer.Id: Bool] = [:] var peerStoryMapping: [EnginePeer.Id: ChatListNodeState.StoryState] = [:]
for item in rawStorySubscriptions.items { for item in rawStorySubscriptions.items {
if item.peer.id == self.context.account.peerId { if item.peer.id == self.context.account.peerId {
continue continue
} }
peerStoryMapping[item.peer.id] = item.hasUnseen peerStoryMapping[item.peer.id] = ChatListNodeState.StoryState(
hasUnseen: item.hasUnseen,
hasUnseenCloseFriends: item.hasUnseenCloseFriends
)
} }
chatListState.peerStoryMapping = peerStoryMapping chatListState.peerStoryMapping = peerStoryMapping

View File

@ -65,6 +65,19 @@ public enum ChatListItemContent {
} }
} }
public struct StoryState: Equatable {
public var hasUnseen: Bool
public var hasUnseenCloseFriends: Bool
public init(
hasUnseen: Bool,
hasUnseenCloseFriends: Bool
) {
self.hasUnseen = hasUnseen
self.hasUnseenCloseFriends = hasUnseenCloseFriends
}
}
public struct PeerData { public struct PeerData {
public var messages: [EngineMessage] public var messages: [EngineMessage]
public var peer: EngineRenderedPeer public var peer: EngineRenderedPeer
@ -83,7 +96,7 @@ public enum ChatListItemContent {
public var forumTopicData: EngineChatList.ForumTopicData? public var forumTopicData: EngineChatList.ForumTopicData?
public var topForumTopicItems: [EngineChatList.ForumTopicData] public var topForumTopicItems: [EngineChatList.ForumTopicData]
public var autoremoveTimeout: Int32? public var autoremoveTimeout: Int32?
public var storyState: Bool? public var storyState: StoryState?
public init( public init(
messages: [EngineMessage], messages: [EngineMessage],
@ -103,7 +116,7 @@ public enum ChatListItemContent {
forumTopicData: EngineChatList.ForumTopicData?, forumTopicData: EngineChatList.ForumTopicData?,
topForumTopicItems: [EngineChatList.ForumTopicData], topForumTopicItems: [EngineChatList.ForumTopicData],
autoremoveTimeout: Int32?, autoremoveTimeout: Int32?,
storyState: Bool? storyState: StoryState?
) { ) {
self.messages = messages self.messages = messages
self.peer = peer self.peer = peer
@ -2747,9 +2760,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + revealOffset, dy: 0.0) let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + revealOffset, dy: 0.0)
var displayStoryIndicator: Bool? var storyState: ChatListItemContent.StoryState?
if case let .peer(peerData) = item.content { if case let .peer(peerData) = item.content {
displayStoryIndicator = peerData.storyState storyState = peerData.storyState
} }
let avatarFrame = CGRect(origin: CGPoint(x: leftInset - avatarLeftInset + editingOffset + 10.0 + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter)) let avatarFrame = CGRect(origin: CGPoint(x: leftInset - avatarLeftInset + editingOffset + 10.0 + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter))
@ -2764,7 +2777,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} }
let storyIndicatorScale = avatarScale let storyIndicatorScale = avatarScale
if displayStoryIndicator != nil { if storyState != nil {
avatarScale *= (avatarFrame.width - 4.0 * 2.0) / avatarFrame.width avatarScale *= (avatarFrame.width - 4.0 * 2.0) / avatarFrame.width
} }
@ -2775,7 +2788,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.avatarNode.updateSize(size: avatarFrame.size) strongSelf.avatarNode.updateSize(size: avatarFrame.size)
strongSelf.updateVideoVisibility() strongSelf.updateVideoVisibility()
if let displayStoryIndicator { if let storyState {
var indicatorTransition = Transition(transition) var indicatorTransition = Transition(transition)
let avatarStoryIndicator: ComponentView<Empty> let avatarStoryIndicator: ComponentView<Empty>
if let current = strongSelf.avatarStoryIndicator { if let current = strongSelf.avatarStoryIndicator {
@ -2792,7 +2805,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
let _ = avatarStoryIndicator.update( let _ = avatarStoryIndicator.update(
transition: indicatorTransition, transition: indicatorTransition,
component: AnyComponent(AvatarStoryIndicatorComponent( component: AnyComponent(AvatarStoryIndicatorComponent(
hasUnseen: displayStoryIndicator, hasUnseen: storyState.hasUnseen,
hasUnseenCloseFriendsItems: storyState.hasUnseenCloseFriends,
isDarkTheme: item.presentationData.theme.overallDarkAppearance, isDarkTheme: item.presentationData.theme.overallDarkAppearance,
activeLineWidth: 2.0, activeLineWidth: 2.0,
inactiveLineWidth: 1.0 + UIScreenPixel, inactiveLineWidth: 1.0 + UIScreenPixel,

View File

@ -218,6 +218,16 @@ private func areFoundPeerArraysEqual(_ lhs: [(EnginePeer, EnginePeer?)], _ rhs:
} }
public struct ChatListNodeState: Equatable { public struct ChatListNodeState: Equatable {
public struct StoryState: Equatable {
public var hasUnseen: Bool
public var hasUnseenCloseFriends: Bool
public init(hasUnseen: Bool, hasUnseenCloseFriends: Bool) {
self.hasUnseen = hasUnseen
self.hasUnseenCloseFriends = hasUnseenCloseFriends
}
}
public struct ItemId: Hashable { public struct ItemId: Hashable {
public var peerId: EnginePeer.Id public var peerId: EnginePeer.Id
public var threadId: Int64? public var threadId: Int64?
@ -241,7 +251,7 @@ public struct ChatListNodeState: Equatable {
public var foundPeers: [(EnginePeer, EnginePeer?)] public var foundPeers: [(EnginePeer, EnginePeer?)]
public var selectedPeerMap: [EnginePeer.Id: EnginePeer] public var selectedPeerMap: [EnginePeer.Id: EnginePeer]
public var selectedThreadIds: Set<Int64> public var selectedThreadIds: Set<Int64>
public var peerStoryMapping: [EnginePeer.Id: Bool] public var peerStoryMapping: [EnginePeer.Id: ChatListNodeState.StoryState]
public init( public init(
presentationData: ChatListPresentationData, presentationData: ChatListPresentationData,
@ -257,7 +267,7 @@ public struct ChatListNodeState: Equatable {
hiddenItemShouldBeTemporaryRevealed: Bool, hiddenItemShouldBeTemporaryRevealed: Bool,
hiddenPsaPeerId: EnginePeer.Id?, hiddenPsaPeerId: EnginePeer.Id?,
selectedThreadIds: Set<Int64>, selectedThreadIds: Set<Int64>,
peerStoryMapping: [EnginePeer.Id: Bool] peerStoryMapping: [EnginePeer.Id: ChatListNodeState.StoryState]
) { ) {
self.presentationData = presentationData self.presentationData = presentationData
self.editing = editing self.editing = editing
@ -396,7 +406,12 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
forumTopicData: forumTopicData, forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems, topForumTopicItems: topForumTopicItems,
autoremoveTimeout: peerEntry.autoremoveTimeout, autoremoveTimeout: peerEntry.autoremoveTimeout,
storyState: peerEntry.storyState storyState: peerEntry.storyState.flatMap { storyState in
return ChatListItemContent.StoryState(
hasUnseen: storyState.hasUnseen,
hasUnseenCloseFriends: storyState.hasUnseenCloseFriends
)
}
)), )),
editing: editing, editing: editing,
hasActiveRevealControls: hasActiveRevealControls, hasActiveRevealControls: hasActiveRevealControls,
@ -742,7 +757,12 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
forumTopicData: forumTopicData, forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems, topForumTopicItems: topForumTopicItems,
autoremoveTimeout: peerEntry.autoremoveTimeout, autoremoveTimeout: peerEntry.autoremoveTimeout,
storyState: peerEntry.storyState storyState: peerEntry.storyState.flatMap { storyState in
return ChatListItemContent.StoryState(
hasUnseen: storyState.hasUnseen,
hasUnseenCloseFriends: storyState.hasUnseenCloseFriends
)
}
)), )),
editing: editing, editing: editing,
hasActiveRevealControls: hasActiveRevealControls, hasActiveRevealControls: hasActiveRevealControls,

View File

@ -111,7 +111,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
var forumTopicData: EngineChatList.ForumTopicData? var forumTopicData: EngineChatList.ForumTopicData?
var topForumTopicItems: [EngineChatList.ForumTopicData] var topForumTopicItems: [EngineChatList.ForumTopicData]
var revealed: Bool var revealed: Bool
var storyState: Bool? var storyState: ChatListNodeState.StoryState?
init( init(
index: EngineChatList.Item.Index, index: EngineChatList.Item.Index,
@ -136,7 +136,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
forumTopicData: EngineChatList.ForumTopicData?, forumTopicData: EngineChatList.ForumTopicData?,
topForumTopicItems: [EngineChatList.ForumTopicData], topForumTopicItems: [EngineChatList.ForumTopicData],
revealed: Bool, revealed: Bool,
storyState: Bool? storyState: ChatListNodeState.StoryState?
) { ) {
self.index = index self.index = index
self.presentationData = presentationData self.presentationData = presentationData

View File

@ -82,6 +82,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
struct StoryData: Equatable { struct StoryData: Equatable {
var count: Int var count: Int
var unseenCount: Int var unseenCount: Int
var hasUnseenCloseFriends: Bool
} }
case search(PresentationTheme, PresentationStrings) case search(PresentationTheme, PresentationStrings)
@ -201,9 +202,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
})] })]
} }
var storyStats: (total: Int, unseen: Int)? var storyStats: (total: Int, unseen: Int, hasUnseenCloseFriends: Bool)?
if let storyData = storyData { if let storyData = storyData {
storyStats = (storyData.count, storyData.unseenCount) storyStats = (storyData.count, storyData.unseenCount, storyData.hasUnseenCloseFriends)
let text: String let text: String
//TODO:localize //TODO:localize
@ -534,7 +535,7 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text("HIDDEN STORIES", AnyHashable(0)), theme: theme, strings: strings) let header: ListViewItemHeader? = ChatListSearchItemHeader(type: .text("HIDDEN STORIES", AnyHashable(0)), theme: theme, strings: strings)
for item in storySubscriptions.items { for item in storySubscriptions.items {
entries.append(.peer(index, .peer(peer: item.peer._asPeer(), isGlobal: false, participantCount: nil), nil, header, .none, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, true, ContactListNodeEntry.StoryData(count: item.storyCount, unseenCount: item.unseenCount))) entries.append(.peer(index, .peer(peer: item.peer._asPeer(), isGlobal: false, participantCount: nil), nil, header, .none, theme, strings, dateTimeFormat, sortOrder, displayOrder, false, true, ContactListNodeEntry.StoryData(count: item.storyCount, unseenCount: item.unseenCount, hasUnseenCloseFriends: item.hasUnseenCloseFriends)))
index += 1 index += 1
} }
} }

View File

@ -180,7 +180,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
let arrowAction: (() -> Void)? let arrowAction: (() -> Void)?
let animationCache: AnimationCache? let animationCache: AnimationCache?
let animationRenderer: MultiAnimationRenderer? let animationRenderer: MultiAnimationRenderer?
let storyStats: (total: Int, unseen: Int)? let storyStats: (total: Int, unseen: Int, hasUnseenCloseFriends: Bool)?
let openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)? let openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)?
public let selectable: Bool public let selectable: Bool
@ -217,7 +217,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil,
animationCache: AnimationCache? = nil, animationCache: AnimationCache? = nil,
animationRenderer: MultiAnimationRenderer? = nil, animationRenderer: MultiAnimationRenderer? = nil,
storyStats: (total: Int, unseen: Int)? = nil, storyStats: (total: Int, unseen: Int, hasUnseenCloseFriends: Bool)? = nil,
openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)? = nil openStories: ((ContactsPeerItemPeer, ASDisplayNode) -> Void)? = nil
) { ) {
self.presentationData = presentationData self.presentationData = presentationData
@ -1114,6 +1114,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
transition: indicatorTransition, transition: indicatorTransition,
component: AnyComponent(AvatarStoryIndicatorComponent( component: AnyComponent(AvatarStoryIndicatorComponent(
hasUnseen: storyStats.unseen != 0, hasUnseen: storyStats.unseen != 0,
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends,
isDarkTheme: item.presentationData.theme.overallDarkAppearance, isDarkTheme: item.presentationData.theme.overallDarkAppearance,
activeLineWidth: 1.0 + UIScreenPixel, activeLineWidth: 1.0 + UIScreenPixel,
inactiveLineWidth: 1.0 + UIScreenPixel, inactiveLineWidth: 1.0 + UIScreenPixel,

View File

@ -124,6 +124,7 @@ public enum Stories {
case isPinned case isPinned
case isExpired case isExpired
case isPublic case isPublic
case isCloseFriends
} }
public let id: Int32 public let id: Int32
@ -137,6 +138,7 @@ public enum Stories {
public let isPinned: Bool public let isPinned: Bool
public let isExpired: Bool public let isExpired: Bool
public let isPublic: Bool public let isPublic: Bool
public let isCloseFriends: Bool
public init( public init(
id: Int32, id: Int32,
@ -149,7 +151,8 @@ public enum Stories {
privacy: Privacy?, privacy: Privacy?,
isPinned: Bool, isPinned: Bool,
isExpired: Bool, isExpired: Bool,
isPublic: Bool isPublic: Bool,
isCloseFriends: Bool
) { ) {
self.id = id self.id = id
self.timestamp = timestamp self.timestamp = timestamp
@ -162,6 +165,7 @@ public enum Stories {
self.isPinned = isPinned self.isPinned = isPinned
self.isExpired = isExpired self.isExpired = isExpired
self.isPublic = isPublic self.isPublic = isPublic
self.isCloseFriends = isCloseFriends
} }
public init(from decoder: Decoder) throws { public init(from decoder: Decoder) throws {
@ -184,6 +188,7 @@ public enum Stories {
self.isPinned = try container.decodeIfPresent(Bool.self, forKey: .isPinned) ?? false self.isPinned = try container.decodeIfPresent(Bool.self, forKey: .isPinned) ?? false
self.isExpired = try container.decodeIfPresent(Bool.self, forKey: .isExpired) ?? false self.isExpired = try container.decodeIfPresent(Bool.self, forKey: .isExpired) ?? false
self.isPublic = try container.decodeIfPresent(Bool.self, forKey: .isPublic) ?? false self.isPublic = try container.decodeIfPresent(Bool.self, forKey: .isPublic) ?? false
self.isCloseFriends = try container.decodeIfPresent(Bool.self, forKey: .isCloseFriends) ?? false
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -207,6 +212,7 @@ public enum Stories {
try container.encode(self.isPinned, forKey: .isPinned) try container.encode(self.isPinned, forKey: .isPinned)
try container.encode(self.isExpired, forKey: .isExpired) try container.encode(self.isExpired, forKey: .isExpired)
try container.encode(self.isPublic, forKey: .isPublic) try container.encode(self.isPublic, forKey: .isPublic)
try container.encode(self.isCloseFriends, forKey: .isCloseFriends)
} }
public static func ==(lhs: Item, rhs: Item) -> Bool { public static func ==(lhs: Item, rhs: Item) -> Bool {
@ -251,6 +257,9 @@ public enum Stories {
if lhs.isPublic != rhs.isPublic { if lhs.isPublic != rhs.isPublic {
return false return false
} }
if lhs.isCloseFriends != rhs.isCloseFriends {
return false
}
return true return true
} }
@ -456,6 +465,7 @@ public final class EngineStorySubscriptions: Equatable {
public final class Item: Equatable { public final class Item: Equatable {
public let peer: EnginePeer public let peer: EnginePeer
public let hasUnseen: Bool public let hasUnseen: Bool
public let hasUnseenCloseFriends: Bool
public let storyCount: Int public let storyCount: Int
public let unseenCount: Int public let unseenCount: Int
public let lastTimestamp: Int32 public let lastTimestamp: Int32
@ -463,12 +473,14 @@ public final class EngineStorySubscriptions: Equatable {
public init( public init(
peer: EnginePeer, peer: EnginePeer,
hasUnseen: Bool, hasUnseen: Bool,
hasUnseenCloseFriends: Bool,
storyCount: Int, storyCount: Int,
unseenCount: Int, unseenCount: Int,
lastTimestamp: Int32 lastTimestamp: Int32
) { ) {
self.peer = peer self.peer = peer
self.hasUnseen = hasUnseen self.hasUnseen = hasUnseen
self.hasUnseenCloseFriends = hasUnseenCloseFriends
self.storyCount = storyCount self.storyCount = storyCount
self.unseenCount = unseenCount self.unseenCount = unseenCount
self.lastTimestamp = lastTimestamp self.lastTimestamp = lastTimestamp
@ -484,6 +496,9 @@ public final class EngineStorySubscriptions: Equatable {
if lhs.hasUnseen != rhs.hasUnseen { if lhs.hasUnseen != rhs.hasUnseen {
return false return false
} }
if lhs.hasUnseenCloseFriends != rhs.hasUnseenCloseFriends {
return false
}
if lhs.storyCount != rhs.storyCount { if lhs.storyCount != rhs.storyCount {
return false return false
} }
@ -807,7 +822,8 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
privacy: Stories.Item.Privacy(base: privacy.base, additionallyIncludePeers: privacy.additionallyIncludePeers), privacy: Stories.Item.Privacy(base: privacy.base, additionallyIncludePeers: privacy.additionallyIncludePeers),
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic isPublic: item.isPublic,
isCloseFriends: item.isCloseFriends
) )
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
items.append(StoryItemsTableEntry(value: entry, id: item.id)) items.append(StoryItemsTableEntry(value: entry, id: item.id))
@ -954,7 +970,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
privacy: Stories.Item.Privacy(base: privacy.base, additionallyIncludePeers: privacy.additionallyIncludePeers), privacy: Stories.Item.Privacy(base: privacy.base, additionallyIncludePeers: privacy.additionallyIncludePeers),
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic isPublic: item.isPublic,
isCloseFriends: item.isCloseFriends
) )
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
transaction.setStory(id: storyId, value: entry) transaction.setStory(id: storyId, value: entry)
@ -975,7 +992,8 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
privacy: Stories.Item.Privacy(base: privacy.base, additionallyIncludePeers: privacy.additionallyIncludePeers), privacy: Stories.Item.Privacy(base: privacy.base, additionallyIncludePeers: privacy.additionallyIncludePeers),
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic isPublic: item.isPublic,
isCloseFriends: item.isCloseFriends
) )
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
items[index] = StoryItemsTableEntry(value: entry, id: item.id) items[index] = StoryItemsTableEntry(value: entry, id: item.id)
@ -1105,7 +1123,8 @@ func _internal_updateStoriesArePinned(account: Account, ids: [Int32: EngineStory
privacy: item.privacy, privacy: item.privacy,
isPinned: isPinned, isPinned: isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic isPublic: item.isPublic,
isCloseFriends: item.isCloseFriends
) )
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) { if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
items[index] = StoryItemsTableEntry(value: entry, id: item.id) items[index] = StoryItemsTableEntry(value: entry, id: item.id)
@ -1125,7 +1144,8 @@ func _internal_updateStoriesArePinned(account: Account, ids: [Int32: EngineStory
privacy: item.privacy, privacy: item.privacy,
isPinned: isPinned, isPinned: isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic isPublic: item.isPublic,
isCloseFriends: item.isCloseFriends
) )
updatedItems.append(updatedItem) updatedItems.append(updatedItem)
} }
@ -1220,6 +1240,7 @@ extension Stories.StoredItem {
let isPinned = (flags & (1 << 5)) != 0 let isPinned = (flags & (1 << 5)) != 0
let isExpired = (flags & (1 << 6)) != 0 let isExpired = (flags & (1 << 6)) != 0
let isPublic = (flags & (1 << 7)) != 0 let isPublic = (flags & (1 << 7)) != 0
let isCloseFriends = (flags & (1 << 8)) != 0
let item = Stories.Item( let item = Stories.Item(
id: id, id: id,
@ -1232,7 +1253,8 @@ extension Stories.StoredItem {
privacy: parsedPrivacy, privacy: parsedPrivacy,
isPinned: isPinned, isPinned: isPinned,
isExpired: isExpired, isExpired: isExpired,
isPublic: isPublic isPublic: isPublic,
isCloseFriends: isCloseFriends
) )
self = .item(item) self = .item(item)
} else { } else {

View File

@ -43,8 +43,9 @@ public final class EngineStoryItem: Equatable {
public let isExpired: Bool public let isExpired: Bool
public let isPublic: Bool public let isPublic: Bool
public let isPending: Bool public let isPending: Bool
public let isCloseFriends: Bool
public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool) { public init(id: Int32, timestamp: Int32, expirationTimestamp: Int32, media: EngineMedia, text: String, entities: [MessageTextEntity], views: Views?, privacy: EngineStoryPrivacy?, isPinned: Bool, isExpired: Bool, isPublic: Bool, isPending: Bool, isCloseFriends: Bool) {
self.id = id self.id = id
self.timestamp = timestamp self.timestamp = timestamp
self.expirationTimestamp = expirationTimestamp self.expirationTimestamp = expirationTimestamp
@ -57,6 +58,7 @@ public final class EngineStoryItem: Equatable {
self.isExpired = isExpired self.isExpired = isExpired
self.isPublic = isPublic self.isPublic = isPublic
self.isPending = isPending self.isPending = isPending
self.isCloseFriends = isCloseFriends
} }
public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool { public static func ==(lhs: EngineStoryItem, rhs: EngineStoryItem) -> Bool {
@ -96,6 +98,9 @@ public final class EngineStoryItem: Equatable {
if lhs.isPending != rhs.isPending { if lhs.isPending != rhs.isPending {
return false return false
} }
if lhs.isCloseFriends != rhs.isCloseFriends {
return false
}
return true return true
} }
} }
@ -123,7 +128,8 @@ extension EngineStoryItem {
}, },
isPinned: self.isPinned, isPinned: self.isPinned,
isExpired: self.isExpired, isExpired: self.isExpired,
isPublic: self.isPublic isPublic: self.isPublic,
isCloseFriends: self.isCloseFriends
) )
} }
} }
@ -480,7 +486,8 @@ public final class PeerStoryListContext {
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic, isPublic: item.isPublic,
isPending: false isPending: false,
isCloseFriends: item.isCloseFriends
) )
items.append(mappedItem) items.append(mappedItem)
} }
@ -585,7 +592,8 @@ public final class PeerStoryListContext {
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic, isPublic: item.isPublic,
isPending: false isPending: false,
isCloseFriends: item.isCloseFriends
) )
storyItems.append(mappedItem) storyItems.append(mappedItem)
} }
@ -717,7 +725,8 @@ public final class PeerStoryListContext {
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic, isPublic: item.isPublic,
isPending: false isPending: false,
isCloseFriends: item.isCloseFriends
) )
finalUpdatedState = updatedState finalUpdatedState = updatedState
} }
@ -754,7 +763,8 @@ public final class PeerStoryListContext {
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic, isPublic: item.isPublic,
isPending: false isPending: false,
isCloseFriends: item.isCloseFriends
)) ))
updatedState.items.sort(by: { lhs, rhs in updatedState.items.sort(by: { lhs, rhs in
return lhs.timestamp > rhs.timestamp return lhs.timestamp > rhs.timestamp
@ -900,7 +910,8 @@ public final class PeerExpiringStoryListContext {
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic, isPublic: item.isPublic,
isPending: false isPending: false,
isCloseFriends: item.isCloseFriends
) )
items.append(.item(mappedItem)) items.append(.item(mappedItem))
} }
@ -1035,6 +1046,15 @@ public final class PeerExpiringStoryListContext {
return timestamp return timestamp
} }
} }
public var isCloseFriends: Bool {
switch self {
case let .item(item):
return item.isCloseFriends
case .placeholder:
return false
}
}
} }
public final class State: Equatable { public final class State: Equatable {
@ -1046,6 +1066,10 @@ public final class PeerExpiringStoryListContext {
return self.items.contains(where: { $0.id > self.maxReadId }) return self.items.contains(where: { $0.id > self.maxReadId })
} }
public var hasUnseenCloseFriends: Bool {
return self.items.contains(where: { $0.id > self.maxReadId && $0.isCloseFriends })
}
public init(items: [Item], isCached: Bool, maxReadId: Int32) { public init(items: [Item], isCached: Bool, maxReadId: Int32) {
self.items = items self.items = items
self.isCached = isCached self.isCached = isCached

View File

@ -679,6 +679,7 @@ public extension TelegramEngine {
var accountItem: EngineStorySubscriptions.Item = EngineStorySubscriptions.Item( var accountItem: EngineStorySubscriptions.Item = EngineStorySubscriptions.Item(
peer: EnginePeer(accountPeer), peer: EnginePeer(accountPeer),
hasUnseen: false, hasUnseen: false,
hasUnseenCloseFriends: false,
storyCount: 0, storyCount: 0,
unseenCount: 0, unseenCount: 0,
lastTimestamp: 0 lastTimestamp: 0
@ -693,6 +694,7 @@ public extension TelegramEngine {
if let lastEntry = itemsView.items.last?.value.get(Stories.StoredItem.self) { if let lastEntry = itemsView.items.last?.value.get(Stories.StoredItem.self) {
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self) let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
var hasUnseen = false var hasUnseen = false
var hasUnseenCloseFriends = false
var unseenCount = 0 var unseenCount = 0
if let peerState = peerState { if let peerState = peerState {
hasUnseen = peerState.maxReadId < lastEntry.id hasUnseen = peerState.maxReadId < lastEntry.id
@ -700,6 +702,12 @@ public extension TelegramEngine {
for item in itemsView.items { for item in itemsView.items {
if item.id > peerState.maxReadId { if item.id > peerState.maxReadId {
unseenCount += 1 unseenCount += 1
if case let .item(item) = item.value.get(Stories.StoredItem.self) {
if item.isCloseFriends {
hasUnseenCloseFriends = true
}
}
} }
} }
} }
@ -707,6 +715,7 @@ public extension TelegramEngine {
let item = EngineStorySubscriptions.Item( let item = EngineStorySubscriptions.Item(
peer: EnginePeer(accountPeer), peer: EnginePeer(accountPeer),
hasUnseen: hasUnseen, hasUnseen: hasUnseen,
hasUnseenCloseFriends: hasUnseenCloseFriends,
storyCount: itemsView.items.count, storyCount: itemsView.items.count,
unseenCount: unseenCount, unseenCount: unseenCount,
lastTimestamp: lastEntry.timestamp lastTimestamp: lastEntry.timestamp
@ -735,6 +744,7 @@ public extension TelegramEngine {
let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self) let peerState: Stories.PeerState? = stateView.value?.get(Stories.PeerState.self)
var hasUnseen = false var hasUnseen = false
var hasUnseenCloseFriends = false
var unseenCount = 0 var unseenCount = 0
if let peerState = peerState { if let peerState = peerState {
hasUnseen = peerState.maxReadId < lastEntry.id hasUnseen = peerState.maxReadId < lastEntry.id
@ -742,6 +752,12 @@ public extension TelegramEngine {
for item in itemsView.items { for item in itemsView.items {
if item.id > peerState.maxReadId { if item.id > peerState.maxReadId {
unseenCount += 1 unseenCount += 1
if case let .item(item) = item.value.get(Stories.StoredItem.self) {
if item.isCloseFriends {
hasUnseenCloseFriends = true
}
}
} }
} }
} }
@ -749,6 +765,7 @@ public extension TelegramEngine {
let item = EngineStorySubscriptions.Item( let item = EngineStorySubscriptions.Item(
peer: EnginePeer(peer), peer: EnginePeer(peer),
hasUnseen: hasUnseen, hasUnseen: hasUnseen,
hasUnseenCloseFriends: hasUnseenCloseFriends,
storyCount: itemsView.items.count, storyCount: itemsView.items.count,
unseenCount: unseenCount, unseenCount: unseenCount,
lastTimestamp: lastEntry.timestamp lastTimestamp: lastEntry.timestamp
@ -935,7 +952,8 @@ public extension TelegramEngine {
privacy: item.privacy, privacy: item.privacy,
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic isPublic: item.isPublic,
isCloseFriends: item.isCloseFriends
)) ))
if let entry = CodableEntry(updatedItem) { if let entry = CodableEntry(updatedItem) {
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id) currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id)

View File

@ -30,7 +30,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
private var avatarVideoNode: AvatarVideoNode? private var avatarVideoNode: AvatarVideoNode?
public private(set) var avatarStoryView: ComponentView<Empty>? public private(set) var avatarStoryView: ComponentView<Empty>?
public var hasUnseenStories: Bool? public var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
public let statusView: ComponentView<Empty> public let statusView: ComponentView<Empty>
@ -78,7 +78,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
self.avatarNode.frame = self.containerNode.bounds self.avatarNode.frame = self.containerNode.bounds
#if DEBUG #if DEBUG
self.hasUnseenStories = true //self.hasUnseenStories = true
#endif #endif
} }
@ -200,7 +200,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
} }
public func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme) { public func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme) {
if let hasUnseenStories = self.hasUnseenStories { if let storyData = self.storyData {
let avatarStoryView: ComponentView<Empty> let avatarStoryView: ComponentView<Empty>
if let current = self.avatarStoryView { if let current = self.avatarStoryView {
avatarStoryView = current avatarStoryView = current
@ -212,7 +212,8 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
let _ = avatarStoryView.update( let _ = avatarStoryView.update(
transition: Transition(transition), transition: Transition(transition),
component: AnyComponent(AvatarStoryIndicatorComponent( component: AnyComponent(AvatarStoryIndicatorComponent(
hasUnseen: hasUnseenStories, hasUnseen: storyData.hasUnseen,
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
isDarkTheme: theme.overallDarkAppearance, isDarkTheme: theme.overallDarkAppearance,
activeLineWidth: 1.0, activeLineWidth: 1.0,
inactiveLineWidth: 1.0, inactiveLineWidth: 1.0,

View File

@ -16,6 +16,7 @@ public final class AvatarStoryIndicatorComponent: Component {
} }
public let hasUnseen: Bool public let hasUnseen: Bool
public let hasUnseenCloseFriendsItems: Bool
public let isDarkTheme: Bool public let isDarkTheme: Bool
public let activeLineWidth: CGFloat public let activeLineWidth: CGFloat
public let inactiveLineWidth: CGFloat public let inactiveLineWidth: CGFloat
@ -23,12 +24,14 @@ public final class AvatarStoryIndicatorComponent: Component {
public init( public init(
hasUnseen: Bool, hasUnseen: Bool,
hasUnseenCloseFriendsItems: Bool,
isDarkTheme: Bool, isDarkTheme: Bool,
activeLineWidth: CGFloat, activeLineWidth: CGFloat,
inactiveLineWidth: CGFloat, inactiveLineWidth: CGFloat,
counters: Counters? counters: Counters?
) { ) {
self.hasUnseen = hasUnseen self.hasUnseen = hasUnseen
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
self.isDarkTheme = isDarkTheme self.isDarkTheme = isDarkTheme
self.activeLineWidth = activeLineWidth self.activeLineWidth = activeLineWidth
self.inactiveLineWidth = inactiveLineWidth self.inactiveLineWidth = inactiveLineWidth
@ -39,6 +42,9 @@ public final class AvatarStoryIndicatorComponent: Component {
if lhs.hasUnseen != rhs.hasUnseen { if lhs.hasUnseen != rhs.hasUnseen {
return false return false
} }
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
return false
}
if lhs.isDarkTheme != rhs.isDarkTheme { if lhs.isDarkTheme != rhs.isDarkTheme {
return false return false
} }
@ -91,6 +97,27 @@ public final class AvatarStoryIndicatorComponent: Component {
self.indicatorView.image = generateImage(CGSize(width: imageDiameter, height: imageDiameter), rotatedContext: { size, context in self.indicatorView.image = generateImage(CGSize(width: imageDiameter, height: imageDiameter), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size)) context.clear(CGRect(origin: CGPoint(), size: size))
let activeColors: [CGColor]
let inactiveColors: [CGColor]
if component.hasUnseenCloseFriendsItems {
activeColors = [
UIColor(rgb: 0x7CD636).cgColor,
UIColor(rgb: 0x26B470).cgColor
]
} else {
activeColors = [
UIColor(rgb: 0x34C76F).cgColor,
UIColor(rgb: 0x3DA1FD).cgColor
]
}
if component.isDarkTheme {
inactiveColors = [UIColor(rgb: 0x48484A).cgColor, UIColor(rgb: 0x48484A).cgColor]
} else {
inactiveColors = [UIColor(rgb: 0xD8D8E1).cgColor, UIColor(rgb: 0xD8D8E1).cgColor]
}
context.setLineWidth(lineWidth) context.setLineWidth(lineWidth)
if let counters = component.counters, counters.totalCount > 1 { if let counters = component.counters, counters.totalCount > 1 {
@ -127,13 +154,9 @@ public final class AvatarStoryIndicatorComponent: Component {
var locations: [CGFloat] = [1.0, 0.0] var locations: [CGFloat] = [1.0, 0.0]
let colors: [CGColor] let colors: [CGColor]
if pass == 1 { if pass == 1 {
colors = [UIColor(rgb: 0x34C76F).cgColor, UIColor(rgb: 0x3DA1FD).cgColor] colors = activeColors
} else { } else {
if component.isDarkTheme { colors = inactiveColors
colors = [UIColor(rgb: 0x48484A).cgColor, UIColor(rgb: 0x48484A).cgColor]
} else {
colors = [UIColor(rgb: 0xD8D8E1).cgColor, UIColor(rgb: 0xD8D8E1).cgColor]
}
} }
let colorSpace = CGColorSpaceCreateDeviceRGB() let colorSpace = CGColorSpaceCreateDeviceRGB()

View File

@ -138,7 +138,8 @@ public final class StoryContentContextImpl: StoryContentContext {
isPinned: item.isPinned, isPinned: item.isPinned,
isExpired: item.isExpired, isExpired: item.isExpired,
isPublic: item.isPublic, isPublic: item.isPublic,
isPending: false isPending: false,
isCloseFriends: item.isCloseFriends
) )
} }
if peerId == context.account.peerId, let stateView = views.views[PostboxViewKey.storiesState(key: .local)] as? StoryStatesView, let localState = stateView.value?.get(Stories.LocalState.self) { if peerId == context.account.peerId, let stateView = views.views[PostboxViewKey.storiesState(key: .local)] as? StoryStatesView, let localState = stateView.value?.get(Stories.LocalState.self) {
@ -155,7 +156,8 @@ public final class StoryContentContextImpl: StoryContentContext {
isPinned: item.pin, isPinned: item.pin,
isExpired: false, isExpired: false,
isPublic: false, isPublic: false,
isPending: true isPending: true,
isCloseFriends: false
)) ))
} }
} }
@ -434,6 +436,7 @@ public final class StoryContentContextImpl: StoryContentContext {
items: [EngineStorySubscriptions.Item( items: [EngineStorySubscriptions.Item(
peer: peer, peer: peer,
hasUnseen: state.hasUnseen, hasUnseen: state.hasUnseen,
hasUnseenCloseFriends: state.hasUnseenCloseFriends,
storyCount: state.items.count, storyCount: state.items.count,
unseenCount: 0, unseenCount: 0,
lastTimestamp: state.items.last?.timestamp ?? 0 lastTimestamp: state.items.last?.timestamp ?? 0
@ -953,7 +956,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
isPinned: itemValue.isPinned, isPinned: itemValue.isPinned,
isExpired: itemValue.isExpired, isExpired: itemValue.isExpired,
isPublic: itemValue.isPublic, isPublic: itemValue.isPublic,
isPending: false isPending: false,
isCloseFriends: itemValue.isCloseFriends
) )
let stateValue = StoryContentContextState( let stateValue = StoryContentContextState(

View File

@ -603,6 +603,8 @@ public final class StoryPeerListComponent: Component {
var hasUnseen = false var hasUnseen = false
hasUnseen = itemSet.hasUnseen hasUnseen = itemSet.hasUnseen
var hasUnseenCloseFriendsItems = itemSet.hasUnseenCloseFriends
var hasItems = true var hasItems = true
var itemRingAnimation: StoryPeerListItemComponent.RingAnimation? var itemRingAnimation: StoryPeerListItemComponent.RingAnimation?
if peer.id == component.context.account.peerId { if peer.id == component.context.account.peerId {
@ -614,6 +616,8 @@ public final class StoryPeerListComponent: Component {
if let uploadProgress = component.uploadProgress { if let uploadProgress = component.uploadProgress {
itemRingAnimation = .progress(uploadProgress) itemRingAnimation = .progress(uploadProgress)
} }
hasUnseenCloseFriendsItems = false
} }
let measuredItem = calculateItem(i) let measuredItem = calculateItem(i)
@ -655,6 +659,7 @@ public final class StoryPeerListComponent: Component {
strings: component.strings, strings: component.strings,
peer: peer, peer: peer,
hasUnseen: hasUnseen, hasUnseen: hasUnseen,
hasUnseenCloseFriendsItems: hasUnseenCloseFriendsItems,
hasItems: hasItems, hasItems: hasItems,
ringAnimation: itemRingAnimation, ringAnimation: itemRingAnimation,
collapseFraction: isReallyVisible ? (1.0 - collapsedState.maxFraction) : 0.0, collapseFraction: isReallyVisible ? (1.0 - collapsedState.maxFraction) : 0.0,

View File

@ -266,6 +266,7 @@ public final class StoryPeerListItemComponent: Component {
public let strings: PresentationStrings public let strings: PresentationStrings
public let peer: EnginePeer public let peer: EnginePeer
public let hasUnseen: Bool public let hasUnseen: Bool
public let hasUnseenCloseFriendsItems: Bool
public let hasItems: Bool public let hasItems: Bool
public let ringAnimation: RingAnimation? public let ringAnimation: RingAnimation?
public let collapseFraction: CGFloat public let collapseFraction: CGFloat
@ -283,6 +284,7 @@ public final class StoryPeerListItemComponent: Component {
strings: PresentationStrings, strings: PresentationStrings,
peer: EnginePeer, peer: EnginePeer,
hasUnseen: Bool, hasUnseen: Bool,
hasUnseenCloseFriendsItems: Bool,
hasItems: Bool, hasItems: Bool,
ringAnimation: RingAnimation?, ringAnimation: RingAnimation?,
collapseFraction: CGFloat, collapseFraction: CGFloat,
@ -299,6 +301,7 @@ public final class StoryPeerListItemComponent: Component {
self.strings = strings self.strings = strings
self.peer = peer self.peer = peer
self.hasUnseen = hasUnseen self.hasUnseen = hasUnseen
self.hasUnseenCloseFriendsItems = hasUnseenCloseFriendsItems
self.hasItems = hasItems self.hasItems = hasItems
self.ringAnimation = ringAnimation self.ringAnimation = ringAnimation
self.collapseFraction = collapseFraction self.collapseFraction = collapseFraction
@ -327,6 +330,9 @@ public final class StoryPeerListItemComponent: Component {
if lhs.hasUnseen != rhs.hasUnseen { if lhs.hasUnseen != rhs.hasUnseen {
return false return false
} }
if lhs.hasUnseenCloseFriendsItems != rhs.hasUnseenCloseFriendsItems {
return false
}
if lhs.hasItems != rhs.hasItems { if lhs.hasItems != rhs.hasItems {
return false return false
} }
@ -646,7 +652,17 @@ public final class StoryPeerListItemComponent: Component {
let colors: [CGColor] let colors: [CGColor]
if component.hasUnseen || component.ringAnimation != nil { if component.hasUnseen || component.ringAnimation != nil {
colors = [UIColor(rgb: 0x34C76F).cgColor, UIColor(rgb: 0x3DA1FD).cgColor] if component.hasUnseenCloseFriendsItems {
colors = [
UIColor(rgb: 0x7CD636).cgColor,
UIColor(rgb: 0x26B470).cgColor
]
} else {
colors = [
UIColor(rgb: 0x34C76F).cgColor,
UIColor(rgb: 0x3DA1FD).cgColor
]
}
} else { } else {
if component.theme.overallDarkAppearance { if component.theme.overallDarkAppearance {
colors = [UIColor(rgb: 0x48484A).cgColor, UIColor(rgb: 0x48484A).cgColor] colors = [UIColor(rgb: 0x48484A).cgColor, UIColor(rgb: 0x48484A).cgColor]

View File

@ -421,7 +421,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
private let playbackStartDisposable = MetaDisposable() private let playbackStartDisposable = MetaDisposable()
var hasUnseenStories: Bool? var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
init(context: AccountContext) { init(context: AccountContext) {
self.context = context self.context = context
@ -455,7 +455,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
} }
func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme) { func updateStoryView(transition: ContainedViewLayoutTransition, theme: PresentationTheme) {
if let hasUnseenStories = self.hasUnseenStories { if let storyData = self.storyData {
let avatarStoryView: ComponentView<Empty> let avatarStoryView: ComponentView<Empty>
if let current = self.avatarStoryView { if let current = self.avatarStoryView {
avatarStoryView = current avatarStoryView = current
@ -467,7 +467,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
let _ = avatarStoryView.update( let _ = avatarStoryView.update(
transition: Transition(transition), transition: Transition(transition),
component: AnyComponent(AvatarStoryIndicatorComponent( component: AnyComponent(AvatarStoryIndicatorComponent(
hasUnseen: hasUnseenStories, hasUnseen: storyData.hasUnseen,
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends,
isDarkTheme: theme.overallDarkAppearance, isDarkTheme: theme.overallDarkAppearance,
activeLineWidth: 3.0, activeLineWidth: 3.0,
inactiveLineWidth: 2.0, inactiveLineWidth: 2.0,

View File

@ -3879,10 +3879,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} }
self.expiringStoryListState = state self.expiringStoryListState = state
if state.items.isEmpty { if state.items.isEmpty {
self.headerNode.avatarListNode.avatarContainerNode.hasUnseenStories = nil self.headerNode.avatarListNode.avatarContainerNode.storyData = nil
self.headerNode.avatarListNode.listContainerNode.storyParams = nil self.headerNode.avatarListNode.listContainerNode.storyParams = nil
} else { } else {
self.headerNode.avatarListNode.avatarContainerNode.hasUnseenStories = state.hasUnseen self.headerNode.avatarListNode.avatarContainerNode.storyData = (state.hasUnseen, state.hasUnseenCloseFriends)
self.headerNode.avatarListNode.listContainerNode.storyParams = (peer, state.items.prefix(3).compactMap { item -> EngineStoryItem? in self.headerNode.avatarListNode.listContainerNode.storyParams = (peer, state.items.prefix(3).compactMap { item -> EngineStoryItem? in
switch item { switch item {
case let .item(item): case let .item(item):