mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Stories
This commit is contained in:
parent
860124e3be
commit
f1a01e343b
@ -1881,24 +1881,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
self.chatListDisplayNode.scrollToTopIfStoriesAreExpanded()
|
self.chatListDisplayNode.scrollToTopIfStoriesAreExpanded()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*self.chatListDisplayNode.mainContainerNode.currentItemNode.updateState { chatListState in
|
|
||||||
var chatListState = chatListState
|
|
||||||
|
|
||||||
var peerStoryMapping: [EnginePeer.Id: ChatListNodeState.StoryState] = [:]
|
|
||||||
for item in rawStorySubscriptions.items {
|
|
||||||
if item.peer.id == self.context.account.peerId {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
peerStoryMapping[item.peer.id] = ChatListNodeState.StoryState(
|
|
||||||
hasUnseen: item.hasUnseen,
|
|
||||||
hasUnseenCloseFriends: item.hasUnseenCloseFriends
|
|
||||||
)
|
|
||||||
}
|
|
||||||
chatListState.peerStoryMapping = peerStoryMapping
|
|
||||||
|
|
||||||
return chatListState
|
|
||||||
}*/
|
|
||||||
|
|
||||||
self.storiesReady.set(.single(true))
|
self.storiesReady.set(.single(true))
|
||||||
|
|
||||||
Queue.mainQueue().after(1.0, { [weak self] in
|
Queue.mainQueue().after(1.0, { [weak self] in
|
||||||
@ -1935,12 +1917,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
unseenCount += 1
|
unseenCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let hasUnseenCloseFriends = rawStoryArchiveSubscriptions.items.contains(where: { $0.hasUnseenCloseFriends })
|
||||||
archiveStoryState = ChatListNodeState.StoryState(
|
archiveStoryState = ChatListNodeState.StoryState(
|
||||||
stats: EngineChatList.StoryStats(
|
stats: EngineChatList.StoryStats(
|
||||||
totalCount: rawStoryArchiveSubscriptions.items.count,
|
totalCount: rawStoryArchiveSubscriptions.items.count,
|
||||||
unseenCount: unseenCount
|
unseenCount: unseenCount,
|
||||||
|
hasUnseenCloseFriends: hasUnseenCloseFriends
|
||||||
),
|
),
|
||||||
hasUnseenCloseFriends: rawStoryArchiveSubscriptions.items.contains(where: { $0.hasUnseenCloseFriends })
|
hasUnseenCloseFriends: hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2823,13 +2807,28 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let undoValue: Bool
|
||||||
if self.location == .chatList(groupId: .archive) {
|
if self.location == .chatList(groupId: .archive) {
|
||||||
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: false)
|
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: false)
|
||||||
|
undoValue = true
|
||||||
} else {
|
} else {
|
||||||
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true)
|
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: true)
|
||||||
|
undoValue = false
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let parentController = self.parent as? TabBarController, let contactsController = (self.navigationController as? TelegramRootControllerInterface)?.getContactsController(), let sourceFrame = parentController.frameForControllerTab(controller: contactsController) else {
|
//TODO:localize
|
||||||
|
if self.location != .chatList(groupId: .archive) {
|
||||||
|
self.present(UndoOverlayController(presentationData: self.presentationData, content: .archivedChat(peerId: peer.id.toInt64(), title: "", text: "Stories from **\(peer.compactDisplayTitle)** will now be shown in Archived Chats.", undo: true), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { [weak self] action in
|
||||||
|
if case .undo = action {
|
||||||
|
if let self {
|
||||||
|
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: undoValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}), in: .current)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*guard let parentController = self.parent as? TabBarController, let contactsController = (self.navigationController as? TelegramRootControllerInterface)?.getContactsController(), let sourceFrame = parentController.frameForControllerTab(controller: contactsController) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2851,7 +2850,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
location: .point(location, .bottom),
|
location: .point(location, .bottom),
|
||||||
shouldDismissOnTouch: { _, _ in return .dismiss(consume: false) }
|
shouldDismissOnTouch: { _, _ in return .dismiss(consume: false) }
|
||||||
)
|
)
|
||||||
self.present(tooltipController, in: .window(.root))
|
self.present(tooltipController, in: .window(.root))*/
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2107,6 +2107,20 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
} else if let action = media as? TelegramMediaAction, case let .suggestedProfilePhoto(image) = action.action, let _ = image {
|
} else if let action = media as? TelegramMediaAction, case let .suggestedProfilePhoto(image) = action.action, let _ = image {
|
||||||
let fitSize = contentImageSize
|
let fitSize = contentImageSize
|
||||||
contentImageSpecs.append((message, .action(action), fitSize))
|
contentImageSpecs.append((message, .action(action), fitSize))
|
||||||
|
} else if let storyMedia = media as? TelegramMediaStory, let story = message.associatedStories[storyMedia.storyId], !story.data.isEmpty, case let .item(storyItem) = story.get(Stories.StoredItem.self) {
|
||||||
|
if let image = storyItem.media as? TelegramMediaImage {
|
||||||
|
if let _ = largestImageRepresentation(image.representations) {
|
||||||
|
let fitSize = contentImageSize
|
||||||
|
contentImageSpecs.append((message, .image(image), fitSize))
|
||||||
|
}
|
||||||
|
break inner
|
||||||
|
} else if let file = storyItem.media as? TelegramMediaFile {
|
||||||
|
if file.isVideo, !file.isInstantVideo, let _ = file.dimensions {
|
||||||
|
let fitSize = contentImageSize
|
||||||
|
contentImageSpecs.append((message, .file(file), fitSize))
|
||||||
|
}
|
||||||
|
break inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3369,7 +3383,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
inputActivitiesApply?()
|
inputActivitiesApply?()
|
||||||
|
|
||||||
var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: floor((measureLayout.size.height - contentImageSize.height) / 2.0))
|
var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: 1.0 + floor((measureLayout.size.height - contentImageSize.height) / 2.0))
|
||||||
|
|
||||||
var messageTypeIcon: UIImage?
|
var messageTypeIcon: UIImage?
|
||||||
var messageTypeIconOffset = mediaPreviewOffset
|
var messageTypeIconOffset = mediaPreviewOffset
|
||||||
|
@ -697,7 +697,7 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
|
|||||||
storyState: entry.renderedPeer.peerId == accountPeerId ? nil : entry.storyStats.flatMap { stats -> ChatListNodeState.StoryState in
|
storyState: entry.renderedPeer.peerId == accountPeerId ? nil : entry.storyStats.flatMap { stats -> ChatListNodeState.StoryState in
|
||||||
return ChatListNodeState.StoryState(
|
return ChatListNodeState.StoryState(
|
||||||
stats: stats,
|
stats: stats,
|
||||||
hasUnseenCloseFriends: false
|
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
@ -1187,7 +1187,7 @@ open class TextNode: ASDisplayNode {
|
|||||||
if brokenLineRange.location + brokenLineRange.length > attributedString.length {
|
if brokenLineRange.location + brokenLineRange.length > attributedString.length {
|
||||||
brokenLineRange.length = attributedString.length - brokenLineRange.location
|
brokenLineRange.length = attributedString.length - brokenLineRange.location
|
||||||
}
|
}
|
||||||
if lineRange.length == 0 {
|
if lineRange.length == 0 && !didClipLinebreak {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1202,7 +1202,11 @@ open class TextNode: ASDisplayNode {
|
|||||||
|
|
||||||
let truncatedTokenString: NSAttributedString
|
let truncatedTokenString: NSAttributedString
|
||||||
if let customTruncationToken {
|
if let customTruncationToken {
|
||||||
truncatedTokenString = customTruncationToken
|
if lineRange.length == 0 && customTruncationToken.string.hasPrefix("\u{2026} ") {
|
||||||
|
truncatedTokenString = customTruncationToken.attributedSubstring(from: NSRange(location: 2, length: customTruncationToken.length - 2))
|
||||||
|
} else {
|
||||||
|
truncatedTokenString = customTruncationToken
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var truncationTokenAttributes: [NSAttributedString.Key : AnyObject] = [:]
|
var truncationTokenAttributes: [NSAttributedString.Key : AnyObject] = [:]
|
||||||
truncationTokenAttributes[NSAttributedString.Key.font] = font
|
truncationTokenAttributes[NSAttributedString.Key.font] = font
|
||||||
|
@ -1358,7 +1358,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount,
|
totalCount: storyStats.totalCount,
|
||||||
unseenCount: storyStats.unseenCount,
|
unseenCount: storyStats.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: false
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
||||||
|
@ -260,10 +260,12 @@ public enum ChatListEntry: Comparable {
|
|||||||
public struct PeerStoryStats: Equatable {
|
public struct PeerStoryStats: Equatable {
|
||||||
public var totalCount: Int
|
public var totalCount: Int
|
||||||
public var unseenCount: Int
|
public var unseenCount: Int
|
||||||
|
public var hasUnseenCloseFriends: Bool
|
||||||
|
|
||||||
public init(totalCount: Int, unseenCount: Int) {
|
public init(totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool) {
|
||||||
self.totalCount = totalCount
|
self.totalCount = totalCount
|
||||||
self.unseenCount = unseenCount
|
self.unseenCount = unseenCount
|
||||||
|
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,9 +284,9 @@ func fetchPeerStoryStats(postbox: PostboxImpl, peerId: PeerId) -> PeerStoryStats
|
|||||||
|
|
||||||
if topItems.isExact {
|
if topItems.isExact {
|
||||||
let stats = postbox.storyItemsTable.getStats(peerId: peerId, maxSeenId: maxSeenId)
|
let stats = postbox.storyItemsTable.getStats(peerId: peerId, maxSeenId: maxSeenId)
|
||||||
return PeerStoryStats(totalCount: stats.total, unseenCount: stats.unseen)
|
return PeerStoryStats(totalCount: stats.total, unseenCount: stats.unseen, hasUnseenCloseFriends: stats.hasUnseenCloseFriends)
|
||||||
} else {
|
} else {
|
||||||
return PeerStoryStats(totalCount: 1, unseenCount: topItems.id > maxSeenId ? 1 : 0)
|
return PeerStoryStats(totalCount: 1, unseenCount: topItems.id > maxSeenId ? 1 : 0, hasUnseenCloseFriends: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1311,6 +1311,11 @@ public final class Transaction {
|
|||||||
self.postbox!.setStoryItemsInexactMaxId(peerId: peerId, id: id)
|
self.postbox!.setStoryItemsInexactMaxId(peerId: peerId, id: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func clearStoryItemsInexactMaxId(peerId: PeerId) {
|
||||||
|
assert(!self.disposed)
|
||||||
|
self.postbox!.clearStoryItemsInexactMaxId(peerId: peerId)
|
||||||
|
}
|
||||||
|
|
||||||
public func getStoryItems(peerId: PeerId) -> [StoryItemsTableEntry] {
|
public func getStoryItems(peerId: PeerId) -> [StoryItemsTableEntry] {
|
||||||
return self.postbox!.getStoryItems(peerId: peerId)
|
return self.postbox!.getStoryItems(peerId: peerId)
|
||||||
}
|
}
|
||||||
@ -2259,6 +2264,12 @@ final class PostboxImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func clearStoryItemsInexactMaxId(peerId: PeerId) {
|
||||||
|
if let value = self.storyTopItemsTable.get(peerId: peerId), !value.isExact {
|
||||||
|
self.storyTopItemsTable.set(peerId: peerId, entry: nil, events: &self.currentStoryTopItemEvents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func getStoryItems(peerId: PeerId) -> [StoryItemsTableEntry] {
|
fileprivate func getStoryItems(peerId: PeerId) -> [StoryItemsTableEntry] {
|
||||||
return self.storyItemsTable.get(peerId: peerId)
|
return self.storyItemsTable.get(peerId: peerId)
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,18 @@ public final class StoryItemsTableEntry: Equatable {
|
|||||||
public let value: CodableEntry
|
public let value: CodableEntry
|
||||||
public let id: Int32
|
public let id: Int32
|
||||||
public let expirationTimestamp: Int32?
|
public let expirationTimestamp: Int32?
|
||||||
|
public let isCloseFriends: Bool
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
value: CodableEntry,
|
value: CodableEntry,
|
||||||
id: Int32,
|
id: Int32,
|
||||||
expirationTimestamp: Int32?
|
expirationTimestamp: Int32?,
|
||||||
|
isCloseFriends: Bool
|
||||||
) {
|
) {
|
||||||
self.value = value
|
self.value = value
|
||||||
self.id = id
|
self.id = id
|
||||||
self.expirationTimestamp = expirationTimestamp
|
self.expirationTimestamp = expirationTimestamp
|
||||||
|
self.isCloseFriends = isCloseFriends
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: StoryItemsTableEntry, rhs: StoryItemsTableEntry) -> Bool {
|
public static func ==(lhs: StoryItemsTableEntry, rhs: StoryItemsTableEntry) -> Bool {
|
||||||
@ -28,6 +31,9 @@ public final class StoryItemsTableEntry: Equatable {
|
|||||||
if lhs.expirationTimestamp != rhs.expirationTimestamp {
|
if lhs.expirationTimestamp != rhs.expirationTimestamp {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.isCloseFriends != rhs.isCloseFriends {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,22 +139,53 @@ final class StoryItemsTable: Table {
|
|||||||
return key.successor
|
return key.successor
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getStats(peerId: PeerId, maxSeenId: Int32) -> (total: Int, unseen: Int) {
|
public func getStats(peerId: PeerId, maxSeenId: Int32) -> (total: Int, unseen: Int, hasUnseenCloseFriends: Bool) {
|
||||||
var total = 0
|
var total = 0
|
||||||
var unseen = 0
|
var unseen = 0
|
||||||
|
var hasUnseenCloseFriends = false
|
||||||
|
|
||||||
self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), keys: { key in
|
self.valueBox.range(self.table, start: self.lowerBound(peerId: peerId), end: self.upperBound(peerId: peerId), values: { key, value in
|
||||||
let id = key.getInt32(8)
|
let id = key.getInt32(8)
|
||||||
|
|
||||||
total += 1
|
total += 1
|
||||||
if id > maxSeenId {
|
if id > maxSeenId {
|
||||||
unseen += 1
|
unseen += 1
|
||||||
|
|
||||||
|
var isCloseFriends = false
|
||||||
|
let readBuffer = ReadBuffer(data: value.makeData())
|
||||||
|
var magic: UInt32 = 0
|
||||||
|
readBuffer.read(&magic, offset: 0, length: 4)
|
||||||
|
if magic == 0xabcd1234 {
|
||||||
|
} else if magic == 0xabcd1235 {
|
||||||
|
var length: Int32 = 0
|
||||||
|
readBuffer.read(&length, offset: 0, length: 4)
|
||||||
|
if length > 0 && readBuffer.offset + Int(length) <= readBuffer.length {
|
||||||
|
readBuffer.skip(Int(length))
|
||||||
|
if readBuffer.offset + 4 <= readBuffer.length {
|
||||||
|
readBuffer.skip(4)
|
||||||
|
|
||||||
|
if readBuffer.offset + 1 <= readBuffer.length {
|
||||||
|
var flags: UInt8 = 0
|
||||||
|
readBuffer.read(&flags, offset: 0, length: 1)
|
||||||
|
isCloseFriends = (flags & (1 << 0)) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
if isCloseFriends {
|
||||||
|
hasUnseenCloseFriends = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, limit: 10000)
|
}, limit: 10000)
|
||||||
|
|
||||||
return (total, unseen)
|
return (total, unseen, hasUnseenCloseFriends)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func get(peerId: PeerId) -> [StoryItemsTableEntry] {
|
public func get(peerId: PeerId) -> [StoryItemsTableEntry] {
|
||||||
@ -161,6 +198,7 @@ final class StoryItemsTable: Table {
|
|||||||
|
|
||||||
let entry: CodableEntry
|
let entry: CodableEntry
|
||||||
var expirationTimestamp: Int32?
|
var expirationTimestamp: Int32?
|
||||||
|
var isCloseFriends = false
|
||||||
|
|
||||||
let readBuffer = ReadBuffer(data: value.makeData())
|
let readBuffer = ReadBuffer(data: value.makeData())
|
||||||
var magic: UInt32 = 0
|
var magic: UInt32 = 0
|
||||||
@ -173,16 +211,41 @@ final class StoryItemsTable: Table {
|
|||||||
if readBuffer.offset + 4 <= readBuffer.length {
|
if readBuffer.offset + 4 <= readBuffer.length {
|
||||||
var expirationTimestampValue: Int32 = 0
|
var expirationTimestampValue: Int32 = 0
|
||||||
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
||||||
expirationTimestamp = expirationTimestampValue
|
if expirationTimestampValue != 0 {
|
||||||
|
expirationTimestamp = expirationTimestampValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
entry = CodableEntry(data: Data())
|
entry = CodableEntry(data: Data())
|
||||||
}
|
}
|
||||||
|
} else if magic == 0xabcd1235 {
|
||||||
|
var length: Int32 = 0
|
||||||
|
readBuffer.read(&length, offset: 0, length: 4)
|
||||||
|
if length > 0 && readBuffer.offset + Int(length) <= readBuffer.length {
|
||||||
|
entry = CodableEntry(data: readBuffer.readData(length: Int(length)))
|
||||||
|
if readBuffer.offset + 4 <= readBuffer.length {
|
||||||
|
var expirationTimestampValue: Int32 = 0
|
||||||
|
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
||||||
|
if expirationTimestampValue != 0 {
|
||||||
|
expirationTimestamp = expirationTimestampValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if readBuffer.offset + 1 <= readBuffer.length {
|
||||||
|
var flags: UInt8 = 0
|
||||||
|
readBuffer.read(&flags, offset: 0, length: 1)
|
||||||
|
isCloseFriends = (flags & (1 << 0)) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
entry = CodableEntry(data: Data())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
assertionFailure()
|
||||||
entry = CodableEntry(data: value.makeData())
|
entry = CodableEntry(data: value.makeData())
|
||||||
}
|
}
|
||||||
|
|
||||||
result.append(StoryItemsTableEntry(value: entry, id: id, expirationTimestamp: expirationTimestamp))
|
result.append(StoryItemsTableEntry(value: entry, id: id, expirationTimestamp: expirationTimestamp, isCloseFriends: isCloseFriends))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}, limit: 10000)
|
}, limit: 10000)
|
||||||
@ -209,7 +272,22 @@ final class StoryItemsTable: Table {
|
|||||||
if readBuffer.offset + 4 <= readBuffer.length {
|
if readBuffer.offset + 4 <= readBuffer.length {
|
||||||
var expirationTimestampValue: Int32 = 0
|
var expirationTimestampValue: Int32 = 0
|
||||||
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
||||||
expirationTimestamp = expirationTimestampValue
|
if expirationTimestampValue != 0 {
|
||||||
|
expirationTimestamp = expirationTimestampValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if magic == 0xabcd1235 {
|
||||||
|
var length: Int32 = 0
|
||||||
|
readBuffer.read(&length, offset: 0, length: 4)
|
||||||
|
if length > 0 && readBuffer.offset + Int(length) <= readBuffer.length {
|
||||||
|
readBuffer.skip(Int(length))
|
||||||
|
if readBuffer.offset + 4 <= readBuffer.length {
|
||||||
|
var expirationTimestampValue: Int32 = 0
|
||||||
|
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
||||||
|
if expirationTimestampValue != 0 {
|
||||||
|
expirationTimestamp = expirationTimestampValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +322,22 @@ final class StoryItemsTable: Table {
|
|||||||
if readBuffer.offset + 4 <= readBuffer.length {
|
if readBuffer.offset + 4 <= readBuffer.length {
|
||||||
var expirationTimestampValue: Int32 = 0
|
var expirationTimestampValue: Int32 = 0
|
||||||
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
||||||
expirationTimestamp = expirationTimestampValue
|
if expirationTimestampValue != 0 {
|
||||||
|
expirationTimestamp = expirationTimestampValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if magic == 0xabcd1235 {
|
||||||
|
var length: Int32 = 0
|
||||||
|
readBuffer.read(&length, offset: 0, length: 4)
|
||||||
|
if length > 0 && readBuffer.offset + Int(length) <= readBuffer.length {
|
||||||
|
readBuffer.skip(Int(length))
|
||||||
|
if readBuffer.offset + 4 <= readBuffer.length {
|
||||||
|
var expirationTimestampValue: Int32 = 0
|
||||||
|
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
||||||
|
if expirationTimestampValue != 0 {
|
||||||
|
expirationTimestamp = expirationTimestampValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,17 +372,21 @@ final class StoryItemsTable: Table {
|
|||||||
for entry in entries {
|
for entry in entries {
|
||||||
buffer.reset()
|
buffer.reset()
|
||||||
|
|
||||||
var magic: UInt32 = 0xabcd1234
|
var magic: UInt32 = 0xabcd1235
|
||||||
buffer.write(&magic, length: 4)
|
buffer.write(&magic, length: 4)
|
||||||
|
|
||||||
var length: Int32 = Int32(entry.value.data.count)
|
var length: Int32 = Int32(entry.value.data.count)
|
||||||
buffer.write(&length, length: 4)
|
buffer.write(&length, length: 4)
|
||||||
buffer.write(entry.value.data)
|
buffer.write(entry.value.data)
|
||||||
|
|
||||||
if let expirationTimestamp = entry.expirationTimestamp {
|
var expirationTimestampValue: Int32 = entry.expirationTimestamp ?? 0
|
||||||
var expirationTimestampValue: Int32 = expirationTimestamp
|
buffer.write(&expirationTimestampValue, length: 4)
|
||||||
buffer.write(&expirationTimestampValue, length: 4)
|
|
||||||
|
var flags: UInt8 = 0
|
||||||
|
if entry.isCloseFriends {
|
||||||
|
flags |= (1 << 0)
|
||||||
}
|
}
|
||||||
|
buffer.write(&flags, length: 1)
|
||||||
|
|
||||||
self.valueBox.set(self.table, key: self.key(Key(peerId: peerId, id: entry.id)), value: buffer.readBufferNoCopy())
|
self.valueBox.set(self.table, key: self.key(Key(peerId: peerId, id: entry.id)), value: buffer.readBufferNoCopy())
|
||||||
}
|
}
|
||||||
|
@ -4481,12 +4481,12 @@ func replayFinalState(
|
|||||||
if let currentIndex = updatedPeerEntries.firstIndex(where: { $0.id == storedItem.id }) {
|
if let currentIndex = updatedPeerEntries.firstIndex(where: { $0.id == storedItem.id }) {
|
||||||
if case .item = storedItem {
|
if case .item = storedItem {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
if let codedEntry = CodableEntry(storedItem) {
|
||||||
updatedPeerEntries[currentIndex] = StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp)
|
updatedPeerEntries[currentIndex] = StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
if let codedEntry = CodableEntry(storedItem) {
|
||||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp))
|
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -382,6 +382,15 @@ public enum Stories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isCloseFriends: Bool {
|
||||||
|
switch self {
|
||||||
|
case let .item(item):
|
||||||
|
return item.isCloseFriends
|
||||||
|
case .placeholder:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
@ -886,7 +895,7 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
|
|||||||
isEdited: item.isEdited
|
isEdited: item.isEdited
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||||
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp))
|
items.append(StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends))
|
||||||
}
|
}
|
||||||
updatedItems.append(updatedItem)
|
updatedItems.append(updatedItem)
|
||||||
}
|
}
|
||||||
@ -1064,7 +1073,7 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
|||||||
isEdited: item.isEdited
|
isEdited: item.isEdited
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp)
|
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedItems.append(updatedItem)
|
updatedItems.append(updatedItem)
|
||||||
@ -1191,7 +1200,7 @@ func _internal_updateStoriesArePinned(account: Account, ids: [Int32: EngineStory
|
|||||||
isEdited: item.isEdited
|
isEdited: item.isEdited
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
if let entry = CodableEntry(Stories.StoredItem.item(updatedItem)) {
|
||||||
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp)
|
items[index] = StoryItemsTableEntry(value: entry, id: item.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedItems.append(updatedItem)
|
updatedItems.append(updatedItem)
|
||||||
@ -1661,7 +1670,7 @@ public final class EngineStoryViewListContext {
|
|||||||
isEdited: item.isEdited
|
isEdited: item.isEdited
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp)
|
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1838,7 +1847,7 @@ func _internal_refreshStories(account: Account, peerId: PeerId, ids: [Int32]) ->
|
|||||||
if let updatedItem = result.first(where: { $0.id == currentItems[i].id }) {
|
if let updatedItem = result.first(where: { $0.id == currentItems[i].id }) {
|
||||||
if case .item = updatedItem {
|
if case .item = updatedItem {
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp)
|
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ public final class StorySubscriptionsContext {
|
|||||||
updatedPeerEntries.append(previousEntry)
|
updatedPeerEntries.append(previousEntry)
|
||||||
} else {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
if let codedEntry = CodableEntry(storedItem) {
|
||||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp))
|
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1014,7 +1014,7 @@ public final class PeerExpiringStoryListContext {
|
|||||||
updatedPeerEntries.append(previousEntry)
|
updatedPeerEntries.append(previousEntry)
|
||||||
} else {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
if let codedEntry = CodableEntry(storedItem) {
|
||||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp))
|
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1180,7 +1180,7 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun
|
|||||||
updatedPeerEntries.append(previousEntry)
|
updatedPeerEntries.append(previousEntry)
|
||||||
} else {
|
} else {
|
||||||
if let codedEntry = CodableEntry(storedItem) {
|
if let codedEntry = CodableEntry(storedItem) {
|
||||||
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp))
|
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1003,7 +1003,7 @@ public extension TelegramEngine {
|
|||||||
isEdited: item.isEdited
|
isEdited: item.isEdited
|
||||||
))
|
))
|
||||||
if let entry = CodableEntry(updatedItem) {
|
if let entry = CodableEntry(updatedItem) {
|
||||||
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp)
|
currentItems[i] = StoryItemsTableEntry(value: entry, id: updatedItem.id, expirationTimestamp: updatedItem.expirationTimestamp, isCloseFriends: updatedItem.isCloseFriends)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,15 +43,15 @@ func updatePeers(transaction: Transaction, accountPeerId: PeerId, peers: Accumul
|
|||||||
for (_, user) in peers.users {
|
for (_, user) in peers.users {
|
||||||
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||||
parsedPeers.append(telegramUser)
|
parsedPeers.append(telegramUser)
|
||||||
|
|
||||||
switch user {
|
switch user {
|
||||||
case let .user(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, storiesMaxId):
|
case let .user(flags, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, storiesMaxId):
|
||||||
|
let isMin = (flags & (1 << 20)) != 0
|
||||||
|
let storiesUnavailable = (flags2 & (1 << 4)) != 0
|
||||||
if let storiesMaxId = storiesMaxId {
|
if let storiesMaxId = storiesMaxId {
|
||||||
transaction.setStoryItemsInexactMaxId(peerId: user.peerId, id: storiesMaxId)
|
transaction.setStoryItemsInexactMaxId(peerId: user.peerId, id: storiesMaxId)
|
||||||
|
} else if !isMin && storiesUnavailable {
|
||||||
|
transaction.clearStoryItemsInexactMaxId(peerId: user.peerId)
|
||||||
}
|
}
|
||||||
/*#if DEBUG
|
|
||||||
transaction.setStoryItemsInexactMaxId(peerId: user.peerId, id: 10)
|
|
||||||
#endif*/
|
|
||||||
case .userEmpty:
|
case .userEmpty:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -341,4 +341,5 @@ public enum PresentationResourceParameterKey: Hashable {
|
|||||||
case statusAutoremoveIcon(isActive: Bool)
|
case statusAutoremoveIcon(isActive: Bool)
|
||||||
|
|
||||||
case chatExpiredStoryIndicatorIcon(type: ChatExpiredStoryIndicatorType)
|
case chatExpiredStoryIndicatorIcon(type: ChatExpiredStoryIndicatorType)
|
||||||
|
case chatReplyStoryIndicatorIcon(type: ChatExpiredStoryIndicatorType)
|
||||||
}
|
}
|
||||||
|
@ -1272,4 +1272,30 @@ public struct PresentationResourcesChat {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func chatReplyStoryIndicatorIcon(_ theme: PresentationTheme, type: ChatExpiredStoryIndicatorType) -> UIImage? {
|
||||||
|
return theme.image(PresentationResourceParameterKey.chatReplyStoryIndicatorIcon(type: type), { theme in
|
||||||
|
return generateImage(CGSize(width: 16.0, height: 16.0), rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
let foregroundColor: UIColor
|
||||||
|
switch type {
|
||||||
|
case .incoming:
|
||||||
|
foregroundColor = theme.chat.message.incoming.mediaActiveControlColor
|
||||||
|
case .outgoing:
|
||||||
|
foregroundColor = theme.chat.message.outgoing.mediaActiveControlColor
|
||||||
|
case .free:
|
||||||
|
foregroundColor = theme.chat.serviceMessage.components.withDefaultWallpaper.primaryText
|
||||||
|
}
|
||||||
|
|
||||||
|
if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/ReplyStoryIcon"), color: foregroundColor) {
|
||||||
|
UIGraphicsPushContext(context)
|
||||||
|
|
||||||
|
let fittedSize = image.size
|
||||||
|
image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - fittedSize.width) * 0.5), y: floor((size.height - fittedSize.height) * 0.5)), size: fittedSize), blendMode: .normal, alpha: 1.0)
|
||||||
|
|
||||||
|
UIGraphicsPopContext()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ public final class AvatarStoryIndicatorComponent: Component {
|
|||||||
|
|
||||||
let maxOuterInset = component.activeLineWidth * 2.0
|
let maxOuterInset = component.activeLineWidth * 2.0
|
||||||
diameter = availableSize.width + maxOuterInset * 2.0
|
diameter = availableSize.width + maxOuterInset * 2.0
|
||||||
let imageDiameter = availableSize.width + ceilToScreenPixels(maxOuterInset) * 2.0
|
let imageDiameter = ceil(availableSize.width + maxOuterInset * 2.0)
|
||||||
|
|
||||||
let activeColors: [CGColor]
|
let activeColors: [CGColor]
|
||||||
let inactiveColors: [CGColor]
|
let inactiveColors: [CGColor]
|
||||||
|
@ -360,7 +360,7 @@ public final class PeerListItemComponent: Component {
|
|||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
||||||
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
||||||
hasUnseenCloseFriendsItems: false
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: component.theme),
|
colors: AvatarNode.Colors(theme: component.theme),
|
||||||
|
@ -916,6 +916,7 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
|
|
||||||
public final class SingleStoryContentContextImpl: StoryContentContext {
|
public final class SingleStoryContentContextImpl: StoryContentContext {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
|
private let readGlobally: Bool
|
||||||
|
|
||||||
public private(set) var stateValue: StoryContentContextState?
|
public private(set) var stateValue: StoryContentContextState?
|
||||||
public var state: Signal<StoryContentContextState, NoError> {
|
public var state: Signal<StoryContentContextState, NoError> {
|
||||||
@ -935,9 +936,11 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
storyId: StoryId
|
storyId: StoryId,
|
||||||
|
readGlobally: Bool
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
self.readGlobally = readGlobally
|
||||||
|
|
||||||
self.storyDisposable = (combineLatest(queue: .mainQueue(),
|
self.storyDisposable = (combineLatest(queue: .mainQueue(),
|
||||||
context.engine.data.subscribe(
|
context.engine.data.subscribe(
|
||||||
@ -1084,6 +1087,9 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func markAsSeen(id: StoryId) {
|
public func markAsSeen(id: StoryId) {
|
||||||
|
if self.readGlobally {
|
||||||
|
let _ = self.context.engine.messages.markStoryAsSeen(peerId: id.peerId, id: id.id, asPinned: false).start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,15 +94,15 @@ private final class StoryLongPressRecognizer: UILongPressGestureRecognizer {
|
|||||||
|
|
||||||
override var state: UIGestureRecognizer.State {
|
override var state: UIGestureRecognizer.State {
|
||||||
didSet {
|
didSet {
|
||||||
switch self.state {
|
/*switch self.state {
|
||||||
case .began, .cancelled, .ended, .failed:
|
case .cancelled, .ended, .failed:
|
||||||
if self.isTracking {
|
if self.isTracking {
|
||||||
self.isTracking = false
|
self.isTracking = false
|
||||||
self.updateIsTracking?(false)
|
self.updateIsTracking?(self.isTracking)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,6 +372,8 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
|
|
||||||
var longPressRecognizer: StoryLongPressRecognizer?
|
var longPressRecognizer: StoryLongPressRecognizer?
|
||||||
|
|
||||||
|
private var pendingNavigationToItemId: (peerId: EnginePeer.Id, id: Int32)?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.backgroundLayer = SimpleLayer()
|
self.backgroundLayer = SimpleLayer()
|
||||||
self.backgroundLayer.backgroundColor = UIColor.black.cgColor
|
self.backgroundLayer.backgroundColor = UIColor.black.cgColor
|
||||||
@ -971,16 +973,19 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
self.commitHorizontalPan(velocity: CGPoint(x: 200.0, y: 0.0))
|
self.commitHorizontalPan(velocity: CGPoint(x: 200.0, y: 0.0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mappedDirection: StoryContentContextNavigation.ItemDirection
|
var mappedId: Int32?
|
||||||
switch direction {
|
switch direction {
|
||||||
case .previous:
|
case .previous:
|
||||||
mappedDirection = .previous
|
mappedId = slice.previousItemId
|
||||||
case .next:
|
case .next:
|
||||||
mappedDirection = .next
|
mappedId = slice.nextItemId
|
||||||
case let .id(id):
|
case let .id(id):
|
||||||
mappedDirection = .id(id)
|
mappedId = id
|
||||||
|
}
|
||||||
|
if let mappedId {
|
||||||
|
self.pendingNavigationToItemId = (slice.peer.id, mappedId)
|
||||||
|
component.content.navigate(navigation: .item(.id(mappedId)))
|
||||||
}
|
}
|
||||||
component.content.navigate(navigation: .item(mappedDirection))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1073,6 +1078,16 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
|
if let pendingNavigationToItemId = self.pendingNavigationToItemId {
|
||||||
|
if let slice = component.content.stateValue?.slice, slice.peer.id == pendingNavigationToItemId.peerId {
|
||||||
|
if slice.item.storyItem.id == pendingNavigationToItemId.id {
|
||||||
|
self.pendingNavigationToItemId = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.pendingNavigationToItemId = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
transition.setFrame(view: self.transitionCloneMasterView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setFrame(view: self.transitionCloneMasterView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
|
|
||||||
transition.setFrame(layer: self.backgroundLayer, frame: CGRect(origin: CGPoint(), size: availableSize))
|
transition.setFrame(layer: self.backgroundLayer, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
@ -1104,6 +1119,9 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
if !environment.isVisible {
|
if !environment.isVisible {
|
||||||
isProgressPaused = true
|
isProgressPaused = true
|
||||||
}
|
}
|
||||||
|
if self.pendingNavigationToItemId != nil {
|
||||||
|
isProgressPaused = true
|
||||||
|
}
|
||||||
|
|
||||||
var dismissPanOffset: CGFloat = 0.0
|
var dismissPanOffset: CGFloat = 0.0
|
||||||
var dismissPanScale: CGFloat = 1.0
|
var dismissPanScale: CGFloat = 1.0
|
||||||
@ -1291,10 +1309,21 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
|
|
||||||
switch self.audioMode {
|
switch self.audioMode {
|
||||||
case .ambient:
|
case .ambient:
|
||||||
self.audioMode = .on
|
if self.isMuteSwitchOn {
|
||||||
for (_, itemSetView) in self.visibleItemSetViews {
|
self.audioMode = .off
|
||||||
if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
|
|
||||||
componentView.leaveAmbientMode()
|
for (_, itemSetView) in self.visibleItemSetViews {
|
||||||
|
if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
|
||||||
|
componentView.enterAmbientMode(ambient: !self.isMuteSwitchOn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.audioMode = .on
|
||||||
|
|
||||||
|
for (_, itemSetView) in self.visibleItemSetViews {
|
||||||
|
if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
|
||||||
|
componentView.leaveAmbientMode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .on:
|
case .on:
|
||||||
|
@ -170,8 +170,16 @@ final class StoryItemContentComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var shouldLoop = false
|
||||||
if self.progressMode == .blurred {
|
if self.progressMode == .blurred {
|
||||||
|
shouldLoop = true
|
||||||
|
} else if let component = self.component, component.item.isPending {
|
||||||
|
shouldLoop = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldLoop {
|
||||||
self.rewind()
|
self.rewind()
|
||||||
|
|
||||||
if let videoNode = self.videoNode {
|
if let videoNode = self.videoNode {
|
||||||
if self.contentLoaded {
|
if self.contentLoaded {
|
||||||
videoNode.play()
|
videoNode.play()
|
||||||
@ -251,12 +259,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
|
|
||||||
private func updateProgressMode(update: Bool) {
|
private func updateProgressMode(update: Bool) {
|
||||||
if let videoNode = self.videoNode {
|
if let videoNode = self.videoNode {
|
||||||
var canPlay = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
let canPlay = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
||||||
if let component = self.component {
|
|
||||||
if component.item.isPending {
|
|
||||||
canPlay = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if canPlay {
|
if canPlay {
|
||||||
videoNode.play()
|
videoNode.play()
|
||||||
@ -274,7 +277,10 @@ final class StoryItemContentComponent: Component {
|
|||||||
var needsTimer = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
var needsTimer = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
||||||
if let component = self.component {
|
if let component = self.component {
|
||||||
if component.item.isPending {
|
if component.item.isPending {
|
||||||
needsTimer = false
|
if case .file = self.currentMessageMedia {
|
||||||
|
} else {
|
||||||
|
needsTimer = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +614,18 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let captionItemView = self.captionItem?.view.view as? StoryContentCaptionComponent.View {
|
||||||
|
if captionItemView.hitTest(self.convert(point, to: captionItemView), with: nil) != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.controlsContainerView.frame.contains(point) {
|
if self.controlsContainerView.frame.contains(point) {
|
||||||
|
if let result = self.controlsContainerView.hitTest(self.convert(point, to: self.controlsContainerView), with: nil) {
|
||||||
|
if result != self.controlsContainerView {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyStoryIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyStoryIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_chatstoryreply.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
162
submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyStoryIcon.imageset/ic_chatstoryreply.pdf
vendored
Normal file
162
submodules/TelegramUI/Images.xcassets/Chat/Message/ReplyStoryIcon.imageset/ic_chatstoryreply.pdf
vendored
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.000000 1.000000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
7.000000 12.800000 m
|
||||||
|
6.974974 12.800000 6.949986 12.799842 6.925035 12.799525 c
|
||||||
|
5.418858 12.780447 4.051039 12.187255 3.030348 11.228721 c
|
||||||
|
2.952010 11.155152 2.875716 11.079432 2.801562 11.001655 c
|
||||||
|
1.840759 9.993905 1.239081 8.640710 1.201836 7.147378 c
|
||||||
|
1.200614 7.098401 1.200000 7.049272 1.200000 7.000000 c
|
||||||
|
1.200000 6.974975 1.200159 6.949987 1.200475 6.925036 c
|
||||||
|
1.218704 5.485904 1.761072 4.173086 2.645467 3.168697 c
|
||||||
|
2.788374 3.006401 2.940211 2.852158 3.100211 2.706736 c
|
||||||
|
4.082876 1.813602 5.373415 1.253222 6.793827 1.203597 c
|
||||||
|
6.862257 1.201206 6.930987 1.200001 7.000000 1.200001 c
|
||||||
|
7.257290 1.200001 7.510377 1.216717 7.758291 1.249055 c
|
||||||
|
8.086879 1.291916 8.387997 1.060289 8.430859 0.731703 c
|
||||||
|
8.473720 0.403115 8.242093 0.101996 7.913507 0.059135 c
|
||||||
|
7.614261 0.020102 7.309328 0.000000 7.000000 0.000000 c
|
||||||
|
6.939593 0.000000 6.879366 0.000765 6.819326 0.002286 c
|
||||||
|
5.110955 0.045568 3.554689 0.700914 2.361623 1.757235 c
|
||||||
|
2.171016 1.925995 1.989679 2.104989 1.818473 2.293358 c
|
||||||
|
0.688624 3.536468 0.000000 5.187816 0.000000 7.000000 c
|
||||||
|
0.000000 8.859291 0.724890 10.549273 1.907470 11.802750 c
|
||||||
|
1.992771 11.893165 2.080454 11.981308 2.170417 12.067080 c
|
||||||
|
3.426557 13.264702 5.127409 14.000000 7.000000 14.000000 c
|
||||||
|
7.309328 14.000000 7.614261 13.979899 7.913506 13.940866 c
|
||||||
|
8.242093 13.898004 8.473720 13.596886 8.430858 13.268298 c
|
||||||
|
8.387997 12.939711 8.086878 12.708084 7.758291 12.750946 c
|
||||||
|
7.510377 12.783284 7.257290 12.800000 7.000000 12.800000 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.000000 12.462891 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
10.420465 0.980462 m
|
||||||
|
10.622378 1.243212 10.999063 1.292530 11.261813 1.090617 c
|
||||||
|
11.746525 0.718136 12.181026 0.283636 12.553507 -0.201076 c
|
||||||
|
12.755420 -0.463827 12.706101 -0.840511 12.443351 -1.042424 c
|
||||||
|
12.180601 -1.244337 11.803917 -1.195019 11.602004 -0.932268 c
|
||||||
|
11.293073 -0.530255 10.932634 -0.169816 10.530622 0.139114 c
|
||||||
|
10.267871 0.341027 10.218553 0.717711 10.420465 0.980462 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.000000 12.052734 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
13.268296 -2.621875 m
|
||||||
|
13.596884 -2.579013 13.898003 -2.810640 13.940864 -3.139227 c
|
||||||
|
13.979897 -3.438472 13.999999 -3.743406 13.999999 -4.052734 c
|
||||||
|
13.999999 -4.362062 13.979897 -4.666995 13.940864 -4.966240 c
|
||||||
|
13.898003 -5.294827 13.596884 -5.526454 13.268296 -5.483593 c
|
||||||
|
12.939710 -5.440731 12.708083 -5.139613 12.750944 -4.811026 c
|
||||||
|
12.783282 -4.563112 12.799998 -4.310025 12.799998 -4.052734 c
|
||||||
|
12.799998 -3.795443 12.783282 -3.542356 12.750944 -3.294442 c
|
||||||
|
12.708083 -2.965855 12.939710 -2.664736 13.268296 -2.621875 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.000000 12.462891 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
12.443351 -7.883357 m
|
||||||
|
12.706101 -8.085270 12.755420 -8.461954 12.553507 -8.724705 c
|
||||||
|
12.181025 -9.209416 11.746525 -9.643917 11.261813 -10.016398 c
|
||||||
|
10.999063 -10.218311 10.622378 -10.168993 10.420465 -9.906242 c
|
||||||
|
10.218553 -9.643493 10.267871 -9.266809 10.530621 -9.064896 c
|
||||||
|
10.932634 -8.755964 11.293073 -8.395526 11.602003 -7.993513 c
|
||||||
|
11.803917 -7.730762 12.180601 -7.681444 12.443351 -7.883357 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 1.000000 8.817383 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
6.618695 1.719694 m
|
||||||
|
6.618695 1.022617 l
|
||||||
|
9.362720 1.022617 10.293095 -0.953440 10.605222 -2.430902 c
|
||||||
|
10.706805 -2.911749 10.757596 -3.152171 10.711860 -3.231688 c
|
||||||
|
10.668221 -3.307556 10.609138 -3.344767 10.521865 -3.351348 c
|
||||||
|
10.430392 -3.358245 10.195158 -3.177592 9.724692 -2.816287 c
|
||||||
|
9.077857 -2.319534 8.082672 -1.857383 6.618695 -1.857383 c
|
||||||
|
6.618695 -2.554460 l
|
||||||
|
6.618695 -3.016783 6.618695 -3.247944 6.525906 -3.359222 c
|
||||||
|
6.445327 -3.455857 6.323976 -3.508946 6.198317 -3.502536 c
|
||||||
|
6.053617 -3.495155 5.883851 -3.338263 5.544319 -3.024477 c
|
||||||
|
3.231889 -0.887401 l
|
||||||
|
3.055273 -0.724178 2.966961 -0.642563 2.934227 -0.546968 c
|
||||||
|
2.905464 -0.462972 2.905464 -0.371794 2.934227 -0.287798 c
|
||||||
|
2.966961 -0.192202 3.055270 -0.110590 3.231889 0.052636 c
|
||||||
|
5.544319 2.189712 l
|
||||||
|
5.883851 2.503497 6.053617 2.660389 6.198317 2.667771 c
|
||||||
|
6.323976 2.674181 6.445327 2.621092 6.525906 2.524457 c
|
||||||
|
6.618695 2.413178 6.618695 2.182017 6.618695 1.719694 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
4202
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 16.000000 16.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000004292 00000 n
|
||||||
|
0000004315 00000 n
|
||||||
|
0000004488 00000 n
|
||||||
|
0000004562 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
4621
|
||||||
|
%%EOF
|
@ -4531,7 +4531,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let storyContent = SingleStoryContentContextImpl(context: self.context, storyId: storyId)
|
let storyContent = SingleStoryContentContextImpl(context: self.context, storyId: storyId, readGlobally: false)
|
||||||
let _ = (storyContent.state
|
let _ = (storyContent.state
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
@ -4556,7 +4556,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
transitionIn = StoryContainerScreen.TransitionIn(
|
transitionIn = StoryContainerScreen.TransitionIn(
|
||||||
sourceView: result,
|
sourceView: result,
|
||||||
sourceRect: result.bounds,
|
sourceRect: result.bounds,
|
||||||
sourceCornerRadius: 2.0,
|
sourceCornerRadius: 6.0,
|
||||||
sourceIsAvatar: false
|
sourceIsAvatar: false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -5005,9 +5005,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
totalCount: storyStats.totalCount,
|
||||||
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
unseenCount: storyStats.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: false
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: strongSelf.presentationData.theme),
|
colors: AvatarNode.Colors(theme: strongSelf.presentationData.theme),
|
||||||
|
@ -611,7 +611,7 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
|||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: storyStats.totalCount,
|
totalCount: storyStats.totalCount,
|
||||||
unseenCount: storyStats.unseenCount,
|
unseenCount: storyStats.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: false
|
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
colors: AvatarNode.Colors(theme: theme),
|
colors: AvatarNode.Colors(theme: theme),
|
||||||
|
@ -113,6 +113,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
let isMedia: Bool
|
let isMedia: Bool
|
||||||
let isText: Bool
|
let isText: Bool
|
||||||
var isExpiredStory: Bool = false
|
var isExpiredStory: Bool = false
|
||||||
|
var isStory: Bool = false
|
||||||
|
|
||||||
if let message = arguments.message {
|
if let message = arguments.message {
|
||||||
let author = message.effectiveAuthor
|
let author = message.effectiveAuthor
|
||||||
@ -143,6 +144,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
textString = NSAttributedString(string: "Expired story")
|
textString = NSAttributedString(string: "Expired story")
|
||||||
isMedia = false
|
isMedia = false
|
||||||
} else {
|
} else {
|
||||||
|
isStory = true
|
||||||
textString = NSAttributedString(string: "Story")
|
textString = NSAttributedString(string: "Story")
|
||||||
isMedia = true
|
isMedia = true
|
||||||
}
|
}
|
||||||
@ -188,7 +190,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
case let .bubble(incoming):
|
case let .bubble(incoming):
|
||||||
titleColor = incoming ? (authorNameColor ?? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor) : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
titleColor = incoming ? (authorNameColor ?? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor) : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||||
lineImage = incoming ? (authorNameColor.flatMap({ PresentationResourcesChat.chatBubbleVerticalLineImage(color: $0) }) ?? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(arguments.presentationData.theme.theme)) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(arguments.presentationData.theme.theme)
|
lineImage = incoming ? (authorNameColor.flatMap({ PresentationResourcesChat.chatBubbleVerticalLineImage(color: $0) }) ?? PresentationResourcesChat.chatBubbleVerticalLineIncomingImage(arguments.presentationData.theme.theme)) : PresentationResourcesChat.chatBubbleVerticalLineOutgoingImage(arguments.presentationData.theme.theme)
|
||||||
if isExpiredStory {
|
if isExpiredStory || isStory {
|
||||||
textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||||
} else if isMedia {
|
} else if isMedia {
|
||||||
textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
|
textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor
|
||||||
@ -301,19 +303,19 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
let textInsets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0)
|
let textInsets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0)
|
||||||
|
|
||||||
let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets))
|
let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||||
if isExpiredStory {
|
if isExpiredStory || isStory {
|
||||||
contrainedTextSize.width -= 24.0
|
contrainedTextSize.width -= 26.0
|
||||||
}
|
}
|
||||||
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets))
|
let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets))
|
||||||
|
|
||||||
let imageSide: CGFloat
|
let imageSide: CGFloat
|
||||||
imageSide = titleLayout.size.height + textLayout.size.height - 16.0
|
imageSide = titleLayout.size.height + textLayout.size.height - 13.0
|
||||||
|
|
||||||
var applyImage: (() -> TransformImageNode)?
|
var applyImage: (() -> TransformImageNode)?
|
||||||
if let imageDimensions = imageDimensions {
|
if let imageDimensions = imageDimensions {
|
||||||
let boundingSize = CGSize(width: imageSide, height: imageSide)
|
let boundingSize = CGSize(width: imageSide, height: imageSide)
|
||||||
leftInset += imageSide + 2.0
|
leftInset += imageSide + 6.0
|
||||||
var radius: CGFloat = 2.0
|
var radius: CGFloat = 6.0
|
||||||
var imageSize = imageDimensions.aspectFilled(boundingSize)
|
var imageSize = imageDimensions.aspectFilled(boundingSize)
|
||||||
if hasRoundImage {
|
if hasRoundImage {
|
||||||
radius = boundingSize.width / 2.0
|
radius = boundingSize.width / 2.0
|
||||||
@ -359,8 +361,8 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var size = CGSize(width: max(titleLayout.size.width - textInsets.left - textInsets.right, textLayout.size.width - textInsets.left - textInsets.right) + leftInset, height: titleLayout.size.height + textLayout.size.height - 2 * (textInsets.top + textInsets.bottom) + 2 * spacing)
|
var size = CGSize(width: max(titleLayout.size.width - textInsets.left - textInsets.right, textLayout.size.width - textInsets.left - textInsets.right) + leftInset, height: titleLayout.size.height + textLayout.size.height - 2 * (textInsets.top + textInsets.bottom) + 2 * spacing)
|
||||||
if isExpiredStory {
|
if isExpiredStory || isStory {
|
||||||
size.width += 14.0
|
size.width += 16.0
|
||||||
}
|
}
|
||||||
|
|
||||||
return (size, { attemptSynchronous in
|
return (size, { attemptSynchronous in
|
||||||
@ -403,7 +405,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
node.addSubnode(imageNode)
|
node.addSubnode(imageNode)
|
||||||
node.imageNode = imageNode
|
node.imageNode = imageNode
|
||||||
}
|
}
|
||||||
imageNode.frame = CGRect(origin: CGPoint(x: 8.0, y: 4.0 + UIScreenPixel), size: CGSize(width: imageSide, height: imageSide))
|
imageNode.frame = CGRect(origin: CGPoint(x: 8.0, y: 3.0), size: CGSize(width: imageSide, height: imageSide))
|
||||||
|
|
||||||
if let updateImageSignal = updateImageSignal {
|
if let updateImageSignal = updateImageSignal {
|
||||||
imageNode.setSignal(updateImageSignal)
|
imageNode.setSignal(updateImageSignal)
|
||||||
@ -419,9 +421,9 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
titleNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: spacing - textInsets.top + 1.0), size: titleLayout.size)
|
titleNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: spacing - textInsets.top + 1.0), size: titleLayout.size)
|
||||||
|
|
||||||
let textFrame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top - 2.0), size: textLayout.size)
|
let textFrame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top - 2.0), size: textLayout.size)
|
||||||
textNode.textNode.frame = textFrame.offsetBy(dx: isExpiredStory ? 16.0 : 0.0, dy: 0.0)
|
textNode.textNode.frame = textFrame.offsetBy(dx: (isExpiredStory || isStory) ? 18.0 : 0.0, dy: 0.0)
|
||||||
|
|
||||||
if isExpiredStory {
|
if isExpiredStory || isStory {
|
||||||
let expiredStoryIconView: UIImageView
|
let expiredStoryIconView: UIImageView
|
||||||
if let current = node.expiredStoryIconView {
|
if let current = node.expiredStoryIconView {
|
||||||
expiredStoryIconView = current
|
expiredStoryIconView = current
|
||||||
@ -439,10 +441,20 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
imageType = incoming ? .incoming : .outgoing
|
imageType = incoming ? .incoming : .outgoing
|
||||||
}
|
}
|
||||||
|
|
||||||
expiredStoryIconView.image = PresentationResourcesChat.chatExpiredStoryIndicatorIcon(arguments.presentationData.theme.theme, type: imageType)
|
if isExpiredStory {
|
||||||
|
expiredStoryIconView.image = PresentationResourcesChat.chatExpiredStoryIndicatorIcon(arguments.presentationData.theme.theme, type: imageType)
|
||||||
|
} else {
|
||||||
|
expiredStoryIconView.image = PresentationResourcesChat.chatReplyStoryIndicatorIcon(arguments.presentationData.theme.theme, type: imageType)
|
||||||
|
}
|
||||||
if let image = expiredStoryIconView.image {
|
if let image = expiredStoryIconView.image {
|
||||||
let imageSize = CGSize(width: floor(image.size.width * 1.22), height: floor(image.size.height * 1.22))
|
let imageSize: CGSize
|
||||||
expiredStoryIconView.frame = CGRect(origin: CGPoint(x: textFrame.minX - 2.0, y: textFrame.minY + 2.0), size: imageSize)
|
if isExpiredStory {
|
||||||
|
imageSize = CGSize(width: floor(image.size.width * 1.22), height: floor(image.size.height * 1.22))
|
||||||
|
expiredStoryIconView.frame = CGRect(origin: CGPoint(x: textFrame.minX - 2.0, y: textFrame.minY + 2.0), size: imageSize)
|
||||||
|
} else {
|
||||||
|
imageSize = image.size
|
||||||
|
expiredStoryIconView.frame = CGRect(origin: CGPoint(x: textFrame.minX - 1.0, y: textFrame.minY + 3.0 + UIScreenPixel), size: imageSize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let expiredStoryIconView = node.expiredStoryIconView {
|
} else if let expiredStoryIconView = node.expiredStoryIconView {
|
||||||
expiredStoryIconView.removeFromSuperview()
|
expiredStoryIconView.removeFromSuperview()
|
||||||
|
@ -209,7 +209,7 @@ class ChatMessageStoryMentionContentNode: ChatMessageBubbleContentNode {
|
|||||||
if let story, let selectedMedia {
|
if let story, let selectedMedia {
|
||||||
if mediaUpdated {
|
if mediaUpdated {
|
||||||
if story.isForwardingDisabled {
|
if story.isForwardingDisabled {
|
||||||
let maxImageSize = CGSize(width: 180.0, height: 180.0)
|
let maxImageSize = CGSize(width: 180.0, height: 180.0).aspectFitted(imageSize)
|
||||||
let boundingImageSize = maxImageSize
|
let boundingImageSize = maxImageSize
|
||||||
|
|
||||||
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||||
|
@ -37,7 +37,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||||||
if let story {
|
if let story {
|
||||||
let navigationController = params.navigationController
|
let navigationController = params.navigationController
|
||||||
let context = params.context
|
let context = params.context
|
||||||
let storyContent = SingleStoryContentContextImpl(context: params.context, storyId: story.storyId)
|
let storyContent = SingleStoryContentContextImpl(context: params.context, storyId: story.storyId, readGlobally: story.isMention)
|
||||||
let _ = (storyContent.state
|
let _ = (storyContent.state
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak navigationController] _ in
|
|> deliverOnMainQueue).start(next: { [weak navigationController] _ in
|
||||||
|
@ -814,7 +814,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { exists in
|
|> deliverOnMainQueue).start(next: { exists in
|
||||||
if exists {
|
if exists {
|
||||||
let storyContent = SingleStoryContentContextImpl(context: context, storyId: StoryId(peerId: peerId, id: id))
|
let storyContent = SingleStoryContentContextImpl(context: context, storyId: StoryId(peerId: peerId, id: id), readGlobally: false)
|
||||||
let _ = (storyContent.state
|
let _ = (storyContent.state
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak navigationController] _ in
|
|> deliverOnMainQueue).start(next: { [weak navigationController] _ in
|
||||||
|
@ -423,7 +423,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let playbackStartDisposable = MetaDisposable()
|
private let playbackStartDisposable = MetaDisposable()
|
||||||
|
|
||||||
var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
|
var storyData: (totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool)?
|
||||||
|
|
||||||
init(context: AccountContext) {
|
init(context: AccountContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -464,8 +464,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
|||||||
]
|
]
|
||||||
self.avatarNode.setStoryStats(storyStats: self.storyData.flatMap { storyData in
|
self.avatarNode.setStoryStats(storyStats: self.storyData.flatMap { storyData in
|
||||||
return AvatarNode.StoryStats(
|
return AvatarNode.StoryStats(
|
||||||
totalCount: 1,
|
totalCount: storyData.totalCount,
|
||||||
unseenCount: storyData.hasUnseen ? 1 : 0,
|
unseenCount: storyData.unseenCount,
|
||||||
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends
|
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends
|
||||||
)
|
)
|
||||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||||
|
@ -3895,7 +3895,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
self.headerNode.avatarListNode.avatarContainerNode.storyData = 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.storyData = (state.hasUnseen, state.hasUnseenCloseFriends && peer.id != self.context.account.peerId)
|
let totalCount = state.items.count
|
||||||
|
var unseenCount = 0
|
||||||
|
for item in state.items {
|
||||||
|
if item.id > state.maxReadId {
|
||||||
|
unseenCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.headerNode.avatarListNode.avatarContainerNode.storyData = (totalCount, unseenCount, state.hasUnseenCloseFriends && peer.id != self.context.account.peerId)
|
||||||
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):
|
||||||
|
@ -129,7 +129,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
self.animatedStickerNode = nil
|
self.animatedStickerNode = nil
|
||||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
|
|
||||||
|
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||||
|
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||||
|
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
||||||
|
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in
|
||||||
|
return ("URL", contents)
|
||||||
|
}), textAlignment: .natural)
|
||||||
|
self.textNode.attributedText = attributedText
|
||||||
displayUndo = undo
|
displayUndo = undo
|
||||||
self.originalRemainingSeconds = 5
|
self.originalRemainingSeconds = 5
|
||||||
case let .hidArchive(title, text, undo):
|
case let .hidArchive(title, text, undo):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user