mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
e2afc7304c
@ -936,6 +936,12 @@ public final class AvatarNode: ASDisplayNode {
|
||||
|
||||
public func pushLoadingStatus(signal: Signal<Never, NoError>) -> Disposable {
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
for d in self.loadingStatuses.copyItems() {
|
||||
d.dispose()
|
||||
}
|
||||
self.loadingStatuses.removeAll()
|
||||
|
||||
let index = self.loadingStatuses.add(disposable)
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in
|
||||
|
@ -1881,24 +1881,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
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))
|
||||
|
||||
Queue.mainQueue().after(1.0, { [weak self] in
|
||||
@ -1935,12 +1917,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
unseenCount += 1
|
||||
}
|
||||
}
|
||||
let hasUnseenCloseFriends = rawStoryArchiveSubscriptions.items.contains(where: { $0.hasUnseenCloseFriends })
|
||||
archiveStoryState = ChatListNodeState.StoryState(
|
||||
stats: EngineChatList.StoryStats(
|
||||
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 {
|
||||
return
|
||||
}
|
||||
let undoValue: Bool
|
||||
if self.location == .chatList(groupId: .archive) {
|
||||
self.context.engine.peers.updatePeerStoriesHidden(id: peer.id, isHidden: false)
|
||||
undoValue = true
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
@ -2851,7 +2850,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
location: .point(location, .bottom),
|
||||
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 {
|
||||
let fitSize = contentImageSize
|
||||
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?()
|
||||
|
||||
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 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
|
||||
return ChatListNodeState.StoryState(
|
||||
stats: stats,
|
||||
hasUnseenCloseFriends: false
|
||||
hasUnseenCloseFriends: stats.hasUnseenCloseFriends
|
||||
)
|
||||
}
|
||||
))
|
||||
|
@ -1064,4 +1064,18 @@ public struct Transition {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func animateContentsImage(layer: CALayer, from fromImage: CGImage, to toImage: CGImage, duration: Double, curve: Transition.Animation.Curve, completion: ((Bool) -> Void)? = nil) {
|
||||
layer.animate(
|
||||
from: fromImage,
|
||||
to: toImage,
|
||||
keyPath: "contents",
|
||||
duration: duration,
|
||||
delay: 0.0,
|
||||
curve: .easeInOut,
|
||||
removeOnCompletion: true,
|
||||
additive: false,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1187,7 +1187,7 @@ open class TextNode: ASDisplayNode {
|
||||
if brokenLineRange.location + brokenLineRange.length > attributedString.length {
|
||||
brokenLineRange.length = attributedString.length - brokenLineRange.location
|
||||
}
|
||||
if lineRange.length == 0 {
|
||||
if lineRange.length == 0 && !didClipLinebreak {
|
||||
break
|
||||
}
|
||||
|
||||
@ -1202,7 +1202,11 @@ open class TextNode: ASDisplayNode {
|
||||
|
||||
let truncatedTokenString: NSAttributedString
|
||||
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 {
|
||||
var truncationTokenAttributes: [NSAttributedString.Key : AnyObject] = [:]
|
||||
truncationTokenAttributes[NSAttributedString.Key.font] = font
|
||||
|
@ -1358,7 +1358,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
return AvatarNode.StoryStats(
|
||||
totalCount: storyStats.totalCount,
|
||||
unseenCount: storyStats.unseenCount,
|
||||
hasUnseenCloseFriendsItems: false
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: item.presentationData.theme),
|
||||
|
@ -260,10 +260,12 @@ public enum ChatListEntry: Comparable {
|
||||
public struct PeerStoryStats: Equatable {
|
||||
public var totalCount: 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.unseenCount = unseenCount
|
||||
self.hasUnseenCloseFriends = hasUnseenCloseFriends
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,9 +284,9 @@ func fetchPeerStoryStats(postbox: PostboxImpl, peerId: PeerId) -> PeerStoryStats
|
||||
|
||||
if topItems.isExact {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
public func clearStoryItemsInexactMaxId(peerId: PeerId) {
|
||||
assert(!self.disposed)
|
||||
self.postbox!.clearStoryItemsInexactMaxId(peerId: peerId)
|
||||
}
|
||||
|
||||
public func getStoryItems(peerId: PeerId) -> [StoryItemsTableEntry] {
|
||||
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] {
|
||||
return self.storyItemsTable.get(peerId: peerId)
|
||||
}
|
||||
|
@ -4,15 +4,18 @@ public final class StoryItemsTableEntry: Equatable {
|
||||
public let value: CodableEntry
|
||||
public let id: Int32
|
||||
public let expirationTimestamp: Int32?
|
||||
public let isCloseFriends: Bool
|
||||
|
||||
public init(
|
||||
value: CodableEntry,
|
||||
id: Int32,
|
||||
expirationTimestamp: Int32?
|
||||
expirationTimestamp: Int32?,
|
||||
isCloseFriends: Bool
|
||||
) {
|
||||
self.value = value
|
||||
self.id = id
|
||||
self.expirationTimestamp = expirationTimestamp
|
||||
self.isCloseFriends = isCloseFriends
|
||||
}
|
||||
|
||||
public static func ==(lhs: StoryItemsTableEntry, rhs: StoryItemsTableEntry) -> Bool {
|
||||
@ -28,6 +31,9 @@ public final class StoryItemsTableEntry: Equatable {
|
||||
if lhs.expirationTimestamp != rhs.expirationTimestamp {
|
||||
return false
|
||||
}
|
||||
if lhs.isCloseFriends != rhs.isCloseFriends {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -133,22 +139,53 @@ final class StoryItemsTable: Table {
|
||||
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 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)
|
||||
|
||||
total += 1
|
||||
if id > maxSeenId {
|
||||
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
|
||||
}, limit: 10000)
|
||||
|
||||
return (total, unseen)
|
||||
return (total, unseen, hasUnseenCloseFriends)
|
||||
}
|
||||
|
||||
public func get(peerId: PeerId) -> [StoryItemsTableEntry] {
|
||||
@ -161,6 +198,7 @@ final class StoryItemsTable: Table {
|
||||
|
||||
let entry: CodableEntry
|
||||
var expirationTimestamp: Int32?
|
||||
var isCloseFriends = false
|
||||
|
||||
let readBuffer = ReadBuffer(data: value.makeData())
|
||||
var magic: UInt32 = 0
|
||||
@ -173,16 +211,41 @@ final class StoryItemsTable: Table {
|
||||
if readBuffer.offset + 4 <= readBuffer.length {
|
||||
var expirationTimestampValue: Int32 = 0
|
||||
readBuffer.read(&expirationTimestampValue, offset: 0, length: 4)
|
||||
expirationTimestamp = expirationTimestampValue
|
||||
if expirationTimestampValue != 0 {
|
||||
expirationTimestamp = expirationTimestampValue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
assertionFailure()
|
||||
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
|
||||
}, limit: 10000)
|
||||
@ -209,7 +272,22 @@ final class StoryItemsTable: Table {
|
||||
if readBuffer.offset + 4 <= readBuffer.length {
|
||||
var expirationTimestampValue: Int32 = 0
|
||||
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 {
|
||||
var expirationTimestampValue: Int32 = 0
|
||||
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 {
|
||||
buffer.reset()
|
||||
|
||||
var magic: UInt32 = 0xabcd1234
|
||||
var magic: UInt32 = 0xabcd1235
|
||||
buffer.write(&magic, length: 4)
|
||||
|
||||
var length: Int32 = Int32(entry.value.data.count)
|
||||
buffer.write(&length, length: 4)
|
||||
buffer.write(entry.value.data)
|
||||
|
||||
if let expirationTimestamp = entry.expirationTimestamp {
|
||||
var expirationTimestampValue: Int32 = expirationTimestamp
|
||||
buffer.write(&expirationTimestampValue, length: 4)
|
||||
var expirationTimestampValue: Int32 = entry.expirationTimestamp ?? 0
|
||||
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())
|
||||
}
|
||||
|
@ -4481,12 +4481,12 @@ func replayFinalState(
|
||||
if let currentIndex = updatedPeerEntries.firstIndex(where: { $0.id == storedItem.id }) {
|
||||
if case .item = 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 {
|
||||
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 {
|
||||
|
@ -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 {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
@ -888,7 +897,7 @@ func _internal_uploadStoryImpl(postbox: Postbox, network: Network, accountPeerId
|
||||
isEdited: item.isEdited
|
||||
)
|
||||
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)
|
||||
}
|
||||
@ -1066,7 +1075,7 @@ func _internal_editStoryPrivacy(account: Account, id: Int32, privacy: EngineStor
|
||||
isEdited: item.isEdited
|
||||
)
|
||||
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)
|
||||
@ -1193,7 +1202,7 @@ func _internal_updateStoriesArePinned(account: Account, ids: [Int32: EngineStory
|
||||
isEdited: item.isEdited
|
||||
)
|
||||
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)
|
||||
@ -1663,7 +1672,7 @@ public final class EngineStoryViewListContext {
|
||||
isEdited: item.isEdited
|
||||
))
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1840,7 +1849,7 @@ func _internal_refreshStories(account: Account, peerId: PeerId, ids: [Int32]) ->
|
||||
if let updatedItem = result.first(where: { $0.id == currentItems[i].id }) {
|
||||
if case .item = 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)
|
||||
} else {
|
||||
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)
|
||||
} else {
|
||||
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)
|
||||
} else {
|
||||
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
|
||||
))
|
||||
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 {
|
||||
if let telegramUser = TelegramUser.merge(transaction.getPeer(user.peerId) as? TelegramUser, rhs: user) {
|
||||
parsedPeers.append(telegramUser)
|
||||
|
||||
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 {
|
||||
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:
|
||||
break
|
||||
}
|
||||
|
@ -341,4 +341,5 @@ public enum PresentationResourceParameterKey: Hashable {
|
||||
case statusAutoremoveIcon(isActive: Bool)
|
||||
|
||||
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
|
||||
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 inactiveColors: [CGColor]
|
||||
|
@ -360,7 +360,7 @@ public final class PeerListItemComponent: Component {
|
||||
return AvatarNode.StoryStats(
|
||||
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
||||
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
||||
hasUnseenCloseFriendsItems: false
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: component.theme),
|
||||
|
@ -916,6 +916,7 @@ public final class StoryContentContextImpl: StoryContentContext {
|
||||
|
||||
public final class SingleStoryContentContextImpl: StoryContentContext {
|
||||
private let context: AccountContext
|
||||
private let readGlobally: Bool
|
||||
|
||||
public private(set) var stateValue: StoryContentContextState?
|
||||
public var state: Signal<StoryContentContextState, NoError> {
|
||||
@ -935,9 +936,11 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
storyId: StoryId
|
||||
storyId: StoryId,
|
||||
readGlobally: Bool
|
||||
) {
|
||||
self.context = context
|
||||
self.readGlobally = readGlobally
|
||||
|
||||
self.storyDisposable = (combineLatest(queue: .mainQueue(),
|
||||
context.engine.data.subscribe(
|
||||
@ -1084,6 +1087,9 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
||||
}
|
||||
|
||||
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 {
|
||||
didSet {
|
||||
switch self.state {
|
||||
case .began, .cancelled, .ended, .failed:
|
||||
/*switch self.state {
|
||||
case .cancelled, .ended, .failed:
|
||||
if self.isTracking {
|
||||
self.isTracking = false
|
||||
self.updateIsTracking?(false)
|
||||
self.updateIsTracking?(self.isTracking)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,6 +372,8 @@ private final class StoryContainerScreenComponent: Component {
|
||||
|
||||
var longPressRecognizer: StoryLongPressRecognizer?
|
||||
|
||||
private var pendingNavigationToItemId: (peerId: EnginePeer.Id, id: Int32)?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundLayer = SimpleLayer()
|
||||
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))
|
||||
}
|
||||
} else {
|
||||
let mappedDirection: StoryContentContextNavigation.ItemDirection
|
||||
var mappedId: Int32?
|
||||
switch direction {
|
||||
case .previous:
|
||||
mappedDirection = .previous
|
||||
mappedId = slice.previousItemId
|
||||
case .next:
|
||||
mappedDirection = .next
|
||||
mappedId = slice.nextItemId
|
||||
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.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(layer: self.backgroundLayer, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
@ -1104,6 +1119,9 @@ private final class StoryContainerScreenComponent: Component {
|
||||
if !environment.isVisible {
|
||||
isProgressPaused = true
|
||||
}
|
||||
if self.pendingNavigationToItemId != nil {
|
||||
isProgressPaused = true
|
||||
}
|
||||
|
||||
var dismissPanOffset: CGFloat = 0.0
|
||||
var dismissPanScale: CGFloat = 1.0
|
||||
@ -1291,10 +1309,21 @@ private final class StoryContainerScreenComponent: Component {
|
||||
|
||||
switch self.audioMode {
|
||||
case .ambient:
|
||||
self.audioMode = .on
|
||||
for (_, itemSetView) in self.visibleItemSetViews {
|
||||
if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
|
||||
componentView.leaveAmbientMode()
|
||||
if self.isMuteSwitchOn {
|
||||
self.audioMode = .off
|
||||
|
||||
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:
|
||||
|
@ -125,10 +125,10 @@ final class StoryContentCaptionComponent: Component {
|
||||
private let scrollFullMaskView: UIView
|
||||
private let scrollCenterMaskView: UIView
|
||||
private let scrollBottomMaskView: UIImageView
|
||||
private let scrollBottomFullMaskView: UIView
|
||||
private let scrollTopMaskView: UIImageView
|
||||
|
||||
private let shadowGradientLayer: SimpleGradientLayer
|
||||
private let shadowPlainLayer: SimpleLayer
|
||||
private let shadowGradientView: UIImageView
|
||||
|
||||
private var component: StoryContentCaptionComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
@ -140,9 +140,15 @@ final class StoryContentCaptionComponent: Component {
|
||||
|
||||
private var isExpanded: Bool = false
|
||||
|
||||
private static let shadowImage: UIImage? = {
|
||||
UIImage(named: "Stories/PanelGradient")
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.shadowGradientLayer = SimpleGradientLayer()
|
||||
self.shadowPlainLayer = SimpleLayer()
|
||||
self.shadowGradientView = UIImageView()
|
||||
if let image = StoryContentCaptionComponent.View.shadowImage {
|
||||
self.shadowGradientView.image = image.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(image.size.height - 1.0))
|
||||
}
|
||||
|
||||
self.scrollViewContainer = UIView()
|
||||
|
||||
@ -170,6 +176,10 @@ final class StoryContentCaptionComponent: Component {
|
||||
], locations: [0.0, 1.0]))
|
||||
self.scrollMaskContainer.addSubview(self.scrollBottomMaskView)
|
||||
|
||||
self.scrollBottomFullMaskView = UIView()
|
||||
self.scrollBottomFullMaskView.backgroundColor = .white
|
||||
self.scrollMaskContainer.addSubview(self.scrollBottomFullMaskView)
|
||||
|
||||
self.scrollTopMaskView = UIImageView(image: generateGradientImage(size: CGSize(width: 8.0, height: 8.0), colors: [
|
||||
UIColor(white: 1.0, alpha: 0.0),
|
||||
UIColor(white: 1.0, alpha: 1.0)
|
||||
@ -181,8 +191,7 @@ final class StoryContentCaptionComponent: Component {
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.layer.addSublayer(self.shadowGradientLayer)
|
||||
self.layer.addSublayer(self.shadowPlainLayer)
|
||||
self.addSubview(self.shadowGradientView)
|
||||
|
||||
self.scrollViewContainer.addSubview(self.scrollView)
|
||||
self.scrollView.delegate = self
|
||||
@ -258,8 +267,7 @@ final class StoryContentCaptionComponent: Component {
|
||||
|
||||
let shadowOverflow: CGFloat = 58.0
|
||||
let shadowFrame = CGRect(origin: CGPoint(x: 0.0, y: -self.scrollView.contentOffset.y + itemLayout.containerSize.height - itemLayout.visibleTextHeight - itemLayout.verticalInset - shadowOverflow), size: CGSize(width: itemLayout.containerSize.width, height: itemLayout.visibleTextHeight + itemLayout.verticalInset + shadowOverflow))
|
||||
transition.setFrame(layer: self.shadowGradientLayer, frame: shadowFrame)
|
||||
transition.setFrame(layer: self.shadowPlainLayer, frame: CGRect(origin: CGPoint(x: shadowFrame.minX, y: shadowFrame.maxY), size: CGSize(width: shadowFrame.width, height: self.scrollView.contentSize.height + 1000.0)))
|
||||
transition.setFrame(view: self.shadowGradientView, frame: CGRect(origin: CGPoint(x: shadowFrame.minX, y: shadowFrame.minY), size: CGSize(width: shadowFrame.width, height: self.scrollView.contentSize.height + 1000.0)))
|
||||
|
||||
let expandDistance: CGFloat = 50.0
|
||||
var expandFraction: CGFloat = self.scrollView.contentOffset.y / expandDistance
|
||||
@ -626,7 +634,7 @@ final class StoryContentCaptionComponent: Component {
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: availableSize.height)))
|
||||
transition.setFrame(view: self.scrollViewContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: availableSize.height)))
|
||||
|
||||
if self.shadowGradientLayer.colors == nil {
|
||||
/*if self.shadowGradientLayer.colors == nil {
|
||||
var locations: [NSNumber] = []
|
||||
var colors: [CGColor] = []
|
||||
let numStops = 10
|
||||
@ -646,7 +654,7 @@ final class StoryContentCaptionComponent: Component {
|
||||
self.shadowGradientLayer.type = .axial
|
||||
|
||||
self.shadowPlainLayer.backgroundColor = UIColor(white: 0.0, alpha: baseAlpha).cgColor
|
||||
}
|
||||
}*/
|
||||
|
||||
self.ignoreScrolling = false
|
||||
self.updateScrolling(transition: transition)
|
||||
@ -656,6 +664,7 @@ final class StoryContentCaptionComponent: Component {
|
||||
transition.setFrame(view: self.scrollFullMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: gradientEdgeHeight), size: CGSize(width: availableSize.width, height: availableSize.height - gradientEdgeHeight)))
|
||||
transition.setFrame(view: self.scrollCenterMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: gradientEdgeHeight), size: CGSize(width: availableSize.width, height: availableSize.height - gradientEdgeHeight * 2.0)))
|
||||
transition.setFrame(view: self.scrollBottomMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - gradientEdgeHeight), size: CGSize(width: availableSize.width, height: gradientEdgeHeight)))
|
||||
transition.setFrame(view: self.scrollBottomFullMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - gradientEdgeHeight), size: CGSize(width: availableSize.width, height: gradientEdgeHeight)))
|
||||
transition.setFrame(view: self.scrollTopMaskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: gradientEdgeHeight)))
|
||||
|
||||
self.ignoreExternalState = false
|
||||
@ -685,8 +694,10 @@ final class StoryContentCaptionComponent: Component {
|
||||
isExpandedTransition.setAlpha(view: dustNode.view, alpha: !self.isExpanded ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
isExpandedTransition.setAlpha(layer: self.shadowPlainLayer, alpha: self.isExpanded ? 0.0 : 1.0)
|
||||
isExpandedTransition.setAlpha(layer: self.shadowGradientLayer, alpha: self.isExpanded ? 0.0 : 1.0)
|
||||
isExpandedTransition.setAlpha(view: self.shadowGradientView, alpha: self.isExpanded ? 0.0 : 1.0)
|
||||
|
||||
isExpandedTransition.setAlpha(view: self.scrollBottomMaskView, alpha: self.isExpanded ? 1.0 : 0.0)
|
||||
isExpandedTransition.setAlpha(view: self.scrollBottomFullMaskView, alpha: self.isExpanded ? 0.0 : 1.0)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
@ -170,8 +170,16 @@ final class StoryItemContentComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
var shouldLoop = false
|
||||
if self.progressMode == .blurred {
|
||||
shouldLoop = true
|
||||
} else if let component = self.component, component.item.isPending {
|
||||
shouldLoop = true
|
||||
}
|
||||
|
||||
if shouldLoop {
|
||||
self.rewind()
|
||||
|
||||
if let videoNode = self.videoNode {
|
||||
if self.contentLoaded {
|
||||
videoNode.play()
|
||||
@ -251,12 +259,7 @@ final class StoryItemContentComponent: Component {
|
||||
|
||||
private func updateProgressMode(update: Bool) {
|
||||
if let videoNode = self.videoNode {
|
||||
var canPlay = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
||||
if let component = self.component {
|
||||
if component.item.isPending {
|
||||
canPlay = false
|
||||
}
|
||||
}
|
||||
let canPlay = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
||||
|
||||
if canPlay {
|
||||
videoNode.play()
|
||||
@ -274,7 +277,10 @@ final class StoryItemContentComponent: Component {
|
||||
var needsTimer = self.progressMode != .pause && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
||||
if let component = self.component {
|
||||
if component.item.isPending {
|
||||
needsTimer = false
|
||||
if case .file = self.currentMessageMedia {
|
||||
} else {
|
||||
needsTimer = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,6 +327,10 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
private static let shadowImage: UIImage? = {
|
||||
UIImage(named: "Stories/PanelGradient")
|
||||
}()
|
||||
|
||||
public final class View: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate {
|
||||
let sendMessageContext: StoryItemSetContainerSendMessage
|
||||
|
||||
@ -334,7 +338,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
let itemsContainerView: UIView
|
||||
let controlsContainerView: UIView
|
||||
let topContentGradientLayer: SimpleGradientLayer
|
||||
let topContentGradientView: UIImageView
|
||||
let bottomContentGradientLayer: SimpleGradientLayer
|
||||
let contentDimView: UIView
|
||||
|
||||
@ -417,7 +421,12 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.controlsContainerView.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
self.topContentGradientLayer = SimpleGradientLayer()
|
||||
self.topContentGradientView = UIImageView()
|
||||
if let image = StoryItemSetContainerComponent.shadowImage {
|
||||
self.topContentGradientView.image = image.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(image.size.height - 1.0))
|
||||
self.topContentGradientView.transform = CGAffineTransformMakeScale(1.0, -1.0)
|
||||
}
|
||||
|
||||
self.bottomContentGradientLayer = SimpleGradientLayer()
|
||||
|
||||
self.contentDimView = UIView()
|
||||
@ -441,7 +450,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.addSubview(self.controlsContainerView)
|
||||
|
||||
self.controlsContainerView.addSubview(self.contentDimView)
|
||||
self.controlsContainerView.layer.addSublayer(self.topContentGradientLayer)
|
||||
self.controlsContainerView.addSubview(self.topContentGradientView)
|
||||
self.layer.addSublayer(self.bottomContentGradientLayer)
|
||||
|
||||
self.closeButton.addSubview(self.closeButtonIconView)
|
||||
@ -605,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 let result = self.controlsContainerView.hitTest(self.convert(point, to: self.controlsContainerView), with: nil) {
|
||||
if result != self.controlsContainerView {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -1257,7 +1277,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
self.closeButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
|
||||
self.topContentGradientLayer.animateAlpha(from: 0.0, to: CGFloat(self.topContentGradientLayer.opacity), duration: 0.25)
|
||||
self.topContentGradientView.layer.animateAlpha(from: 0.0, to: self.topContentGradientView.alpha, duration: 0.25)
|
||||
|
||||
let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self)
|
||||
let innerSourceLocalFrame = CGRect(origin: CGPoint(x: sourceLocalFrame.minX - contentContainerView.frame.minX, y: sourceLocalFrame.minY - contentContainerView.frame.minY), size: sourceLocalFrame.size)
|
||||
@ -1385,7 +1405,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
captionView.layer.animateAlpha(from: captionView.alpha, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
}
|
||||
self.closeButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
self.topContentGradientLayer.animateAlpha(from: CGFloat(self.topContentGradientLayer.opacity), to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
self.topContentGradientView.layer.animateAlpha(from: self.topContentGradientView.alpha, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
|
||||
if let leftInfoView = self.leftInfoItem?.view.view {
|
||||
if transitionOut.destinationIsAvatar {
|
||||
@ -1624,7 +1644,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
resetScrollingOffsetWithItemTransition = true
|
||||
}
|
||||
|
||||
if self.topContentGradientLayer.colors == nil {
|
||||
/*if self.topContentGradientLayer.colors == nil {
|
||||
var locations: [NSNumber] = []
|
||||
var colors: [CGColor] = []
|
||||
let numStops = 4
|
||||
@ -1642,7 +1662,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.topContentGradientLayer.locations = locations
|
||||
self.topContentGradientLayer.colors = colors
|
||||
self.topContentGradientLayer.type = .axial
|
||||
}
|
||||
}*/
|
||||
if self.bottomContentGradientLayer.colors == nil {
|
||||
var locations: [NSNumber] = []
|
||||
var colors: [CGColor] = []
|
||||
@ -1662,7 +1682,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.bottomContentGradientLayer.colors = colors
|
||||
self.bottomContentGradientLayer.type = .axial
|
||||
|
||||
self.contentDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.3)
|
||||
self.contentDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
||||
}
|
||||
|
||||
let wasPanning = self.component?.isPanning ?? false
|
||||
@ -2569,9 +2589,10 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
let gradientHeight: CGFloat = 74.0
|
||||
transition.setFrame(layer: self.topContentGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: gradientHeight)))
|
||||
transition.setAlpha(layer: self.topContentGradientLayer, alpha: (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0)
|
||||
let topGradientHeight: CGFloat = 90.0
|
||||
let topContentGradientRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: topGradientHeight))
|
||||
transition.setPosition(view: self.topContentGradientView, position: topContentGradientRect.center)
|
||||
transition.setBounds(view: self.topContentGradientView, bounds: CGRect(origin: CGPoint(), size: topContentGradientRect.size))
|
||||
|
||||
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
||||
var inputPanelAlpha: CGFloat = component.slice.peer.id == component.context.account.peerId || component.hideUI || self.isEditingStory ? 0.0 : 1.0
|
||||
@ -2983,9 +3004,13 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
//transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: inputPanelIsOverlay ? 1.0 : 0.0)
|
||||
transition.setAlpha(layer: self.bottomContentGradientLayer, alpha: 0.0)
|
||||
|
||||
var topGradientAlpha: CGFloat = (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0
|
||||
var normalDimAlpha: CGFloat = 0.0
|
||||
var forceDimAnimation = false
|
||||
if let captionItem = self.captionItem {
|
||||
if captionItem.externalState.isExpanded {
|
||||
topGradientAlpha = 0.0
|
||||
}
|
||||
normalDimAlpha = captionItem.externalState.isExpanded ? 1.0 : 0.0
|
||||
if transition.animation.isImmediate && transition.userData(StoryContentCaptionComponent.TransitionHint.self)?.kind == .isExpandedUpdated {
|
||||
forceDimAnimation = true
|
||||
@ -2998,6 +3023,12 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
transition.setFrame(view: self.contentDimView, frame: CGRect(origin: CGPoint(), size: contentFrame.size))
|
||||
|
||||
if transition.animation.isImmediate && forceDimAnimation && self.topContentGradientView.alpha != topGradientAlpha {
|
||||
Transition(animation: .curve(duration: 0.25, curve: .easeInOut)).setAlpha(view: self.topContentGradientView, alpha: topGradientAlpha)
|
||||
} else {
|
||||
transition.setAlpha(view: self.topContentGradientView, alpha: topGradientAlpha)
|
||||
}
|
||||
|
||||
if transition.animation.isImmediate && forceDimAnimation && self.contentDimView.alpha != dimAlpha {
|
||||
Transition(animation: .curve(duration: 0.25, curve: .easeInOut)).setAlpha(view: self.contentDimView, alpha: dimAlpha)
|
||||
} else {
|
||||
|
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
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "smoothGradient.png",
|
||||
"filename" : "smoothGradient 0.4.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
BIN
submodules/TelegramUI/Images.xcassets/Stories/PanelGradient.imageset/smoothGradient 0.4.png
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Stories/PanelGradient.imageset/smoothGradient 0.4.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB |
@ -4531,7 +4531,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
let storyContent = SingleStoryContentContextImpl(context: self.context, storyId: storyId)
|
||||
let storyContent = SingleStoryContentContextImpl(context: self.context, storyId: storyId, readGlobally: false)
|
||||
let _ = (storyContent.state
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
@ -4556,7 +4556,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
transitionIn = StoryContainerScreen.TransitionIn(
|
||||
sourceView: result,
|
||||
sourceRect: result.bounds,
|
||||
sourceCornerRadius: 2.0,
|
||||
sourceCornerRadius: 6.0,
|
||||
sourceIsAvatar: false
|
||||
)
|
||||
}
|
||||
@ -5005,9 +5005,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return nil
|
||||
}
|
||||
return AvatarNode.StoryStats(
|
||||
totalCount: storyStats.totalCount == 0 ? 0 : 1,
|
||||
unseenCount: storyStats.unseenCount == 0 ? 0 : 1,
|
||||
hasUnseenCloseFriendsItems: false
|
||||
totalCount: storyStats.totalCount,
|
||||
unseenCount: storyStats.unseenCount,
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: strongSelf.presentationData.theme),
|
||||
|
@ -611,7 +611,7 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
return AvatarNode.StoryStats(
|
||||
totalCount: storyStats.totalCount,
|
||||
unseenCount: storyStats.unseenCount,
|
||||
hasUnseenCloseFriendsItems: false
|
||||
hasUnseenCloseFriendsItems: storyStats.hasUnseenCloseFriends
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
colors: AvatarNode.Colors(theme: theme),
|
||||
|
@ -113,6 +113,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
let isMedia: Bool
|
||||
let isText: Bool
|
||||
var isExpiredStory: Bool = false
|
||||
var isStory: Bool = false
|
||||
|
||||
if let message = arguments.message {
|
||||
let author = message.effectiveAuthor
|
||||
@ -143,6 +144,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
textString = NSAttributedString(string: "Expired story")
|
||||
isMedia = false
|
||||
} else {
|
||||
isStory = true
|
||||
textString = NSAttributedString(string: "Story")
|
||||
isMedia = true
|
||||
}
|
||||
@ -188,7 +190,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
case let .bubble(incoming):
|
||||
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)
|
||||
if isExpiredStory {
|
||||
if isExpiredStory || isStory {
|
||||
textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor
|
||||
} else if isMedia {
|
||||
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 (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 {
|
||||
contrainedTextSize.width -= 24.0
|
||||
if isExpiredStory || isStory {
|
||||
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 imageSide: CGFloat
|
||||
imageSide = titleLayout.size.height + textLayout.size.height - 16.0
|
||||
imageSide = titleLayout.size.height + textLayout.size.height - 13.0
|
||||
|
||||
var applyImage: (() -> TransformImageNode)?
|
||||
if let imageDimensions = imageDimensions {
|
||||
let boundingSize = CGSize(width: imageSide, height: imageSide)
|
||||
leftInset += imageSide + 2.0
|
||||
var radius: CGFloat = 2.0
|
||||
leftInset += imageSide + 6.0
|
||||
var radius: CGFloat = 6.0
|
||||
var imageSize = imageDimensions.aspectFilled(boundingSize)
|
||||
if hasRoundImage {
|
||||
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)
|
||||
if isExpiredStory {
|
||||
size.width += 14.0
|
||||
if isExpiredStory || isStory {
|
||||
size.width += 16.0
|
||||
}
|
||||
|
||||
return (size, { attemptSynchronous in
|
||||
@ -403,7 +405,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
node.addSubnode(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 {
|
||||
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)
|
||||
|
||||
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
|
||||
if let current = node.expiredStoryIconView {
|
||||
expiredStoryIconView = current
|
||||
@ -439,10 +441,20 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
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 {
|
||||
let 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)
|
||||
let imageSize: CGSize
|
||||
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 {
|
||||
expiredStoryIconView.removeFromSuperview()
|
||||
|
@ -209,7 +209,7 @@ class ChatMessageStoryMentionContentNode: ChatMessageBubbleContentNode {
|
||||
if let story, let selectedMedia {
|
||||
if mediaUpdated {
|
||||
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
|
||||
|
||||
var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
|
||||
|
@ -37,7 +37,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||
if let story {
|
||||
let navigationController = params.navigationController
|
||||
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
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak navigationController] _ in
|
||||
|
@ -814,7 +814,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { exists in
|
||||
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
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak navigationController] _ in
|
||||
|
@ -423,7 +423,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
|
||||
private let playbackStartDisposable = MetaDisposable()
|
||||
|
||||
var storyData: (hasUnseen: Bool, hasUnseenCloseFriends: Bool)?
|
||||
var storyData: (totalCount: Int, unseenCount: Int, hasUnseenCloseFriends: Bool)?
|
||||
|
||||
init(context: AccountContext) {
|
||||
self.context = context
|
||||
@ -464,8 +464,8 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
|
||||
]
|
||||
self.avatarNode.setStoryStats(storyStats: self.storyData.flatMap { storyData in
|
||||
return AvatarNode.StoryStats(
|
||||
totalCount: 1,
|
||||
unseenCount: storyData.hasUnseen ? 1 : 0,
|
||||
totalCount: storyData.totalCount,
|
||||
unseenCount: storyData.unseenCount,
|
||||
hasUnseenCloseFriendsItems: storyData.hasUnseenCloseFriends
|
||||
)
|
||||
}, presentationParams: AvatarNode.StoryPresentationParams(
|
||||
|
@ -3895,7 +3895,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
self.headerNode.avatarListNode.avatarContainerNode.storyData = nil
|
||||
self.headerNode.avatarListNode.listContainerNode.storyParams = nil
|
||||
} 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
|
||||
switch item {
|
||||
case let .item(item):
|
||||
|
@ -129,7 +129,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
self.animatedStickerNode = nil
|
||||
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
|
||||
self.originalRemainingSeconds = 5
|
||||
case let .hidArchive(title, text, undo):
|
||||
|
Loading…
x
Reference in New Issue
Block a user