This commit is contained in:
Ali 2023-07-21 19:58:19 +04:00
parent 9dd2e07b95
commit a11401b352
21 changed files with 417 additions and 78 deletions

View File

@ -9716,3 +9716,5 @@ Sorry for the inconvenience.";
"MediaEditor.Draft" = "Draft";
"Notification.LockScreenStoryPlaceholder" = "New Story";
"Chat.OpenStory" = "OPEN STORY";

View File

@ -3601,6 +3601,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
strongSelf.view.accessibilityCustomActions = nil
}
strongSelf.avatarTapRecognizer?.isEnabled = item.interaction.inlineNavigationLocation == nil
}
})
}

View File

@ -8,7 +8,7 @@ import ComponentFlow
import MultilineTextComponent
public protocol SparseItemGridLayer: CALayer {
func update(size: CGSize)
func update(size: CGSize, insets: UIEdgeInsets, displayItem: SparseItemGridDisplayItem, binding: SparseItemGridBinding, item: SparseItemGrid.Item?)
func needsShimmer() -> Bool
func getContents() -> Any?
@ -967,7 +967,7 @@ public final class SparseItemGrid: ASDisplayNode {
var bindItems: [Item] = []
var bindLayers: [SparseItemGridDisplayItem] = []
var updateLayers: [SparseItemGridDisplayItem] = []
var updateLayers: [(SparseItemGridDisplayItem, Int)] = []
let addBlur = layout.centerItems
@ -980,7 +980,7 @@ public final class SparseItemGrid: ASDisplayNode {
let itemLayer: VisibleItem
if let current = self.visibleItems[item.id] {
itemLayer = current
updateLayers.append(itemLayer)
updateLayers.append((itemLayer, index))
} else {
itemLayer = VisibleItem(layer: items.itemBinding.createLayer(), view: items.itemBinding.createView())
self.visibleItems[item.id] = itemLayer
@ -1057,10 +1057,11 @@ public final class SparseItemGrid: ASDisplayNode {
items.itemBinding.bindLayers(items: bindItems, layers: bindLayers, size: layout.containerLayout.size, insets: layout.containerLayout.insets, synchronous: synchronous)
}
for item in updateLayers {
for (item, index) in updateLayers {
let item = item as! VisibleItem
let contentItem = items.item(at: index)
if let layer = item.layer {
layer.update(size: layer.frame.size)
layer.update(size: layer.frame.size, insets: layout.containerLayout.insets, displayItem: item, binding: items.itemBinding, item: contentItem)
} else if let view = item.view {
view.update(size: view.layer.frame.size, insets: layout.containerLayout.insets)
}

View File

@ -1158,6 +1158,7 @@ public class Account {
self.managedOperationsDisposable.add(managedAutoexpireStoryOperations(network: self.network, postbox: self.postbox).start())
self.managedOperationsDisposable.add(managedPeerTimestampAttributeOperations(network: self.network, postbox: self.postbox).start())
self.managedOperationsDisposable.add(managedSynchronizeViewStoriesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedSynchronizePeerStoriesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedLocalTypingActivities(activities: self.localInputActivityManager.allActivities(), postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start())
let extractedExpr: [Signal<AccountRunningImportantTasks, NoError>] = [

View File

@ -197,6 +197,7 @@ private var declaredEncodables: Void = {
declareEncodable(SynchronizeAutosaveItemOperation.self, f: { SynchronizeAutosaveItemOperation(decoder: $0) })
declareEncodable(TelegramMediaStory.self, f: { TelegramMediaStory(decoder: $0) })
declareEncodable(SynchronizeViewStoriesOperation.self, f: { SynchronizeViewStoriesOperation(decoder: $0) })
declareEncodable(SynchronizePeerStoriesOperation.self, f: { SynchronizePeerStoriesOperation(decoder: $0) })
return
}()

View File

@ -4489,6 +4489,11 @@ func replayFinalState(
updatedPeerEntries.append(StoryItemsTableEntry(value: codedEntry, id: storedItem.id, expirationTimestamp: storedItem.expirationTimestamp, isCloseFriends: storedItem.isCloseFriends))
}
}
if case .item = storedItem {
if let codedEntry = CodableEntry(storedItem) {
transaction.setStory(id: StoryId(peerId: peerId, id: storedItem.id), value: codedEntry)
}
}
} else {
if case let .storyItemDeleted(id) = story {
if let index = updatedPeerEntries.firstIndex(where: { $0.id == id }) {
@ -4981,5 +4986,18 @@ func replayFinalState(
requestChatListFiltersSync(transaction: transaction)
}
for update in storyUpdates {
switch update {
case let .added(peerId, _):
if shouldKeepUserStoriesInFeed(peerId: peerId, isContact: transaction.isPeerContact(peerId: peerId)) {
if !transaction.storySubscriptionsContains(key: .hidden, peerId: peerId) && !transaction.storySubscriptionsContains(key: .filtered, peerId: peerId) {
_internal_addSynchronizePeerStoriesOperation(peerId: peerId, transaction: transaction)
}
}
default:
break
}
}
return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedReactionEvents: addedReactionEvents, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, storyUpdates: storyUpdates, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates, updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated)
}

View File

@ -0,0 +1,121 @@
import Foundation
import Postbox
import SwiftSignalKit
import TelegramApi
import MtProtoKit
private final class ManagedSynchronizePeerStoriesOperationsHelper {
var operationDisposables: [PeerId: Disposable] = [:]
func update(_ entries: [PeerMergedOperationLogEntry]) -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) {
var disposeOperations: [Disposable] = []
var beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)] = []
var validPeerIds: [PeerId] = []
for entry in entries {
guard let _ = entry.contents as? SynchronizePeerStoriesOperation else {
continue
}
validPeerIds.append(entry.peerId)
var replace = true
if let _ = self.operationDisposables[entry.peerId] {
} else {
replace = true
}
if replace {
let disposable = MetaDisposable()
self.operationDisposables[entry.peerId] = disposable
beginOperations.append((entry, disposable))
}
}
var removedPeerIds: [PeerId] = []
for (peerId, info) in self.operationDisposables {
if !validPeerIds.contains(peerId) {
removedPeerIds.append(peerId)
disposeOperations.append(info)
}
}
for peerId in removedPeerIds {
self.operationDisposables.removeValue(forKey: peerId)
}
return (disposeOperations, beginOperations)
}
func reset() -> [Disposable] {
let disposables = Array(self.operationDisposables.values)
self.operationDisposables.removeAll()
return disposables
}
}
private func withTakenOperation(postbox: Postbox, peerId: PeerId, tagLocalIndex: Int32, _ f: @escaping (Transaction, PeerMergedOperationLogEntry?) -> Signal<Void, NoError>) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Signal<Void, NoError> in
var result: PeerMergedOperationLogEntry?
transaction.operationLogUpdateEntry(peerId: peerId, tag: OperationLogTags.SynchronizePeerStories, tagLocalIndex: tagLocalIndex, { entry in
if let entry = entry, let _ = entry.mergedIndex, entry.contents is SynchronizePeerStoriesOperation {
result = entry.mergedEntry!
return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none)
} else {
return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none)
}
})
return f(transaction, result)
} |> switchToLatest
}
func managedSynchronizePeerStoriesOperations(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
return Signal { _ in
let helper = Atomic<ManagedSynchronizePeerStoriesOperationsHelper>(value: ManagedSynchronizePeerStoriesOperationsHelper())
let disposable = postbox.mergedOperationLogView(tag: OperationLogTags.SynchronizePeerStories, limit: 10).start(next: { view in
let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) in
return helper.update(view.entries)
}
for disposable in disposeOperations {
disposable.dispose()
}
for (entry, disposable) in beginOperations {
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
if let entry = entry {
if let operation = entry.contents as? SynchronizePeerStoriesOperation {
if let peer = transaction.getPeer(entry.peerId) {
return pushStoriesAreSeen(postbox: postbox, network: network, stateManager: stateManager, peer: peer, operation: operation)
} else {
return .complete()
}
} else {
assertionFailure()
}
}
return .complete()
})
|> then(postbox.transaction { transaction -> Void in
let _ = transaction.operationLogRemoveEntry(peerId: entry.peerId, tag: OperationLogTags.SynchronizePeerStories, tagLocalIndex: entry.tagLocalIndex)
})
disposable.set(signal.start())
}
})
return ActionDisposable {
let disposables = helper.with { helper -> [Disposable] in
return helper.reset()
}
for disposable in disposables {
disposable.dispose()
}
disposable.dispose()
}
}
}
private func pushStoriesAreSeen(postbox: Postbox, network: Network, stateManager: AccountStateManager, peer: Peer, operation: SynchronizePeerStoriesOperation) -> Signal<Void, NoError> {
return _internal_pollPeerStories(postbox: postbox, network: network, accountPeerId: stateManager.accountPeerId, peerId: peer.id, peerReference: PeerReference(peer))
|> map { _ -> Void in
}
}

View File

@ -186,6 +186,7 @@ public struct OperationLogTags {
public static let SynchronizeInstalledEmoji = PeerOperationLogTag(value: 22)
public static let SynchronizeAutosaveItems = PeerOperationLogTag(value: 23)
public static let SynchronizeViewStories = PeerOperationLogTag(value: 24)
public static let SynchronizePeerStories = PeerOperationLogTag(value: 25)
}
public struct LegacyPeerSummaryCounterTags: OptionSet, Sequence, Hashable {

View File

@ -0,0 +1,32 @@
import Foundation
import Postbox
public final class SynchronizePeerStoriesOperation: PostboxCoding {
public init() {
}
public init(decoder: PostboxDecoder) {
}
public func encode(_ encoder: PostboxEncoder) {
}
}
func _internal_addSynchronizePeerStoriesOperation(peerId: PeerId, transaction: Transaction) {
let tag: PeerOperationLogTag = OperationLogTags.SynchronizePeerStories
var topOperation: (SynchronizePeerStoriesOperation, Int32)?
transaction.operationLogEnumerateEntries(peerId: peerId, tag: tag, { entry in
if let operation = entry.contents as? SynchronizePeerStoriesOperation {
topOperation = (operation, entry.tagLocalIndex)
}
return false
})
var replace = false
if topOperation == nil {
replace = true
}
if replace {
transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizePeerStoriesOperation())
}
}

View File

@ -108,6 +108,10 @@ func _internal_deleteContactPeerInteractively(account: Account, peerId: PeerId)
account.stateManager.addUpdates(updates)
}
return account.postbox.transaction { transaction -> Void in
if let user = peer as? TelegramUser {
_internal_updatePeerIsContact(transaction: transaction, user: user, isContact: false)
}
var peerIds = transaction.getContactPeerIds()
if peerIds.contains(peerId) {
peerIds.remove(peerId)

View File

@ -1187,9 +1187,9 @@ public final class PeerExpiringStoryListContext {
}
}
public func _internal_pollPeerStories(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) -> Signal<Never, NoError> {
public func _internal_pollPeerStories(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, peerReference: PeerReference? = nil) -> Signal<Never, NoError> {
return postbox.transaction { transaction -> Api.InputUser? in
return transaction.getPeer(peerId).flatMap(apiInputUser)
return transaction.getPeer(peerId).flatMap(apiInputUser) ?? peerReference?.inputUser
}
|> mapToSignal { inputUser -> Signal<Never, NoError> in
guard let inputUser = inputUser else {
@ -1236,7 +1236,7 @@ public func _internal_pollPeerStories(postbox: Postbox, network: Network, accoun
transaction.setStoryItems(peerId: peerId, items: updatedPeerEntries)
if !updatedPeerEntries.isEmpty {
if !updatedPeerEntries.isEmpty, shouldKeepUserStoriesInFeed(peerId: peerId, isContact: transaction.isPeerContact(peerId: peerId)) {
if let user = transaction.getPeer(peerId) as? TelegramUser, let storiesHidden = user.storiesHidden {
if storiesHidden {
if !transaction.storySubscriptionsContains(key: .hidden, peerId: peerId) {

View File

@ -38,6 +38,13 @@ func minTimestampForPeerInclusion(_ peer: Peer) -> Int32? {
}
}
func shouldKeepUserStoriesInFeed(peerId: PeerId, isContact: Bool) -> Bool {
if peerId.namespace == Namespaces.Peer.CloudUser && (peerId.id._internalGetInt64Value() == 777000 || peerId.id._internalGetInt64Value() == 333000) {
return true
}
return isContact
}
func updatePeers(transaction: Transaction, accountPeerId: PeerId, peers: AccumulatedPeers) {
var parsedPeers: [Peer] = []
for (_, user) in peers.users {
@ -47,11 +54,17 @@ func updatePeers(transaction: Transaction, accountPeerId: PeerId, peers: Accumul
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 !isMin {
let isContact = (flags & (1 << 11)) != 0
_internal_updatePeerIsContact(transaction: transaction, user: telegramUser, isContact: isContact)
}
case .userEmpty:
break
}
@ -65,6 +78,54 @@ func updatePeers(transaction: Transaction, accountPeerId: PeerId, peers: Accumul
updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peers.users)
}
func _internal_updatePeerIsContact(transaction: Transaction, user: TelegramUser, isContact: Bool) {
let previousValue = shouldKeepUserStoriesInFeed(peerId: user.id, isContact: transaction.isPeerContact(peerId: user.id))
let updatedValue = shouldKeepUserStoriesInFeed(peerId: user.id, isContact: isContact)
if previousValue != updatedValue, let storiesHidden = user.storiesHidden {
if updatedValue {
if storiesHidden {
if transaction.storySubscriptionsContains(key: .filtered, peerId: user.id) {
var (state, peerIds) = transaction.getAllStorySubscriptions(key: .filtered)
peerIds.removeAll(where: { $0 == user.id })
transaction.replaceAllStorySubscriptions(key: .filtered, state: state, peerIds: peerIds)
}
if !transaction.storySubscriptionsContains(key: .hidden, peerId: user.id) {
var (state, peerIds) = transaction.getAllStorySubscriptions(key: .hidden)
if !peerIds.contains(user.id) {
peerIds.append(user.id)
transaction.replaceAllStorySubscriptions(key: .hidden, state: state, peerIds: peerIds)
}
}
} else {
if transaction.storySubscriptionsContains(key: .hidden, peerId: user.id) {
var (state, peerIds) = transaction.getAllStorySubscriptions(key: .hidden)
peerIds.removeAll(where: { $0 == user.id })
transaction.replaceAllStorySubscriptions(key: .hidden, state: state, peerIds: peerIds)
}
if !transaction.storySubscriptionsContains(key: .filtered, peerId: user.id) {
var (state, peerIds) = transaction.getAllStorySubscriptions(key: .filtered)
if !peerIds.contains(user.id) {
peerIds.append(user.id)
transaction.replaceAllStorySubscriptions(key: .filtered, state: state, peerIds: peerIds)
}
}
}
} else {
if transaction.storySubscriptionsContains(key: .filtered, peerId: user.id) {
var (state, peerIds) = transaction.getAllStorySubscriptions(key: .filtered)
peerIds.removeAll(where: { $0 == user.id })
transaction.replaceAllStorySubscriptions(key: .filtered, state: state, peerIds: peerIds)
}
if transaction.storySubscriptionsContains(key: .hidden, peerId: user.id) {
var (state, peerIds) = transaction.getAllStorySubscriptions(key: .hidden)
peerIds.removeAll(where: { $0 == user.id })
transaction.replaceAllStorySubscriptions(key: .hidden, state: state, peerIds: peerIds)
}
}
}
}
public func updatePeersCustom(transaction: Transaction, peers: [Peer], update: (Peer?, Peer) -> Peer?) {
transaction.updatePeersInternal(peers, update: { previous, updated in
let peerId = updated.id
@ -79,7 +140,8 @@ public func updatePeersCustom(transaction: Transaction, peers: [Peer], update: (
switch peerId.namespace {
case Namespaces.Peer.CloudUser:
if let updated = updated as? TelegramUser, let previous = previous as? TelegramUser, let storiesHidden = updated.storiesHidden, storiesHidden != previous.storiesHidden {
if let updated = updated as? TelegramUser, let previous = previous as? TelegramUser {
if let storiesHidden = updated.storiesHidden, storiesHidden != previous.storiesHidden {
if storiesHidden {
if transaction.storySubscriptionsContains(key: .filtered, peerId: updated.id) {
var (state, peerIds) = transaction.getAllStorySubscriptions(key: .filtered)
@ -110,6 +172,7 @@ public func updatePeersCustom(transaction: Transaction, peers: [Peer], update: (
}
}
}
}
case Namespaces.Peer.CloudGroup:
if let group = updated as? TelegramGroup {
if group.flags.contains(.deactivated) {

View File

@ -270,6 +270,13 @@ final class PeerInfoStoryGridScreenComponent: Component {
})
}
func scrollToTop() {
guard let paneNode = self.paneNode else {
return
}
let _ = paneNode.scrollToTop()
}
func update(component: PeerInfoStoryGridScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
self.component = component
self.state = state
@ -500,6 +507,13 @@ public class PeerInfoStoryGridScreen: ViewControllerComponentContainer {
self.navigationItem.titleView = self.titleView
self.updateTitle()
self.scrollToTop = { [weak self] in
guard let self, let componentView = self.node.hostView.componentView as? PeerInfoStoryGridScreenComponent.View else {
return
}
componentView.scrollToTop()
}
}
required public init(coder aDecoder: NSCoder) {

View File

@ -329,10 +329,14 @@ private final class GenericItemLayer: CALayer, ItemLayer {
return !self.hasContents
}
func update(size: CGSize) {
/*if let durationLayer = self.durationLayer {
func update(size: CGSize, insets: UIEdgeInsets, displayItem: SparseItemGridDisplayItem, binding: SparseItemGridBinding, item: SparseItemGrid.Item?) {
if let durationLayer = self.durationLayer {
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize())
}*/
}
if let binding = binding as? SparseItemGridBindingImpl, let item = item as? VisualMediaItem, let previousItem = self.item, previousItem.story.media.id != item.story.media.id {
binding.bindLayers(items: [item], layers: [displayItem], size: size, insets: insets, synchronous: .none)
}
}
}
@ -469,10 +473,10 @@ private final class CaptureProtectedItemLayer: AVSampleBufferDisplayLayer, ItemL
return !self.hasContents
}
func update(size: CGSize) {
/*if let durationLayer = self.durationLayer {
func update(size: CGSize, insets: UIEdgeInsets, displayItem: SparseItemGridDisplayItem, binding: SparseItemGridBinding, item: SparseItemGrid.Item?) {
if let durationLayer = self.durationLayer {
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize())
}*/
}
}
}
@ -1229,7 +1233,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
self.itemGridBinding.itemInteraction = self._itemInteraction
self.contextGestureContainerNode.isGestureEnabled = true
self.contextGestureContainerNode.isGestureEnabled = false
self.contextGestureContainerNode.addSubnode(self.itemGrid)
self.addSubnode(self.contextGestureContainerNode)
@ -1631,6 +1635,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
gridSnapshot = self.itemGrid.view.snapshotView(afterScreenUpdates: false)
}
self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: false, transition: .immediate)
self.updateSelectedItems(animated: false)
if let gridSnapshot = gridSnapshot {
self.view.addSubview(gridSnapshot)
gridSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak gridSnapshot] _ in
@ -1733,16 +1738,16 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
self.itemGrid.addToTransitionSurface(view: view)
}
private var gridSelectionGesture: MediaPickerGridSelectionGesture<EngineMessage.Id>?
private var gridSelectionGesture: MediaPickerGridSelectionGesture<Int32>?
override public func didLoad() {
super.didLoad()
let selectionRecognizer = MediaListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:)))
/*let selectionRecognizer = MediaListSelectionRecognizer(target: self, action: #selector(self.selectionPanGesture(_:)))
selectionRecognizer.shouldBegin = {
return true
}
self.view.addGestureRecognizer(selectionRecognizer)
self.view.addGestureRecognizer(selectionRecognizer)*/
}
private var selectionPanState: (selecting: Bool, initialMessageId: EngineMessage.Id, toggledMessageIds: [[EngineMessage.Id]])?
@ -1807,17 +1812,18 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
}
override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let location = gestureRecognizer.location(in: gestureRecognizer.view)
/*let location = gestureRecognizer.location(in: gestureRecognizer.view)
if location.x < 44.0 {
return false
}
}*/
return true
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer.state != .failed, let otherGestureRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer {
otherGestureRecognizer.isEnabled = false
otherGestureRecognizer.isEnabled = true
let _ = otherGestureRecognizer
//otherGestureRecognizer.isEnabled = false
//otherGestureRecognizer.isEnabled = true
return true
} else {
return false
@ -1841,27 +1847,29 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
itemLayer.updateSelection(theme: self.itemGridBinding.checkNodeTheme, isSelected: self.itemInteraction.selectedIds?.contains(item.story.id), animated: animated)
}
/*let isSelecting = self.chatControllerInteraction.selectionState != nil
let isSelecting = self._itemInteraction?.selectedIds != nil
self.itemGrid.pinchEnabled = !isSelecting
self.view.disablesInteractiveTransitionGestureRecognizer = isSelecting
if isSelecting {
if self.gridSelectionGesture == nil {
let selectionGesture = MediaPickerGridSelectionGesture<EngineMessage.Id>()
let selectionGesture = MediaPickerGridSelectionGesture<Int32>()
selectionGesture.delegate = self
selectionGesture.sideInset = 44.0
selectionGesture.updateIsScrollEnabled = { [weak self] isEnabled in
self?.itemGrid.isScrollEnabled = isEnabled
}
selectionGesture.itemAt = { [weak self] point in
if let strongSelf = self, let itemLayer = strongSelf.itemGrid.item(at: point)?.layer as? ItemLayer, let messageId = itemLayer.item?.message.id {
return (messageId, strongSelf.chatControllerInteraction.selectionState?.selectedIds.contains(messageId) ?? false)
if let strongSelf = self, let itemLayer = strongSelf.itemGrid.item(at: point)?.layer as? ItemLayer, let storyId = itemLayer.item?.story.id {
return (storyId, strongSelf._itemInteraction?.selectedIds?.contains(storyId) ?? false)
} else {
return nil
}
}
selectionGesture.updateSelection = { [weak self] messageId, selected in
selectionGesture.updateSelection = { [weak self] storyId, selected in
if let strongSelf = self {
strongSelf.chatControllerInteraction.toggleMessagesSelection([messageId], selected)
strongSelf._itemInteraction?.toggleSelection(storyId, selected)
}
}
self.itemGrid.view.addGestureRecognizer(selectionGesture)
@ -1870,7 +1878,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
} else if let gridSelectionGesture = self.gridSelectionGesture {
self.itemGrid.view.removeGestureRecognizer(gridSelectionGesture)
self.gridSelectionGesture = nil
}*/
}
}
private func updateHiddenItems() {

View File

@ -473,7 +473,7 @@ private final class GenericItemLayer: CALayer, ItemLayer {
return !self.hasContents
}
func update(size: CGSize) {
func update(size: CGSize, insets: UIEdgeInsets, displayItem: SparseItemGridDisplayItem, binding: SparseItemGridBinding, item: SparseItemGrid.Item?) {
/*if let durationLayer = self.durationLayer {
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize())
}*/
@ -613,7 +613,7 @@ private final class CaptureProtectedItemLayer: AVSampleBufferDisplayLayer, ItemL
return !self.hasContents
}
func update(size: CGSize) {
func update(size: CGSize, insets: UIEdgeInsets, displayItem: SparseItemGridDisplayItem, binding: SparseItemGridBinding, item: SparseItemGrid.Item?) {
/*if let durationLayer = self.durationLayer {
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize())
}*/

View File

@ -914,9 +914,11 @@ public final class StoryContentContextImpl: StoryContentContext {
}
public func markAsSeen(id: StoryId) {
if !self.context.sharedContext.immediateExperimentalUISettings.skipReadHistory {
let _ = self.context.engine.messages.markStoryAsSeen(peerId: id.peerId, id: id.id, asPinned: false).start()
}
}
}
public final class SingleStoryContentContextImpl: StoryContentContext {
private let context: AccountContext
@ -1094,10 +1096,12 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
public func markAsSeen(id: StoryId) {
if self.readGlobally {
if !self.context.sharedContext.immediateExperimentalUISettings.skipReadHistory {
let _ = self.context.engine.messages.markStoryAsSeen(peerId: id.peerId, id: id.id, asPinned: false).start()
}
}
}
}
public final class PeerStoryListContentContextImpl: StoryContentContext {
private let context: AccountContext
@ -1397,9 +1401,11 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
}
public func markAsSeen(id: StoryId) {
if !self.context.sharedContext.immediateExperimentalUISettings.skipReadHistory {
let _ = self.context.engine.messages.markStoryAsSeen(peerId: id.peerId, id: id.id, asPinned: true).start()
}
}
}
public func preloadStoryMedia(context: AccountContext, peer: PeerReference, storyId: Int32, media: EngineMedia) -> Signal<Never, NoError> {
var signals: [Signal<Never, NoError>] = []

View File

@ -271,6 +271,7 @@ public final class StoryItemSetContainerComponent: Component {
final class VisibleItem {
let externalState = StoryContentItem.ExternalState()
let contentContainerView: UIView
let contentTintLayer = SimpleLayer()
let view = ComponentView<StoryContentItem.Environment>()
var currentProgress: Double = 0.0
var isBuffering: Bool = false
@ -282,6 +283,8 @@ public final class StoryItemSetContainerComponent: Component {
if #available(iOS 13.0, *) {
self.contentContainerView.layer.cornerCurve = .continuous
}
self.contentTintLayer.backgroundColor = UIColor(white: 0.0, alpha: 1.0).cgColor
}
}
@ -952,6 +955,9 @@ public final class StoryItemSetContainerComponent: Component {
if self.sendMessageContext.shareController != nil {
return .pause
}
if self.sendMessageContext.statusController != nil {
return .pause
}
if let navigationController = component.controller()?.navigationController as? NavigationController {
let topViewController = navigationController.topViewController
if !(topViewController is StoryContainerScreen) && !(topViewController is MediaEditorScreen) && !(topViewController is ShareWithPeersScreen) && !(topViewController is AttachmentController) {
@ -1119,6 +1125,7 @@ public final class StoryItemSetContainerComponent: Component {
if let view = visibleItem.view.view {
if visibleItem.contentContainerView.superview == nil {
self.itemsContainerView.addSubview(visibleItem.contentContainerView)
self.itemsContainerView.layer.addSublayer(visibleItem.contentTintLayer)
visibleItem.contentContainerView.addSubview(view)
}
@ -1137,6 +1144,9 @@ public final class StoryItemSetContainerComponent: Component {
})
itemTransition.setBounds(view: visibleItem.contentContainerView, bounds: CGRect(origin: CGPoint(), size: itemLayout.contentFrame.size))
itemTransition.setPosition(layer: visibleItem.contentTintLayer, position: CGPoint(x: itemPositionX, y: itemLayout.contentFrame.center.y))
itemTransition.setBounds(layer: visibleItem.contentTintLayer, bounds: CGRect(origin: CGPoint(), size: itemLayout.contentFrame.size))
var transform = CATransform3DMakeScale(itemScale, itemScale, 1.0)
if let pinchState = component.pinchState {
let pinchOffset = CGPoint(
@ -1154,6 +1164,8 @@ public final class StoryItemSetContainerComponent: Component {
itemTransition.setTransform(view: visibleItem.contentContainerView, transform: transform)
itemTransition.setCornerRadius(layer: visibleItem.contentContainerView.layer, cornerRadius: 12.0 * (1.0 / itemScale))
itemTransition.setTransform(layer: visibleItem.contentTintLayer, transform: transform)
let countedFractionDistanceToCenter: CGFloat = max(0.0, min(1.0, unboundFractionDistanceToCenter / 3.0))
var itemAlpha: CGFloat = 1.0 * (1.0 - countedFractionDistanceToCenter) + 0.0 * countedFractionDistanceToCenter
itemAlpha = max(0.0, min(1.0, itemAlpha))
@ -1161,7 +1173,7 @@ public final class StoryItemSetContainerComponent: Component {
let collapsedAlpha = itemAlpha * itemLayout.contentScaleFraction + 0.0 * (1.0 - itemLayout.contentScaleFraction)
itemAlpha = (1.0 - fractionDistanceToCenter) * itemAlpha + fractionDistanceToCenter * collapsedAlpha
itemTransition.setAlpha(view: visibleItem.contentContainerView, alpha: itemAlpha)
itemTransition.setAlpha(layer: visibleItem.contentTintLayer, alpha: 1.0 - itemAlpha)
var itemProgressMode = self.itemProgressMode()
if index != centralIndex {
@ -1182,6 +1194,7 @@ public final class StoryItemSetContainerComponent: Component {
if !validIds.contains(id) {
removeIds.append(id)
visibleItem.contentContainerView.removeFromSuperview()
visibleItem.contentTintLayer.removeFromSuperlayer()
}
}
for id in removeIds {

View File

@ -57,6 +57,7 @@ final class StoryItemSetContainerSendMessage {
weak var shareController: ShareController?
weak var tooltipScreen: ViewController?
weak var actionSheet: ViewController?
weak var statusController: ViewController?
var isViewingAttachedStickers = false
var currentInputMode: InputMode = .text
@ -2473,14 +2474,21 @@ final class StoryItemSetContainerSendMessage {
var cancelImpl: (() -> Void)?
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let progressSignal = Signal<Never, NoError> { [weak parentController] subscriber in
let progressSignal = Signal<Never, NoError> { [weak self, weak view, weak parentController] subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
cancelImpl?()
}))
parentController?.present(controller, in: .window(.root))
self?.statusController = controller
view?.updateIsProgressPaused()
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
self?.statusController = nil
view?.updateIsProgressPaused()
}
}
}
@ -2547,14 +2555,21 @@ final class StoryItemSetContainerSendMessage {
}
var cancelImpl: (() -> Void)?
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let progressSignal = Signal<Never, NoError> { [weak parentController] subscriber in
let progressSignal = Signal<Never, NoError> { [weak parentController, weak self, weak view] subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: {
cancelImpl?()
}))
parentController?.present(controller, in: .window(.root))
self?.statusController = controller
view?.updateIsProgressPaused()
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
self?.statusController = nil
view?.updateIsProgressPaused()
}
}
}
@ -2739,12 +2754,19 @@ final class StoryItemSetContainerSendMessage {
}
let context = component.context
let presentationData = context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkPresentationTheme)
let progressSignal = Signal<Never, NoError> { [weak parentController] subscriber in
let progressSignal = Signal<Never, NoError> { [weak parentController, weak self, weak view] subscriber in
let progressController = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
parentController?.present(progressController, in: .window(.root), with: nil)
self?.statusController = progressController
view?.updateIsProgressPaused()
return ActionDisposable { [weak progressController] in
Queue.mainQueue().async() {
progressController?.dismiss()
self?.statusController = nil
view?.updateIsProgressPaused()
}
}
}

View File

@ -356,6 +356,8 @@ public final class StoryPeerListComponent: Component {
public private(set) var overscrollSelectedId: EnginePeer.Id?
public private(set) var overscrollHiddenChatItemsAllowed: Bool = false
private var anchorForTooltipRect: CGRect?
private var sharedBlurEffect: NSObject?
public override init(frame: CGRect) {
@ -482,7 +484,11 @@ public final class StoryPeerListComponent: Component {
}
public func anchorForTooltip() -> (UIView, CGRect)? {
return (self.collapsedButton, self.collapsedButton.bounds)
if let anchorForTooltipRect = self.anchorForTooltipRect {
return (self, anchorForTooltipRect)
} else {
return nil
}
}
public func titleFrame() -> CGRect {
@ -631,6 +637,7 @@ public final class StoryPeerListComponent: Component {
var minFraction: CGFloat
var maxFraction: CGFloat
var sideAlphaFraction: CGFloat
var expandEffectFraction: CGFloat
var titleWidth: CGFloat
var activityFraction: CGFloat
}
@ -668,6 +675,18 @@ public final class StoryPeerListComponent: Component {
let timestamp = CACurrentMediaTime()
let calculateOverscrollEffectFraction: (CGFloat, CGFloat) -> CGFloat = { maxFraction, bounceFraction in
var expandEffectFraction: CGFloat = max(0.0, min(1.0, maxFraction))
expandEffectFraction = 1.0 - pow(1.0 - expandEffectFraction, 2.0)
let overscrollEffectFraction = max(0.0, maxFraction - 1.0)
expandEffectFraction += overscrollEffectFraction * 0.1
expandEffectFraction += pow(bounceFraction, 1.4) * 0.2 * maxFraction
return expandEffectFraction
}
let collapsedState: CollapseState
let expandBoundsFraction: CGFloat
if let animationState = self.animationState {
@ -703,16 +722,6 @@ public final class StoryPeerListComponent: Component {
let animatedTitleWidth = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: animationState.fromTitleWidth, toFraction: realTitleContentWidth)
let animatedActivityFraction = animationState.interpolatedFraction(at: timestamp, effectiveFromFraction: animationState.fromActivityFraction, toFraction: targetActivityFraction)
collapsedState = CollapseState(
globalFraction: animatedGlobalFraction,
scaleFraction: animatedScaleFraction,
minFraction: animatedMinFraction,
maxFraction: animatedMaxFraction,
sideAlphaFraction: animatedSideAlphaFraction,
titleWidth: animatedTitleWidth,
activityFraction: animatedActivityFraction
)
var rawProgress = CGFloat((timestamp - animationState.startTime) / animationState.duration)
rawProgress = max(0.0, min(1.0, rawProgress))
@ -724,6 +733,17 @@ public final class StoryPeerListComponent: Component {
} else {
expandBoundsFraction = 0.0
}
collapsedState = CollapseState(
globalFraction: animatedGlobalFraction,
scaleFraction: animatedScaleFraction,
minFraction: animatedMinFraction,
maxFraction: animatedMaxFraction,
sideAlphaFraction: animatedSideAlphaFraction,
expandEffectFraction: calculateOverscrollEffectFraction(animatedMaxFraction, expandBoundsFraction),
titleWidth: animatedTitleWidth,
activityFraction: animatedActivityFraction
)
} else {
collapsedState = CollapseState(
globalFraction: targetFraction,
@ -731,6 +751,7 @@ public final class StoryPeerListComponent: Component {
minFraction: targetMinFraction,
maxFraction: targetMaxFraction,
sideAlphaFraction: targetSideAlphaFraction,
expandEffectFraction: calculateOverscrollEffectFraction(targetMaxFraction, 0.0),
titleWidth: realTitleContentWidth,
activityFraction: targetActivityFraction
)
@ -1031,6 +1052,7 @@ public final class StoryPeerListComponent: Component {
scale: itemScale,
fullWidth: expandedItemWidth,
expandedAlphaFraction: collapsedState.sideAlphaFraction,
expandEffectFraction: collapsedState.expandEffectFraction,
leftNeighborDistance: leftNeighborDistance,
rightNeighborDistance: rightNeighborDistance,
action: component.peerAction,
@ -1166,6 +1188,7 @@ public final class StoryPeerListComponent: Component {
scale: itemScale,
fullWidth: expandedItemWidth,
expandedAlphaFraction: collapsedState.sideAlphaFraction,
expandEffectFraction: collapsedState.expandEffectFraction,
leftNeighborDistance: leftNeighborDistance,
rightNeighborDistance: rightNeighborDistance,
action: component.peerAction,
@ -1229,6 +1252,7 @@ public final class StoryPeerListComponent: Component {
}
transition.setFrame(view: self.collapsedButton, frame: CGRect(origin: CGPoint(x: component.minTitleX, y: 6.0 - 59.0), size: CGSize(width: max(0.0, component.maxTitleX - component.minTitleX), height: 44.0)))
self.anchorForTooltipRect = CGRect(origin: CGPoint(x: collapsedContentOrigin, y: -59.0 + 6.0 + 2.0), size: CGSize(width: collapsedContentWidth, height: 44.0))
let defaultCollapsedTitleOffset: CGFloat = 0.0

View File

@ -57,7 +57,7 @@ private func calculateCircleIntersection(center: CGPoint, otherCenter: CGPoint,
return (point1Angle, point2Angle)
}
private func calculateMergingCircleShape(center: CGPoint, leftCenter: CGPoint?, rightCenter: CGPoint?, radius: CGFloat, totalCount: Int, unseenCount: Int, isSeen: Bool, segmentFraction: CGFloat) -> CGPath {
private func calculateMergingCircleShape(center: CGPoint, leftCenter: CGPoint?, rightCenter: CGPoint?, radius: CGFloat, totalCount: Int, unseenCount: Int, isSeen: Bool, segmentFraction: CGFloat, rotationFraction: CGFloat) -> CGPath {
let leftAngles = leftCenter.flatMap { calculateCircleIntersection(center: center, otherCenter: $0, radius: radius) }
let rightAngles = rightCenter.flatMap { calculateCircleIntersection(center: center, otherCenter: $0, radius: radius) }
@ -113,7 +113,7 @@ private func calculateMergingCircleShape(center: CGPoint, leftCenter: CGPoint?,
}
var startAngle = segmentSpacingAngle * 0.5 - CGFloat.pi * 0.5 + CGFloat(i) * (segmentSpacingAngle + segmentAngle)
startAngle += (1.0 - segmentFraction) * CGFloat.pi * 2.0 * (-0.25)
startAngle += -1.0 * (1.0 - rotationFraction) * CGFloat.pi * 2.0 * 0.5
let endAngle = startAngle + segmentAngle
path.move(to: CGPoint(x: center.x + cos(startAngle) * radius, y: center.y + sin(startAngle) * radius))
@ -343,6 +343,7 @@ public final class StoryPeerListItemComponent: Component {
public let scale: CGFloat
public let fullWidth: CGFloat
public let expandedAlphaFraction: CGFloat
public let expandEffectFraction: CGFloat
public let leftNeighborDistance: CGPoint?
public let rightNeighborDistance: CGPoint?
public let action: (EnginePeer) -> Void
@ -361,6 +362,7 @@ public final class StoryPeerListItemComponent: Component {
scale: CGFloat,
fullWidth: CGFloat,
expandedAlphaFraction: CGFloat,
expandEffectFraction: CGFloat,
leftNeighborDistance: CGPoint?,
rightNeighborDistance: CGPoint?,
action: @escaping (EnginePeer) -> Void,
@ -378,6 +380,7 @@ public final class StoryPeerListItemComponent: Component {
self.scale = scale
self.fullWidth = fullWidth
self.expandedAlphaFraction = expandedAlphaFraction
self.expandEffectFraction = expandEffectFraction
self.leftNeighborDistance = leftNeighborDistance
self.rightNeighborDistance = rightNeighborDistance
self.action = action
@ -421,6 +424,9 @@ public final class StoryPeerListItemComponent: Component {
if lhs.expandedAlphaFraction != rhs.expandedAlphaFraction {
return false
}
if lhs.expandEffectFraction != rhs.expandEffectFraction {
return false
}
if lhs.leftNeighborDistance != rhs.leftNeighborDistance {
return false
}
@ -785,8 +791,8 @@ public final class StoryPeerListItemComponent: Component {
}
Transition.immediate.setShapeLayerPath(layer: self.avatarShapeLayer, path: avatarPath)
Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeSeenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: true, segmentFraction: component.expandedAlphaFraction))
Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeUnseenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: false, segmentFraction: component.expandedAlphaFraction))
Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeSeenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: true, segmentFraction: component.expandedAlphaFraction, rotationFraction: component.expandEffectFraction))
Transition.immediate.setShapeLayerPath(layer: self.indicatorShapeUnseenLayer, path: calculateMergingCircleShape(center: indicatorCenter, leftCenter: mappedLeftCenter, rightCenter: mappedRightCenter, radius: indicatorRadius - indicatorLineUnseenWidth * 0.5, totalCount: component.totalCount, unseenCount: component.unseenCount, isSeen: false, segmentFraction: component.expandedAlphaFraction, rotationFraction: component.expandEffectFraction))
let titleString: String
if component.peer.id == component.context.account.peerId {

View File

@ -338,7 +338,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode {
title = EnginePeer(peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
subtitle = nil
}
actionTitle = "OPEN STORY"
actionTitle = item.presentationData.strings.Chat_OpenStory
default:
break
}